API SmartPG memungkinkan sistem Anda membuat tagihan QRIS dan Virtual Account (BCA, BNI, BRI, Mandiri), mengecek status, dan menerima notifikasi (callback) saat pembayaran lunas.
Content-Type: application/json)50000)POST /qris/create atau POST /va/create.checkout_url (halaman bayar siap pakai).GET /transactions/{trx_id}.Setiap request wajib menyertakan API key pada header:
X-API-KEY: spg_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx Content-Type: application/json Accept: application/json
Alternatif: Authorization: Bearer <api_key>. Jika Anda mengisi IP Whitelist di portal, request dari IP lain akan ditolak (IP_NOT_ALLOWED).
Semua respons memakai struktur yang sama:
// Sukses
{
"success": true,
"code": "SUCCESS",
"message": "QRIS berhasil dibuat",
"data": { ... }
}
// Gagal
{
"success": false,
"code": "VALIDATION_ERROR",
"message": "The amount field is required.",
"data": { "amount": ["The amount field is required."] }
}
Setiap produk memiliki biaya admin (flat dan/atau persen) yang diatur per mitra. Biaya bisa ditanggung:
total_amount = amount + admin_fee, Anda menerima amount.total_amount = amount, Anda menerima amount − admin_fee.Nilai pasti ada di respons: admin_fee, total_amount (yang dibayar customer) dan net_amount (yang masuk ke saldo Anda).
| product_code | Produk | Biaya default |
|---|---|---|
| QRIS | QRIS | Rp 500 |
| VA_BCA | Virtual Account BCA | Rp 5.000 |
| VA_BNI | Virtual Account BNI | Rp 5.000 |
| VA_BRI | Virtual Account BRI | Rp 5.000 |
| VA_MANDIRI | Virtual Account Mandiri | Rp 5.000 |
Biaya aktual akun Anda bisa berbeda — cek di Portal Mitra atau GET /products.
Produk aktif untuk akun Anda beserta biaya & batas nominal.
{
"success": true,
"code": "SUCCESS",
"message": "Success",
"data": [
{
"product_code": "QRIS",
"name": "QRIS",
"payment_method": "qris",
"fee_flat": 500,
"fee_percent": 0,
"fee_bearer": "customer",
"min_amount": 1000,
"max_amount": 10000000
},
{
"product_code": "VA_BCA",
"name": "Virtual Account BCA",
"payment_method": "va",
"fee_flat": 5000,
"fee_percent": 0,
"fee_bearer": "customer",
"min_amount": 10000,
"max_amount": 50000000
}
]
}
Membuat QRIS dinamis (MPM) dengan nominal tertentu. QR berlaku ±15 menit.
| Field | Tipe | Wajib | Keterangan |
|---|---|---|---|
| reference_id | string(64) | Ya | ID unik dari sistem Anda (no. invoice). Hanya huruf, angka, -_.. Tidak boleh dipakai ulang. |
| amount | integer | Ya | Nominal tagihan (sebelum biaya admin). |
| customer_name | string | Tidak | Nama customer. |
| customer_phone | string | Tidak | No. HP customer (angka). |
| customer_email | string | Tidak | Email customer. |
| description | string | Tidak | Keterangan / nama order. |
| callback_url | url | Tidak | Override callback URL khusus transaksi ini. |
| metadata | object | Tidak | Data bebas, dikembalikan apa adanya di status & callback. |
curl -X POST https://www.smartpgd.rizaluardi.cloud/api/v1/qris/create \
-H "X-API-KEY: spg_xxx" \
-H "Content-Type: application/json" \
-d '{
"reference_id": "INV-2026-0001",
"amount": 50000,
"customer_name": "Budi",
"customer_phone": "081234567890",
"description": "Top up 50K",
"metadata": { "user_id": 77 }
}'
{
"success": true,
"code": "SUCCESS",
"message": "QRIS berhasil dibuat",
"data": {
"trx_id": "SPGQR261011143005AB12CD",
"reference_id": "INV-2026-0001",
"product_code": "QRIS",
"status": "PENDING",
"amount": 50000,
"admin_fee": 500,
"fee_bearer": "customer",
"total_amount": 50500,
"net_amount": 50000,
"qr_content": "00020101021226670016COM.NOBUBANK.WWW...6304ABCD",
"qr_image_url": "https://.../qr/SPGQR261011143005AB12CD.svg",
"checkout_url": "https://.../pay/SPGQR261011143005AB12CD",
"expired_at": "2026-10-11T14:45:05+07:00",
"created_at": "2026-10-11T14:30:05+07:00"
}
}
Render qr_content menjadi QR code di aplikasi Anda, atau langsung gunakan qr_image_url (SVG) / checkout_url.
Membuat nomor Virtual Account sekali pakai (closed amount — customer wajib membayar persis sebesar total_amount). Berlaku ±24 jam.
| Field | Tipe | Wajib | Keterangan |
|---|---|---|---|
| bank | string | Ya | Kode bank: BCA, BNI, BRI, MANDIRI. Lihat produk aktif akun Anda di GET /products. |
| reference_id | string(64) | Ya | ID unik dari sistem Anda. |
| amount | integer | Ya | Nominal tagihan (sebelum biaya admin). |
| customer_name | string(40) | Ya | Nama yang tampil di VA. |
| customer_phone | string | Tidak | No. HP customer. |
| customer_email, description, callback_url, metadata | Tidak | Sama seperti QRIS. |
curl -X POST https://www.smartpgd.rizaluardi.cloud/api/v1/va/create \
-H "X-API-KEY: spg_xxx" \
-H "Content-Type: application/json" \
-d '{
"bank": "BCA",
"reference_id": "INV-2026-0002",
"amount": 150000,
"customer_name": "Budi Santoso",
"customer_phone": "081234567890"
}'
{
"success": true,
"code": "SUCCESS",
"message": "Virtual Account berhasil dibuat",
"data": {
"trx_id": "SPGVA261011143210EF34GH",
"reference_id": "INV-2026-0002",
"product_code": "VA_BCA",
"status": "PENDING",
"amount": 150000,
"admin_fee": 5000,
"fee_bearer": "customer",
"total_amount": 155000,
"net_amount": 150000,
"bank": "BCA",
"va_number": "1234500012345678",
"va_name": "Budi Santoso",
"checkout_url": "https://.../pay/SPGVA261011143210EF34GH",
"expired_at": "2026-10-12T14:32:10+07:00",
"created_at": "2026-10-11T14:32:10+07:00"
}
}
Bisa memakai trx_id atau reference_id Anda. Jika masih PENDING, sistem otomatis mengecek ke provider (nonaktifkan dengan ?sync=0).
{
"success": true,
"code": "SUCCESS",
"message": "Success",
"data": {
"trx_id": "SPGQR261011143005AB12CD",
"reference_id": "INV-2026-0001",
"product_code": "QRIS",
"payment_method": "qris",
"status": "PAID",
"amount": 50000,
"admin_fee": 500,
"fee_bearer": "customer",
"total_amount": 50500,
"net_amount": 50000,
"qris": {
"qr_content": "000201010212...",
"qr_image_url": "https://.../qr/SPGQR261011143005AB12CD.svg"
},
"customer": { "name": "Budi", "phone": "081234567890", "email": null },
"description": "Top up 50K",
"metadata": { "user_id": 77 },
"expired_at": "2026-10-11T14:45:05+07:00",
"paid_at": "2026-10-11T14:31:40+07:00",
"created_at": "2026-10-11T14:30:05+07:00"
}
}
Untuk VA, objek qris diganti virtual_account: {"bank","va_number","biller_code","va_name"}.
biller_code (Kode Perusahaan) dan va_number (Kode Bayar) ke customer. Pada bank lain biller_code bernilai null.Query opsional: status, reference_id, date_from, date_to (YYYY-MM-DD), per_page (maks 100), page.
{
"success": true,
"code": "SUCCESS",
"message": "Success",
"data": {
"items": [ { ...objek transaksi... } ],
"pagination": { "page": 1, "per_page": 20, "total": 125, "last_page": 7 }
}
}
{
"success": true,
"code": "SUCCESS",
"message": "Success",
"data": { "merchant_code": "M00001", "balance": 1250000, "currency": "IDR" }
}
Saat status transaksi berubah menjadi PAID, EXPIRED, atau FAILED, kami mengirim POST JSON ke Callback URL Anda.
Content-Type: application/json X-Callback-Event: payment.paid X-Callback-Timestamp: 1791707500 X-Callback-Signature: 5f2c1e...e9a0 (hex HMAC-SHA256) X-Merchant-Code: M00001
{
"event": "payment.paid",
"data": {
"trx_id": "SPGVA261011143210EF34GH",
"reference_id": "INV-2026-0002",
"product_code": "VA_BCA",
"payment_method": "va",
"status": "PAID",
"amount": 150000,
"admin_fee": 5000,
"fee_bearer": "customer",
"total_amount": 155000,
"net_amount": 150000,
"virtual_account": { "bank": "BCA", "va_number": "1234500012345678", "va_name": "Budi Santoso" },
"customer": { "name": "Budi Santoso", "phone": "081234567890", "email": null },
"description": null,
"metadata": null,
"expired_at": "2026-10-12T14:32:10+07:00",
"paid_at": "2026-10-11T15:02:51+07:00",
"created_at": "2026-10-11T14:32:10+07:00"
}
}
trx_id / reference_id.payment.paid, payment.expired, payment.failed.Wajib diverifikasi untuk memastikan callback benar-benar dari kami:
signature = hex( HMAC_SHA256( key = CALLBACK_SECRET, message = X-Callback-Timestamp + "." + RAW_BODY ) )
Gunakan raw body persis seperti diterima (jangan di-decode lalu di-encode ulang). Tolak jika timestamp selisih > 5 menit.
$raw = file_get_contents('php://input');
$timestamp = $_SERVER['HTTP_X_CALLBACK_TIMESTAMP'] ?? '';
$signature = $_SERVER['HTTP_X_CALLBACK_SIGNATURE'] ?? '';
$secret = 'sk_xxx'; // Callback Secret dari portal
$expected = hash_hmac('sha256', $timestamp . '.' . $raw, $secret);
if (!hash_equals($expected, $signature) || abs(time() - (int) $timestamp) > 300) {
http_response_code(401);
exit('invalid signature');
}
$payload = json_decode($raw, true);
if ($payload['event'] === 'payment.paid') {
// cari order by $payload['data']['reference_id'], tandai lunas jika belum
}
http_response_code(200);
echo 'OK';
const crypto = require('crypto');
app.post('/callback/spg', express.raw({ type: 'application/json' }), (req, res) => {
const ts = req.get('X-Callback-Timestamp');
const sig = req.get('X-Callback-Signature');
const expected = crypto.createHmac('sha256', process.env.SPG_CALLBACK_SECRET)
.update(ts + '.' + req.body.toString('utf8'))
.digest('hex');
if (sig.length !== expected.length ||
!crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) {
return res.status(401).send('invalid signature');
}
const payload = JSON.parse(req.body);
// proses payload.data secara idempoten
res.send('OK');
});
| Status | Arti |
|---|---|
| PENDING | Menunggu pembayaran. |
| PAID | Lunas. Saldo sudah masuk ke akun Anda (net_amount). |
| EXPIRED | Melewati batas waktu, tidak bisa dibayar lagi. |
| FAILED | Gagal dibuat / ditolak provider. |
| HTTP | code | Arti |
|---|---|---|
| 401 | UNAUTHORIZED | API key tidak ada / tidak valid. |
| 403 | MERCHANT_SUSPENDED | Akun mitra dinonaktifkan. |
| 403 | MERCHANT_NOT_VERIFIED | Data perusahaan belum diverifikasi admin. |
| 403 | IP_NOT_ALLOWED | IP tidak ada di whitelist. |
| 403 | PRODUCT_NOT_AVAILABLE | Produk tidak aktif untuk akun Anda. |
| 404 | NOT_FOUND | Transaksi / endpoint tidak ditemukan. |
| 409 | DUPLICATE_REFERENCE | reference_id sudah pernah dipakai (termasuk transaksi gagal). data.trx_id berisi transaksi yang ada. |
| 422 | VALIDATION_ERROR | Parameter tidak valid. Detail di data. |
| 422 | INVALID_AMOUNT | Nominal di luar batas min/max produk. |
| 429 | RATE_LIMITED | Melebihi batas request. |
| 502 | PROVIDER_ERROR | Tagihan gagal dibuat. Coba lagi dengan reference_id baru. |
| 503 | SERVICE_UNAVAILABLE | Produk sedang tidak tersedia sementara. Coba lagi nanti. |
| 500 | SERVER_ERROR | Kesalahan internal. |
Penarikan dilakukan lewat Portal Mitra → Tarik Saldo (tidak melalui API):
reference_id unik untuk setiap transaksi.GET /transactions/{id}) untuk order yang belum menerima callback.