Sandbox — test mode¶
After this page you will know how to test your integration without moving real money, what works today and what does not yet. Time: 5 minutes. You need: an app registered in the developer console and its test key.
Test keys¶
The key decides the environment: an intent created with a test key is livemode: false, and that key sees
only test objects. Test and live data never mix — lists, GET and the webhook's livemode are all separate.
| Key | Client id | Secret | How to get it |
|---|---|---|---|
| API key (test) | gk_test_… |
gks_test_… |
console → Keys → "New key", a name, a preset or scopes (POST /dev/apps/{id}/keys) |
| Default key (test) | mapp_test_… |
gma_cs_… |
POST /dev/apps/{id}/credentials — every scope of its environment |
| Live | gk_live_… / mapp_… |
gks_live_… / gma_cs_… |
from the console's «Keys» (or POST /dev/apps/{id}/credentials): miniapp.keys.live and a sign-in within the last ten minutes (on 403 fresh_sign_in_required sign in again and repeat); works only once the app is approved; never in a sandbox organisation |
A test key works at once, even while the app is a draft and not approved. The secret is shown once.
export GEREGE_CLIENT_ID=gk_test_xxxxxxxxxxxxxxxxxxxx
export GEREGE_CLIENT_SECRET=gks_test_…
curl -s "https://geregeapp.mn/api/v1/miniapp/payment-intents?limit=5" \
-u "$GEREGE_CLIENT_ID:$GEREGE_CLIENT_SECRET"
What is different in test mode¶
- No money moves. Core banking is not called and no hold is placed; no payout account is needed.
- The last two digits of the amount decide the outcome — Test amounts.
- A capture is
succeededat once, and so is a refund (no ledger answer to wait for). - Time limits (
expires_in,capture_within) work exactly as in live — an intent past its time becomesexpired. - Webhooks arrive with the same types and shapes as in live,
livemode: false. Endpoints are per environment — add a test endpoint (a tunnel, say) without touching live (Local webhooks). The app's original webhook URL (endpoint №1) serves both environments — there, tell them apart bylivemode. GET /settlementswith a test key is always empty — settlements are for live money only.- A test key never gets a live-only scope (
terminals:write,sub_merchants:write,payout_links:write,webhooks:write): it reads terminals, sub-merchants and payout links, but writing them is403 live_only. The registry is one for both environments (a leaked sandbox secret must not change real machines), so register them with a live key or in the console. A test key may print the sticker PDF (POST /terminals/print-sheet). - One terminal may have a test basket and a live basket open at the same time (one open unclaimed intent per
terminal per environment). Only the app's owner and its test users are offered the test basket;
everybody else, and the public
/pay/t/<code>page, see only the live one.
What can be tested end to end today¶
| Flow | Today | Notes |
|---|---|---|
Claimless intents (qr, redirect, terminal): create, replay, list, cancel, unclaim, capture, refund |
✓ | with the test key; pay_url comes in the answer |
Paying a claimless intent with a phone — Gerege App's QR button, "Scan NFC", a /pay/… link from the camera (Universal Link), touching the sticker |
✓ | Gerege App iOS build 122+ (TestFlight); a test user or the app's owner; Face ID on a test intent moves no money |
The https://geregeapp.mn/pay/<token> QR page and the /pay/t/<code> terminal page on a computer |
✓ | the terminal page shows only the live basket |
| Playing the person without a phone (claim, pay, cancel, expire, touch the sticker) | ✓ | Testing without a phone |
Mini app intents (presentment: "app") — the console's test intents, or a test user opening the app in Gerege App and paying |
✓ | a draft app shows in its test users' launcher |
| Reading terminals, sub-merchants and payout links; the sticker PDF | ✓ | writing is a live key's or the console's |
| Webhook endpoints (≤ 5 per environment), deliveries, "send again", test sends; the events API | ✓ | Local webhooks, Events API |
| App Clip | coming soon | App Clip |
| A sandbox organisation for people without a legal entity | ✓ | «Sandbox organisations» below |
Testing with a phone
Only the app's owner and its test users can claim a test claimless intent; to anybody else
it does not exist (404). So whoever tests with a phone must be a member of your organisation added as a test
user. Make the QR from pay_url, or use your terminal's sticker (Testing NFC).
Sandbox organisations (developers without a legal entity)¶
A developer without a company opens one sandbox organisation from their profile (Profile → Organisations → «Open a sandbox») — «Mini apps and payments» arrives installed in it. It is an ordinary organisation: the same console, team and permissions, and test mode in full (test keys, endpoints, simulation, terminals, sub-merchants, test intents).
- Test users are you (the app's owner) and the members you invite to the sandbox (Test users).
- An app's
slugbegins withsbx-(sbx-myshop;^sbx-[a-z0-9-]{3,27}$, otherwise400 sandbox_slug) — real names belong to real organisations, and no organisation that is not a sandbox may usesbx-. - Nothing live: a live key, a live secret, review, an agreement signature, a payout link or the app's payout is
409 sandbox_only. - To go live, open your organisation (a legal entity) and register the app there anew — nothing moves over.
From test to live¶
Going live changes only the key: swap the client id/secret for a live key and set up the live environment's webhook endpoint (with its own secret). Code, routes and shapes are the same. Test data does not move to live; terminals and sub-merchants are already one registry, so nothing is registered again. Before going live, go through the Go-live checklist. The concept: Test and live.