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

Event-ийн лавлах

Платформын илгээдэг 12 event тус бүр: хэзээ, юуны дараа, хэн өдөөх, livemode юу байх, data.object ямар объект, жишээ payload. Handler бичихдээ хэрэгтэй event-ээ олоод уншина уу; эхлээд Idempotent handler-ийг.

Жишээ payload-ууд нь SDK-ийн тест векторуудын (sdk/testdata/vectors.json, платформын кодоос үүсгэсэн) бодит body-г уншихад эвтэйгээр задалсан хувилбар. Утсаар ирэх byte-д <, >, & нь \u003c, \u003e, \u0026 гэж бичигдэнэ — гарын үсгийг үргэлж түүхий byte дээр шалга (Гарын үсэг). «Жишээ» гэж тэмдэглэсэн гурав нь векторт байхгүй тул бодит бүтцээр бичсэн дүрслэл.

Event-ийн бүтэц

{
  "id": "evt_Vec0000000000000000000001",
  "type": "payment_intent.succeeded",
  "created_at": "2026-10-22T09:00:00Z",
  "livemode": true,
  "app": "esim",
  "data": { "object": { "…": "…" } }
}
Талбар Утга
id evt_ + 24 тэмдэгт base62. Давхардал арилгах түлхүүр; Gerege-Event-Id толгойтой ижил. Цаг дараалсан биш
type Доорх 12-ын нэг; minor хувилбарт шинэ төрөл нэмэгдэж болно — мэдэхгүйд 2xx
created_at Event үүссэн агшин, UTC, секундийн нарийвчлалтай (RFC 3339)
livemode true — бодит мөнгө / нийтэд нөлөөлсөн; false — test
app Апп-ын slug
data.object Өөрчлөгдсөн объект — API-ийн хариутай ижил хэлбэр (PaymentIntent, Refund, …)

Body нь өөрчлөлт commit хийгдэх үед нэг удаа үүснэ: retry-д ч, endpoint бүрт ч, хожуу ирсэн ч тэр агшны төлөвийг харуулна; Events API ч яг энэ body-г буцаана. Одоогийн төлөвийг GET-ээр уншина.

Төрлүүд товчоор

type Хэн өдөөх data.object
payment_intent.succeeded хүн (automatic) · таны backend (manual capture) PaymentIntent
payment_intent.requires_capture хүн PaymentIntent
payment_intent.canceled таны backend · хүн · job (app intent-ийн хугацаа) PaymentIntent
payment_intent.failed хүн (ledger татгалзсан) · таны backend (capture) PaymentIntent
payment_intent.claimed хүн PaymentIntent
payment_intent.expired job / уншилт (claimless intent-ийн хугацаа) PaymentIntent
refund.succeeded таны backend · job Refund
refund.failed таны backend · job Refund
app.status_changed хөгжүүлэгч · оператор AppStatus
consent.revoked хүн Consent
settlement.paid job (оператор асаасан тооцоо) Settlement
webhook.test хөгжүүлэгч (консол, /webhook-endpoints/{eid}/test) WebhookTest

Бүх объектын талбар бүрийн тайлбар: Объектууд.

payment_intent.succeeded

Мөнгө хүлээн авагч руу шилжсэн — бараа олгох дохио.

  • capture_method: "automatic": хүн Gerege App-д баталгаажуулж, барьцаа ба capture нэг дор амжилттай болсны дараа.
  • capture_method: "manual": таны backend POST /payment-intents/{id}/capture хийж, capture амжилттай болсны дараа.
  • status: "succeeded", confirmed_at бөглөгдсөн. Тооцоо асаалттай апп-д fee, net тоо; бусад үед null.
  • Test горимд ledger-гүй, шууд (дүнгийн сүүлийн хоёр оронгоор — Тест дүн).
{
  "id": "evt_Vec0000000000000000000001",
  "type": "payment_intent.succeeded",
  "created_at": "2026-10-22T09:00:00Z",
  "livemode": true,
  "app": "esim",
  "data": {
    "object": {
      "id": "pi_4fQ2mX8kLp0aZr7TbN3cVw9E",
      "object": "payment_intent",
      "status": "succeeded",
      "amount": 45000,
      "currency": "MNT",
      "description": "Japan 5GB · 15 хоног <roaming & data>",
      "reference": "order_8812",
      "user_ref": "u_9f2c4e7a1b3d5f60718293a4b5c6d7e8",
      "livemode": true,
      "capture_method": "automatic",
      "refunded_amount": 0,
      "fee": null,
      "net": null,
      "failure_code": null,
      "expires_at": "2026-10-22T09:10:00Z",
      "capture_before": null,
      "confirmed_at": "2026-10-22T08:59:55Z",
      "created_at": "2026-10-22T08:59:00Z",
      "metadata": { "package": "JC071" },
      "presentment": "app",
      "terminal": null,
      "sub_merchant": null,
      "return_url": null,
      "claimed_at": null,
      "min_age": null,
      "capture_within": null
    }
  }
}

payment_intent.requires_capture

manual intent-ийн мөнгө хүний дансанд барьцаалагдсан (hold) — одоо таны ээлж: бараа гарвал POST …/capture, гараагүй бол POST …/cancel (барьцааг суллана).

  • capture_before хүртэл шийдэхгүй бол барьцаа автоматаар суллагдаж intent expired болно (failure_code: "authorization_expired"; event нь доорх payment_intent.expired эсвэл app intent-д payment_intent.canceled).
  • capture_within нь баталгаажуулснаас хойших цонх секундээр (анхдагч 604 800 = 7 хоног).
{
  "id": "evt_Vec0000000000000000000002",
  "type": "payment_intent.requires_capture",
  "created_at": "2026-10-22T09:00:00Z",
  "livemode": false,
  "app": "esim",
  "data": {
    "object": {
      "id": "pi_8HkWq3ZsT0yLm5Nc2Vb7Xp1R",
      "object": "payment_intent",
      "status": "requires_capture",
      "amount": 45000,
      "currency": "MNT",
      "description": "Japan 5GB · 15 хоног <roaming & data>",
      "reference": null,
      "user_ref": "u_9f2c4e7a1b3d5f60718293a4b5c6d7e8",
      "livemode": false,
      "capture_method": "manual",
      "refunded_amount": 0,
      "fee": null,
      "net": null,
      "failure_code": null,
      "expires_at": "2026-10-22T09:10:00Z",
      "capture_before": "2026-10-29T09:00:00Z",
      "confirmed_at": null,
      "created_at": "2026-10-22T08:59:00Z",
      "metadata": {},
      "presentment": "app",
      "terminal": null,
      "sub_merchant": null,
      "return_url": null,
      "claimed_at": null,
      "min_age": null,
      "capture_within": 604800
    }
  }
}

payment_intent.canceled

Intent цуцлагдсан — мөнгө хөдлөөгүй (эсвэл барьцаа суллагдсан). data.object.status-аас аль нь болохыг ял:

status Шалтгаан
canceled Таны backend POST …/cancel хийсэн (төлөгдөөгүй intent, эсвэл manual барьцааг суллах); хүн төлөх цонхыг цуцалсан; test горимд дүн …02
expired Зөвхөн app presentment (mini app): expires_at өнгөрсөн, эсвэл manual барьцааны capture_before өнгөрсөн (failure_code: "authorization_expired")

app intent-ийн хугацаа дуусахад payment_intent.canceled ирдэг нь OMAS-д бичсэн backend-ууд эвдрэхгүйн тулд; claimless (qr/redirect/terminal) intent-д оронд нь payment_intent.expired ирнэ.

Жишээ (дүрслэл — mini app-ын intent хугацаандаа төлөгдөөгүй):

{
  "id": "evt_3Jd8sKq0Lw5Nz2Xc7Vb9Mp1R",
  "type": "payment_intent.canceled",
  "created_at": "2026-10-22T09:10:20Z",
  "livemode": true,
  "app": "esim",
  "data": {
    "object": {
      "id": "pi_Ws5Tq8Lm2Kx0Pz7Rb4Nc9Vd1E",
      "object": "payment_intent",
      "status": "expired",
      "amount": 45000,
      "currency": "MNT",
      "description": "Japan 5GB · 15 хоног",
      "reference": "order_8813",
      "user_ref": "u_9f2c4e7a1b3d5f60718293a4b5c6d7e8",
      "livemode": true,
      "capture_method": "automatic",
      "refunded_amount": 0,
      "fee": null,
      "net": null,
      "failure_code": null,
      "expires_at": "2026-10-22T09:10:00Z",
      "capture_before": null,
      "confirmed_at": null,
      "created_at": "2026-10-22T09:00:00Z",
      "metadata": {},
      "presentment": "app",
      "terminal": null,
      "sub_merchant": null,
      "return_url": null,
      "claimed_at": null,
      "min_age": null,
      "capture_within": null
    }
  }
}

payment_intent.failed

Төлбөрийн оролдлого амжилтгүй — failure_code шалтгааныг хэлнэ (Алдааны код). failed intent дахин төлөгдөж болно (OMAS): хүн өөр данс сонгоод дахин баталгаажуулж болно.

failure_code Хэзээ
insufficient_funds, limit_exceeded, hold_mismatch, payment_failed Хүн баталгаажуулахад ledger барьцааг татгалзсан
merchant_unavailable Барьцаа тавигдсан ч апп хооронд нь түдгэлзсэн эсвэл payout данс алга болсон — барьцаа суллагдсан
capture_failed Capture-ийг ledger татгалзсан — барьцаа суллагдсан
test_declined Test горим, дүн …01

Хүн өөрийнх биш / хаалттай данс сонгосон (account_unavailable) үед event илгээгдэхгүй: intent requires_confirmation хэвээр үлдэж, хүн өөр данс сонгоно.

Жишээ (дүрслэл — test горимд дүн …01):

{
  "id": "evt_Lq2Wm8Zr5Tn0Xs3Kd7Pb9Vc1F",
  "type": "payment_intent.failed",
  "created_at": "2026-10-22T09:01:00Z",
  "livemode": false,
  "app": "esim",
  "data": {
    "object": {
      "id": "pi_Tz6Hc1Nq8Lv3Rw0Km5Xb2Pd9G",
      "object": "payment_intent",
      "status": "failed",
      "amount": 45001,
      "currency": "MNT",
      "description": "Japan 5GB · 15 хоног",
      "reference": "order_test_1",
      "user_ref": "u_9f2c4e7a1b3d5f60718293a4b5c6d7e8",
      "livemode": false,
      "capture_method": "automatic",
      "refunded_amount": 0,
      "fee": null,
      "net": null,
      "failure_code": "test_declined",
      "expires_at": "2026-10-22T09:10:00Z",
      "capture_before": null,
      "confirmed_at": null,
      "created_at": "2026-10-22T09:00:00Z",
      "metadata": {},
      "presentment": "app",
      "terminal": null,
      "sub_merchant": null,
      "return_url": null,
      "claimed_at": null,
      "min_age": null,
      "capture_within": null
    }
  }
}

payment_intent.claimed

Хүн claimless (qr, redirect, terminal) intent-ийг Gerege App-д өөртөө авсан — QR уншуулсан, холбоос нээсэн, sticker-т утсаа хүргэсэн. Дэлгэц, машин дээрээ «Утсаараа баталгаажуулна уу» гэж харуулах дохио; мөнгө хараахан хөдлөөгүй (status: "requires_confirmation").

  • user_ref (энэ төлбөрт зориулсан санамсаргүй u_…) ба claimed_at бөглөгдсөн.
  • Баталгаажуулаагүй claim 90 секундэд хүчингүй болдог: тэр хугацааны дараа өөр хүн claim хийвэл шинэ user_ref-тэй дахин payment_intent.claimed ирнэ. Нэг хүн дахин claim хийх нь шинэ event үүсгэхгүй.
  • Таны backend POST …/unclaim хийхэд event илгээгдэхгүй (intent дахин user_ref: null).
{
  "id": "evt_Vec0000000000000000000008",
  "type": "payment_intent.claimed",
  "created_at": "2026-10-22T09:00:00Z",
  "livemode": true,
  "app": "vending",
  "data": {
    "object": {
      "id": "pi_T3rm1nalVend0000000000Ab",
      "object": "payment_intent",
      "status": "requires_confirmation",
      "amount": 3500,
      "currency": "MNT",
      "description": "Ус 0.5л",
      "reference": "basket_77120",
      "user_ref": "u_6b1e0c9d2f4a8e7c3b5d1f0a9e8c7b6d",
      "livemode": true,
      "capture_method": "manual",
      "refunded_amount": 0,
      "fee": null,
      "net": null,
      "failure_code": null,
      "expires_at": "2026-10-22T09:05:00Z",
      "capture_before": null,
      "confirmed_at": null,
      "created_at": "2026-10-22T08:59:00Z",
      "metadata": {},
      "presentment": "terminal",
      "terminal": "4b8e6c1a-2d3f-4a5b-9c7d-0e1f2a3b4c5d",
      "sub_merchant": "9a7c5e3b-1d2f-4e6a-8b0c-7d9e1f3a5b7c",
      "return_url": null,
      "claimed_at": "2026-10-22T08:59:40Z",
      "min_age": null,
      "capture_within": 120
    }
  }
}

payment_intent.expired

Claimless (qr, redirect, terminal) intent-ийн хугацаа дууссан — claim хийгдсэн ч, хийгдээгүй ч:

  • expires_at хүртэл хэн ч төлөөгүй (failure_code: null; баталгаажуулалт явж байхад дууссан бол "expired").
  • Manual барьцааны capture_before өнгөрсөн, эсвэл capture хийхэд барьцаа аль хэдийн дууссан — мөнгө хүнд буцсан (failure_code: "authorization_expired").

Хугацааг минут тутмын job, эсвэл тэр intent-ийг уншсан хүсэлт (GET) бүртгэнэ — expires_at-аас хойш хамгийн ихдээ нэг минутын дотор. app intent-д энэ event биш, payment_intent.canceled (status: "expired").

{
  "id": "evt_Vec0000000000000000000009",
  "type": "payment_intent.expired",
  "created_at": "2026-10-22T09:00:00Z",
  "livemode": true,
  "app": "kiosk",
  "data": {
    "object": {
      "id": "pi_QrK1osk00000000000000Cd2",
      "object": "payment_intent",
      "status": "expired",
      "amount": 12000,
      "currency": "MNT",
      "description": "Хураамж",
      "reference": null,
      "user_ref": null,
      "livemode": true,
      "capture_method": "automatic",
      "refunded_amount": 0,
      "fee": null,
      "net": null,
      "failure_code": null,
      "expires_at": "2026-10-22T08:59:59Z",
      "capture_before": null,
      "confirmed_at": null,
      "created_at": "2026-10-22T08:50:00Z",
      "metadata": {},
      "presentment": "qr",
      "terminal": null,
      "sub_merchant": null,
      "return_url": null,
      "claimed_at": null,
      "min_age": null,
      "capture_within": null
    }
  }
}

refund.succeeded

Буцаалт хүний дансанд орсон. POST …/refunds нь ихэвчлэн тэр дор нь succeeded-ээр хариулна (event ч тэр агшинд); ledger хариу өгөөгүй бол 202 pending, дараа нь job дуусгаж event илгээнэ. Test горимд шууд.

Intent-ийн refunded_amount ба status (partially_refunded / refunded) өөрчлөгдөнө, гэхдээ payment_intent.* event илгээгдэхгүй — зөвхөн refund.*. Intent-ийн шинэ төлөвийг GET-ээр уншина.

{
  "id": "evt_Vec0000000000000000000003",
  "type": "refund.succeeded",
  "created_at": "2026-10-22T09:00:00Z",
  "livemode": true,
  "app": "esim",
  "data": {
    "object": {
      "id": "re_2kLm9Pq4Rs6Tu8Vw0Xy1Za3B",
      "object": "refund",
      "intent_id": "pi_4fQ2mX8kLp0aZr7TbN3cVw9E",
      "amount": 10000,
      "status": "succeeded",
      "reason": "supplier_failed",
      "failure_code": null,
      "livemode": true,
      "created_at": "2026-10-22T09:00:00Z"
    }
  }
}

refund.failed

Буцаалтыг татгалзсан — мөнгө хөдлөөгүй, intent-ийн refunded_amount өөрчлөгдөөгүй. failure_code:

failure_code Утга, юу хийх
insufficient_funds Таны payout дансанд төлбөрийн мөнгө үлдээгүй (зарцуулсан) — дансаа цэнэглээд шинэ Idempotency-Key-ээр дахин
settlement_shortfall Тооцоо асаалттай апп: клирингийн үлдэгдэл буцаалтад хүрэлцэхгүй (мөнгө аль хэдийн тооцоогоор шилжсэн) — оператортой холбогд
refund_rejected Ledger бусад шалтгаанаар татгалзсан

Жишээ (дүрслэл):

{
  "id": "evt_Pn4Xq9Lr2Wm7Tz0Kc5Hb8Vd3J",
  "type": "refund.failed",
  "created_at": "2026-10-22T11:20:00Z",
  "livemode": true,
  "app": "shop",
  "data": {
    "object": {
      "id": "re_Hk7Ns2Qw9Lm4Xt0Rz5Pc8Vb1D",
      "object": "refund",
      "intent_id": "pi_Mx3Lq8Tn1Wz6Rk0Pc4Hs9Vb2F",
      "amount": 45000,
      "status": "failed",
      "reason": "customer_request",
      "failure_code": "settlement_shortfall",
      "livemode": true,
      "created_at": "2026-10-22T11:20:00Z"
    }
  }
}

app.status_changed

Апп-ын status эсвэл review_status өөрчлөгдсөн: review-д илгээх, батлах, татгалзах, түдгэлзүүлэх, maintenance асаах/унтраах. Өөрчлөлттэй нэг transaction-д бичигдэнэ.

  • livemode: true — апп өөрчлөлтийн өмнө эсвэл дараа нийтлэгдсэн (батлагдсан, active/maintenance) байсан — олон нийтэд нөлөөлнө; false — зөвхөн хөгжүүлэгч, тест хэрэглэгчдэд хамаатай (жишээ нь ноорог review-д орсон).
  • review_note — татгалзах, түдгэлзүүлэх үеийн операторын тэмдэглэл; бусад үед null байж болно.
  • Шууд оролдлогогүй — job-оор 1 минутын дотор (Хүргэлт).
{
  "id": "evt_Vec0000000000000000000004",
  "type": "app.status_changed",
  "created_at": "2026-10-22T09:00:00Z",
  "livemode": true,
  "app": "esim",
  "data": {
    "object": {
      "object": "app",
      "slug": "esim",
      "status": "active",
      "review_status": "suspended",
      "previous_status": "active",
      "previous_review_status": "approved",
      "review_note": "Хэрэглэгчийн гомдол шалгагдаж байна"
    }
  }
}

consent.revoked

Хүн Gerege App-д таны mini app-ыг салгасан (зөвшөөрлөө цуцалсан). Тэр хүний бүх access / refresh token аль хэдийн хүчингүй — тэдгээрээр хийх дуудлага 401 invalid_token. Таны талд тэр хүний session-ийг хааж, user_ref-ээр холбосон мэдээллийг өөрийн нууцлалын бодлогоор устга.

  • user_ref — тэр хүний pairwise sub (апп-д тогтмол нэр).
  • livemode: true — апп нийтлэгдсэн; false — зөвхөн хөгжүүлэгч, тест хэрэглэгчид нээж чаддаг үед.
  • Хамгийн боломжоор (best effort): салгалт өөрөө үргэлж хэрэгжинэ; event бичигдэж чадаагүй ховор тохиолдолд ирэхгүй байж болно. Шууд оролдлогогүй — job-оор.
{
  "id": "evt_Vec0000000000000000000005",
  "type": "consent.revoked",
  "created_at": "2026-10-22T09:00:00Z",
  "livemode": true,
  "app": "esim",
  "data": {
    "object": {
      "object": "consent",
      "revoked_at": "2026-10-22T09:00:00Z",
      "user_ref": "u_78a08c3afb344c1b4b765aead45e1693"
    }
  }
}

settlement.paid

Тооцоо таны payout данс руу шилжсэн (Тооцоо). Оператор таны апп-д тооцоог асаасан үед л илгээгдэнэ; асаагаагүй апп-д capture шууд payout данс руу ордог тул энэ event ирэхгүй.

  • Үргэлж livemode: true.
  • paid 0 байж болно (бүгд шимтгэл, эсвэл дараагийн тооцоо руу шилжсэн). sub_merchant — дэд худалдагчийн тооцоо бол түүний id, апп-ын өөрийнх бол null.
  • Тэнцэтгэл: net = gross − refunds − fee, paid = net − holdback + released − reserve_refunds − recovered.
{
  "id": "evt_Vec0000000000000000000007",
  "type": "settlement.paid",
  "created_at": "2026-10-22T09:00:00Z",
  "livemode": true,
  "app": "esim",
  "data": {
    "object": {
      "id": "st_7QwErTy2UiOp4AsDf6GhJk8L",
      "object": "settlement",
      "status": "paid",
      "cutoff": "2026-10-21T16:00:00Z",
      "gross": 100000,
      "refunds": 5000,
      "reserve_refunds": 0,
      "fee": 2000,
      "net": 93000,
      "holdback": 0,
      "released": 0,
      "recovered": 0,
      "paid": 93000,
      "intent_count": 3,
      "destination_masked": "******0042",
      "holdback_release_at": null,
      "livemode": true,
      "created_at": "2026-10-22T08:58:00Z",
      "paid_at": "2026-10-22T08:59:00Z",
      "refund_count": 1,
      "fee_assessed": 2000,
      "fee_returned": 0,
      "fee_carried_in": 0,
      "fee_carried_out": 0,
      "clearing_carried_in": 0,
      "clearing_carried_out": 0,
      "failure_code": null,
      "sub_merchant": null
    }
  }
}

webhook.test

Хөгжүүлэгчийн консолын «Тест илгээх» товч (POST /dev/apps/{id}/webhook/test, …/webhook-endpoints/{eid}/test) эсвэл таны серверийн POST /webhook-endpoints/{eid}/test — бодит event-ийн замаар (ижил гарын үсэг, outbox, retry), гэхдээ зөвхөн сонгосон нэг endpoint руу (шүүлтүүр, төлөвөөс үл хамааран), live endpoint руу ч үргэлж livemode: false. event_types шүүлтүүрт нэрлэж болохгүй, Events API-д гарахгүй. OMAS-ын ping-ийн оронд. Үүгээр юу ч бүү хий — 2xx буцаахад л хангалттай.

{
  "id": "evt_Vec0000000000000000000006",
  "type": "webhook.test",
  "created_at": "2026-10-22T09:00:00Z",
  "livemode": false,
  "app": "esim",
  "data": {
    "object": {
      "message": "Gerege App webhook test",
      "object": "webhook_test"
    }
  }
}

Нийцэл

  • Нэмэгдэх, хасагдахгүй: minor хувилбар шинэ event төрөл, объектод шинэ талбар нэмж болно. Мэдэхгүй type-д 2xx, мэдэхгүй талбарыг үл тоомсорло (JSON-оо strict schema-аар бүү татгалз).
  • payment_intent.* event бүрийн data.object-д presentment, terminal, sub_merchant байна (app intent-д "app", null, null).
  • OMAS-д бичсэн backend: OpenPay-Signature / OpenPay-Event-Id толгой ижил утгатай ирнэ; app intent-ийн хугацаа дуусахад OMAS-ын адил payment_intent.canceled.