Jersey Stripe checkout + second tryout announcement email #103
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#103
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
plan-wkq→ Phase 11 → Second tryout operations + jersey orderingRepo
forgejo_admin/basketball-apiUser Story
As an admin
I want to send all parents an announcement email with second tryout details, jersey ordering via Stripe, and profile completion link
So that parents can complete profiles, order jerseys, and show up to the second tryout
Context
Marcus communicated the following on 2026-03-18:
The Gmail OAuth email service exists (
services/email.py) with branded HTML templates and_brand_wrapper()helper. Stripe integration exists insubscriptions.py(checkout sessions, webhooks, customer management). Parent model hasregistration_tokenfor token-based auth. Keycloak admin API integration exists inservices/keycloak.py.Auto-generated passwords use pattern
Westside-{FirstName}-{2digits}but are NOT stored in the DB — only shown once during registration. To include credentials in the email, generate a new password via Keycloak Admin API and include it.Stripe secrets: env var
BASKETBALL_STRIPE_API_KEY. Test keys available at~/secrets/stripe/test-secret-key.File Targets
Files the agent should modify or create:
src/basketball_api/models.py— addJerseyOptionenum (reversible, jersey_warmup, opt_out), addjersey_optionandjersey_order_statusfields to Player model.jersey_order_statusenum: none, pending, paid, shipped.src/basketball_api/routes/admin.py— addPOST /admin/email/tryout-announcementendpoint that sends branded email to all parents (or single parent for test mode via?test_email=marcus@email.comquery param)src/basketball_api/routes/jersey.py— NEW file:GET /jersey/options— returns available jersey options with prices (public, no auth needed)POST /jersey/checkout— token-authenticated (viaregistration_tokenquery param), creates Stripe Checkout Session for selected jersey option, returns checkout URL. Must setstripe_customer_idfrom player record as the Stripe customer and includeplayer_id+jersey_optionin session metadata.main.pysrc/basketball_api/routes/webhooks.py— add handler for jersey checkout session completed: updateplayer.jersey_optionandplayer.jersey_order_status = paidsrc/basketball_api/services/email.py— addsend_tryout_announcement_email(tenant, parent, players, credentials, db)function with branded HTML including: second tryout details, jersey options with link tohttps://westside.tail5b443a.ts.net/jersey?token={registration_token}, profile CTA with link tohttps://westside.tail5b443a.ts.net/players/{player_id}, login credentials (email + new password)src/basketball_api/services/keycloak.py— addreset_parent_password(parent_email, new_password)function using existing Keycloak Admin API to set a new password. Use existinggenerate_password()fromservices/password.pyto create the new password.alembic/versions/012_add_jersey_fields.py— migration for new enum + fieldsFiles the agent should NOT touch:
src/basketball_api/routes/register.py— registration flow is separatesrc/basketball_api/routes/subscriptions.py— monthly dues are separate from jersey one-time purchasesAcceptance Criteria
GET /jersey/optionsreturns 3 options with correct pricesPOST /jersey/checkout?token={reg_token}creates a Stripe Checkout Session tied to the correct customer, returns checkout URLjersey_optionandjersey_order_statuson the player after successful paymentPOST /admin/email/tryout-announcementsends branded email to all parents with: tryout details, jersey link, profile link, login credentialsPOST /admin/email/tryout-announcement?test_email=someone@test.comsends only to that email (for Marcus test)Test Expectations
pytest tests/ -k "jersey or tryout_announcement"Constraints
subscriptions.pyandwebhooks.pyservices/email.py— use_brand_wrapper()and brand colorsroutes/admin.pypaymentmode, notsubscriptionstripe.checkout.Session.create()withmode="payment"andcustomer=player.stripe_customer_idstripe_customer_id, create one using parent email (same pattern as subscriptions.py)registration_token, then get their playersChecklist
ruff checkcleanRelated
phase-wkq-11-girls-tryout— second tryout operations