Test & Live Modes
Unchurn operates in one of two modes: 'test' or 'live'. Mode controls which Stripe account your cancel-flow sessions run against and is set at session creation. The backend echoes back the confirmed mode — the widget always renders based on the server-confirmed value, not the caller-supplied one.
Overview
| Mode | Stripe account | Real charges | Widget badge |
|---|---|---|---|
live (default) | Your Stripe live account | Yes | None |
test | Your Stripe test account | No | Visible “TEST” badge |
Use test mode during development and in staging environments. Use live mode in production.
Setting the mode
Via showCancelFlow
Pass mode directly to showCancelFlow:
import { showCancelFlow } from '@unchurn.dev/widget'
showCancelFlow({
merchantId: 'mch_YOUR_MERCHANT_ID',
subscriptionId: 'sub_test_STRIPE_SUB_ID',
mode: 'test',
})When mode is omitted it defaults to 'live'.
Via createUnchurnHandler / resolveUser
When you use the Next.js server helper, return mode from resolveUser:
// app/api/unchurn/token/route.ts
import { createUnchurnHandler } from '@unchurn.dev/widget/nextjs'
import { getCurrentUser } from '@/lib/auth'
export const POST = createUnchurnHandler({
secret: process.env.UNCHURN_SECRET!,
merchantId: process.env.UNCHURN_MERCHANT_ID!,
resolveUser: async (req) => {
const user = await getCurrentUser(req)
if (!user) return null
return {
subscriptionId: user.stripeSubscriptionId,
mode: process.env.NODE_ENV === 'production' ? 'live' : 'test',
}
},
})The mode is embedded in the signed auth token. useUnchurn reads it from the token endpoint response — you do not need to pass mode to showCancelFlow separately when using the hook.
Via useUnchurn
When you use the useUnchurn React hook, mode comes from your token endpoint. The hook passes it through automatically. There is nothing extra to configure on the client.
'use client'
import { useUnchurn } from '@unchurn.dev/widget/react'
export function SubscriptionManagement() {
// mode is derived from the server token — no client config needed
const { show, isReady } = useUnchurn({
tokenEndpoint: '/api/unchurn/token',
})
return (
<button onClick={show} disabled={!isReady}>
Cancel subscription
</button>
)
}Env-driven mode selection
The recommended pattern is to derive mode from NODE_ENV so your staging and local environments always use test mode without manual intervention:
const mode = process.env.NODE_ENV === 'production' ? 'live' : 'test'Use this in resolveUser (as shown above) or inline in a showCancelFlow call for non-Next.js stacks:
showCancelFlow({
merchantId: process.env.UNCHURN_MERCHANT_ID,
subscriptionId: currentUser.stripeSubscriptionId,
mode: process.env.NODE_ENV === 'production' ? 'live' : 'test',
})What changes in test mode
- TEST badge — a visible “TEST” label appears in the widget dialog header so end users (and your team) can immediately recognize a rehearsal session.
- Stripe test account — all subscription operations (cancellations, pauses, coupon applications, trial extensions) run against your Stripe test account. No real charges are made.
- Test Stripe IDs — use test subscription IDs (e.g.
sub_test_...) and test customer IDs from your Stripe test dashboard. - Full flow parity — every cancel-flow path (discount, pause, trial extension, cancellation confirmation) behaves identically in test mode. Offers configured in your dashboard are served in both modes.
Rate limiting
Test-mode traffic and live-mode traffic are tracked in separate rate limit buckets. Running a load test or integration test suite in test mode cannot exhaust the budget for live-mode sessions. Each mode’s limits are evaluated independently.
Common mistake: mismatched Stripe key
If you pass mode: 'test' but your Unchurn dashboard has only a Stripe live key configured (or vice versa), the session will fail when Unchurn attempts to look up the subscription. Confirm that your dashboard has the correct Stripe key for each mode:
- Dashboard > Settings > Stripe — verify both a live key and a test key are connected if you intend to use both modes.
- Test subscription IDs (beginning
sub_test_) will only resolve against a connected test key. - Live subscription IDs will only resolve against a connected live key.
Passing a test subscription ID with mode: 'live' (or a live ID with mode: 'test') will result in a session error.