Payment Gateway Virtual Account, Episode 0
Endy Muhardin, ArtiVisi Intermedia
Seorang penonton, programmer di daerah, bertanya di kolom komentar:
"Saya jarang dapat project serumit payment gateway. Boleh dijelaskan fitur standar yang harus ada?"
Payment gateway VA menghubungkan aplikasi penagihan dengan bank. Tugasnya:
Tanpa rekening perantara. Uang pembayar masuk dari bank langsung ke rekening institusi, sedangkan gateway hanya mengelola datanya: tagihan, nomor VA, notifikasi pembayaran, dan hasil rekonsiliasi.
Dana mengalir dari bank langsung ke rekening institusi, sedangkan gateway hanya mengelola data. Account Receivable bersifat opsional karena aplikasi hulu yang sudah mengelola tagihan sendiri dapat langsung menjadi consumer.
| Aspek | Gateway self-hosted | Provider, model agregatorbawaan di semua provider | Provider, model fasilitatorMidtrans: Fasilitator · DOKU: Direct · Xendit: Switcher · Faspay: Direct |
|---|---|---|---|
| Hubungan kontrak | Institusi berkontrak langsung dengan tiap bank. Penyedia gateway hanya menjadi penghubung dan konsultan. | Institusi berkontrak dengan provider saja. VA menggunakan company code milik provider. | Institusi berkontrak langsung dengan bank dan mendapat company code sendiri. Provider menjadi penghubung (BCA menyebutnya IT Gateway) dan membantu pengurusan berkas. |
| Jalur integrasi API | Consumer → gateway di server institusi → API tiap bank | Consumer → API provider → API bank | Consumer → API provider → API bank |
| Aliran dana | Bank → rekening institusi | Bank → rekening provider → dicairkan ke institusi, T+0 hingga 3 hari kerja | Bank → rekening institusi, sesuai perjanjian dengan bank |
| Biaya per transaksi | Biaya bank saja (contoh BSI: Rp 2.000) | Tarif flat yang sudah mencakup biaya bank (Midtrans, DOKU: Rp 4.000 + PPN) | Biaya bank ditambah fee provider (Midtrans, Xendit: Rp 2.000 + PPN) |
| Server dan data | Milik institusi | Di provider | Di provider |
| Source code | Open source (Apache 2.0) | Proprietary | Proprietary |
Sumber: halaman harga dan dokumentasi Midtrans, DOKU, Xendit, Faspay, serta halaman Virtual Account BCA, diakses 19 September 2026. Fee fasilitator Midtrans berasal dari S&K yang lebih lama; tarif Xendit per Desember 2024 dan direvisi pada 2026. BCA mewajibkan rekening BCA atas nama badan usaha, termasuk pada model agregator.
| Istilah | Arti di sini |
|---|---|
| Virtual Account (VA) | Nomor rekening per pembayar yang mengarahkan transfer ke rekening institusi sekaligus menandai tagihan mana yang dibayar. Tidak ada uang yang mengendap di dalamnya. |
| Inquiry | Panggilan sinkron bank saat pembayar mengetik nomor VA. Jawabannya nama dan jumlah tagihan, atau NOT_FOUND, dalam 2–3 detik. |
| Payment callback | Panggilan kedua dari bank setelah pembayar mengonfirmasi: gateway mencatat pembayaran, menjawab ack, lalu mengirim webhook ke consumer. |
| Settlement | Bank mengkredit rekening institusi atas pembayaran VA hari itu, minus biaya per transaksi. |
| Rekonsiliasi | Pencocokan akhir hari berkas settlement bank dengan catatan pembayaran gateway untuk memulihkan notifikasi hilang dan menandai selisih, duplikat, dan kredit tak dikenal. |
src/main/resources/db/migration/ di repo. Setiap migrasi mencatat keputusan produk.
| Bank | Protokol | Bentuk endpoint |
|---|---|---|
| Maybank | SNAP v1.0.2 (BI/ASPI) via REST/JSON | 3 endpoint: access-token, inquiry, payment |
| BSI | REST/JSON proprietary | 1 endpoint, field action: inquiry, payment, reversal |
| CIMB | SOAP/XML proprietary | Servlet Spring-WS di path terpisah |
Protokol dan cara autentikasi ketiga bank berbeda-beda. Adapter ditentukan oleh escrow account, satu per relasi bank.
SNAP menetapkan token bertanda tangan RSA dan tanda tangan HMAC-SHA512 untuk setiap transaksi. Kode: src/main/java/com/artivisi/paymentgateway/adapter/{maybank, bsi, cimb, snap}
POST /api/charges, lalu cancel, extend, repriceX-Client-Id dan X-Client-SecretPermintaan yang sama boleh datang lebih dari sekali, tetapi hanya dicatat sekali.
Mencocokkan catatan pembayaran di gateway dengan uang yang benar-benar masuk ke rekening, menurut laporan settlement dari bank.
uq_payment_va_reference)uq_charge_consumer_reference)snap_external_id)GATEWAY_SECRET_KEY.| Yang berbeda | Contoh | Dampaknya pada kode |
|---|---|---|
| Protokol | SNAP (REST/JSON), REST/JSON proprietary, SOAP/XML | Satu adapter per bank, masing-masing dengan endpoint dan parser sendiri |
| Autentikasi dan tanda tangan request | Token bertanda tangan RSA, checksum, atau tanpa tanda tangan di dalam request | Verifikasi dilakukan di adapter. Jika bank tidak menandatangani request, gateway menambah pengamanan di jaringan, misalnya allowlist IP. |
| Nama field, kode respons, format tanggal | Istilah, kode jawaban, dan format waktu yang berbeda-beda | Adapter menerjemahkan semuanya ke model internal yang sama untuk inquiry dan pembayaran |
| Format nomor VA dan biaya | Prefix, jumlah digit, dan biaya per transaksi | Disimpan sebagai konfigurasi per escrow account, bukan sebagai konstanta di kode |
| Berkas settlement | Susunan kolom CSV dari portal bank | Dipetakan ke format baku sebelum rekonsiliasi |
| Tempat nomor VA disimpan | Di database gateway, atau di database bank | Gateway harus mendukung kedua model |
Karena perbedaan antarbank ditangani oleh adapter dan konfigurasi per escrow account, logika tagihan, pembayaran, dan rekonsiliasi di inti aplikasi tidak bergantung pada bank tertentu.
Bank menyusun laporan settlement per tanggal menurut WIB, sedangkan server umumnya menyimpan waktu dalam UTC, yang tertinggal tujuh jam dari WIB.
Jika batas tanggal rekonsiliasi dihitung dalam UTC, pembayaran pukul 04.38 WIB tanggal 25 jatuh ke tanggal 24. Akibatnya, rekonsiliasi tanggal 25 menemukan uang masuk yang tidak tercatat di gateway, sedangkan rekonsiliasi tanggal 24 menemukan pembayaran yang uangnya tidak ada di laporan bank.
Satu pembayaran yang sah menghasilkan dua selisih palsu. Karena itu, batas tanggal rekonsiliasi harus dihitung dalam zona waktu bank.
| Disimpan di gateway | Disimpan di bank | |
|---|---|---|
| Cara kerja | Bank meneruskan setiap inquiry ke gateway, lalu mengirim notifikasi pembayaran. | Gateway mendaftarkan setiap nomor VA ke bank saat tagihan dibuat. Bank memeriksa pembayaran dengan datanya sendiri dan hanya mengirim notifikasi pembayaran. |
| Contoh bank | Maybank, BSI, CIMB | BNI |
| Membatalkan atau mengubah nominal tagihan | Cukup mengubah data di gateway, tanpa memanggil bank | Perubahan harus dikirim ke bank, dan bisa terjadi bersamaan dengan pembayaran yang sedang diproses bank (race condition). Selisihnya diselesaikan lewat rekonsiliasi. |
| Batas waktu pembayaran | Diperiksa gateway saat inquiry | Dikirim ke bank, lalu bank yang memeriksanya |
| Status implementasi | Tiga adapter sudah dibuat | Sudah disiapkan di model data. Adapternya dibuat saat bank seperti BNI diintegrasikan. |
Karena setiap bank menentukan modelnya sendiri, gateway harus mendukung kedua model.
| Kriteria | Monolit + PostgreSQL | Event-sourced, Kafka + RocksDB |
|---|---|---|
| Menjawab inquiry dalam 2–3 detik | Dijawab langsung di dalam proses aplikasi | Harus menunggu event tersimpan di Kafka lebih dahulu |
| Dua pembayaran untuk tagihan yang sama | Baris tagihan dikunci dalam satu transaksi database, sehingga pembayaran kedua menunggu pembayaran pertama selesai | Tidak ada lock, sehingga pembayaran ganda baru terdeteksi dan ditandai setelah terjadi |
| Throughput | Beban puncak institusi hanya puluhan hingga ratusan TPS, masih dalam kapasitas PostgreSQL | Kemampuan scale-out Kafka belum dibutuhkan pada beban sebesar itu |
| Operasional | 1 aplikasi + 1 PostgreSQL | Tambah 1 broker Kafka, dan lebih banyak kode yang harus ditulis serta dirawat |
Pilihannya: monolit dengan PostgreSQL. Proses perbandingannya, termasuk benchmark yang sempat menyesatkan, dibahas di episode 1 hingga 4.
| Fitur | Status | Alasan |
|---|---|---|
| QRIS, kartu kredit, e-wallet | Dibuat jika klien membutuhkan | Untuk nominal sebesar SPP, MDR persentase lebih mahal dibandingkan tarif flat VA |
| Disbursement (transfer keluar) | Produk terpisah | Ditangani Disbursement Platform |
| Pengelolaan dana: saldo, deposit, penarikan | Tidak direncanakan | Uang pembayar masuk dari bank langsung ke rekening institusi, sedangkan gateway hanya mengelola data pembayaran |
| Penyusunan nomor VA di gateway | Belum dibuat | Saat ini consumer yang menyusun nomor VA, lalu gateway memeriksa prefix dan jumlah digitnya |
| Settlement pull API | Menunggu bank | Bank yang sudah terintegrasi belum menyediakan API untuk menarik data settlement |
| Membaca PDF rekening koran | Tidak direncanakan | Berkas CSV dari portal bank lebih dapat diandalkan |
| Circuit breaker otomatis pada webhook | Tidak direncanakan | Sebagai gantinya, tersedia kill-switch (penghenti manual) dan replay (pengiriman ulang) |
| Nilai default konfigurasi | Tidak direncanakan | Tidak ada bank default maupun adapter fallback. Jika konfigurasi tidak lengkap, aplikasi berhenti dengan pesan kesalahan yang jelas. |
github.com/artivisi/payment-gateway · artivisi.com/products/payment-gateway/