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,
GETand 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), GoPaymentIntents.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; scopeevents:read),GET /events/{id}— the webhook's very body. Catch up on missed events withafter, then decide by re-reading the object withGET. 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
}