Payment testing
Test payments and webhooks in local development
This guide covers how to test payments in local development, including test-mode setup and webhook debugging for each provider.
Before you start: Finish the base setup in Payment configuration.
Test environment
Use test credentials in development.
Stripe test mode
# Test keys
STRIPE_SECRET_KEY=sk_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
STRIPE_PUBLIC_KEY=pk_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
STRIPE_WEBHOOK_SECRET=whsec_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxTest cards:
- Successful payment:
4242424242424242 - Declined payment:
4000000000000002 - Authentication required:
4000002500003155
WeChat Pay
WeChat Pay has no sandbox. Charge a tiny real amount, such as ¥0.01 per order. Amounts in config/payment.ts are yuan. The provider multiplies by 100 before it calls WeChat.
Creem test mode
# Test API (this is also the default when CREEM_SERVER_URL is unset)
CREEM_SERVER_URL=https://test-api.creem.io
CREEM_API_KEY=creem_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxDodo Payments test mode
DODO_PAYMENTS_API_KEY=TSxxxxxxxxxx
DODO_PAYMENTS_WEBHOOK_KEY=whsec_xxxxxxxxxx
DODO_PAYMENTS_TEST_MODE=trueTest cards:
- Successful payment:
4242424242424242 - Declined payment:
4000000000000002 - Expiry: any future date (for example
06/32) - CVC: any three digits (for example
123) - Cardholder name: any English name. Digits are rejected.
Local webhook testing
Local development: Use the methods below to receive webhooks. You do not need a production webhook endpoint first.
WeChat Pay, Creem, and Dodo Payments need a public URL. Map your local server to one. Stripe can stay fully local with the Stripe CLI.
| Provider | Recommended approach |
|---|---|
| Stripe | Stripe CLI (no tunnel) |
| WeChat Pay | ngrok / Cloudflare Tunnel |
| Creem | ngrok / Cloudflare Tunnel |
| Dodo Payments | ngrok / Cloudflare Tunnel |
Tunnel tools
WeChat Pay, Creem, and Dodo Payments need a tunnel:
# Public tunnel to the local app (port 7001)
npx ngrok http 7001Point each provider at the tunnel:
- WeChat Pay:
https://abc123.ngrok.io/api/payment/webhook/wechat - Creem:
https://abc123.ngrok.io/api/payment/webhook/creem - Dodo Payments:
https://abc123.ngrok.io/api/payment/webhook/dodo
Stripe webhooks
Stripe supports two local webhook setups.
Option 1: Stripe CLI (recommended)
Docs: https://docs.stripe.com/stripe-cli
# 1. Install the Stripe CLI
# macOS
brew install stripe/stripe-cli/stripe
# Windows
scoop bucket add stripe https://github.com/stripe/scoop-stripe-cli.git
scoop install stripe
# 2. Log in
stripe login
# 3. Forward webhooks to the local app
stripe listen --forward-to localhost:7001/api/payment/webhook/stripe
# 4. Copy the signing secret the CLI prints into STRIPE_WEBHOOK_SECRET
# Example: whsec_1234567890abcdef...What you get:
- The whole flow stays on your machine
- You receive real webhook events as they happen
- Signature verification is handled for you
- Event logs are available in the CLI
Option 2: ngrok and the Stripe Dashboard
Use this when the Stripe CLI is unavailable:
# 1. Start a tunnel
ngrok http 7001
# 2. Copy the https URL ngrok prints
# Example: https://abc123.ngrok.io
# 3. In the Stripe Dashboard, add a webhook endpoint:
# https://abc123.ngrok.io/api/payment/webhook/stripe
# Events:
# - checkout.session.completed
# - customer.subscription.updated
# - customer.subscription.deletedSet the signing secret:
# .env.local
STRIPE_WEBHOOK_SECRET=whsec_your_webhook_secret_hereCheck that events arrive
1. Trigger a basic event
# Watch webhook events (Stripe CLI)
stripe listen --forward-to localhost:7001/api/payment/webhook/stripe --events checkout.session.completed,customer.subscription.updated
# Send built-in test events
stripe trigger checkout.session.completed
stripe trigger customer.subscription.updated
stripe trigger customer.subscription.deleted2. What stripe trigger does
stripe trigger sends fixed fixture data. You cannot customize the payload, and it still runs the real webhook path:
# List triggers
stripe trigger --help
# Subscription-related events
stripe trigger checkout.session.completed # Checkout completed
stripe trigger invoice.payment_succeeded # Invoice paid
stripe trigger customer.subscription.created # Subscription createdThe fixture:
- Matches the shape of a real event
- Includes the required fields and relations
- Uses fixed test values
- Does not let you set a specific user ID or order ID
3. Test with your own data
Stripe Dashboard
# 1. Create a real Checkout Session in the Stripe Dashboard (test mode)
# 2. Pay with 4242 4242 4242 4242
# 3. Watch the webhook your local app receivesSend a custom webhook yourself
curl -X POST http://localhost:7001/api/payment/webhook/stripe \
-H "Content-Type: application/json" \
-H "Stripe-Signature: YOUR_TEST_SIGNATURE" \
-d '{
"id": "evt_test_webhook",
"object": "event",
"type": "checkout.session.completed",
"data": {
"object": {
"id": "cs_test_custom_session_id",
"mode": "subscription",
"customer": "cus_test_customer",
"metadata": {
"planId": "your_plan_id",
"userId": "your_user_id",
"orderId": "your_order_id"
}
}
}
}'Recommended test flow
# 1. Forward Stripe webhooks
stripe listen --forward-to localhost:7001/api/payment/webhook/stripe
# 2. Start the app
pnpm run dev
# 3. Open pricing and pay with a test card
open http://localhost:7001/pricing
# 4. Watch the server logs and confirm the webhook handler ranTest cards
Stripe
| Card number | Scenario |
|---|---|
4242 4242 4242 4242 | Successful payment |
4000 0000 0000 0002 | Declined |
4000 0000 0000 9995 | Insufficient funds |
4000 0025 0000 3155 | 3D Secure required |
WeChat Pay
- Pay with a real WeChat scan
- Use ¥0.01
- Refund the charge after the test
Creem
- Use the test cards Creem provides
- See the Creem docs
Dodo Payments
| Card number | Scenario |
|---|---|
4242 4242 4242 4242 | Successful payment |
4000 0000 0000 0002 | Declined |
- Expiry: any future date (for example
06/32) - CVC: any three digits (for example
123) - Cardholder name: any English name (no digits)
- See the Dodo Payments docs
Related docs:
- Payment configuration — providers and plans
- Dodo Payments — Dodo setup
- Credits — credit packs and consumption