C ChaosPay API Docs
API REFERENCE ยท v1

Dokumentasi API ChaosPay

Terima pembayaran QRIS, pantau statusnya, cek saldo, dan kirim payout ke rekening bank yang sudah terdaftar melalui REST API berbasis JSON. Semua endpoint menjawab dengan format yang sama, dan setiap notifikasi yang kami kirim ke Anda selalu ditandatangani.

Base URL

Semua request API dikirim melalui HTTPS ke:

Production
https://api.labubu4d.lol

Body request POST memakai Content-Type: application/json, sedangkan GET memakai query parameter. Nominal selalu berupa bilangan bulat Rupiah (IDR). Waktu memakai zona Asia/Jakarta (UTC+7).

Contoh kode di halaman ini memakai komentar berbahasa Inggris, sama dengan versi English.

Mulai Cepat

  1. Ambil kredensial Anda. Login ke dashboard merchant lalu buka 2FA & API. Di sana ada Merchant Identifier dan API Key. Secret Key hanya ditampilkan satu kali, yaitu saat diterbitkan atau dirotasi, jadi simpan baik-baik saat itu juga.
  2. Atur URL callback. Di Callback Settings, isi URL HTTPS yang akan menerima notifikasi pembayaran (dan URL callback penarikan jika Anda memakai payout).
  3. Daftarkan IP server Anda (disarankan) di tempat yang sama.
  4. Buat pembayaran dengan POST /payment, tampilkan QR yang dikembalikan ke pelanggan, lalu tunggu callback.
  5. Uji alurnya dari awal sampai akhir di sandbox sebelum go-live.
cURL
# client_signature = md5(username + amount + secret_key)
curl -X POST https://api.labubu4d.lol/payment \
  -H "Content-Type: application/json" \
  -H "X-API-KEY: YOUR_API_KEY" \
  -d '{
    "merchant": "YOUR_MERCHANT_ID",
    "username": "customer001",
    "amount": 50000,
    "order_id": "INV-10023",
    "client_signature": "3f2a9c0e7b1d4a8e9f6c5b2a1d0e9f8c"
  }'

Sandbox

Setiap merchant juga bisa menerbitkan API key dan secret khusus sandbox dari 2FA & API. Gunakan dengan base URL yang sama:

  • Pembayaran yang dibuat dengan key sandbox tidak pernah dikirim ke bank atau provider, tidak menyentuh saldo asli Anda, dan dikembalikan dengan "sandbox": true.
  • Di halaman Sandbox pada dashboard, Anda bisa menandai pembayaran sandbox sebagai paid atau failed. Setelah itu kami mengirim callback sungguhan ke URL callback pembayaran Anda, ditandatangani dengan secret sandbox, sehingga handler Anda bisa diuji.
  • IP whitelist tidak diberlakukan untuk key sandbox.
  • Payout dan daftar rekening bank tidak tersedia di sandbox (NOT_AVAILABLE_IN_SANDBOX). Saldo selalu bernilai nol.

Batas Request

Batas dihitung per API key, per menit. Jika melewati batas, API menjawab HTTP 429 dengan header Retry-After (dalam detik).

EndpointBatas
POST /payment120 request / menit
GET /payment/status, GET /account/balance300 request / menit
POST /payout, GET /payout/status, GET /account/banks30 request / menit (gabungan)

Nilai di atas adalah default. Setiap batasan di halaman ini (nominal pembayaran dan payout, batas harian, batas request, serta jumlah percobaan dan timeout callback) bisa diatur per akun; batasan akun Anda tercantum di dashboard pada My Account.

Utamakan callback daripada polling. Jika memang perlu polling status pembayaran, jeda beberapa detik sudah cukup.

Autentikasi

Setiap request mengidentifikasi akun Anda lewat API key di header dan merchant identifier di dalam request (body JSON untuk POST, query string untuk GET).

HeaderWajibKeterangan
X-API-KEYWajibAPI key Anda (atau API key sandbox). Jangan pernah dikirim di URL.
Content-TypePOSTapplication/json
X-Timestampv2Unix time dalam detik. Selisihnya dengan jam server kami maksimal 5 menit.
X-Noncev2String acak 8โ€“64 karakter dari A-Z a-z 0-9 _ -, tidak boleh dipakai ulang.
X-Signaturev2HMAC-SHA256 dalam hex huruf kecil, lihat Request Signature v2.

Secret key dipakai untuk menandatangani request dan memverifikasi callback dari kami. Secret key tidak pernah dikirim di dalam request. Jika Anda menduga secret key bocor, rotasi key dari 2FA & API: API key dan secret key baru diterbitkan bersamaan, dan pasangan lama langsung tidak berlaku.

Client Signature (Buat Pembayaran)

Request POST /payment yang tidak ditandatangani dengan v2 wajib menyertakan client_signature:

Rumus
client_signature = md5(username + amount + secret_key)

Ketiga nilai digabung sebagai teks tanpa pemisah, dan hasilnya 32 karakter hex huruf kecil. Untuk username = "customer001", amount = 50000, dan secret "sec", input-nya adalah customer00150000sec.

Request Signature v2

Signature v2 menandatangani seluruh request dan melindunginya dari replay. Signature ini wajib untuk semua endpoint payout (/payout, /payout/status, /account/banks) dan diterima di semua endpoint lain. Jika request membawa signature v2 yang valid, client_signature tidak diperlukan. Akun Anda juga bisa diatur agar mewajibkan v2 di semua endpoint.

String yang ditandatangani
METHOD + "\n" + PATH + "\n" + X-Timestamp + "\n" + X-Nonce + "\n" + sha256_hex(PAYLOAD)

X-Signature = hmac_sha256_hex(string_to_sign, secret_key)
BagianNilai
METHODHuruf besar, misalnya POST
PATHHanya path, tanpa host dan query, misalnya /payout
PAYLOADPOST: body request mentah, persis byte demi byte. GET: query string mentah tanpa ? di depan (string kosong jika tidak ada).
<?php
// ChaosPay request signature v2
function chaospaySign(string $method, string $path, string $payload, string $secretKey): array
{
    $timestamp = (string) time();
    $nonce = bin2hex(random_bytes(8));
    $stringToSign = implode("\n", [strtoupper($method), $path, $timestamp, $nonce, hash('sha256', $payload)]);

    return [
        'X-Timestamp' => $timestamp,
        'X-Nonce' => $nonce,
        'X-Signature' => hash_hmac('sha256', $stringToSign, $secretKey),
    ];
}
Kesalahan yang sering terjadi
  • Menandatangani satu string JSON tetapi mengirim string lain (misalnya karena HTTP client men-serialize ulang body). Kirim persis string yang Anda hash.
  • Membangun query string dua kali. Bangun sekali, tandatangani, lalu tempelkan string yang sama ke URL.
  • Memakai ulang nonce: setiap nonce hanya diterima sekali (NONCE_REUSED). Jam yang meleset lebih dari 5 menit menghasilkan REQUEST_EXPIRED.

IP Whitelist

Di Callback Settings Anda bisa mendaftarkan IP server yang boleh memanggil API dengan key live, dipisahkan dengan |. Jika daftarnya kosong, semua IP diterima. Request dari IP lain ditolak dengan IP_NOT_WHITELISTED. Kami sangat menyarankan mengisinya sebelum mengaktifkan payout.

Format Respons

Setiap respons, baik sukses maupun gagal, berupa envelope JSON:

FieldTipeKeterangan
codeinteger200 jika sukses, selain itu status error
messagestringOK, atau kode error yang bisa dibaca mesin
dataobject | array | nullHasilnya; biasanya null jika error
Sukses
{
  "code": 200,
  "message": "OK",
  "data": { }
}
Error
{
  "code": 403,
  "message": "IP_NOT_WHITELISTED",
  "data": null
}

Gunakan code dan message untuk percabangan logika, jangan hanya status HTTP. Saat provider upstream gagal, envelope berisi 502 sedangkan status HTTP-nya 503.

Kode Error

HTTPmessageArti
400INVALID_PARAMETERAda parameter yang kosong atau formatnya salah.
401INVALID_SIGNATURESignature v2 atau header-nya tidak valid atau tidak cocok.
401REQUEST_EXPIREDX-Timestamp berselisih lebih dari 5 menit dari jam server kami.
401NONCE_REUSEDX-Nonce ini sudah pernah dipakai.
401SIGNATURE_V2_REQUIREDEndpoint ini (atau akun Anda) mewajibkan signature v2.
403API_KEY_REQUIREDHeader X-API-KEY tidak ada.
403INVALID_API_KEY_OR_MERCHANTAPI key dan merchant identifier tidak cocok dengan akun yang aktif.
403IP_NOT_WHITELISTEDIP pemanggil tidak ada di whitelist Anda.
403INVALID_CLIENT_SIGNATUREclient_signature tidak cocok.
403UNAUTHORIZEDRequest tidak dapat dikaitkan dengan akun Anda.
403API_PAYOUTS_DISABLEDPayout lewat API belum diaktifkan untuk akun Anda. Hubungi kami untuk mengaktifkannya.
403NOT_AVAILABLE_IN_SANDBOXEndpoint ini tidak bisa dipakai dengan key sandbox.
404TRANSACTION_NOT_FOUNDTidak ada pembayaran dengan transaction_id / order_id tersebut di akun Anda.
404PAYOUT_NOT_FOUNDTidak ada payout dengan payout_id / withdraw_id tersebut di akun Anda.
404NOT_FOUNDEndpoint tidak dikenal.
405METHOD_NOT_ALLOWEDMethod HTTP salah untuk endpoint ini.
409DUPLICATE_PAYOUT_IDpayout_id sudah dipakai dengan rekening atau nominal berbeda.
422INVALID_BANKbank_id bukan salah satu rekening terdaftar Anda.
422AMOUNT_BELOW_MINIMUMNominal payout di bawah minimum; nilainya ada di data.minimum.
422AMOUNT_ABOVE_MAXIMUMNominal payout melebihi maksimum per payout; nilainya ada di data.maximum.
422DAILY_LIMIT_EXCEEDEDBatas harian akun Anda (volume pembayaran, volume payout, atau jumlah payout) sudah tercapai. Buat Pembayaran mengembalikan data.limit dan data.used.
422INSUFFICIENT_BALANCESaldo tersedia tidak cukup untuk nominal payout ditambah fee-nya.
429TOO_MANY_REQUESTSMelebihi batas request. Tunggu selama Retry-After detik.
500SERVICE_ERRORError tak terduga di sisi kami. Cek status pembayaran berdasarkan order_id sebelum mencoba ulang pembayaran; payout aman dicoba ulang dengan payout_id yang sama.
503 (code 502)GATEWAY_UNREACHABLE, INVALID_PROVIDER_RESPONSE, INVALID_SIGNATUREProvider pembayaran tidak bisa dihubungi, atau jawabannya rusak atau gagal pemeriksaan signature kami. Mencoba ulang akan membuat pembayaran baru.
503PROVIDER_NOT_CONFIGUREDSaat ini tidak ada channel pembayaran yang tersedia untuk akun Anda.

Jika provider pembayaran menolak request, kode dan pesan dari provider diteruskan apa adanya di envelope, misalnya {"code": 400, "message": "Amount range is from IDR 10,000 to 10,000,000", "data": null}.

Buat Pembayaran

POST/payment

Membuat pembayaran QRIS dan mengembalikan payload QR untuk ditampilkan ke pelanggan. Pembayaran kedaluwarsa pada expire_date; hasil akhirnya dikirim ke callback pembayaran Anda.

Body Request

ParameterTipeWajibKeterangan
merchantstringWajibMerchant identifier Anda
usernamestringWajibUsername atau ID pelanggan Anda (maks. 191)
amountintegerWajibNominal dalam IDR. Rentang default 10.000 โ€“ 10.000.000; akun Anda bisa memiliki rentang sendiri.
client_signaturestringWajib*md5(username + amount + secret_key). *Tidak diperlukan jika request ditandatangani dengan v2.
order_idstringOpsionalReferensi Anda sendiri, maks. 64, A-Z a-z 0-9 . _ : -. Dikembalikan di respons dan callback, dan bisa dipakai untuk mencari pembayaran.
emailstringOpsionalEmail pembayar
mobilestringOpsionalNomor HP pembayar, 6โ€“20 digit, boleh diawali +
account_namestringOpsionalNama pembayar (maks. 100)
account_nostringOpsionalNomor rekening pembayar, 3โ€“32 digit
bank_codestringOpsionalKode bank pembayar, 2โ€“32 karakter

Setiap request membuat pembayaran baru, termasuk dengan order_id yang sudah pernah dipakai. Setelah timeout, cek dulu pembayarannya berdasarkan order_id sebelum mengirim ulang, agar pelanggan tidak mendapat QR kedua. Pencarian status dengan order_id yang dipakai lebih dari sekali mengembalikan pembayaran terbaru.

Contoh Request

# client_signature = md5(username + amount + secret_key)
curl -X POST https://api.labubu4d.lol/payment \
  -H "Content-Type: application/json" \
  -H "X-API-KEY: YOUR_API_KEY" \
  -d '{
    "merchant": "YOUR_MERCHANT_ID",
    "username": "customer001",
    "amount": 50000,
    "order_id": "INV-10023",
    "client_signature": "3f2a9c0e7b1d4a8e9f6c5b2a1d0e9f8c"
  }'

Respons

200 OK
{
  "code": 200,
  "message": "OK",
  "data": {
    "transaction_id": "TRX2610211430151234",
    "order_id": "INV-10023",
    "amount": 50000,
    "create_date": "2026-10-21 14:30:15",
    "expire_date": "2026-10-21 14:40:15",
    "status": 0,
    "qr": "00020101021226...6304ABCD",
    "payment_url": null,
    "sandbox": false
  }
}
FieldTipeKeterangan
transaction_idstringReferensi pembayaran dari kami. Simpan bersama order Anda.
order_idstring | nullReferensi Anda, sesuai yang dikirim
amountintegerNominal dalam IDR
create_datestringY-m-d H:i:s, Asia/Jakarta
expire_datestringSetelah waktu ini QR tidak bisa dibayar lagi
statusinteger0 saat dibuat, lihat kode status
qrstringPayload QRIS. Tampilkan sebagai kode QR untuk dipindai pelanggan.
payment_urlstring | nullHalaman pembayaran hosted, jika channel menyediakannya (hanya https)
sandboxbooleantrue jika dibuat dengan key sandbox

Status Pembayaran

GET/payment/status

Mengembalikan kondisi terkini salah satu pembayaran Anda. Gunakan untuk rekonsiliasi, atau jika callback tidak sampai ke Anda.

Query Parameter

ParameterTipeWajibKeterangan
merchantstringWajibMerchant identifier Anda
transaction_idstringSalah satuReferensi dari Buat Pembayaran
order_idstringSalah satuReferensi Anda

Contoh Request

curl -G https://api.labubu4d.lol/payment/status \
  -H "X-API-KEY: YOUR_API_KEY" \
  --data-urlencode "merchant=YOUR_MERCHANT_ID" \
  --data-urlencode "order_id=INV-10023"

Respons

200 OK
{
  "code": 200,
  "message": "OK",
  "data": {
    "merchant": "YOUR_MERCHANT_ID",
    "transaction_id": "TRX2610211430151234",
    "order_id": "INV-10023",
    "idrpayment_id": "88231907",
    "username": "customer001",
    "amount": 50000,
    "status": 1,
    "local_status": "completed",
    "remark": "",
    "sandbox": false
  }
}
FieldTipeKeterangan
merchantstringMerchant identifier Anda
transaction_idstringReferensi dari kami
order_idstring | nullReferensi Anda
idrpayment_idstring | nullReferensi dari provider pembayaran
usernamestringUsername pelanggan yang dikirim saat pembuatan
amountintegerNominal dalam IDR
statusinteger0, 1, atau 2, lihat di bawah
local_statusstringpending, completed, failed, atau expired
remarkstringCatatan dari provider, jika ada
sandboxbooleantrue untuk pembayaran sandbox

Kode Status Pembayaran

statuslocal_statusArtiFinal
0pendingMenunggu pelanggan membayarTidak
0expiredTidak dibayar sebelum expire_dateYa
1completedSudah dibayar. Kreditkan ke pelanggan Anda.Ya
2failedGagal atau dibatalkanYa

Hanya status = 1 yang berarti uang sudah diterima. Gunakan local_status untuk membedakan pembayaran yang masih pending dari yang sudah kedaluwarsa.

Saldo

GET/account/balance

Mengembalikan saldo Anda saat ini. Query parameter: merchant (wajib).

cURL
curl -G https://api.labubu4d.lol/account/balance \
  -H "X-API-KEY: YOUR_API_KEY" \
  --data-urlencode "merchant=YOUR_MERCHANT_ID"
200 OK
{
  "code": 200,
  "message": "OK",
  "data": {
    "merchant": "YOUR_MERCHANT_ID",
    "currency": "IDR",
    "available": "12500000.00",
    "pending_settlement": "1500000.00",
    "settled_total": "84000000.00"
  }
}
FieldTipeKeterangan
currencystringSelalu IDR
availablestringSaldo yang bisa ditarik sekarang (string desimal, 2 angka di belakang koma)
pending_settlementstringSaldo yang ditahan untuk payout yang belum final
settled_totalstringTotal yang sudah dibayarkan ke Anda

Rekening Bank

GET/account/banksSignature v2

Menampilkan rekening bank yang terdaftar di dashboard Anda (Settlement Banks). Gunakan bank_id saat membuat payout. Rekening hanya bisa ditambahkan dari dashboard. Query parameter: merchant (wajib). Tandatangani seperti GET v2 lainnya, dengan PATH = /account/banks dan PAYLOAD = merchant=YOUR_MERCHANT_ID.

200 OK
{
  "code": 200,
  "message": "OK",
  "data": [
    {
      "bank_id": 12,
      "bank_name": "BCA",
      "account_name": "PT CONTOH MERCHANT",
      "account_number": "123****890",
      "is_default": true
    }
  ]
}

Buat Payout

POST/payoutSignature v2

Menarik saldo tersedia ke salah satu rekening bank terdaftar Anda. Nominal ditambah fee withdraw akun Anda (jika ada) langsung ditahan; bank menerima nominal penuh. Hasilnya dikirim ke URL callback penarikan Anda. Payout lewat API harus diaktifkan dulu untuk akun Anda oleh tim kami.

Body Request

ParameterTipeWajibKeterangan
merchantstringWajibMerchant identifier Anda
payout_idstringWajibReferensi unik Anda, maks. 64, A-Z a-z 0-9 . _ : -
bank_idintegerWajibDari Rekening Bank
amountintegerWajibNominal dalam IDR. Minimum default 10.000; akun Anda juga bisa memiliki batas maksimum dan batas harian.
notestringOpsionalTeks bebas, maks. 191

Idempotensi. Mengulang payout_id dengan rekening dan nominal yang sama akan mengembalikan payout yang sudah ada; tidak ada penarikan ganda. Dengan nilai berbeda, hasilnya DUPLICATE_PAYOUT_ID.

Contoh Request

# POST requests signed with v2: the payload is the raw JSON body, byte for byte.
API_KEY='YOUR_API_KEY'; SECRET='YOUR_SECRET_KEY'
BODY='{"merchant":"YOUR_MERCHANT_ID","payout_id":"PO-2026-0001","bank_id":12,"amount":1500000,"note":"October settlement"}'

TS=$(date +%s); NONCE=$(openssl rand -hex 8)
BODY_HASH=$(printf '%s' "$BODY" | openssl dgst -sha256 -hex | sed 's/^.* //')
SIGNATURE=$(printf 'POST\n/payout\n%s\n%s\n%s' "$TS" "$NONCE" "$BODY_HASH" \
  | openssl dgst -sha256 -hmac "$SECRET" -hex | sed 's/^.* //')

curl -X POST https://api.labubu4d.lol/payout \
  -H "Content-Type: application/json" \
  -H "X-API-KEY: $API_KEY" -H "X-Timestamp: $TS" -H "X-Nonce: $NONCE" -H "X-Signature: $SIGNATURE" \
  --data-raw "$BODY"

Respons

200 OK
{
  "code": 200,
  "message": "OK",
  "data": {
    "payout_id": "PO-2026-0001",
    "withdraw_id": 41,
    "amount": 1500000,
    "fee": 5000,
    "total_debit": 1505000,
    "status": "pending",
    "bank": {
      "bank_id": 12,
      "bank_name": "BCA",
      "account_number": "123****890"
    },
    "note": "October settlement",
    "requested_at": "2026-10-21T14:32:00+07:00",
    "processed_at": null
  }
}
422
{
  "code": 422,
  "message": "AMOUNT_BELOW_MINIMUM",
  "data": { "minimum": 10000 }
}
FieldTipeKeterangan
payout_idstringReferensi Anda
withdraw_idintegerReferensi dari kami
amountintegerNominal dalam IDR yang diterima bank
feeintegerFee withdraw, dikenakan di atas amount (0 jika tidak ada)
total_debitintegeramount + fee, dipotong dari saldo Anda
statusstringLihat nilai status payout
bankobjectbank_id, bank_name, account_number yang disamarkan
notestring | nullCatatan Anda
requested_atstringISO 8601
processed_atstring | nullISO 8601, terisi saat sudah final

Status Payout

GET/payout/statusSignature v2

Query parameter: merchant (wajib) dan salah satu dari payout_id atau withdraw_id. Isi data pada respons sama dengan Buat Payout.

# GET requests signed with v2: the payload is the raw query string, exactly as sent.
API_KEY='YOUR_API_KEY'; SECRET='YOUR_SECRET_KEY'
URI_PATH='/payout/status'
QUERY='merchant=YOUR_MERCHANT_ID&payout_id=PO-2026-0001'

TS=$(date +%s); NONCE=$(openssl rand -hex 8)
BODY_HASH=$(printf '%s' "$QUERY" | openssl dgst -sha256 -hex | sed 's/^.* //')
SIGNATURE=$(printf 'GET\n%s\n%s\n%s\n%s' "$URI_PATH" "$TS" "$NONCE" "$BODY_HASH" \
  | openssl dgst -sha256 -hmac "$SECRET" -hex | sed 's/^.* //')

curl "https://api.labubu4d.lol$URI_PATH?$QUERY" \
  -H "X-API-KEY: $API_KEY" -H "X-Timestamp: $TS" -H "X-Nonce: $NONCE" -H "X-Signature: $SIGNATURE"

Nilai Status Payout

statusArtiFinal
pendingDiterima; nominal ditahan dari saldo AndaTidak
processingSedang ditransfer ke bankTidak
successSudah ditransferYa
failedDitolak atau gagal; nominal dan fee yang ditahan dikembalikan ke saldo tersediaYa

Callback Pembayaran

Saat pembayaran mencapai status final, kami mengirim POST berisi body JSON ke URL callback pembayaran Anda.

HeaderNilai
Content-Typeapplication/json
Acceptapplication/json
Payload
{
  "idrpayment_id": "88231907",
  "transaction_id": "TRX2610211430151234",
  "order_id": "INV-10023",
  "amount": 50000,
  "status": 1,
  "signature_v2": "5d1c7e0f6a2b...e93a",
  "sandbox": false,
  "signature": "a8f5f167f44f4964e6c998dee827110c"
}
FieldTipeKeterangan
idrpayment_idstringReferensi dari provider pembayaran
transaction_idstringReferensi dari kami
order_idstring | nullReferensi Anda
amountintegerNominal dalam IDR
statusinteger1 dibayar, 2 gagal, 0 kedaluwarsa (dilaporkan oleh provider)
signature_v2stringHMAC-SHA256, verifikasi yang ini
signaturestringLama: md5(transaction_id + amount + secret_key). Dipertahankan untuk integrasi lama; tidak mencakup status.
sandboxbooleantrue untuk pembayaran sandbox

Memverifikasi signature

Rumus
signature_v2 = hmac_sha256_hex(transaction_id|order_id|amount|status|idrpayment_id, secret_key)

Gabungkan kelima nilai dengan |, gunakan string kosong jika order_id bernilai null. Bandingkan dengan perbandingan constant-time.

<?php
$secretKey = 'YOUR_SECRET_KEY';
$p = json_decode(file_get_contents('php://input'), true);

$expected = hash_hmac('sha256', implode('|', [
    $p['transaction_id'], $p['order_id'] ?? '', $p['amount'], $p['status'], $p['idrpayment_id'],
]), $secretKey);

if (! hash_equals($expected, (string) ($p['signature_v2'] ?? ''))) {
    http_response_code(401);
    exit('invalid signature');
}

// Idempotent: the same notification may arrive more than once.
if ((int) $p['status'] === 1) {
    // mark order $p['order_id'] / $p['transaction_id'] as paid (once)
}

http_response_code(200);
echo 'OK';

Callback Payout

Saat payout menjadi success atau failed, kami mengirim POST berisi body JSON ke URL callback penarikan Anda. Ini berlaku untuk payout yang diminta lewat API maupun dari dashboard (untuk yang dari dashboard, payout_id bernilai null).

Payload
{
  "payout_id": "PO-2026-0001",
  "withdraw_id": 41,
  "reference": "October settlement",
  "amount": 1500000,
  "fee": 5000,
  "status": "success",
  "note": "October settlement",
  "processed_at": "2026-10-21T14:35:12+07:00",
  "signature_v2": "9b0e44c1d2f3...7a10"
}
FieldTipeKeterangan
payout_idstring | nullReferensi Anda
withdraw_idintegerReferensi dari kami
referencestringIsi note, atau payout_id jika note tidak diisi
amountintegerNominal dalam IDR yang diterima bank
feeintegerFee withdraw yang dikenakan di atas nominal (dikembalikan jika failed). Tidak termasuk dalam signature_v2.
statusstringsuccess atau failed
notestring | nullCatatan pemrosesan
processed_atstring | nullISO 8601
signature_v2stringHMAC-SHA256
Rumus
signature_v2 = hmac_sha256_hex(withdraw_id|payout_id|amount|status, secret_key)
<?php
$secretKey = 'YOUR_SECRET_KEY';
$p = json_decode(file_get_contents('php://input'), true);

$expected = hash_hmac('sha256', implode('|', [
    $p['withdraw_id'], $p['payout_id'] ?? '', $p['amount'], $p['status'],
]), $secretKey);

if (! hash_equals($expected, (string) ($p['signature_v2'] ?? ''))) {
    http_response_code(401);
    exit('invalid signature');
}

// $p['status'] is "success" or "failed". Apply it once per withdraw_id.

http_response_code(200);
echo 'OK';

Pengiriman & Retry

  • Balas dengan status HTTP 2xx apa pun sebagai tanda diterima. Selain itu, atau jika tidak ada jawaban dalam 5 detik (default), dianggap gagal.
  • Pengiriman yang gagal akan dicoba ulang, secara default total 3 kali percobaan, dengan jeda sekitar 10, 60, dan 300 detik (300 detik untuk percobaan tambahan jika akun Anda diatur lebih banyak).
  • URL callback wajib memakai https dan mengarah ke alamat publik. Redirect tidak diikuti.
  • Notifikasi yang sama bisa datang lebih dari sekali. Buat handler Anda idempoten: gunakan transaction_id (atau withdraw_id) sebagai kunci dan terapkan setiap status final hanya sekali.
  • Setiap pengiriman beserta responsnya tercatat di dashboard pada Callback Log, dan pengiriman yang gagal bisa dikirim ulang dari sana.
  • Selalu verifikasi signature_v2 sebelum memercayai callback, dan cocokkan nominalnya dengan order Anda.
  • Pembayaran yang hanya melewati expire_date belum tentu menghasilkan callback. Untuk order yang masih terbuka setelah kedaluwarsa, tanyakan ke Status Pembayaran.

Endpoint Lama

Integrasi lama tetap bisa memakai alias berikut. Perilakunya sama persis dengan endpoint yang baru. Integrasi baru sebaiknya memakai path yang baru.

LamaBaru
POST /services/CreatePayment.phpPOST /payment
GET /services/GetStatus.phpGET /payment/status

API key hanya diterima di header X-API-KEY, tidak pernah di URL.

Health Check

GET/up

Mengembalikan HTTP 200 jika API berjalan. Tanpa autentikasi.

Bantuan

Untuk kredensial, aktivasi payout lewat API, atau pertanyaan tentang transaksi tertentu, hubungi account manager Anda dan sertakan transaction_id atau withdraw_id. Jangan pernah mengirim secret key Anda kepada siapa pun, termasuk kepada kami.