📚 Dokumentasi Integrasi

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.

POST /api/v1/transactions
{
  "merchant_ref": "INV-001",
  "amount": 150000,
  "payment_method": "qris",
  "customer_name": "Budi",
  "return_url": "https://merchant.example/orders/INV-001"
}
AuthHMAC
APIJSON
ui.webhookSigned
01Create OrderMerchant membuat order di website sendiri.
02Request APIKirim transaksi ke RouterPay memakai signature.
03Payment URLCustomer diarahkan ke payment page RouterPay.
04ui.webhookRouterPay mengirim status final ke merchant.

Autentikasi

Header dan signature wajib.

Setiap request merchant memakai Merchant Code, API Key, timestamp, dan HMAC signature. API Secret tidak pernah dikirim langsung.

Base URL Productionhttps://api.routerpay.co.id

Gunakan 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.pdf

Receipt 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.