Test and live¶
Two environments inside one app. The credential's environment sets the intent's
livemode; the data never mix.
1. What decides the environment¶
| test | live | |
|---|---|---|
| Key (client id) | gk_test_… (or the default mapp_test_…) |
gk_live_… (or the default mapp_…) |
| Intent | livemode: false |
livemode: true |
| Money | does not move — the amount decides the outcome | a hold on the person's Gerege Wallet account → capture |
| Works from | registration (even for a draft app) | the app's approval only (before it, 401 invalid_client) |
| Who can pay | the person who registered the app, its test users | anyone (subject to the app's min_age / requires_verified) |
| Payout account, core banking | not needed | needed (409 payouts_not_configured, 503 payments_unavailable) |
| Webhook endpoints | separate per environment (≤ 5), livemode: false |
separate per environment (≤ 5), livemode: true |
Settlements (/settlements) |
empty / 404 |
✓ |
A key sees only its own environment's intents and events: reading a live intent with a test key is 404. A test key
never gets the registry's write scopes (terminals:write, sub_merchants:write, payout_links:write,
webhooks:write).
2. Test amounts¶
The last two digits of the amount decide the outcome of confirm:
| Amount | Outcome |
|---|---|
…01 (e.g. 3501) |
failed, failure_code: "test_declined" |
…02 (e.g. 3502) |
canceled |
anything else, automatic |
succeeded |
anything else, manual |
requires_capture → your capture / cancel |
Refunds succeed at once in test. Test users (≤ 50, active members of your organisation), the console's test intents,
and webhook deliveries / resend / webhook.test: Developer sandbox.
3. One registry for both environments¶
The app, its terminals and sub-merchants are one registry for both environments — there is no separate «test terminal». So:
- Writing to the registry (creating or editing terminals and sub-merchants, requesting payout links) takes a
live key or the console; a test key only reads —
403 live_only. A leaked sandbox secret must not change a real machine's owner and send live money elsewhere. In the sandbox, register from the console. - A terminal has one open basket per environment: a test basket never blocks a live machine.
- Intents, refunds, events and settlements are separate per environment.
4. The app's state and going live¶
Two separate states: review (draft → in_review → approved | rejected, suspended) and status (draft,
active ⇄ maintenance, disabled).
draft ──submit──▶ in_review ──approve──▶ approved (live keys work, people see it)
▲ │
└──── fix ◀──── rejected approved ──suspend (operator)──▶ suspended (everything stops)
- Submit (
POST /dev/apps/{id}/submit): legal pages (privacy, terms, support — 200 when https), a live secret, a webhook URL (withpayments), and for a mini app a checked manifest. Anything missing:400 not_ready+details[]. During review the app is frozen (409 locked_for_review). - The operator checks the business (today the agreement and KYB documents by hand), approves it and sets the payout account.
- Every state change sends an
app.status_changedwebhook. suspended— nothing opens, every token is revoked, credentials are refused.
The self-service steps to live from the dashboard — the organisation's representative signing the agreement with
eID, the business profile (KYB), a go-live checklist, limits (tiers), live keys from the new API (behind a fresh
sign-in; today through POST /dev/apps/{id}/credentials) — are «удахгүй». An individual without a legal entity uses the whole of test mode (a sandbox organisation —
«удахгүй»); live needs an organisation.