================================================================================ INSTANPAY API DOCUMENTATION Jasa Link Pembayaran QRIS + Crypto (USDT/USDC) Indonesia v2.0 ================================================================================ BASE URL: https://instanpay.my.id CONTENT-TYPE: application/json RESPONSE FORMAT: JSON DOCS ONLINE: https://instanpay.my.id/docs ================================================================================ AUTHENTICATION ================================================================================ Semua request ke endpoint /api/v1/* memerlukan API Key di header: X-API-Key: sk_live_xxxxxxxxxxxxxxxx Content-Type: application/json Cara Mendapatkan API Key: 1. Register di POST /api/auth/register 2. Login di POST /api/auth/login 3. API Key akan diberikan di response login PENTING: Jangan share API Key Anda. API Key bersifat rahasia seperti password. Simpan API Key di server-side, jangan expose ke client-side/browser. ================================================================================ ENDPOINTS ================================================================================ -------------------------------------------------------------------------------- 1. CREATE PAYMENT (Generate QRIS) -------------------------------------------------------------------------------- POST /api/v1/payments Request Headers: X-API-Key: sk_live_xxxx Content-Type: application/json Request Body: { "amount": 10000, "customer_name": "John Doe", // optional "webhook_url": "https://example.com/webhook" // optional } Success Response (200): { "success": true, "data": { "transactionId": "TXN-1751411106-161", "qrisString": "0002010102112657...", "qrCodeSvg": "...", "baseAmount": 10000, "uniqueCode": 161, "totalAmount": 10161, "baseFormatted": "Rp 10.000", "uniqueFormatted": "Rp 161", "totalFormatted": "Rp 10.161", "expiredAt": "2026-07-02 02:05:06", "status": "PENDING" } } Error Responses: 400: { "error": "Nominal minimal Rp 100" } 400: { "error": "Nominal harus berupa bilangan bulat positif" } 401: { "error": "API Key tidak valid" } 503: { "error": "Server monitor sedang offline. Silakan coba beberapa saat lagi." } -------------------------------------------------------------------------------- 2. CHECK PAYMENT STATUS -------------------------------------------------------------------------------- GET /api/v1/status/:transactionId Contoh: GET /api/v1/status/TXN-1751411106-161 Success Response (200): { "success": true, "data": { "transactionId": "TXN-1751411106-161", "baseAmount": 10000, "uniqueCode": 161, "totalAmount": 10161, "status": "PENDING", "expiredAt": "2026-07-02 02:05:06", "paidAt": null } } Status Values: PENDING - Menunggu pembayaran PAID - Pembayaran berhasil EXPIRED - Transaksi kadaluarsa (2 jam) Error Response (404): { "error": "Transaksi tidak ditemukan" } -------------------------------------------------------------------------------- 3. MERCHANT REGISTER -------------------------------------------------------------------------------- POST /api/auth/register Request Body: { "name": "Toko ABC", "email": "admin@tokoabc.com", "password": "password123" } Success Response (200): { "success": true, "message": "Pendaftaran berhasil!" } Error Response (400): { "error": "Email sudah terdaftar" } { "error": "Nama, email, dan password wajib diisi" } -------------------------------------------------------------------------------- 4. MERCHANT LOGIN -------------------------------------------------------------------------------- POST /api/auth/login Request Body: { "email": "admin@tokoabc.com", "password": "password123" } Success Response (200): { "success": true, "data": { "token": "eyJ1c2VySWQiOiJVU1ItMTc1...", "name": "Toko ABC", "email": "admin@tokoabc.com", "apiKey": "sk_live_hbzxd02an5qyjqeg", "role": "merchant" } } Error Response (401): { "error": "Email atau password salah" } -------------------------------------------------------------------------------- 5. CREATE CRYPTO PAYMENT (USDT / USDC) -------------------------------------------------------------------------------- POST /api/v1/crypto-payments Request Headers: X-API-Key: sk_live_xxxx Content-Type: application/json Request Body: { "amount_usd": 5.00, "chain": "BSC", "token": "USDT", "customer_name": "John Doe", // optional "customer_email": "j@mail.com" // optional } Chain yang didukung : BSC, BASE, ETH, POLYGON, SOL Token yang didukung : USDT, USDC (stablecoin only, tidak ada BNB/ETH/SOL) Expiry : 30 menit sejak dibuat Success Response (200): { "success": true, "data": { "transactionId": "CRYPTO-1751500000-AB1CD", "gatewayOrderId": "ORD_MRKxxxx_XXXXXX", "amount_usd": 5.00, "chain": "BSC", "token": "USDT", "deposit_address": "0xAbCd...1234", "status": "PENDING", "expires_at": "2026-07-14T10:30:00.000Z", "payment_url": "https://instanpay.my.id/pay/crypto/ORD_MRKxxxx_XXXXXX" } } Error Responses: 400: { "error": "amount_usd harus berupa angka positif" } 400: { "error": "chain wajib diisi (BSC, BASE, ETH, POLYGON, SOL)" } 400: { "error": "Hanya USDT dan USDC yang didukung" } 401: { "error": "API Key tidak valid" } 503: { "error": "Crypto gateway belum dikonfigurasi" } -------------------------------------------------------------------------------- 6. CHECK CRYPTO PAYMENT STATUS -------------------------------------------------------------------------------- GET /api/v1/crypto-status/:transactionId transactionId bisa berupa: - ID lokal instanpay : CRYPTO-1751500000-AB1CD - ID payment-gateway : ORD_MRKxxxx_XXXXXX Contoh: GET /api/v1/crypto-status/CRYPTO-1751500000-AB1CD Success Response (200): { "success": true, "data": { "transactionId": "CRYPTO-1751500000-AB1CD", "gatewayOrderId": "ORD_MRKxxxx_XXXXXX", "amount_usd": 5.00, "chain": "BSC", "token": "USDT", "deposit_address": "0xAbCd...1234", "status": "PENDING", "tx_hash": null, "amount_received": null, "asset_received": null, "created_at": "2026-07-14T10:00:00.000Z", "expires_at": "2026-07-14T10:30:00.000Z", "paid_at": null, "payment_url": "https://instanpay.my.id/pay/crypto/ORD_MRKxxxx_XXXXXX" } } Status Values: PENDING - Menunggu pembayaran on-chain PAID - Pembayaran diterima dan dikonfirmasi EXPIRED - Transaksi kadaluarsa (30 menit) ================================================================================ RULES & CONSTRAINTS ================================================================================ 1. NOMINAL MINIMUM & MAXIMUM - Minimum: Rp 100 - Maximum: Rp 99.999.999 - Harus berupa bilangan bulat (integer), tidak boleh desimal - Tidak boleh negatif atau nol 2. UNIQUE CODE SYSTEM - Setiap transaksi akan ditambahkan kode unik secara otomatis - Range kode unik: 500 - 999 (prioritas), fallback ke 100-499 - Total yang harus dibayar = Amount + Unique Code - Contoh: Amount Rp 10.000 + Unique Code 652 = Total Rp 10.652 PENTING: Customer HARUS membayar EXACT amount (totalAmount)! Jika pembayaran tidak sesuai, sistem tidak bisa match. 3. TRANSACTION EXPIRY - Setiap transaksi valid selama 2 JAM sejak dibuat - Status otomatis berubah ke EXPIRED jika tidak dibayar dalam 2 jam - Format expiredAt: "YYYY-MM-DD HH:MM:SS" (UTC+7) 4. BALANCE CALCULATION (QRIS) - Balance yang masuk = Base Amount (TANPA unique code) - Contoh: Customer bayar Rp 10.161, merchant dapat Rp 10.000 - Unique code TIDAK masuk ke balance merchant 5. BALANCE CALCULATION (KRIPTO) - Balance dikreditkan dalam satuan sen USD (integer) - Rumus: amount_usd x 100 - Contoh: Customer bayar 5.00 USDT → balance +500 unit - Toleransi pembayaran: ±1% (misal order $5.00, diterima $4.95 = tetap PAID) - Konversi ke IDR dilakukan manual oleh owner saat proses withdrawal ================================================================================ ERROR CODES ================================================================================ HTTP Status Codes: 200 - Success 400 - Bad Request (parameter tidak valid, nominal invalid, dll) 401 - Unauthorized (API Key tidak valid atau tidak ada) 404 - Not Found (transaksi tidak ditemukan) 503 - Service Unavailable (server monitor / crypto gateway offline) 500 - Internal Server Error Contoh Error Response: { "error": "Nominal minimal Rp 100" } { "error": "API Key diperlukan di header X-API-Key" } { "error": "Transaksi tidak ditemukan" } ================================================================================ WEBHOOKS — QRIS (TIDAK BERUBAH) ================================================================================ Webhook dikirim ke URL yang dikonfigurasi saat pembayaran QRIS berhasil. Payload ini TIDAK BERUBAH untuk menjaga kompatibilitas sistem yang sudah ada. Webhook Payload QRIS (POST): { "transactionId": "TXN-1751411106-161", "status": "PAID", "baseAmount": 10000, "uniqueCode": 161, "totalAmount": 10161, "paidAt": "2026-07-02 01:30:45" } Field: transactionId - ID transaksi instanpay status - Selalu "PAID" baseAmount - Nominal dasar yang di-input merchant (tanpa kode unik) uniqueCode - Kode unik yang ditambahkan totalAmount - Total yang dibayar customer (base + unique) paidAt - Waktu konfirmasi pembayaran ================================================================================ WEBHOOKS — KRIPTO (PAYLOAD TERPISAH) ================================================================================ Webhook dikirim ke URL yang sama (webhook_url merchant) saat pembayaran crypto berhasil dikonfirmasi on-chain. Cara membedakan webhook QRIS vs Crypto: cek field "type" - QRIS : field "type" TIDAK ADA - Crypto: field "type" = "CRYPTO" Webhook Payload KRIPTO (POST): { "transactionId": "CRYPTO-1751500000-AB1CD", "status": "PAID", "paidAt": "2026-07-14T10:15:30.000Z", "type": "CRYPTO", "gatewayOrderId": "ORD_MRKxxxx_XXXXXX", "amount_usd": 5.00, "chain": "BSC", "token": "USDT", "tx_hash": "0xabc123...def456", "amount_received": "5.00", "asset_received": "USDT" } Field: transactionId - ID transaksi lokal instanpay (CRYPTO-xxx) status - Selalu "PAID" paidAt - Waktu konfirmasi on-chain (ISO 8601) type - "CRYPTO" — penanda ini transaksi crypto gatewayOrderId - ID order di payment-gateway (ORD_xxx) amount_usd - Nominal order dalam USD chain - Chain yang dipakai: BSC / BASE / ETH / POLYGON / SOL token - Token yang diterima: USDT / USDC tx_hash - Hash transaksi on-chain (bisa null jika belum tersedia) amount_received - Jumlah aktual yang diterima (string, bisa null) asset_received - Simbol asset yang diterima (bisa null) Persyaratan webhook (berlaku untuk QRIS maupun Crypto): - Server Anda harus merespon dengan HTTP 200 - Timeout: 10 detik - Content-Type: application/json Cara Set Webhook URL: POST /api/dashboard/profile Body: { "webhooks": [{ "label": "Toko Saya", "url": "https://..." }] } Contoh handler webhook yang bisa bedakan QRIS vs Crypto (Node.js): app.post('/webhook', (req, res) => { const payload = req.body; if (payload.type === 'CRYPTO') { // Transaksi crypto console.log('Crypto PAID:', payload.amount_usd, payload.token, payload.chain); console.log('TX Hash:', payload.tx_hash); } else { // Transaksi QRIS (payload lama, tidak ada field type) console.log('QRIS PAID:', payload.totalAmount, 'transactionId:', payload.transactionId); } res.sendStatus(200); }); ================================================================================ CODE EXAMPLES — QRIS ================================================================================ JavaScript/Node.js: ------------------- const axios = require('axios'); async function createPayment() { try { const response = await axios.post('https://instanpay.my.id/api/v1/payments', { amount: 10000, customer_name: 'John Doe' }, { headers: { 'X-API-Key': 'sk_live_xxxx', 'Content-Type': 'application/json' } } ); console.log('QRIS:', response.data.data.qrisString); console.log('Total Bayar:', response.data.data.totalFormatted); } catch (error) { console.error('Error:', error.response?.data || error.message); } } async function checkStatus(transactionId) { const response = await axios.get( 'https://instanpay.my.id/api/v1/status/' + transactionId, { headers: { 'X-API-Key': 'sk_live_xxxx' } } ); console.log('Status:', response.data.data.status); } PHP: ---- $apiKey = 'sk_live_xxxx'; // Create Payment $ch = curl_init('https://instanpay.my.id/api/v1/payments'); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(['amount' => 10000])); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Content-Type: application/json', 'X-API-Key: ' . $apiKey ]); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $response = curl_exec($ch); curl_close($ch); $data = json_decode($response, true); echo "Transaction ID: " . $data['data']['transactionId']; // Check Status $txId = 'TXN-1751411106-161'; $ch = curl_init('https://instanpay.my.id/api/v1/status/' . $txId); curl_setopt($ch, CURLOPT_HTTPHEADER, ['X-API-Key: ' . $apiKey]); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $response = curl_exec($ch); curl_close($ch); Python: ------- import requests api_key = 'sk_live_xxxx' # Create QRIS Payment response = requests.post( 'https://instanpay.my.id/api/v1/payments', json={'amount': 10000, 'customer_name': 'John Doe'}, headers={'X-API-Key': api_key} ) data = response.json() print(f"Transaction ID: {data['data']['transactionId']}") print(f"Total: {data['data']['totalFormatted']}") # Check QRIS Status tx_id = 'TXN-1751411106-161' response = requests.get( f'https://instanpay.my.id/api/v1/status/{tx_id}', headers={'X-API-Key': api_key} ) print(f"Status: {response.json()['data']['status']}") ================================================================================ CODE EXAMPLES — KRIPTO ================================================================================ JavaScript/Node.js: ------------------- const axios = require('axios'); const apiKey = 'sk_live_xxxx'; // Buat order crypto async function createCryptoPayment() { const response = await axios.post( 'https://instanpay.my.id/api/v1/crypto-payments', { amount_usd: 5.00, chain: 'BSC', token: 'USDT', customer_name: 'John Doe' }, { headers: { 'X-API-Key': apiKey, 'Content-Type': 'application/json' } } ); const data = response.data.data; console.log('Transaction ID :', data.transactionId); console.log('Deposit Address :', data.deposit_address); console.log('Halaman Bayar :', data.payment_url); console.log('Expired :', data.expires_at); return data.transactionId; } // Cek status order crypto async function checkCryptoStatus(transactionId) { const response = await axios.get( 'https://instanpay.my.id/api/v1/crypto-status/' + transactionId, { headers: { 'X-API-Key': apiKey } } ); const data = response.data.data; console.log('Status :', data.status); console.log('TX Hash :', data.tx_hash); console.log('Diterima :', data.amount_received, data.token); } PHP: ---- $apiKey = 'sk_live_xxxx'; // Buat order crypto $ch = curl_init('https://instanpay.my.id/api/v1/crypto-payments'); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([ 'amount_usd' => 5.00, 'chain' => 'BSC', 'token' => 'USDT', 'customer_name' => 'John Doe' ])); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Content-Type: application/json', 'X-API-Key: ' . $apiKey ]); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $response = curl_exec($ch); curl_close($ch); $data = json_decode($response, true)['data']; echo "Transaction ID : " . $data['transactionId'] . " "; echo "Deposit Address: " . $data['deposit_address'] . " "; echo "Halaman Bayar : " . $data['payment_url'] . " "; // Cek status $txId = 'CRYPTO-1751500000-AB1CD'; $ch = curl_init('https://instanpay.my.id/api/v1/crypto-status/' . $txId); curl_setopt($ch, CURLOPT_HTTPHEADER, ['X-API-Key: ' . $apiKey]); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $status = json_decode(curl_exec($ch), true)['data']['status']; curl_close($ch); echo "Status: " . $status; Python: ------- import requests api_key = 'sk_live_xxxx' # Buat order crypto response = requests.post( 'https://instanpay.my.id/api/v1/crypto-payments', json={ 'amount_usd': 5.00, 'chain': 'BSC', 'token': 'USDT', 'customer_name': 'John Doe' }, headers={'X-API-Key': api_key} ) data = response.json()['data'] print(f"Transaction ID : {data['transactionId']}") print(f"Deposit Address : {data['deposit_address']}") print(f"Halaman Bayar : {data['payment_url']}") # Cek status tx_id = data['transactionId'] resp = requests.get( f"https://instanpay.my.id/api/v1/crypto-status/{tx_id}", headers={'X-API-Key': api_key} ) print(f"Status : {resp.json()['data']['status']}") print(f"TX Hash : {resp.json()['data']['tx_hash']}") ================================================================================ CONTACT & SUPPORT ================================================================================ Website: https://instanpay.my.id Docs: https://instanpay.my.id/docs Admin Dashboard: https://paticuan.my.id ================================================================================