Payment recovery for abandoned Stripe registrations #389
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#389
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
Feature
Lineage
Standalone — discovered during registration flow audit (2026-04-07). Current flow has no recovery path for abandoned Stripe checkouts.
Repo
forgejo_admin/basketball-apiUser Story
As a parent who started registration but didn't complete Stripe payment
I want to receive a reminder email with a payment link
So that I can complete my registration without re-filling the entire form
Context
Current card payment flow: form → API creates PENDING registration → redirects to checkout.stripe.com → if user pays, webhook fires and completes registration. If user closes the tab or abandons Stripe, the registration sits as PENDING forever. No recovery email is sent. The Stripe session URL expires after 24h. The user must return to /register and fill out the form again to get a new Stripe session.
The payment gate is correct — no payment = no account = no junk registrations. But abandoned registrations have no recovery path. This is lost revenue.
Important: Both card (
signup_method="stripe") and cash (signup_method="cash") registrations create PENDING records. Only Stripe registrations should receive recovery emails — cash payments are handled offline by Marcus.File Targets
Files to modify:
src/basketball_api/services/email.py— newsend_payment_reminder_email()function using the MJML template system (load_email_template)src/basketball_api/routes/admin.py— new admin endpoint following the existing outbox pattern (seeadmin.py:899-913for the pattern). Admin-triggered, not cron — keeps it simple and auditable.src/basketball_api/models.py— addrecovery_email_sent: boolcolumn to Registration model (follow existingconfirmation_email_sentpattern at line 289). This enforces the "only sent once" requirement at the data layer.Files NOT to touch:
Acceptance Criteria
recovery_email_sentcolumnsignup_method="cash") PENDING registrations are NOT sent recovery emails — onlysignup_method="stripe"registrations qualifyTest Expectations
pytest tests/ -k test_payment_recoveryConstraints
load_email_template)admin.py:899-913signup_method == "stripe"to exclude cash registrationsregistration_tokengeneration needed for recovery email URLs)Checklist
Related
westside-basketballScope Review: NEEDS_REFINEMENT
Review note:
review-890-2026-04-08Ticket is well-structured but has 5 fixable gaps before an agent can execute cleanly.
models.pyneedsrecovery_email_sentboolean column + Alembic migration (followconfirmation_email_sentpattern)signup_method='cash') also go PENDING — query must filtersignup_method == "stripe"to avoid emailing cash parentsarch-registrationdoes not exist in pal-e-docs — create itScope refinement applied (review-890-2026-04-08):
models.py+ Alembic migration as file targets —recovery_email_sentboolean column needed (followsconfirmation_email_sentpattern at line 289)signup_method="stripe"andsignup_method="cash"create PENDING records. Added explicitsignup_method == "stripe"filter requirement.admin.py:899-913Arch note gap (
arch:registrationlabel with no backing note) tracked as separate backlog item.Scope Review: READY
Review note:
review-890-2026-04-08All 6 refinements verified — template complete, file targets confirmed against codebase, traceability intact, ACs testable. Ready for dispatch once #390 lands.
Missing
arch-registrationnote tracked as discovered scope (not a blocker).Validation: FAIL
Tiers executed: Tier 1 (local tests), Tier 3 (prod health check)
Validation note:
validation-389-2026-04-088 checks: 6 PASS, 2 FAIL
Failures:
update-kustomize-tagstep was SKIPPED due to pre-existing test failure. Image was built and pushed to Harbor but kustomize tag was never updated.2d85242cc9(PR #399), not16b96f5(PR #397). PR #397 code is not live in production.Tier 1 (local): All 4 payment recovery unit tests PASS. Full suite 913/917 pass (4 pre-existing failures unrelated to #397). No regressions.
Remediation: Re-trigger pipeline or manually update kustomize tag to
16b96f547bb73b8680bf164a53db408a33adda4a. Verify basketball-api.woodpecker.yamlhas the fix from pal-e-platform PR #275 (run update-kustomize-tag even when test step fails).