Test amounts¶
In test mode the amount decides how a payment ends — produce a success, a decline and a cancellation without a phone or money, and check that your handler deals with every event correctly. Time: 10 minutes. You need: a test credential (Sandbox) and a webhook URL.
The rule¶
The last two digits of the amount (amount % 100) decide the outcome at the moment the person (or the console)
confirms:
| Last two digits | Outcome | Webhook | Person-side answer |
|---|---|---|---|
01 |
failed, failure_code: "test_declined" |
payment_intent.failed |
422 test_declined |
02 |
canceled |
payment_intent.canceled |
200, status: "canceled" |
anything else, capture_method: "automatic" |
succeeded |
payment_intent.succeeded |
200 |
anything else, capture_method: "manual" |
requires_capture, capture_before = now + the capture window |
payment_intent.requires_capture |
200 |
Example amounts:
amount |
Automatic | Manual |
|---|---|---|
1000 |
succeeded |
requires_capture |
45000 |
succeeded |
requires_capture |
1001 |
failed (test_declined) |
failed (test_declined) |
1002 |
canceled |
canceled |
3502 |
canceled |
canceled |
Live's rule that the amount is 100 – 50,000,000₮ holds in test too (100 ends in 00 → success).
After the confirmation (test)¶
- Capture (
POST /payment-intents/{id}/capture, manual,requires_capture) →succeededat once,payment_intent.succeeded. - Cancel on
requires_capture→canceled,payment_intent.canceled(there is no ledger release in test). - Refund (
POST /payment-intents/{id}/refunds) →succeededat once,refund.succeeded; the intent becomespartially_refundedorrefunded(no intent event is sent — onlyrefund.*). - Expiry works as in live: an unconfirmed intent becomes
expiredatexpires_at(anappintent sendspayment_intent.canceledwithstatus: "expired", a claimless onepayment_intent.expired); a manual intent pastcapture_beforebecomesexpiredwithfailure_code: "authorization_expired". - A
failedintent stays payable (failedcan be confirmed again), but its amount still ends in01, so every retry ends intest_declinedagain.
Example: testing a declined payment¶
With the console's test intents and the simulation (Testing without a phone), no phone needed. For a
claimless (qr, terminal) intent, first {"action": "claim"} (or simulate-tap), then confirm; with a phone, a
test user scans it in Gerege App and confirms with Face ID — the outcome follows the same rule.
# 1. A test intent ending in …01 (with your console session — the console's «Test payment» button does this)
POST /api/v1/miniapp/dev/apps/{id}/test-intents
{"amount": 1001, "description": "Test decline"}
# 2. Play the person's «pay»
POST /api/v1/miniapp/dev/apps/{id}/test-intents/{pi}/simulate
{"action": "confirm"}
# → 200, "status": "failed", "failure_code": "test_declined"
# → webhook: payment_intent.failed (at once)
What your handler should get right: on failed do not cancel the order (the person may pick another account and
pay again), on canceled and expired close the order, and hand over goods only on succeeded (re-reading the
intent with GET first — Idempotent handler).