Autentikasi SNAP BI
Spesifikasi
Alur setiap request API
Ambil access token (sekali per sesi)
Kirim client_id + client_secret ke POST /auth/token. Terima access_token yang berlaku 15 menit. Kode Anda melakukan ini otomatis — bukan manual.
Tanda tangani setiap request dengan RSA private key
Buat string yang akan ditandatangani dari HTTP method + path + timestamp + hash body. Tanda tangani dengan private_key.pem menggunakan RS256. Kirim hasilnya di header X-SIGNATURE.
Bank ABC memverifikasi tanda tangan
Bank ABC menggunakan public_key.pem yang sudah Anda daftarkan di Credentials untuk memverifikasi. Jika cocok, request diterima. Jika tidak, server mengembalikan 401 Unauthorized.
Contoh implementasi signing (Python)
import jwt, time, hashlib, base64
from cryptography.hazmat.primitives import serialization
# 1. Muat private key (dilakukan sekali saat startup)
with open("private_key.pem", "rb") as f:
private_key = serialization.load_pem_private_key(f.read(), password=None)
# 2. Buat access token (auto-refresh jika kedaluwarsa)
def get_access_token(client_id, client_secret):
payload = {
"iss": client_id,
"sub": client_id,
"iat": int(time.time()),
"exp": int(time.time()) + 900, # 15 menit — dibuat otomatis
}
return jwt.encode(payload, private_key, algorithm="RS256")
# 3. Tanda tangani setiap request
def sign_request(method, path, body, timestamp):
body_hash = base64.b64encode(
hashlib.sha256(body.encode()).digest()
).decode()
string_to_sign = f"{method}:{path}:{body_hash}:{timestamp}"
return jwt.encode({"data": string_to_sign}, private_key, algorithm="RS256")
const jose = require('jose');
const fs = require('fs');
const crypto = require('crypto');
// 1. Muat private key sekali saat startup
const privateKey = crypto.createPrivateKey(fs.readFileSync('private_key.pem'));
// 2. Buat access token (auto-refresh jika kedaluwarsa)
async function getAccessToken(clientId) {
return new jose.SignJWT({ iss: clientId, sub: clientId })
.setProtectedHeader({ alg: 'RS256' })
.setIssuedAt()
.setExpirationTime('15m') // auto-dibuat tiap request
.sign(privateKey);
}
// 3. Buat signature per request
function signRequest(method, path, body, timestamp) {
const bodyHash = crypto.createHash('sha256').update(body).digest('base64');
const stringToSign = `${method}:${path}:${bodyHash}:${timestamp}`;
return crypto.sign('sha256', Buffer.from(stringToSign), privateKey).toString('base64');
}
// 1. Muat private key sekali saat startup
PrivateKey privateKey = loadPrivateKeyFromPem("private_key.pem");
// 2. Buat access token (auto-refresh jika kedaluwarsa)
String getAccessToken(String clientId) {
long now = Instant.now().getEpochSecond();
return Jwts.builder()
.setIssuer(clientId).setSubject(clientId)
.setIssuedAt(new Date(now * 1000))
.setExpiration(new Date((now + 900) * 1000)) // 15 menit
.signWith(privateKey, SignatureAlgorithm.RS256)
.compact();
}
// 3. Tanda tangani setiap request
String signRequest(String method, String path, String body, String timestamp) {
String bodyHash = Base64.getEncoder().encodeToString(
MessageDigest.getInstance("SHA-256").digest(body.getBytes()));
String toSign = method + ":" + path + ":" + bodyHash + ":" + timestamp;
Signature sig = Signature.getInstance("SHA256withRSA");
sig.initSign(privateKey);
sig.update(toSign.getBytes());
return Base64.getEncoder().encodeToString(sig.sign());
}
Error autentikasi yang umum
| Response Code | HTTP | Penyebab | Solusi |
|---|---|---|---|
| 4012900 | 401 | Token JWT kedaluwarsa (>15 menit) | Generate token baru secara otomatis dan ulangi request |
| 4012901 | 401 | Signature tidak valid | Periksa format string_to_sign dan pastikan menggunakan private key yang benar |
| 4012902 | 401 | Public key belum terdaftar | Daftarkan public key di Credentials > Autentikasi SNAP terlebih dahulu |
| 4002900 | 400 | Header X-TIMESTAMP tidak sesuai format | Gunakan format ISO 8601: 2026-05-23T09:00:00+07:00 |
Siap setup? Daftarkan RSA public key Anda di halaman Credentials untuk mulai menggunakan autentikasi SNAP di sandbox.
Daftarkan Public KeyVA Disbursement — Fintech
API VA Disbursement memungkinkan platform fintech mencairkan dana ke rekening tujuan secara otomatis menggunakan Virtual Account Bank ABC sebagai jalur distribusi. Use case utama meliputi pencairan pinjaman P2P lending, top-up saldo e-wallet, pembayaran gaji (payroll), dan distribusi reward merchant.
Alur dana berjalan dari rekening giro fintech → VA Bank ABC → rekening tujuan akhir, dengan settlement T+0 pada hari kerja sebelum pukul 15.00 WIB. Transaksi yang masuk setelah batas waktu akan diselesaikan pada hari kerja berikutnya.
Aktor Utama
Alur Uang (Settlement T+0)
Arsitektur & Sequence Disbursement
Integrasi VA Disbursement menggunakan pola H2H (Host-to-Host) berbasis standar SNAP BI dengan autentikasi dua lapisan: client credential OAuth 2.0 untuk mendapatkan access token, diikuti request signing menggunakan asymmetric key (RSA-256) per transaksi.
Sequence Diagram — Disbursement Flow
Persyaratan Koneksi
Autentikasi SNAP BI
SNAP BI menggunakan dua lapis autentikasi: access token berbasis client credential, ditambah request signing menggunakan RSA asymmetric key. Keduanya ditangani otomatis oleh kode Anda setelah setup awal dilakukan sekali.
Alur setiap request
Ambil access token
POST /auth/token dengan client_id + client_secret. Token berlaku 15 menit, di-refresh otomatis oleh kode Anda.
Tanda tangani request dengan RSA private key
Buat hash dari method + path + body + timestamp, tanda tangani dengan private key, kirim di header X-SIGNATURE.
Bank ABC verifikasi dengan public key Anda
Menggunakan public key yang sudah Anda daftarkan. Jika tidak cocok: 401 Unauthorized.
Setup diperlukan sekali. Daftarkan RSA public key Anda di Credentials untuk mulai. Panduan lengkap dan contoh kode tersedia di halaman Autentikasi SNAP.
POST /va/disburse
/snap/v1.0/va/disburseRequest Parameters
| Parameter | Tipe | Wajib | Deskripsi |
|---|---|---|---|
| partnerReferenceNo | string | Wajib | Nomor referensi unik dari sistem fintech. Maksimal 64 karakter. Digunakan untuk idempotency check. |
| beneficiaryAccountNo | string | Wajib | Nomor rekening tujuan penerima dana. Harus rekening aktif di Bank ABC. |
| beneficiaryBankCode | string | Wajib | Kode bank tujuan (format SNAP BI). Gunakan "008" untuk Bank ABC. |
| amount.value | string | Wajib | Jumlah transfer dalam string desimal. Format: "100000.00". Minimal IDR 10.000. |
| amount.currency | string | Wajib | Kode mata uang. Saat ini hanya mendukung "IDR". |
| remark | string | Opsional | Keterangan transfer yang tercetak di rekening koran penerima. Maksimal 40 karakter. |
| callbackUrl | string | Opsional | Override URL callback untuk transaksi ini. Jika kosong, menggunakan URL callback terdaftar di credential. |
Contoh Request
curl -X POST https://api-sandbox.bankabc.co.id/snap/v1.0/va/disburse \
-H "Authorization: Bearer {access_token}" \
-H "Content-Type: application/json" \
-H "X-TIMESTAMP: 2026-05-20T12:00:00+07:00" \
-H "X-SIGNATURE: {rsa_signature}" \
-H "X-PARTNER-ID: your_partner_id" \
-d '{
"partnerReferenceNo": "TRF-2026-001",
"beneficiaryAccountNo": "1234567890",
"beneficiaryBankCode": "008",
"amount": { "value": "500000.00", "currency": "IDR" },
"remark": "Pencairan pinjaman TRF-001"
}'HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api-sandbox.bankabc.co.id/snap/v1.0/va/disburse"))
.header("Authorization", "Bearer " + accessToken)
.header("Content-Type", "application/json")
.header("X-TIMESTAMP", timestamp)
.header("X-SIGNATURE", signature)
.POST(HttpRequest.BodyPublishers.ofString("""
{
"partnerReferenceNo": "TRF-2026-001",
"beneficiaryAccountNo": "1234567890",
"beneficiaryBankCode": "008",
"amount": { "value": "500000.00", "currency": "IDR" },
"remark": "Pencairan pinjaman TRF-001"
}"""))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());import requests
url = "https://api-sandbox.bankabc.co.id/snap/v1.0/va/disburse"
headers = {
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json",
"X-TIMESTAMP": timestamp,
"X-SIGNATURE": signature,
"X-PARTNER-ID": partner_id
}
payload = {
"partnerReferenceNo": "TRF-2026-001",
"beneficiaryAccountNo": "1234567890",
"beneficiaryBankCode": "008",
"amount": {"value": "500000.00", "currency": "IDR"},
"remark": "Pencairan pinjaman TRF-001"
}
response = requests.post(url, json=payload, headers=headers)
print(response.json())const response = await fetch(
'https://api-sandbox.bankabc.co.id/snap/v1.0/va/disburse',
{
method: 'POST',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json',
'X-TIMESTAMP': timestamp,
'X-SIGNATURE': signature,
'X-PARTNER-ID': partnerId
},
body: JSON.stringify({
partnerReferenceNo: 'TRF-2026-001',
beneficiaryAccountNo: '1234567890',
beneficiaryBankCode: '008',
amount: { value: '500000.00', currency: 'IDR' },
remark: 'Pencairan pinjaman TRF-001'
})
}
);
const data = await response.json();Response & Error Codes
| Response Code | HTTP | Deskripsi | Tindakan |
|---|---|---|---|
| 2002900 | 200 | Transaksi berhasil diproses | Simpan responseId untuk rekonsiliasi |
| 4002901 | 400 | Saldo tidak mencukupi | Periksa saldo rekening giro fintech |
| 4002902 | 400 | Rekening tujuan tidak ditemukan | Validasi nomor rekening dan kode bank |
| 4012900 | 401 | Token tidak valid atau kedaluwarsa | Refresh access token dan ulangi request |
| 4092900 | 409 | partnerReferenceNo sudah diproses | Gunakan ID unik baru; request idempoten |
| 5002900 | 500 | Kesalahan internal Bank ABC | Retry setelah 30 detik; hubungi support jika berlanjut |
| 5042900 | 504 | Timeout — tidak ada respons dari CBS | Jangan retry langsung; cek status via GET /va/status |