Skip to content

Reconciliation

On this page

What: reconcile your order records, Gerege's intents and settlements every day, to find missed webhooks, stuck holds and extra payments. Time: ~1 hour. You need: a client credential (Basic), and the reference and intent id stored with every order.

1. The key is reference

Make your own order/basket number the reference of every intent (≤ 64), and the Idempotency-Key too (or <reference>-<attempt>). Then:

  • GET /payment-intents?reference=ord_8812 — every attempt for that order (newest first).
  • Webhooks, GET and settlement lines (intents[].reference) all come back carrying your number.

2. Listing and filters

curl -G https://geregeapp.mn/api/v1/miniapp/payment-intents -u "$GEREGE_CLIENT_ID:$GEREGE_CLIENT_SECRET" \
  --data-urlencode status=requires_capture --data-urlencode limit=100
  • status (requires_confirmation, processing, requires_capture, succeeded, partially_refunded, refunded, canceled, expired, failed), reference, limit (1–100, default 20), after (the last id of the previous page) → {"data": […], "has_more": true}, newest first.
  • The key's environment: a test key lists test intents, a live key live ones.
  • SDK: Node paymentIntents.listAll({status}) (every page), Go PaymentIntents.List + After.

3. The daily check

Check How If found
Paid but not fulfilled status=succeeded (the last day's) ↔ your "paid" a missed webhook — fulfil it
Fulfilled but not paid GET /payment-intents/{id} for each of your "paid" if not succeeded, investigate and stop
Stuck holds status=requires_capture goods handed over → capture, otherwise cancel — do not hold a person's money for nothing
Long processing status=processing the platform finishes it itself; if it takes over an hour, contact support with the intent id
Two payments for one order succeeded > 1 by reference refund the extra one
Refunds refund.succeeded / refund.failed ↔ your refunds failed (settlement_shortfall etc.) — retry, or by hand
Settlements (when settlements are on) GET /settlements → GET /settlements/{id} (intents, refund_items) every line matches one of your payments

4. When webhooks are missed

  • Delivery is at-least-once; on failure it is retried after 1m, 5m, 30m, 2h, 6h, 12h, 24h; the developer console shows each delivery's state and «дахин илгээх» ("resend") (developer console).
  • If your webhook has been down for a long time: catch up with the listing above (status, reference) — the webhook is the fast path, the listing is the truth.
  • Events API: GET /events?type=&after=<evt id>&created_gte=<RFC 3339>&limit=1..100 → {"data": […], "next_cursor"} (oldest first, 30 days, the key's environment; scope events:read), GET /events/{id} — the webhook's very body. Catch up on missed events with after, then decide by re-reading the object with GET. CSV reports (dashboard) — «coming soon».

5. Code

for await (const pi of gerege.paymentIntents.listAll({ status: "succeeded", limit: 100 })) {
  if (Date.parse(pi.created_at) < since) break;              // newest first
  const order = await db.orderByReference(pi.reference);
  if (order && !order.paid && pi.amount === order.amount) await fulfil(order, pi.id);
}
after := ""
for {
    page, err := c.PaymentIntents.List(ctx, miniapp.ListPaymentIntentsParams{Status: "requires_capture", Limit: 100, After: after})
    if err != nil || len(page.Data) == 0 {
        break
    }
    for _, pi := range page.Data {
        settleOrRelease(ctx, pi) // your rules
    }
    if !page.HasMore {
        break
    }
    after = page.Data[len(page.Data)-1].ID
}