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

Буцаалт

Төлөгдсөн intent-ийн мөнгийг хүнд бүтнээр эсвэл хэсэгчлэн буцаана — нэг intent-д хэд ч удаа, нийлбэр нь төлсөн дүнгээс хэтрэхгүй, төлбөрөөс хойш 90 хоногийн дотор. Уншихад ~5 минут. Хэрэгтэй: refunds:write scope-той API түлхүүр (эсвэл mini app-ын хувьд хүний token), буцаалт бүрт шинэ Idempotency-Key.

Буцаах — POST /payment-intents/{id}/refunds

curl -sS https://geregeapp.mn/api/v1/miniapp/payment-intents/pi_4fQ2mX8kLp0aZr7TbN3cVw9E/refunds \
  -u "$GEREGE_CLIENT_ID:$GEREGE_CLIENT_SECRET" \
  -H "Idempotency-Key: refund-order-8812-1" \
  -H "Content-Type: application/json" \
  -d '{"amount": 10000, "reason": "supplier_failed"}'
Талбар Дүрэм
amount заавал биш. Орхивол хараахан буцаагүй бүх дүн (amount − refunded_amount). Өгвөл ≥ 1; өмнөх буцаалтуудтай нийлээд төлсөн дүнгээс хэтэрвэл 400 validation_error (path amount)
reason заавал биш, ≤ 255 тэмдэгт. Таны API, webhook, консолд харагдана; хүнд очих мэдэгдэлд хэзээ ч орохгүй
  • Idempotency-Key заавал (1–128 хэвлэгдэх ASCII). Key нь intent дотроо өвөрмөц: ижил intent-д ижил key + ижил бие → эхний буцаалт (200); ижил key өөр биетэй → 409 idempotency_conflict. Шинэ буцаалт = шинэ key.
  • Зөвхөн succeeded эсвэл partially_refunded intent-ийг буцаана (бусад → 409 not_refundable). requires_capture дээрх барьцааг буцаалт биш, cancel суллана.
  • Төлбөр амжилттай болсон (confirmed_at) мөчөөс хойш 90 хоног өнгөрсөн бол → 409 refund_window_expired.

Хариу ба төлөв

{
  "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"
}
HTTP Утга
201 шинэ буцаалт, дуусгавар төлөвтэй — succeeded эсвэл failed (failure_code-ийг үз)
202 pending — дэвтрийн хариу тодорхойгүй; платформ ижил key-ээр өөрөө дуусгана (минут тутмын reconcile job), үр дүн нь refund.succeeded / refund.failed webhook-оор ирнэ
200 давталт (ижил key + ижил бие) — тэр буцаалт одоогийн төлвөөрөө
status Утга
pending мөнгө буцаагдаж байна. Энэ дүн refunded_amount-д аль хэдийн тоологдсон (хоёр дахь буцаалт түүнийг давхар амлахгүй)
succeeded мөнгө хүний төлсөн дансанд буцсан
failed буцаагдсангүй; дүн нь refunded_amount-аас хасагдана — засаад шинэ key-ээр дахин буцааж болно

failed буцаалтын failure_code:

failure_code Утга, юу хийх
insufficient_funds Мөнгө хүлээн авсан payout данс тэр мөнгийг аль хэдийн зарцуулсан. Дансаа цэнэглээд дахин буцаа
settlement_shortfall Төлбөр тооцоонд орсон бөгөөд апп-ын клирингт буцаах хүрэлцээтэй мөнгө алга. Авлага (дараагийн тооцооноос суутгах) хараахан хийгдээгүй — оператор харна; операторт хандана уу
refund_rejected Дэвтэр өөр шалтгаанаар татгалзав

Амжилттай буцаалтын дараа intent-ийн status нь partially_refunded, бүх дүн буцсан бол refunded болно; refunded_amount өснө. Intent-д тусдаа payment_intent.* event ирэхгүй — refund.* event л.

Мөнгө хаанаас буцах

Буцаалт нь capture-ийн мөнгө очсон газраас гарна (буцаалт үүсэх үед тогтоно):

  • Тооцоо асаагүй апп (одоогийн бүх апп): мөнгө capture-ийн үед шууд апп-ын payout данс (эсвэл дэд худалдагчийн батлагдсан payout данс) руу орсон — буцаалт тэр данснаас гарна. Тиймээс дэд худалдагчийн payout-ыг батлахад эзэмшигч «буцаалтыг энэ данснаас хасна» гэсэн нөхцөлийг зөвшөөрдөг (Payout данс).
  • Тооцоо асаалттай апп: төлбөр апп-ын клирингт байна — буцаалт клирингээс гарна. Аль хэдийн тооцоонд орж payout руу шилжсэн төлбөрийн буцаалт дараагийн төлбөрүүдийн клирингээс гарч, дараагийн тооцоо түүнийг суутгана (Тооцоо). Клиринг хүрэлцэхгүй бол settlement_shortfall.

Шимтгэл

Тооцоо асаалттай апп-ын төлбөрийн шимтгэл буцаалтаар буцахгүй — операторын дүрэм refund_fee: true гэсэн үед л буцаасан дүнгийн хувиар буцна. Хэсэгчилсэн буцаалтуудын буцсан шимтгэлийн нийлбэр бүтэн буцаалтад шимтгэлтэй яг тэнцүү. Дэлгэрэнгүй: Шимтгэл.

Webhook ба мэдэгдэл

  • refund.succeeded / refund.failed — data.object нь дээрх Refund (Event-ийн лавлах).
  • Хүн Gerege App-д «буцаалт» мэдэгдэл авна — дүн ба апп-ын батлагдсан нэр л; таны reason хэзээ ч биш (апп-ын бичсэн текст платформын нэрийн доор фишингийн суваг болно).

Test горим

Test (livemode: false) буцаалт дэвтэр дуудахгүй, тэр дор нь succeeded (201) болж refund.succeeded илгээнэ. Ингэснээр бүтэн, хэсэгчилсэн, олон удаагийн буцаалт, давталт, idempotency_conflict-ийг мөнгөгүй туршина (Sandbox).

Самбараас (консолоос) буцаах: POST /api/v1/miniapp/dev/payments/{pi}/refunds {amount?, reason} — ижил хөдөлгүүр, ижил дүрэм, гэхдээ reason заавал, Idempotency-Key 1–100 тэмдэгт, эрх miniapp.payments; live ≥ 1 000 000₮ бол сүүлийн 10 минутад дахин нэвтэрсэн байх (403 fresh_sign_in_required). Консолын key таны backend-ийн key-тэй хэзээ ч давхцахгүй (тусдаа угтвартай). Буцаалт бүр хэн хүссэнээ (actor: app эсвэл dashboard:<гишүүн>) GET /api/v1/miniapp/dev/refunds-д харуулна — Payment intent-ын «Самбараас» хэсэг.

Алдаа

HTTP code Хэзээ
400 idempotency_key_required Idempotency-Key алга / буруу
403 insufficient_scope түлхүүрт refunds:write алга (required_scope)
400 validation_error amount < 1, нийлбэр нь төлсөн дүнгээс хэтэрнэ; reason > 255
400 invalid_request бие уншигдсангүй (JSON биш, мэдэгдэхгүй талбар, 4 KB-аас их)
404 — intent алга, эсвэл өөр апп / орчных
409 idempotency_conflict key энэ intent-д өөр биетэй ашиглагдсан
409 not_refundable intent succeeded / partially_refunded биш
409 refund_window_expired төлбөрөөс хойш 90 хоног өнгөрсөн
503 payments_unavailable live: core banking идэвхгүй

Бүтэн жагсаалт: Алдааны код.