Guide

How to Test a Stripe Cancellation Flow Locally

Shipping a cancel button without exercising the full path — API call, subscription fields, webhook handler, and access revocation — is how teams discover production bugs on the first real cancellation. Local testing does not require charging real cards. Stripe test mode, the Stripe CLI, and deliberate fixture subscriptions are enough to validate both immediate and end-of-period cancels.

This guide covers practical local and staging checks for voluntary cancellation. It complements how to build a Stripe cancellation flow (implementation) and Stripe cancellation webhooks (event handling). Scope is voluntary cancel only — not dunning or card-decline recovery.

Last updated October 13, 2026

Prerequisites

  • Stripe account with test mode API keys (publishable + secret) — see Stripe testing
  • Stripe CLI installed and logged in — Stripe CLI
  • A test Customer with an active test-mode Subscription (create via Dashboard or API)
  • Webhook endpoint in your app (even a minimal route) that verifies signatures

Test mode vs live mode

Never point local cancel experiments at live keys. Test mode objects are isolated; webhook endpoints should use a test-mode signing secret from the Dashboard or from stripe listen.

Use Stripe test card numbers when you need a paid subscription that invoices successfully. For cancel-only tests, a trialing or active subscription created in the Dashboard is often faster than running checkout.

Forward webhooks to localhost with the CLI

The CLI opens a tunnel and delivers Stripe events to your machine. Per stripe listen, run listen against your local URL before you trigger cancels.

# Terminal 1 — your app
npm run dev

# Terminal 2 — forward webhooks (use the printed whsec_ secret in your env)
stripe listen --forward-to localhost:3000/api/stripe/webhook

Replace the path with your real webhook route. Update your local STRIPE_WEBHOOK_SECRET to the secret printed by listen, not your Dashboard endpoint secret, while forwarding.

Test API cancellation paths

Cancel at period end

Call Subscriptions update with cancel_at_period_end: true (or the equivalent for your billing mode). Confirm:

  • API response shows scheduled cancel fields
  • customer.subscription.updated arrives at your webhook handler
  • Your app still grants access according to policy
stripe subscriptions update sub_XXXXX \
  -d cancel_at_period_end=true

Cancel immediately

Use the cancel endpoint documented in cancel subscriptions. Confirm customer.subscription.deleted (and that your handler revokes access).

stripe subscriptions cancel sub_XXXXX

When to use stripe trigger

stripe trigger emits sample events without going through your product UI. Useful to unit-test handler branching, but it does not replace an end-to-end cancel:

  • Trigger teaches your parser and idempotency layer
  • API or portal cancel tests teach your business logic and field mapping
stripe trigger customer.subscription.updated
stripe trigger customer.subscription.deleted

Smoke-test Customer Portal cancellation

If production uses the portal path, run the same path in test mode. Create a portal session per integrate the customer portal, open the session URL locally, and cancel. You should see the same webhook sequence as API cancels, plus portal-collected cancellation_details when configured.

Portal setup details: configure Stripe Customer Portal cancellation.

Simulate period end without waiting

Scheduled cancels are painful to test if you must wait a month. Test clocks advance simulated time for test-mode customers so cancel_at and renewal boundaries fire quickly. Use them when your access rules depend on current_period_end.

Local cancellation test checklist

CheckPass criteria
Scheduled cancel APIupdated webhook; cancel fields set; access per policy
Immediate cancel APIdeleted webhook; access revoked
Duplicate webhookSecond delivery does not duplicate side effects
Portal cancel (if used)Reason fields appear when configured
Wrong signatureHandler returns 4xx and does not mutate state

Testing flows before you build everything custom?

ChurnIntel embeds a voluntary cancellation experience in your app and applies retention actions in Stripe — useful when you want to validate cancel UX without writing the full widget first.