Sandbox
Audience: anyone integrating Mooov — platforms on Mooov Connect and merchants using a direct API key. Build and certify with test keys before you touch live money.
Test mode uses the same canonical API host and the same HMAC auth,
webhook envelopes, and state machine as live — no real money moves.
Merchant keys select their environment. Connect platform calls select a
grant with ?environment=test; omitting the query selects live.
Building with an AI agent or MCP-capable editor? It can pull these docs and schemas itself; see Connect your AI agent.
1. Base URLs
API https://api.mooov.money
Authorize https://connect.mooov.money/authorize (Connect only)
Use mk_test_*, or a platform key with ?environment=test and an active
test merchant grant, against the canonical host. https://sandbox.api.mooov.money remains a temporary
compatibility alias only; it does not select the request environment.
Test payments and merchant webhook destinations are separate from live within the same merchant account. Connect grants are environment scoped, while platform keys are shared across environments. The full hostname matrix (portals, docs, MCP) is on the environments page.
2. Test credentials
A merchant API key's environment is fixed at minting time. Call
https://api.mooov.money with mk_test_* keys while building.
Test keys are visually distinct so a leaked-into-live-config mistake
is greppable:
| Credential | Test prefix | Live prefix |
|---|---|---|
| Merchant API key | mk_test_ |
mk_live_ |
| Platform (Connect) key | mk_platform_ + ?environment=test + test grant |
mk_platform_ + ?environment=live + live grant |
- Merchants mint test keys from the merchant portal
(
mooov3.mooov.money→ Developers → API keys, Test mode). The secret is shown once at creation; store it in your secret manager. - Platforms receive a platform key through the credential ceremony in
§9 of the Connect protocol. Authorize an existing
merchant with
mode=test, then pass?environment=teston merchant-scoped API calls. Platform keys are shared; grants are separate by environment. Both omitted consent mode and omitted API environment default to live.
Signing is identical in both environments — the four-line canonical string and headers from §2 of the Connect protocol.
3. Test cards
Which test cards work depends on how your charge routes. Mooov picks an eligible route using the merchant configuration and requested payment method. Confirm the enabled sandbox route during onboarding. The card scenarios below apply only to the named card routes, not to every merchant or alternative payment method.
3.1 Stripe-routed merchants
These Stripe test PANs cover common card outcomes on a Stripe test route:
| Card number | Result |
|---|---|
4242 4242 4242 4242 |
Approved immediately |
4000 0025 0000 3155 |
Requires 3DS authentication (challenge flow) |
4000 0000 0000 9995 |
Declined — insufficient_funds |
4000 0000 0000 0002 |
Declined — generic card_declined |
4000 0000 0000 0341 |
Attaches for future use, then fails when charged off-session |
Use any future expiry date, any 3-digit CVC, and any postcode. On a
hosted checkout (flow: "redirect" / flow: "embedded") you type
these into the Stripe-hosted form; for server flow you tokenise them
first via a test-mode Setup Intent or Payment Element.
Customer-backed and saved-method flows are temporarily unavailable with
test credentials because customer/provider mappings are not yet
environment-scoped. customer_ref, POST /v1/charges/saved,
POST /v1/subscription_checkouts, and GET /v1/customers/by_ref return
test_customer_mappings_unavailable without reading live mappings.
Direct tokenized test payments remain supported through
POST /v1/payment_intents when customer_ref is omitted.
3.2 High-risk hosted card sandbox
Three things are different from Stripe before you reach for a card:
- HKD only. This hosted card sandbox accepts
currency: "HKD"exclusively. A test charge in GBP/EUR/USD will be rejected at creation. (Production supports more currencies; the sandbox does not.) - Hosted checkout only. Use
flow: "redirect"and send the customer toprovider.hosted_url. Card entry happens on the acquirer-hosted page; there is no test-token server flow. - Some cards require an exact amount to trigger their 3DS scenario — see the table.
| Brand | Card number | Scenario | Notes |
|---|---|---|---|
| JCB | 3566 1111 1111 1113 |
Approved, no 3DS | The dedicated non-3DS card — use this first |
| Mastercard | 5555 5555 5555 4444 |
Usually approves without 3DS | Can be pushed into authentication; if it fails, use the JCB above |
| JCB | 3566 0000 0000 0001 |
3DS frictionless | Auto-passes, no challenge UI |
| JCB | 3566 0000 0000 0007 |
3DS challenge | Shows a challenge page |
| Mastercard | 5186 0076 0009 0864 |
3DS frictionless | Amount must be exactly HKD 2.01 (amount: 201) |
| Mastercard | 5506 9001 4010 0305 |
3DS challenge | Amount must be exactly HKD 20.90 (amount: 2090) |
| Visa | 4012 0000 3333 0026 |
3DS challenge | Amount must be exactly HKD 2.09 (amount: 209) |
Any future expiry and any 3-digit CVV work for all of the above. If the hosted form asks for billing address fields, any plausible values are accepted.
Refunds work the same as Stripe-routed merchants — full and partial
via POST /v1/payment_intents/{id}/refund — with one provider rule
that also applies in production: the refund window is six months from
capture, and the refund currency must match the charge currency.
4. Test tenants (Connect)
Use an existing merchant account and run the consent flow from
§3 of the Connect protocol with mode=test at
connect.mooov.money/authorize. Exchanging the code returns that merchant's
identifier and grant environment; it does not create a merchant or complete
provider onboarding.
Before charging, confirm that the merchant has an enabled test provider
connection and the required payment capability. Use ?environment=test
with the platform key and Mooov-Merchant header on subsequent calls. If
onboarding is incomplete, handle 422 merchant_not_charge_capable and its
setup instructions; see the error reference.
Arrange reusable test merchants and grants before running CI.
5. Webhooks in test mode
For direct merchant webhooks, register a test destination separately from
live in the portal: Developers → Webhooks in Test mode. Deliveries are
signed with your test webhook secret using the same t=<unix>,v1=<hex> scheme
documented in §6 of the Connect protocol.
Connect limitation: test events are not currently forwarded to platform webhook destinations. A successful test payment does not certify Connect webhook delivery. Validate your consumer with signed fixtures and coordinate live delivery certification during activation. Do not assume a shared platform destination has a separate test secret.
For direct merchant destinations on an enabled Stripe test route, two ways to exercise events are:
- Drive a real test payment. Complete a
4242…checkout and check delivery ofpayment.captured; refund it viaPOST /v1/payment_intents/{id}/refundto receivepayment.refunded. This exercises the full pipeline end to end and is what we recommend for CI. - Decline paths. Use the decline PANs above to receive
payment.failedwith a populatedfailure_code/failure_category.
The full event list and payload shapes are in the webhook event catalog.
Delivery failures use exponential backoff based on 30 seconds, doubling with the attempt counter and capped at one hour, with eight attempts by default. Scheduling can delay actual delivery. For a merchant test destination, return a 500 to exercise redelivery and deduplicate by the stable delivery or event identifier. This does not enable Connect test fan-out.
6. Go-live checklist
Before live activation, verify the supported test flows and record any checks that require coordinated live certification:
- Signature verification rejects a tampered body and a stale timestamp (>5 min skew).
- Idempotent retries: replaying a
POSTwith the sameIdempotency-Keyand body returns the original result; a reused key with a different body returns 409. - Merchant
payment.captured,payment.failed, andpayment.refundedhandlers deduplicate by eventid. Connect consumers usepayment.succeededin place ofpayment.captured; test their handlers with fixtures until live delivery certification. - The
422 merchant_not_charge_capablepath surfaces thesetup_urlto the merchant instead of erroring blindly. - (Connect) Grant revocation: after
POST /v1/grants/{id}/revokeyour system marks the tenant disconnected on the next 403MERCHANT_GRANT_REQUIREDin that grant's environment.
Then follow §9 of the Connect protocol (platforms)
or mint a live key from the portal (merchants). Keep the base URL as
https://api.mooov.money. Platforms need a live grant and explicit
?environment=live; merchants switch the key and webhook destination.
Certify Connect webhook delivery during coordinated live activation.