Integrasi RouterPay dalam satu API.
Panduan lengkap untuk authentication, create transaction, payment URL, status check, webhook merchant, contoh kode, dan demo merchant end-to-end.
{
"merchant_ref": "INV-001",
"amount": 150000,
"payment_method": "qris",
"customer_name": "Budi",
"return_url": "https://merchant.example/orders/INV-001"
}
Autentikasi
Header dan signature wajib.
Setiap request merchant memakai Merchant Code, API Key, timestamp, dan HMAC signature. API Secret tidak pernah dikirim langsung.
https://api.routerpay.co.idGunakan subdomain API untuk integrasi, bukan domain web utama.
Credential Wajib
X-RouterPay-Merchant: {merchant_code}
X-RouterPay-Key: {api_key}
X-RouterPay-Timestamp: {unix_timestamp}
X-RouterPay-Signature: {hmac_sha256}
Idempotency-Key: {unique_retry_key_optional}Signature
hash_hmac('sha256', timestamp + '.' + raw_json_body, api_secret)Timestamp berlaku 5 menit. Kirim Idempotency-Key agar retry aman dan tidak membuat transaksi dobel.
Create Transaction
Buat transaksi dan redirect customer.
Response 201 berisi reference, status, amount, payable amount, fee, net amount, payment URL, dan payment options.
POST https://api.routerpay.co.id/api/v1/transactions
Content-Type: application/json
{
"merchant_ref": "INV-001",
"amount": 150000,
"payment_method": "qris",
"currency": "IDR",
"auto_provider": true,
"customer_name": "Budi",
"customer_email": "budi@example.com",
"customer_phone": "081234567890",
"return_url": "https://merchant.example/orders/INV-001",
"success_url": "https://merchant.example/orders/INV-001/success",
"failed_url": "https://merchant.example/orders/INV-001/failed",
"metadata": {"cart_id":"CART-001"}
}Redirect customer ke data.payment_url. Jangan update order hanya dari redirect browser; validasi status final dari webhook atau endpoint check transaction.
Contoh Kode
Implementasi cepat di backend merchant.
Laravel / PHP
$merchantCode = 'DEMO';
$apiKey = 'rpk_demo_xxxxx';
$apiSecret = 'rps_xxxxx';
$payload = [
'merchant_ref' => 'INV-'.time(),
'amount' => 150000,
'customer_name' => 'Budi',
'customer_email' => 'budi@example.com',
'customer_phone' => '081234567890',
];
$body = json_encode($payload, JSON_UNESCAPED_SLASHES);
$timestamp = (string) time();
$signature = hash_hmac('sha256', $timestamp.'.'.$body, $apiSecret);
$response = Http::withHeaders([
'X-RouterPay-Merchant' => $merchantCode,
'X-RouterPay-Key' => $apiKey,
'X-RouterPay-Timestamp' => $timestamp,
'X-RouterPay-Signature' => $signature,
'Idempotency-Key' => $payload['merchant_ref'],
'Content-Type' => 'application/json',
])->withBody($body, 'application/json')
->post('https://api.routerpay.co.id/api/v1/transactions')
->throw()
->json();
return redirect()->away($response['data']['payment_url']);Node.js
import crypto from 'crypto';
const payload = {
merchant_ref: `INV-${Date.now()}`,
amount: 150000,
customer_name: 'Budi',
};
const body = JSON.stringify(payload);
const timestamp = Math.floor(Date.now() / 1000).toString();
const signature = crypto
.createHmac('sha256', process.env.ROUTERPAY_API_SECRET)
.update(`${timestamp}.${body}`)
.digest('hex');
const res = await fetch('https://api.routerpay.co.id/api/v1/transactions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-RouterPay-Merchant': process.env.ROUTERPAY_MERCHANT_CODE,
'X-RouterPay-Key': process.env.ROUTERPAY_API_KEY,
'X-RouterPay-Timestamp': timestamp,
'X-RouterPay-Signature': signature,
'Idempotency-Key': payload.merchant_ref,
},
body,
});
const json = await res.json();PHP Native / cURL
$merchantCode = getenv('ROUTERPAY_MERCHANT_CODE');
$apiKey = getenv('ROUTERPAY_API_KEY');
$apiSecret = getenv('ROUTERPAY_API_SECRET');
$payload = ['merchant_ref' => 'INV-'.time(), 'amount' => 150000];
$body = json_encode($payload, JSON_UNESCAPED_SLASHES);
$timestamp = (string) time();
$signature = hash_hmac('sha256', $timestamp.'.'.$body, $apiSecret);
$ch = curl_init('https://api.routerpay.co.id/api/v1/transactions');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $body,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'X-RouterPay-Merchant: '.$merchantCode,
'X-RouterPay-Key: '.$apiKey,
'X-RouterPay-Timestamp: '.$timestamp,
'X-RouterPay-Signature: '.$signature,
'Idempotency-Key: '.$payload['merchant_ref'],
],
]);
$response = json_decode(curl_exec($ch), true);
header('Location: '.$response['data']['payment_url']);Check Transaction
GET https://api.routerpay.co.id/api/v1/transactions/{reference}
GET https://api.routerpay.co.id/api/v1/transactions/{merchant_ref}Payment URL, Link & Receipt
Customer diarahkan ke payment_url. Merchant juga bisa membuat Payment Link dari dashboard untuk dibagikan manual ke customer.
Payment Link: https://routerpay.co.id/link/{uuid}
Receipt/status: https://routerpay.co.id/pay/{transaction_uuid}/receipt
PDF: https://routerpay.co.id/pay/{transaction_uuid}/receipt.pdfReceipt redirect ke success_url hanya jika status transaksi sudah paid. Pending tidak otomatis diarahkan keluar.
ui.webhook
Status final dikirim dengan signed webhook.
Payload webhook ditandatangani menggunakan ui.webhook Secret agar merchant bisa memverifikasi sumber callback.
Header ui.webhook
X-RouterPay-ui.webhook-Timestamp: {unix_timestamp}
X-RouterPay-ui.webhook-Signature: {hmac_sha256}{
"event": "transaction.paid",
"reference": "RP260512172800ABC123",
"merchant_ref": "INV-001",
"status": "paid",
"amount": 150000,
"fee_amount": 2050,
"net_amount": 147950,
"currency": "IDR",
"payment_method": "qris",
"payment_url": "https://.../pay/{uuid}"
}Receiver Laravel
Route::post('/webhook/routerpay', function (Request $request) {
$webhookSecret = env('ROUTERPAY_WEBHOOK_SECRET');
$timestamp = $request->header('X-RouterPay-ui.webhook-Timestamp');
$signature = $request->header('X-RouterPay-ui.webhook-Signature');
$rawBody = $request->getContent();
if (! $timestamp || abs(time() - (int) $timestamp) > 300) abort(401);
$expected = hash_hmac('sha256', $timestamp.'.'.$rawBody, $webhookSecret);
if (! hash_equals($expected, (string) $signature)) abort(401);
$payload = json_decode($rawBody, true);
// update order by merchant_ref + status
return response()->json(['ok' => true]);
});ui.webhook Retry Otomatis
routerpay:webhooks-retry --limit=50
Jadwal retry: 1 menit, 5 menit, 15 menit, 1 jam, 3 jam, 6 jam, 12 jam, lalu harian sampai maksimum 10 percobaan.
Provider ui.webhook
POST /api/webhooks/provider/midtrans POST /api/webhooks/provider/xendit POST /api/webhooks/provider/routerpay
Status dinormalisasi menjadi pending, paid, failed, expired, cancelled, atau refunded.
Demo Merchant End-to-End
Coba integrasi tanpa coding dulu.
Demo membuat order lokal, memanggil API RouterPay, redirect ke payment page, lalu menerima webhook untuk update status order.