Public Beta — use code LAUNCH15 for 15% off any plan
Back to blog

The case of the disappearing discount

17 May 2026 · 3 min read

The case of the disappearing discount

The symptom

A customer messaged us after running through checkout with a LAUNCH15 coupon. The Configure page added their plan, applied the promo, and showed exactly what you'd want:

Plan          $3.32
Promo (LAUNCH15)   -$0.45
─────────────────
Total         $2.82

They clicked through to Payment, scanned the USDT QR — and the wallet showed $3.32. The discount line was there, the math was right on screen, but the on-chain amount was the gross price.

That's the kind of bug that costs more than the discount it ate. If a customer pays $3.32 thinking they got a deal, the trust hit is bigger than the 45 cents we saved by refunding.

Tracing the money

The order pipeline writes through three records — Order → Invoice → Payment. Each step references the previous one, so to find where the discount disappeared I followed amount_cents from the top.

Configure-page submit hit OrderController::persistDraftAsCartOrder(). The controller computed subtotal_cents, discount_cents, and total_cents correctly in PHP — verified by dumping them mid-handler. But the row it created looked like this:

Order::create([
    'amount_cents'   => $subtotal_cents,   // ← gross, before discount
    'discount_cents' => $discount_cents,
    // ...
]);

amount_cents was the gross. The discount was saved alongside but never subtracted into the column the rest of the system reads.

Two steps later, when the customer clicked Pay on Review, OrderController::store() summed amount_cents across each cart Order row to build the Invoice total. Invoice total then drove the Payment row, which drove the USDT QR amount, which drove what landed on chain.

The discount line in the Configure UI? That came from a separate computed property reading discount_cents directly. Two parallel paths, only one of which respected the promo.

The fix

Three places to update so net and gross stayed coherent:

  1. OrderController::persistDraftAsCartOrder() — write net to amount_cents (subtotal minus discount). The discount_cents column stays for audit / receipt rendering.
  2. OrderController::store() review-step subtotal — subtract draft-order discount_cents when totalling. Without this, the controller would have re-derived a gross from the corrected rows during a re-render.
  3. Review.vue draft section — mirror the live cartOrders[] rendering: show the promo line and net subtotal, not just the gross. Customers should see what the back end is going to charge, before they click Pay.

The test that locks it

Postmortems without a regression test are wishes. The end-to-end test now exercises:

Configure with a 15% coupon → Review → Store → Order persisted with net amount → Invoice picks up the net → Payment row carries the net → USDT QR encodes the net.

If any of those four hops drifts back to gross, the test fails before it ships. The discount is now a thing the system enforces, not just something the UI displays.

What we changed in our process

Two small things that would have caught this earlier:

  • End-to-end tests across the order pipeline, not just per-controller. Unit tests on persistDraftAsCartOrder could pass while the column mapping was wrong, because the mapping was the bug.
  • Asserting on the on-chain amount, not the UI total. The test now reads Payment::wallet_amount and compares to the expected net — the same number a customer will see in their wallet when they scan the QR.

What you should do

If you have an open invoice with a coupon applied and it was generated before this fix shipped (anything before v1.20), the QR may carry the gross amount. Email support and we'll regenerate it at the discounted rate — no questions asked, our bug, our fix.

For new orders, the Configure → Review → Payment pipeline is end-to-end coherent. You see what you pay.

Welcome to GoCelerus

Save 15% on any plan

Launch promo — 15% off any plan

Your coupon code

LAUNCH15
Browse plans