Merchant API
Integrate StablePay from your server. Create card and wallet payments, issue refunds, send payouts, and receive signed result notifications. Every endpoint is a JSON POST, signed with HMAC-SHA256.
Base URLs
Use the test environment while you integrate, then switch the base URL and credentials for production. Every path in this reference is relative to the base URL and prefixed with /api/v1.
https://api-test.stablepay.linkhttps://api.stablepay.linkTest and production use separate merchant IDs and API secrets. A signature made with a test secret is rejected in production, and vice versa.
How a request works
- Build the JSON bodySerialize the request once. Field names are snake_case; amounts are decimal strings.
- Sign itHMAC-SHA256 over merchantID + "\n" + timestamp + "\n" + nonce + "\n" + rawBody, hex-encoded in lowercase.
- Send the POSTContent-Type: application/json plus X-MerchantID, X-Timestamp, X-Nonce and X-Sign headers.
- Verify the responseResponses carry the same four headers. Recompute the signature over the raw response body before you trust it.
Quick start
The fastest first payment is a hosted checkout: you send the order, StablePay returns a checkout_url, and the customer enters card details on our page. No card data touches your servers.
{
"payment_method": "CARD",
"merchant_order_no": "M202606240002",
"trans_amount": {
"currency": "USD",
"value": "49.99"
},
"notify_url": "https://merchant.example.com/callback/payment",
"return_url": "https://merchant.example.com/pay/success",
"trade_info": {
"goods_name": "VIP Membership",
"description": "Monthly subscription"
},
"metadata": "biz=member&uid=10001"
}{
"code": 0,
"msg": "success",
"data": {
"payment_method": "CARD",
"order_no": "O202606240002",
"merchant_order_no": "M202606240002",
"status": "PENDING",
"trans_amount": {
"currency": "USD",
"value": "49.99"
},
"psp_order_no": "",
"next_action": "CHECKOUT_REQUIRED",
"token": "ck_5c4f0f53c0e94d11",
"checkout_url": "https://cashier.example.com/pay/ck_5c4f0f53c0e94d11",
"created_at": "2026-06-24T10:05:00Z"
}
}Redirect the customer to checkout_url. When the payment completes, StablePay POSTs a signed notification to your notify_url, and the customer returns to return_url. Treat the notification — not the redirect — as the source of truth.
Endpoints
| Group | Method & path | Description |
|---|---|---|
| Payments | POST /api/v1/payments/create | Direct payment — card or wallet |
| Payments | POST /api/v1/payments/checkout | Hosted checkout payment (card) |
| Payments | POST /api/v1/payments/query | Query a payment order |
| Refunds | POST /api/v1/refunds/create | Create a refund |
| Refunds | POST /api/v1/refunds/query | Query a refund |
| Payouts | POST /api/v1/payouts/create | Create a payout |
| Payouts | POST /api/v1/payouts/query | Query a payout |
| Payouts | POST /api/v1/payouts/cancel | Cancel a payout (where supported) |
| Payouts | POST /api/v1/payouts/precheck | Balance and fee precheck |
Result notifications for payments, refunds, and payouts are delivered to the notify_url you pass when creating each object — see Webhooks.
Integration checklist
- Sign the raw bytes. Build the final body string first, sign that exact string, and send that exact string. Never re-serialize after signing.
- Verify everything you receive. API responses and webhook notifications are signed with the same scheme. Reject anything that fails verification.
- Make it idempotent.
merchant_order_no,merchant_refund_noandmerchant_payout_noare your idempotency keys. Notification handling must also tolerate duplicates. - Use strings for money. Amounts are decimal strings such as
"99.99". Never do floating-point arithmetic on them. - Notifications are final. Use the query endpoints for reconciliation and fallback, not as your primary status signal — even when a direct payment returns
SUCCESSsynchronously. - Answer webhooks with
success. Plain text, lowercase, no quotes, no JSON.
Credentials & environments
You receive a merchant ID (for example M123456) and an API secret for each environment during onboarding. The merchant ID goes in the X-MerchantID header; the secret is the HMAC key and must never leave your servers or be embedded in client-side code.
401 and 403 responses. See signature troubleshooting.