Агуулгыг алгасах

Payment intent

Энэ хуудас бол payment intent-ийн лавлах: объектын талбар бүр, үүсгэх хүсэлтийн хязгаар, төлөв бүр ба түүнийг хэн шилжүүлж ямар webhook ирэх, capture / cancel / unclaim-ийн яг утга, үйлдэл бүрийн алдаа. Уншихад ~15 минут. Хэрэгтэй: апп-ын API түлхүүр (gk_test_… / gk_live_… + gks_… secret, эсвэл хуучин «Анхдагч түлхүүр» mapp_… + gma_cs_…) эсвэл mini app-ын хувьд хүний access token. Алхам алхмаар интеграц хийх бол Gerege App-аар төлөх ба mini app-ын төлбөр-өөс эхэл.

Бүх зам https://geregeapp.mn/api/v1/miniapp угтвартай. Мөнгө бүхэл төгрөгөөр (₮), цаг RFC 3339 UTC.

Хоёр төрлийн intent

Хэн үүсгэх Нэвтрэлт presentment Хэнийх
Mini app-ын intent mini app-ын backend хүний access token (Bearer gma_at_…, payments scope) app (эсвэл орхих) эхнээсээ тухайн хүнийх
Claimless intent худалдагчийн backend client credential (Basic) qr · redirect · terminal (заавал) хэн нэг нь claim хийтэл хэнийх ч биш

Хоёр зам хэзээ ч холилдохгүй: хүний token-оор qr/redirect/terminal үүсгэвэл 400 validation_error (details[].path = presentment), client credential-аар app intent (эсвэл presentment-гүй) үүсгэвэл 400 presentment_required. Үүсгэхээс бусад бүх үйлдэл (унших, жагсаах, capture, cancel, refund, unclaim) хоёр нэвтрэлтийн аль алиныг хүлээн авна. Апп зөвхөн өөрийн, тухайн credential-ын орчны (test / live) intent-үүдийг хардаг — өөр апп-ын эсвэл өөр орчны intent 404.

Түлхүүрийн scope. Client credential-аар (Basic) дуудахад түлхүүр тухайн үйлдлийн scope-ыг барьсан байх ёстой; үгүй бол 403 insufficient_scope + "required_scope". Хүний access token-д түлхүүрийн scope хамаагүй — хүн апп-д payments зөвшөөрсөн байхад л хангалттай. «Анхдагч түлхүүр» тухайн орчны бүх scope-ыг барина.

Үйлдэл Scope
POST /payment-intents, …/{id}/capture, …/{id}/cancel, …/{id}/unclaim payments:write
GET /payment-intents, GET /payment-intents/{id} payments:read
POST /payment-intents/{id}/refunds refunds:write

Түлхүүр, scope, preset-ийн тухай — Алдааны код-ын «Түлхүүр ба scope» хэсэг ба аюулгүй байдлын загвар.

Үүсгэх — POST /payment-intents

Idempotency-Key толгой заавал (1–128 хэвлэгдэх ASCII тэмдэгт, зайгүй). Захиалга/сагсны дугаараа key болго — нэг захиалга = нэг intent.

curl -sS 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/orders/8812/done",
        "expires_in": 900
      }'
Талбар Төрөл Дүрэм
amount integer заавал, 100 – 50 000 000 ₮, бүхэл. Оператор апп-д max_amount тогтоосон бол түүнээс ихгүй (validation_error, path amount)
currency string зөвхөн MNT (орхивол MNT)
description string заавал, 1–80 тэмдэгт. Төлөх цонхонд хүнд харагдана — захиалгын товч, бүтээгдэхүүний ангилал биш. Вэб хаяг, имэйл, утасны дугаар хориотой (400 description_not_allowed, фишинг) — өөрийн дугаараа reference-д
reference string ≤ 64 тэмдэгт, tab/шинэ мөргүй. Таны захиалгын дугаар — жагсаалтаас хайхад
expires_in integer 60 – 1800 секунд, анхдагч 600; qr, redirect-д 604 800 (7 хоног — нэхэмжлэл) хүртэл
capture_method string automatic (анхдагч) эсвэл manual
capture_within integer зөвхөн manual: 60 – 604 800 секунд, анхдагч 604 800 (7 хоног) — баталгаажуулснаас хойш capture хийх хугацаа
metadata object ≤ 20 түлхүүр, түлхүүр 1–40 байт, утга ≤ 500 тэмдэгт, утга нь string. Хүнд хэзээ ч харагдахгүй
presentment string app · qr · redirect · terminal (дээрх хүснэгт)
terminal_id UUID terminal-д заавал; qr-д сонголтоор (kiosk, касс өөрийн дэлгэц дээр QR зурахад — терминалын тоо, дэд худалдагчид орно; терминалын «нэг нээлттэй сагс» дүрэм зөвхөн terminal-д, sticker уншуулахад QR intent олдохгүй); бусдад хориотой. Апп-ын идэвхтэй терминал
sub_merchant_id UUID заавал биш, апп-ын идэвхтэй дэд худалдагч. Терминал дэд худалдагчтай бол түүнийх автоматаар; өөр утга өгвөл validation_error
return_url string redirect-д заавал, qr/terminal-д заавал биш, app-д хориотой. ≤ 2048, host нь апп-ын бүртгэлтэй return_hosts-ийн нэг
min_age integer 13 – 21, апп-ын min_age-аас бага байж болохгүй. Claim ба баталгаажуулалтын үед шалгагдана

Мэдэхгүй талбартай, JSON биш эсвэл хэт том (16 KB-аас их) бие → 400 invalid_request.

Хариу. 201 — шинэ intent; 200 — ижил key + ижил бие (давталт): эхний оролдлогын intent. Claimless intent-ийн хариунд нэмэлт pay_url талбар бий:

{
  "id": "pi_4fQ2mX8kLp0aZr7TbN3cVw9E",
  "object": "payment_intent",
  "status": "requires_confirmation",
  "amount": 45000,
  "currency": "MNT",
  "description": "Захиалга #8812",
  "reference": "order-8812",
  "user_ref": null,
  "livemode": false,
  "capture_method": "automatic",
  "refunded_amount": 0,
  "fee": null,
  "net": null,
  "failure_code": null,
  "expires_at": "2026-10-27T09:15:00Z",
  "capture_before": null,
  "confirmed_at": null,
  "created_at": "2026-10-27T09:00:00Z",
  "metadata": {},
  "presentment": "redirect",
  "terminal": null,
  "sub_merchant": null,
  "return_url": "https://shop.mn/orders/8812/done",
  "claimed_at": null,
  "min_age": null,
  "capture_within": null,
  "pay_url": "https://geregeapp.mn/pay/Qk3vR8sT2uW5xY7zA1bC4dE6"
}
  • pay_url бол claim хийх нэг удаагийн нууц: зөвхөн үүсгэх хариунд (ба ижил key-ийн давталтад — ижил холбоос) байна; GET, жагсаалт, webhook-д хэзээ ч байхгүй. Сервер зөвхөн hash-ийг нь хадгална. URL-д дүн, хүлээн авагч байхгүй. Дүнг хүн төлөх цонхонд серверээс хардаг.
  • Idempotency эхэлж шалгагдана: claimless intent-ийн давталтыг терминал, дэд худалдагч, return host-ийг шалгахаас өмнө хайна — хооронд нь терминал идэвхгүй болсон ч давталт тэр intent, тэр pay_url-ийг буцаана. Ижил key өөр биетэй → 409 idempotency_conflict.
  • Live intent-ийг үүсгэх үед татгалзана, хэрэв мөнгө хүлээн авах газар алга бол: апп-ын payout данс тохируулаагүй, эсвэл дэд худалдагчийн payout-ыг эзэмшигч нь батлаагүй → 409 payouts_not_configured (Payout данс).
  • Терминалд claim хийгдээгүй нээлттэй intent орчин бүрт нэг: дахин үүсгэвэл 409 terminal_busy, нэмэлт payment_intent талбарт нээлттэй intent-ийн id — эхлээд түүнийг цуцал. Хугацаа нь өнгөрсөн ч хараахан хаагдаагүй intent-ийг сервер өөрөө дуусгаад дахин оролдоно.

PaymentIntent объект

API-ийн хариу ба webhook-ийн data.object нэг хэлбэртэй.

Талбар Төрөл Утга
id string pi_ + 24 тэмдэгт
object string үргэлж payment_intent
status string доорх 9 төлвийн нэг
amount integer ₮
currency string MNT
description string хүнд харагдсан тайлбар
reference string | null таны reference
user_ref string | null хүний нэр томьёо (u_ + 32 hex). Mini app-ын intent-д апп-д тогтвортой pairwise нэр (/me-ийн sub). Claimless intent-д claim хүртэл null, claim хийгдмэгц тухайн төлбөрт л санамсаргүй нэр (худалдагч хүний тогтвортой нэр авахгүй); unclaim хийвэл дахин null
livemode boolean true = live, false = test
capture_method string automatic | manual
refunded_amount integer буцаасан (ба явагдаж буй) буцаалтын нийлбэр — амжилтгүй болсон буцаалт хасагдана
fee · net integer | null шимтгэл ба цэвэр дүн. Зөвхөн апп-д тооцоо асаалттай үед capture хийгдсэн төлбөрт; бусад үед, test-д null (Шимтгэл)
failure_code string | null сүүлийн амжилтгүй оролдлогын шалтгаан (Алдааны код)
expires_at timestamp баталгаажуулах сүүлийн мөч
capture_before timestamp | null manual: барьцаа тавигдсаны дараа capture хийх сүүлийн мөч (баталгаажуулсан мөч + capture-ийн цонх)
confirmed_at timestamp | null төлбөр амжилттай болсон (мөнгө шилжсэн) мөч; буцаалтын 90 хоног үүнээс тоологдоно
created_at timestamp
metadata object таны metadata, байхгүй бол {}
presentment string app · qr · redirect · terminal
terminal string | null терминалын id
sub_merchant string | null дэд худалдагчийн id (өгсөн эсвэл терминалынх)
return_url string | null таны return_url
claimed_at timestamp | null хүн claim хийсэн мөч (claimless)
min_age integer | null intent-ийн өөрийн насны доод хязгаар
capture_within integer | null manual intent-ийн capture-ийн цонх секундээр (өгөөгүй бол 604 800); automatic-д null

Ирээдүйн хувилбар шинэ талбар нэмж болно — мэдэхгүй талбарыг үл тоо.

Төлвүүд ба шилжилт

                       ┌────────── cancel (апп, хүн) ──────────▶ canceled
                       │      ┌─── expires_at ─────────────────▶ expired
                       │      │
 үүсгэх ─▶ requires_confirmation ◀──── account_unavailable ──┐
              │   ▲  (claim / unclaim төлөв өөрчлөхгүй)       │
   хүн confirm│   └──── failed ◀── татгалзсан ─┐              │
              ▼                                │              │
          processing ──────────────────────────┴──────────────┘
              │ automatic: hold + capture
              ├──────────────────────────────────────────────▶ succeeded
              │ manual: hold
              ▼
       requires_capture ── capture ─▶ processing ─▶ succeeded
              │  ├── cancel (апп) ─────────────────▶ canceled (барьцаа суллагдана)
              │  └── capture_before ───────────────▶ expired (authorization_expired)
              │
 succeeded ── буцаалт ─▶ partially_refunded ── буцаалт ─▶ refunded
Шилжилт Хэн / юу Webhook
→ requires_confirmation үүсгэх —
claim (төлөв хэвээр, user_ref, claimed_at орно) хүн pay_url, QR, терминалаар payment_intent.claimed (claim бүрт, 90 с-ын дараа дахин claim хийгдвэл дахин)
requires_confirmation / failed → processing хүн Gerege App-д баталгаажуулав (live) —
processing → succeeded automatic: барьцаа + capture амжилттай payment_intent.succeeded
processing → requires_capture manual: барьцаа тавигдав payment_intent.requires_capture
processing → failed дэвтэр татгалзав (insufficient_funds, limit_exceeded, hold_mismatch, payment_failed); эсвэл барьцааны дараа апп түдгэлзсэн / payout алга (merchant_unavailable, барьцаа суллагдана); эсвэл capture татгалзсан (capture_failed) payment_intent.failed
processing → requires_confirmation сонгосон данснаас төлөх боломжгүй (account_unavailable) — хүн өөр данс сонгоно — (webhook ирэхгүй)
requires_confirmation / failed → canceled апп cancel, эсвэл хүн төлөх цонхноос татгалзав payment_intent.canceled
requires_capture → processing → succeeded апп capture payment_intent.succeeded
requires_capture → canceled апп cancel (барьцаа хүнд буцна) payment_intent.canceled
→ expired expires_at өнгөрөв; manual барьцааны capture_before өнгөрөв (authorization_expired) claimless: payment_intent.expired; app: payment_intent.canceled (status: "expired")
succeeded → partially_refunded → refunded амжилттай буцаалт refund.succeeded (intent-ийн event биш) — Буцаалт
  • failed нь дахин төлөгдөнө (OMAS): хүн өөр данс сонгоод дахин баталгаажуулж болно; expires_at хүртэл хүлээ. Захиалгаа failed дээр хаах хэрэггүй — expired эсвэл canceled-ийг хүлээ.
  • Test горимд (livemode: false) дэвтэр дуудагдахгүй: баталгаажуулалт тэр дор нь дүнгийн сүүлийн хоёр оронгоор шийдэгдэнэ (…01 → failed test_declined, …02 → canceled, бусад → succeeded эсвэл requires_capture) — Тест дүн.
  • Төгсгөлийн төлөв: canceled, expired, refunded. succeeded / partially_refunded-аас зөвхөн буцаалт.

processing — хариу тодорхойгүй

processing нь дэвтрийн (core banking) дуудлага явагдаж байгаа, үр дүн нь хараахан тодорхойгүй гэсэн үг — амжилт ч биш, алдаа ч биш. Timeout бол татгалзал биш: барьцаа тавигдсан байж магадгүй тул платформ ижил idempotency key-ээр дахин асууж (хүсэлт өөрөө эсвэл минут тутмын reconcile job) нэг л удаа дуусгана.

  • Ийм үед capture болон refunds нь 202 Accepted хариулна (бие нь intent / refund өөрийн одоогийн төлөвтэйгөө).
  • Хэрэглэгчид «амжилтгүй» гэж бүү харуул, бараа ч бүү олго. Webhook (succeeded / failed) хүлээ эсвэл хэсэг хугацааны дараа GET /payment-intents/{id}-ээр дахин унш.
  • processing үед cancel → 409 in_progress.

Хугацаа дуусах

  • requires_confirmation эсвэл failed intent expires_at-д хүрмэгц expired болно. Job минут тутам ажилладаг ч унших бүрт (GET) хугацаа нь өнгөрсөн intent-ийг тэр дор нь дуусгаж expired гэж хариулна — та хэзээ ч «төлөгдөх боломжтой» мэт хуучирсан төлөв харахгүй.
  • Баталгаажуулалт явж байх үед хугацаа дуусвал платформ шинэ барьцаа хэзээ ч тавихгүй; тавигдсан бол суллана (failure_code: "expired").
  • Claimless (qr/redirect/terminal) intent-ийн хугацаа дуусахад payment_intent.expired ирнэ (claim хийгдсэн ч, хийгдээгүй ч). Mini app-ын (app) intent OMAS-ийн дагуу payment_intent.canceled-ийг status: "expired"-тэй авна — OMAS-д бичсэн backend-ууд эвдрэхгүйн тулд.

Manual capture — POST /payment-intents/{id}/capture

capture_method: "manual" intent-ийг хүн баталгаажуулахад мөнгө хүний дансанд барьцаалагдана (requires_capture, payment_intent.requires_capture), хараахан шилжээгүй. Та capture_before-оос өмнө шийднэ:

  • capture → мөнгийг бүтнээр авна → succeeded. Хэсэгчилсэн capture ({amount}) «удахгүй» — одоохондоо бүтэн capture хийгээд олгоогүй хэсгийг буцаа.
  • cancel → барьцаа хүнд суллагдана → canceled.
  • юу ч хийхгүй бол capture_before-д барьцаа автоматаар суллагдаж expired (failure_code: "authorization_expired").

Хариу:

HTTP Хэзээ
200 capture хийгдэв; эсвэл intent аль хэдийн succeeded / partially_refunded / refunded (timeout-ын дараах давталт ердийн зүйл — дахин capture хийхгүй)
202 processing — дэвтрийн хариу тодорхойгүй, дараа нь дахин унш
409 not_capturable manual биш, эсвэл requires_capture төлөвт биш (нэмэлт payment_intent)
409 expired capture хийх хугацаа өнгөрсөн — мөнгө хүнд буцсан (нэмэлт payment_intent)
409 payouts_not_configured live: мөнгө хүлээн авах данс алга болсон
422 payment_failed дэвтэр capture-ийг татгалзав → intent failed (capture_failed), барьцаа суллагдана (нэмэлт payment_intent)
503 payments_unavailable live: core banking идэвхгүй

Capture хийхэд Idempotency-Key шаардахгүй — intent өөрөө idempotent (нэг intent нэг л удаа capture хийгдэнэ).

Цуцлах — POST /payment-intents/{id}/cancel

Одоогийн төлөв Үр дүн
requires_confirmation, failed canceled, payment_intent.canceled
requires_capture canceled, барьцаа хүнд суллагдана (дэвтрийн суллалт хойшлогдвол платформ дахин оролддог — цуцлалт хүчинтэй хэвээр), payment_intent.canceled
canceled 200, байгаагаар нь (давталт)
processing 409 in_progress — түр хүлээгээд дахин унш
succeeded, partially_refunded, refunded, expired 409 not_cancelable (нэмэлт status) — төлөгдсөн бол буцаалт хий

Хүн өөрөө зөвхөн төлөөгүй intent-ээ цуцалж чадна; барьцааг суллах нь апп-ын эрх.

Claim-ийг суллах — POST /payment-intents/{id}/unclaim

Claimless intent-ийг хүн claim хийгээд баталгаажуулалгүй орхисон (явчихсан, буруу хүн уншуулсан) үед дараагийн хүн төлөхийн тулд суллана.

Нөхцөл Хариу
claim хийгдсэн, баталгаажаагүй 200 — intent дахин хэнийх ч биш, user_ref: null, claimed_at: null
claim хийгдээгүй (requires_confirmation) 200, байгаагаар нь
баталгаажуулалт явж байна эсвэл өөр төлөв 409 not_unclaimable (нэмэлт payment_intent)
терминал хооронд нь өөр нээлттэй сагс нээсэн 409 terminal_busy — энэ intent-ийг цуцал

Unclaim хийхгүй ч баталгаажаагүй claim 90 секундэд өөрөө дуусна — дараагийн claim хийгч intent-ийг авч, шинэ user_ref-тэй дахин payment_intent.claimed ирнэ. Нэг хүн нэг дор баталгаажаагүй 3 хүртэл claim барьж болно.

Унших ба жагсаах

GET /payment-intents/{id} — intent ба түүний буцаалтууд (refunds[], хуучнаас шинэ рүү; 1.13.0). GET /payment-intents — апп-ын intent-үүд, шинэ нь эхэнд, тухайн credential-ын орчинд.

Параметр Утга
reference энэ reference-тэй intent-үүд (нэг захиалгын бүх оролдлого)
status 9 төлвийн нэг; өөр утга → 400 invalid_status
after өмнөх хуудасны сүүлийн intent-ийн id; буруу → 400 invalid_cursor
limit 1 – 100, анхдагч 20; өөр → 400 invalid_limit
curl -sS "https://geregeapp.mn/api/v1/miniapp/payment-intents?reference=order-8812&limit=20" \
  -u "$GEREGE_CLIENT_ID:$GEREGE_CLIENT_SECRET"
# → {"data": [PaymentIntent, …], "has_more": false}

has_more: true бол after=<data-ийн сүүлийн id>-ээр дараагийн хуудсыг ав. Хуудаслалтын ерөнхий дүрэм: API-ийн нийтлэг дүрэм.

Самбараас — консол

Хөгжүүлэгчийн консол байгууллагын бүх апп-ын төлбөрийг нэг жагсаалтаар харуулж, support-ын ажилтанд app backend-ийн яг ижил үйлдлийг хийлгэнэ (ижил хамгаалалт, ижил дэвтрийн key, ижил webhook):

Зам (консол, нэвтэрсэн сесс) Юу Эрх
GET /api/v1/miniapp/dev/payments байгууллагын төлбөрүүд, шинэ нь эхэнд, {data, next_cursor}; шүүлтүүр app_id, environment (анхдагч live), status (таслалаар олон), presentment, terminal_id, sub_merchant_id, created_gte/created_lt (RFC 3339), amount_gte/amount_lte, q (pi_…, reference эсвэл user_ref яг таарах), cursor, limit 1–100 (анхдагч 25) miniapp.read
GET /api/v1/miniapp/dev/payments?format=csv ижил шүүлтүүрээр CSV (≤ 100 000 мөр, UTF-8 BOM, Улаанбаатарын цаг) miniapp.finance
GET /api/v1/miniapp/dev/payments/{pi} intent + refunds[] + timeline[] (created, claimed, unclaimed, held, captured, canceled, expired, failed, refund_*, settled; actor нь app, person, job эсвэл dashboard:<гишүүн>, гишүүний нэр actor_name — байгууллагын жагсаалтаас) + events[] (webhook бүрийн хүргэлтийн төлөв) miniapp.read
POST …/dev/payments/{pi}/capture · /cancel · /unclaim app backend-ийнхтэй ижил үр дүн miniapp.payments
POST …/dev/payments/{pi}/refunds {amount?, reason} буцаалт; reason заавал miniapp.payments
GET /api/v1/miniapp/dev/refunds байгууллагын буцаалтууд (status, app_id, environment, cursor, limit) — хэн хүссэнийг (actor) хамт miniapp.read
  • POST үйлдлүүд консолын ерөнхий miniapp.manage эрхийг ч шаардана.
  • Үйлдэл бүрт Idempotency-Key заавал (1–100 тэмдэгт) — давхар дарахад нэг л capture, нэг л буцаалт болно.
  • Мөнгө хөдөлгөх үйлдэл (capture, буцаалт) түдгэлзүүлсэн, идэвхгүй эсвэл (live-д) батлагдаагүй апп-д 409 merchant_unavailable; цуцлах, claim суллах нь хүний мөнгийг буцаадаг тул үргэлж нээлттэй.
  • Live ≥ 1 000 000₮ буцаалт сүүлийн 10 минутад дахин нэвтэрсэн байхыг шаардана (403 fresh_sign_in_required {within_seconds: 600} — дахин нэвтэрч давтана).
  • Үйлдэл бүр апп-ын түүхэнд (/dev/audit) гишүүний нэртэй бичигдэнэ. Хүнийг хэзээ ч нэрлэхгүй — зөвхөн user_ref.

Алдаа

Үйлдэл Боломжит code
Бүгд 401 invalid_client / invalid_token, 403 insufficient_scope (required_scope) / ip_not_allowed, 404 (intent алга, өөр апп эсвэл өөр орчных), 429, 500 server_error
Үүсгэх 400 idempotency_key_required, invalid_request, validation_error, presentment_required; 409 idempotency_conflict, terminal_busy, terminal_disabled, sub_merchant_disabled, payouts_not_configured, agreement_required, business_profile_required, business_profile_rejected, business_restricted, business_profile_changed, business_limit_exceeded; 503 payments_unavailable, pay_url_unavailable
Жагсаах 400 invalid_status, invalid_cursor, invalid_limit
Capture 409 not_capturable, expired, payouts_not_configured; 422 payment_failed; 503 payments_unavailable
Cancel 409 in_progress, not_cancelable
Unclaim 409 not_unclaimable, terminal_busy

Код бүрийн утга, юу хийх, дахин оролдох эсэх — Алдааны код. Сүлжээний алдаа, 429, 5xx бол ижил Idempotency-Key-ээр дахин илгээх нь аюулгүй.