Migration from UTpay
This guide helps you move from the Legacy (UTpay) API to the Merchant API V1. The V1 API offers a simpler auth model and richer response objects while keeping amounts in normal Rupiah units.
You do not have to adopt V1 at the same time as the infrastructure migration. First change only the base URL and continue using the UTpay-compatible API. Adopt V1 later as a separate, tested security upgrade.
Side-by-Side Comparison
Authentication
| Legacy API | Merchant API V1 | |
|---|---|---|
| Orders | SHA-512 body signature | X-API-Key header |
| Disbursements | RSA X-SIGNATURE header | X-API-Key header |
| Credentials | legacy_client_id + legacy_client_secret + RSA key | api_key only |
V1 change: Add X-API-Key to every request. Order creation also uses X-Signature: sha256=<HMAC-SHA256 of the exact request body> when a webhook secret is configured.
Amount Format
| Legacy API | Merchant API V1 | |
|---|---|---|
| Create order | totalAmount: 50000 (IDR 50,000) | amount: 50000 (IDR 50,000) |
| Disbursement request | requestAmount: 500000 (IDR 500,000) | amount: 500000 |
| Balance | "5000000.00" (decimal string) | balance: 5000000 (Rupiah number) |
V1 amount rule: Rename the fields, but keep their Rupiah values unchanged. Do not multiply or divide by 100.
URL Path Changes
| Legacy path | V1 path | Method |
|---|---|---|
POST /api/order/create | POST /v1/orders | Create order |
POST /api/order/query | GET /v1/orders/:ref_code | Get order |
POST /api/disbursement/get-balance | GET /v1/balance | Wallet balance |
POST /api/disbursement/create | POST /v1/disbursements (channel: BANK) | Bank disbursement |
POST /api/disbursement/create-ewallet | POST /v1/disbursements (channel: EWALLET) | E-wallet disbursement |
POST /api/disbursement/query | GET /v1/disbursements/:ref_code | Get disbursement |
Request Field Mapping
Create Order
| Legacy field | V1 field | Notes |
|---|---|---|
clientId | (removed) | Identified by API key |
orderId | merchant_order_id | Same purpose |
totalAmount | amount | Same Rupiah value |
paymentType | payment_method | V1 order creation currently accepts QRIS only; do not migrate bank-transfer creation calls yet |
signature | (removed) | Auth via header |
customerName | customer_name | snake_case |
customerEmail | customer_email | snake_case |
customerPhone | customer_phone | snake_case |
notifyUrl | notify_url | snake_case |
returnUrl | return_url | snake_case |
Create Bank Disbursement
| Legacy field | V1 field | Notes |
|---|---|---|
clientId | (removed) | Auth via header |
orderId | idempotency_key | Same purpose |
bankCode | bank_short_code | Same values |
accountNumber | account_number | snake_case |
accountName | account_name | snake_case |
requestAmount | amount | Same Rupiah value |
channel | channel: "BANK" | Add explicitly |
Response Shape
Order
| Legacy field | V1 field |
|---|---|
data.refCode | data.ref_code |
data.orderId | data.merchant_order_id |
data.totalAmount | data.amount |
data.transactionTime | data.created_at (ISO 8601, UTC) |
data.paidAt | data.paid_at (ISO 8601, UTC) |
data.qrString | data.qris_string |
Webhook
| Legacy field | V1 field |
|---|---|
refCode | ref_code |
orderId | merchant_order_id |
totalAmount (IDR int) | amount (Rupiah number) |
status | status + event |
paidAt (WIB string) | paid_at (ISO 8601 UTC) |
Migration Checklist
Use this checklist to migrate without downtime:
- Obtain your V1
api_keyfrom the merchant dashboard - Run both integrations in parallel: Legacy for existing orders, V1 for new orders
- Update create-order code: use
X-API-Keyand rename fields; keep Rupiah values unchanged - Update webhook handler for the new field names;
amountis already Rupiah - Update disbursement code: use
X-API-Key, rename fields, update channel field - Update balance display: V1 returns
balancein Rupiah - Test full flow: create order → pay → receive webhook → query status
- Test disbursements: get balance → create → query
- Switch all new traffic to V1
- Monitor for errors for 1 week
- Notify your account manager to disable Legacy credentials (optional)
Frequently Asked Questions
Will my orderId (Legacy) conflict with merchant_order_id (V1)?
They share the same merchant order namespace in FlyPay. Reuse the identifier only when referring to the same logical transaction; use a new identifier for a new order.
Can I use the Legacy API for some orders and V1 for others?
Yes. Both APIs are fully active simultaneously. The source_api field on orders in the Admin Dashboard indicates which API was used.
What happens to my RSA key pair after migration?
Nothing — your keys remain stored. You only need them as long as you use the Legacy API.
Do settlement calculations change when I migrate?
No. MDR, fees, and net payout calculations are identical regardless of which API created the order.