Getting started

Test mode

Build and test your integration without moving real money.

A test API key creates payment requests that look and behave like real ones — same fields, same fees, same webhooks — but no real money ever moves.

Test keys

Alongside your live key you can hold one test key, prefixed kp_test_. Rotate and revoke it under Settings → Developer, exactly like the live key — one active key per mode. The dashboard shows one environment at a time: switch it to Sandbox to see the test key, Live for the live one. It goes in the same header:

Authorization: Bearer kp_test_...

The mode of the key decides the mode of everything it creates. There is no field to set and nothing to configure — create with kp_test_ and the payment request is a test object; create with kp_live_ and it is live. Each key sees only its own mode: a test key can never retrieve or list live payment requests, and vice versa.

Test objects

Every /v1 response and every webhook carries livemode false for test objects, true for live ones. The checkout page of a test payment shows a visible test badge, and what it hands the buyer is a deliberately unusable stub: a QR no wallet will accept, a Virtual Account number no transfer will reach. Which is correct — nothing about a test payment should be payable.

curl https://pay.kasera.id/v1/transactions \
  -H "Authorization: Bearer kp_test_..." \
  -H "Idempotency-Key: test-order-1234" \
  -H "Content-Type: application/json" \
  -d '{ "amount": 150000, "description": "Kaos komunitas" }'

Driving outcomes

A test payment never confirms on its own — you drive it. Take the token from the checkout_url in the create response (the part after /p/) and post the outcome you want:

curl -X POST https://pay.kasera.id/api/v1/checkout/{token}/simulate-payment \
  -H "Content-Type: application/json" \
  -d '{ "outcome": "succeeded" }'
curl -X POST https://pay.kasera.id/api/v1/checkout/{token}/simulate-payment \
  -H "Content-Type: application/json" \
  -d '{ "outcome": "expired" }'

An empty body means succeeded. Any other value is refused 422 invalid_outcome. The endpoint is authorized by the checkout token itself — no API key. Calling it on a live payment answers 404, the same as a token that does not exist.

Cards in test mode

A live card payment sends the buyer to a card page (payment.redirect_url), where they type the card and pass 3-D Secure, and brings them back. In test mode nothing reaches a card processor, so redirect_url is the Kasera Pay Checkout page for that payment, and the card step there is a simulated card form: number, expiry, CVV, name — validated in the browser and never sent anywhere. Only the test cards below are accepted; each number plays out one result, so every outcome can be rehearsed.

ResultMastercardVISA
Paid (3DS)5573 3810 1111 11014617 0069 1111 1106
Paid (no 3DS)5573 3810 1111 11354617 0069 1111 1130
Rejected by processor (3DS)5573 3810 1111 11194617 0069 1111 1114
Rejected by processor (no 3DS)5573 3810 1111 12004617 0069 1111 1213
Rejected by bank (3DS)5573 3810 1111 11274617 0069 1111 1122
Rejected by bank (no 3DS)5573 3810 1111 12264617 0069 1111 1221

Expiry 01/31 (any future month works), CVV 123 (any three digits), any name. Cards marked 3DS show a one-time-code step first; the step prints the code (123456) and only that code verifies. A rejected card leaves the payment pending and the buyer may try another — a declined attempt is not a failed payment, in test mode or live.

A paid test card fires the same payment.paid webhook a real card would, the buyer is sent on to your return_url, and a test refund through POST /v1/refunds succeeds without touching a processor.

Webhooks

Simulating succeeded fires a real, signed payment.paid delivery — to the test endpoint, with the test signing secret. Live and test are separate endpoints configured in their own dashboard mode, so set up the test one in Sandbox before you expect a delivery. Verify the signature exactly as you do for live events.

Check livemode before fulfilling. A test event is indistinguishable from a live one in every other way — that is the point.

What test mode never touches

Test payments are excluded from every money path: payouts, your balance, revenue reporting, reconciliation, and your daily limits — a test create neither consumes your daily allowance nor is refused by it. Test payments also never send buyer or merchant email or push notifications; only the developer webhook fires.

Going live

Swap kp_test_ for kp_live_. Nothing else changes. The two modes do not share a namespace of payments — idempotency keys included — so the keys your integration used in test mode are fresh in live mode: your first live requests create live payments, never replays of test ones.