Project

General

Profile

Edit Copy Actions

Feature #8385

open

Feature #8327: Subscriptions Analytics & visualisation

Working On Create a new invoice for each Auto recurring payment and Resend Invoice PDF Funsationality in Service Subscribers

Added by Divya Inapakurthi 15 days ago. Updated 14 days ago.

Status:
Resolved
Priority:
Normal
Target version:
-
Start date:
03/25/2026
Due date:
03/25/2026 (14 days late)
% Done:

100%

Estimated time:

Files


Add

Subtasks


Add

Related issues

Updated by Divya Inapakurthi 14 days ago

  • Due date set to 03/25/2026
  • Status changed from New to Resolved

Issues Fixed
Subscription Invoice Processing
Recurring invoice duplication: Fixed the

handleInvoicePaidEvent
to correctly detect billing_reason from Stripe, separating initial payments (subscription_create) from recurring auto-payments — preventing duplicate invoice records and double job creation
Invoice lookup bug: Removed faulty fallback that reused the initial invoice for recurring cycles; each Stripe invoice now gets its own MongoDB document
Atomic idempotency guard: Replaced simple save() with MongoDB updateOne + conditional match on "invoices.stripeInvoiceId": { $ne: stripeInvoice.id } to prevent race-condition duplicate payment pushes
invoice.payment_succeeded double processing: Changed the webhook handler to skip this event (previously it ran the full handler), since Stripe always emits invoice.paid for the same invoice — preventing duplicate emails and jobs
Wrong isPaidStatus check: Tightened the guard to only consider an invoice "already paid for this cycle" if the stripeInvoiceId on the document matches the current Stripe invoice ID
Subscription Contract Activation (Fallback)
customerType missing on fallback contracts: Added multi-level resolution — reads from PendingSubscription, then Stripe metadata, then linked invoice doc
customPlanEndDate not saved: Added parsing from Stripe metadata and invoice period end as a fallback
serviceAddress stored as empty object: Added a check for empty objects (not just null) before triggering re-resolution
invoiceId lookup in PendingSubscription: Added metaInvoiceId as a third filter key for more precise lookup, reducing cross-subscription mis-matching
Auto-Job Creation
Jobs created on every recurring payment: Gated

createAutojobsForSubscription
behind isInitialSubscriptionPayment flag — jobs are now only created on the first payment, not on every renewal
Contract-Invoice Linkage
Contract not updated with new invoice IDs: Added $addToSet: { invoices: existingInvoice._id } to keep the contract's invoice array in sync on all processing paths, including retries
Email URLs
Hardcoded production URLs in emails: Replaced all "https://evergreenfarmsusa.com/" hardcoded URLs with dynamic process.env.clientBaseUrl || process.env.CLIENT_URL across:

invoicePaid.js
(service plan confirmation emails)

subscripiton.service.js
(checkout sessions, cancellation emails)

checkoutCompleted.js
(product plan confirmation dashboard URL)
Code Quality
Global debugger cleanup: Commented out all active debugger statements across 10+ files (controllers, services, webhook handlers, helpers)
✅ New Implementations
Subscription Email Cron System (

subscriptionEmailCron.js
)
Pickup Reminder: Daily job at 9:30 AM ET — emails customers about uncollected subscription orders, repeats every day until picked up
Auto-Debit Reminder: Daily job at 10:00 AM ET — emails customers 2 days before their next payment is auto-debited
Premium Email Design
Redesigned the Auto-Debit Reminder email with full branded HTML layout — logo, summary card (plan, amount, debit date), CTA button, and professional footer
Made email CTA URLs dynamic — product subs link to /product-plans, service subs link to /service-plans
Smart Price Fetching (Cron)
Product subscriptions: Fetch actual amount from the latest successful PaymentTransaction (includes taxes & delivery fees), with variant finalPrice + deliveryFee as fallback
Service subscriptions: Correctly use contract.totalAmount instead of plan-level price
Per-Cycle Invoice Documents (Off-Store & On-Store)
Each Stripe recurring invoice now creates a separate invoice document in MongoDB — giving proper per-cycle payment history instead of updating one document repeatedly Issues Fixed
Subscription Invoice Processing
Recurring invoice duplication: Fixed the

handleInvoicePaidEvent
to correctly detect billing_reason from Stripe, separating initial payments (subscription_create) from recurring auto-payments — preventing duplicate invoice records and double job creation
Invoice lookup bug: Removed faulty fallback that reused the initial invoice for recurring cycles; each Stripe invoice now gets its own MongoDB document
Atomic idempotency guard: Replaced simple save() with MongoDB updateOne + conditional match on "invoices.stripeInvoiceId": { $ne: stripeInvoice.id } to prevent race-condition duplicate payment pushes
invoice.payment_succeeded double processing: Changed the webhook handler to skip this event (previously it ran the full handler), since Stripe always emits invoice.paid for the same invoice — preventing duplicate emails and jobs
Wrong isPaidStatus check: Tightened the guard to only consider an invoice "already paid for this cycle" if the stripeInvoiceId on the document matches the current Stripe invoice ID
Subscription Contract Activation (Fallback)
customerType missing on fallback contracts: Added multi-level resolution — reads from PendingSubscription, then Stripe metadata, then linked invoice doc
customPlanEndDate not saved: Added parsing from Stripe metadata and invoice period end as a fallback
serviceAddress stored as empty object: Added a check for empty objects (not just null) before triggering re-resolution
invoiceId lookup in PendingSubscription: Added metaInvoiceId as a third filter key for more precise lookup, reducing cross-subscription mis-matching
Auto-Job Creation
Jobs created on every recurring payment: Gated

createAutojobsForSubscription
behind isInitialSubscriptionPayment flag — jobs are now only created on the first payment, not on every renewal
Contract-Invoice Linkage
Contract not updated with new invoice IDs: Added $addToSet: { invoices: existingInvoice._id } to keep the contract's invoice array in sync on all processing paths, including retries
Email URLs
Hardcoded production URLs in emails: Replaced all "https://evergreenfarmsusa.com/" hardcoded URLs with dynamic process.env.clientBaseUrl || process.env.CLIENT_URL across:

invoicePaid.js
(service plan confirmation emails)

subscripiton.service.js
(checkout sessions, cancellation emails)

checkoutCompleted.js
(product plan confirmation dashboard URL)
Code Quality
Global debugger cleanup: Commented out all active debugger statements across 10+ files (controllers, services, webhook handlers, helpers)

✅ New Implementations

Premium Email Design
Redesigned the Auto-Debit Reminder email with full branded HTML layout — logo, summary card (plan, amount, debit date), CTA button, and professional footer
Made email CTA URLs dynamic — product subs link to /product-plans, service subs link to /service-plans
Smart Price Fetching (Cron)
Product subscriptions: Fetch actual amount from the latest successful PaymentTransaction (includes taxes & delivery fees), with variant finalPrice + deliveryFee as fallback
Service subscriptions: Correctly use contract.totalAmount instead of plan-level price
Per-Cycle Invoice Documents (Off-Store & On-Store)
Each Stripe recurring invoice now creates a separate invoice document in MongoDB — giving proper per-cycle payment history instead of updating one document repeatedly

Updated by Divya Inapakurthi 14 days ago

  • % Done changed from 0 to 100

New Implementations
Standardized Transaction IDs: Created a permanent, human-readable ID system for product subscription payments (e.g., TRN_001, TRN_002). This replaces the raw MongoDB/Stripe IDs in customer-facing communications for better professionalism and tracking.
Resend Invoice PDF Feature: Fully implemented the "Resend Invoice PDF" functionality for product subscriptions. Admins can now manually resend stylized invoice summaries directly from the subscriber's payment progress bar.
Dynamic Email Branding: Refined subscription emails to dynamically pull company details (Logo, Address, Support Email, Website) based on the business slug, ensuring consistent branding across different stores.
Enhanced Payment Tooltips: Updated the Payment Progress Bar UI to show the new Transaction ID and added a direct action button to resend invoice emails for any specific past payment.
Refinements & Professionalization
Case Consistency: Applied toTitleCase across all major invoice controllers, webhooks, and email templates to ensure customer names and billing frequencies (e.g., Monthly, Quarterly) always appear correctly formatted.
Clean Invoicing Logic: Modified the payment progress UI to intelligently hide the "Remaining Balance" field for subscription transactions, as these are typically paid in full per cycle.
Backend Security: Replaced hardcoded SMTP credentials in the invoice controller with secure environment variables (EMAIL_USER, EMAIL_PASSWORD).
Optimized Webhooks: Integrated the unique ID generation logic into both the initial checkout and recurring renewal handlers to ensure every payment record is unique and trackable from day one.
Issues Resolved
Email Deliverability: Fixed inconsistent "From" addresses and hardcoded text in the primary invoice controller to ensure emails are correctly branded by the sender's company name.
Model Synchronization: Updated the Transaction schema to include the missing transactionId field, preventing data loss for new human-readable IDs.

Edit Copy Actions

Also available in: Atom PDF