Bug: jersey_option set on checkout creation, not payment confirmation #137

Closed
opened 2026-03-21 20:51:28 +00:00 by forgejo_admin · 0 comments

Type

Bug

Lineage

plan-wkq → Phase 11 (Girls Tryout) → discovered during jersey email blast 2026-03-21

Repo

forgejo_admin/basketball-api

What Broke

POST /jersey/checkout (jersey.py:169-172) sets player.jersey_option and player.jersey_order_status = pending immediately when the Stripe checkout session is created — before the parent even sees the payment page. If the parent abandons checkout (closes tab, card declined, navigates away), the DB retains the jersey option as if it were a real order.

Found on 2026-03-21: 4 players had jersey_option set (reversible/jersey_warmup) with zero matching Stripe charges. Stripe showed all jersey checkout sessions as unpaid. Data was manually cleaned — fake orders cleared, only legitimate opt-outs retained.

Repro Steps

  1. Navigate to /jersey?token={parent_token}
  2. Select "Reversible Jersey — $90" and click "Order — $90"
  3. Stripe checkout page opens
  4. Close the tab without completing payment
  5. Query DB: SELECT jersey_option FROM players WHERE parent_id = {id} → shows reversible (should be NULL)

Expected Behavior

jersey_option should remain NULL until Stripe confirms payment via webhook. The webhook handler (webhooks.py:199-200) already reads the option from Stripe session metadata and sets it correctly on checkout.session.completed. The checkout endpoint should only create the Stripe session, not pre-set the order.

Opt-out is correct — no Stripe involved, sets jersey_option = opt_out immediately (jersey.py:116-120). No change needed there.

Environment

  • Cluster/namespace: basketball-api (prod)
  • Service version/commit: a81d8817710bbce5a9da558a1af73db4ff25c3dd
  • Related alerts: none (data integrity issue, not a crash)

Acceptance Criteria

  • When a parent starts jersey checkout but abandons, jersey_option remains NULL
  • When a parent completes payment, webhook sets both jersey_option and jersey_order_status = paid
  • Opt-out flow unchanged (no Stripe involved, sets immediately)
  • Existing tests updated to verify checkout does NOT set jersey_option
  • plan-wkq — Phase 11
  • project-westside-basketball
### Type Bug ### Lineage `plan-wkq` → Phase 11 (Girls Tryout) → discovered during jersey email blast 2026-03-21 ### Repo `forgejo_admin/basketball-api` ### What Broke `POST /jersey/checkout` (jersey.py:169-172) sets `player.jersey_option` and `player.jersey_order_status = pending` immediately when the Stripe checkout session is created — before the parent even sees the payment page. If the parent abandons checkout (closes tab, card declined, navigates away), the DB retains the jersey option as if it were a real order. Found on 2026-03-21: 4 players had `jersey_option` set (reversible/jersey_warmup) with zero matching Stripe charges. Stripe showed all jersey checkout sessions as `unpaid`. Data was manually cleaned — fake orders cleared, only legitimate opt-outs retained. ### Repro Steps 1. Navigate to `/jersey?token={parent_token}` 2. Select "Reversible Jersey — $90" and click "Order — $90" 3. Stripe checkout page opens 4. Close the tab without completing payment 5. Query DB: `SELECT jersey_option FROM players WHERE parent_id = {id}` → shows `reversible` (should be NULL) ### Expected Behavior `jersey_option` should remain NULL until Stripe confirms payment via webhook. The webhook handler (webhooks.py:199-200) already reads the option from Stripe session metadata and sets it correctly on `checkout.session.completed`. The checkout endpoint should only create the Stripe session, not pre-set the order. Opt-out is correct — no Stripe involved, sets `jersey_option = opt_out` immediately (jersey.py:116-120). No change needed there. ### Environment - Cluster/namespace: basketball-api (prod) - Service version/commit: `a81d8817710bbce5a9da558a1af73db4ff25c3dd` - Related alerts: none (data integrity issue, not a crash) ### Acceptance Criteria - [ ] When a parent starts jersey checkout but abandons, `jersey_option` remains NULL - [ ] When a parent completes payment, webhook sets both `jersey_option` and `jersey_order_status = paid` - [ ] Opt-out flow unchanged (no Stripe involved, sets immediately) - [ ] Existing tests updated to verify checkout does NOT set jersey_option ### Related - `plan-wkq` — Phase 11 - `project-westside-basketball`
forgejo_admin 2026-03-21 21:01:22 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
forgejo_admin/basketball-api#137
No description provided.