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

Терминалаар test төлбөр (sticker, NFC, машин)

Юу хийх: байнгын терминал (QR sticker / NFC tag / вендинг машин) бүртгэж, түүнд test сагс уяж, «уншуулаад» барьцаалж, capture эсвэл cancel хийнэ. Хугацаа: ~10 минут, Quickstart-ийн §1–§5-ыг хийсэн бол. Мөнгө хөдлөхгүй.

qr-аас ялгаа: QR/sticker нь терминалыг л нэрлэнэ, дүнг хэзээ ч биш. Дүн нь тухайн мөчид терминалд уясан нэг нээлттэй intent-ээс ирнэ. Ойлголт: Худалдагч, дэд худалдагч, терминал.

1. Бэлтгэл

Quickstart-аас дараах хувьсагчид бэлэн байна: GEREGE, GEREGE_SESSION, APP_ID, GEREGE_CLIENT_ID / GEREGE_CLIENT_SECRET (test түлхүүр, payments:*), ажиллаж байгаа webhook.py ба tunnel.

2. Терминал бүртгэх (консолоос)

Терминал, дэд худалдагч нь хоёр орчинд нэг бүртгэл — тиймээс бичих нь зөвхөн live түлхүүр эсвэл консол; test түлхүүрээр POST /terminals → 403 live_only (алдагдсан sandbox secret бодит машиныг өөрчлөхгүй). Sandbox-д консолын API-аар бүртгэнэ:

curl -sS "$GEREGE/dev/apps/$APP_ID/terminals" \
  -H "Authorization: Bearer $GEREGE_SESSION" -H 'Content-Type: application/json' \
  -d '{"label": "Тест машин", "location": "Оффис, 3 давхар", "reference": "test-vm-1"}' | tee terminal.json | jq
export TERMINAL_ID=$(jq -r .id terminal.json)
export TERMINAL_CODE=$(jq -r .code terminal.json)
{
  "id": "4b8e2f0a-7c1d-4e3b-9a6f-2d5c8e1b7a90",
  "object": "terminal",
  "code": "7k2m9x4q3p",
  "url": "https://geregeapp.mn/pay/t/7k2m9x4q3p",
  "label": "Тест машин",
  "location": "Оффис, 3 давхар",
  "reference": "test-vm-1",
  "sub_merchant": null,
  "status": "active",
  "created_at": "2026-10-27T03:00:00Z",
  "updated_at": "2026-10-27T03:00:00Z"
}
  • reference — таны машины дугаар, апп дотор өвөрмөц: ижил reference-ээр дахин POST → тэр терминал 200.
  • code серверийнх, хэзээ ч өөрчлөгдөхгүй. url-ийг sticker-ийн QR ба NFC tag-ийн NDEF URI record болгоно.
  • label, location нь хүний төлөх цонхонд Face ID-аас өмнө гарна (бүртгэлээс — sticker дээрх бичгээс биш).

3. Sticker

Туршилтад QR-ээ өөрөө зурна: qrencode -o sticker.png "$(jq -r .url terminal.json)" (эсвэл дурын QR үүсгэгч). Серверээс хэвлэх хуудас (PDF: QR + нэр + код) — «удахгүй». NFC tag бичих, sticker сонгох заавар — «удахгүй» (Guides-ийн терминалын хуудас).

4. Сагс уях

Сагс бүрт таны сервер терминалд intent уяна — manual capture, богино capture_within:

curl -sS "$GEREGE/payment-intents" -u "$GEREGE_CLIENT_ID:$GEREGE_CLIENT_SECRET" \
  -H 'Content-Type: application/json' -H 'Idempotency-Key: basket-2001' \
  -d "{\"amount\": 2500, \"description\": \"Захиалга №2001\", \"reference\": \"basket-2001\",
       \"presentment\": \"terminal\", \"terminal_id\": \"$TERMINAL_ID\", \"expires_in\": 120,
       \"capture_method\": \"manual\", \"capture_within\": 120}" | tee basket.json | jq '{id, status, presentment, terminal, capture_within}'
export PI=$(jq -r .id basket.json)
{ "id": "pi_3Fk9Lm2Qw8Er5Ty7Ui1Op4As", "status": "requires_confirmation", "presentment": "terminal",
  "terminal": "4b8e2f0a-7c1d-4e3b-9a6f-2d5c8e1b7a90", "capture_within": 120 }

Терминалд claim хийгдээгүй нээлттэй intent орчин бүрт нэг. Хоёр дахь сагс:

curl -sS "$GEREGE/payment-intents" -u "$GEREGE_CLIENT_ID:$GEREGE_CLIENT_SECRET" \
  -H 'Content-Type: application/json' -H 'Idempotency-Key: basket-2002' \
  -d "{\"amount\": 1800, \"description\": \"Захиалга №2002\", \"presentment\": \"terminal\",
       \"terminal_id\": \"$TERMINAL_ID\", \"capture_method\": \"manual\"}" | jq
{ "error": "…", "code": "terminal_busy", "payment_intent": "pi_3Fk9Lm2Qw8Er5Ty7Ui1Op4As" }

Нэг sticker дээр хоёр сагс байвал хүн аль машины өмнө зогсож байгаагаа сонгох болно — тиймээс эхлээд хуучныг POST /payment-intents/{id}/cancel хийгээд дахин үүсгэнэ. Test сагс live сагсыг хаахгүй (орчин тусдаа).

5. Уншуулах (claim) ба төлөх

Утсаар уншуулах

Gerege App (iOS)-ын апп доторх QR товч ба «NFC уншуулах» нь терминалыг claim хийнэ — TestFlight 121-ээс хойшх build-д (test сагсыг «ТЕСТ» тэмдэгтэй). Камераар/ар талын NFC-ээр (Universal Link) ба App Clip — «удахгүй». Тэр build байхгүй бол апп-ын хийх дуудлагыг өөрийн session-ээр хийнэ. Терминалаар claim хийхэд апп-ын эзэн ба тест хэрэглэгчид test сагсыг эхэлж, бусад хүн зөвхөн live-ийг авна.

curl -sS "$GEREGE/payments/claim" -H "Authorization: Bearer $GEREGE_SESSION" \
  -H 'Content-Type: application/json' -d "{\"terminal\": \"$TERMINAL_CODE\"}" \
  | jq '{id, status, merchant, terminal, sub_merchant}'
{ "id": "pi_3Fk9Lm2Qw8Er5Ty7Ui1Op4As", "status": "requires_confirmation",
  "merchant": {"slug": "my-shop-test", "name": "Миний дэлгүүр", "icon_url": ""},
  "terminal": {"label": "Тест машин", "location": "Оффис, 3 давхар"}, "sub_merchant": null }

Webhook: payment_intent.claimed. Машин «Утсаараа баталгаажуулна уу» гэж харуулна. Төлөх:

export ACCOUNT=$(curl -sS https://geregeapp.mn/api/v1/bank/me/accounts \
  -H "Authorization: Bearer $GEREGE_SESSION" | jq -r '.accounts[0].id')
curl -sS -X POST "$GEREGE/payments/$PI/confirm" -H "Authorization: Bearer $GEREGE_SESSION" \
  -H 'Content-Type: application/json' -H "Idempotency-Key: confirm-$PI" \
  -d "{\"account_id\": \"$ACCOUNT\"}" | jq '{status}'

{"status": "requires_capture"} ба webhook payment_intent.requires_capture — машинд «гарга» гэх дохио.

6. Гарсан бол capture, гараагүй бол cancel

# бараа гарсан:
curl -sS -X POST "$GEREGE/payment-intents/$PI/capture" -u "$GEREGE_CLIENT_ID:$GEREGE_CLIENT_SECRET" | jq '{status}'
# эсвэл гараагүй:
curl -sS -X POST "$GEREGE/payment-intents/$PI/cancel"  -u "$GEREGE_CLIENT_ID:$GEREGE_CLIENT_SECRET" | jq '{status}'
Машины үр дүн Таны дуудлага Төлөв · webhook
Бүгд гарсан capture succeeded · payment_intent.succeeded
Юу ч гараагүй cancel canceled · payment_intent.canceled (барьцаа хүнд буцна — буцаалт биш)
Хэсэгчлэн capture бүтэн + POST …/refunds {"amount": <гараагүйн үнэ>} succeeded → partially_refunded (дүнтэй хэсэгчилсэн capture «удахгүй»)
Хариу ирээгүй (offline) — capture_within өнгөрөхөд expired · payment_intent.expired, failure_code: "authorization_expired", барьцаа өөрөө суллагдана

Хүн уншуулаад явчихвал баталгаажаагүй claim 90 с-д дуусч, дараагийн хүн авна; эсвэл таны сервер POST /payment-intents/{id}/unclaim хийнэ. Нэг хүн нэг дор 3-аас олон баталгаажаагүй claim барихгүй (429 too_many_open_claims).

7. Нийтийн харагдац

Sticker-ийн хаягийг компьютер дээр нээсэн хүнд үзүүлэх мэдээлэл (session-гүй, хувийн мэдээлэлгүй):

curl -sS "$GEREGE/pay/t/$TERMINAL_CODE" | jq
{ "object": "terminal", "merchant": {"name": "Миний дэлгүүр", "icon_url": ""},
  "terminal": {"code": "7k2m9x4q3p", "label": "Тест машин", "location": "Оффис, 3 давхар"},
  "pending": null }

pending нь зөвхөн live нээлттэй сагс (test сагс нийтэд хэзээ ч гарахгүй). Тайлбар, reference, хэн claim хийсэн — энд хэзээ ч байхгүй.

8. Дараа нь