Direct API
Credit card
Visa, Mastercard, JCB and Amex, with 3-D Secure.
Cards reach buyers outside Indonesia, and they are the only method where what you may build is limited by rules other than ours: card numbers are regulated data. One code, card, covers all four networks. It is a redirect shape: you send the buyer to payment.redirect_url, they enter the card and pass 3-D Secure there, and they are brought back.
Card data never touches your server — or ours
Handling raw card numbers requires PCI DSS certification. Neither integration asks that of you: on Checkout our page sends the buyer on, and here you do. The card is typed on the processor's own page and never posts anywhere else.
There is no endpoint that accepts a card number, and there will not be one. If you find yourself wanting to POST a PAN to us, the answer is redirect_url.
Integration steps
- Create the payment request.
- Send the buyer to payment.redirect_url.
- Acknowledge the payment — the payment.paid webhook.
- Check status when you need certainty: GET /v1/transactions/:id.
1. Create the payment request
A card needs customer.email; a create naming card without it is refused 422 customer_required. The card page opens for up to expires_in_minutes.
curl https://pay.kasera.id/v1/transactions \
-H "Authorization: Bearer kp_live_..." \
-H "Idempotency-Key: order-1234" \
-H "Content-Type: application/json" \
-d '{
"amount": 150000,
"external_id": "ORD-1234",
"payment_methods": ["card"],
"customer": { "email": "budi@toko.example" },
"return_url": "https://toko.example/selesai"
}'{
"id": "payreq_9b2f...",
"status": "pending",
"payment_method": "card",
"payment": {
"type": "redirect",
"redirect_url": "https://.../credit-card/checkout?...",
"display_name": "Credit Card"
},
"instructions": {
"title": "Cara membayar dengan kartu kredit",
"steps": ["..."]
},
"expires_at": "2026-08-27T13:00:00+07:00"
}2. Send the buyer there
A top-level navigation, not an iframe: the issuer's 3-D Secure step breaks out of any frame anyway. When the buyer is done — paid, declined or abandoned — the processor sends their browser to the Kasera Pay Checkout page for this payment, which confirms the result with the processor and then forwards them to your return_url with ?id=payreq_…&status=succeeded. You never see the return yourself, and you do not need to.
In test mode there is no processor page: redirect_url is the Checkout page for the payment, where the simulator marks it paid. Same code path on your side either way.
3. Acknowledge the payment
payment.paid fires at authorisation, so it usually arrives while the buyer is still on their way back. Fulfil on it, not on the buyer landing on return_url — a URL anyone can type.
4. Check status
GET /v1/transactions/:id — and keep the record afterwards. See the dispute note below.
Refunds
Cards are the first method that refunds through the API. POST /v1/refunds returns money on a succeeded card payment; omit amount for the whole amount, or send one for a partial refund. Partial refunds may be repeated until the amount paid is spent; one rupiah past it is refused 422 refund_exceeds_amount. Send an Idempotency-Key header: a retried refund with the same key returns the original refund instead of moving money twice.
curl https://pay.kasera.id/v1/refunds \
-H "Authorization: Bearer kp_live_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: refund-order-1234" \
-d '{
"transaction_id": "payreq_9b2f...",
"amount": 50000,
"reason": "Satu barang dikembalikan"
}'{
"id": "rfd_7c1a...",
"transaction_id": "payreq_9b2f...",
"status": "succeeded",
"currency": "IDR",
"amount": 50000,
"reason": "Satu barang dikembalikan",
"created_at": "2026-08-31T10:12:00+07:00",
"livemode": true
}| Answer | Meaning |
|---|---|
201 status: succeeded | Refunded. Read it back with GET /v1/refunds/:id. |
422 refund_not_supported | Not a card. QRIS and Virtual Account refunds are manual. |
409 not_refundable | The payment has not succeeded. |
422 refund_exceeds_amount | More than what is left. After a partial refund, send the amount explicitly. |
502 refund_refused | The processor declined. Nothing moved; the amount is available to retry. |
502 refund_unresolved | The processor could not be reached. The amount is held pending — do not retry; contact support. |
A refund returns the buyer's money; it does not return the fee. The fee on the original payment stands whatever is refunded, in full or in part. The refunded amount is withheld from your next payout — on a full refund that leaves the fee for you to cover, shown on the payout as refund_withheld.
Good to know
| Card | |
|---|---|
| Confirmation | Immediate, at authorisation |
| Refundable | Yes, including repeated partial refunds |
| Chargebacks | Possible — a buyer can dispute months later |
| Fee | Higher than QRIS and VA — read it from GET /v1/payment_methods |
Cards are the only method that can take money back after you have shipped. A payment that succeeded is not final the way a QRIS payment is — keep the order record and the buyer details you sent in customer, they are what answers a dispute.