API Documentation
Panduan integrasi REST API untuk merchant yang ingin menghubungkan sistem pembayaran mereka.
Machine-readable spec (OpenAPI 3.0)
Butuh spesifikasi dalam format terstruktur untuk AI, Postman, atau code generator? Download /openapi.json — deskripsi lengkap semua endpoint, schema request/response, autentikasi, dan webhook. Bisa langsung disalin ke AI assistant atau di-import ke Postman.
Arsitektur
API ini adalah payment gateway abstraksi di atas LinkQu. Anda tidak perlu integrasi langsung dengan LinkQu. Cukup gunakan API kami dan kami yang mengurus semua komunikasi dengan payment provider.
Quick Start
1. Dapatkan API Credentials
Daftar dan verifikasi KYC. Buat API Client dari dashboard merchant di menu API Integration.
2. Base URL
https://merchant.jualpulsaaja.com/api/v1
3. Authentication
Gunakan Bearer token di header:
Authorization: Bearer {client_id}:{client_secret}
Format: Bearer spasi client_id titik dua client_secret. client_id dan client_secret hanya boleh berisi karakter A-Z a-z 0-9 _ - (divalidasi dengan regex); header dengan format lain ditolak 401 invalid_authorization_header.
4. Content Type
Content-Type: application/json
5. Idempotency untuk request mutasi
Untuk endpoint yang membuat/mengubah transaksi, kirim header X-Idempotency-Key yang unik per order/request. Jika request yang sama terkirim dua kali, sistem akan mengembalikan response pertama dan tidak membuat transaksi dobel.
X-Idempotency-Key: order-INV-2026-0001
Header ini optional untuk kompatibilitas, tapi sangat direkomendasikan untuk integrasi produksi. Ketentuan:
- Key maksimal 128 karakter, charset
A-Z a-z 0-9 : _ - .— key tidak valid ditolak422 invalid_idempotency_key. - Key yang sama dengan body berbeda ditolak
409 idempotency_key_conflict. - Dua request duplikat yang berjalan bersamaan (concurrent): request kedua ditolak
409 idempotency_key_in_progress. - Response replay ditandai header
X-Idempotency-Replay: true(request pertama:false). - Kunci berlaku 24 jam dan scoped per API client.
- Hanya response sukses (2xx) yang di-replay — percobaan yang gagal akan dieksekusi ulang saat request dikirim kembali dengan key yang sama.
Endpoints
/api/v1/qris
Buat QRIS baru untuk menerima pembayaran. Rate limit: 60 request/menit per alamat IP.
{
"amount": 50000,
"customer_name": "Nama Customer",
"customer_email": "customer@email.com",
"customer_phone": "08123456789",
"description": "Pembayaran invoice #001"
}
amount— wajib, integer, minimal 10.000customer_name— opsional, max 100 karaktercustomer_email— opsional, email valid, max 200 karaktercustomer_phone— opsional, max 50 karakterdescription— opsional, max 200 karakter
{
"ok": true,
"transaction": {
"id": 42,
"partner_reff": "QR-17817279850470051400",
"amount": 50000,
"status": "waiting_payment",
"expires_at": "2026-06-18T04:00:00+00:00"
},
"qris": {
"qr_image_url": "https://...",
"qr_string": "00020101...",
"qris_id": "QRIS123456"
},
"fees": {
"linkqu_fee": 700,
"platform_fee": 300,
"total_fee": 1000,
"net_amount": 49000
}
}
Object qris dapat bernilai null jika data QR belum tersedia dari provider. Nilai fee pada contoh hanya ilustrasi — fee aktual mengikuti pengaturan platform dan selalu dikembalikan di object fees.
{
"ok": false,
"error": "upstream_error",
"upstream_status": 500,
"message": "LinkQu API error"
}
/api/v1/virtual-accounts
Ambil daftar Virtual Account (VA) aktif untuk menerima pembayaran.
{
"ok": true,
"data": [
{
"id": 1,
"bank_code": "002",
"bank_name": "BRI",
"virtual_account_number": "1234567890123456",
"account_name": "Merchant Name",
"status": "active",
"created_at": "2026-06-18T10:00:00+00:00"
}
]
}
/api/v1/virtual-accounts
Buat atau dapatkan Virtual Account (VA) untuk menerima pembayaran. VA bersifat permanen untuk setiap merchant+bank. Jika VA baru dibuat, response berstatus 201. Jika VA untuk bank tersebut sudah ada, sistem mengembalikan VA yang sudah ada dengan status 200.
{
"bank_code": "002"
}
Bank codes yang tersedia untuk dedicated VA saat ini: 002 (BRI) dan 013 (Permata).
{
"ok": true,
"virtual_account": {
"id": 1,
"bank_code": "002",
"bank_name": "BRI",
"virtual_account_number": "1234567890123456",
"account_name": "Merchant Name",
"status": "active",
"created_at": "2026-06-18T10:00:00+00:00"
}
}
{
"ok": false,
"error": "bank_not_enabled_for_dedicated_va",
"message": "Bank tersebut belum tersedia untuk dedicated VA.",
"supported_bank_codes": ["002", "013"]
}
/api/v1/virtual-accounts/{id}
Ambil detail Virtual Account berdasarkan ID.
{
"ok": true,
"virtual_account": {
"id": 1,
"bank_code": "002",
"bank_name": "BRI",
"virtual_account_number": "1234567890123456",
"account_name": "Merchant Name",
"status": "active",
"created_at": "2026-06-18T10:00:00+00:00"
}
}
One-time Virtual Account (Pembayaran per-Transaksi)
Untuk menerima pembayaran dari customer dengan nominal spesifik. Tiap transaksi menghasilkan VA baru dengan jumlah & masa aktif tetap. Cocok untuk: e-commerce, invoice, donasi, billing.
/api/v1/payments/va/banks
Ambil daftar bank one-time VA yang sudah terverifikasi terbaca bank pada akun LinkQu kami. Daftar ini mengikuti hasil diagnostic nyata, bukan sekadar daftar capability LinkQu.
{
"ok": true,
"banks": [
{ "code": "002", "name": "BRI", "method": "va" },
{ "code": "022", "name": "CIMB Niaga", "method": "va" },
{ "code": "013", "name": "Permata", "method": "va" }
]
}
/api/v1/payments/va
Buat VA sekali-pakai untuk menerima pembayaran dari customer. Customer transfer ke nomor VA tersebut dengan nominal tepat sebelum expires_at. Setelah dibayar, callback akan otomatis mengkredit saldo merchant.
{
"amount": 100000,
"bank_code": "002",
"customer_name": "Budi Santoso",
"customer_email": "budi@example.com",
"customer_phone": "081234567890",
"expires_in_hours": 24,
"description": "Order #INV-2026-0001"
}
{
"ok": true,
"payment": {
"partner_reff": "VA-6A41CFBBA9E1A-49d6",
"amount": 100000,
"status": "waiting_payment",
"bank_code": "002",
"bank_name": "BANK BRI",
"virtual_account_number": "1456195200000009",
"account_name": "Budi Santoso",
"expires_at": "2026-06-30T08:53:31+07:00"
},
"fees": {
"linkqu_fee": 2000,
"platform_fee": 0,
"total_fee": 2000,
"net_amount": 98000
}
}
amount— integer, 10.000 s/d 2.000.000.000bank_code— kode bank dari endpoint/payments/va/banks
customer_name,customer_email,customer_phoneexpires_in_hours— default 24, max 720 (30 hari)description— catatan transaksi (max 200 char)
/api/v1/payments/va/{partner_reff}
Cek status pembayaran VA. Polling endpoint ini sampai status menjadi "paid" (sukses), "expired" (lewat masa aktif), atau "failed".
{
"ok": true,
"payment": {
"partner_reff": "VA-6A41CFBBA9E1A-49d6",
"amount": 100000,
"status": "waiting_payment",
"bank_code": "002",
"bank_name": "BANK BRI",
"virtual_account_number": "1456195200000009",
"account_name": "Budi Santoso",
"expires_at": "2026-06-30T08:53:31+07:00",
"paid_at": null,
"failed_at": null
},
"fees": {
"linkqu_fee": 2000,
"platform_fee": 0,
"total_fee": 2000,
"net_amount": 98000
}
}
/api/v1/transactions
Ambil daftar transaksi milik merchant.
per_page— jumlah data per halaman (default 20, max 100)page— halaman ke-
{
"ok": true,
"data": [
{
"partner_reff": "QR-17817279850470051400",
"type": "qris",
"status": "paid",
"amount": 50000,
"total_fee": 500,
"net_amount": 49500,
"created_at": "2026-06-18T10:00:00+00:00"
}
],
"meta": {
"current_page": 1,
"last_page": 5,
"total": 100
}
}
/api/v1/transactions/{partner_reff}
Ambil detail transaksi berdasarkan partner_reff, termasuk event log.
{
"ok": true,
"transaction": {
"partner_reff": "QR-17817279850470051400",
"type": "qris",
"status": "paid",
"amount": 50000,
"paid_at": "2026-06-18T10:05:00+00:00",
"expires_at": "2026-06-18T10:30:00+00:00",
"created_at": "2026-06-18T10:00:00+00:00"
},
"fees": {
"linkqu_fee": 350,
"platform_fee": 150,
"total_fee": 500,
"net_amount": 49500
},
"events": [
{
"event_type": "created",
"from": null,
"to": "created",
"source": "api",
"description": "QRIS created via API",
"at": "2026-06-18T10:00:00+00:00"
},
{
"event_type": "callback_received",
"from": "waiting_payment",
"to": "paid",
"source": "linkqu_callback",
"description": "Payment received via LinkQu callback",
"at": "2026-06-18T10:05:00+00:00"
}
]
}
Nilai event_type yang mungkin: created, qris_created, callback_received, payout_success, payout_failed, settlement, reconcile. Nilai source: user, api, linkqu_callback, scheduler.
Webhook Callback (Notifikasi Pembayaran)
Setiap kali transaksi Anda berubah status ke paid, expired, atau failed, sistem kami akan mengirim POST ke URL callback yang Anda daftarkan di API client. URL ini diatur di /api-clients.
{api_clients.callback_url}
Sistem akan POST JSON notifikasi setiap kali ada transaksi yang settle.
Content-Type: application/json Accept: application/json User-Agent: MerchantJualPulsaAja-Webhook/1.0 X-Webhook-Event: transaction.paid X-Webhook-Delivery: 123 X-Webhook-Signature: <hmac-sha256>
X-Webhook-Delivery adalah ID unik pengiriman webhook — simpan untuk keperluan pelaporan/deduplikasi.
transaction.paid— Customer sudah bayar, saldo merchant bertambahtransaction.expired— Transaksi kadaluarsa. Saat ini event ini hanya terkirim jika payment provider mengirim callback expired; transaksi yang di-expire secara lokal oleh sistem tidak memicu webhook — gunakan polling status sebagai pelengkap.transaction.failed— Pembayaran gagal / refund / cancel
- Connect timeout 10 detik, total timeout 30 detik — endpoint Anda harus merespon dalam waktu kurang dari 30 detik.
- Redirect tidak diikuti — URL callback harus merespon langsung tanpa redirect (301/302 dianggap gagal).
- Verifikasi SSL aktif — sertifikat HTTPS endpoint Anda harus valid.
{
"event": "transaction.paid",
"partner_reff": "17828138963880051406",
"transaction": {
"partner_reff": "17828138963880051406",
"type": "qris",
"status": "paid",
"amount": 10000,
"fees": {
"linkqu_fee": 70,
"platform_fee": 30,
"total_fee": 100,
"net_amount": 9900
},
"bank_code": null,
"bank_name": null,
"account_number": null,
"account_name": null,
"product_code": "QRIS",
"payment_method": "qris",
"paid_at": "2026-06-30T17:35:05+07:00",
"failed_at": null,
"expires_at": "2026-06-30T17:34:56+07:00",
"created_at": "2026-06-30T17:04:56+07:00"
}
}
linkqu_fee adalah biaya gateway yang kami terima dari payment provider. platform_fee adalah fee platform sesuai pengaturan fee merchant. total_fee adalah total biaya yang dicatat sistem untuk transaksi tersebut. Besaran fee mengikuti pengaturan platform dan selalu dikembalikan di object fees.
X-Webhook-Signature sudah dikirim, tetapi signature ini belum bisa diverifikasi oleh merchant dan skemanya akan di-upgrade. Untuk sementara, gunakan HTTPS, validasi struktur payload, cek partner_reff lewat endpoint detail transaksi (GET /api/v1/transactions/{partner_reff}) sebelum memproses, dan whitelist sumber request jika diperlukan.
HTTP 2xx (200 / 201 / 202 / 204) — dianggap sukses HTTP 4xx/5xx, redirect, atau timeout — dianggap gagal
Kami TIDAK melakukan auto-retry. Setiap delivery yang gagal (HTTP 4xx/5xx, network error, atau callback URL belum diatur) tercatat di sistem kami. Jika Anda memerlukan pengiriman ulang webhook, silakan hubungi support untuk resend. Sebagai fallback, gunakan polling GET /api/v1/transactions/{partner_reff}.
Webhook hanya dikirim untuk transaksi yang dibuat melalui API Anda (via POST /api/v1/transactions atau endpoint one-time VA). Transaksi yang dibuat lewat dashboard merchant tidak memicu webhook ke callback URL Anda.
/api/v1/devices
Register device token untuk push notification (FCM/OneSignal).
{
"token": "onesignal_player_id_atau_fcm_token",
"platform": "web",
"app_version": "1.0.0",
"device_name": "Chrome on Windows"
}
token— wajib, max 255 karakterplatform— wajib, salah satu dariandroid,ios,web
{
"ok": true,
"device": {
"id": 12,
"platform": "web",
"last_seen_at": "2026-06-18T10:00:00+00:00"
}
}
/api/v1/devices/{token}
Unregister device token (soft delete). Field disabled bernilai true jika ada device yang dinonaktifkan, false jika token tidak ditemukan.
{
"ok": true,
"disabled": true
}
/api/v1/ewallet/inquiry
Inquiry untuk top-up e-wallet (OVO, DANA, GoPay, dll). Memerlukan KYC terverifikasi dan saldo cukup.
{
"amount": 50000,
"product_code": "OVO",
"phone_number": "08123456789"
}
Product codes: OVO, DANA, GOPAY, SHOPEEPAY, LINKAJA
amount— wajib, integer, 10.000 s/d 5.000.000phone_number— wajib, format08xxatau628xxproduct_code— wajib, dari endpoint/ewallet/products
{
"ok": true,
"transaction": {
"id": 42,
"partner_reff": "EW-ABC123",
"amount": 50000,
"status": "pending",
"product_code": "OVO",
"phone_number": "08123456789"
},
"fees": {
"linkqu_fee": 700,
"platform_fee": 300,
"total_fee": 1000,
"net_amount": 49000
}
}
Nilai fee pada contoh hanya ilustrasi — fee aktual mengikuti pengaturan platform dan dikembalikan di object fees.
/api/v1/ewallet/confirm
Konfirmasi top-up e-wallet dengan PIN. Saldo merchant ditahan (di-debit) sebelum eksekusi ke provider.
{
"partner_reff": "EW-ABC123",
"pin": "123456"
}
pin — wajib, tepat 6 digit angka.
{
"ok": true,
"transaction": {
"partner_reff": "EW-ABC123",
"status": "paid",
"amount": 50000
}
}
{
"ok": true,
"transaction": {
"partner_reff": "EW-ABC123",
"status": "pending",
"amount": 50000
},
"message": "Transaksi sedang diproses. Status final akan dikirim via webhook."
}
pending (HTTP 200, ok: true). Status final datang lewat webhook transaction.paid / transaction.failed, atau polling GET /api/v1/transactions/{partner_reff}. Jika gagal (422 topup_failed), dana yang ditahan otomatis dikembalikan ke saldo.
/api/v1/ewallet/products
Ambil daftar produk e-wallet yang tersedia.
{
"ok": true,
"data": [
{"code": "DANA", "name": "DANA", "category": "e-wallet"},
{"code": "OVO", "name": "OVO", "category": "e-wallet"},
{"code": "GOPAY", "name": "GoPay", "category": "e-wallet"}
]
}
/api/v1/transfer/inquiry
Inquiry untuk transfer bank. Mengembalikan nama pemilik rekening dan detail fee. Memerlukan KYC terverifikasi.
{
"amount": 100000,
"bank_code": "014",
"account_number": "1234567890"
}
amount— wajib, integer, 10.000 s/d 50.000.000bank_code— wajib, dari endpoint/transfer/banksaccount_number— wajib, 4 s/d 64 karakter
{
"ok": true,
"transaction": {
"id": 43,
"partner_reff": "TRF-ABC123",
"amount": 100000,
"status": "pending",
"bank_code": "014",
"bank_name": "BCA",
"account_number": "1234567890",
"account_name": "JOHN DOE"
},
"fees": {
"linkqu_fee": 2000,
"platform_fee": 500,
"total_fee": 2500,
"net_amount": 97500,
"total_debit": 102500
}
}
Nilai fee pada contoh hanya ilustrasi — fee aktual mengikuti pengaturan platform dan dikembalikan di object fees.
/api/v1/transfer/confirm
Konfirmasi transfer bank dengan PIN. Saldo merchant ditahan (di-debit) sebelum eksekusi ke provider.
{
"partner_reff": "TRF-ABC123",
"pin": "123456"
}
pin — wajib, tepat 6 digit angka.
{
"ok": true,
"transaction": {
"partner_reff": "TRF-ABC123",
"status": "paid",
"amount": 100000
}
}
{
"ok": true,
"transaction": {
"partner_reff": "TRF-ABC123",
"status": "pending",
"amount": 100000
},
"message": "Transfer sedang diproses. Status final akan dikirim via webhook."
}
pending (HTTP 200, ok: true). Status final datang lewat webhook transaction.paid / transaction.failed, atau polling GET /api/v1/transactions/{partner_reff}. Jika gagal (422 transfer_failed), dana yang ditahan otomatis dikembalikan ke saldo.
/api/v1/transfer/banks
Ambil daftar bank yang tersedia untuk transfer.
{
"ok": true,
"data": [
{"code": "008", "name": "BANK MANDIRI"},
{"code": "014", "name": "BANK BCA"},
{"code": "002", "name": "BANK BRI"}
]
}
E-Wallet Cash-In
Status Transaksi
created
Transaksi baru dibuat
waiting_payment
Menunggu pembayaran dari customer
pending
Payout (transfer/e-wallet) sedang diproses, menunggu status final dari provider
paid
Pembayaran berhasil diterima / payout berhasil dieksekusi
settled
Dana sudah di-settle ke saldo merchant (settled_at diisi saat settlement)
failed
Transaksi gagal
expired
Transaksi kedaluwarsa. QRIS berlaku 30 menit; VA one-time mengikuti expires_in_hours (default 24 jam, max 720 jam)
cancelled
Transaksi dibatalkan
manual_review
Transaksi ditahan untuk pemeriksaan manual oleh tim kami
refunded
Dana dikembalikan sepenuhnya
partially_refunded
Dana dikembalikan sebagian
Alur Pembayaran
QRIS (Pembayaran Masuk)
- Merchant panggil
POST /api/v1/qrisdengan amount - Sistem buat QR code dan kembalikan ke merchant
- Merchant tampilkan QR ke customer
- Customer scan dan bayar
- Sistem terima callback dari LinkQu
- Saldo merchant otomatis bertambah
Virtual Account (Pembayaran Masuk)
- Merchant panggil
POST /api/v1/virtual-accountsdengan bank_code - Sistem buat VA number permanen untuk merchant
- Merchant bagikan VA number ke customer
- Customer transfer ke VA number
- Sistem terima callback dari LinkQu
- Saldo merchant otomatis bertambah
E-Wallet Top-up (Pengeluaran)
- Merchant panggil
POST /api/v1/ewallet/inquirydengan amount, product_code, phone_number - Sistem kembalikan detail transaksi dan fee
- Merchant panggil
POST /api/v1/ewallet/confirmdengan PIN — saldo ditahan (di-debit) sebelum eksekusi - Response bisa
paid(sukses langsung) ataupending(masih diproses) - Jika
pending, tunggu webhooktransaction.paid/transaction.failedatau pollingGET /api/v1/transactions/{partner_reff} - Jika gagal, dana yang ditahan otomatis dikembalikan ke saldo
Transfer Bank (Pengeluaran)
- Merchant panggil
POST /api/v1/transfer/inquirydengan amount, bank_code, account_number - Sistem kembalikan nama pemilik rekening dan detail fee
- Merchant panggil
POST /api/v1/transfer/confirmdengan PIN — saldo ditahan (di-debit) sebelum eksekusi - Response bisa
paid(sukses langsung) ataupending(masih diproses) - Jika
pending, tunggu webhooktransaction.paid/transaction.failedatau pollingGET /api/v1/transactions/{partner_reff} - Jika gagal, dana yang ditahan otomatis dikembalikan ke saldo
E-Wallet Cash In (Pembayaran Masuk)
E-Wallet Cash-In saat ini tersedia melalui dashboard merchant, belum tersedia via API.
Error Codes
Format Error
Semua error mengikuti envelope JSON yang sama:
{
"ok": false,
"error": "<kode>",
"message": "Penjelasan yang bisa dibaca manusia"
}
Field message hanya teks untuk manusia dan bisa berubah sewaktu-waktu — logika client wajib mengacu pada field error (kode mesin), bukan message. Beberapa error menyertakan field tambahan (mis. supported_bank_codes pada bank_not_enabled_for_dedicated_va, atau upstream_status pada upstream_error).
200
Berhasil
201
Berhasil dibuat
401
Auth gagal — invalid_authorization_header (format header salah), invalid_client (client_id/secret salah)
403
Akses ditolak — client_not_active, permission_denied, ip_not_allowed, kyc_required, feature_disabled
404
Resource tidak ditemukan — not_found
409
Konflik idempotency — idempotency_key_conflict (key sama, body beda), idempotency_key_in_progress (request duplikat masih diproses)
422
Validasi/bisnis gagal — validation, invalid_idempotency_key, insufficient_balance, pin_required, invalid_pin, unsupported_bank, invalid_argument, transfer_failed, topup_failed, bank_not_enabled_for_dedicated_va
429
Rate limit terlampaui (60 req/menit per alamat IP)
500
Server error — internal_error
502
Payment provider error — upstream_error
Catatan: kegagalan validasi input dikembalikan sebagai 422, bukan 400.
Keamanan
HTTPS Only
Semua request harus menggunakan HTTPS
Bearer Token Auth
Format: Bearer {client_id}:{client_secret}
IP Whitelist (Opsional)
Batasi akses dari IP tertentu
Permission System
Setiap API client punya permission spesifik
Rate Limiting
60 request per menit per alamat IP (bukan per API client). Kuota dibagi antar semua endpoint dan semua API client yang mengakses dari IP yang sama; request yang gagal auth (401) juga menghabiskan kuota.
Permissions
qris:create
POST /qris — buat QRIS baru
qris:read
GET /transactions dan GET /transactions/{partner_reff} — baca daftar/detail transaksi (mencakup semua tipe transaksi, bukan hanya QRIS)
va:create
POST /virtual-accounts, GET /payments/va/banks, POST /payments/va — buat dedicated VA dan one-time VA
va:read
GET /virtual-accounts, GET /virtual-accounts/{id}, GET /payments/va/{partner_reff} — baca VA dan status pembayaran VA
device:write
POST /devices, DELETE /devices/{token} — register/unregister device
ewallet:create
Semua endpoint /ewallet/* (inquiry, confirm, products)
transfer:create
Semua endpoint /transfer/* (inquiry, confirm, banks)
Catatan: beberapa endpoint GET memerlukan scope create, bukan read: GET /payments/va/banks butuh va:create, GET /ewallet/products butuh ewallet:create, dan GET /transfer/banks butuh transfer:create.
Butuh bantuan integrasi?
Hubungi Support