Dokumentasi Developer — Sarana Payment API
Integrasi server-to-server ke payment gateway Sarana memakai API key.
Surface developer berada di /api/v2 (Client / Developer API).
Tersedia dua environment — sandbox untuk development/uji coba (tanpa uang nyata), production untuk transaksi nyata:
| Environment | Base URL | Catatan |
|---|---|---|
| Production | https://api.internal.saranatechnology.com/api/v2 | Transaksi & payout uang nyata |
| Sandbox | https://api.internal-go.saranatechnology.com/api/v2 | Payment provider mode test; data & API key terpisah dari production |
- Referensi interaktif (Swagger UI):
/api/v2/docs(sandbox: di sini) - Spec OpenAPI mentah:
/api/v2/docs/openapi.yaml
Swagger adalah sumber kebenaran untuk request/response tiap endpoint. Halaman ini panduan getting started yang merangkumnya. Path & payload identik di kedua environment — cukup ganti host (dan API key) saat pindah sandbox → production. Mulai dari sandbox, pindah ke production setelah alur end-to-end teruji.
1. Mendapatkan API key
Satu client = satu API key aktif. Generate ulang akan mengganti key lama (key lama langsung non-aktif). Dua cara memperolehnya:
| Jalur | Siapa | Syarat |
|---|---|---|
| Portal klien (self-service) | Client sendiri di client.saranatechnology.com → menu API Keys | Akun harus punya akses developer (di-approve admin) |
| Admin panel | Admin Sarana menerbitkan untuk client | Tidak ada — admin bisa untuk client mana pun |
Akses developer adalah izin sisi client: hanya client dengan akses developer yang boleh men-generate key-nya sendiri lewat portal. Key yang sudah terbit tetap valid dipakai tanpa bergantung pada flag tersebut.
Format key: sk_live_ + 48 karakter heksadesimal. Simpan baik-baik — key penuh
hanya ditampilkan sekali saat dibuat.
Key per-client, bukan per-app. Satu client bisa punya banyak apps, tapi API key dimiliki di level client — satu key dipakai untuk semua apps client tersebut. Anda tidak perlu generate key per-app.
client_idjuga otomatis diambil dari key, jadi tidak perlu dikirim di body request. Saat client punya >1 app, sertakanapp_idpada request pembayaran agar biaya transaksi (transaction_fee) yang benar dipakai — lihat bagian Apps.
2. Autentikasi
Sertakan API key pada header X-API-Key di setiap request:
X-API-Key: sk_live_<48-karakter-hex-key-anda>
Tanpa key yang valid & aktif → 401 Unauthorized.
API key khusus untuk endpoint integrasi (payments, fees, withdrawals, transactions). Endpoint portal (login, tiket, dsb.) memakai Bearer token, bukan API key.
3. Format response
Semua response memakai envelope seragam:
{
"success": true,
"message": "...",
"code": 200,
"data": { },
"meta": { "total": 0, "per_page": 10, "current_page": 1, "last_page": 1 }
}
meta hanya muncul pada endpoint berpaginasi. Saat error, success=false dan
message berisi alasannya, code = HTTP status.
| Code | Arti |
|---|---|
| 400 | Request tidak valid (field wajib kosong / salah format) |
| 401 | API key tidak ada / tidak valid |
| 404 | Resource tidak ditemukan |
| 500 | Kesalahan server |
4. Quickstart — membuat pembayaran
Simpan key sebagai environment variable agar tidak ikut tercatat:
export SARANA_API_KEY="sk_live_<48-karakter-hex-key-anda>"
client_idtidak perlu dikirim — diambil dari API key. Bila client punya lebih dari satu app, sertakanapp_id(lihat bagian Apps) agar transaction_fee app yang benar dipakai.
Virtual Account
curl -X POST https://api.internal.saranatechnology.com/api/v2/payments/va \
-H "X-API-Key: $SARANA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"app_id": 45,
"amount": 150000,
"bank_code": "BCA",
"customer_name": "Budi Santoso"
}'
Contoh memakai host production. Untuk uji coba, ganti host ke sandbox
api.internal-go.saranatechnology.com(dengan API key sandbox).
Response 201 berisi nomor VA yang ditampilkan ke pembeli.
QRIS
curl -X POST .../api/v2/payments/qris \
-H "X-API-Key: $SARANA_API_KEY" -H "Content-Type: application/json" \
-d '{ "app_id": 45, "amount": 50000 }'
Response berisi qr_string untuk dirender sebagai QR.
E-wallet
curl -X POST .../api/v2/payments/ewallet \
-H "X-API-Key: $SARANA_API_KEY" -H "Content-Type: application/json" \
-d '{
"app_id": 45, "amount": 75000,
"channel_code": "DANA", "customer_phone": "08123456789",
"success_url": "https://toko-anda.com/selesai"
}'
Response berisi link / deeplink pembayaran (OVO, DANA, SHOPEEPAY, LINKAJA).
Invoice
curl -X POST .../api/v2/payments/invoice \
-H "X-API-Key: $SARANA_API_KEY" -H "Content-Type: application/json" \
-d '{ "app_id": 45, "amount": 250000 }'
Cek status pembayaran
curl .../api/v2/payments/{id}/status -H "X-API-Key: $SARANA_API_KEY"
Daftar metode tersedia: GET .../api/v2/payments/methods.
5. Apps
Satu client bisa punya banyak apps. Tiap app punya transaction_fee sendiri,
jadi saat membuat pembayaran Anda perlu menyebut app mana lewat app_id (wajib
bila app lebih dari satu). Ambil daftar app milik Anda:
curl .../api/v2/apps -H "X-API-Key: $SARANA_API_KEY"
Response (client_id otomatis dari key — tidak perlu dikirim):
{
"success": true,
"data": [
{
"id": 12,
"app_id": 45,
"type": "Production",
"url": "https://toko-anda.com",
"app": { "id": 45, "kode": "APP-XYZ", "name": "Toko XYZ", "jenis": "B2B" }
}
]
}
Pakai app_id dari sini di request pembayaran. Bila client hanya punya satu app,
app_id boleh dikosongkan — sistem memakai satu-satunya app itu.
6. Profil akun sendiri
Baca & perbarui profil client Anda sendiri (nama, PIC, alamat, serta norek + bank
tujuan penarikan) langsung lewat API key — tanpa login portal. client_id diambil
dari key, jadi tidak perlu dikirim.
# Baca profil
curl .../api/v2/account -H "X-API-Key: $SARANA_API_KEY"
Response:
{
"success": true,
"code": 200,
"message": "Account retrieved",
"data": {
"id": 14, "nama": "Toko XYZ", "email": "owner@toko-xyz.com", "kode": "CLT-1",
"pic": "Budi", "no_telp_pic": "08123456789", "email_pic": "budi@toko-xyz.com",
"alamat": "Jl. Merdeka 1", "norek": "1234567890", "bank": "BCA",
"status": "approved", "has_developer": true, "is_auto_approve_withdrawal": false,
"saldo": 226687500, "saldo_yang_bisa_ditarik": 200000000
}
}
# Perbarui field yang boleh diubah (semua opsional)
curl -X PUT .../api/v2/account \
-H "X-API-Key: $SARANA_API_KEY" -H "Content-Type: application/json" \
-d '{ "norek": "9876543210", "bank": "BCA", "pic": "Budi Santoso" }'
Field yang boleh diubah: nama, email, alamat, pic, no_telp_pic,
email_pic, norek, bank. Field terkunci admin — id, kode, balance,
has_developer, is_auto_approve_withdrawal, plan/subscription — diabaikan bila
dikirim (tidak akan mengubah data). bank harus salah satu dari
GET /api/v2/bank-channels/active, norek numeric.
Saldo saja:
GET /api/v2/account/balance(aliasGET /api/v2/clients/{id}/balance).
7. Menghitung biaya (fees)
Hitung biaya sebelum membuat transaksi:
curl -X POST .../api/v2/fees/calculate \
-H "X-API-Key: $SARANA_API_KEY" -H "Content-Type: application/json" \
-d '{ "client_id": 123, "amount": 150000, "payment_method": "VA" }'
Endpoint fee lain: /fees/rates, /fees/calculate/bulk, /fees/calculate/vat,
/fees/calculate/withdrawal, /fees/calculate/amount-to-pay. Lihat Swagger untuk
parameter masing-masing.
8. Transaksi & saldo
# List transaksi (paginated: ?page=1&per_page=20)
curl ".../api/v2/transactions?page=1&per_page=20" -H "X-API-Key: $SARANA_API_KEY"
# Detail per id internal
curl .../api/v2/transactions/{id} -H "X-API-Key: $SARANA_API_KEY"
# Cari berdasarkan external id Anda
curl .../api/v2/transactions/external/{externalId} -H "X-API-Key: $SARANA_API_KEY"
# Saldo client
curl .../api/v2/clients/{id}/balance -H "X-API-Key: $SARANA_API_KEY"
9. Penarikan dana (withdrawals) & settlement
# Buat penarikan
curl -X POST .../api/v2/withdrawals \
-H "X-API-Key: $SARANA_API_KEY" -H "Content-Type: application/json" \
-d '{ "client_id": 123, "amount": 1000000 }'
# Cek / batalkan
curl .../api/v2/withdrawals/{id} -H "X-API-Key: $SARANA_API_KEY"
curl -X POST .../api/v2/withdrawals/{id}/cancel -H "X-API-Key: $SARANA_API_KEY"
# Settlement
curl -X POST .../api/v2/settlements \
-H "X-API-Key: $SARANA_API_KEY" -H "Content-Type: application/json" \
-d '{ "client_id": 123, "amount": 500000 }'
Daftar bank untuk penarikan: GET .../api/v2/bank-channels/active.
Bentuk body persis (field opsional, validasi norek/bank) ada di Swagger.
10. Webhook / callback
Sarana mengirim notifikasi status ke endpoint Anda. Jenis callback yang tersedia
(lihat Swagger bagian Callbacks): pembayaran/invoice, transaksi, withdrawal, disbursement.
Selalu balas cepat (2xx) dan proses secara asinkron.
Callback yang di-forward Sarana ke
callback_urlAnda tidak membawa headerX-Callback-Token. Token itu hanya dipakai di leg Xendit→Sarana. Amankan endpoint Anda dengan IP allowlist / URL rahasia, bukan mengecek token pada payload ini.
Callback pembayaran (invoice)
Saat status invoice berubah ke terminal (PAID/SETTLED, atau EXPIRED), Sarana
POST ke callback_url yang Anda sertakan di metadata waktu membuat invoice. Bila
metadata.type_transaksi diisi, Sarana menambahkan query ?via=<type_transaksi> ke URL.
{
"id": "6a52ff4de3f0aa9c76feb053",
"external_id": "INV-11-1720771200000000000",
"transaction_id": "INV-11-1720771200000000000",
"client_external_id": "ORDER-2026-0042",
"user_id": "5f2d…",
"status": "PAID",
"amount": 250000,
"paid_amount": 250000,
"adjusted_received_amount": 247500,
"paid_at": "2026-07-12T09:14:03.123Z",
"payment_method": "BANK_TRANSFER",
"payment_channel": "BCA",
"biayavat": 2500,
"metadata": { "callback_url": "https://…", "type_transaksi": "topup" }
}
| field | arti |
|---|---|
id | Xendit invoice id (hex) |
transaction_id | referensi transaksi Sarana — INV-<client>-<nano> |
external_id | sama dengan transaction_id (nilai yang dikirim Sarana ke Xendit) |
client_external_id | external_id yang Anda kirim saat membuat invoice (kosong bila tak diisi) |
status | status Xendit mentah, UPPERCASE: PAID, SETTLED, atau EXPIRED — normalkan sendiri |
amount / paid_amount | nominal tagihan / dibayar |
adjusted_received_amount | nominal bersih diterima (setelah fee) |
biayavat | biaya + VAT yang dihitung Sarana |
Matching: cocokkan pada transaction_id (INV-…, ambil dari response create invoice)
atau client_external_id (referensi Anda sendiri). Jangan andalkan id numerik — id
selalu string hex Xendit.
Penting — status non-lunas: Sarana hanya mem-forward PAID/SETTLED dan EXPIRED.
Untuk status lain (mis. pending yang tak pernah dibayar) poll GET /api/v2/payments/{id}/status
atau GET /api/v2/transactions/external/{clientExternalId}; jangan menunggu callback.
Kirim external_id Anda saat membuat invoice supaya bisa dicari & muncul di callback:
curl -X POST .../api/v2/payments/invoice \
-H "X-API-Key: $SARANA_API_KEY" -H "Content-Type: application/json" \
-d '{ "app_id": 45, "amount": 250000, "external_id": "ORDER-2026-0042",
"metadata": { "callback_url": "https://toko-anda.com/callback", "type_transaksi": "topup" } }'
Callback penarikan (withdrawal)
Saat penarikan selesai ditransfer, Sarana mengirim POST ke url yang Anda
sertakan waktu membuat withdrawal. Body persis tiga field:
{
"status": "completed",
"di_transfer_oleh": "Xendit",
"transfer_at": "2026-07-10T17:20:21+07:00"
}
| field | arti |
|---|---|
status | status akhir penarikan — completed atau failed |
di_transfer_oleh | siapa yang mentransfer |
transfer_at | waktu transfer (ISO-8601) |
Endpoint callback Anda harus menerima ketiga field ini apa adanya dan membalas
2xx. Jangan mewajibkan field lain (mis. id, jumlah) — payload memang hanya berisi
tiga field di atas. Callback dikirim asinkron dengan satu kali retry bila gagal.
11. Ringkas
- Dapatkan API key (portal bila punya akses developer, atau minta admin). Satu key per-client untuk semua apps.
- Kirim
X-API-Keydi setiap request ke…/api/v2/….client_idotomatis dari key. - Ambil
app_idviaGET /api/v2/apps; sertakan di request pembayaran bila app lebih dari satu. - Buat pembayaran → tampilkan instruksi ke pembeli → tunggu callback / poll status.
- Tarik saldo via
/withdrawals. - Uji dulu di sandbox (
api.internal-go.…), lalu pindah production (api.internal.…) — path & payload sama, ganti host + API key. - Referensi lengkap & coba langsung:
/api/v2/docs.