

Buat tagihan retribusi dan ikuti status pembayarannya dari backend aplikasi Anda. Panduan ini menjelaskan request, respons, dan langkah penanganan untuk API SIMPUL PAD Kabupaten Garut.
Siapkan aplikasi yang disetujui, API key aktif, dan ID layanan retribusi. Contoh memakai VA dengan nominal Rp25.000. Ganti nominal sesuai tarif layanan Anda.
Buka portal pengembang, pilih aplikasi, lalu ambil API key dan ID layanan yang disetujui.
Ganti placeholder pada contoh terminal. Tentukan satu key untuk satu invoice dan simpan waktu kedaluwarsa untuk digunakan saat retry.
Gunakan POST /api/v1/bills. Simpan data.id dari respons. HTTP 202 berarti tagihan diterima dan metode pembayaran sedang diterbitkan.
Gunakan GET tagihan atau webhook. Tampilkan metode pembayaran aktif. Tandai lunas saat status paid telah terkonfirmasi.
# Jalankan di terminal backend dengan shell Bash.
export BASE_URL="https://simpul-pad.bapenda.garutkab.go.id"
export SIMPUL_API_KEY="<API_KEY_APLIKASI_ANDA>"
export SERVICE_ID="<UUID_LAYANAN_YANG_DISETUJUI>"
export IDEMPOTENCY_KEY="invoice-2026-00000001"
# Gunakan Node.js untuk membuat waktu kedaluwarsa 24 jam ke depan.
# Jalankan sekali, lalu simpan nilainya untuk request dan retry.
export EXPIRES_AT="$(node -e 'process.stdout.write(new Date(Date.now() + 86400000).toISOString())')"cURL, Node.js, dan Python pada referensi endpoint menggunakan variabel ini. Untuk lingkungan lain, sesuaikan BASE_URL dengan alamat SIMPUL PAD yang diberikan operator. ID, nomor pembayaran, dan waktu pada contoh respons hanya ilustrasi.
Setiap endpoint tagihan memerlukan API key aplikasi pada header Authorization. Key memiliki awalan sp_ dan hanya ditampilkan sekali saat dibuat di portal pengembang.
AuthorizationheaderWajibContent-Typeheader · POSTWajibIdempotency-Keyheader · buat tagihanWajibX-Request-IdheaderOpsionalAuthorization: Bearer <API_KEY_APLIKASI_ANDA>
Content-Type: application/json
Idempotency-Key: invoice-2026-00000001
X-Request-Id: request-2026-00000001JSONrequest & responsRupiahstring digitTanggalISO 8601 · UTCPOST → 202 / issuingTagihan tersimpan, metode pembayaran belum siap.
GET → 200 / unpaidMetode pembayaran aktif dapat ditampilkan.
Webhook / GET → paidPembayaran penuh sudah tercatat.
HTTP sukses menjelaskan hasil request. data.status menjelaskan keadaan tagihan. Jangan menyimpulkan pembayaran lunas dari HTTP 200 atau 202 saja.
Buat tagihan untuk layanan yang disetujui. SIMPUL PAD memvalidasi tarif dan menyimpan tagihan, lalu menerbitkan VA atau QRIS secara asinkron. Semua request memerlukan header autentikasi dan Idempotency-Key.
service_idstring · UUIDWajibpayment_methodstringOpsionalexternal_referencestringOpsionalpayerobjectWajibamount_rupiahstringWajibexpires_atstring · UTCWajibpayer.namestringWajibpayer.addressstringOpsionalcurl -i -X POST "$BASE_URL/api/v1/bills" \
-H "Authorization: Bearer $SIMPUL_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $IDEMPOTENCY_KEY" \
--data @- <<JSON
{
"service_id": "$SERVICE_ID",
"external_reference": "invoice/2026/001",
"payer": {
"name": "Wajib Retribusi",
"address": "Garut"
},
"amount_rupiah": "25000",
"payment_method": "virtual_account",
"expires_at": "$EXPIRES_AT"
}
JSON{
"data": {
"id": "018f0f79-16ea-7ad0-a9dd-120a8c001001",
"billing_number": "3512345678901234",
"status": "issuing",
"amount_rupiah": "25000",
"paid_rupiah": "0",
"expires_at": "2026-10-09T06:30:00.000Z",
"payment_methods": [],
"created_at": "2026-10-08T06:30:00.000Z"
},
"request_id": "request-01",
"replayed": false
}Header Location berisi URL untuk membaca tagihan. Retry dengan key dan payload sama mengembalikan HTTP 200 dan replayed: true. Lihat aturan idempotensi.
Baca status terbaru dan metode pembayaran tagihan milik aplikasi Anda. Gunakan endpoint ini setelah pembuatan, saat menerima webhook, atau ketika perlu memastikan hasil transaksi.
idpath · UUIDWajibGunakan variabel BILL_ID pada contoh. Endpoint ini tidak memerlukan body atau Idempotency-Key.
export BILL_ID="<DATA_ID_DARI_RESPONS_PEMBUATAN>"HTTP 404 berarti tagihan tidak ditemukan atau berada di luar akses aplikasi Anda. Daftar field respons ada pada objek tagihan.
curl -i -X GET "$BASE_URL/api/v1/bills/$BILL_ID" \
-H "Authorization: Bearer $SIMPUL_API_KEY"{
"data": {
"id": "018f0f79-16ea-7ad0-a9dd-120a8c001001",
"billing_number": "3512345678901234",
"status": "unpaid",
"amount_rupiah": "25000",
"paid_rupiah": "0",
"expires_at": "2026-10-09T06:30:00.000Z",
"payment_methods": [
{
"type": "virtual_account",
"provider": "bank_bjb",
"display_value": "8881234567890123",
"status": "active",
"expires_at": "2026-10-09T06:30:00.000Z"
}
],
"created_at": "2026-10-08T06:30:00.000Z"
},
"request_id": "request-02"
}Batalkan tagihan sebelum proses penerbitan metode pembayaran dimulai, atau setelah penerbitan gagal secara pasti. SIMPUL PAD tetap menyimpan tagihan dan riwayat transaksi.
idpath · UUIDWajibreasonbody · stringWajibGunakan Authorization dan Content-Type: application/json. Pembatalan tidak memerlukan Idempotency-Key. Pengulangan pembatalan yang sudah berhasil mengembalikan HTTP 200 dengan replayed: true.
curl -i -X POST "$BASE_URL/api/v1/bills/$BILL_ID/cancel" \
-H "Authorization: Bearer $SIMPUL_API_KEY" \
-H "Content-Type: application/json" \
--data @- <<JSON
{ "reason": "Invoice dibatalkan oleh sistem sumber." }
JSON{
"data": {
"id": "018f0f79-16ea-7ad0-a9dd-120a8c001001",
"billing_number": "3512345678901234",
"status": "cancelled",
"amount_rupiah": "25000",
"paid_rupiah": "0",
"expires_at": "2026-10-09T06:30:00.000Z",
"payment_methods": [],
"created_at": "2026-10-08T06:30:00.000Z"
},
"request_id": "request-03",
"replayed": false
}Respons sukses berisi data, yaitu objek tagihan di bawah, dan request_id untuk pelacakan. Pembuatan dan pembatalan juga menyertakan replayed. Respons GET tidak menyertakan field tersebut.
idstring · UUIDbilling_numberstringstatusstringamount_rupiahstringpaid_rupiahstringexpires_atstring · UTCpayment_methodsarraycreated_atstring · UTCtypestringproviderstringdisplay_valuestringstatusstringexpires_atstring · UTCqr_content / qr_url / qr_imagestring | null · QRISBaca status dari respons GET untuk menentukan tindakan aplikasi. Webhook memberi tahu perubahan, lalu GET membantu memastikan keadaan terbaru jika event diterima berulang atau terlambat.
| Status | Arti | Tindakan aplikasi |
|---|---|---|
draft | Tagihan belum masuk proses penerbitan. | Tagihan baru dari API dimulai pada status issuing. |
issuing | Metode pembayaran sedang diterbitkan. | Tunggu webhook atau baca kembali dengan GET. Jangan tandai lunas. |
unpaid | Metode pembayaran siap, belum ada pembayaran. | Tampilkan metode pembayaran aktif dari respons GET. |
partially_paid | Sebagian nominal sudah dibayar. | Gunakan paid_rupiah untuk menampilkan jumlah yang sudah tercatat. |
paid | Pembayaran penuh sudah tercatat. | Tandai invoice lunas setelah verifikasi webhook atau pembacaan GET. |
expired | Batas waktu tagihan sudah lewat. | Hentikan penawaran pembayaran untuk tagihan ini. |
cancelled | Tagihan dibatalkan. | Hentikan pembayaran, simpan riwayat invoice. |
failed | Penerbitan metode pembayaran gagal. | Periksa tagihan dan hubungi operator sebelum membuat pengganti. |
needs_reconciliation | Hasil transaksi memerlukan pemeriksaan operator. | Tahan perubahan status akhir dan hubungi operator. Jangan membuat tagihan pengganti otomatis. |
Pembayaran sebagian yang melewati batas waktu dapat menghasilkan needs_reconciliation. Gunakan status dari API, jangan menghitung status akhir hanya dari waktu kedaluwarsa.
Gunakan satu Idempotency-Key stabil untuk satu invoice. Key berlaku dalam aplikasi Anda. SIMPUL PAD membandingkan isi request untuk mencegah tagihan ganda saat koneksi terputus atau request diulang.
202, dan replayed: false.200, dan replayed: true.409 IDEMPOTENCY_CONFLICT.Saat timeout, koneksi terputus, atau HTTP 503, coba kembali dengan key dan payload asli. Gunakan jeda yang bertambah antarpercobaan dan batasi jumlah percobaan di aplikasi Anda. Jika respons sudah berisi data.id, baca tagihan dengan GET.
Untuk HTTP 400, 401, 404, 409, 413, 415, dan 422, periksa kode error sebelum mengulang request.
Retry pembuatan tetap melalui validasi waktu kedaluwarsa. Jika expires_at tidak lagi memenuhi batas minimal 5 menit ke depan, gunakan GET dengan ID tagihan yang tersimpan. Jika ID belum diketahui, hubungi operator untuk memeriksa hasil request sebelum membuat tagihan pengganti.
Daftarkan URL HTTPS backend Anda pada detail aplikasi di portal pengembang. Simpan signing secret yang ditampilkan sekali. Endpoint harus berhasil menerima event verifikasi sebelum menerima event pembayaran.
X-Simpul-DeliverystringX-Simpul-EventstringX-Simpul-Timestampepoch detikX-Simpul-SignaturestringJSON.parse(). Bandingkan signature dengan perbandingan waktu konstan.id event untuk menghindari efek ganda, termasuk saat replay manual.data.bill_id dan baca tagihan dengan GET sebelum menyelaraskan status invoice.import { createHmac, timingSafeEqual } from "node:crypto";
// rawBody harus berupa body asli, sebelum JSON.parse().
// headers adalah objek Headers dari request webhook.
export function verifyWebhook(rawBody, headers, secret) {
const timestamp = headers.get("X-Simpul-Timestamp");
const signature = headers.get("X-Simpul-Signature");
if (!secret || !timestamp || !/^\d+$/.test(timestamp)) return false;
if (!signature || !/^v1=[a-f0-9]{64}$/i.test(signature)) return false;
const seconds = Number(timestamp);
const now = Math.floor(Date.now() / 1000);
if (!Number.isSafeInteger(seconds) || Math.abs(now - seconds) > 300) {
return false;
}
const expected = createHmac("sha256", secret)
.update(`${seconds}.${rawBody}`, "utf8")
.digest();
const received = Buffer.from(signature.slice(3), "hex");
return received.length === expected.length &&
timingSafeEqual(received, expected);
}{
"id": "bill_018f0f79-16ea-7ad0-a9dd-120a8c001001_unpaid",
"type": "bill.payment_method_ready",
"created_at": "2026-10-08T06:31:00.000Z",
"data": {
"bill_id": "018f0f79-16ea-7ad0-a9dd-120a8c001001",
"billing_number": "3512345678901234",
"status": "unpaid",
"expires_at": "2026-10-09T06:30:00.000Z",
"payment_methods": [
{
"type": "virtual_account",
"provider": "bank_bjb",
"display_value": "8881234567890123",
"expires_at": "2026-10-09T06:30:00.000Z"
}
]
}
}Bentuk data mengikuti jenis event. Contoh ini adalah bill.payment_method_ready. Fungsi di atas hanya memverifikasi signature. Tambahkan penyimpanan event dan deduplikasi pada handler Anda.
webhook.endpoint_verificationbill.payment_method_readybill.failedbill.needs_reconciliationbill.expiredpayment.partially_paidpayment.paidRespons 2xx menandai delivery berhasil. Gangguan jaringan, HTTP 408, 425, 429, dan 5xx dapat dicoba kembali sampai total 12 percobaan dengan jeda yang bertambah. Jeda dimulai sekitar 15 detik dan dibatasi 6 jam. Respons lain dianggap gagal permanen. Periksa riwayat delivery di portal pengembang untuk pengiriman yang gagal dan replay setelah masalah diperbaiki.
Respons error memiliki error.code, error.message, dan request_id. Field error.field disertakan bila error terkait field tertentu. Gunakan kode error untuk logika aplikasi, lalu gunakan pesan untuk penelusuran.
Simpan status HTTP, kode error, ID tagihan, dan request_id saat menghubungi operator BAPENDA. Hindari menyertakan API key, signing secret, atau data pribadi wajib retribusi dalam log.
{
"error": {
"code": "INVALID_BILL_REQUEST",
"message": "expires_at must be between five minutes and 366 days in the future.",
"field": "expires_at"
},
"request_id": "request-04"
}| HTTP | Kode error | Cara menangani |
|---|---|---|
| 400 | INVALID_BILL_REQUEST / INVALID_JSON / INVALID_BILL_ID / INVALID_CANCELLATION_REQUEST | Perbaiki JSON, field yang disebut pada error.field, UUID, atau alasan pembatalan. Field yang tidak dikenal juga ditolak. |
| 401 | INVALID_API_CREDENTIAL | Periksa Bearer key. Pastikan aplikasi disetujui dan key aktif, belum kedaluwarsa, serta belum dicabut. |
| 404 | BILL_NOT_FOUND / RETRIBUTION_SERVICE_NOT_FOUND | Periksa ID dan akses aplikasi ke tagihan atau layanan. Tagihan aplikasi lain tidak dapat dibaca. |
| 409 | IDEMPOTENCY_CONFLICT | Key sudah dipakai dengan payload berbeda. Gunakan kembali payload asli, atau key baru untuk invoice baru. |
| 409 | PAYMENT_ALREADY_RECORDED / PROVIDER_CANCELLATION_UNAVAILABLE / PROVIDER_OUTCOME_PENDING / RECONCILIATION_REQUIRED / BILL_NOT_CANCELLABLE | Pembatalan ditolak. Baca status tagihan dan hubungi operator jika perlu pemeriksaan transaksi. |
| 413 | BODY_TOO_LARGE | Kurangi ukuran body. Batas body JSON adalah 64 KiB. |
| 415 | UNSUPPORTED_MEDIA_TYPE | Kirim Content-Type: application/json pada request POST. |
| 422 | AMOUNT_OUTSIDE_TARIFF | Sesuaikan amount_rupiah dengan tarif atau batas nominal layanan. |
| 503 | BILLING_UNAVAILABLE / TARIFF_CONFIGURATION_UNAVAILABLE | Coba kembali dengan jeda bertambah. Untuk pembuatan, gunakan key dan payload asli. Hubungi operator jika berulang. |