Skip to main content
Recurso is designed to be integrated against safely before a single real rupee or dollar moves. This page covers the three payment modes, how to confirm which one you’re in, how to simulate successes and failures, and how to test webhooks and dunning.

The three payment modes

Whether you’re moving real money is decided by your payment-gateway keys, not by your Recurso API key. Recurso surfaces the current mode as gateway_mode:
Your API key’s mode must match the server’s gateway_mode. A test key (rsk_test_…) is accepted on a none/test server and rejected on a live one (401 key_mode_mismatch); a live key (rsk_live_…) is the opposite. So a test key can never move real money — develop with one against a none/test instance. See Authentication.

Which mode am I in?

Ask the server. GET /version is unauthenticated and returns the mode:
If you see none, you’re fully sandboxed on the mock gateway. If you see live, stop and point your tests at a non-production instance. The dashboard shows a Test mode chip driven by this same value.

Sandbox mode (mock gateway)

Run Recurso with no STRIPE_SECRET_KEY or RAZORPAY_KEY_ID set and every payment path is served by the mock gateway. This is the fastest way to wire up your integration — create plans, subscribe customers, generate invoices, and drive the full lifecycle without any provider account. What the mock gateway does:

Orders & checkout

CreateOrder returns a mock order_… id. Checkout verification succeeds.

Mandates (UPI)

CreateMandate returns a mock token and a mock authorization URL; mandate debits succeed.

Refunds

Refunds return status processed immediately.

Retries

Retry attempts succeed ~40% of the time with random decline codes — see Testing dunning.

Forcing a payment failure

To exercise your failure handling on the checkout verify path, pass the literal signature fail — the mock gateway will reject it:
Any other signature verifies successfully, so you can toggle between the happy and unhappy paths deterministically.

Gateway test mode

When you’re ready to test real gateway behavior (3-D Secure, actual UPI collect requests, provider webhooks), configure test keys and the mode flips to test:
Then pay with the providers’ documented test instruments:
Any future expiry and any CVC. Full list in Stripe’s testing docs.
Because routing is currency-based (INR → Razorpay, else → Stripe), test an INR invoice to hit Razorpay and a USD invoice to hit Stripe.

Testing webhooks

Two directions of webhooks are worth testing: Inbound (gateway → Recurso). Stripe and Razorpay call back to /webhooks/stripe and /webhooks/razorpay; Recurso verifies the signature on every event. To receive them on a local instance, use the Stripe CLI (stripe listen --forward-to localhost:8080/webhooks/stripe) or a tunnel such as ngrok for Razorpay, and set the matching webhook secret in your env. Outbound (Recurso → your app). Register an endpoint, then use the delivery log to inspect and replay:
See Webhooks for signature verification code and the full event catalog.

Testing dunning and retries

On the mock gateway, retry attempts succeed ~40% of the time and otherwise return a random decline code (insufficient_funds, card_expired, card_declined, processing_error). That’s enough randomness to watch the smart retry engine schedule attempts, escalate a dunning campaign, and eventually mark an invoice recovered or exhausted. Create a subscription, let an invoice go past due, and observe the retry schedule and recovered-revenue attribution. See Smart Retry for how the bandit picks retry timing.

Idempotency in tests

Every mutating request accepts an Idempotency-Key header. Reusing the same key returns the original result instead of creating a duplicate — so a test that retries a flaky network call won’t double-charge or double-create:
Give each logical test operation a stable key. See Idempotency.

Loading and resetting test data

The demo dataset gives you a tenant with plans, customers, subscriptions, and invoices to poke at:
make seed wipes all data in the target database before loading — only run it against a development database.
After seeding, sign in to the dashboard and use the API with the demo key sk_test_12345. Emails the system “sends” land in Mailhog at http://localhost:8025 instead of real inboxes.

A pre-launch testing checklist

1

Sandbox the happy path

On gateway_mode: none, drive plan → customer → subscription → invoice → payment end-to-end.
2

Exercise failures

Force a verify failure (signature: fail), let a retry cycle run, and confirm your app handles declines and past-due states.
3

Switch to gateway test mode

Add sk_test/rzp_test keys and repeat with real test cards and UPI, in both INR and a non-INR currency.
4

Verify webhooks both ways

Confirm inbound gateway events are accepted and your outbound endpoint receives and can replay events.
5

Go live deliberately

Only then follow Going to Production to switch to live keys.