From 770808613010e2f6d84e6f3c7d40851433d1b48d Mon Sep 17 00:00:00 2001 From: justin Date: Wed, 10 Jun 2026 07:04:06 -0500 Subject: [PATCH] Fix batch payment totals in Telegram and ERPNext invoice flow --- api/src/routes/checkout.ts | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/api/src/routes/checkout.ts b/api/src/routes/checkout.ts index a34fd30..5d8f4fe 100644 --- a/api/src/routes/checkout.ts +++ b/api/src/routes/checkout.ts @@ -1676,6 +1676,21 @@ export async function handlePaymentComplete( const paymentMethod = (order.payment_method as string) || "card"; console.log(`[checkout] Payment confirmed: ${order_type} ${order_id} via ${paymentMethod}`); + // Prefer the payment gateway's recorded total when available. This keeps + // downstream notifications honest even if older rows still carry stale + // per-line surcharge values from before the batch-surcharge split fix. + let actualPaidCents: number | null = null; + if (stripe && session_id && !session_id.startsWith("crypto-")) { + try { + const session = await stripe.checkout.sessions.retrieve(session_id); + if (typeof session.amount_total === "number") { + actualPaidCents = session.amount_total; + } + } catch (stripeErr) { + console.warn(`[checkout] Could not retrieve Stripe session ${session_id} for notification totals:`, stripeErr); + } + } + // ── Telegram order notification ────────────────────────────────────── try { const botToken = process.env.TELEGRAM_BOT_TOKEN; @@ -1696,7 +1711,11 @@ export async function handlePaymentComplete( surchargeCents += Number(r.surcharge_cents || 0); serviceNames.push((r.service_name as string) || (r.service_slug as string) || ""); } - const totalCents = subtotalCents - discountCents + surchargeCents; + const totalCents = actualPaidCents ?? (subtotalCents - discountCents + surchargeCents); + const derivedSurchargeCents = totalCents - subtotalCents + discountCents; + if (derivedSurchargeCents >= 0) { + surchargeCents = derivedSurchargeCents; + } const totalDollars = (totalCents / 100).toFixed(2); const serviceLine = serviceNames.length <= 1 @@ -1970,10 +1989,13 @@ export async function handlePaymentComplete( // Create Sales Invoice try { - // Calculate batch total (sum of all line items minus discounts) - const batchTotalCents = (batchRows.rows as any[]).reduce((sum, bo) => { + // Calculate the actual paid amount for the batch: service + gov fees + // - discounts + surcharge. Older versions passed only the pre- + // surcharge total, which understated the Payment Entry amount. + const batchBaseCents = (batchRows.rows as any[]).reduce((sum, bo) => { return sum + (bo.service_fee_cents || 0) + (bo.gov_fee_cents || 0) - (bo.discount_cents || 0); }, 0); + const batchTotalCents = batchBaseCents + ((Number(order.surcharge_cents) || 0)); const invName = await createInvoiceFromSalesOrder({ salesOrderName: soName, paymentGateway: paymentMethod,