Support Stripe discount coupons in registration (WESTSIDE50, GIRLSHOOPS) #391
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#391
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). Stripe has active promotion codes (WESTSIDE50 50% off, GIRLSHOOPS 20% off) but the registration form only supports free promo codes.
Repo
forgejo_admin/basketball-apiUser Story
As a parent with a discount code from Coach Marcus
I want to enter it during registration and pay the reduced amount
So that I get the discount without needing a separate payment arrangement
Context
Current promo code flow: code is checked against
BASKETBALL_TRYOUT_PROMO_CODESenv var (comma-separated list:TESTFREE,GIRLS2026). If it matches, registration is free — Stripe is bypassed entirely. If it doesn't match, the code is rejected.Stripe already has active promotion codes:
WESTSIDE50— 50% off ($15 instead of $30), active, 6 redemptionsGIRLSHOOPS— 20% off ($24 instead of $30), activeCASHPAID— 100% off, activeTESTFREE— 100% off, activeThe fix: when a promo code doesn't match the free list, check Stripe for a valid promotion code. If found, create a Checkout Session with the discount applied and redirect to Stripe for the reduced amount.
File Targets
Files to modify:
src/basketball_api/routes/register.pylines 1187-1197 — after free code check, add Stripe promotion code lookup. If valid, fall through to the card payment path withdiscountsparameter onSession.create()Specifically in the
Session.create()call (line 1349), add:Files NOT to touch:
src/basketball_api/services/email.py— email works the same regardless of discountAcceptance Criteria
Test Expectations
pytest tests/test_promo_registration.pyConstraints
stripe.PromotionCode.list(code=code_str, active=True)to validate — returns list, check if non-emptydiscountsparampayment_status=paidimmediately — discount codes should setpayment_status=pendinglike card payments (payment completes via webhook)Checklist
Related
westside-basketballAPPROVED
Scope review findings:
1. File targets verified
register.pylines 1187-1197: promo code validation block confirmed. Checksbody.promo_codeagainstsettings.tryout_promo_codes(comma-separated env var). Returns 400 if no match. This is the insertion point for Stripe promotion code fallback.register.pyline 1349:stripe.checkout.Session.create()confirmed at this line, inside theif body.payment_method == "card"block. Currently usesline_itemswithprice_data(dynamic pricing, $30 tryout / $40 remote). Nodiscountsparam yet.2. Stripe API confirmed
stripe.PromotionCode.list()acceptscode: strandactive: boolfilter params (verified instripe/params/_promotion_code_list_params.py). Case-insensitive matching per Stripe docs.Session.create()acceptsdiscountsparam (list of dicts withpromotion_codekey). Confirmed via Session model havingdiscounts: Optional[List[Discount]]attribute. Stripe SDK v14.4.1 installed.3. Frontend handles redirect
westside-app register/+page.svelteline 154:if (result?.redirect_url) { window.location.href = result.redirect_url; return; }— already handles redirect responses. The promo path returning aredirect_urlinstead of credentials will work without frontend changes.4. No scope conflicts
register.py. Branch108-register-photo-uploadis stale (no associated PR). Safe to proceed.5. Test file exists
tests/test_promo_registration.pyexists with 4 existing tests inTestPromoCodeRegistrationclass: valid code, case-insensitive matching, invalid code returns 400, missing code returns 400. New Stripe discount tests can extend this class.All file targets, line numbers, and API assumptions check out. Ready for dispatch.