> ## Documentation Index
> Fetch the complete documentation index at: https://docs.vibepay.mn/llms.txt
> Use this file to discover all available pages before exploring further.

# Going live

> The checklist to work through before your integration takes real customers' money.

Work through this before the first real customer stands in front of the till.

## Correctness

<AccordionGroup>
  <Accordion title="Amounts are whole tugrik" icon="coins">
    ₮1,500 is `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.
  </Accordion>

  <Accordion title="The field is amountMNT" icon="font">
    Uppercase `MNT`. `amountMnt` is rejected with `400 invalid request body`. Verify against a
    live call, not against your own type definitions.
  </Accordion>

  <Accordion title="You store the transaction id at the moment of sale" icon="database">
    Written to your own order record before you show the cashier a success screen. Without it a
    refund becomes a manual search.
  </Accordion>

  <Accordion title="The QR string is passed through unmodified" icon="qrcode">
    No trimming, no prefix stripping, no case changes. If your scanner appends a newline or a
    configured prefix, strip that in your code before sending.
  </Accordion>
</AccordionGroup>

## Safety

<AccordionGroup>
  <Accordion title="Every charge carries an Idempotency-Key" icon="fingerprint">
    Generated **once per sale attempt** and reused across every retry of that attempt. Confirm by
    forcing a timeout and checking the customer was charged once.
  </Accordion>

  <Accordion title="A timeout or 5xx is never treated as a decline" icon="clock">
    The charge may have committed. Retry idempotently or reconcile against
    `GET /v1/transactions` — never silently fail the sale, and never silently recharge.
  </Accordion>

  <Accordion title="409 is handled as 'already done', not as an error" icon="circle-exclamation">
    `qr token already used` → verify before recharging. `transaction already reversed` → mark it
    refunded and show success.
  </Accordion>

  <Accordion title="Status is checked before parsing JSON" icon="code">
    Only `422` returns JSON. Calling `.json()` on a `409` throws and turns a handled decline into
    an unhandled crash at the till.
  </Accordion>

  <Accordion title="Credentials are not in the client" icon="shield-halved">
    Not in a shipped mobile binary, not in a browser bundle, not in git. One terminal per till, so
    a lost device is one rotation rather than a store-wide re-credential.
  </Accordion>
</AccordionGroup>

## Cashier experience

<AccordionGroup>
  <Accordion title="Every 422 code maps to a message a cashier can act on" icon="comment">
    All ten of them, in Mongolian. Do not show the raw English `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.
  </Accordion>

  <Accordion title="Token errors offer Rescan, not failure" icon="rotate">
    `TOKEN_EXPIRED` and `TOKEN_INVALID` are recoverable at the counter. Give the cashier a button.
  </Accordion>

  <Accordion title="401 tells staff to re-pair, not to retry" icon="key">
    A dead credential cannot be retried into working.
  </Accordion>

  <Accordion title="Nothing blocks on the VAT receipt" icon="receipt">
    It does not exist at the moment of sale and never will. If your receipt template has a slot
    for it, leave it blank or fill it in later.
  </Accordion>
</AccordionGroup>

## Operations

<AccordionGroup>
  <Accordion title="You log the transaction id, status, and 422 code on every charge" icon="file-lines">
    Never log the QR string or the terminal password. The id is what support will ask for.
  </Accordion>

  <Accordion title="Retries back off exponentially with jitter" icon="chart-line">
    A tight retry loop against a shared rate limit turns one slow request into an outage.
  </Accordion>

  <Accordion title="Device clocks are synchronised" icon="clock">
    Skewed clocks produce confusing history windows and spurious token-age complaints.
  </Accordion>

  <Accordion title="Day boundaries are computed in Asia/Ulaanbaatar" icon="globe">
    Spend rules and daily limits reset at Mongolian midnight; the API returns UTC timestamps.
    Convert, do not assume.
  </Accordion>
</AccordionGroup>

## The final rehearsal

Run this end to end on the real hardware, with a real card, before you open:

<Steps>
  <Step title="Charge ₮100">
    `201`, `vatStatus: "pending"`. Save the id.
  </Step>

  <Step title="Rescan the same code">
    `409 qr token already used`. Your POS should offer to verify, not to charge again.
  </Step>

  <Step title="Replay the original request with the same Idempotency-Key">
    `201` with the **same** `id`. The customer was charged once.
  </Step>

  <Step title="Pull the balance up in the customer's pass">
    ₮100 gone, once.
  </Step>

  <Step title="Wait a few minutes, then list transactions">
    `vatStatus: "issued"` with a receipt number in `vatReceiptID`.
  </Step>

  <Step title="Reverse it">
    `204`. Then reverse it again: `409`, handled as success.
  </Step>

  <Step title="Check the pass and the status">
    ₮100 returned. `vatStatus` moves to `voided`.
  </Step>
</Steps>

If all seven behave as described, your integration is sound.

## Getting help

Email [info@vibepay.mn](mailto:info@vibepay.mn) with the transaction `id` and the approximate time.
That is enough for us to trace any payment end to end.
