API-ийн нийтлэг дүрэм¶
Энэ хуудсаар: бүх server API-д нийтлэг дүрмийг — хаяг, нэвтрэлт, орчин, формат, id, idempotency, давталт,
хуудаслалт — нэг удаа уншаад, endpoint бүрийн хуудсанд дахин хайхгүй болно. 10 минут. Хэрэгтэй: консолоос авсан test
API түлхүүр (gk_test_… / gks_test_…).
Хаяг ба хувилбар¶
- Бүх server route
https://geregeapp.mn/api/v1/miniappугтвартай (SDK-уудынbaseUrl/WithBaseURL-ийн анхдагчhttps://geregeapp.mn). - Major хувилбар нь замд (
/api/v1). Minor хувилбар (модулийн хувилбар, жишээ нь 1.7.0) зөвхөн нэмнэ: шинэ талбар, шинэ утга, шинэ event, шинэ алдааны код. Тиймээс таны код мэдэхгүй талбар, event, кодыг үл тоох ёстой — JSON-оо хатуу (additionalProperties: false,DisallowUnknownFields) задлахгүй. - Хувилбар бүрийн өөрчлөлт: changelog (баримтын «Лавлах» хэсэгт гарна).
Нэвтрэлт¶
Хоёр төрлийн credential. Аль route юуг авахыг доорх хүснэгт хэлнэ.
| Credential | Толгой | Хэн, хэзээ |
|---|---|---|
| Апп-ын API түлхүүр (client credential) | Authorization: Basic base64(client_id:client_secret) |
таны сервер өөрийнхөө өмнөөс: худалдагчийн claimless intent, intent-ийн уншилт/үйлдэл, тооцоо, тайлан, терминал, дэд худалдагч, webhook endpoint, event, token endpoint |
| Хүний access token | Authorization: Bearer gma_at_… (payments consent scope) |
mini app-ын backend хүний өмнөөс: app intent үүсгэх, GET /me; мөн intent-ийн уншилт/үйлдэл, тооцоо |
| Route | Basic (түлхүүрийн scope) | Bearer |
|---|---|---|
POST /oauth/token |
✓ oauth:token (эсвэл body-д client_id/client_secret — хоёуланг нь биш) |
— |
GET /me |
— | ✓ |
POST /payment-intents |
✓ payments:write — зөвхөн qr · redirect · terminal |
✓ — зөвхөн app |
GET /payment-intents[/{id}] |
✓ payments:read |
✓ |
…/{id}/capture, …/{id}/cancel, …/{id}/unclaim |
✓ payments:write |
✓ |
…/{id}/refunds |
✓ refunds:write |
✓ |
GET /settlements[/{id}] |
✓ settlements:read |
✓ |
GET /statements |
✓ settlements:read (зөвхөн Basic) |
— |
GET /terminals[/{id}], POST /terminals/print-sheet |
✓ terminals:read (зөвхөн Basic) |
— |
POST /terminals, PATCH /terminals/{id}, POST /terminals/batch |
✓ terminals:write (live түлхүүр) |
— |
GET /sub-merchants[/{id}], …/{id}/payout-links |
✓ sub_merchants:read |
— |
POST /sub-merchants, PATCH /sub-merchants/{id} |
✓ sub_merchants:write (live түлхүүр) |
— |
POST /sub-merchants/{id}/payout-link |
✓ payout_links:write (live түлхүүр) |
— |
/webhook-endpoints… |
✓ webhooks:write (live түлхүүр) |
— |
GET /events[/{id}] |
✓ events:read |
— |
GET /pay/{token}, GET /pay/t/{code} |
нэвтрэлтгүй (нийтийн) |
API түлхүүр (1.8.0). Апп орчин бүрт олон нэртэй түлхүүртэй (≤ 20 идэвхтэй), тус бүр өөрийн scope, сонголтоор IP
allowlist ба дуусах хугацаатай. Консолын апп-ын «Түлхүүр» (/dev/apps/{id}/keys)-ээс үүсгэж, солиж (roll — хуучин нь
давхцлын турш ажилласаар, 0 – 7 хоног, анхдагч 24 цаг), хүчингүй болгоно.
- Client id:
gk_live_/gk_test_+ 20 base62 (нууц биш, log-д бичиж болно). Secret:gks_live_/gks_test_+ 43 тэмдэгт base64url +_+ 6 тэмдэгт base62 checksum (түүнээс өмнөх бүх хэсгийн CRC32) — консолд нэг удаа харагдана, платформ зөвхөн SHA-256-ийг нь хадгална. Checksum нууц биш: secret scanner мэдээллийн сангүйгээр жинхэнэ хэлбэрийг таниулна (Аюулгүй байдлын checklist). - Хуучин хос (
mapp_…/mapp_test_…+gma_cs_…) нь «Анхдагч түлхүүр» болж ажилласаар — тухайн орчны бүх scope-той.POST /dev/apps/{id}/credentialsтүүнийг шууд (давхцалгүй) солино. - Scope дутуу бол
403 insufficient_scope+{"required_scope": "refunds:write"}баWWW-Authenticate: Basic error="insufficient_scope", scope="refunds:write"(401биш — secret зөв). Түлхүүрийн scope өөрчлөгдөхгүй — өөр scope-той шинэ түлхүүр үүсгэнэ. Preset: «Бүрэн эрх», «Checkout сервер», «Вендинг / kiosk backend», «Терминал суулгагч», «Зөвхөн унших», «Mini app backend». terminals:write,sub_merchants:write,payout_links:write,webhooks:writeнь test түлхүүрт олгогдохгүй: бүртгэл нь хоёр орчинд нэг тул test түлхүүр бичвэл403 live_only(scope-оос өмнө шалгагдана).- Түлхүүрийн IP allowlist-д ороогүй хаягаас —
403 ip_not_allowed(ip— харсан хаяг). Хаягийг платформ өөрөө тодорхойлно (итгэмжлэгдсэн proxy-ийн ард) —X-Forwarded-For-оо бүү тааварла. - Live түлхүүрийг үүсгэх, солих, IP-г нь өөрчлөх нь
miniapp.keys.liveба дахин нэвтрэлт шаардана (403 fresh_sign_in_required); суулгац шалгаж чадахгүй үед503 fresh_sign_in_unavailable. - Хүчингүй болсон, хугацаа нь дууссан, солигдоод давхцал нь өнгөрсөн түлхүүр —
401 invalid_client, буруу secret-ээс ялгагдахгүй. - Live түлхүүр нь апп батлагдсаны дараа л ажиллана; апп түдгэлзсэн/идэвхгүй бол бүх түлхүүр
401 invalid_client. - Апп зөвхөн өөрийн объектыг харна; өөр апп-ынх
404. - Төлбөр, бүртгэл, тайлан,
/me-ийн хариуCache-Control: no-store— завсрын cache хадгалахгүй. - Secret, token-ыг зөвхөн сервер дээр — Аюулгүй байдлын checklist.
curl -s https://geregeapp.mn/api/v1/miniapp/payment-intents?limit=5 \
-u "$GEREGE_CLIENT_ID:$GEREGE_CLIENT_SECRET"
Орчин¶
- Орчныг credential шийднэ:
gk_test_…(хуучинmapp_test_…) → test,gk_live_…(mapp_…) → live; хүний token нь нээлтийн орчноо авч явна. - Объект бүрт
livemode(true= live). Test түлхүүр live объектыг хэзээ ч харахгүй, эсрэгээрээ ч (404). - Test горимд мөнгө хөдлөхгүй; тест дүн, тест хэрэглэгч — Sandbox.
- Терминал, дэд худалдагч нь хоёр орчинд нэг бүртгэл: test түлхүүр уншина, бичих нь live түлхүүр эсвэл консол
(
403 live_only). Webhook endpoint нь орчин бүрт тусдаа, гэхдээ server API-аар (/webhook-endpoints) зөвхөн live түлхүүр удирдана — test endpoint-ийг консолоос. Тооцоо зөвхөн live (test-д жагсаалт хоосон); тайлан (/statements) ба/eventsтүлхүүрийн орчныг уншина.
Хүсэлтийн формат¶
- Body нь JSON (
Content-Type: application/json); token endpoint ньapplication/x-www-form-urlencodedэсвэл JSON. - Route-ийн авдаггүй талбар, JSON биш эсвэл хэт том body →
400 invalid_request(хэмжээ: Хязгаар). Энэ нь санаатай: буруу бичсэн талбар чимээгүй үл тоогдохгүй. - Хоосон утгатай заавал биш талбарыг орхих нь анхдагч утгыг авна (
currency: "MNT",expires_in: 600,capture_method: "automatic"). - Хариу JSON; «null болно» гэсэн талбар үргэлж байна (Объектууд).
Мөнгө ба цаг¶
- Мөнгө — бүхэл ₮ (
3500= 3 500 ₮), бутархайгүй; валют одоогоор зөвхөнMNT. - Цаг — RFC 3339, UTC (
2026-10-22T09:00:00Z). Тооцооны хуваарь, «өдөр» нь Улаанбаатарын цагаар (Тооцоо). - Хугацааг секундээр өгнө (
expires_in,capture_within).
Id ба угтвар¶
| Объект | Хэлбэр |
|---|---|
| Payment intent · Refund · Event · Settlement · Webhook endpoint | pi_ · re_ · evt_ · st_ · we_ + 24 base62 |
Терминал, дэд худалдагч, payout холболт, API түлхүүр (консолын id) |
UUID (жижиг үсгээр) |
Терминалын code |
10 тэмдэгт, 23456789abcdefghjkmnpqrstuvwxyz (0/o, 1/l/i-гүй — нүдээр уншина) |
| Хүн | user_ref / sub (u_ + 32 hex): mini app-д апп бүрт тогтмол pairwise, claimless төлбөрт төлбөр бүрт санамсаргүй |
| Credential | gk_live_, gk_test_ (client id), gks_live_, gks_test_ (secret); хуучин mapp_, mapp_test_, gma_cs_; gml_ (launch code), gma_at_, gma_rt_, whsec_ |
Буруу хэлбэрийн id нь 404 (400 биш). Угтвар нь secret scanner-т таних, буруу төрлийн утгыг эрт татгалзахад тусална.
Idempotency¶
Мөнгө хөдөлгөх хүсэлт Idempotency-Key толгой шаардана:
| Хүсэлт | Key-ийн хүрээ |
|---|---|
POST /payment-intents |
апп + орчин бүрт давтагдашгүй |
POST /payment-intents/{id}/refunds |
intent бүрт давтагдашгүй |
Хүний POST /payments/{id}/confirm (Gerege App) |
intent бүрт |
- Утга: 1 – 128 хэвлэгдэх ASCII, зайгүй. Алга/буруу →
400 idempotency_key_required. - Ижил key + ижил body →
200ба анх үүссэн объект (одоогийн төлвөөрөө; claimless intent-д ижилpay_url). Шинээр үүсвэл201. - Ижил key + өөр body →
409 idempotency_conflict.appintent-д «body»-д хүн ч орно — өөр хүний token-оор ижил key бол зөрчил. - Зөвлөмж: нэг захиалга = нэг key — захиалга/сагсны дугаараа key (ба
reference) болго (order:8812). Тэгвэл timeout-ийн дараах давталт хоёр дахь төлбөр үүсгэхгүй. - Бүртгэл нь өөрөөр idempotent: ижил
reference-тэй терминал, ижилcode-той дэд худалдагч дахин үүсгэвэл200ба байгаа мөр. - Capture, cancel, unclaim нь key-гүй ч дахин дуудахад аюулгүй: аль хэдийн capture хийгдсэнийг capture хийвэл байгаагаар нь, цуцлагдсаныг цуцалбал мөн.
curl -s https://geregeapp.mn/api/v1/miniapp/payment-intents \
-u "$GEREGE_CLIENT_ID:$GEREGE_CLIENT_SECRET" \
-H "Idempotency-Key: order:8812" -H "Content-Type: application/json" \
-d '{"amount": 45000, "description": "Захиалга #8812", "reference": "order:8812", "presentment": "redirect", "return_url": "https://shop.mn/done"}'
Давталт¶
- Сүлжээний алдаа, timeout,
429,5xx→ ижил key-ээр дахин (exponential backoff). SDK-дerr.retryable/apiErr.Retryable(). 202 Accepted→ ledger-ийн хариу хараахан тодорхойгүй (status: "processing"эсвэл буцаалтpending). Платформ өөрөө дуусгана;GET-ээр дахин унш эсвэл webhook хүлээ.409 in_progress→ түр хүлээгээд дахин унш.4xx-ийг өөрчлөлтгүй давтахгүй — Алдааны код-ын «Давтах» багана.- SDK өөрөө давтахгүй — давталтын бодлого таных.
Хуудаслалт¶
| Жагсаалт | Хэлбэр | Дараагийн хуудас |
|---|---|---|
GET /payment-intents?reference=&status=&limit=&after= (шинэ нь эхэнд) |
{"data": [...], "has_more": bool} |
after = энэ хуудасны сүүлийн id |
GET /settlements?status=&limit=&cursor= (шинэ нь эхэнд) |
{"items": [...], "next_cursor": string\|null, "has_more": bool} |
cursor = next_cursor |
GET /events?type=&created_gte=&limit=&after= (хуучин нь эхэнд, 30 хоног) |
{"data": [...], "next_cursor": "evt_…"\|null} |
after = next_cursor; null — одоохондоо шинэ event алга, дараа нь сүүлийн id-аасаа дахин асуу |
GET /terminals, GET /sub-merchants, …/payout-links, GET /webhook-endpoints |
{"data": [...]} |
хуудасгүй |
GET /statements?from=&to=&format= |
нэг тайлан (data мөрүүд) |
хуудасгүй — хугацаагаар хуваана (≤ 100 000 мөр) |
limit 1 – 100 (анхдагч 20; /events-д 50). Буруу утга → 400 invalid_limit / invalid_cursor / invalid_status. Node-д
listAll() бүх хуудсыг дагана.
let after;
do {
const page = await gerege.paymentIntents.list({ status: "succeeded", limit: 100, after });
for (const pi of page.data) reconcile(pi);
after = page.has_more ? page.data.at(-1).id : undefined;
} while (after);
Алдаа¶
- Хоёр хэлбэр (OAuth ба модулийн),
code-оор шийднэ — Алдааны код. - Хариунд
X-Request-Idтолгой байвал SDK түүнийгrequestId/RequestID-д өгнө — тусламж хүсэхдээ хавсаргана (Тусламж). Secret-ийг хэзээ ч бүү хавсарга.