This page requires an active session with a
customerId. Users who navigate directly to /prices without completing registration will not have a customer ID in their session and the subscription action will fail.How plans are loaded
Plan data is fetched on every page load directly from the Stripe API — there is no local cache or database. This means any changes you make to your products and prices in the Stripe Dashboard are reflected immediately.app/services/payment.ts
expand: ["data.product"] option tells Stripe to inline the full product object on each price, so the UI can display the product name without a second API call.
Plan card contents
Each plan is displayed as a card showing:Product name
The name of the Stripe product associated with the price (e.g. “Pro Plan”, “Starter”).
Monthly price
The unit amount from the Stripe price object, formatted as a currency value per month.
Subscribe button
A form button that POSTs the selected
priceId to the page action.Subscription initiation flow
User clicks Subscribe
Each plan card contains a form with a hidden
priceId input. Clicking Subscribe submits that form via POST.Server retrieves session and creates subscription
The route action reads the
customerId from the session cookie and the priceId from the form body, then calls stripe.subscriptions.create.app/routes/prices.tsx
Stripe creates an incomplete subscription
The subscription is created with The
payment_behavior: "default_incomplete". This means Stripe holds the subscription in an incomplete state until payment is confirmed — the customer is not charged yet.app/services/payment.ts
expand: ["latest_invoice.payment_intent"] option retrieves the PaymentIntent client secret in the same call, which is required by Stripe Elements on the payment page.Stripe objects created
| Object | State | Notes |
|---|---|---|
Subscription | incomplete | Becomes active after payment is confirmed |
Invoice | open | The first invoice for the subscription |
PaymentIntent | requires_payment_method | Confirmed on the payment page |
Because
payment_behavior is set to default_incomplete, the subscription will remain in incomplete state indefinitely until the user completes payment. Stripe will eventually cancel incomplete subscriptions that are never paid (after 23 hours by default).