Checkout endpoint: GET /checkout/first-payment with Stripe redirect #368
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#368
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
Decomposed from
forgejo_admin/basketball-api#366. Ticket B of 3 — depends on A (migration, merged as PR #370). Independent of C (email).Repo
forgejo_admin/basketball-apiUser Story
As a parent who clicks the payment link in my email
I want to be redirected straight to Stripe's checkout page with my prorated amount
So that I can pay in one click and have my card saved for future monthly billing
Context
The email CTA links to
GET /checkout/first-payment?token={contract_token}. This endpoint validates the contract token, calculates the player's prorated fee fromPlayer.monthly_fee, creates a Stripe Checkout Session, and 302-redirects to Stripe's hosted page. No frontend intermediary needed.Critical: save card for recurring billing. The Checkout Session must include
payment_intent_data={"setup_future_usage": "off_session"}so Stripe saves the payment method to the Customer. This allows creating subscriptions on May 1 using the saved card — no second card entry needed.Proration formula (matches westside-contracts
+page.svelte:9):Existing patterns to follow:
checkout.pyStripe Customer create/reuse, Order creation,_SUCCESS_URL/_CANCEL_URLredirects. Existing webhook_handle_generic_order_completedhandles payment completion — no webhook changes needed.File Targets
Files to modify:
src/basketball_api/routes/checkout.py— addGET /checkout/first-paymentendpoint, add_calculate_prorated_cents()helper, add imports forContractStatus,Player,ProductCategory,RedirectResponseFiles to create:
tests/test_first_payment.py— endpoint testsFiles NOT to touch:
src/basketball_api/routes/webhooks.py— existing handler covers thissrc/basketball_api/services/email.py— that's ticket Csrc/basketball_api/routes/admin.py— that's ticket CAcceptance Criteria
GET /checkout/first-payment?token={valid_signed_token}returns 307 redirect to Stripe checkout URLmonthly_feedefaults to $200 (prorated $165)amount_cents,product_id,stripe_checkout_session_idplayer.stripe_customer_id)payment_intent_data={"setup_future_usage": "off_session"}to save card for future billingTest Expectations
setup_future_usagepassed to Stripe Checkout Session create callpytest tests/test_first_payment.py -vConstraints
contract_token(Player model, unique) notregistration_token(Parent model)@patch("basketball_api.routes.checkout.stripe"))RedirectResponse(url=..., status_code=307)for the redirectProductCategory.monthly+active=Trueconftest.pyprovidesclientfixture with DB override (no auth needed — token-based)payment_intent_data={"setup_future_usage": "off_session"}in Checkout SessionChecklist
Related
westside-basketball— projectforgejo_admin/basketball-api#366~/pal-e-platform/docs/superpowers/plans/2026-04-06-first-payment-email.md(Task 2)Scope Review: READY
Review note:
review-874-2026-04-06Ticket is fully scoped — all template sections present, file targets verified against codebase, traceability triangle complete (story:WS-S7 confirmed on project page), dependency on migration ticket A (#366) correctly documented. 8 acceptance criteria are specific and testable. Fits single agent pass (~3-4 min, 2 discrete changes).
Scope update (2026-04-06): Added
payment_intent_data={"setup_future_usage": "off_session"}requirement. Parents enter card once during prorated checkout; Stripe saves it for recurring monthly billing starting May 1. Also updated lineage — migration dependency (#367) merged as PR #370.