Go live with Stripe
Move a web app, Chrome extension, direct Mac app or Stripe-only backend from Sandbox to Production: live Stripe connection, live prices, origins, keys and a first live sale.
You build against Sandbox: Stripe's test mode, a test server key and sandbox origins. Going live adds the Production side next to it. Sandbox keeps working, so you can still test after launch. This page applies to the web SDK, Chrome extensions, direct Mac apps and Stripe-only backends.
| Sandbox | Production | |
|---|---|---|
| Stripe mode | Test mode | Live mode |
| Stripe restricted key | rk_test_… | rk_live_… |
| Webhook URL | The Sandbox connection's, registered in test mode | The Production connection's, registered in live mode |
| Prices | Test prices | Live prices, with different price_… IDs |
| CashSDK server key | csk_rk_test_… | csk_rk_live_… |
| Origins | Sandbox origins, loopback HTTP allowed | Production origins, HTTPS and chrome-extension:// only |
Client environment | "sandbox", or .sandbox in Swift | "production", or .production in Swift |
| Publishable key | The app's one csk_pk_… | The same key |
Do the steps in order. A Stripe-only backend skips the origins and the client steps.
Connect Stripe in Production#
In Stripe, switch to live mode and open Developers → API keys → Create restricted key. Give it the same permissions as your test key:
| Permission | Access |
|---|---|
| Checkout Sessions, Customers, Customer portal | Write |
| Prices, Products, Subscriptions, Invoices, Charges, Refunds, Payment Intents, Disputes, Events | Read |
All of them are required; only Account read is optional. Without read access to Subscriptions, subscription events cannot be applied and are set aside after their retries.
In CashSDK, open Your App → Marketplace → Stripe → Connect, choose Production and paste
the rk_live_… key. Leave the signing secret empty for now. The Sandbox connection stays as it
is. CashSDK refuses a test key for Production, and a live key for Sandbox, with
environment_mismatch.
Copy the webhook URL of the Production connection. It is not the Sandbox URL. In Stripe's live mode, open Developers → Webhooks, add an endpoint with that URL and select these events:
customer.subscription.created
customer.subscription.updated
customer.subscription.deleted
customer.subscription.paused
customer.subscription.resumed
invoice.paid
invoice.payment_failed
invoice.voided
invoice.marked_uncollectible
charge.refunded
charge.dispute.funds_withdrawn
charge.dispute.funds_reinstated
charge.dispute.closed
checkout.session.completed
checkout.session.expired
checkout.session.async_payment_succeeded
checkout.session.async_payment_failed
Back in CashSDK, click Reconnect on the Production connection, paste the live key and the
live endpoint's signing secret (whsec_…), then click Activate.
If your live Stripe account already has customers, click Sync from Stripe on the Production connection so their subscriptions and payments count. It reads everything in live mode. What happened before activation is recorded as history and sends no webhooks to your server. See Sync your existing Stripe data.
To rotate the live key or the signing secret later, reconnect with the new value. An active
connection stays active; there is nothing to activate again. Pausing the Production connection
holds Stripe's events and applies them when you activate it again. A disconnected connection
answers Stripe with 410 Gone, so remove its endpoint in Stripe. More in
How CashSDK handles Stripe events.
Import the live prices#
Live and test prices have different IDs, so the live prices become products of their own. Open
Your App → Products → Import from Stripe, set Import from to Production, pick the live
prices, and choose the same entitlement the test prices unlock, for example pro. Customers then
get the same access whichever environment they paid in.
Products, offerings and entitlements are shared by both environments. A package holds one product,
so add each live product to the current offering as a package of its own, next to the test one.
CashSDK records which environment each imported price came from. The offering a customer reads in
Sandbox leaves out the live packages, and the one read in Production leaves out the test packages,
so a page that lists the current offering's packages shows each environment only its own. Package
identifiers are unique within an offering: name them, for example, monthly for the live price
and monthly_sandbox for the test price. A checkout for a package of the other environment is
refused with unsupported_checkout.
A Stripe-only backend that keeps its own Checkout does not need an offering. It needs the live products, mapped to the entitlement.
Allow your production origins#
On Your App → Web and Stripe, under Production origins:
- Web: your site's exact HTTPS origin, such as
https://app.example.com. - Chrome extension:
chrome-extension://followed by the Chrome Web Store build's ID, which differs from the unpacked build's unless you pin it with akeyin the manifest. Add the HTTPS origin of your hosted checkout return page too. - Mac app: the HTTPS origin of your hosted return page.
Tick Allow browser sessions and save. Production does not accept loopback HTTP. Changing these origins later ends every open session in Production, so customers' clients ask for a new one.
Create a live server key#
In Developers → API keys, on the live tab, create a restricted key (csk_rk_live_…) with
Create customer web sessions and Customers. A Stripe-only backend only needs
Customers. A live key reads and writes Production only: a test key cannot reach live data, and
the reverse.
Store it on your server next to the test key, for example as CASHSDK_SECRET_KEY in your
production environment.
Switch your code to production#
The publishable key does not change. The environment does, everywhere at once:
// Your backend
const cashsdkServer = new CashSDKServer({
secretKey: process.env.CASHSDK_SECRET_KEY!, // csk_rk_live_…
environment: "production",
});
// The browser, or createExtensionCashSDK in an extension
const cashsdk = createCashSDK({ publishableKey: "csk_pk_…", environment: "production", getSessionToken });
In a Mac app, create CashSDKCommerceClient with environment: .production. The server
constructor refuses a key whose mode contradicts environment, and the clients refuse a session
from the other environment.
A Stripe-only backend reads GET /v1/customers/{userId}/entitlements with the live key. The key's
mode picks Production, so leave out environment, or pass environment=Production.
Switch the app to Production#
In the dashboard, click Exit sandbox in the sandbox bar, or switch the environment in the sidebar. The app's dashboard then shows real customers, revenue and events instead of test data. It needs the admin role.
If you sell with checkout started by the SDK, decide whether Production takes promotion codes or uses Stripe Tax: both are off in each environment until you turn them on, and Sandbox settings do not carry over. See Checkout settings. Save the customer portal settings in Stripe's live mode too, or the portal refuses to open.
Verify the first live purchase and refund#
Sign in as an account of your own and buy your cheapest plan with a real card.
- The return page shows the access within seconds.
waitForEntitlementresolves withactive: true. - Your server's
getEntitlements(customerId)with the live key returns the entitlement. - In the dashboard, Customers shows the customer with the entitlement, and Transactions shows the payment.
- The Production connection under Your App → Marketplace is active and shows no error.
Through MCP,
get_marketplace_statuswithprovider: "stripe"returns its last event, such asinvoice.paid.
Then refund the payment in full from Stripe's live dashboard. charge.refunded arrives, and the
access ends within seconds. Check your server's getEntitlements again: the entitlement is gone.
A partial refund only adjusts revenue and leaves access.
If access does not arrive, check that the webhook endpoint is the Production connection's URL, in
live mode, and that the customer is named: cashsdk_user_id in the subscription's metadata for
your own Checkout, or the checkout CashSDK started. A customer that no event names waits on
Web and Stripe → Customer identity until you
link it.
After launch#
Keep testing in Sandbox. The Sandbox connection, the test prices and the sandbox origins keep working, and Sandbox purchases never grant Production access.