Integrasi Mobile•Headless

Native Message: Saldo, Inquiry QRIS & E-Wallet

{BASE_URL}/native/message adalah halaman WebView tanpa tampilan. Native mengirim permintaan lewat postMessage, halaman mengerjakannya di server, lalu membalas ke native. Halaman ini hanya untuk data baca-saja: tidak ada pembayaran, transfer, atau PIN di sini.

Type yang Tersedia

TypeBodyIsi data balasan
GETBALANCE{}Saldo QoinCash user.
GETINQUIRYQRIS{ qrvalue }Data inquiry QRIS untuk pembayaran.
GETWALLETLIST{}Daftar e-wallet (grup produk).
GETWALLETDENOM{ walletType }Daftar nominal untuk satu e-wallet.
GETWALLETINQUIRY{ phoneNumber, code }Inquiry top up e-wallet (nama pemilik, nominal, biaya admin).
GETPHONECREDITDENOM{ phoneNumber, kind? }Daftar nominal pulsa / paket data (provider, produk, productsToken).
GETPHONECREDITINQUIRY{ phoneNumber, kind?, code, productsToken }Inquiry pulsa / paket data (nominal, biaya admin, inquiryToken).
GETHISTORYTRANSACTION{ page?, size? }Riwayat transaksi QoinCash per halaman (default page 1, size 5; size maksimal 25).

1. Cara Memanggil

  1. Muat WebView (boleh headless, misalnya HeadlessInAppWebView) ke {BASE_URL}/native/message. Cookie store dan penyimpanan lokal harus sama dengan WebView yang terlihat (di iOS jangan pakai WKWebsiteDataStore.nonPersistent()), kalau tidak sesi tidak terbaca.
  2. Setelah halaman dimuat, kirim satu permintaan postMessage (JSON string). Ulangi kirim tiap ±500 ms sampai balasan datang.
  3. Tangkap balasan lewat bridge native. Begitu diterima, hentikan WebView tersebut.
Permintaan (Native → WebView)
// Format umum: { type, body }. TIDAK perlu mengirim Signature.
window.postMessage(JSON.stringify({ type: "GETBALANCE", body: {} }), "*");
window.postMessage(JSON.stringify({ type: "GETINQUIRYQRIS", body: { qrvalue: "<string QRIS>" } }), "*");
window.postMessage(JSON.stringify({ type: "GETWALLETLIST", body: {} }), "*");
window.postMessage(JSON.stringify({ type: "GETWALLETDENOM", body: { walletType: "DANA" } }), "*");
window.postMessage(JSON.stringify({ type: "GETWALLETINQUIRY", body: { phoneNumber: "085777142098", code: "DANA10H" } }), "*");
window.postMessage(JSON.stringify({ type: "GETPHONECREDITDENOM", body: { phoneNumber: "081234567890", kind: "pulsa" } }), "*");
window.postMessage(JSON.stringify({ type: "GETPHONECREDITINQUIRY", body: { phoneNumber: "081234567890", kind: "pulsa", code: "TSEL10", productsToken: "<token>" } }), "*");
window.postMessage(JSON.stringify({ type: "GETHISTORYTRANSACTION", body: { page: 1, size: 5 } }), "*"); // page & size opsional (default 1 dan 5)
Signature tidak dikirim native
Halaman membaca Signature dari penyimpanan lokal WebView (diisi saat aktivasi). Kalau native tetap mengirim body.Signature, nilainya diabaikan (permintaan tetap diproses). Jika penyimpanan tidak berisi Signature, artinya perangkat belum pernah aktivasi: halaman langsung membalas 401 tanpa memanggil API apa pun.
Tidak ada properti close
Balasan halaman ini tidak membawa close. Native yang memutuskan kapan WebView ditutup. Halaman tetap hidup dan boleh menerima permintaan berikutnya. Permintaan bertipe sama yang sedang berjalan akan diabaikan, jadi aman mengirim ulang tiap 500 ms.

2. Format Balasan

Balasan (WebView → Native)
{
  "type": "GETBALANCE",
  "code": 200,
  "message": "Berhasil mengambil saldo",
  "data": { "amount": 150000, "grossAmount": 160000, "monthlyTotal": 1120000, "hasIdrAsset": true }
}
Kode Balasan (code)
ParameterTipe DataStatusDeskripsi & Contoh
200numberOpsional
Sukses. Data ada di properti data.
400numberOpsional
Permintaan tidak valid: type tidak didukung, body bukan objek, atau field wajib (qrvalue, walletType, phoneNumber, code) kosong/tidak valid. Dicek lebih dulu dan tidak memanggil API.
401numberOpsional
Belum aktivasi (tidak ada Signature di penyimpanan, tanpa panggilan API) atau sesi tidak bisa dipakai. Buka WebView biasa ke / untuk aktivasi.
403numberOpsional
Sesi habis tetapi akun sudah pernah aktivasi: minta user memasukkan PIN lewat WebView biasa. (GETINQUIRYQRIS hanya mengenal 401.)
500numberOpsional
Kesalahan sistem atau penyedia layanan. Pesan ada di message.
0numberOpsional
Jaringan terputus.

Tipe di luar daftar dibalas code 400 dengan type yang sama (huruf besar, angka, dan garis bawah saja), atau UNKNOWN.

3. GETBALANCE

Mengambil saldo QoinCash user. Body kosong. Saldo ditentukan oleh sesi login (JWT), bukan Signature. Aktivasi ulang mereset sesi, sehingga permintaan berikutnya mendapat 401 atau 403.

data (saat code 200)
{ "amount": 150000, "grossAmount": 160000, "monthlyTotal": 1120000, "hasIdrAsset": true }

4. GETINQUIRYQRIS

Memvalidasi string QRIS (hasil scan native) dan mengembalikan data yang dibutuhkan untuk membayar. Ini hanya inquiry, tidak ada pembayaran. Hasilnya dipakai pada halaman pembayaran QRIS.

body
ParameterTipe DataStatusDeskripsi & Contoh
qrvaluestringWajib
Isi QR QRIS (payload EMVCo) apa adanya, maksimal 1024 karakter. Nama kunci tidak peka huruf besar/kecil (qrValue, QRValue juga diterima).
Contoh: 00020101021126610014COM.GO-JEK.WWW0118936009143055035934...
data (saat code 200)
ParameterTipe DataStatusDeskripsi & Contoh
PaymentIdstringWajib
ID pembayaran dari inquiry.
ReferenceNumberstringWajib
Nomor referensi dari inquiry. Hanya bisa dipakai sekali.
AccountNamestringOpsional
Nama merchant.
AmountstringWajib
Nominal dari QR. "0" untuk QR statis (nominal diisi native); selain itu nominal sudah ditentukan QR.
Contoh: "1000"
TipAmountstringWajib
Tip dari inquiry ("0" bila tidak ada).
TerminalIdstringWajib
ID terminal merchant.
TicketIdstringOpsional
ID tiket inquiry.
inquiryTokenstringOpsional
Token buram dari server yang mengunci PaymentId, ReferenceNumber, merchant, dan nominal QR dinamis. Teruskan apa adanya ke /payment/qris. Akan menjadi wajib (lihat Bayar QRIS dari Native).
Semua token bersifat buram
inquiryToken, productsToken, dan token lain dari halaman ini adalah string buram (opaque). Simpan dan teruskan apa adanya; jangan memeriksa awalan, jumlah titik, atau panjangnya, karena formatnya bisa berubah tanpa pemberitahuan.
Data inquiry punya masa berlaku
PaymentId, ReferenceNumber, TicketId, dan inquiryToken kedaluwarsa (inquiryToken ±5 menit). Setelah inquiry, segera lanjut ke pembayaran. Jika ditolak karena kedaluwarsa, scan dan inquiry ulang.
Hanya baca
Tipe di halaman ini sengaja terbatas pada data baca-saja. Aksi yang menggerakkan uang tidak tersedia lewat /native/message: PIN wajib diketik di WebView yang terlihat dan tidak pernah dikirim lewat postMessage.

5. E-Wallet: GETWALLETLIST, GETWALLETDENOM, GETWALLETINQUIRY

Tiga type berurutan untuk menyiapkan top up e-wallet: ambil daftar e-wallet, pilih nominal, lalu inquiry nomor tujuan. Seluruhnya baca-saja. Eksekusi pembayaran tidak tersedia lewat halaman ini.

GETWALLETLIST

Tanpa body. Balasan data.groups berisi daftar e-wallet; nilai pGroup dipakai sebagai walletType. Daftarnya diteruskan apa adanya (tidak ada penyaringan provider seperti di layar Top Up).

data (saat code 200)
{ "groups": [ { "pGroup": "DANA" }, { "pGroup": "OVO" } ] }

GETWALLETDENOM

body
ParameterTipe DataStatusDeskripsi & Contoh
walletTypestringWajib
Nilai pGroup dari GETWALLETLIST. Huruf atau angka di awal, lalu huruf, angka, spasi, titik, garis bawah, & atau tanda hubung; maksimal 64 karakter. Nama kunci tidak peka huruf besar/kecil.
Contoh: DANA

Balasan data.products berisi nominal untuk e-wallet itu (urutan mentah dari server). Nilai pCode dipakai sebagai code pada GETWALLETINQUIRY.

data (saat code 200)
{ "products": [ { "pCode": "DANA10H", "pDenom": "10000", "pPrice": "10500" } ] }

GETWALLETINQUIRY

body
ParameterTipe DataStatusDeskripsi & Contoh
phoneNumberstring | numberWajib
Nomor HP tujuan top up. Hanya digit yang dipakai; awalan 62 atau 0 dibuang lalu dibentuk menjadi 08…, dan harus 10–15 digit. +62 857-7714-2098, 6285777142098, dan 085777142098 dianggap sama.
Contoh: 085777142098
codestringWajib
Kode produk (pCode) dari GETWALLETDENOM. Huruf atau angka di awal, lalu huruf, angka, titik, garis bawah, & atau tanda hubung (tanpa spasi); maksimal 64 karakter.
Contoh: DANA10H
data (saat code 200)
{ "custNumber": "085777142098", "custName": "…", "productCode": "DANA10H", "productDesc": "…", "amount": 10000, "adminFee": 500, "period": "0", "freeTexts": [], "inquiryToken": "<token>" }
inquiryToken untuk halaman bayar
data.inquiryToken adalah token tersegel yang dipakai saat pembayaran. Teruskan apa adanya ke WebView /payment/ewallet(lihat Bayar E-Wallet dari Native); jangan diubah. Token terikat ke akun user dan berlaku singkat (±5 menit). Jika inquiry sukses tetapi token tidak tersedia, balasannya code 500, bukan 200.

6. Pulsa & Paket Data: GETPHONECREDITDENOM, GETPHONECREDITINQUIRY

Dua type berurutan untuk menyiapkan pembelian pulsa & paket data dari native: ambil daftar nominal sesuai operator nomor HP, lalu inquiry sebelum pembayaran. Operator dideteksi otomatis oleh server dari prefix nomor HP dan dikembalikan sebagai data.provider.

GETPHONECREDITDENOM

body
ParameterTipe DataStatusDeskripsi & Contoh
phoneNumberstring | numberWajib
Nomor HP tujuan. Digit saja, awalan 62 atau 0 dinormalkan menjadi 08… (10–15 digit).
Contoh: 081234567890
kindstringOpsional
Jenis produk: "pulsa" atau "data". Default "pulsa" bila tidak dikirim. Nilai lain ditolak 400.
Contoh: "pulsa"

Balasan data.products memuat daftar nominal operator tersebut, dan data.productsToken adalah token allowlist produk yang WAJIB dikirimkan kembali ke GETPHONECREDITINQUIRY.

data (saat code 200)
{
  "provider": "TELKOMSEL",
  "products": [
    { "pCode": "TSEL10", "pName": "Telkomsel 10.000", "pPrice": 11000, "pGroup": "TELKOMSEL" }
  ],
  "productsToken": "<token>"
}

GETPHONECREDITINQUIRY

body
ParameterTipe DataStatusDeskripsi & Contoh
phoneNumberstring | numberWajib
Nomor HP tujuan (10–15 digit).
Contoh: 081234567890
kindstringOpsional
Jenis produk ("pulsa" atau "data"). Default "pulsa".
Contoh: "pulsa"
codestringWajib
Kode produk (pCode) yang dipilih dari daftar DENOM.
Contoh: TSEL10
productsTokenstringWajib
Token tersegel dari balasan GETPHONECREDITDENOM (data.productsToken).
Contoh: <token>
data (saat code 200)
{
  "custNumber": "081234567890",
  "custName": "",
  "productCode": "TSEL10",
  "productDesc": "Telkomsel 10.000",
  "amount": 10000,
  "adminFee": 1000,
  "period": "0",
  "freeTexts": [],
  "inquiryToken": "<token>"
}
inquiryToken untuk halaman bayar
data.inquiryToken diteruskan ke WebView /payment/phonecredit (lihat Bayar Pulsa & Paket Data dari Native). Jika token nominal kedaluwarsa (> 5 menit), server mengembalikan code 410 ("Daftar nominal sudah tidak berlaku"); jika kode produk tidak tersedia untuk operator tersebut, dikembalikan code 422.

7. Riwayat: GETHISTORYTRANSACTION

Mengambil riwayat transaksi QoinCash user per halaman. Baca-saja. Memakai data yang sama dengan layar Riwayat di WebView.

body
ParameterTipe DataStatusDeskripsi & Contoh
pagenumberOpsional
Nomor halaman, bilangan bulat mulai dari 1. Angka atau string digit saja ("2"). Tidak dikirim atau null = 1.
Contoh: 1
sizenumberOpsional
Jumlah item per halaman, bilangan bulat 1 sampai 25. Tidak dikirim atau null = 5. Lebih dari 25 ditolak langsung dengan code 400 dan pesan, tanpa memanggil API.
Contoh: 5
data (saat code 200)
{
  "items": [
    {
      "trxId": "…", "trxReceipt": "…", "trxProductName": "…",
      "trxCategory": "payment", "trxCategoryLabel": "…",
      "trxAmount": 10000, "trxDirection": "D",
      "trxDate": "…", "trxStatus": 1, "trxStatusString": "…"
    }
  ],
  "pagination": { "page": 1, "pageSize": 5, "totalPage": 3, "totalRecord": 27 }
}
Halaman berikutnya
Pakai pagination.totalPage untuk tahu halaman terakhir. User tanpa transaksi dijawab code 200 dengan items kosong. Kirim satu permintaan per halaman; permintaan GETHISTORYTRANSACTION yang masih berjalan akan mengabaikan permintaan berikutnya.