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.

SandboxProduction
Stripe modeTest modeLive mode
Stripe restricted keyrk_test_…rk_live_…
Webhook URLThe Sandbox connection's, registered in test modeThe Production connection's, registered in live mode
PricesTest pricesLive prices, with different price_… IDs
CashSDK server keycsk_rk_test_…csk_rk_live_…
OriginsSandbox origins, loopback HTTP allowedProduction origins, HTTPS and chrome-extension:// only
Client environment"sandbox", or .sandbox in Swift"production", or .production in Swift
Publishable keyThe 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#

1
Create a live restricted key

In Stripe, switch to live mode and open Developers → API keys → Create restricted key. Give it the same permissions as your test key:

PermissionAccess
Checkout Sessions, Customers, Customer portalWrite
Prices, Products, Subscriptions, Invoices, Charges, Refunds, Payment Intents, Disputes, EventsRead

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.

2
Add it as the Production connection

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.

3
Register the Production webhook in live mode

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:

text
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.

4
Bring in what Stripe already holds

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 a key in 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:

ts
// 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.

  1. The return page shows the access within seconds. waitForEntitlement resolves with active: true.
  2. Your server's getEntitlements(customerId) with the live key returns the entitlement.
  3. In the dashboard, Customers shows the customer with the entitlement, and Transactions shows the payment.
  4. The Production connection under Your App → Marketplace is active and shows no error. Through MCP, get_marketplace_status with provider: "stripe" returns its last event, such as invoice.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.