Skip to content

[enterprise][P0 CRITICAL] A WALLET-funded org with a CREDIT_POOL programme set to CHARGE_ORG has Payment.amount inflated by the overage leg, and a later cancellation refunds the inflated figure to the wallet #1458

Description

@teetangh

Found 2026-09-05 in wave-1B E2E on the deploy preview of #1422 (union of the merged train). Reproduced on a throwaway org (LearnPro Academy cmqb27jge06fttxyokp19psoj, WALLET rail, CREDIT_POOL programme with overageBehavior=CHARGE_ORG and a 100-paise budget).

Chain: lib/payments/operations/checkout.ts ~L3263-3277 writes a WALLET PaymentLeg for the FULL nominal price whenever isOrgWalletPayment is true and debits walletBalance (10,000,000 → 9,741,674 for a 258,326-paise booking). Then lib/payments/billing/overage-settlement.ts ~L280-303 (chargeTo ORG) tries to carve the covered base out of an INVOICE_ACCRUAL leg; a WALLET org has none (carved = 0), so it falls into the additive branch its own comment calls unreachable: it creates an OVERAGE_INVOICE_ACCRUAL leg for the marginal (248,326) and increments Payment.amount to 506,652 (originalAmount 218,920 + tax 39,406 ≠ amount). The OverageEvent (PENDING) has no collection path on the WALLET rail. Cancelling within the 100% window refunds amountRefundedPaise 506,652 on the INTERNAL rail and the wallet returns to 10,000,000 — 248,326 paise richer than before the booking (Payment 68092bbd…, Refund 6ddb2f0c…, OverageEvent cmtnocd52…).

Related P1s from the same run: CHARGE_MEMBER on a WALLET-funded parent throws in overage-settlement.ts ~L225-243 (#715 gap) with no config guard and no actionable error; PROGRAM_CAP_EXHAUSTED (402) is classified as a modelled outcome for Sentry at checkout.ts ~L3748 but the rethrow at ~L3792-3810 still answers a generic 500 UNKNOWN_ERROR.

Fix (orchestrator decision): (1) on the WALLET rail an overage is already paid by the wallet debit — the ORG branch must not add a leg or touch Payment.amount; record the OverageEvent as collected-via-wallet against the WALLET leg; (2) lib/enterprise config-lock/validators refuse CHARGE_MEMBER on a WALLET-funded billing account (unimplemented, #715) with a clear error, and document CHARGE_ORG-on-WALLET as wallet-collected; (3) checkout answers PROGRAM_CAP_EXHAUSTED with its own httpStatus/code and an actionable toast; (4) one pin per change and a refund pin proving a cancelled WALLET+CHARGE_ORG booking refunds exactly the wallet debit.

🤖 Generated with Claude Code

https://claude.ai/code/session_01MgadJydWEKkdhhzY58yiL7

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    EnterpriseEnterprise tier — B2B org features, Architecture 4bugSomething isn't workingfinancePayments, refunds, earnings, payouts, invoicing, ledgerlaunch: pre-mvpGates launch — money, data, or a failure we would not detectpriority: critical

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions