Fix: first-payment checkout returns 409 for all parents (stale pending orders) #473
Labels
No labels
domain:backend
domain:devops
domain:frontend
status:approved
status:in-progress
status:needs-fix
status:qa
type:bug
type:devops
type:feature
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
forgejo_admin/basketball-api#473
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Type
Bug
Lineage
Standalone — discovered Apr 13 during payment pipeline investigation. Blocking all monthly revenue collection.
Repo
forgejo_admin/basketball-apiWhat Broke
GET /checkout/first-payment?token=Xreturns 409 for every parent. 100% failure rate in prod logs since at least 18:08 Apr 13. Only 1 of 7 monthly orders completed payment — the other 6 are stuck inpendingstatus, permanently blocking those parents from retrying.The duplicate order guard at
checkout.py:347-361treatspendingthe same aspaid. Once a pending Order is created (by first click or by link-preview bots likefacebookexternalhit/1.1 Facebot), all subsequent clicks return 409.Prod log evidence (all 409):
Current order state:
Repro Steps
GET /checkout/first-payment?token=X— creates pending Order + Stripe sessionExpected Behavior
Parent clicks the payment link and reaches Stripe checkout. If a previous attempt created a stale pending order, the system should cancel it and create a fresh checkout session.
Environment
basketball-apibb7723a(main)forgejo_admin/pal-e-platform#290)File Targets
Files to modify:
src/basketball_api/routes/checkout.py— lines 347-361,first_payment_checkout()duplicate guard. If apendingorder exists older than 30 min, cancel it and create a new checkout session. Only hard-block onpaid.Files to NOT touch:
src/basketball_api/routes/webhooks.py— working correctlysrc/basketball_api/services/email.py— not the issuesrc/basketball_api/routes/subscriptions.py— separate systemAcceptance Criteria
Test Expectations
test_first_payment_stale_pending_replaced— pending order >30 min is canceled, new session created, returns 307test_first_payment_paid_still_blocked— paid order returns 409test_first_payment_fresh_pending_returns_redirect— pending order <30 min redirects to existing Stripe URLpytest tests/test_first_payment.py -vConstraints
status=canceled), do not delete — audit trailcheckout.pyRelated
westside-basketball— project this affectsforgejo_admin/pal-e-platform#290— observability ticket for payment pipeline alertingScope Review: READY
Review note:
review-1003-2026-04-13Ticket scope is solid — all Bug template sections present (plus File Targets, Test Expectations, Constraints beyond minimum). File target at checkout.py:347-361 verified against live codebase. Traceability complete (story:WS-S11 confirmed, arch:basketball-api label present). Single file, single repo, 5 AC, 3 tests — well under 5-minute rule. Ready for dispatch.
Blast radius note: Same
pending-as-blocker pattern exists increate_checkout_session()(line 181) andcreate_player_checkout_session()(line 485) — track as separate tickets if those flows also get bot-triggered 409s.