Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Relay Merchant API

Selamat datang di dokumentasi Relay Merchant API — platform PPOB (Payment Point Online Bank) untuk merchant yang ingin mengintegrasikan layanan pembayaran, transfer dana, dan penerimaan pembayaran QRIS ke dalam sistem mereka.

Cakupan Dokumentasi

Dokumentasi ini mencakup seluruh endpoint API yang tersedia untuk merchant:

KategoriDeskripsiBase Path
PPOBPulsa, e-wallet, token PLN, tagihan PLN, BPJS/api/v1/merchant/ppob
DisbursementTransfer dana ke rekening bank/api/v1/merchant/disbursement
PayIn QRISTerima pembayaran via QRIS/api/v1/merchant/payin
UtilitasCek saldo, katalog produk/api/v1/balance, /api/v1/product

Base URL

LingkunganURL
Sandboxhttps://api-sandbox.alfakios.com
Productionhttps://api.alfakios.com

Gunakan lingkungan Sandbox untuk pengembangan dan pengujian. Transaksi sandbox tidak memotong saldo nyata.

Prasyarat Integrasi

Sebelum mulai mengintegrasikan API, pastikan Anda memiliki:

  1. Akun merchant yang telah aktif di CMS (backoffice).
  2. API Key yang di-generate dari menu API Keys di CMS.
  3. Signature Secret (opsional, jika fitur Signature Validation diaktifkan) — diberikan oleh tim support.
  4. Saldo deposit yang cukup untuk melakukan transaksi.

Cara Membaca Dokumentasi Ini

  • Memulai Integrasi — baca bagian ini terlebih dahulu untuk memahami mekanisme autentikasi dan format request/response.
  • Kategori API — setiap kategori memiliki halaman gambaran umum yang menjelaskan alur transaksi, diikuti halaman detail per endpoint.
  • Lampiran — contoh integrasi end-to-end dalam berbagai bahasa pemrograman.

Konvensi Dokumen

  • Field bertanda *Ya* pada tabel request wajib disertakan.
  • Semua contoh menggunakan apk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx sebagai placeholder API Key — ganti dengan API Key Anda yang sebenarnya.
  • Nominal (amount) selalu berupa string digit dalam satuan IDR (contoh: "100000" = Rp 100.000).

Bantuan Teknis

Untuk pertanyaan teknis terkait integrasi, hubungi tim Backend API melalui grup WhatsApp yang dibuat oleh marketing.

Autentikasi API Key

Semua endpoint Merchant API menggunakan autentikasi berbasis API Key melalui header X-API-Key.

Generate API Key dari CMS

  1. Login ke CMS dengan akun merchant.
  2. Buka menu API Keys.
  3. Klik tombol Generate New API Key.
  4. Salin dan simpan API Key yang ditampilkan — nilai ini hanya ditampilkan sekali.
  5. API Key yang aktif dapat dinonaktifkan atau dihapus kapan saja dari halaman yang sama.

Keamanan: Jangan simpan API Key di repositori kode, file .env yang di-commit, atau tempat yang dapat diakses publik. Jika API Key bocor, segera nonaktifkan dan generate ulang melalui CMS.

Format API Key

apk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Header Wajib

Setiap request ke Merchant API harus menyertakan header berikut:

HeaderTipeWajibKeterangan
X-API-KeyStringYaAPI Key merchant dari CMS
Content-TypeStringYaHarus bernilai application/json

Header Opsional (jika Signature Validation aktif)

HeaderTipeKondisiKeterangan
X-SignatureStringJika aktifBase64 HMAC-SHA256 signature dari request body
X-TimestampStringJika aktifUnix timestamp dalam detik (integer)

Lihat Signature Validation untuk detail lebih lanjut.

Contoh Request

POST /api/v1/merchant/ppob/phone-credit/transaction HTTP/1.1
Host: api-sandbox.alfakios.com
Content-Type: application/json
X-API-Key: apk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

{
  "req_id": "TXN-20240501-001",
  "product_id": "XL5",
  "cust_id": "08123456789"
}

Response Error Autentikasi

API Key tidak ada atau tidak valid

{
  "success": false,
  "message": "Unauthorized",
  "error": "Invalid or missing API Key"
}

HTTP Status: 401 Unauthorized

API Key tidak memiliki akses endpoint ini

{
  "success": false,
  "message": "Forbidden",
  "error": "API Key does not have permission to access this resource"
}

HTTP Status: 403 Forbidden

Akun merchant tidak aktif

{
  "success": false,
  "message": "Forbidden",
  "error": "Merchant account is inactive"
}

HTTP Status: 403 Forbidden

Validasi Tambahan

Selain API Key, sistem juga menerapkan:

  • IP Restriction — jika merchant telah mendaftarkan IP whitelist, hanya request dari IP tersebut yang diterima.
  • Rate Limiting — request dibatasi per merchant untuk mencegah abuse.
  • Timeout — setiap request memiliki batas waktu pemrosesan 8 detik. Jika biller tidak merespons dalam 8 detik, sistem akan mengembalikan error.

Signature Validation

Signature Validation adalah lapisan keamanan tambahan menggunakan HMAC-SHA256 untuk memverifikasi integritas dan keaslian setiap request. Fitur ini opsional dan dapat diaktifkan per merchant oleh admin.

Cara Kerja

1. timestamp      = unix timestamp saat ini (integer, dalam detik)
2. body_string    = JSON string persis dari request body
3. string_to_sign = body_string + ":" + timestamp
4. signature      = Base64( HMAC-SHA256( secret_key, string_to_sign ) )

Catatan penting: body_string harus identik byte-for-byte dengan body yang dikirim dalam request. Jangan pretty-print atau ubah urutan field JSON.

Signature Secret

secret_key adalah nilai terpisah dari X-API-Key. Nilai ini:

  • Tidak ditampilkan di CMS
  • Diberikan langsung oleh tim support saat aktivasi fitur Signature Validation
  • Harus disimpan dengan aman dan tidak pernah di-commit ke repositori

Validasi Timestamp

Sistem menolak request jika:

  • Timestamp lebih dari 300 detik (5 menit) di masa lalu
  • Timestamp lebih dari 60 detik di masa depan

Hal ini mencegah replay attack.

Implementasi per Bahasa

PHP

<?php
$secretKey  = "signature_secret_dari_support";
$body       = ['product_id' => 'XL5', 'cust_id' => '08123456789', 'req_id' => 'TXN-001'];
$bodyString = json_encode($body, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
$timestamp  = time();
$toSign     = $bodyString . ":" . $timestamp;

$signature = base64_encode(hash_hmac('sha256', $toSign, $secretKey, true));

// Kirim dalam header
// X-Signature: $signature
// X-Timestamp: $timestamp
?>

Node.js

const crypto = require('crypto');

const secretKey  = 'signature_secret_dari_support';
const body       = { product_id: 'XL5', cust_id: '08123456789', req_id: 'TXN-001' };
const bodyString = JSON.stringify(body); // urutan key harus sama dengan yang dikirim
const timestamp  = Math.floor(Date.now() / 1000);
const toSign     = `${bodyString}:${timestamp}`;

const signature = crypto
  .createHmac('sha256', secretKey)
  .update(toSign)
  .digest('base64');

Python

import hmac, hashlib, base64, time, json

secret_key  = "signature_secret_dari_support"
body        = {"product_id": "XL5", "cust_id": "08123456789", "req_id": "TXN-001"}
body_string = json.dumps(body, separators=(',', ':'), ensure_ascii=False)
timestamp   = int(time.time())
to_sign     = f"{body_string}:{timestamp}"

signature = base64.b64encode(
    hmac.new(secret_key.encode(), to_sign.encode(), hashlib.sha256).digest()
).decode()

Java

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;

String secretKey  = "signature_secret_dari_support";
String bodyString = "{\"product_id\":\"XL5\",\"cust_id\":\"08123456789\",\"req_id\":\"TXN-001\"}";
long   timestamp  = System.currentTimeMillis() / 1000;
String toSign     = bodyString + ":" + timestamp;

Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(secretKey.getBytes("UTF-8"), "HmacSHA256"));
String signature = Base64.getEncoder().encodeToString(mac.doFinal(toSign.getBytes("UTF-8")));

Go

import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/base64"
    "encoding/json"
    "fmt"
    "time"
)

secretKey := "signature_secret_dari_support"
body := map[string]string{
    "product_id": "XL5",
    "cust_id":    "08123456789",
    "req_id":     "TXN-001",
}
bodyBytes, _ := json.Marshal(body)
timestamp := time.Now().Unix()
toSign    := fmt.Sprintf("%s:%d", string(bodyBytes), timestamp)

mac := hmac.New(sha256.New, []byte(secretKey))
mac.Write([]byte(toSign))
signature := base64.StdEncoding.EncodeToString(mac.Sum(nil))

Rust

#![allow(unused)]
fn main() {
use hmac::{Hmac, Mac};
use sha2::Sha256;
use base64::{engine::general_purpose, Engine};
use std::time::{SystemTime, UNIX_EPOCH};

type HmacSha256 = Hmac<Sha256>;

let secret_key  = "signature_secret_dari_support";
let body_string = r#"{"product_id":"XL5","cust_id":"08123456789","req_id":"TXN-001"}"#;
let timestamp   = SystemTime::now()
    .duration_since(UNIX_EPOCH)
    .unwrap()
    .as_secs();
let to_sign = format!("{}:{}", body_string, timestamp);

let mut mac = HmacSha256::new_from_slice(secret_key.as_bytes()).unwrap();
mac.update(to_sign.as_bytes());
let signature = general_purpose::STANDARD.encode(mac.finalize().into_bytes());
}

Tambahkan ke Cargo.toml: hmac = "0.12", sha2 = "0.10", base64 = "0.22"

Contoh Request dengan Signature

POST /api/v1/merchant/disbursement/payment HTTP/1.1
Host: api-sandbox.alfakios.com
Content-Type: application/json
X-API-Key: apk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
X-Signature: uW8tV+R6zKpN1234ABCDabcd==
X-Timestamp: 1714435200

{"req_id":"PAY-001","product_id":"DSTF","account_number":"1234567890","amount":"100000"}

Response Error Signature

{
  "success": false,
  "message": "Invalid signature",
  "error": "Signature validation failed"
}

HTTP Status: 401 Unauthorized

Format Response

Semua endpoint Merchant API mengembalikan response dalam format JSON dengan struktur yang konsisten.

Envelope Response

{
  "success": true,
  "message": "Keterangan singkat hasil operasi",
  "rc": "000",
  "data": { ... }
}
FieldTipeKeterangan
successBooleantrue jika request diproses tanpa error HTTP-level
messageStringPesan singkat yang mendeskripsikan hasil
rcStringKode respon bisnis — "000" = sukses; kode lain lihat Kode Respon
dataObject/Array/nullPayload hasil operasi; struktur bervariasi per endpoint

Catatan: success: true berarti HTTP request berhasil diproses, bukan berarti transaksi bisnis berhasil. Selalu cek field rc dan data.status untuk status transaksi.

HTTP Status Codes

HTTP StatusKapan Terjadi
200 OKRequest berhasil diproses (termasuk transaksi FAILED/PENDING)
400 Bad RequestFormat body tidak valid
401 UnauthorizedAPI Key tidak valid atau signature gagal
403 ForbiddenAPI Key valid tapi tidak punya akses, atau merchant nonaktif
404 Not FoundResource tidak ditemukan (contoh: transaksi tidak ada)
422 Unprocessable EntityValidasi field gagal (contoh: field wajib kosong, format salah)
500 Internal Server ErrorError internal server

Response Validasi Error (422)

{
  "success": false,
  "message": "Validation failed",
  "errors": [
    {
      "field": "amount",
      "message": "amount must be a positive integer (digits only)"
    },
    {
      "field": "product_id",
      "message": "product_id is required"
    }
  ]
}

Response Transaksi Sukses

{
  "success": true,
  "message": "Transaction completed successfully",
  "rc": "000",
  "data": {
    "req_id": "TXN-20240501-001",
    "ref_id": "1777946295495371000",
    "product_id": "XL5",
    "cust_id": "08123456789",
    "status": "SUCCESS",
    "rc": "000",
    "description": "Transaksi Sukses",
    "message": "PULSA XL 5000 KE 08123456789 SUKSES",
    "amount": "5000",
    "unit_price": "5500",
    "balance_used": "5500",
    "balance": "494500",
    "serial_no": "REF123456789",
    "data": {}
  }
}

Response Transaksi Gagal

{
  "success": true,
  "message": "Transaction completed",
  "rc": "008",
  "data": {
    "req_id": "TXN-20240501-002",
    "ref_id": "1777946295495372000",
    "status": "FAILED",
    "rc": "008",
    "description": "Insufficient Balance",
    "message": "Saldo deposit tidak mencukupi",
    "balance": "1000",
    "data": {}
  }
}

Field data Umum

Hampir semua endpoint transaksi mengembalikan field berikut dalam data:

FieldTipeKeterangan
req_idStringID unik dari merchant (idempotency key)
ref_idStringID referensi internal sistem
product_idStringKode produk yang digunakan
cust_idStringID customer / nomor tujuan
statusStringSUCCESS, FAILED, atau PENDING
rcStringKode respon biller atau sistem
descriptionStringKeterangan RC
messageStringPesan detail dari biller/sistem
amountStringNominal transaksi dalam IDR
unit_priceStringHarga satuan produk
balance_usedStringSaldo yang dipotong
balanceStringSaldo merchant setelah transaksi
serial_noStringNomor seri / token dari biller
dataObjectData tambahan dari biller (bervariasi)

Idempotency

Semua endpoint transaksi berbayar mendukung idempotency via field req_id:

  • Jika request gagal karena timeout atau error jaringan, kirim ulang dengan req_id yang sama — sistem mengembalikan hasil transaksi pertama tanpa debit ganda.
  • Gunakan req_id berbeda untuk setiap transaksi baru.
  • Format req_id yang disarankan: {PREFIX}-{YYYYMMDD}-{SEQUENCE} — contoh: TXN-20240501-001.

Strict JSON

Semua endpoint menggunakan strict JSON parsing:

  • Field yang tidak dikenal dalam request body akan ditolak dengan 422 Unprocessable Entity.
  • Kirim hanya field yang didokumentasikan.

Kode Respon (RC)

Field rc pada response menunjukkan hasil bisnis dari transaksi. Kode "000" selalu berarti sukses.

Kode Umum (Semua Kategori)

RCStatusKeteranganSaldo Terpotong
000SUCCESSTransaksi berhasilYa
002FAILEDTransaksi tidak ditemukanTidak
005FAILEDKode produk tidak dikenal / pricing tidak tersediaTidak
006FAILEDProduk tidak aktifTidak
007FAILEDProduk sedang maintenanceTidak
008FAILEDSaldo deposit tidak cukupTidak
009FAILEDError pada biller saat inquiryTidak
021PENDINGTransaksi sedang diproses (menunggu konfirmasi)Ya (menunggu final)
022FAILEDKoneksi ke biller gagal (auto-refund)Auto-refunded
023FAILEDResponse biller kosong (auto-refund)Auto-refunded

Kode Disbursement

RCStatusKeteranganSaldo Terpotong
051FAILEDRekening tujuan tidak ditemukan / tidak aktifTidak
00SUCCESSTransfer sukses (RC dari biller)Ya

Kode PPOB

RCStatusKeterangan
00SUCCESSTransaksi sukses (RC dari biller APKITA/TEKTAYA)
14FAILEDNomor pelanggan tidak valid
40FAILEDProduk tidak tersedia untuk operator ini
68PENDINGTransaksi diproses, menunggu konfirmasi
91FAILEDBiller tidak tersedia (maintenance)

Kode RC dari biller pihak ketiga dapat bervariasi. Selalu cek field description untuk keterangan yang lebih detail.

Kode QRIS PayIn

RCStatusKeterangan
000SUCCESSQRIS berhasil dibuat / dikonfirmasi
021PENDINGMenunggu pembayaran dari pelanggan
031FAILEDQRIS kedaluwarsa (expired)
032FAILEDQRIS sudah dibayar sebelumnya

Policy Refund Otomatis

KondisiSaldoPolicy
RC 005, 006, 007, 008, 009Tidak pernah terpotongTidak ada refund
RC 051 (rekening tidak ditemukan)Tidak terpotongTidak ada refund
RC 021 → konfirmasi SUCCESSTerpotong, final suksesTidak ada refund
RC 021 → konfirmasi FAILEDTerpotongSistem otomatis refund
RC 022 (biller call failed)TerpotongSistem otomatis refund
RC 023 (empty biller response)TerpotongSistem otomatis refund

Menangani Status PENDING

Ketika menerima rc: "021" atau status: "PENDING":

  1. Jangan mengirim ulang request payment — ini dapat menyebabkan debit ganda.
  2. Gunakan endpoint cek status (/ppob/check atau /disbursement/status) dengan req_id yang sama.
  3. Polling dengan interval yang meningkat:
Attempt 1 : tunggu  30 detik
Attempt 2 : tunggu  60 detik
Attempt 3 : tunggu 120 detik
Attempt 4 : tunggu 300 detik
Attempt 5+: eskalasi ke tim support dengan menyertakan req_id

Gambaran Umum PPOB

PPOB (Payment Point Online Bank) API memungkinkan merchant untuk melakukan transaksi pembelian produk digital dan pembayaran tagihan melalui sistem Relay.

Endpoint yang Tersedia

EndpointMetodeKeterangan
POST /api/v1/merchant/ppob/checkPOSTCek status transaksi berdasarkan order_req_id
POST /api/v1/merchant/ppob/phone-credit/transactionPOSTBeli pulsa / paket data
POST /api/v1/merchant/ppob/ewallet/inquiryPOSTInquiry sebelum top-up e-wallet
POST /api/v1/merchant/ppob/ewallet/transactionPOSTEksekusi top-up e-wallet
POST /api/v1/merchant/ppob/pln/prepaid/inquiryPOSTInquiry token listrik PLN prabayar
POST /api/v1/merchant/ppob/pln/prepaid/transactionPOSTBeli token listrik PLN prabayar
POST /api/v1/merchant/ppob/pln/postpaid/inquiryPOSTInquiry tagihan PLN pascabayar
POST /api/v1/merchant/ppob/pln/postpaid/transactionPOSTBayar tagihan PLN pascabayar
POST /api/v1/merchant/ppob/bpjs/inquiryPOSTInquiry tagihan BPJS Kesehatan
POST /api/v1/merchant/ppob/bpjs/transactionPOSTBayar tagihan BPJS Kesehatan

Autentikasi

Semua endpoint PPOB menggunakan API Key via header X-API-Key dan opsional Signature Validation. Lihat Autentikasi.

Alur Transaksi

Produk Tanpa Inquiry (Pulsa, Paket Data)

Merchant                         Sistem
  │                                 │
  │  POST /ppob/phone-credit/       │
  │  transaction                    │
  │────────────────────────────────>│
  │                                 │  proses ke biller
  │  200 OK { status, serial_no }   │
  │<────────────────────────────────│

Produk Dengan Inquiry (E-Wallet, PLN, BPJS)

Merchant                         Sistem
  │                                 │
  │  POST /ppob/ewallet/inquiry     │
  │────────────────────────────────>│
  │  200 OK { amount, unit_price }  │
  │<────────────────────────────────│
  │                                 │
  │  POST /ppob/ewallet/transaction │
  │────────────────────────────────>│
  │                                 │  potong saldo, kirim ke biller
  │  200 OK { status, serial_no }   │
  │<────────────────────────────────│

Format Request Umum

Semua endpoint transaksi PPOB menerima JSON dengan field berikut (field spesifik per endpoint dijelaskan di halaman masing-masing):

FieldTipeWajibKeterangan
req_idStringYa (untuk payment)ID unik merchant — idempotency key
product_idStringYaKode produk dari Katalog Produk
cust_idStringYaID pelanggan (nomor HP, ID meter PLN, nomor BPJS, dll)

Format Response Umum

{
  "success": true,
  "message": "...",
  "rc": "000",
  "data": {
    "req_id": "TXN-20240501-001",
    "ref_id": "1777946295495371000",
    "product_id": "XL5",
    "cust_id": "08123456789",
    "status": "SUCCESS",
    "rc": "000",
    "description": "Transaksi Sukses",
    "message": "...",
    "amount": "5000",
    "unit_price": "5500",
    "balance_used": "5500",
    "balance": "494500",
    "serial_no": "SN123456",
    "data": {}
  }
}

Poin Penting

  • Strict JSON — field yang tidak dikenal dalam request body ditolak dengan HTTP 422.
  • Minimal Amount — nominal minimum transaksi adalah Rp 1.000 ("1000").
  • Idempotency — gunakan req_id yang sama untuk retry jika terjadi timeout; jangan kirim req_id baru untuk transaksi yang sama.
  • Produk dengan status: MAINTENANCE atau status: CLOSED pada katalog tidak dapat diproses.

Cek Status Transaksi

Mengecek status terkini dari transaksi PPOB berdasarkan order_req_id.

URL: POST /api/v1/merchant/ppob/check

Kapan Menggunakan

  • Setelah menerima status: "PENDING" dari endpoint transaksi.
  • Untuk memverifikasi hasil transaksi yang gagal karena timeout jaringan.
  • Jangan kirim ulang transaksi sebelum mengecek status terlebih dahulu.

Request

Headers

HeaderWajibKeterangan
X-API-KeyYaAPI Key merchant
Content-TypeYaapplication/json

Query Parameter

ParameterTipeKeterangan
directBooleanJika true, juga query live status dari biller (action status)

Body

FieldTipeWajibKeterangan
order_req_idStringYareq_id dari transaksi yang ingin dicek

Contoh Request

{
  "order_req_id": "TXN-20240501-001"
}
curl -X POST "https://api-sandbox.alfakios.com/api/v1/merchant/ppob/check" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: apk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -d '{"order_req_id": "TXN-20240501-001"}'
# Dengan live check ke biller
curl -X POST "https://api-sandbox.alfakios.com/api/v1/merchant/ppob/check?direct=true" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: apk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -d '{"order_req_id": "TXN-20240501-001"}'

Response

Spesifikasi

FieldTipeKeterangan
req_idStringorder_req_id yang dikirim
ref_idStringID referensi internal sistem
product_idStringKode produk
cust_idStringID customer / nomor tujuan
statusStringSUCCESS, FAILED, atau PENDING
rcStringKode respon
descriptionStringKeterangan RC
messageStringPesan detail
unit_priceStringHarga produk
balance_usedStringSaldo yang terpotong
balanceStringSaldo merchant setelah transaksi
serial_noStringNomor seri / token dari biller
dataObjectData tambahan dari biller
billerObject(opsional) Payload live biller jika direct=true

Contoh Response — Transaksi Ditemukan

{
  "success": true,
  "message": "Transaction status checked successfully",
  "rc": "000",
  "data": {
    "req_id": "TXN-20240501-001",
    "ref_id": "1777946295495371000",
    "product_id": "XL5",
    "cust_id": "08123456789",
    "status": "SUCCESS",
    "rc": "000",
    "description": "Transaksi Sukses",
    "message": "PULSA XL 5000 KE 08123456789 SUKSES",
    "unit_price": "5500",
    "balance_used": "5500",
    "balance": "494500",
    "serial_no": "REF123456789",
    "data": {}
  }
}

Contoh Response — Transaksi Tidak Ditemukan

{
  "success": false,
  "message": "Transaction not found",
  "rc": "002"
}

HTTP Status: 400 Bad Request

Catatan Penting

  • Endpoint ini hanya bisa mengecek transaksi yang dibuat oleh merchant yang sama (API Key yang sama).
  • Parameter direct=true menyebabkan query langsung ke biller — gunakan dengan hati-hati karena dapat memperlambat response.
  • Jika order_req_id tidak ditemukan, pastikan ejaan sudah benar dan transaksi memang pernah dibuat dengan merchant ini.

Pulsa & Paket Data

Membeli pulsa atau paket data untuk nomor pelanggan.

URL: POST /api/v1/merchant/ppob/phone-credit/transaction

Request

Headers

HeaderWajibKeterangan
X-API-KeyYaAPI Key merchant
Content-TypeYaapplication/json

Body

FieldTipeWajibKeterangan
req_idStringYaID unik transaksi dari merchant (idempotency key)
product_idStringYaKode produk pulsa/paket (dari Katalog Produk)
cust_idStringYaNomor HP tujuan (format: 08xxxxxxxxxx atau 628xxxxxxxxxx)

Contoh Request — Pulsa

{
  "req_id": "TXN-20240501-001",
  "product_id": "XL5",
  "cust_id": "08123456789"
}

Contoh Request — Paket Data

{
  "req_id": "TXN-20240501-002",
  "product_id": "TSEL1GB7",
  "cust_id": "08211223344"
}
curl -X POST "https://api-sandbox.alfakios.com/api/v1/merchant/ppob/phone-credit/transaction" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: apk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -d '{
    "req_id": "TXN-20240501-001",
    "product_id": "XL5",
    "cust_id": "08123456789"
  }'

Response

Spesifikasi

FieldTipeKeterangan
req_idStringreq_id yang dikirim merchant
ref_idStringID referensi internal sistem
product_idStringKode produk
cust_idStringNomor HP tujuan
statusStringSUCCESS, FAILED, atau PENDING
rcStringKode respon
descriptionStringKeterangan RC
messageStringPesan detail dari biller
amountStringNominal produk (nilai pulsa)
unit_priceStringHarga jual produk (yang dipotong dari saldo)
balance_usedStringSaldo yang benar-benar terpotong
balanceStringSaldo merchant setelah transaksi
serial_noStringNomor referensi dari biller
dataObjectData tambahan

Contoh Response Berhasil

{
  "success": true,
  "message": "PULSA XL 5000 KE 08123456789 SUKSES",
  "rc": "000",
  "data": {
    "req_id": "TXN-20240501-001",
    "ref_id": "1777946295495371000",
    "product_id": "XL5",
    "cust_id": "08123456789",
    "status": "SUCCESS",
    "rc": "000",
    "description": "Transaksi Sukses",
    "message": "PULSA XL 5000 KE 08123456789 SUKSES",
    "amount": "5000",
    "unit_price": "5500",
    "balance_used": "5500",
    "balance": "494500",
    "serial_no": "REF123456789",
    "data": {}
  }
}

Contoh Response Gagal — Nomor Tidak Valid

{
  "success": true,
  "message": "PULSA XL 5000 KE 08123456789 GAGAL",
  "rc": "14",
  "data": {
    "req_id": "TXN-20240501-003",
    "ref_id": "1777946295495372000",
    "product_id": "XL5",
    "cust_id": "0812345678",
    "status": "FAILED",
    "rc": "14",
    "description": "Nomor tidak valid",
    "message": "Nomor HP tidak valid untuk operator ini",
    "balance": "494500",
    "data": {}
  }
}

Contoh Kode Produk

Gunakan endpoint Katalog Produk untuk mendapatkan daftar lengkap. Beberapa contoh:

product_idDeskripsiNominal
XL5Pulsa XL Rp 5.0005.000
XL10Pulsa XL Rp 10.00010.000
TSEL5Pulsa Telkomsel Rp 5.0005.000
TSEL1GB7Paket Data Telkomsel 1GB / 7 hari
ISAT10Pulsa Indosat Rp 10.00010.000
AXIS5Pulsa Axis Rp 5.0005.000

Catatan

  • Transaksi pulsa umumnya bersifat instan (tidak ada alur inquiry).
  • Jika status: "PENDING", gunakan Cek Status untuk memantau hasilnya.
  • req_id berfungsi sebagai idempotency key — retry aman tanpa risiko pengiriman ganda.

E-Wallet

Top-up saldo e-wallet pelanggan (GoPay, OVO, Dana, ShopeePay, dll) menggunakan alur dua langkah: inquiry → payment.

Endpoint

EndpointKeterangan
POST /api/v1/merchant/ppob/ewallet/inquiryValidasi transaksi sebelum eksekusi
POST /api/v1/merchant/ppob/ewallet/transactionEksekusi top-up e-wallet

Alur Transaksi

1. Inquiry  — validasi nomor e-wallet dan cek harga
2. Payment  — eksekusi top-up (potong saldo merchant)

Inquiry E-Wallet

Memvalidasi nomor e-wallet dan mendapatkan informasi harga sebelum eksekusi. Tidak memotong saldo.

URL: POST /api/v1/merchant/ppob/ewallet/inquiry

Request

FieldTipeWajibKeterangan
product_idStringYaKode produk e-wallet (dari katalog)
cust_idStringYaNomor HP / ID akun e-wallet tujuan
amountStringYaNominal top-up dalam IDR (digits only, min "1000")
req_idStringTidakID request untuk tracing (opsional pada inquiry)

Contoh Request

{
  "product_id": "GOPAY",
  "cust_id": "08123456789",
  "amount": "50000"
}
curl -X POST "https://api-sandbox.alfakios.com/api/v1/merchant/ppob/ewallet/inquiry" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: apk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -d '{
    "product_id": "GOPAY",
    "cust_id": "08123456789",
    "amount": "50000"
  }'

Response Inquiry Berhasil

{
  "success": true,
  "message": "E-wallet inquiry completed successfully",
  "rc": "000",
  "data": {
    "req_id": "",
    "ref_id": "1777946295495371000",
    "product_id": "GOPAY",
    "cust_id": "08123456789",
    "status": "SUCCESS",
    "rc": "000",
    "description": "Transaksi Sukses",
    "message": "INQUIRY GOPAY 50000 KE 08123456789 SUKSES",
    "amount": "50000",
    "unit_price": "51500",
    "balance_used": "0",
    "balance": "500000",
    "serial_no": "",
    "data": {}
  }
}

Payment E-Wallet

Mengeksekusi top-up e-wallet. Saldo merchant akan dipotong.

URL: POST /api/v1/merchant/ppob/ewallet/transaction

Request

FieldTipeWajibKeterangan
req_idStringYaID unik transaksi (idempotency key)
product_idStringYaKode produk e-wallet (sama dengan inquiry)
cust_idStringYaNomor HP / ID akun e-wallet (sama dengan inquiry)
amountStringYaNominal top-up (sama dengan inquiry)

Contoh Request

{
  "req_id": "TXN-20240501-010",
  "product_id": "GOPAY",
  "cust_id": "08123456789",
  "amount": "50000"
}
curl -X POST "https://api-sandbox.alfakios.com/api/v1/merchant/ppob/ewallet/transaction" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: apk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -d '{
    "req_id": "TXN-20240501-010",
    "product_id": "GOPAY",
    "cust_id": "08123456789",
    "amount": "50000"
  }'

Response Payment Berhasil

{
  "success": true,
  "message": "E-wallet payment created successfully",
  "rc": "000",
  "data": {
    "req_id": "TXN-20240501-010",
    "ref_id": "1777946295495372000",
    "product_id": "GOPAY",
    "cust_id": "08123456789",
    "status": "SUCCESS",
    "rc": "000",
    "description": "Transaksi Sukses",
    "message": "TOPUP GOPAY 50000 KE 08123456789 SUKSES",
    "amount": "50000",
    "unit_price": "51500",
    "balance_used": "51500",
    "balance": "448500",
    "serial_no": "GW123456789",
    "data": {}
  }
}

Response Saldo Tidak Cukup

{
  "success": true,
  "message": "Transaction failed",
  "rc": "008",
  "data": {
    "req_id": "TXN-20240501-011",
    "status": "FAILED",
    "rc": "008",
    "description": "Insufficient Balance",
    "message": "Saldo deposit tidak mencukupi",
    "balance": "10000",
    "data": {}
  }
}

Contoh Kode Produk E-Wallet

product_idOperatorKeterangan
GOPAYGoPayTop-up GoPay via nomor HP
OVOOVOTop-up OVO via nomor HP
DANADANATop-up DANA via nomor HP
SHOPEEPAYShopeePayTop-up ShopeePay
LINKAJALinkAjaTop-up LinkAja

Gunakan endpoint Katalog Produk untuk daftar lengkap yang tersedia di akun merchant Anda.

Catatan

  • Inquiry tidak wajib dilakukan sebelum payment — Anda bisa langsung ke payment jika sudah mengetahui harganya dari katalog.
  • Gunakan req_id yang sama untuk retry jika terjadi timeout.

PLN Prabayar (Token Listrik)

Beli token listrik PLN prabayar untuk nomor meter pelanggan. Menggunakan alur dua langkah: inquiry → transaksi.

Endpoint

EndpointKeterangan
POST /api/v1/merchant/ppob/pln/prepaid/inquiryInquiry nomor meter, cek nama pelanggan & tarif
POST /api/v1/merchant/ppob/pln/prepaid/transactionBeli token listrik

Inquiry PLN Prabayar

Memvalidasi nomor meter dan mendapatkan informasi pelanggan sebelum pembelian. Tidak memotong saldo.

URL: POST /api/v1/merchant/ppob/pln/prepaid/inquiry

Request

FieldTipeWajibKeterangan
product_idStringYaKode produk PLN prabayar (dari katalog)
cust_idStringYaNomor meter listrik atau ID pelanggan PLN

Contoh Request

{
  "product_id": "PLNPREP",
  "cust_id": "12345678901"
}
curl -X POST "https://api-sandbox.alfakios.com/api/v1/merchant/ppob/pln/prepaid/inquiry" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: apk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -d '{"product_id": "PLNPREP", "cust_id": "12345678901"}'

Response Inquiry Berhasil

{
  "success": true,
  "message": "PLN prepaid inquiry completed successfully",
  "rc": "000",
  "data": {
    "req_id": "",
    "ref_id": "1777946295495371000",
    "product_id": "PLNPREP",
    "cust_id": "12345678901",
    "status": "SUCCESS",
    "rc": "000",
    "description": "Transaksi Sukses",
    "message": "INQUIRY PLN PREPAID KE 12345678901 SUKSES",
    "amount": "20000",
    "unit_price": "21500",
    "balance_used": "0",
    "balance": "500000",
    "serial_no": "",
    "data": {
      "nama_pelanggan": "BUDI SANTOSO",
      "tarif": "R1/TR",
      "daya": "1300 VA",
      "nomor_meter": "12345678901"
    }
  }
}

Transaksi PLN Prabayar

Membeli token listrik. Saldo merchant akan dipotong.

URL: POST /api/v1/merchant/ppob/pln/prepaid/transaction

Request

FieldTipeWajibKeterangan
req_idStringYaID unik transaksi (idempotency key)
product_idStringYaKode produk PLN prabayar
cust_idStringYaNomor meter (sama dengan inquiry)

Contoh Request

{
  "req_id": "TXN-20240501-020",
  "product_id": "PLNPREP",
  "cust_id": "12345678901"
}
curl -X POST "https://api-sandbox.alfakios.com/api/v1/merchant/ppob/pln/prepaid/transaction" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: apk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -d '{
    "req_id": "TXN-20240501-020",
    "product_id": "PLNPREP",
    "cust_id": "12345678901"
  }'

Response Berhasil

{
  "success": true,
  "message": "PLN prepaid top-up created successfully",
  "rc": "000",
  "data": {
    "req_id": "TXN-20240501-020",
    "ref_id": "1777946295495372000",
    "product_id": "PLNPREP",
    "cust_id": "12345678901",
    "status": "SUCCESS",
    "rc": "000",
    "description": "Transaksi Sukses",
    "message": "TOKEN PLN 20000 KE 12345678901 SUKSES",
    "amount": "20000",
    "unit_price": "21500",
    "balance_used": "21500",
    "balance": "478500",
    "serial_no": "0123-4567-8901-2345-6789",
    "data": {
      "token": "0123-4567-8901-2345-6789",
      "kwh": "9.1",
      "nama_pelanggan": "BUDI SANTOSO",
      "tarif": "R1/TR"
    }
  }
}

Token Listrik tersedia di field serial_no dan data.token. Tampilkan nilai ini kepada pelanggan untuk diinput ke meteran listrik.

Kode Produk PLN Prabayar

product_idDenomKeterangan
PLNPREPDinamisToken PLN Prabayar (harga sesuai nominal)
PLN2020.000Token PLN Rp 20.000
PLN5050.000Token PLN Rp 50.000
PLN100100.000Token PLN Rp 100.000
PLN200200.000Token PLN Rp 200.000
PLN500500.000Token PLN Rp 500.000
PLN10001.000.000Token PLN Rp 1.000.000

Gunakan endpoint Katalog Produk untuk daftar produk yang tersedia di akun Anda.

Catatan

  • Token listrik tersedia di field serial_no setelah transaksi sukses.
  • Format token: XXXX-XXXX-XXXX-XXXX-XXXX (20 digit).
  • Jika status: "PENDING", gunakan Cek Status untuk polling.

PLN Pascabayar (Tagihan Listrik)

Bayar tagihan listrik PLN pascabayar. Menggunakan alur dua langkah: inquiry tagihan → pembayaran.

Endpoint

EndpointKeterangan
POST /api/v1/merchant/ppob/pln/postpaid/inquiryInquiry tagihan, cek jumlah yang harus dibayar
POST /api/v1/merchant/ppob/pln/postpaid/transactionBayar tagihan listrik

Inquiry PLN Pascabayar

Mengambil informasi tagihan listrik pelanggan. Tidak memotong saldo.

URL: POST /api/v1/merchant/ppob/pln/postpaid/inquiry

Request

FieldTipeWajibKeterangan
product_idStringYaKode produk PLN pascabayar (dari katalog)
cust_idStringYaNomor ID pelanggan PLN (bukan nomor meter)

Contoh Request

{
  "product_id": "PLNPOST",
  "cust_id": "512345678901"
}
curl -X POST "https://api-sandbox.alfakios.com/api/v1/merchant/ppob/pln/postpaid/inquiry" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: apk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -d '{"product_id": "PLNPOST", "cust_id": "512345678901"}'

Response Inquiry Berhasil

{
  "success": true,
  "message": "PLN prepaid inquiry completed successfully",
  "rc": "000",
  "data": {
    "req_id": "",
    "ref_id": "1777946295495371000",
    "product_id": "PLNPOST",
    "cust_id": "512345678901",
    "status": "SUCCESS",
    "rc": "000",
    "description": "Transaksi Sukses",
    "message": "INQUIRY PLN POSTPAID KE 512345678901 SUKSES",
    "amount": "285000",
    "unit_price": "291000",
    "balance_used": "0",
    "balance": "500000",
    "serial_no": "",
    "data": {
      "nama_pelanggan": "SITI RAHAYU",
      "tarif": "R2/TR",
      "daya": "2200 VA",
      "id_pelanggan": "512345678901",
      "periode": "APR 2024",
      "tagihan": "285000",
      "admin": "6000"
    }
  }
}

Pembayaran PLN Pascabayar

Membayar tagihan listrik. Saldo merchant akan dipotong.

URL: POST /api/v1/merchant/ppob/pln/postpaid/transaction

Request

FieldTipeWajibKeterangan
req_idStringYaID unik transaksi (idempotency key)
product_idStringYaKode produk PLN pascabayar
cust_idStringYaID pelanggan PLN (sama dengan inquiry)

Contoh Request

{
  "req_id": "TXN-20240501-030",
  "product_id": "PLNPOST",
  "cust_id": "512345678901"
}
curl -X POST "https://api-sandbox.alfakios.com/api/v1/merchant/ppob/pln/postpaid/transaction" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: apk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -d '{
    "req_id": "TXN-20240501-030",
    "product_id": "PLNPOST",
    "cust_id": "512345678901"
  }'

Response Berhasil

{
  "success": true,
  "message": "PLN prepaid top-up created successfully",
  "rc": "000",
  "data": {
    "req_id": "TXN-20240501-030",
    "ref_id": "1777946295495372000",
    "product_id": "PLNPOST",
    "cust_id": "512345678901",
    "status": "SUCCESS",
    "rc": "000",
    "description": "Transaksi Sukses",
    "message": "BAYAR TAGIHAN PLN 512345678901 SUKSES",
    "amount": "285000",
    "unit_price": "291000",
    "balance_used": "291000",
    "balance": "209000",
    "serial_no": "REF-PLN-20240501",
    "data": {
      "nama_pelanggan": "SITI RAHAYU",
      "periode": "APR 2024",
      "nomor_struk": "REF-PLN-20240501"
    }
  }
}

Perbedaan PLN Prabayar vs Pascabayar

AspekPrabayarPascabayar
Jenis ProdukBeli tokenBayar tagihan
cust_idNomor meterID pelanggan PLN
OutputToken 20 digitNomor struk
unit_priceHarga beli + adminTagihan + biaya admin

Catatan

  • Satu ID pelanggan PLN pascabayar bisa memiliki tagihan untuk beberapa bulan sekaligus. Konfirmasi dengan pelanggan sebelum melakukan pembayaran.
  • Nomor struk pembayaran tersedia di serial_no setelah transaksi sukses.

BPJS Kesehatan

Bayar iuran BPJS Kesehatan untuk nomor kartu/peserta BPJS. Menggunakan alur dua langkah: inquiry → pembayaran.

Endpoint

EndpointKeterangan
POST /api/v1/merchant/ppob/bpjs/inquiryInquiry tagihan BPJS, cek nama & jumlah
POST /api/v1/merchant/ppob/bpjs/transactionBayar iuran BPJS

Inquiry BPJS

Mengambil informasi tagihan BPJS Kesehatan. Tidak memotong saldo.

URL: POST /api/v1/merchant/ppob/bpjs/inquiry

Request

FieldTipeWajibKeterangan
product_idStringYaKode produk BPJS (dari katalog)
cust_idStringYaNomor kartu / peserta BPJS Kesehatan
periodStringYaJumlah bulan yang dibayar (2 digit, contoh: "01" = 1 bulan)
mobile_noStringTidakNomor HP peserta (untuk notifikasi)

Catatan period: Nilai harus berupa 2 digit numerik, misalnya "01" untuk 1 bulan, "03" untuk 3 bulan. Maksimum 12 bulan.

Contoh Request

{
  "product_id": "BPJSKS",
  "cust_id": "0001234567890",
  "period": "01",
  "mobile_no": "08123456789"
}
curl -X POST "https://api-sandbox.alfakios.com/api/v1/merchant/ppob/bpjs/inquiry" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: apk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -d '{
    "product_id": "BPJSKS",
    "cust_id": "0001234567890",
    "period": "01",
    "mobile_no": "08123456789"
  }'

Response Inquiry Berhasil

{
  "success": true,
  "message": "BPJS inquiry completed successfully",
  "rc": "000",
  "data": {
    "req_id": "",
    "ref_id": "1777946295495371000",
    "product_id": "BPJSKS",
    "cust_id": "0001234567890",
    "status": "SUCCESS",
    "rc": "000",
    "description": "Transaksi Sukses",
    "message": "INQUIRY BPJSKS KE 0001234567890 SUKSES",
    "amount": "42000",
    "unit_price": "42500",
    "balance_used": "0",
    "balance": "500000",
    "serial_no": "",
    "data": {
      "nama_peserta": "AHMAD FAUZAN",
      "jumlah_peserta": "1",
      "periode": "01",
      "tagihan": "42000",
      "admin": "500",
      "total": "42500"
    }
  }
}

Pembayaran BPJS

Membayar iuran BPJS Kesehatan. Saldo merchant akan dipotong.

URL: POST /api/v1/merchant/ppob/bpjs/transaction

Request

FieldTipeWajibKeterangan
req_idStringYaID unik transaksi (idempotency key)
product_idStringYaKode produk BPJS
cust_idStringYaNomor BPJS (sama dengan inquiry)
periodStringYaPeriode bulan (sama dengan inquiry, format 2 digit)
inquiry_req_idStringYareq_id dari step inquiry — digunakan oleh biller sebagai referensi
mobile_noStringTidakNomor HP peserta
emailStringTidakEmail peserta (untuk notifikasi)

Penting: inquiry_req_id harus diisi dengan nilai req_id yang dikembalikan dari response inquiry. Jika inquiry dilakukan tanpa req_id, gunakan ref_id dari response inquiry.

Contoh Request

{
  "req_id": "TXN-20240501-040",
  "product_id": "BPJSKS",
  "cust_id": "0001234567890",
  "period": "01",
  "inquiry_req_id": "INQ-20240501-040",
  "mobile_no": "08123456789",
  "email": "ahmad@email.com"
}
curl -X POST "https://api-sandbox.alfakios.com/api/v1/merchant/ppob/bpjs/transaction" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: apk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -d '{
    "req_id": "TXN-20240501-040",
    "product_id": "BPJSKS",
    "cust_id": "0001234567890",
    "period": "01",
    "inquiry_req_id": "INQ-20240501-040",
    "mobile_no": "08123456789"
  }'

Response Berhasil

{
  "success": true,
  "message": "BPJS payment completed successfully",
  "rc": "000",
  "data": {
    "req_id": "TXN-20240501-040",
    "ref_id": "1777946295495372000",
    "product_id": "BPJSKS",
    "cust_id": "0001234567890",
    "status": "SUCCESS",
    "rc": "000",
    "description": "Transaksi Sukses",
    "message": "BAYAR BPJSKS 0001234567890 SUKSES",
    "amount": "42000",
    "unit_price": "42500",
    "balance_used": "42500",
    "balance": "457500",
    "serial_no": "BPJS-REF-2024050112345",
    "data": {
      "nama_peserta": "AHMAD FAUZAN",
      "periode": "01",
      "nomor_struk": "BPJS-REF-2024050112345"
    }
  }
}

Kode Produk BPJS

product_idKeterangan
BPJSKSBPJS Kesehatan

Gunakan endpoint Katalog Produk untuk daftar produk terbaru.

Catatan

  • Field period wajib dan harus berupa string 2 digit ("01" s/d "12").
  • Field inquiry_req_id pada payment harus berisi req_id yang digunakan saat inquiry — ini adalah referensi transaksi yang dibutuhkan biller untuk melanjutkan proses.
  • Nomor struk pembayaran tersedia di serial_no setelah transaksi sukses.

Gambaran Umum Disbursement

Disbursement API memungkinkan merchant untuk melakukan transfer dana ke rekening bank tujuan secara terprogram. Menggunakan mekanisme dua langkah (inquiry → payment) untuk memastikan validitas rekening sebelum dana dikirim.

Endpoint

EndpointMetodeKeterangan
POST /api/v1/merchant/disbursement/inquiryPOSTValidasi rekening tujuan (tidak memotong saldo)
POST /api/v1/merchant/disbursement/paymentPOSTEksekusi transfer dana
POST /api/v1/merchant/disbursement/statusPOSTCek status transaksi disbursement

Karakteristik

  • Protokol: HTTP POST
  • Format Data: JSON / Content-Type: application/json
  • Autentikasi: API Key via header X-API-Key
  • Keamanan Tambahan: Signature Validation (opsional)
  • Timeout Request: 8 detik

Alur Transaksi

Merchant                              Sistem
  │                                      │
  │  POST /disbursement/inquiry          │
  │─────────────────────────────────────>│
  │                                      │  validasi rekening ke biller
  │  200 OK { inquiry_reff,              │
  │           account_name, fee }        │
  │<─────────────────────────────────────│
  │                                      │
  │  POST /disbursement/payment          │
  │─────────────────────────────────────>│
  │                                      │  potong saldo, kirim ke bank tujuan
  │  200 OK { status, ref_id }           │
  │<─────────────────────────────────────│
  │                                      │
  │  (opsional) POST /disbursement/status│
  │─────────────────────────────────────>│
  │  200 OK { status final }             │
  │<─────────────────────────────────────│

Poin Penting

  1. Inquiry tidak memotong saldo — hanya memvalidasi rekening dan mendapatkan informasi fee.
  2. Payment memotong saldo — jika rc: "000" atau rc: "00", saldo langsung berkurang.
  3. Status PENDING — terjadi jika biller belum konfirmasi final. Gunakan /status untuk polling.
  4. Jangan kirim payment ulang sebelum cek status — risiko double transfer.
  5. Idempotency via req_id — retry aman dengan req_id yang sama jika terjadi timeout.

Validasi Request

  • Nominal (amount) harus berupa string digit (contoh: "100000"), tidak boleh negatif, desimal, atau scientific notation.
  • Minimal transfer: Rp 10.000 ("10000").
  • Field yang tidak dikenal dalam JSON body akan ditolak dengan HTTP 422.

Kode Bank Umum

KodeNama Bank
002Bank Rakyat Indonesia (BRI)
008Mandiri
009Bank Negara Indonesia (BNI)
014Bank Central Asia (BCA)
022CIMB Niaga
028Wulandari Kencana (OCBC NISP)
032Bank Tabungan Pensiun Nasional (BTPN)
036Bank Tabungan Negara (BTN)
046DKI Jakarta
200Danamon
213BTPN Syariah
426Mega
451Syariah Indonesia (BSI)
484Sahabat Sampoerna
553BRI Syariah / BSI
898Nobu (National Nobu)

Gunakan endpoint Katalog Produk untuk mendapatkan daftar produk transfer yang tersedia (kode bank dapat juga diambil dari sana).

Inquiry Rekening

Memvalidasi rekening bank tujuan sebelum transfer. Tidak memotong saldo deposit.

URL: POST /api/v1/merchant/disbursement/inquiry

Request

Headers

HeaderWajibKeterangan
X-API-KeyYaAPI Key merchant
Content-TypeYaapplication/json
X-SignatureKondisionalJika Signature Validation aktif
X-TimestampKondisionalUnix timestamp (jika Signature Validation aktif)

Body

FieldTipeWajibKeterangan
product_idStringYaKode produk transfer (contoh: "DSTF")
account_numberStringYaNomor rekening tujuan
amountStringYaNominal transfer dalam IDR (digits only, min "10000")
bank_codeStringTidakKode bank tujuan — disarankan untuk akurasi routing

Contoh Request

{
  "product_id": "DSTF",
  "account_number": "1380610457",
  "amount": "100000",
  "bank_code": "014"
}
curl --location 'https://api-sandbox.alfakios.com/api/v1/merchant/disbursement/inquiry' \
  --header 'Content-Type: application/json' \
  --header 'X-API-Key: apk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' \
  --data '{
    "product_id": "DSTF",
    "account_number": "1380610457",
    "amount": "100000",
    "bank_code": "014"
  }'

Response

Spesifikasi

FieldTipeKeterangan
trx_idStringID transaksi internal sistem
req_idStringreq_id yang dikirim merchant
ref_idStringID referensi transaksi (sama dengan trx_id)
inquiry_reffStringReferensi inquiry dari biller — digunakan internal pada step payment
account_nameStringNama pemilik rekening tujuan
account_numberStringNomor rekening tujuan
bank_codeStringKode bank tujuan
bank_nameStringNama bank tujuan
amountStringNominal transfer
feeStringBiaya admin transfer
total_costStringTotal biaya (amount + fee)
balanceStringSaldo deposit merchant saat ini
statusStringStatus inquiry: SUCCESS atau FAILED
rcStringKode respon dari biller
descriptionStringKeterangan RC
messageStringKeterangan detail
dataObjectInformasi tambahan dari biller
detailsStringString OtomaX-style — lihat Integrasi OtomaX

Contoh Response Berhasil

{
  "success": true,
  "message": "Disbursement inquiry completed",
  "rc": "000",
  "data": {
    "trx_id": "1777946295495371000",
    "req_id": "PAY-20240430-001",
    "ref_id": "1777946295495371000",
    "inquiry_reff": "1017797",
    "account_name": "DUMMY NAME",
    "account_number": "1380610457",
    "bank_code": "014",
    "bank_name": "Bank Central Asia",
    "amount": "100000",
    "fee": "3000",
    "total_cost": "103000",
    "balance": "281994",
    "status": "SUCCESS",
    "rc": "00",
    "description": "Transaksi Sukses",
    "message": "INQUIRY DISBURSEMENT DSTF KE 1380610457 SUKSES",
    "data": {},
    "details": "REQID:PAY-20240430-001.REFID:1777946295495371000.INQREF:1017797.STATUS:SUCCESS.RC:00.NAMA:DUMMY NAME.REK:1380610457.KODEBANK:014.NOMINAL:100000.FEE:3000.TOTAL:103000.SALDO:281994.KET:Transaksi Sukses"
  }
}

Contoh Response Rekening Tidak Ditemukan

{
  "success": true,
  "message": "Disbursement inquiry completed",
  "rc": "051",
  "data": {
    "req_id": "",
    "ref_id": "REF-1714435200200002",
    "inquiry_reff": "",
    "account_name": "",
    "account_number": "9999999999",
    "bank_code": "014",
    "bank_name": "Bank Central Asia",
    "amount": "500000",
    "fee": "0",
    "total_cost": "0",
    "balance": "5000000",
    "status": "FAILED",
    "rc": "051",
    "description": "Account Not Found",
    "message": "Rekening tidak ditemukan atau tidak aktif",
    "data": {}
  }
}

Contoh Response Error Validasi (422)

{
  "success": false,
  "message": "Validation failed",
  "errors": [
    {
      "field": "amount",
      "message": "amount must be a positive integer (digits only)"
    }
  ]
}

Catatan

  • inquiry_reff digunakan secara internal oleh sistem — Anda tidak perlu meneruskan nilai ini ke endpoint payment.
  • Catat fee dan total_cost dari response inquiry untuk ditampilkan kepada pengguna sebelum konfirmasi transfer.
  • Rekening yang tidak ditemukan (rc: "051") tidak memotong saldo; aman untuk dicoba ulang dengan nomor yang berbeda.

Eksekusi Transfer Dana

Mengeksekusi transfer dana ke rekening bank tujuan. Saldo deposit merchant akan dipotong.

URL: POST /api/v1/merchant/disbursement/payment

Lakukan Inquiry Rekening terlebih dahulu untuk memvalidasi rekening dan mendapatkan informasi fee sebelum menjalankan payment.

Request

Headers

HeaderWajibKeterangan
X-API-KeyYaAPI Key merchant
Content-TypeYaapplication/json
X-SignatureKondisionalJika Signature Validation aktif
X-TimestampKondisionalUnix timestamp (jika Signature Validation aktif)

Body

FieldTipeWajibKeterangan
req_idStringYaID unik transaksi dari merchant — idempotency key
product_idStringYaKode produk transfer (contoh: "DSTF")
account_numberStringYaNomor rekening tujuan (sama dengan inquiry)
amountStringYaNominal transfer dalam IDR (sama dengan inquiry)
bank_codeStringTidakKode bank tujuan (opsional)

Idempotency via req_id

req_id berfungsi sebagai idempotency key:

  • Retry dengan req_id yang sama → sistem mengembalikan hasil transaksi pertama, tanpa debit ganda.
  • Gunakan req_id yang berbeda hanya untuk transaksi yang memang baru.

Contoh Request

{
  "req_id": "PAY-20240430-001",
  "product_id": "DSTF",
  "account_number": "1380610457",
  "amount": "10000",
  "bank_code": "014"
}
curl --location 'https://api-sandbox.alfakios.com/api/v1/merchant/disbursement/payment' \
  --header 'Content-Type: application/json' \
  --header 'X-API-Key: apk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' \
  --data '{
    "req_id": "PAY-20240430-001",
    "product_id": "DSTF",
    "account_number": "1380610457",
    "amount": "10000",
    "bank_code": "014"
  }'

Response

Spesifikasi

FieldTipeKeterangan
trx_idStringID transaksi internal sistem
req_idStringreq_id yang dikirim merchant
ref_idStringID referensi dari biller
account_numberStringNomor rekening tujuan
account_nameStringNama pemilik rekening
bank_codeStringKode bank tujuan
bank_nameStringNama bank tujuan
statusStringSUCCESS, FAILED, atau PENDING
rcStringKode respon dari biller atau sistem
descriptionStringKeterangan RC
messageStringKeterangan detail
amountStringNominal transfer
feeStringBiaya admin yang dipotong
total_costStringTotal yang dipotong (amount + fee)
balance_usedStringSaldo yang benar-benar terpotong
balanceStringSaldo deposit setelah transaksi
serial_noStringNomor referensi transfer dari biller
dataObjectInformasi tambahan
detailsStringString OtomaX-style — lihat Integrasi OtomaX

Contoh Response Berhasil

{
  "success": true,
  "message": "Disbursement payment completed",
  "rc": "000",
  "data": {
    "trx_id": "177794638960894000",
    "req_id": "PAY-20240430-001",
    "ref_id": "980173",
    "account_number": "1380610457",
    "account_name": "DUMMY NAME",
    "bank_code": "014",
    "bank_name": "",
    "status": "SUCCESS",
    "rc": "00",
    "description": "Transaksi Sukses",
    "message": "DISBURSEMENT DSTF KE 1380610457 SUKSES. SAL=268994,ID=177794638960894000",
    "amount": "10000",
    "fee": "3000",
    "total_cost": "13000",
    "balance_used": "13000",
    "balance": "268994",
    "serial_no": "980173",
    "data": {},
    "details": "REQID:PAY-20240430-001.REFID:980173.STATUS:SUCCESS.RC:00.NAMA:DUMMY NAME.REK:1380610457.KODEBANK:014.NOMINAL:10000.FEE:3000.TOTAL:13000.SALDO:268994.TERPAKAI:13000.SN:980173.KET:Transaksi Sukses"
  }
}

Contoh Response Saldo Tidak Cukup

{
  "success": true,
  "message": "Disbursement payment completed",
  "rc": "008",
  "data": {
    "req_id": "PAY-20240430-002",
    "ref_id": "",
    "account_number": "1380610457",
    "account_name": "",
    "status": "FAILED",
    "rc": "008",
    "description": "Insufficient Balance",
    "message": "Saldo deposit tidak mencukupi",
    "amount": "500000",
    "fee": "6500",
    "total_cost": "506500",
    "balance_used": "0",
    "balance": "100000",
    "serial_no": "",
    "data": {}
  }
}

Contoh Response PENDING

{
  "success": true,
  "message": "Disbursement payment completed",
  "rc": "021",
  "data": {
    "req_id": "PAY-20240430-003",
    "ref_id": "980174",
    "account_number": "1380610457",
    "account_name": "BUDI SANTOSO",
    "status": "PENDING",
    "rc": "021",
    "description": "Transaction Pending",
    "message": "Transfer sedang diproses oleh bank tujuan",
    "amount": "500000",
    "fee": "6500",
    "total_cost": "506500",
    "balance_used": "506500",
    "balance": "4493500",
    "serial_no": "",
    "data": {}
  }
}

Jika status: "PENDING", gunakan endpoint Cek Status untuk polling hasil akhir.

Rekomendasi Retry Policy (PENDING)

Attempt 1 : tunggu  30 detik
Attempt 2 : tunggu  60 detik
Attempt 3 : tunggu 120 detik
Attempt 4 : tunggu 300 detik
Attempt 5+: eskalasi ke tim support dengan menyertakan req_id

JANGAN kirim payment baru sebelum mendapatkan status final. Transfer ganda dapat terjadi jika payment dikirim lebih dari sekali untuk transaksi yang sama.

Cek Status Disbursement

Mengecek status terbaru dari transaksi disbursement yang sebelumnya dikirim. Gunakan endpoint ini ketika transaksi mengembalikan status: "PENDING".

URL: POST /api/v1/merchant/disbursement/status

Request

Headers

HeaderWajibKeterangan
X-API-KeyYaAPI Key merchant
Content-TypeYaapplication/json
X-SignatureKondisionalJika Signature Validation aktif
X-TimestampKondisionalUnix timestamp (jika Signature Validation aktif)

Body

FieldTipeWajibKeterangan
product_idStringYaKode produk yang digunakan saat transaksi
account_numberStringYaNomor rekening tujuan
amountStringYaNominal yang ditransfer
req_idStringYareq_id dari response payment yang ingin dicek
bank_codeStringTidakKode bank tujuan (opsional)

Contoh Request

{
  "product_id": "DSTF",
  "account_number": "1380610457",
  "amount": "100000",
  "bank_code": "014",
  "req_id": "PAY-20240430-001"
}
curl --location 'https://api-sandbox.alfakios.com/api/v1/merchant/disbursement/status' \
  --header 'Content-Type: application/json' \
  --header 'X-API-Key: apk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' \
  --data '{
    "product_id": "DSTF",
    "account_number": "1380610457",
    "amount": "100000",
    "bank_code": "014",
    "req_id": "PAY-20240430-001"
  }'

Response

Response menggunakan shape yang identik dengan response Inquiry Rekening, termasuk field details berformat OtomaX.

Spesifikasi

FieldTipeKeterangan
trx_idStringID transaksi internal sistem
req_idStringreq_id yang dicek
ref_idStringID referensi dari biller
inquiry_reffStringReferensi inquiry dari biller
account_nameStringNama pemilik rekening
account_numberStringNomor rekening tujuan
bank_codeStringKode bank
bank_nameStringNama bank
amountStringNominal transfer
feeStringBiaya admin
total_costStringTotal biaya
balanceStringSaldo merchant (bisa kosong — gunakan /balance/merchant untuk saldo terbaru)
statusStringStatus final: SUCCESS, FAILED, atau PENDING
rcStringKode respon
descriptionStringKeterangan RC
messageStringKeterangan detail
dataObjectInformasi tambahan
detailsStringString OtomaX-style

Contoh Response — Konfirmasi Sukses

{
  "success": true,
  "message": "Disbursement direct transfer completed",
  "rc": "000",
  "data": {
    "trx_id": "177794638960894000",
    "req_id": "PAY-20240430-001",
    "ref_id": "980173",
    "inquiry_reff": "976373",
    "account_name": "DUMMY NAME",
    "account_number": "1380610457",
    "bank_code": "014",
    "bank_name": "",
    "amount": "10000",
    "fee": "3000",
    "total_cost": "13000",
    "balance": "",
    "status": "SUCCESS",
    "rc": "000",
    "description": "Transaksi Sukses",
    "message": "STATUS DISBURSEMENT DSTF KE 1380610457: SUCCESS",
    "data": {},
    "details": "REQID:PAY-20240430-001.REFID:980173.INQREF:976373.STATUS:SUCCESS.RC:000.NAMA:DUMMY NAME.REK:1380610457.KODEBANK:014.NOMINAL:10000.FEE:3000.TOTAL:13000.KET:Transaksi Sukses"
  }
}

Contoh Response — Masih PENDING

{
  "success": true,
  "message": "Disbursement direct transfer completed",
  "rc": "021",
  "data": {
    "req_id": "PAY-20240430-003",
    "ref_id": "980174",
    "account_number": "1380610457",
    "account_name": "BUDI SANTOSO",
    "status": "PENDING",
    "rc": "021",
    "description": "Transaction Pending",
    "message": "STATUS DISBURSEMENT DSTF KE 1380610457: PENDING",
    "data": {}
  }
}

Catatan

  • Field balance pada response /status bisa kosong. Gunakan endpoint Cek Saldo untuk mendapatkan saldo merchant terbaru.
  • Jika setelah 5 kali polling status masih PENDING, eskalasi ke tim support dengan menyertakan req_id.

Kode Respon Disbursement

Tabel Kode Respon

RCKeteranganStatusSaldo Terpotong
000Transfer sukses (kode sistem)SUCCESSYa
00Transfer sukses (kode biller)SUCCESSYa
002Transaksi tidak ditemukanFAILEDTidak
005Kode produk tidak dikenal / pricing tidak tersediaFAILEDTidak
006Produk tidak aktifFAILEDTidak
007Produk dalam maintenanceFAILEDTidak
008Saldo deposit tidak cukupFAILEDTidak
009Error pada biller saat inquiryFAILEDTidak
021Transaksi pending (diproses bank tujuan)PENDINGYa (menunggu final)
022Koneksi ke biller gagal (auto-refund)FAILEDAuto-refunded
023Response biller kosong (auto-refund)FAILEDAuto-refunded
051Rekening tujuan tidak ditemukan / tidak aktifFAILEDTidak

Detail Kode Respon

RC: 000 / 00 — Transfer Sukses

Transfer berhasil. Saldo sudah terpotong dan dana telah dikirim ke rekening tujuan. serial_no berisi nomor referensi transfer dari bank.

RC: 021 — Transaksi Pending

Transfer sedang dalam proses di bank tujuan. Saldo sudah terpotong. Merchant harus mengecek status secara berkala menggunakan endpoint /status hingga mendapat status final.

RC: 022 — Biller Call Failed

Koneksi ke biller gagal (timeout atau error jaringan). Saldo yang terpotong akan otomatis di-refund oleh sistem dalam waktu singkat. Merchant dapat mengirim ulang request dengan req_id yang berbeda.

RC: 023 — Empty Biller Response

Biller mengembalikan response kosong atau tidak valid. Saldo yang terpotong akan otomatis di-refund oleh sistem.

RC: 051 — Rekening Tidak Ditemukan

Rekening tujuan tidak ditemukan, tidak aktif, atau tidak sesuai dengan bank yang dipilih. Tidak ada saldo yang terpotong. Verifikasi kembali nomor rekening dan kode bank.

RC: 008 — Saldo Tidak Cukup

Saldo deposit merchant tidak mencukupi untuk menutup amount + fee. Tidak ada saldo yang terpotong. Deposit saldo terlebih dahulu melalui CMS.

Policy Refund Otomatis

KondisiSaldoPolicy
RC 005, 006, 007, 008, 009, 051Tidak pernah terpotongTidak ada refund
RC 021 → konfirmasi SUCCESSTerpotong, final suksesTidak ada refund
RC 021 → konfirmasi FAILEDTerpotongSistem otomatis refund
RC 022, 023TerpotongSistem otomatis refund

Alur Keputusan

Response Payment
      │
      ├── rc = "000" / "00"  → ✅ Transfer sukses — catat serial_no
      │
      ├── rc = "021"         → ⏳ Pending — polling via /status
      │      │
      │      ├── status = SUCCESS → ✅ Transfer sukses
      │      └── status = FAILED  → ❌ Gagal, saldo di-refund
      │
      ├── rc = "022" / "023" → ❌ Gagal, saldo di-refund otomatis
      │                         → Boleh retry dengan req_id berbeda
      │
      ├── rc = "008"         → ❌ Saldo tidak cukup — isi deposit
      │
      └── rc = "051"         → ❌ Rekening tidak valid — cek ulang nomor

Integrasi OtomaX Modul IP

Seluruh response endpoint disbursement menyertakan field details berformat flat string yang dirancang untuk dikonsumsi langsung oleh OtomaX Modul IP regex parser tanpa mem-parse JSON.

Format

Key dan value dipisahkan oleh titik dua (:), setiap pasangan key-value dipisahkan oleh titik (.). Field yang kosong dihilangkan otomatis.

KEY1:VALUE1.KEY2:VALUE2.KEY3:VALUE3

Daftar Key

KeyKeteranganTersedia di
REQIDID transaksi dari merchant (req_id)Inquiry, Payment, Status
REFIDID referensi server (ref_id)Inquiry, Payment, Status
INQREFReferensi inquiry dari biller (inquiry_reff)Inquiry, Status
STATUSStatus: SUCCESS, FAILED, PENDINGInquiry, Payment, Status
RCKode responInquiry, Payment, Status
NAMANama pemilik rekening tujuanInquiry, Payment, Status
REKNomor rekening tujuanInquiry, Payment, Status
BANKNama bank tujuan (hanya jika tidak kosong)Inquiry, Payment, Status
KODEBANKKode bank tujuanInquiry, Payment, Status
NOMINALNominal transferInquiry, Payment, Status
FEEBiaya adminInquiry, Payment, Status
TOTALTotal biaya (nominal + fee)Inquiry, Payment, Status
SALDOSaldo merchant setelah transaksiInquiry, Payment
TERPAKAISaldo yang benar-benar terpotong (balance_used)Payment
SNSerial number / referensi transfer dari bankPayment
KETKeterangan (description atau message)Inquiry, Payment, Status

Contoh String details

Inquiry berhasil

REQID:PAY-20240430-001.REFID:1777946295495371000.INQREF:1017797.STATUS:SUCCESS.RC:00.NAMA:DUMMY NAME.REK:1380610457.KODEBANK:014.NOMINAL:100000.FEE:3000.TOTAL:103000.SALDO:281994.KET:Transaksi Sukses

Payment berhasil

REQID:PAY-20240430-001.REFID:980173.STATUS:SUCCESS.RC:00.NAMA:DUMMY NAME.REK:1380610457.KODEBANK:014.NOMINAL:10000.FEE:3000.TOTAL:13000.SALDO:268994.TERPAKAI:13000.SN:980173.KET:Transaksi Sukses

Status check berhasil

REQID:PAY-20240430-001.REFID:980173.INQREF:976373.STATUS:SUCCESS.RC:000.NAMA:DUMMY NAME.REK:1380610457.KODEBANK:014.NOMINAL:10000.FEE:3000.TOTAL:13000.KET:Transaksi Sukses

Payment pending

REQID:PAY-20240430-002.REFID:980174.STATUS:PENDING.RC:021.NAMA:DUMMY NAME.REK:1380610457.KODEBANK:014.NOMINAL:100000.FEE:3000.TOTAL:103000.SALDO:168994.TERPAKAI:103000.KET:Transaction Pending

Payment gagal (saldo tidak cukup)

REQID:PAY-20240430-003.REFID:980175.STATUS:FAILED.RC:008.REK:1380610457.KODEBANK:014.NOMINAL:100000.FEE:3000.TOTAL:103000.SALDO:50000.KET:Insufficient Balance

Regex Parser untuk OtomaX Modul IP

Cek sukses dengan SN

STATUS:SUCCESS.*SN:(?P<sn>[^.]+).*SALDO:(?P<saldo>[0-9]+)

Cek sukses atau pending (tanpa SN)

STATUS:(?P<status>SUCCESS|PENDING).*RC:(?P<rc>[0-9]+).*SALDO:(?P<saldo>[0-9]+)

Ekstrak semua field utama

REQID:(?P<reqid>[^.]+).*REFID:(?P<refid>[^.]+).*STATUS:(?P<status>[^.]+).*RC:(?P<rc>[^.]+).*SALDO:(?P<saldo>[0-9]+)

Konfigurasi OtomaX Modul IP

Konfigurasi parsing untuk payment sukses di OtomaX:

Regex SN     : SN:(?P<sn>[^.]+)
Regex Saldo  : SALDO:(?P<saldo>[0-9]+)
Regex Status : STATUS:(?P<status>SUCCESS|PENDING|FAILED)
Regex RC     : RC:(?P<rc>[0-9]+)

Catatan: Nilai STATUS dalam field details menggunakan bahasa Inggris (SUCCESS, FAILED, PENDING). Sesuaikan regex Modul IP Anda jika sebelumnya menggunakan token berbeda seperti SUKSES atau GAGAL.

Gambaran Umum PayIn

PayIn API memungkinkan merchant untuk menerima pembayaran dari pelanggan melalui QRIS (QR Code Indonesian Standard). Pelanggan membayar menggunakan aplikasi dompet digital atau m-banking yang mendukung QRIS.

Endpoint

EndpointMetodeKeterangan
POST /api/v1/merchant/payin/qris/createPOSTBuat QRIS untuk satu transaksi
POST /api/v1/merchant/payin/qris/checkPOSTCek status pembayaran QRIS
POST /api/v1/merchant/payin/qris/refundPOSTRefund pembayaran QRIS yang berhasil

Biller

PayIn QRIS menggunakan biller GPay (GPAY) — payment gateway yang menyediakan QRIS merchant.

Alur Transaksi PayIn QRIS

Merchant                         Sistem                    Pelanggan
  │                                 │                          │
  │  POST /payin/qris/create        │                          │
  │────────────────────────────────>│                          │
  │  200 OK { qr_code, trace_number}│                          │
  │<────────────────────────────────│                          │
  │                                 │                          │
  │  Tampilkan QR kepada pelanggan ─────────────────────────>  │
  │                                 │    Pelanggan scan & bayar │
  │                                 │<─────────────────────────│
  │                                 │                          │
  │  Webhook dari GPay (callback)   │                          │
  │<────────────────────────────────│                          │
  │                                 │                          │
  │  ATAU polling /qris/check       │                          │
  │────────────────────────────────>│                          │
  │  200 OK { status }              │                          │
  │<────────────────────────────────│                          │

Konsep Penting

trace_number

Setiap QRIS yang dibuat memiliki trace_number unik yang diterbitkan oleh GPay. Gunakan nilai ini untuk:

  • Cek status pembayaran via /qris/check
  • Inisiasi refund via /qris/refund

Webhook Callback

GPay mengirimkan notifikasi webhook ke sistem Relay ketika pembayaran QRIS dikonfirmasi. Sistem Relay kemudian memperbarui status transaksi secara otomatis. Anda dapat:

  1. Polling status via /qris/check, atau
  2. Menyediakan callback URL di konfigurasi merchant untuk menerima notifikasi push.

Status Transaksi

StatusKeterangan
SUCCESSPembayaran dikonfirmasi oleh bank/dompet pelanggan
PENDINGMenunggu pembayaran dari pelanggan (QRIS belum dibayar)
FAILEDQRIS kedaluwarsa atau pembayaran gagal

Autentikasi

Semua endpoint PayIn menggunakan API Key via header X-API-Key dan opsional Signature Validation. Lihat Autentikasi.

QRIS — Buat Transaksi

Membuat QRIS pembayaran untuk satu transaksi. Pelanggan memindai QR code yang dihasilkan menggunakan aplikasi m-banking atau dompet digital.

URL: POST /api/v1/merchant/payin/qris/create

Request

Headers

HeaderWajibKeterangan
X-API-KeyYaAPI Key merchant
Content-TypeYaapplication/json
X-SignatureKondisionalJika Signature Validation aktif
X-TimestampKondisionalUnix timestamp (jika Signature Validation aktif)

Body

FieldTipeWajibKeterangan
req_idStringYaID unik transaksi dari merchant (idempotency key)
product_idStringYaKode produk QRIS (contoh: "GPAY")
amountStringYaNominal pembayaran dalam IDR (digits only)

Contoh Request

{
  "req_id": "QRIS-20240501-001",
  "product_id": "GPAY",
  "amount": "150000"
}
curl -X POST "https://api-sandbox.alfakios.com/api/v1/merchant/payin/qris/create" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: apk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -d '{
    "req_id": "QRIS-20240501-001",
    "product_id": "GPAY",
    "amount": "150000"
  }'

Response

Spesifikasi

FieldTipeKeterangan
req_idStringreq_id yang dikirim merchant
ref_idStringID referensi internal sistem
product_idStringKode produk QRIS
trace_numberStringNomor unik dari GPay — simpan untuk cek status dan refund
statusStringSUCCESS (QRIS berhasil dibuat), PENDING, atau FAILED
rcStringKode respon
descriptionStringKeterangan RC
messageStringPesan detail
amountStringNominal pembayaran
dataObjectData tambahan dari GPay, termasuk QR string/URL

Contoh Response Berhasil

{
  "success": true,
  "message": "QRIS created successfully",
  "rc": "000",
  "data": {
    "req_id": "QRIS-20240501-001",
    "ref_id": "1777946295495371000",
    "product_id": "GPAY",
    "trace_number": "240501001234",
    "status": "SUCCESS",
    "rc": "000",
    "description": "QRIS Created",
    "message": "QRIS berhasil dibuat",
    "amount": "150000",
    "data": {
      "qr_string": "00020101021226670016ID.CO.BCA.WWW01189360050300000068790215240501001234...",
      "qr_url": "https://api.gpay.co.id/qris/240501001234.png",
      "expired_at": "2024-05-01T10:15:00Z"
    }
  }
}

Contoh Response Gagal

{
  "success": true,
  "message": "QRIS creation failed",
  "rc": "009",
  "data": {
    "req_id": "QRIS-20240501-002",
    "status": "FAILED",
    "rc": "009",
    "description": "Biller Error",
    "message": "GPay tidak tersedia saat ini",
    "data": {}
  }
}

Langkah Selanjutnya

Setelah mendapatkan QRIS:

  1. Tampilkan QR code kepada pelanggan menggunakan data.qr_string (render menjadi QR image) atau data.qr_url (langsung tampilkan gambar).
  2. Simpan trace_number — digunakan untuk cek status dan refund.
  3. Polling status via Cek Status QRIS atau tunggu webhook callback.

Catatan

  • QR code memiliki masa berlaku (lihat data.expired_at). Jika kedaluwarsa tanpa pembayaran, buat QRIS baru dengan req_id baru.
  • Jika koneksi terputus saat membuat QRIS, coba lagi dengan req_id yang sama — sistem akan mengembalikan QRIS yang sudah dibuat tanpa membuat duplikat.

QRIS — Cek Status

Mengecek status pembayaran QRIS menggunakan trace_number yang didapat dari response Buat QRIS.

URL: POST /api/v1/merchant/payin/qris/check

Request

Headers

HeaderWajibKeterangan
X-API-KeyYaAPI Key merchant
Content-TypeYaapplication/json

Body

FieldTipeWajibKeterangan
trace_numberStringYatrace_number dari response QRIS create

Contoh Request

{
  "trace_number": "240501001234"
}
curl -X POST "https://api-sandbox.alfakios.com/api/v1/merchant/payin/qris/check" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: apk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -d '{"trace_number": "240501001234"}'

Response

Spesifikasi

FieldTipeKeterangan
req_idStringreq_id dari transaksi asal
ref_idStringID referensi internal
product_idStringKode produk QRIS
trace_numberStringTrace number yang dicek
statusStringSUCCESS, PENDING, atau FAILED
rcStringKode respon
descriptionStringKeterangan RC
messageStringPesan detail
amountStringNominal pembayaran
dataObjectData tambahan dari GPay (payer info, dll)

Contoh Response — Dibayar (SUCCESS)

{
  "success": true,
  "message": "Pembayaran QRIS diterima",
  "rc": "000",
  "data": {
    "req_id": "QRIS-20240501-001",
    "ref_id": "1777946295495371000",
    "product_id": "GPAY",
    "trace_number": "240501001234",
    "status": "SUCCESS",
    "rc": "000",
    "description": "Payment Received",
    "message": "Pembayaran QRIS berhasil dikonfirmasi",
    "amount": "150000",
    "data": {
      "payer_name": "ANDI WIJAYA",
      "payer_number": "08123456789",
      "issuer": "BCA Mobile",
      "paid_at": "2024-05-01T10:08:32Z",
      "rrn": "240501123456"
    }
  }
}

Contoh Response — Belum Dibayar (PENDING)

{
  "success": true,
  "message": "Menunggu pembayaran",
  "rc": "021",
  "data": {
    "req_id": "QRIS-20240501-001",
    "ref_id": "1777946295495371000",
    "product_id": "GPAY",
    "trace_number": "240501001234",
    "status": "PENDING",
    "rc": "021",
    "description": "Waiting Payment",
    "message": "QRIS belum dibayar",
    "amount": "150000",
    "data": {}
  }
}

Contoh Response — Tidak Ditemukan

Jika trace_number tidak valid atau tidak terdaftar di merchant ini:

HTTP Status: 404 Not Found

{
  "success": false,
  "message": "Transaction not found"
}

Rekomendasi Polling

Gunakan interval polling yang meningkat agar tidak membebani server:

Detik 0–30  : polling setiap 3 detik
Detik 30–60 : polling setiap 5 detik
Detik 60+   : polling setiap 10 detik
> 5 menit   : tampilkan "QR kedaluwarsa" ke pelanggan

Catatan

  • Endpoint ini hanya bisa mengecek QRIS yang dibuat oleh merchant yang sama (API Key yang sama).
  • Setelah pembayaran dikonfirmasi (status: "SUCCESS"), trace_number dapat digunakan untuk refund via QRIS Refund jika diperlukan.

QRIS — Refund

Mengajukan refund untuk pembayaran QRIS yang sudah berhasil. Dana dikembalikan ke akun pelanggan yang membayar.

URL: POST /api/v1/merchant/payin/qris/refund

Refund hanya dapat dilakukan untuk transaksi dengan status: "SUCCESS". Pastikan Anda sudah mengonfirmasi status pembayaran via Cek Status QRIS sebelum mengajukan refund.

Request

Headers

HeaderWajibKeterangan
X-API-KeyYaAPI Key merchant
Content-TypeYaapplication/json
X-SignatureKondisionalJika Signature Validation aktif
X-TimestampKondisionalUnix timestamp (jika Signature Validation aktif)

Body

FieldTipeWajibKeterangan
trace_numberStringYatrace_number dari transaksi QRIS yang akan di-refund

Contoh Request

{
  "trace_number": "240501001234"
}
curl -X POST "https://api-sandbox.alfakios.com/api/v1/merchant/payin/qris/refund" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: apk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -d '{"trace_number": "240501001234"}'

Response

Spesifikasi

FieldTipeKeterangan
req_idStringreq_id dari transaksi asal
ref_idStringID referensi refund
product_idStringKode produk QRIS
trace_numberStringTrace number yang di-refund
statusStringSUCCESS, PENDING, atau FAILED
rcStringKode respon
descriptionStringKeterangan RC
messageStringPesan detail
amountStringNominal yang di-refund
dataObjectData tambahan dari GPay

Contoh Response Berhasil

{
  "success": true,
  "message": "Refund berhasil",
  "rc": "000",
  "data": {
    "req_id": "QRIS-20240501-001",
    "ref_id": "1777946295495380000",
    "product_id": "GPAY",
    "trace_number": "240501001234",
    "status": "SUCCESS",
    "rc": "000",
    "description": "Refund Processed",
    "message": "Refund QRIS 150000 berhasil diproses",
    "amount": "150000",
    "data": {
      "refund_id": "RF-240501001234",
      "refunded_at": "2024-05-01T10:30:00Z"
    }
  }
}

Contoh Response Gagal — Transaksi Tidak Ditemukan

HTTP Status: 404 Not Found

{
  "success": false,
  "message": "Transaction not found"
}

Contoh Response Gagal — Transaksi Belum Sukses

{
  "success": true,
  "message": "Refund failed",
  "rc": "009",
  "data": {
    "trace_number": "240501001234",
    "status": "FAILED",
    "rc": "009",
    "description": "Transaction not eligible for refund",
    "message": "Hanya transaksi dengan status SUCCESS yang dapat di-refund",
    "data": {}
  }
}

Catatan

  • Refund hanya dapat dilakukan untuk transaksi yang sudah SUCCESS.
  • Kebijakan refund (batas waktu, ketersediaan) ditentukan oleh GPay — hubungi tim support jika refund ditolak.
  • Setelah refund sukses, dana dikembalikan ke akun pelanggan dalam 1–3 hari kerja (tergantung bank/dompet pelanggan).
  • Setiap trace_number hanya dapat di-refund satu kali.

Cek Saldo Merchant

Mengecek saldo deposit merchant saat ini.

URL: POST /api/v1/balance/merchant

Autentikasi

Endpoint ini menggunakan API Key via header X-API-Key. Signature Validation tidak diterapkan pada endpoint ini.

Request

Headers

HeaderWajibKeterangan
X-API-KeyYaAPI Key merchant
Content-TypeYaapplication/json

Body

Request body bersifat opsional. Request kosong ({}) atau tanpa body juga diterima.

FieldTipeWajibKeterangan
req_idStringTidakID request untuk tracing
ref_idStringTidakID referensi untuk tracing
product_idStringTidakKode produk untuk konteks
cust_idStringTidakID customer untuk konteks

Contoh Request (minimal)

curl -s -X POST "https://api-sandbox.alfakios.com/api/v1/balance/merchant" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: apk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -d '{}'

Contoh Request (dengan tracing)

curl -s -X POST "https://api-sandbox.alfakios.com/api/v1/balance/merchant" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: apk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -d '{
    "req_id": "BAL-CHECK-001"
  }'

Response

Spesifikasi

FieldTipeKeterangan
req_idStringreq_id yang dikirim (kosong jika tidak dikirim)
ref_idStringref_id yang dikirim (kosong jika tidak dikirim)
product_idStringproduct_id yang dikirim
cust_idStringcust_id yang dikirim
statusStringSUCCESS atau FAILED
rcStringKode respon
descriptionStringKeterangan RC
messageStringPesan detail
unit_priceStringKosong untuk balance check
balance_usedStringKosong untuk balance check
balanceStringSaldo deposit merchant saat ini (dalam IDR)
serial_noStringBernilai "balance" untuk endpoint ini
dataAnyData tambahan (biasanya null)

Contoh Response

{
  "success": true,
  "message": "Balance retrieved successfully",
  "rc": "000",
  "data": {
    "req_id": "",
    "ref_id": "",
    "product_id": "",
    "cust_id": "",
    "status": "SUCCESS",
    "rc": "000",
    "description": "",
    "message": "balance retrieved",
    "unit_price": "",
    "balance_used": "",
    "balance": "4493500",
    "serial_no": "balance",
    "data": null
  }
}

Nilai balance adalah Rp 4.493.500 dalam contoh di atas.

Kapan Menggunakannya

  • Sebelum melakukan transaksi besar, untuk memastikan saldo mencukupi.
  • Setelah menerima response disbursement dengan rc: "021" (PENDING) — field balance pada response /disbursement/status bisa kosong, gunakan endpoint ini untuk saldo terbaru.
  • Untuk menampilkan saldo di dashboard aplikasi merchant.

Katalog Produk

Mengembalikan daftar produk yang tersedia untuk merchant berdasarkan API Key yang digunakan.

URL: GET /api/v1/product/inquiry

Autentikasi

Endpoint ini menggunakan API Key via header X-API-Key. Tidak memerlukan Signature Validation.

Request

Tidak ada parameter atau body — cukup kirim GET request dengan API Key.

curl --location 'https://api-sandbox.alfakios.com/api/v1/product/inquiry' \
  --header 'X-API-Key: apk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'

Response

Field data berisi array flat produk yang tersedia.

Spesifikasi per Produk

FieldTipeKeterangan
operatorStringNama operator/provider produk
product_idStringKode produk — gunakan nilai ini di field product_id pada request transaksi
denomStringDenominasi/nilai produk ("0" = harga dinamis berdasarkan nominal)
descriptionStringDeskripsi produk
unit_priceStringHarga jual dalam IDR ("0" = harga dinamis)
statusStringOPEN, CLOSED, atau MAINTENANCE

Contoh Response

{
  "success": true,
  "message": "Products retrieved successfully",
  "rc": "000",
  "data": [
    {
      "operator": "Bank Transfer",
      "product_id": "DSTF",
      "denom": "0",
      "description": "Disbursement Transfer",
      "unit_price": "0",
      "status": "OPEN"
    },
    {
      "operator": "Token PLN",
      "product_id": "PLNPREP",
      "denom": "0",
      "description": "PLN Token Prabayar",
      "unit_price": "0",
      "status": "OPEN"
    },
    {
      "operator": "PLN Pascabayar",
      "product_id": "PLNPOST",
      "denom": "0",
      "description": "Tagihan PLN Pascabayar",
      "unit_price": "0",
      "status": "OPEN"
    },
    {
      "operator": "BPJS Kesehatan",
      "product_id": "BPJSKS",
      "denom": "0",
      "description": "BPJS Kesehatan",
      "unit_price": "0",
      "status": "OPEN"
    },
    {
      "operator": "GoPay",
      "product_id": "GOPAY",
      "denom": "0",
      "description": "Top-up GoPay",
      "unit_price": "0",
      "status": "OPEN"
    },
    {
      "operator": "XL Axiata",
      "product_id": "XL5",
      "denom": "5000",
      "description": "Pulsa XL Rp 5.000",
      "unit_price": "5500",
      "status": "OPEN"
    },
    {
      "operator": "Telkomsel",
      "product_id": "TSEL10",
      "denom": "10000",
      "description": "Pulsa Telkomsel Rp 10.000",
      "unit_price": "10800",
      "status": "MAINTENANCE"
    }
  ]
}

Catatan Penggunaan

  • Gunakan product_id dari response ini sebagai nilai field product_id pada request transaksi apa pun.
  • Produk dengan status: "MAINTENANCE" atau status: "CLOSED" tidak dapat digunakan untuk transaksi dan akan mengembalikan error rc: "007" atau rc: "006".
  • unit_price: "0" artinya harga dihitung dinamis berdasarkan amount yang dikirim — biaya final tampil di field fee dan total_cost pada response inquiry/payment.
  • Daftar produk bisa berbeda antar merchant tergantung konfigurasi akun.

Integrasi dengan Endpoint Lain

KategoriFieldSumber dari Katalog
PPOBproduct_idproduct_id dengan operator sesuai layanan
Disbursementproduct_idproduct_id: "DSTF" atau produk transfer lainnya
PayIn QRISproduct_idproduct_id: "GPAY"

Contoh Integrasi Lengkap

Contoh alur integrasi end-to-end untuk berbagai use case.


1. Beli Pulsa (PHP)

<?php
class RelayAPI {
    private $baseUrl;
    private $apiKey;

    public function __construct(string $baseUrl, string $apiKey) {
        $this->baseUrl = rtrim($baseUrl, '/');
        $this->apiKey  = $apiKey;
    }

    public function post(string $path, array $body): array {
        $ch = curl_init("{$this->baseUrl}{$path}");
        curl_setopt_array($ch, [
            CURLOPT_POST           => true,
            CURLOPT_POSTFIELDS     => json_encode($body),
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HTTPHEADER     => [
                'Content-Type: application/json',
                "X-API-Key: {$this->apiKey}",
            ],
        ]);
        $response = curl_exec($ch);
        curl_close($ch);
        return json_decode($response, true);
    }
}

$api    = new RelayAPI('https://api-sandbox.alfakios.com', 'apk_live_xxx');
$result = $api->post('/api/v1/merchant/ppob/phone-credit/transaction', [
    'req_id'     => 'TXN-' . date('YmdHis') . '-' . rand(1000, 9999),
    'product_id' => 'XL5',
    'cust_id'    => '08123456789',
]);

if ($result['data']['status'] === 'SUCCESS') {
    echo "Sukses! SN: " . $result['data']['serial_no'];
} elseif ($result['data']['status'] === 'PENDING') {
    echo "Pending. Cek status dengan req_id: " . $result['data']['req_id'];
} else {
    echo "Gagal: " . $result['data']['description'];
}

2. Disbursement Lengkap (Node.js)

const axios = require('axios');

const API = axios.create({
  baseURL: 'https://api-sandbox.alfakios.com',
  headers: {
    'Content-Type': 'application/json',
    'X-API-Key': 'apk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
  },
  timeout: 15000,
});

async function transferDana(reqId, accountNumber, amount, bankCode = '014') {
  // Step 1: Inquiry
  const inquiry = await API.post('/api/v1/merchant/disbursement/inquiry', {
    product_id: 'DSTF', account_number: accountNumber,
    amount, bank_code: bankCode,
  });
  if (inquiry.data.data.status !== 'SUCCESS') {
    throw new Error(`Inquiry gagal: ${inquiry.data.data.description}`);
  }
  const { account_name, fee, total_cost } = inquiry.data.data;
  console.log(`Nama: ${account_name} | Fee: Rp ${fee} | Total: Rp ${total_cost}`);

  // Step 2: Payment
  const payment = await API.post('/api/v1/merchant/disbursement/payment', {
    req_id: reqId, product_id: 'DSTF',
    account_number: accountNumber, amount, bank_code: bankCode,
  });
  const d = payment.data.data;

  if (d.status === 'SUCCESS') {
    console.log(`✅ Sukses! Ref: ${d.serial_no}, Saldo: Rp ${d.balance}`);
    return d;
  }
  if (d.status === 'PENDING') {
    return await pollDisbursementStatus(reqId, accountNumber, amount, bankCode);
  }
  throw new Error(`Gagal: ${d.description} (RC: ${d.rc})`);
}

async function pollDisbursementStatus(reqId, accountNumber, amount, bankCode) {
  const delays = [30000, 60000, 120000, 300000];
  for (const delay of delays) {
    await new Promise(r => setTimeout(r, delay));
    const res = await API.post('/api/v1/merchant/disbursement/status', {
      product_id: 'DSTF', account_number: accountNumber,
      amount, bank_code: bankCode, req_id: reqId,
    });
    const { status, description } = res.data.data;
    if (status === 'SUCCESS') { console.log('✅ Sukses'); return res.data.data; }
    if (status === 'FAILED')  { throw new Error(`Gagal: ${description}`); }
    console.log('Masih PENDING...');
  }
  throw new Error('Timeout — eskalasi ke support dengan req_id: ' + reqId);
}

transferDana('PAY-' + Date.now(), '1380610457', '100000').catch(console.error);

3. Token PLN Prabayar (Python)

import requests, time, uuid

BASE_URL = "https://api-sandbox.alfakios.com"
HEADERS  = {"Content-Type": "application/json",
            "X-API-Key": "apk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}

def beli_token_pln(nomor_meter: str) -> dict:
    req_id = f"PLN-{int(time.time())}-{uuid.uuid4().hex[:6]}"

    # Inquiry
    r = requests.post(f"{BASE_URL}/api/v1/merchant/ppob/pln/prepaid/inquiry",
                      json={"product_id": "PLNPREP", "cust_id": nomor_meter},
                      headers=HEADERS, timeout=15)
    r.raise_for_status()
    inq = r.json()["data"]
    if inq["status"] != "SUCCESS":
        raise ValueError(f"Inquiry gagal: {inq['description']}")
    print(f"Pelanggan: {inq['data'].get('nama_pelanggan', '-')}, Harga: Rp {inq['unit_price']}")

    # Transaksi
    r = requests.post(f"{BASE_URL}/api/v1/merchant/ppob/pln/prepaid/transaction",
                      json={"req_id": req_id, "product_id": "PLNPREP", "cust_id": nomor_meter},
                      headers=HEADERS, timeout=15)
    r.raise_for_status()
    result = r.json()["data"]

    if result["status"] == "SUCCESS":
        print(f"✅ Token: {result['serial_no']}")
    elif result["status"] == "PENDING":
        print(f"⏳ Pending — req_id: {req_id}")
    else:
        print(f"❌ Gagal: {result['description']}")
    return result

beli_token_pln("12345678901")

4. QRIS PayIn (Go)

package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "net/http"
    "time"
)

const (
    baseURL = "https://api-sandbox.alfakios.com"
    apiKey  = "apk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
)

func callAPI(path string, body interface{}) map[string]interface{} {
    b, _  := json.Marshal(body)
    req, _ := http.NewRequest("POST", baseURL+path, bytes.NewReader(b))
    req.Header.Set("Content-Type", "application/json")
    req.Header.Set("X-API-Key", apiKey)
    resp, _ := http.DefaultClient.Do(req)
    defer resp.Body.Close()
    raw, _  := io.ReadAll(resp.Body)
    var result map[string]interface{}
    json.Unmarshal(raw, &result)
    return result
}

func main() {
    reqID := fmt.Sprintf("QRIS-%d", time.Now().Unix())

    // Buat QRIS
    res := callAPI("/api/v1/merchant/payin/qris/create", map[string]string{
        "req_id": reqID, "product_id": "GPAY", "amount": "75000",
    })
    data := res["data"].(map[string]interface{})
    traceNumber := data["trace_number"].(string)
    fmt.Printf("QRIS dibuat. Trace: %s\n", traceNumber)

    // Polling sampai dibayar
    for i := 0; i < 12; i++ {
        time.Sleep(5 * time.Second)
        status := callAPI("/api/v1/merchant/payin/qris/check",
            map[string]string{"trace_number": traceNumber})
        d := status["data"].(map[string]interface{})
        s := d["status"].(string)
        fmt.Printf("Status [%d]: %s\n", i+1, s)
        if s == "SUCCESS" { fmt.Println("✅ Pembayaran diterima!"); return }
        if s == "FAILED"  { fmt.Println("❌ QRIS gagal/kedaluwarsa"); return }
    }
    fmt.Println("Timeout polling — buat QRIS baru")
}

5. Cek Saldo (cURL)

curl -s -X POST "https://api-sandbox.alfakios.com/api/v1/balance/merchant" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: apk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -d '{}' | python3 -c "import sys,json; d=json.load(sys.stdin); print('Saldo: Rp', d['data']['balance'])"

6. BPJS Lengkap (PHP)

<?php
$api    = new RelayAPI('https://api-sandbox.alfakios.com', 'apk_live_xxx');
$inqId  = 'INQ-' . time();

// Step 1: Inquiry
$inquiry = $api->post('/api/v1/merchant/ppob/bpjs/inquiry', [
    'product_id' => 'BPJSKS',
    'cust_id'    => '0001234567890',
    'period'     => '01',
    'mobile_no'  => '08123456789',
]);

if ($inquiry['data']['status'] !== 'SUCCESS') {
    die("Inquiry gagal: " . $inquiry['data']['description']);
}

echo "Nama: " . $inquiry['data']['data']['nama_peserta'] . "\n";
echo "Tagihan: Rp " . $inquiry['data']['data']['tagihan'] . "\n";

// Step 2: Payment
$payment = $api->post('/api/v1/merchant/ppob/bpjs/transaction', [
    'req_id'         => 'PAY-' . time(),
    'product_id'     => 'BPJSKS',
    'cust_id'        => '0001234567890',
    'period'         => '01',
    'inquiry_req_id' => $inqId,
    'mobile_no'      => '08123456789',
]);

echo "Status: " . $payment['data']['status'] . "\n";
echo "Struk: "  . $payment['data']['serial_no'] . "\n";

Catatan Perubahan

Disbursement API

VersiTanggalKeteranganPIC
1.030 April 2026Dokumen perdana: inquiry, payment, statusBackend API Team
1.130 April 2026Ganti autentikasi dari OAuth2 ke API Key (X-API-Key)Backend API Team
1.205 Mei 2026Tambah req_id wajib di payment (idempotency); field details OtomaX; dokumentasi cek saldo dan katalog produkBackend API Team

PPOB API

VersiTanggalKeteranganPIC
1.001 Mei 2026Rilis perdana: pulsa, e-wallet, PLN prabayar/pascabayar, BPJSBackend API Team
1.105 Mei 2026Tambah endpoint cek status (/ppob/check); timeout 8 detikBackend API Team

PayIn QRIS API

VersiTanggalKeteranganPIC
1.007 Mei 2026Rilis perdana: create, check, refund via GPayBackend API Team

Dokumentasi mdBook

TanggalKeterangan
08 Mei 2026Dokumentasi komprehensif merchant API dikonversi ke format mdBook