Spike — right primitive for email-blast checkout: Payment Links vs lazy-mint vs 30d TTL #489
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#489
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
Spike
Lineage
Standalone — emerged from the 2026-04-17 Utah Invitational stranded-orders investigation. The 30-day-TTL patch (sibling Bug issue) stops the immediate bleed; this spike picks the permanent architecture so we don't hit a 30-day cliff on the next late-opening parent.
Repo
forgejo_admin/basketball-api(primary — this is where the decision will be implemented; touches westside-app only if lazy-mint wins)Question
Which payment primitive should basketball-api use for email-blast checkout flows (tournament fees, monthly fees, future campaigns): Stripe Payment Links, lazy-mint
/pay/{order_token}, or keep Checkout Sessions with 30d TTL?What to Explore
Stripe Payment Links (
stripe.PaymentLink.create):order_id,player_id,product_id) such that the current webhook handler atroutes/webhooks.py::_handle_generic_order_completedstill matches?Lazy-mint
/pay/{order_token}:stripe.checkout.Sessionwith 24h TTL and redirects tosession.url.Keep Checkout Sessions with 30d TTL (the patch from the Bug issue):
For each option, evaluate on:
Reference existing code:
src/basketball_api/services/tournament_checkout.py— blessed helpersrc/basketball_api/routes/webhooks.py::_handle_generic_order_completed— webhook matcherdocs/tournament-billing-runbook.md— current (incorrect) framingAlso consider the monthly-fee retry cohort (parents 111, 118, 127 each had 2–4 canceled sessions before succeeding). The right architecture eliminates those retries.
Success Criteria
docs/adr-payment-blast-pattern.mdTime-box
Maximum time to spend: 1 session (~2-3 hours). If time-box expires without a decisive recommendation, close spike with documented findings and escalate to Lucas for direction. Rabbit-hole risk: getting lost comparing every Stripe product. Anchor on "what ships this quarter" when stuck.
Related
project-pal-e-platformforgejo_admin/basketball-api #486— stranded-orders recovery (the incident that surfaced this)forgejo_admin/basketball-api #487— expired-session metricforgejo_admin/pal-e-platform #295— alert ruleexpires_atpatch (filed in parallel — the patch proceeds regardless of this spike's outcome)