ACAPAY DEVELOPER DOCS

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.

Base: /api/v1MDR 0.7% • Rp 1k/bulanPending approvalX-Request-ID

Overview

Semua endpoint merchant di /api/v1/*. Webhook pembayaran internal diproses otomatis (async). Response envelope konsisten. Approval admin wajib sebelum API aktif.

Auth ganda
Cookie + Bearer acp_sec_/acp_pub_
Ledger akurat
credit / hold / release / debit
Pending approval
pending → active → subscription
PRODUCTION: https://pay.xiamostore.xyz
API: /api/v1
WEBHOOK: Internal (otomatis)
MDR: 0.7% • Subscription: Rp 1.000 / 30 hari

Getting Started

  1. Buat akun merchant — Daftar di /register (status pending).
  2. Tunggu approval admin — Admin cek data via GET /api/v1/admin/merchants lalu POST /api/v1/admin/merchants/[id]/approve. Jika ditolak → status rejected.
  3. Dapatkan API Key — Setelah active, cek GET /api/v1/merchant/api-keys. Subscription 30 hari otomatis aktif (Rp 1.000/bulan).
  4. Buat invoice — POST /api/v1/invoices → dapat payment_url.
  5. Redirect customer ke payment_url (/pay/inv_xxx) untuk scan QRIS.
  6. Terima webhook — ACAPAY POST ke callback_url || merchant.webhook_url saat paid.
  7. Verifikasi status — GET /api/v1/invoices/[id] atau GET /api/v1/transactions/[id].
  8. Kelola payout & subscription — GET /api/v1/subscription & POST /api/v1/subscription/renew (potong saldo Rp 1.000).
Tanpa approval, semua pembuatan invoice akan 403 MERCHANT_PENDING_APPROVAL. Tanpa subscription aktif akan 402 SUBSCRIPTION_REQUIRED.

Authentication

Cookie acapay_sessionBearer acp_sec_* / acp_pub_*X-API-Key
Hanya gunakan Authorization: Bearer YOUR_SECRET_KEY. Jangan expose secret di client / repo. Contoh di bawah pakai placeholder acp_sec_xxx.
POST/api/v1/auth/registerPublic • 10/min
Daftar merchant → status pending, butuh approval admin. Set cookie.
bash
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"}'
POST/api/v1/auth/loginPublic • 15/min
Login → set httpOnly cookie 7 hari.
bash
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"}'
GET/api/v1/auth/meCookie / Bearer
Profil + merchant.status + balance. Jika pending → tetap 200 tapi status pending.
bash
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.

GET/api/v1/merchant/api-keysMerchant
Lihat public_key, webhook_url, webhook_secret. Jika pending → 403.
POST/api/v1/merchant/api-keysMerchant session-only
Regenerate acp_sec_* (Bearer ditolak).
GET/api/v1/subscriptionMerchant
Cek status langganan: price 1000, period 30 hari.
POST/api/v1/subscription/renewMerchant • active only
Perpanjang langganan — potong saldo Rp 1.000 (hold→debit).
bash
curl https://pay.xiamostore.xyz/api/v1/subscription -b cookies.txt
curl -X POST https://pay.xiamostore.xyz/api/v1/subscription/renew -b cookies.txt

Projects (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).

Maks 5 proyek / merchant. Detail proyek: Nama, Aktif (Ya/Tidak), Mode (Sandbox/Live), Fee By Merchant (Ya=Tidak? = fee ditanggung admin/merchant, Tidak=customer), Webhook URL, Total Transaksi (update jam 12 siang).
GET/api/v1/projectsMerchant session
List proyek milik merchant + total_transaksi (paid sum per project).
POST/api/v1/projectsMerchant • max 5
Buat proyek: body { name: "acamedia" } → is_active:true, mode:sandbox, fee_by_merchant:true, webhook_url:null. Return prj_sec_xxx sekali saja.
GET/api/v1/projects/[id]Merchant
Detail proyek (sesuai screenshot: Nama/Aktif/Mode/Fee By Merchant/Webhook/Nofbox/Total)
PATCH/api/v1/projects/[id]Merchant
Ubah: { name, is_active, mode:"live", fee_by_merchant:false, webhook_url, notif_nofbox }. Tombol Ubah per field.
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}'
Pakai di invoice: 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

POST/api/v1/invoicesMerchant • 30/min • active + subscribed
Buat invoice + transaksi pending + payment_url. 402 jika subscription habis, 403 jika pending. Jika merchant punya proyek, wajib pakai apikey proyek (prj_sec_xxx) atau project_id.
Request
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"
  }'
Headers: Authorization: Bearer acp_sec_xxx, Content-Type: application/json
Body: amount (int IDR), description, customer{name, email}, callback_url?, fee_bearer? (merchant|customer)
Status: 201 success, 402 SUBSCRIPTION_REQUIRED, 403 MERCHANT_PENDING_APPROVAL, 429 rate limit
Response 201
json
{
  "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"
  }
}
MDR 0.7% tanpa fixed: fee = ceil(150000*0.007)=1050. Jika customer tanggung → total 151050, merchant terima 150000.

Get Invoice

GET/api/v1/invoicesAuthenticated
List invoice. Admin = semua, merchant = miliknya.
GET/api/v1/invoices/[id]Public (detail) / Auth list
Detail invoice + transaction + merchant. Auto-expire jika lewat expired_at.
curl https://pay.xiamostore.xyz/api/v1/invoices/acp_inv_abc123
curl https://pay.xiamostore.xyz/api/v1/invoices -b cookies.txt

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

Butuh QRIS_STATIC_CODE di env. Jika kosong → 503 QRIS belum dikonfigurasi. Kirim Authorization: Bearer acp_sec_xxx agar QR tercatat untuk merchant (disarankan).
GET/api/v1/qris?amount=250000Bearer acp_sec_* • Cache private 300s
Generate QR dinamis: amount int IDR + Bearer merchant → image_data_url. Tanpa Bearer tetap bisa tapi tidak terikat merchant. invoiceId diabaikan.
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"}}
Flow Rekomendasi (Invoice + QRIS)
POST /api/v1/invoices + Bearer + external_id: ORDER-123 → payment_url: /pay/inv_xxx
/pay/inv_xxx fetch /api/v1/qris?amount=total + Bearer → QR
Payhook masuk → matching → paid
bash
# 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"
Cek Status dengan Custom ID
Jika invoice dibuat dengan external_id (mis. ORDER-123), cek status pakai ID itu langsung — tanpa simpan invoice_id.
bash
# 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"}}}
Catatan Payhook → QRIS
  • Bearer wajib agar QRIS & invoice terikat merchant → saldo masuk merchant tersebut saat payhook cocok.
  • Custom ID via external_id / external_reference saat create invoice → cek GET /api/v1/invoices/[ORDER-123] (fallback cari external_reference).
  • Nominal tertanam — customer scan tanpa input manual.
  • Cache 5 menit + Amount integer IDR.

Transactions

GET/api/v1/transactionsAuthenticated
List transaksi.
GET/api/v1/transactions/[id]Owner/admin
Detail transaksi.
bash
curl https://pay.xiamostore.xyz/api/v1/transactions -b cookies.txt

Payment Status

pendingprocessingpaidfailedexpired

Cek via GET /api/v1/invoices/[id] atau GET /api/v1/transactions/[id]. Webhook pembayaran akan update otomatis → ledger credit.

json
{
  "success": true,
  "data": { "invoice": {"id":"acp_inv_xxx","status":"paid","amount":150000,"fee":1050}, "transaction":{"id":"acp_tx_xxx","status":"paid"} }
}

Webhooks

webhook URL: invoice.callback_url || merchant.webhook_url (set via POST /api/v1/merchant/webhooks)
event: payment.paid, payment.failed, dll (saat ini payment.paid).
payload: JSON dengan invoice_id, transaction_id, amount, reference, paid_at
signature verification: HMAC-SHA256(header X-Acapay-Signature: sha256=HMAC-SHA256(webhook_secret, timestamp.rawBody)) + X-Acapay-Timestamp
timestamp/nonce: drift 5 menit, nonce TTL 10 menit
idempotency/event_id: event_id PayHook unik, duplicate → 200 tanpa proses ulang
response: merchant harus 200. Jika bukan 200 → retry 30s → 5m.
retry: otomatis hingga 3x, log di GET /api/v1/merchant/webhooks

Webhook Pembayaran (Incoming)

Notifikasi pembayaran masuk dari bank/e-wallet (QRIS) diproses otomatis secara async (202). Verifikasi HMAC + timestamp + nonce dilakukan internal.
bash
GET /api/webhooks/* → 200 health
bash
POST /api/webhooks/* (internal)
Headers: X-Signature, X-Timestamp, X-Nonce: sha256=HMAC(SECRET, timestamp.rawBody)
Payload Webhook
json
{ "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.

json
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" }
Verify Node.js
js
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

Merchant→Acapay API→Invoice→Customer Checkout→Payment→Notifikasi Bank→Acapay Webhook→Merchant
Merchant create invoice → dapat payment_url → customer scan QRIS → notifikasi bank masuk → Acapay cocokkan transaksi → ledger credit → callback ke merchant.

Payout — MANUAL 1×24 jam

Payout diproses MANUAL maksimal 1×24 jam. Bukan otomatis. Status: pending → processing (admin approve) → completed (transfer_reference) atau rejected/failed (saldo kembali).
POST/api/v1/payoutsAuthenticated • 10/min • active
Ajukan payout (available→held). Jika pending approval → 403.
bash
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"}'
GET/api/v1/payoutsAuthenticated
List payout. Admin plain, merchant masked.
GET/api/v1/payouts/[id]Owner/admin
Detail + sla_notice: Pencairan diproses secara manual maksimal 1x24 jam.
GET/api/v1/admin/payouts/[id]/approveAdmin
Approve → processing
POST/api/v1/admin/payouts/[id]/completeAdmin
Complete → completed (transfer_reference*)

Error Codes

Error CodeHTTPDescriptionSolution
VALIDATION_ERROR400Zod gagalPerbaiki payload sesuai schema
UNAUTHORIZED401Session/API key invalidLogin / ganti key
MERCHANT_PENDING_APPROVAL403Merchant menunggu approvalHubungi admin
SUBSCRIPTION_REQUIRED402Langganan habis (1k/bulan)POST /subscription/renew
FORBIDDEN403Role/owner salahCek role
INVOICE_NOT_FOUND404Invoice tidak adaCek id
RATE_LIMIT_EXCEEDED429Kena limitTunggu 1 menit
INTERNAL_ERROR500ExceptionKontak 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
Pelanggaran → akun tersuspend, saldo hangus / tidak dapat ditarik. Admin berhak menolak/menangguhkan payout terkait.

API Reference

OpenAPI JSON di /openapi.json. Semua endpoint akurat, tidak ada yang dikarang.

Auth
POST /auth/register, /auth/login, GET /auth/me
Invoices
POST/GET /invoices, GET /invoices/[id]
Transactions
GET /transactions, GET /transactions/[id]
Payouts
POST/GET /payouts, GET /payouts/[id]
Subscription
GET /subscription, POST /subscription/renew
Admin
GET /admin/merchants, POST /admin/merchants/[id]/approve|reject

Changelog

v1.2.0 — 2026-09-03
  • 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
v1.1.0 — 2026-01-15
  • Payout manual 1×24 jam
v1.0.0 — 2026-01-01
  • Rilis awal ACAPAY
Siap integrasi?
Butuh bantuan? Hubungi support atau buka OpenAPI Reference.