Skip to Content
SimulatorsStripeHosted Checkout and customer portal

Simulators

Hosted Checkout and customer portal

Hosted pages let a browser test finish the flows Stripe hosts: a Checkout Session’s url and a customer portal session’s url open the simulator’s own pages, on the origin you called, instead of Stripe’s. Make a subscription Checkout Session and open its page:

curl -s "$RYSTIC_URL/v1/prices" -u sk_test_123: -d unit_amount=1500 -d currency=usd -d 'recurring[interval]=month' -d 'product_data[name]=Hosting' | jq -c '{id}'
# -> {"id":"price_1"}
curl -s "$RYSTIC_URL/v1/checkout/sessions" -u sk_test_123: -d mode=subscription -d 'line_items[0][price]=price_1' -d 'line_items[0][quantity]=2' -d success_url=https://example.com/done -d cancel_url=https://example.com/cancel | jq -r .url
# -> .../c/pay/cs_test_a1Rystic0000000000000001#fidRystic1
curl -s -o /dev/null -w '%{http_code}\n' "$RYSTIC_URL/c/pay/cs_test_a1Rystic0000000000000001"
# -> 200

Checkout

A Checkout Session’s url is re-based from checkout.stripe.com onto the origin you called, and /c/pay/{session} renders the session’s line items, total and prefilled email, with a link back to cancel_url. Subscribing on a subscription-mode session creates the customer (unless the session names one) and a subscription carrying the session’s prices, quantities and subscription_data[metadata], completes the session and redirects to success_url with {CHECKOUT_SESSION_ID} filled in. customer.created, customer.subscription.created and checkout.session.completed go out in that order. Any card number is accepted, and the session’s amounts and payment_status read as its line items price them. Submitting a payment or setup session answers 501 and leaves it open. An expired session’s page answers 410 and an unknown one’s 404.

Expiry

A Checkout Session expires at its expires_at, 24 hours after its create unless the create sets it: once the clock reaches it, an open session reads expired with a null url, and checkout.session.expired goes out, naming no request. A clock move stops at each open session’s expires_at on its way, so the event’s created is that expires_at; a session the running clock passes between requests expires at the next request, stamped then. checkout.session.expired is the platform’s event even for a session made acting as a connected account.

The create takes an expires_at from 30 minutes to 24 hours after the clock, both ends included. It refuses any other integer with Stripe’s 400, and a value that is no integer (abc, 1e9, 1800.0, an empty or space-prefixed value) with Stripe’s 400 parameter_invalid_integer, quoting the value as sent. A seeded session whose expires_at is missing, null, zero, negative or no timestamp reads and expires a day after its created.

Customer portal

A portal session’s url is re-based from billing.stripe.com onto the origin you called and opens the simulator’s portal at /p/session?secret=…. It lists the customer’s subscriptions with their seats and renewal date, and offers a seat change, a cancellation and a way to take a cancellation back. Canceling sets cancel_at to the period end and leaves cancel_at_period_end false, as Stripe’s portal does. Each action is a subscription update that sends customer.subscription.updated to your endpoints; once the clock reaches cancel_at, the subscription reads canceled and customer.subscription.deleted goes out. The portal shows no payment method, billing details or invoice history, and an unknown secret answers 404.

Other hosted URLs

A finalized invoice’s hosted_invoice_url and invoice_pdf, a credit note’s pdf, a charge’s receipt_url, a payment link’s url and a bank debit’s hosted_verification_url are on the origin you called, never on invoice.stripe.com, pay.stripe.com or buy.stripe.com. The simulator serves no page at those paths: they answer 404.

Last updated on