feat: Stripe Subscription endpoints + webhooks for monthly payments (Phase 9a) #83
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#83
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?
Lineage
plan-2026-03-08-tryout-prep→ Phase 9 (Monthly Club Payments) → Phase 9a (Backend)Repo
forgejo_admin/basketball-apiUser Story
As an admin
I want to create monthly subscriptions for players and see their payment status
So that the club collects $200/month per player and I know who's current vs behind
Context
The club charges $200/month per player for the season. Currently payments are one-time tryout fees ($30) via Stripe Checkout. This phase adds recurring billing via Stripe Subscriptions.
Key decisions from planning:
BASKETBALL_STRIPE_API_KEY) and webhook secret (BASKETBALL_STRIPE_WEBHOOK_SECRET) are already deployed in k8s and sufficient/webhooks/stripehandlescheckout.session.completed— extend it for subscription eventsStripe Subscriptions flow:
subscription_statuson Player model based on eventsFile Targets
Files the agent should modify:
src/basketball_api/models.py— addsubscription_status(enum: active/past_due/canceled/none),stripe_customer_id,stripe_subscription_idto Player modelalembic/versions/xxx_add_subscription_fields.py— new migration for Player subscription fieldssrc/basketball_api/routes/webhooks.py— extend existing webhook handler with subscription event typessrc/basketball_api/main.py— register subscriptions routerFiles the agent should create:
src/basketball_api/routes/subscriptions.py— new route file: subscription CRUD, payment overviewtests/test_subscriptions.py— tests for all subscription endpoints and webhook handlersFiles the agent should NOT touch:
src/basketball_api/routes/teams.py— Phase 10, separate concernsrc/basketball_api/routes/register.py— registration flow unchangedsrc/basketball_api/auth.py— auth already workingAcceptance Criteria
subscription_statusenum field (active/past_due/canceled/none, default none)stripe_customer_id(nullable String)stripe_subscription_id(nullable String)POST /api/subscriptions/setup— admin-only, one-time: creates Stripe Product + Price if not exists, returns product/price IDs (idempotent)POST /api/subscriptions/{player_id}— admin-only: creates Stripe Customer (using parent email) + Subscription for player, stores IDs on Player model, sets status to activeGET /api/subscriptions— admin-only: list all players with subscription status, grouped by statusGET /api/subscriptions/overview— admin-only: summary (total active, past_due, canceled, none; total monthly revenue; count by status)DELETE /api/subscriptions/{player_id}— admin-only: cancels Stripe Subscription, updates status to canceledinvoice.paid→ confirms status activeinvoice.payment_failed→ sets status to past_duecustomer.subscription.updated→ syncs statuscustomer.subscription.deleted→ sets status to canceledGET /api/subscriptions/mine— authenticated player/parent: returns own subscription status + Stripe Customer Portal linkTest Expectations
pytest tests/test_subscriptions.py -vConstraints
require_role("admin")fromsrc/basketball_api/auth.pysrc/basketball_api/routes/webhooks.pyBASKETBALL_STRIPE_API_KEY,BASKETBALL_STRIPE_WEBHOOK_SECRETstripePython package if not already in requirementsruff formatbefore committingstripe.billing_portal.Session.create()to generate a portal link for players to manage their payment methodChecklist
Related
westside-basketball— projectroutes/webhooks.pyhandlescheckout.session.completed