Checkout & Portal
Stripe Checkout session creation and customer portal integration.
Checkout Flow
1. Create Checkout Session
POST /api/subscriptions/checkout/{
"price_id": "price_1234567890",
"workspace_id": "ws-uuid"
}The request is refused with 400 unless:
- the price is an active price of a plan on sale ("That plan isn't on sale."),
- the workspace is one the user owns or administers ("Choose a workspace you own or administer."), and
- the workspace has no subscription yet. To change plan, use the billing portal ("This workspace already has a subscription. Change its plan from the billing portal.").
Then the server:
- Gets or creates a
StripeUserfor the authenticated user, and its Stripe customer - Creates a Stripe Checkout Session with the price, naming the workspace in its metadata
- Returns the session's ID and Stripe's URL for it
{
"session_id": "cs_live_...",
"url": "https://checkout.stripe.com/c/pay/cs_live_..."
}2. Redirect to Stripe
The frontend sends the customer to the URL; no Stripe.js is needed:
window.location.assign(data.url);3. Post-Checkout
Stripe returns the customer to the workspace's billing page on the frontend: dashboard/workspaces/{workspace_id}/settings/billing?checkout=success, or ?checkout=canceled if they back out.
Stripe fires webhook events that create the subscription locally and attach it to the workspace named at checkout (see Webhooks). The billing page checks GET /api/workspaces/{id}/plan/ until the subscription appears.
Checkout Parameters
The checkout session is configured with:
{
"customer": customer_id,
"success_url": f"{FRONT_END_BASE_URL}/dashboard/workspaces/{workspace_id}/settings/billing?checkout=success&session={{CHECKOUT_SESSION_ID}}",
"cancel_url": f"{FRONT_END_BASE_URL}/dashboard/workspaces/{workspace_id}/settings/billing?checkout=canceled",
"payment_method_types": ["card"],
"mode": "subscription",
"line_items": [{"price": price_id, "quantity": 1}],
"subscription_data": {
"trial_end": trial_end_timestamp,
"metadata": {"workspace_id": workspace_id}
},
"metadata": {"workspace_id": workspace_id},
"allow_promotion_codes": True
}Free Trial
If NEW_USER_FREE_TRIAL_DAYS is set (default: 7), a customer's first subscription includes a trial period:
trial_end = now() + timedelta(days=NEW_USER_FREE_TRIAL_DAYS + 1)The extra day accounts for timezone rounding. Stripe requires the trial end to be at least 48 hours in the future.
Set NEW_USER_FREE_TRIAL_DAYS = None to disable trials.
During the trial, the workspace gets the plan's limits but only $1 of AI credit (TRIAL_AI_CREDIT). The plan's full monthly credit starts when the trial converts.
Customer Portal
The Stripe billing portal lets users manage their subscription without custom UI:
- View current subscription
- Change plan (upgrade/downgrade)
- Update payment method
- View billing history
- Cancel subscription
Get Portal URL
POST /api/subscriptions/customer-portal/{ "workspace_id": "ws-uuid" }{
"url": "https://billing.stripe.com/p/session/..."
}The portal belongs to a Stripe customer: the person who pays. Given a workspace_id, it opens only for whoever pays for that workspace's subscription, and returns to that workspace's billing page. Anyone else gets 403 with who manages billing ("Billing for this workspace is managed by …"); a workspace with no subscription gets 400. Without workspace_id, it opens the caller's own customer and returns to {FRONT_END_BASE_URL}dashboard/.
Configuration
sync_stripe_plans sets the portal's default configuration to offer exactly the plans and prices in plans.py, so customers can switch between them there. Everything else (cancellation policy, proration, branding) is set in the Stripe Dashboard.
Available Prices Endpoint
GET /api/subscriptions/subscribable-product/Returns every active price of every plan on sale, for the pricing page. No authentication needed. A plan someone already pays for in one workspace is still listed, since they can buy it for another.
Response:
[
{
"price_id": "price_123",
"product_id": "prod_abc",
"name": "Pro",
"price": 2000,
"freq": "month_1",
"avail": true,
"currency": "usd",
"nickname": "Pro monthly",
"services": []
}
]freq is month_1 or year_1.
Configuration
| Setting | Default | Description |
|---|---|---|
STRIPE_API_SECRET | — | Stripe secret key |
FRONT_END_BASE_URL | — | Frontend URL for redirects |
NEW_USER_FREE_TRIAL_DAYS | 7 | Trial period length |
DEFAULT_PAYMENT_METHOD_TYPES | ["card"] | Accepted payment methods |
DEFAULT_CHECKOUT_MODE | "subscription" | Checkout mode |
ALLOW_PROMOTION_CODES | true | Enable promo codes in checkout |
CHECKOUT_SUCCESS_URL_PATH | "dashboard/workspaces/{workspace_id}/settings/billing" | Where checkout returns to; ?checkout=success is added |
CHECKOUT_CANCEL_URL_PATH | "dashboard/workspaces/{workspace_id}/settings/billing" | Where cancelled checkout returns to; ?checkout=canceled is added |