Correctness
Amounts are whole tugrik
Amounts are whole tugrik
1500. Check every path that builds an amount — especially any code ported from an
integration that used minor units. A ×100 error here is a customer charged a hundred times
over.The field is amountMNT
The field is amountMNT
MNT. amountMnt is rejected with 400 invalid request body. Verify against a
live call, not against your own type definitions.You store the transaction id at the moment of sale
You store the transaction id at the moment of sale
The QR string is passed through unmodified
The QR string is passed through unmodified
Safety
Every charge carries an Idempotency-Key
Every charge carries an Idempotency-Key
A timeout or 5xx is never treated as a decline
A timeout or 5xx is never treated as a decline
GET /v1/transactions — never silently fail the sale, and never silently recharge.409 is handled as 'already done', not as an error
409 is handled as 'already done', not as an error
qr token already used → verify before recharging. transaction already reversed → mark it
refunded and show success.Status is checked before parsing JSON
Status is checked before parsing JSON
422 returns JSON. Calling .json() on a 409 throws and turns a handled decline into
an unhandled crash at the till.Credentials are not in the client
Credentials are not in the client
Cashier experience
Every 422 code maps to a message a cashier can act on
Every 422 code maps to a message a cashier can act on
error text and do not show a
generic “declined” — the difference between “not enough balance” and “can’t be used today”
decides what the customer does next.Token errors offer Rescan, not failure
Token errors offer Rescan, not failure
TOKEN_EXPIRED and TOKEN_INVALID are recoverable at the counter. Give the cashier a button.401 tells staff to re-pair, not to retry
401 tells staff to re-pair, not to retry
Nothing blocks on the VAT receipt
Nothing blocks on the VAT receipt
Operations
You log the transaction id, status, and 422 code on every charge
You log the transaction id, status, and 422 code on every charge
Retries back off exponentially with jitter
Retries back off exponentially with jitter
Device clocks are synchronised
Device clocks are synchronised
Day boundaries are computed in Asia/Ulaanbaatar
Day boundaries are computed in Asia/Ulaanbaatar
The final rehearsal
Run this end to end on the real hardware, with a real card, before you open:Charge ₮100
201, vatStatus: "pending". Save the id.Rescan the same code
409 qr token already used. Your POS should offer to verify, not to charge again.Replay the original request with the same Idempotency-Key
201 with the same id. The customer was charged once.Pull the balance up in the customer's pass
Wait a few minutes, then list transactions
vatStatus: "issued" with a receipt number in vatReceiptID.Reverse it
204. Then reverse it again: 409, handled as success.Check the pass and the status
vatStatus moves to voided.Getting help
Email info@vibepay.mn with the transactionid and the approximate time.
That is enough for us to trace any payment end to end.
