Dokumentasi API v2
API v2 dipakai untuk membuat tagihan QRIS, memantau status pembayaran, dan menerima notifikasi webhook. Setiap project punya API key sendiri, sehingga integrasi satu project tidak bercampur dengan project lain.
1. Persiapan
- Buat project di menu Projects bila belum ada. Project menentukan tujuan dana dan slug integrasi.
- Buka menu Developers, pilih project, lalu tab API Keys dan tekan Buat API Key. Key hanya ditampilkan sekali, simpan di tempat aman.
- Di tab Webhook, isi URL HTTPS server Anda, lalu buat signing secret.
- Uji dengan tombol Kirim Webhook Uji sebelum dipakai produksi.
2. Autentikasi
Setiap permintaan wajib menyertakan header berikut.
| Header | Wajib | Keterangan |
|---|---|---|
Authorization | Ya | Bearer <API_KEY>, key diawali ik_live_ |
Content-Type | Untuk POST | application/json |
Idempotency-Key | Untuk POST | UUID v4, dibuat baru untuk setiap tagihan baru |
Contoh request
GET https://invoiceku.net/api/v2/projects
Authorization: Bearer ik_live_xxxxxxxxxxxxxxxx
Contoh response
HTTP/1.1 200 OK
Content-Type: application/json
{
"data": [
{
"id": "6",
"slug": "toko-utama",
"name": "Toko Utama",
"status": "active"
}
]
}
3. Idempotency
Jaringan bisa gagal di tengah jalan. Dengan Idempotency-Key, permintaan yang diulang
dengan key dan body yang sama tidak akan membuat tagihan kedua.
| Situasi | Yang terjadi |
|---|---|
| Key baru | Permintaan diproses normal |
| Key sama, body sama | Response pertama dikirim ulang, ditandai header Idempotency-Replayed: true |
| Key sama, body berbeda | 409 IDEMPOTENCY_CONFLICT |
| Key sama, masih diproses | 409 IDEMPOTENCY_IN_PROGRESS |
4. Buat invoice
Membuat tagihan QRIS baru untuk project milik API key yang dipakai.
Auth: Header Authorization + Idempotency-Key
Parameter
| Nama | Tipe | Wajib | Keterangan |
|---|---|---|---|
reference |
string | Tidak | Nomor tagihan Anda, maksimal 64 karakter, unik di dalam project. Bila kosong sistem membuat REF-... |
amount |
integer | Ya | Nominal rupiah tanpa desimal. Isi 0 hanya untuk donasi bersama allow_customer_amount. |
allow_customer_amount |
boolean | Tidak | Wajib true bila amount bernilai 0. |
description |
string | Tidak | Keterangan tagihan yang tampil ke pembeli. |
customer.name |
string | Tidak | Nama pembeli. |
customer.email |
string | Tidak | Email pembeli. Dipakai untuk mengirim tagihan dan pemberitahuan refund. |
customer.phone |
string | Tidak | Nomor telepon pembeli. |
expires_in |
integer | Tidak | Masa berlaku tagihan dalam detik, 300 sampai 86400. Default 3600. |
project_slug |
string | Tidak | Opsional. Bila diisi harus sama dengan project milik API key. |
Contoh request
POST https://invoiceku.net/api/v2/invoices
Authorization: Bearer ik_live_xxxxxxxxxxxxxxxx
Idempotency-Key: 6f1c2b8e-9a4d-4f3b-8c21-1d2e3f4a5b6c
Content-Type: application/json
{
"reference": "ORD-2026-0001",
"amount": 50000,
"description": "Pembelian Paket Premium",
"customer": {
"name": "Budi Santoso",
"email": "budi@example.com"
},
"expires_in": 3600
}
Contoh response
HTTP/1.1 201 Created
X-Request-Id: req_01JBQ4A7QY3E1M8V6J2K5N0P9R
Content-Type: application/json
{
"id": "5509",
"reference": "ORD-2026-0001",
"reference_source": "merchant",
"invoice_code": "INV-0FC6-5158",
"project": {
"id": "6",
"slug": "toko-utama",
"name": "Toko Utama"
},
"status": "pending",
"amount": 50207,
"fee": 207,
"net_amount": 50000,
"customer": {
"name": "Budi Santoso",
"email": "budi@example.com",
"phone": null
},
"description": "Pembelian Paket Premium",
"payment_url": "https://invoiceku.net/invoice/INV-0FC6-5158",
"qr_string": "00020101021126610016ID.CO.SHOPEE...",
"created_at": "2026-10-25T10:43:29Z",
"expires_at": "2026-10-25T11:43:30Z",
"paid_at": null,
"refunded_at": null
}
5. Ambil satu invoice
Mengambil detail invoice memakai reference milik Anda atau invoice_code dari InvoiceKu.
Auth: Header Authorization
Contoh request
GET https://invoiceku.net/api/v2/invoices/ORD-2026-0001 Authorization: Bearer ik_live_xxxxxxxxxxxxxxxx
Contoh response
HTTP/1.1 200 OK
Content-Type: application/json
{
"id": "5509",
"reference": "ORD-2026-0001",
"reference_source": "merchant",
"invoice_code": "INV-0FC6-5158",
"status": "paid",
"amount": 50207,
"fee": 207,
"net_amount": 50000,
"payment_url": "https://invoiceku.net/invoice/INV-0FC6-5158",
"created_at": "2026-10-25T10:43:29Z",
"expires_at": "2026-10-25T11:43:30Z",
"paid_at": "2026-10-25T10:45:02Z",
"refunded_at": null
}
6. Daftar invoice
Menampilkan invoice milik project dengan pagination cursor.
Auth: Header Authorization
Parameter
| Nama | Tipe | Wajib | Keterangan |
|---|---|---|---|
status |
query | Tidak | pending, paid, expired, failed, atau refunded. |
project_slug |
query | Tidak | Filter project (harus project milik key). |
created_from |
query | Tidak | Batas awal waktu, format ISO-8601. |
created_to |
query | Tidak | Batas akhir waktu, format ISO-8601. |
limit |
query | Tidak | Jumlah data per halaman, 1 sampai 100. Default 20. |
cursor |
query | Tidak | Nilai next_cursor dari halaman sebelumnya. |
Contoh request
GET https://invoiceku.net/api/v2/invoices?status=paid&limit=20 Authorization: Bearer ik_live_xxxxxxxxxxxxxxxx
Contoh response
HTTP/1.1 200 OK
Content-Type: application/json
{
"data": [
{
"id": "5509",
"reference": "ORD-2026-0001",
"invoice_code": "INV-0FC6-5158",
"status": "paid",
"amount": 50207,
"net_amount": 50000,
"paid_at": "2026-10-25T10:45:02Z"
}
],
"has_more": true,
"next_cursor": "5460"
}
7. Batalkan invoice
Membatalkan invoice yang masih pending. Invoice yang sudah dibayar tidak dapat dibatalkan.
Auth: Header Authorization + Idempotency-Key
Parameter
| Nama | Tipe | Wajib | Keterangan |
|---|---|---|---|
reason |
string | Tidak | Alasan pembatalan, maksimal 255 karakter. |
Contoh request
POST https://invoiceku.net/api/v2/invoices/ORD-2026-0001/cancel
Authorization: Bearer ik_live_xxxxxxxxxxxxxxxx
Idempotency-Key: 9c1b7e44-2f1a-4c8e-9b77-0a1b2c3d4e5f
Content-Type: application/json
{
"reason": "Pembeli membatalkan pesanan"
}
Contoh response
HTTP/1.1 200 OK
Content-Type: application/json
{
"id": "5509",
"reference": "ORD-2026-0001",
"invoice_code": "INV-0FC6-5158",
"status": "failed",
"amount": 50207,
"net_amount": 50000
}
8. Project milik key
Menampilkan project yang terkait dengan API key. Satu key hanya untuk satu project.
Auth: Header Authorization
Contoh request
GET https://invoiceku.net/api/v2/projects Authorization: Bearer ik_live_xxxxxxxxxxxxxxxx
Contoh response
HTTP/1.1 200 OK
Content-Type: application/json
{
"data": [
{
"id": "6",
"slug": "toko-utama",
"name": "Toko Utama",
"status": "active",
"callback_url": null,
"webhook_url": "https://toko.example.com/webhook/invoiceku"
}
]
}
9. Webhook
Webhook dikirim ke URL yang Anda isi di menu Developers, tab Webhook. Setiap project punya URL dan secret sendiri.
Event
| Event | Kapan dikirim |
|---|---|
invoice.paid | Pembayaran diterima |
invoice.expired | Tagihan melewati batas waktu |
invoice.failed | Tagihan dibatalkan atau gagal |
invoice.refunded | Tagihan direfund ke customer |
webhook.test | Uji koneksi dari halaman Developers |
Header
POST /webhook/invoiceku HTTP/1.1 Content-Type: application/json X-InvoiceKu-Event: invoice.paid X-InvoiceKu-Event-Signature: t=1793000000,v1=6f1c2b8e9a4d4f3b8c211d2e3f4a5b6c7d8e9f0a1b2c3d4e5f60718293a4b5c6 X-InvoiceKu-Signature: 9b2d...47 X-InvoiceKu-Timestamp: 1793000000
Contoh payload
{
"event": "invoice.paid",
"delivery_id": null,
"invoice": {
"id": "5509",
"reference": "ORD-2026-0001",
"reference_source": "merchant",
"invoice_code": "INV-0FC6-5158",
"project": {
"id": "6",
"slug": "toko-utama",
"name": "Toko Utama"
},
"status": "paid",
"amount": 50207,
"fee": 207,
"net_amount": 50000,
"customer": {
"name": "Budi Santoso",
"email": "budi@example.com",
"phone": null
},
"payment_url": "https://invoiceku.net/invoice/INV-0FC6-5158",
"created_at": "2026-10-25T10:43:29Z",
"expires_at": "2026-10-25T11:43:30Z",
"paid_at": "2026-10-25T10:45:02Z",
"refunded_at": null
}
}
Verifikasi tanda tangan
Ambil nilai t dan v1 dari header, lalu bandingkan hasil HMAC berikut.
<?php
$rawBody = file_get_contents('php://input');
$header = $_SERVER['HTTP_X_INVOICEKU_EVENT_SIGNATURE'] ?? '';
$secret = 'secret_dari_halaman_developers';
if (!preg_match('/t=(\d+),v1=([a-f0-9]{64})/', $header, $m)) {
http_response_code(400);
exit('signature tidak valid');
}
$timestamp = (int) $m[1];
if (abs(time() - $timestamp) > 300) {
http_response_code(400);
exit('timestamp kedaluwarsa');
}
$expected = hash_hmac('sha256', $timestamp . '.' . $rawBody, $secret);
if (!hash_equals($expected, $m[2])) {
http_response_code(400);
exit('signature tidak cocok');
}
http_response_code(200);
echo 'ok';
Kebijakan pengiriman
| Aturan | Nilai |
|---|---|
| Dianggap berhasil | Balasan HTTP 2xx dalam 10 detik |
| Percobaan ulang | Maksimal 8 kali: 1m, 5m, 15m, 1j, 3j, 6j, 12j, 24j |
| Duplikat | Mungkin terjadi, proses secara idempoten memakai invoice.invoice_code |
| Riwayat | Menu Developers, tab Event Log |
10. Alur aman di sisi Anda
- Simpan reference tagihan di database Anda sebelum mengirim permintaan.
- Buat
Idempotency-Keybaru (UUID v4) saat pertama kali membuat tagihan. - Bila permintaan gagal karena timeout, ulangi dengan key yang sama, bukan key baru.
- Jangan mengandalkan webhook saja. Saat pembeli kembali ke halaman sukses, panggil endpoint ambil invoice untuk memastikan status.
- Pada webhook, balas 200 segera setelah validasi tanda tangan, lalu proses di belakang.
11. Kode error
Semua error memakai bentuk yang sama.
HTTP/1.1 422 Unprocessable Entity
Content-Type: application/json
{
"error": {
"code": "AMOUNT_BELOW_MINIMUM",
"message": "Nominal minimal Rp 1.000.",
"request_id": "req_01JBQ4A7QY3E1M8V6J2K5N0P9R"
}
}
Sertakan request_id ketika menghubungi support agar penelusuran cepat.
| HTTP | Code | Arti |
|---|---|---|
| 400 | INVALID_JSON | Body bukan JSON yang sah |
| 400 | INVALID_IDEMPOTENCY_KEY | Header Idempotency-Key bukan UUID v4 |
| 401 | UNAUTHORIZED | Header Authorization tidak ada atau format salah |
| 401 | INVALID_API_KEY | API key tidak terdaftar |
| 401 | KEY_REVOKED | API key sudah dicabut |
| 403 | PROJECT_FORBIDDEN | Project pada body bukan milik key |
| 403 | PROJECT_INACTIVE | Project terkait key tidak aktif |
| 404 | INVOICE_NOT_FOUND | Invoice tidak ditemukan |
| 404 | PROJECT_NOT_FOUND | Project tidak ditemukan |
| 405 | METHOD_NOT_ALLOWED | Metode HTTP tidak didukung |
| 409 | IDEMPOTENCY_CONFLICT | Idempotency-Key sudah dipakai untuk body berbeda |
| 409 | IDEMPOTENCY_IN_PROGRESS | Permintaan dengan key yang sama masih diproses |
| 409 | INVOICE_STATE_CONFLICT | Status invoice tidak mengizinkan aksi |
| 409 | REFERENCE_DUPLICATE | Reference sudah dipakai di project ini |
| 422 | AMOUNT_REQUIRED | Field amount tidak diisi |
| 422 | AMOUNT_BELOW_MINIMUM | Nominal di bawah batas minimum |
| 422 | AMOUNT_ABOVE_MAXIMUM | Nominal melebihi batas maksimum |
| 422 | DONATION_FLAG_REQUIRED | amount bernilai 0 tanpa allow_customer_amount |
| 422 | VALIDATION_FAILED | Format field lain tidak sah |
| 429 | RATE_LIMITED | Melebihi kuota permintaan |
| 500 | INTERNAL_ERROR | Kesalahan pada server |
| 503 | GATEWAY_UNAVAILABLE | Gateway pembayaran sedang sibuk |
12. Rate limit
120 permintaan per menit untuk setiap API key project.
| Header respon | Keterangan |
|---|---|
X-Request-Id | ID permintaan untuk penelusuran |
X-RateLimit-Limit | Batas permintaan per menit |
X-RateLimit-Remaining | Sisa kuota pada menit berjalan |
X-RateLimit-Reset | Waktu pengisian ulang kuota (epoch detik) |
Retry-After | Hanya pada 429, jumlah detik untuk menunggu |
13. Riwayat perubahan
| Versi | Tanggal | Isi |
|---|---|---|
| 2.0 | 25 Oktober 2026 | Rilis awal: API key per project, Idempotency-Key, rate limit, cursor pagination, webhook bertanda tangan. |
API v1 tetap tersedia di /docs/api. Butuh bantuan? Buka menu Bantuan & Tiket di dashboard.