Skip to content

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) → succeeded at 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) → succeeded at once, refund.succeeded; the intent becomes partially_refunded or refunded (no intent event is sent — only refund.*).
  • Expiry works as in live: an unconfirmed intent becomes expired at expires_at (an app intent sends payment_intent.canceled with status: "expired", a claimless one payment_intent.expired); a manual intent past capture_before becomes expired with failure_code: "authorization_expired".
  • A failed intent stays payable (failed can be confirmed again), but its amount still ends in 01, so every retry ends in test_declined again.

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).