Dokumentasi API ACAPAY
Payment Gateway Indonesia — QRIS dinamis real-time, ledger ganda (hold/release), payout manual 1×24 jam, approval merchant, subscription Rp 1.000/bulan, MDR 0.7% tanpa biaya fixed. Base /api/v1 • JSON • HMAC-SHA256.
Overview
Semua endpoint merchant di /api/v1/*. Webhook pembayaran internal diproses otomatis (async). Response envelope konsisten. Approval admin wajib sebelum API aktif.
API: /api/v1
WEBHOOK: Internal (otomatis)
MDR: 0.7% • Subscription: Rp 1.000 / 30 hari
Getting Started
- Buat akun merchant — Daftar di /register (status
pending). - Tunggu approval admin — Admin cek data via
GET /api/v1/admin/merchantslaluPOST /api/v1/admin/merchants/[id]/approve. Jika ditolak → statusrejected. - Dapatkan API Key — Setelah active, cek
GET /api/v1/merchant/api-keys. Subscription 30 hari otomatis aktif (Rp 1.000/bulan). - Buat invoice —
POST /api/v1/invoices→ dapatpayment_url. - Redirect customer ke
payment_url (/pay/inv_xxx)untuk scan QRIS. - Terima webhook — ACAPAY POST ke
callback_url || merchant.webhook_urlsaat paid. - Verifikasi status —
GET /api/v1/invoices/[id]atauGET /api/v1/transactions/[id]. - Kelola payout & subscription —
GET /api/v1/subscription&POST /api/v1/subscription/renew(potong saldo Rp 1.000).
MERCHANT_PENDING_APPROVAL. Tanpa subscription aktif akan 402 SUBSCRIPTION_REQUIRED.Authentication
Authorization: Bearer YOUR_SECRET_KEY. Jangan expose secret di client / repo. Contoh di bawah pakai placeholder acp_sec_xxx.curl -X POST https://pay.xiamostore.xyz/api/v1/auth/register \
-H "Content-Type: application/json" \
-d '{ "name":"Budi Santoso","email":"budi@toko.com","password":"password123","business_name":"Toko Budi","whatsapp":"6281234567890","store_url":"tokobudi.com","purpose":"Jualan QRIS"}'curl -X POST https://pay.xiamostore.xyz/api/v1/auth/login -c cookies.txt -H "Content-Type: application/json" -d '{"email":"budi@toko.com","password":"password123"}'curl https://pay.xiamostore.xyz/api/v1/auth/me -b cookies.txt
# atau
curl https://pay.xiamostore.xyz/api/v1/auth/me -H "Authorization: Bearer acp_sec_xxx"API Keys
Pasangan kunci: acp_sec_* (secret, hashed) & acp_pub_* (public). Subscription Rp 1.000/30 hari aktif setelah approve.
curl https://pay.xiamostore.xyz/api/v1/subscription -b cookies.txt
curl -X POST https://pay.xiamostore.xyz/api/v1/subscription/renew -b cookies.txtProjects (1 merchant = max 5 proyek)
1 merchant harus buat proyek dulu untuk dapat API key proyek (prj_sec_xxx / prj_pub_xxx). Semua transaksi masuk ke proyek sesuai apikey-nya. Fee bisa diatur per proyek. Notif ke Nofbox diabaikan (selalu Ya).
curl https://pay.xiamostore.xyz/api/v1/projects -b cookies.txt
curl -X POST https://pay.xiamostore.xyz/api/v1/projects -b cookies.txt -H "Content-Type: application/json" -d '{"name":"acamedia"}'
# → {"public_key":"prj_pub_xxx","secret_key":"prj_sec_xxx"}
curl -X PATCH https://pay.xiamostore.xyz/api/v1/projects/prj_xxx -b cookies.txt -d '{"fee_by_merchant":false}'Authorization: Bearer prj_sec_xxx (apikey proyek) → transaksi otomatis masuk ke proyek tersebut. Jika merchant sudah punya proyek, POST /invoices tanpa project apikey akan 400 Wajib pilih proyek. Fee proyek (fee_by_merchant) override fee_bearer invoice.Create Invoice
curl -X POST https://pay.xiamostore.xyz/api/v1/invoices \
-H "Authorization: Bearer acp_sec_xxx" \
-H "Content-Type: application/json" \
-d '{
"amount": 150000,
"description": "Langganan Pro",
"customer": {"name":"Budi","email":"budi@acme.id"},
"callback_url": "https://acme.id/webhook/acapay",
"fee_bearer": "merchant"
}'{
"success": true,
"data": {
"invoice_id": "acp_inv_abc123",
"transaction_id": "acp_tx_abc123",
"amount": 150000,
"fee": 1050,
"fee_bearer": "merchant",
"total": 150000,
"net_amount": 148950,
"payment_url": "/pay/acp_inv_abc123",
"expired_at": "2026-01-15T11:30:00Z"
}
}Get Invoice
curl https://pay.xiamostore.xyz/api/v1/invoices/acp_inv_abc123
curl https://pay.xiamostore.xyz/api/v1/invoices -b cookies.txtQRIS Dinamis (Bearer Merchant)
QRIS dinamis butuh Bearer token merchant agar terikat ke merchant Anda — payhook masuk + matching → saldo masuk ke merchant tersebut. QRIS_STATIC_CODE + amount → image_data_url base64. Dipakai di /pay/[invoiceId] (fetch /api/v1/qris?amount=total). Support GoPay • OVO • DANA • ShopeePay • LinkAja • m-Banking.
QRIS_STATIC_CODE di env. Jika kosong → 503 QRIS belum dikonfigurasi. Kirim Authorization: Bearer acp_sec_xxx agar QR tercatat untuk merchant (disarankan).curl "https://pay.xiamostore.xyz/api/v1/qris?amount=250000" \
-H "Authorization: Bearer acp_sec_xxx"
# → {"success":true,"data":{"image_data_url":"data:image/png;base64,iVBOR...","amount":250000,"merchant_id":"mch_xxx"}}/pay/inv_xxx fetch /api/v1/qris?amount=total + Bearer → QR
Payhook masuk → matching → paid
# 1. Buat invoice dengan custom ID
curl -X POST https://pay.xiamostore.xyz/api/v1/invoices \
-H "Authorization: Bearer acp_sec_xxx" \
-d '{"amount":50000,"description":"Order #123","customer":{"name":"Andi","email":"a@x.id"},"external_id":"ORDER-123"}'
# → {"invoice_id":"acp_inv_xxx","transaction_id":"acp_tx_xxx"}
# 2. Generate QRIS (Bearer agar terikat merchant)
curl "https://pay.xiamostore.xyz/api/v1/qris?amount=50000" -H "Authorization: Bearer acp_sec_xxx"external_id (mis. ORDER-123), cek status pakai ID itu langsung — tanpa simpan invoice_id.# Polling status (pakai invoice_id atau external_id)
curl https://pay.xiamostore.xyz/api/v1/invoices/ORDER-123 \
-H "Authorization: Bearer acp_sec_xxx"
# atau original id
curl https://pay.xiamostore.xyz/api/v1/invoices/acp_inv_xxx
# Response
{"success":true,"data":{"invoice":{"id":"acp_inv_xxx","status":"paid","amount":50000},"transaction":{"status":"paid","external_reference":"ORDER-123"}}}- Bearer wajib agar QRIS & invoice terikat merchant → saldo masuk merchant tersebut saat payhook cocok.
- Custom ID via
external_id / external_referencesaat create invoice → cekGET /api/v1/invoices/[ORDER-123](fallback cari external_reference). - Nominal tertanam — customer scan tanpa input manual.
- Cache 5 menit + Amount integer IDR.
Transactions
curl https://pay.xiamostore.xyz/api/v1/transactions -b cookies.txtPayment Status
Cek via GET /api/v1/invoices/[id] atau GET /api/v1/transactions/[id]. Webhook pembayaran akan update otomatis → ledger credit.
{
"success": true,
"data": { "invoice": {"id":"acp_inv_xxx","status":"paid","amount":150000,"fee":1050}, "transaction":{"id":"acp_tx_xxx","status":"paid"} }
}Webhooks
invoice.callback_url || merchant.webhook_url (set via POST /api/v1/merchant/webhooks)payment.paid, payment.failed, dll (saat ini payment.paid).X-Acapay-Signature: sha256=HMAC-SHA256(webhook_secret, timestamp.rawBody)) + X-Acapay-TimestampGET /api/v1/merchant/webhooksWebhook Pembayaran (Incoming)
GET /api/webhooks/* → 200 healthPOST /api/webhooks/* (internal)
Headers: X-Signature, X-Timestamp, X-Nonce: sha256=HMAC(SECRET, timestamp.rawBody){ "event_id":"evt_abc123","event_type":"payment.incoming","amount":150000,"source":"BCA","reference":"ACAPAY-7823","timestamp":"2026-01-15T10:30:00Z" }Merchant Callback
Saat paid, Acapay POST ke merchant dengan header X-Acapay-Signature & X-Acapay-Timestamp.
POST https://toko.com/webhook/acapay
X-Acapay-Signature: sha256=<hex>
X-Acapay-Timestamp: 1705300000
{ "event":"payment.paid","invoice_id":"acp_inv_xxx","transaction_id":"acp_tx_xxx","amount":150000,"reference":"ACAPAY-7823" }import crypto from "crypto";
function verify(req, secret){
const sig=req.headers["x-acapay-signature"]; const ts=req.headers["x-acapay-timestamp"];
const expected="sha256="+crypto.createHmac("sha256",secret).update(ts+"."+JSON.stringify(req.body)).digest("hex");
return crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected));
}Payment Flow
Payout — MANUAL 1×24 jam
curl -X POST https://pay.xiamostore.xyz/api/v1/payouts -b cookies.txt -H "Content-Type: application/json" -d '{"amount":500000,"bank_name":"BCA","account_number":"1234567890","account_name":"Budi"}'Error Codes
| Error Code | HTTP | Description | Solution |
|---|---|---|---|
| VALIDATION_ERROR | 400 | Zod gagal | Perbaiki payload sesuai schema |
| UNAUTHORIZED | 401 | Session/API key invalid | Login / ganti key |
| MERCHANT_PENDING_APPROVAL | 403 | Merchant menunggu approval | Hubungi admin |
| SUBSCRIPTION_REQUIRED | 402 | Langganan habis (1k/bulan) | POST /subscription/renew |
| FORBIDDEN | 403 | Role/owner salah | Cek role |
| INVOICE_NOT_FOUND | 404 | Invoice tidak ada | Cek id |
| RATE_LIMIT_EXCEEDED | 429 | Kena limit | Tunggu 1 menit |
| INTERNAL_ERROR | 500 | Exception | Kontak support |
Idempotency
Gunakan X-Request-ID (invoice/payout) dan event_id (webhook internal). Duplicate diproses 1x, TTL 24 jam.
- event_id webhook unik → duplicate return 200 tanpa ledger ulang (nonce TTL 10m, drift 5m)
- Retry aman: kirim ulang request sama → hasil identik
Security
- HTTPS — wajib di production
- Jangan expose secret — env vars, jangan commit
- Signature verification — HMAC-SHA256 timestamp.rawBody
- Replay protection — nonce 10m, timestamp 5m
- Webhook verification — X-Acapay-Signature
- API key security — rotate via POST /merchant/api-keys
Layanan Dilarang
Produk / layanan berikut tidak boleh diproses via ACAPAY. Pelanggaran berakibat akun tersuspend, saldo hangus atau tidak dapat ditarik.
- Judi online, game poker/gaple, dan sejenisnya
- Ponzi, investasi bodong, dan sejenisnya
- Scam, penipuan, phishing, dll
- Pornografi dan sejenisnya
- Tools/jasa/produk yang bertujuan mengakali, membypass atau melanggar ketentuan suatu sistem
- Produk/layanan yang berpotensi melanggar hukum atau hak kekayaan intelektual
API Reference
OpenAPI JSON di /openapi.json. Semua endpoint akurat, tidak ada yang dikarang.
Changelog
- Merchant approval: pending → admin approve/reject
- MDR 0.7% tanpa fixed (sebelumnya 0.6%+110)
- Subscription API Rp 1.000 / 30 hari
- Docs developer Stripe-style rilis
- Payout manual 1×24 jam
- Rilis awal ACAPAY

