Sandbox & Testing
The sandbox is a full copy of production — same endpoints, same objects, same failure modes — with no real money involved. If it works here, it works live.
Getting sandbox access
Sandbox keys are issued the moment you create an account — before underwriting, before any commercial conversation. You can build the entire integration while your application is still being reviewed.
- Sandbox keys start
sk_test_andpk_test_. - Point at
https://api.sandbox.bearmerchant.com. - Nothing here touches a card network or a bank. No money moves, ever.
- Data is yours to reset — wipe the whole sandbox from the dashboard when it gets messy.
401, so a wrong environment variable fails immediately rather than quietly charging a real card.Test cards
Each number produces a deterministic outcome. Use any future expiry date and any three-digit CVC (four for Amex).
Successful payments
| Number | Brand | Behaviour |
|---|---|---|
4242 4242 4242 4242 | Visa | Approves |
5555 5555 5555 4444 | Mastercard | Approves |
3782 822463 10005 | American Express | Approves |
6011 1111 1111 1117 | Discover | Approves |
4000 0025 0000 3155 | Visa | Approves, but requires 3-D Secure first |
Declines
| Number | Decline code | Should you retry? |
|---|---|---|
4000 0000 0000 0002 | card_declined | No — generic issuer decline |
4000 0000 0000 9995 | insufficient_funds | Yes — worth retrying in a few days |
4000 0000 0000 0069 | expired_card | No — ask for a new card |
4000 0000 0000 0127 | incorrect_cvc | No — re-collect the CVC |
4100 0000 0000 0019 | fraudulent | No — never retry a suspected-fraud decline |
4000 0000 0000 0119 | processing_error | Yes — transient, retry immediately |
Simulating harder scenarios
The interesting failures are the ones that arrive days later. The sandbox can produce all of them on demand rather than making you wait.
Disputes
| Number | Produces |
|---|---|
4000 0000 0000 0259 | A chargeback roughly a minute after capture |
4000 0000 0000 2685 | A pre-dispute alert you can resolve by refunding |
4000 0000 0000 1976 | An enquiry that escalates to a chargeback if ignored |
ACH
| Routing / account | Produces |
|---|---|
110000000 / 000123456789 | Clears normally |
110000000 / 000111111116 | Returns R01 — insufficient funds |
110000000 / 000222222227 | Returns R02 — account closed |
110000000 / 000333333338 | Returns R10 — customer disputes the debit |
Controlling time
Subscription and settlement logic is hard to test when a billing cycle is a month long. The sandbox clock can be moved forward so a year of renewals takes a few seconds.
# Create a test clock and attach a subscription to it
curl https://api.sandbox.bearmerchant.com/v1/test_clocks \
-u sk_test_…: \
-d frozen_time=2026-01-01T00:00:00Z
# Jump forward two months — renewals, dunning and payouts all fire
curl https://api.sandbox.bearmerchant.com/v1/test_clocks/clk_9Fq2/advance \
-u sk_test_…: \
-d frozen_time=2026-03-01T00:00:00Z
Testing webhooks locally
Your development machine is not reachable from the internet, so the CLI opens a tunnel and forwards sandbox events to whatever port you are running on.
# Install
npm install -g @bearmerchant/cli
bm login
# Forward every sandbox event to your local handler
bm listen --forward-to http://localhost:3000/hooks/bearmerchant
# In another terminal, fire events without making payments
bm trigger charge.succeeded
bm trigger subscription.payment_failed
bm trigger dispute.created
bm listen prints a signing secret for the session. Use it as your BM_WEBHOOK_SECRET locally so you are exercising real signature verification, not skipping it.How the sandbox differs
Behaviour is identical in every way that matters to your code. These are the only differences worth knowing about:
| Area | Sandbox behaviour |
|---|---|
| Money | Nothing is ever charged or settled. Balances are simulated. |
| Card networks | Outcomes come from the test-card table, not a real issuer. |
| Payout timing | Settles instantly rather than on a banking schedule. |
| Rate limits | Lower than production, to encourage sensible client behaviour. |
| Data retention | Objects are kept 90 days, then cleared. |
| Underwriting | Not applied — any business type can create sandbox charges. |
Go-live checklist
Work through this before you switch keys. Most launch incidents come from one of these being skipped.
- Live keys are loaded from the environment, not committed anywhere.
- Webhook endpoint is registered against production and its signature verification has been tested with a real signed payload.
- Every decline code in the table above has a handled path in your code, not just the generic one.
- Idempotency keys are sent on every write, and are stable across retries of the same logical operation.
- Your statement descriptor is set to something a cardholder will recognise — vague descriptors are a leading cause of disputes.
- Refund and cancellation policies are visible before checkout.
- Errors are logged with the
error.codeand the request id, so support can trace a specific payment. - Someone owns the
dispute.createdalert, and the deadline is on a calendar. - You have run one real low-value transaction end to end and refunded it.