Referensi API
Pembayaran
Membuat pembayaran QRIS atau Virtual Account, membaca statusnya, dan mengulang request dengan aman.
§4 Membuat pembayaran#
POST /v1/payments
Authorization: Bearer <api_key>
Idempotency-Key: order-10023-attempt-1
Content-Type: application/json{
"project_id": "8f1c2d3e-4a5b-6c7d-8e9f-0a1b2c3d4e5f",
"external_order_id": "ORDER-10023",
"amount": 150000,
"currency": "IDR",
"payment_method": "QRIS",
"metadata": { "customer_id": "C-77" },
"return_url": "https://shop.example.com/orders/ORDER-10023"
}| Field | Wajib | Keterangan |
|---|---|---|
project_id |
ya | project milik merchant Anda |
external_order_id |
ya | ID pesanan Anda; unik per project. Jangan dipakai ulang untuk pesanan lain |
amount |
ya | IDR, bilangan bulat > 0, minimal sesuai metode |
currency |
ya | hanya IDR |
payment_method |
ya | QRIS atau VIRTUAL_ACCOUNT |
metadata |
tidak | objek JSON bebas untuk Anda; ikut dihitung dalam idempotensi |
return_url |
tidak | URL https:// untuk kanal yang mengarahkan pembeli kembali |
location |
tidak | request_country, customer_country, country_source, timezone untuk analitik |
Body dibaca ketat: field yang tidak dikenal ditolak 400 invalid request body.
Respons 200:
{
"payment_id": "2b8f6c1e-0d5a-4c1b-9f3e-7a6d5c4b3a21",
"external_order_id": "ORDER-10023",
"status": "PENDING",
"amount": 150000,
"currency": "IDR",
"payment_method": "QRIS",
"expires_at": "2026-11-01T03:30:00Z",
"checkout": {
"type": "QRIS",
"qr_string": "00020101021226…"
},
"refunded_amount": 0,
"refund_status": "NONE"
}- Untuk
VIRTUAL_ACCOUNT,checkoutberisi{"type":"VIRTUAL_ACCOUNT","account_number":"…"}. payment_urlhanya ada di sandbox (halaman bayar AKD). Di live, tampilkancheckoutdi aplikasi Anda.expires_atmengikuti masa berlaku pembayaran (saat ini 30 menit). Setelah lewat, pembayaran menjadiEXPIREDdan Anda menerimapayment.expired.- Simpan
payment_id; semua webhook dan pembacaan status memakainya. refunded_amount(total refund yang berhasil) danrefund_status(NONE,PARTIAL,FULL) ada sejak v1.0.3. Refund tidak mengubahstatus; lihatpayment.refundeddi bagian 7.
Varian halaman bayar (hanya sandbox): POST /v1/hosted/payments dengan body dan header yang sama mengembalikan payment_url.
Simulasi di sandbox: POST /v1/payments/{payment_id}/simulate (tanpa body) meminta Xendit Test membayar pembayaran PENDING. Jawaban 202 {"status":"simulation_requested"}, atau 200 {"status":"already_paid"}. Webhook payment.paid menyusul beberapa detik kemudian.
§5 Membaca status pembayaran#
GET /v1/payments/{payment_id}
Authorization: Bearer <api_key>Respons sama dengan pembuatan pembayaran. Pembayaran merchant lain dijawab 404. Gunakan ini bila webhook terlambat atau bila Anda ragu: webhook adalah sinyal, GET /v1/payments/{id} adalah kebenaran.
§10 Idempotensi#
Idempotency-Keywajib padaPOST /v1/paymentsdanPOST /v1/hosted/payments; tanpa header dijawab400 missing Idempotency-Key.- Key berlaku per merchant. Request ulang dengan key dan body yang sama (
project_id,external_order_id,amount,currency,payment_method,metadata,return_url) mengembalikan pembayaran yang sama, termasukexpires_ataslinya. Aman untuk retry setelah timeout. - Key yang sama dengan body berbeda dijawab
409 idempotency key conflicts with an existing payment. - Gunakan satu key per percobaan pembuatan pembayaran untuk satu pesanan, misalnya
<order_id>-<nomor percobaan>. Untuk membuat pembayaran baru bagi pesanan yang sama (misalnya setelahEXPIRED), pakaiexternal_order_iddan key baru. - Di sisi webhook, idempotensi Anda adalah deduplikasi
X-AKD-Event-ID.