Dokumentasi

Rest API v1

Integrasi Gopay Merchant dan Shopee Partner lewat satu API yang konsisten. Semua endpoint menerima dan mengembalikan JSON.

Stabil Base URL: https://pay.odzreshop.id/api/v1

Quickstart

  1. 1.Daftar akun lalu masuk ke dashboard.
  2. 2.Buka menu Proyek → buat proyek baru (nama, QRIS, merchant).
  3. 3.Salin API key yang muncul sekali saja.
  4. 4.Panggil endpoint dengan header X-API-Key.

Contoh panggilan pertama (cURL)

curl -X POST https://pay.odzreshop.id/api/v1/transaction/create \
  -H "X-API-Key: odzre_live_xxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 25000,
    "customerName": "Budi Santoso",
    "description": "Pembayaran pesanan #1234"
  }'

Autentikasi

Semua endpoint memerlukan API key. Sertakan pada salah satu dari:

Header (disarankan)

X-API-Key: odzre_live_xxx

Bearer token

Authorization: Bearer odzre_live_xxx

API key hanya tampil satu kali saat dibuat. Simpan di variabel lingkungan server — jangan pernah di kode frontend atau repositori.

Rate Limit & Langganan

Akses API memerlukan langganan aktif (paket di menu Tagihan dashboard). Batas permintaan per menit mengikuti paket Anda — total request tidak dibatasi. Setiap response menyertakan header:

X-RateLimit-Limit: 60        # sesuai paket Anda
X-RateLimit-Remaining: 57
Retry-After: 14        # hanya saat terkena limit (HTTP 429)

Saat langganan kedaluwarsa, semua request API ditolak dengan 403 SUBSCRIPTION_REQUIRED — proyek dan API key tetap utuh, langsung aktif kembali setelah diperpanjang.

GET /api/v1/profile

Mengembalikan informasi akun dan proyek pemilik API key.

Contoh — JavaScript (fetch)

const res = await fetch('https://pay.odzreshop.id/api/v1/profile', {
  headers: { 'X-API-Key': process.env.ODZRE_API_KEY },
});
const json = await res.json();
console.log(json.data.project.name);

Response 200

{
  "success": true,
  "data": {
    "user": {
      "id": "uuid",
      "email": "john.doe@example.com",
      "full_name": "John Doe",
      "company": "Doe Digital",
      "created_at": "2026-01-15T08:00:00.000Z"
    },
    "project": {
      "id": "uuid",
      "name": "Toko Sepatu Online",
      "merchant": "gopay"
    },
    "scopes": ["profile:read", "transaction:write", "transaction:read"]
  },
  "timestamp": "2026-09-13T03:00:00.000Z"
}
POST /api/v1/transaction/create

Membuat transaksi QRIS baru. Status awal pending.

Parameter body

FieldTipeWajibKeterangan
amountintegerYaNominal dalam rupiah (100 – 50.000.000)
orderIdstringTidakID pesanan Anda (4-64 char). Kosong = dibuat otomatis
customerNamestringTidakNama pelanggan
customerEmailstringTidakEmail pelanggan
customerPhonestringTidakNomor telepon pelanggan
descriptionstringTidakKeterangan transaksi (maks. 200 char)
expiryMinutesintegerTidakMasa berlaku (5–1440 menit, default 30)
callbackUrlstringTidakURL webhook saat status berubah — lihat Webhooks

Contoh — PHP (cURL)

<?php
$ch = curl_init('https://pay.odzreshop.id/api/v1/transaction/create');
curl_setopt_array($ch, [
  CURLOPT_POST => true,
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => [
    'X-API-Key: ' . getenv('ODZRE_API_KEY'),
    'Content-Type: application/json',
  ],
  CURLOPT_POSTFIELDS => json_encode(['amount' => 25000]),
]);
$res = json_decode(curl_exec($ch), true);
echo $res['data']['qris_string'];

Response 201

{
  "success": true,
  "data": {
    "order_id": "ODZ-260913-K3P6W6CHKM",
    "status": "pending",
    "merchant": "shopee",
    "requested_amount": 25000,
    "unique_code": 137,
    "amount": 25137,
    "fee": 0,
    "qris_string": "00020101021126...",
    "customer": { "name": "Budi Santoso", "email": null, "phone": null },
    "description": "Pembayaran pesanan #1234",
    "provider_transaction_id": null,
    "paid_at": null,
    "expires_at": "2026-09-13T03:30:00.000Z",
    "created_at": "2026-09-13T03:00:00.000Z"
  },
  "timestamp": "2026-09-13T03:00:00.000Z"
}

Nominal final amount = requested_amount + unique_code (Rp 1–999, agar transaksi nominal sama tidak tertukar). Tampilkan qris_string sebagai QR code di aplikasi Anda — pembayaran terdeteksi otomatis ≤10 detik.

GET /api/v1/transaction/status/:orderId

Memeriksa status transaksi. Endpoint ini membaca status terbaru dari database — worker kami mengecek pembayaran ke merchant setiap 10 detik, sehingga perubahan status (mis. paid) tersedia otomatis tanpa perlu polling dari sisi Anda.

Contoh

curl -H "X-API-Key: odzre_live_xxx" \
  https://pay.odzreshop.id/api/v1/transaction/status/ODZ-260913-K3P6W6CHKM

Nilai status yang mungkin

pending — menunggu pembayaran paid — pembayaran berhasil expired — melewati batas waktu failed — gagal diproses refunded — dana dikembalikan

Webhooks

Saat pembayaran terdeteksi, sistem mengirim notifikasi otomatis (webhook) ke URL Anda berisi detail transaksi — sehingga aplikasi Anda tidak perlu polling berkala.

1. Cara Mengatur Webhook

Ada dua cara menentukan tujuan webhook — bisa dipakai salah satu atau keduanya (jika keduanya terisi, callbackUrl per-transaksi yang dipakai):

  1. Per proyek — isi field Webhook URL di halaman detail proyek. Berlaku untuk semua transaksi proyek tersebut.
  2. Per transaksi — sertakan parameter callbackUrl saat memanggil POST /transaction/create.

2. Kapan Webhook Terkirim

Event transaction.paid dikirim tepat setelah pembayaran terverifikasi (deteksi mutasi berjalan tiap 10 detik, jadi webhook tiba maksimal ±10 detik setelah pelanggan membayar). Sistem kirim POST dengan body JSON:

POST https://app-anda.com/webhook
Content-Type: application/json
X-Odzre-Event: transaction.paid
X-Odzre-Signature: 5f8a3c...hexdigest
User-Agent: ODZRESHOP-Webhook/1.0

{
  "event": "transaction.paid",
  "order_id": "ORD-7K2M9XQ4",
  "status": "paid",
  "merchant": "gopay",
  "requested_amount": 100000,
  "unique_code": 57,
  "amount": 100057,
  "provider_transaction_id": "GP-TRX-88291037",
  "paid_at": "2026-09-26T08:15:33.120Z",
  "expires_at": "2026-09-26T09:00:00.000Z",
  "created_at": "2026-09-26T08:02:11.004Z",
  "timestamp": "2026-09-26T08:15:34.002Z"
}
FieldTipeDeskripsi
eventstringSelalu transaction.paid
order_idstringID transaksi Anda — kunci untuk mencocokkan order di sisi Anda
statusstringpaid
merchantstringKanal pembayaran: gopay atau shopee
requested_amountnumberNominal asli sebelum kode unik
unique_codenumberKode unik yang ditambahkan
amountnumberNominal final yang dibayar (= requested + unique_code)
provider_transaction_idstring | nullID referensi dari Shopee/GoPay
paid_atstring | nullWaktu pembayaran (ISO 8601 UTC)
expires_atstringBatas waktu bayar order
created_atstringWaktu order dibuat
timestampstringWaktu webhook dibuat

3. Verifikasi Signature

Setiap webhook di-sign dengan HMAC-SHA256 atas raw body JSON, pada header X-Odzre-Signature. Kunci yang dipakai adalah SESSION_SECRET server (hubungi admin untuk mendapatkannya — bagikan hanya lewat kanal aman). Selalu verifikasi sebelum memproses, dan bandingkan dengan perbandingan aman.

// Node.js (Express)
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const expected = crypto
    .createHmac('sha256', SESSION_SECRET)
    .update(req.body)                 // raw Buffer — JANGAN re-serialize
    .digest('hex');
  const received = req.headers['x-odzre-signature'] || '';
  const ok = expected.length === received.length &&
    crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(received));
  if (!ok) return res.status(401).end();

  const payload = JSON.parse(req.body.toString());
  if (payload.event === 'transaction.paid') {
    // tandai order payload.order_id sebagai LUNAS di database Anda
  }
  res.status(200).end();
});
<?php // PHP
$raw    = file_get_contents('php://input');   // raw body persis
$expected = hash_hmac('sha256', $raw, $SESSION_SECRET);
$received = $_SERVER['HTTP_X_ODZRE_SIGNATURE'] ?? '';
if (!hash_equals($expected, $received)) {
  http_response_code(401); exit;
}
$payload = json_decode($raw, true);
if ($payload['event'] === 'transaction.paid') {
  // tandai order $payload['order_id'] sebagai LUNAS
}
http_response_code(200);
Penting: hitung HMAC dari raw body yang persis diterima. Jika Anda mem-parse JSON lalu meng-encode ulang, urutan/spasi bisa berubah dan signature tidak akan pernah cocok.

4. Aturan Retry

  • Endpoint harus membalas status 2xx dalam 10 detik — proses berat sebaiknya dilakukan setelah merespons (queue).
  • Gagal jaringan / HTTP ≥ 500 → dicoba ulang otomatis, maksimal 3 percobaan.
  • Respons 4xx (termasuk 401/404) dianggap gagal final — tidak di-retry.

5. Batasan & Keamanan

  • Hanya http/https dengan hostname yang mengarah ke IP publik — localhost/private network ditolak otomatis.
  • Redirect tidak diikuti — pastikan URL endpoint langsung menjawab.
  • Webhook dikirim dari server kami; untuk keamanan, validasi signature (bagian 3) — jangan hanya mengandalkan URL rahasia.

Kode Error

Semua error mengikuti format yang sama:

{
  "success": false,
  "error": {
    "code": "INVALID_API_KEY",
    "message": "API key tidak valid, sudah dicabut, atau proyeknya tidak aktif."
  },
  "timestamp": "2026-09-13T03:00:00.000Z"
}
HTTPCodeArti
401MISSING_API_KEYHeader X-API-Key tidak disertakan
401MALFORMED_API_KEYFormat key salah
401INVALID_API_KEYKey tidak terdaftar / dicabut
403INSUFFICIENT_SCOPEKey tidak punya izin endpoint
404NOT_FOUNDTransaksi tidak ditemukan
409DUPLICATE_ORDER_IDorderId sudah pernah dipakai
409MERCHANT_NOT_CONNECTEDProyek merchant belum terhubung / perlu sambung ulang
403SUBSCRIPTION_REQUIREDLangganan tidak aktif / kedaluwarsa — perpanjang di dashboard
403PROJECT_LIMIT_REACHEDBatas jumlah proyek paket tercapai
422VALIDATION_ERRORParameter tidak valid
429RATE_LIMIT_EXCEEDEDBatas permintaan terlampaui
502PROVIDER_ERRORProvider menolak permintaan
503UNIQUE_AMOUNT_UNAVAILABLESemua variasi nominal unik sedang terpakai
500INTERNAL_ERRORKesalahan internal server

Siap Mencoba?

Buat akun gratis dan dapatkan API key dalam waktu kurang dari lima menit.