Касс, POS, kiosk — дэлгэц дээрх QR¶
Энэ хуудсаар
Юу хийх: касс/kiosk-ийн дэлгэцэнд сагс бүрийн динамик QR гаргаж, хүн Gerege App-аар уншуулж төлөхөд баримтаа хэвлэх. Хугацаа: ~2 цаг. Хэрэгтэй: худалдагч апп ба test credential (бүртгэл), касс ↔ таны backend холболт, webhook URL.
Сагс бүрт шинэ qr intent → pay_url-ийг QR болгож дэлгэцэнд → хүн Gerege App-ын QR товчоор уншуулна →
payment_intent.claimed («Утсаараа баталгаажуулна уу») → payment_intent.succeeded (баримт). Байнгын, хэвлэсэн
QR/NFC (машин, ширээ, хаалга) хэрэгтэй бол — терминал ба sticker.
1. Урсгал¶
Касс / kiosk Таны backend Gerege Хүн (Gerege App)
│ сагс хаагдлаа │ │ │
│── POST /sales ───────▶│ POST /payment-intents │ │
│ │ {presentment:"qr", …} │ │
│ │────────────────────────────▶│ │
│◀── {qr: pay_url, 3500}│◀──── {id, pay_url} ─────────│ │
│ QR + «3 500₮» дэлгэцэнд │◀──── QR уншуулна (claim) ─│
│ │◀── payment_intent.claimed ──│ │
│◀── «Утсаараа баталгаажуулна уу» │◀──── Face ID → төлнө ─────│
│ │◀── payment_intent.succeeded │ │
│◀── «Төлөгдлөө» → баримт хэвлэх │ │
Касс secret барихгүй: зөвхөн таны backend-тэй (өөрийн token, TLS) ярьж, pay_url-ийг харуулна.
2. Сагс бүрт intent¶
curl https://geregeapp.mn/api/v1/miniapp/payment-intents \
-u "$GEREGE_CLIENT_ID:$GEREGE_CLIENT_SECRET" \
-H 'Content-Type: application/json' -H 'Idempotency-Key: sale-0042-1188' \
-d '{"amount": 3500, "description": "Касс №2 · баримт 1188", "reference": "sale-0042-1188",
"expires_in": 180, "presentment": "qr", "metadata": {"branch": "sbd-1", "till": "2"}}'
expires_inбогино (120–300 с): дэлгэцэн дээр үлдсэн QR-ийг дараагийн хүн уншуулж болохгүй.Idempotency-Key= таны борлуулалтын дугаар: касс → backend хүсэлт давтагдсан ч нэг intent, нэг QR.capture_methodанхдагчautomatic— хүн баталмагц мөнгө авна. Бараа дараа нь гардаг kiosk (хоол, түлш, locker) болmanual+capture_within— вендинг-ийн загвар.descriptionхүний хэтэвчний хуулгад харагдана (Gerege App-ын төлөх цонх харуулахгүй): дэлгүүр/касс/баримтын дугаар — хувийн мэдээлэл биш.- Олон эзэмшигчтэй (франчайз, маркетплейс) бол
sub_merchant_id(дэд худалдагч).
3. QR зурах¶
- Агуулга нь яг
pay_url(https://geregeapp.mn/pay/<24 тэмдэгт>) — өөрчлөхгүй, богиносгохгүй, өөр домэйноор дамжуулахгүй. - Error correction M (эсвэл Q), чимээгүй бүс (quiet zone) ≥ 4 модуль, цагаан дэвсгэр дээр бараан, урвуу өнгөгүй.
- Дэлгэцэнд ≥ 3 см талтай (30–40 см-ээс уншигдана); гэрэлтэй дэлгэцэнд гялбаагүй.
- QR-ийн дэргэд: дүн, таны дэлгүүрийн нэр, «Gerege App-аар уншуулна уу», үлдсэн хугацаа.
- QR-ийг баримт дээр хэвлэж «дараа төл» гэхгүй — тэр нь нэхэмжлэх-ийн урсгал.
4. Үр дүнг хүлээх — webhook эсвэл polling¶
Хоёр замыг хослуул: webhook нь хурдан (event commit хийгдмэгц шууд нэг удаа илгээгдэнэ), polling нь webhook хоцорсон/алдагдсан үеийн нөөц.
Event / status |
Касс |
|---|---|
payment_intent.claimed (claimed_at орсон) |
«Утсаараа баталгаажуулна уу» |
payment_intent.succeeded / succeeded |
«Төлөгдлөө» → баримт хэвлэх, сагс хаах |
payment_intent.failed / failed |
«Амжилтгүй — дахин оролдоно уу» (хүн хугацаа дуустал дахин төлж болно) |
payment_intent.expired / expired |
QR-ийг арилгаж «Дахин эхлүүлэх» |
payment_intent.canceled / canceled |
хүн апп дотроо татгалзсан эсвэл та цуцалсан |
Касс таны backend-ийг 1–2 с тутам асууна (Gerege-ийг биш — касс secret барихгүй); backend нь webhook-оор
мэдсэнээ хариулж, 10 с-ээс илүү мэдээгүй бол GET /payment-intents/{id}-оор шалгана. Server API-ийн хязгаар
минутад 600 хүсэлт (burst 120) — касс бүр Gerege-ийг шууд poll хийвэл хурдан дуусна.
5. Цуцлах, сагс өөрчлөх¶
- Кассчин цуцалсан / сагс өөрчлөгдсөн → хуучин intent-ийг
POST /payment-intents/{id}/cancel, шинэ сагсанд шинэ intent (шинэIdempotency-Key). Хүн төлж байх үед (processing)409 in_progress— хэдэн секунд хүлээгээд дахин уншина. - Хүн уншуулаад явчихсан — баталгаажаагүй claim 90 секундэд өөрөө дуусна, эсвэл
POST /payment-intents/{id}/unclaim-аар шууд суллана (дараагийн хүн уншуулж болно). Нэг хүн нэг дор ≤ 3 баталгаажаагүй claim барина. - Буруу хүн уншуулсан (ард зогсож байсан хүн) — мөн
unclaim.pay_url-ийг QR-ээс өөр хаана ч гаргахгүй. - Төлөгдсөнийг буцаах —
POST /payment-intents/{id}/refunds(бүтэн эсвэл хэсэгчлэн, 90 хоногийн дотор).
6. Касс эсвэл сүлжээ тасарвал¶
| Юу болсон | Юу хийх |
|---|---|
| Касс → backend тасарсан | касс борлуулалтаа локал журналд үлдээж, холбогдмогц sale дугаараар backend-ээс асууна |
| Backend → Gerege timeout | ижил Idempotency-Key-ээр давтах — ижил intent, ижил pay_url |
| Webhook ирээгүй | backend GET /payment-intents/{id} (эсвэл ?reference=) — polling нь нөөц |
| Хүн төлсөн ч касс «хүлээж» гацсан | backend-ээс reference-ээр дахин асуу; succeeded бол баримт хэвлэ |
| Касс өдрийн хаалт | GET /payment-intents?reference=… эсвэл тулгалт |
7. Олон касс, салбар¶
metadata(≤ 20 түлхүүр) —branch,till,cashierзэрэг таны ID; webhook баGET-д буцаж ирнэ, хүнд харагдахгүй.terminal_idнь зөвхөнterminalpresentment-д (qr-д өгвөл400 validation_error). Касс бүрийн байнгын QR/NFC хэрэгтэй бол кассыг терминал болгож бүртгэнэ — терминал ба sticker.- Салбар/касс бүрээр задалсан тайлан — «удахгүй» (самбар). Одоогоор
metadata-аар өөрөө нэгтгэнэ.
8. Код¶
Node (@gerege/miniapp-node) — QR зурах нь таны сонгосон сан (жишээ нь qrcode):
const pi = await gerege.paymentIntents.create(
{ presentment: "qr", amount: sale.total, description: `Касс №${sale.till} · баримт ${sale.no}`,
reference: sale.id, expires_in: 180, metadata: { branch: sale.branch, till: String(sale.till) } },
{ idempotencyKey: sale.id },
);
till.showQR(pi.pay_url, sale.total); // зөвхөн дэлгэцэнд; лог-д бичихгүй
Go (sdk/go):
pi, err := c.PaymentIntents.Create(ctx, sale.ID, miniapp.CreatePaymentIntentParams{
Amount: sale.Total, Description: fmt.Sprintf("Касс №%d · баримт %s", sale.Till, sale.No),
Reference: sale.ID, ExpiresIn: 180, Presentment: miniapp.PresentmentQR,
Metadata: map[string]string{"branch": sale.Branch},
})
Webhook хүлээн авагчийн бүтэн жишээ: examples/node/src/webhook.ts, examples/go/cmd/webhook/main.go.
9. Хэрэгжилтийн төлөв¶
| Хэсэг | Төлөв |
|---|---|
qr intent, pay_url, claim (90 с, ≤ 3), unclaim, шууд webhook |
✓ |
Gerege App iOS-ийн QR уншигч (баруун доод QR товч) /pay/<token>-ийг таних → claim → төлөх цонх |
✓ |
| Салбар, кассаар задалсан тайлан | «удахгүй» |