Skip to content

Ask for a new card, not a new purchase, when a payment is declined - #1098

Merged
helpcodeai merged 2 commits into
mainfrom
feat/payment-failed-update-card
Oct 15, 2026
Merged

helpcodeai merged 2 commits into
mainfrom
feat/payment-failed-update-card

Conversation

@helpcodeai

Copy link
Copy Markdown
Contributor

Why

A card trial whose first charge is declined has its licence revoked on the licence site while Stripe keeps retrying the subscription (past_due, 8 retries in 7 days). The Cloud wall then showed View Plans & Purchase License, and customers bought a second subscription beside the first. That caused the duplicate subscriptions on 13 and 14 Oct.

What

Backend (license)

  • getPaymentFailedLicense(org): the latest paid licence that is not active, with billing past_due/unpaid and not cancelling.
  • GET /api/license/status returns it with paymentFailed: true and billing. It takes precedence over a free trial that has run out (a card trial is charged when the free trial ends).
  • createBillingPortalSession falls back to that licence. The new flow payment_method_update is accepted (DTO + service).
  • checkout-link and the card-trial eligibility refuse while a declined card is being retried.
  • verifyLicense stores billing sent for invalid licences.
    • reverifyPaidLicenses also re-checks revoked/expired licences whose subscription is still live, so a retry that succeeds lifts the wall within a day.
    • refreshLicense does the same, right after the card update.

Frontend

  • Wall: a new payment-failed reason.
    • Admins see Update card, which opens Stripe's add-a-card page via the portal flow. Stripe sets the card as the subscription default and pays the open invoice immediately. I've updated my card re-verifies.
    • Other roles are told to ask an admin. No purchase or plan picker.
  • Licence page: Update payment method opens the same card page when a payment failed.

Depends on

HelpCode-ai/anythingmcp-website#387: the portal payment_method_update flow and billing on invalid licences. It must be deployed first; otherwise the wall never appears (no billing for revoked licences) and the portal opens on its home page.

Tests

  • backend jest src/license src/ee: 230 passed. New cases cover status, checkout refusal, eligibility, portal fallback, getPaymentFailedLicense and re-verification.
  • frontend Playwright billing-awareness (2 new), neutral-signup-and-billing, card-trial: all pass.
  • tsc clean, eslint 0 errors.

A card trial whose first charge is declined has its licence revoked while
Stripe keeps retrying the subscription. The app showed "View Plans &
Purchase License", and customers bought a second subscription beside the
first one.

- The licence status reports paymentFailed (with the subscription's billing)
  for a paid licence stopped while its subscription is past_due or unpaid,
  ahead of the free trial that ended with it.
- The wall shows "Update card", which opens Stripe's add-a-card page; Stripe
  sets the card on the subscription and pays the open invoice. "I've updated
  my card" re-verifies the licence.
- The billing portal opens for that licence, the licence page's "Update
  payment method" goes to the same page, and checkout and the card-trial
  offer are refused meanwhile.
- verifyLicense stores the billing the licence site now sends for invalid
  licences, and the daily re-verification includes them, so a retry that
  goes through lifts the wall.
@helpcodeai
helpcodeai merged commit 1c89b49 into main Oct 15, 2026
14 checks passed
@helpcodeai
helpcodeai deleted the feat/payment-failed-update-card branch October 15, 2026 06:11
@github-actions github-actions Bot locked and limited conversation to collaborators Oct 15, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant