DocsDokumentasi API Reseller

Order & Transactions

Pembelian, wallet, idempotency, pengambilan isi produk, dan retry. Endpoint katalog yang dipakai sebelum membeli ada di bagian 2.

Bagian 3 dari 6Diperbarui

3.1 GET /balance

Shell
curl -sS "$BASE/balance" -H "Authorization: Bearer $AYOSEL_API_KEY"
JSON
{ "success": true, "data": { "balance": 250000, "currency": "IDR" } }

Akun yang belum pernah punya wallet mendapat balance: 0.

3.2 GET /balance/transactions

Mutasi saldo, terbaru lebih dulu. Memuat semua pergerakan saldo Anda, termasuk top up dan pembelian lewat bot.

ParameterKeterangan
limitBawaan 20, maksimum 100
offsetBawaan 0
Shell
curl -sS "$BASE/balance/transactions?limit=2" -H "Authorization: Bearer $AYOSEL_API_KEY"
JSON
{
  "success": true,
  "data": [
    {
      "id": "44444444-4444-4444-8444-444444444444",
      "type": "purchase",
      "direction": "debit",
      "amount": 36000,
      "balance_before": 286000,
      "balance_after": 250000,
      "reference_type": "order",
      "reference_id": "33333333-3333-4333-8333-333333333333",
      "description": "Pembelian INV-20261028-33333333",
      "created_at": "2026-10-28T03:15:07.482913Z"
    },
    {
      "id": "55555555-5555-4555-8555-555555555555",
      "type": "deposit",
      "direction": "credit",
      "amount": 300000,
      "balance_before": 0,
      "balance_after": 300000,
      "reference_type": "deposit",
      "reference_id": "77777777-7777-4777-8777-777777777777",
      "description": "Deposit via QRIS - Ref: CONTOH-0001",
      "created_at": "2026-10-27T08:00:00.120000Z"
    }
  ],
  "meta": { "total": 2, "limit": 2, "offset": 0 }
}
FieldKeterangan
typedeposit, purchase, refund, bonus_referral, manual_add, atau manual_deduct
directioncredit bila saldo bertambah, debit bila berkurang
amountNilai mutasi, selalu positif
balance_before, balance_afterSaldo sebelum dan sesudah mutasi
reference_type, reference_idRujukan mutasi, mis. order dengan id order. reference_id bisa null
descriptionKeterangan dari sistem
created_atWaktu mutasi

3.3 POST /orders

Membeli produk dengan saldo wallet. Pemotongan saldo, pembuatan order, pengambilan stok, dan pencatatan mutasi terjadi dalam satu transaksi database. Bila gagal, tidak ada yang terpotong.

Header:

HeaderKeterangan
AuthorizationBearer <API key>
Idempotency-KeyWajib. 8 sampai 64 karakter dari A-Z a-z 0-9 . _ : -. Lihat bagian 3.4
Content-Typeapplication/json

Body (JSON, maksimal 8 KB):

FieldWajibKeterangan
variant_idYaUUID varian
quantityTidakBilangan bulat 1 sampai 20. Bawaan 1

Hanya dua field itu yang diterima. Field lain, termasuk kode voucher, ditolak dengan 422 invalid_request. Voucher tidak didukung lewat API.

Shell
curl -sS -i -X POST "$BASE/orders" \
  -H "Authorization: Bearer $AYOSEL_API_KEY" \
  -H "Idempotency-Key: pesanan-20261028-0001" \
  -H "Content-Type: application/json" \
  -d '{"variant_id":"22222222-2222-4222-8222-222222222222","quantity":2}'

Respons sukses, 201 Created:

HTTP
HTTP/1.1 201 Created
Content-Type: application/json
X-Request-Id: 66666666-6666-4666-8666-666666666666
Cache-Control: no-store
X-RateLimit-Limit: 20
X-RateLimit-Remaining: 19
X-RateLimit-Reset: 60
JSON
{
  "success": true,
  "data": {
    "id": "33333333-3333-4333-8333-333333333333",
    "invoice_number": "INV-20261028-33333333",
    "status": "success",
    "product_id": "11111111-1111-4111-8111-111111111111",
    "product_name": "Akun Streaming Contoh",
    "variant_id": "22222222-2222-4222-8222-222222222222",
    "variant_name": "1 Bulan",
    "quantity": 2,
    "unit_price": 18000,
    "price_tier": "reseller",
    "bulk_discount_amount": 0,
    "final_amount": 36000,
    "currency": "IDR",
    "idempotency_key": "pesanan-20261028-0001",
    "created_at": "2026-10-28T03:15:07.482913Z",
    "paid_at": "2026-10-28T03:15:07.482913Z",
    "refunded_at": null,
    "terms": "Garansi 30 hari. Dilarang mengubah kata sandi.",
    "items": [
      { "content": "[email protected]|kata-sandi-contoh-1" },
      { "content": "[email protected]|kata-sandi-contoh-2" }
    ]
  }
}

Field respons:

FieldKeterangan
id, invoice_numberId order dan nomor invoice
statussuccess untuk pembelian yang selesai. Pembelian yang gagal tidak meninggalkan order
product_id, product_name, variant_id, variant_nameProduk dan varian yang dibeli
quantityJumlah unit
unit_priceHarga satuan yang ditagih
price_tierreseller atau public, tingkat harga yang dipakai
bulk_discount_amountPotongan diskon grosir, 0 bila tidak ada
final_amountTotal yang dipotong dari saldo: unit_price × quantity − bulk_discount_amount
currencySelalu IDR
idempotency_keyNilai Idempotency-Key yang Anda kirim
created_at, paid_atWaktu order dibuat dan dibayar
refunded_atTerisi bila toko merefund order, selain itu null
termsSyarat dan ketentuan varian. Dibaca saat request, bukan salinan saat order dibuat
itemsIsi produk yang terkirim, satu entri per unit, dalam urutan tetap

Hal yang perlu diperhatikan:

  • Simpan items segera. Isi produk hanya ada di respons API. Tidak dikirim ke bot Telegram, WhatsApp, maupun email. Bila terlewat, ambil lagi lewat GET /orders/{id}/items (bagian 3.8).
  • Harga yang ditagih adalah harga saat transaksi, sama dengan harga katalog untuk varian dan jumlah yang sama pada saat itu. Periksa unit_price dan final_amount di respons.
  • Total setelah diskon grosir yang kurang dari Rp 1 ditolak dengan 409 variant_unavailable.
  • Pembelian yang diulang dengan Idempotency-Key dan body yang sama dijawab 200 dengan header Idempotent-Replayed: true dan isi yang sama, tanpa pemotongan baru.

Contoh galat saldo kurang, 402:

JSON
{
  "success": false,
  "error": { "code": "insufficient_balance", "message": "Saldo tidak cukup." },
  "request_id": "66666666-6666-4666-8666-666666666666"
}

Contoh galat validasi, 422:

JSON
{
  "success": false,
  "error": {
    "code": "invalid_request",
    "message": "request tidak valid: quantity harus antara 1 dan 20"
  },
  "request_id": "66666666-6666-4666-8666-666666666666"
}

3.4 Idempotency-Key

Jaringan bisa putus setelah server memproses pembelian tetapi sebelum respons sampai. Idempotency-Key membuat pembelian yang diulang tidak memotong saldo dua kali.

Aturan key

  • Wajib ada di setiap POST /orders. Key yang hilang atau salah format dijawab 422 invalid_request dan tidak membuat order.
  • Format: 8 sampai 64 karakter dari A-Z a-z 0-9 . _ : -.
  • Satu key untuk satu niat beli. Buat key baru untuk setiap pembelian baru. UUID acak atau nomor pesanan di sistem Anda cocok.
  • Simpan key di sistem Anda sebelum mengirim request pertama, lalu pakai ulang key yang sama untuk setiap percobaan ulang pembelian itu.
  • Key berlaku tanpa batas waktu selama ordernya ada. Key milik reseller lain tidak pernah bertabrakan dengan key Anda.

Hasil per kondisi

KondisiHasil
Key baru201 dan order baru
Key sama, variant_id dan quantity sama, order sudah ada200 dengan header Idempotent-Replayed: true, order dan items yang sama, tanpa pemotongan baru
Key sama, tetapi variant_id atau quantity berbeda422 idempotency_key_reuse
Key sama, request sebelumnya masih diproses409 request_in_progress dengan Retry-After: 1
Pembelian sebelumnya gagal tanpa membuat orderKegagalan tidak disimpan. Request ulang dengan key yang sama diproses dari awal

quantity yang tidak dikirim dihitung sebagai 1, jadi request pertama tanpa quantity dan percobaan ulang dengan "quantity": 1 dianggap sama.

Mengirim request kembar bersamaan dengan key dan body yang sama menghasilkan paling banyak satu order dan satu pemotongan saldo.

3.5 Mencoba ulang

Kode galat dibagi dua: yang aman diulang dengan key yang sama dan yang tidak boleh diulang begitu saja.

Kode yang aman dicoba ulang dengan key yang sama

KodeHTTPCara mengulang
insufficient_stock409Tunggu sebentar lalu ulangi, beberapa kali saja. Kode ini bisa muncul sesaat ketika sisa stok sedang dipegang transaksi lain. Bila tetap muncul, stok memang habis: berhenti dan tampilkan stok habis ke pelanggan Anda
request_in_progress409Tunggu sesuai Retry-After (1 detik) lalu ulangi. Request pertama masih berjalan
server_busy503Tunggu sesuai Retry-After (1 detik) lalu ulangi
rate_limited429Tunggu sesuai Retry-After lalu ulangi

Request yang menerima salah satu dari keempat kode itu tidak membuat order dan tidak memotong saldo, jadi mengulang dengan key yang sama aman. Hasil ulangannya bisa 201 (baru diproses) atau 200 dengan Idempotent-Replayed: true (sudah diproses oleh request sebelumnya). Retry-After pada rate_limited biasanya hitungan detik, tetapi bisa sampai 300 detik bila yang terlampaui adalah batas kegagalan autentikasi.

Mengulang dengan key yang sama juga aman untuk timeout atau koneksi putus (hasil tidak diketahui), 500 internal_error, dan 503 service_unavailable. Cek header Idempotent-Replayed untuk tahu apakah ini order yang sama.

Kode yang tidak boleh diulang begitu saja

KodeYang dilakukan
insufficient_balanceTop up saldo dulu. Setelah itu boleh mencoba lagi, karena kegagalan tidak disimpan
stock_reservedStok ditahan pembeli lain. Coba lagi dalam beberapa menit, bukan dalam hitungan detik
invalid_request, variant_not_found, variant_unavailableRequest atau varian bermasalah. Perbaiki dulu, mengulang yang sama menghasilkan galat yang sama
idempotency_key_reuseKey ini sudah dipakai untuk pembelian lain. Bila ini pembelian baru, pakai key baru. Bila ini pembelian yang sama, kirim body yang sama dengan sebelumnya
invalid_api_key, ip_not_allowedPerbaiki key atau allowlist lebih dulu. Setiap pengulangan menambah hitungan kegagalan autentikasi (20 per 5 menit per IP)
account_suspended, api_access_disabled, channel_disabled, plan_not_eligibleMasalahnya ada di akun atau toko. Hubungi toko, mengulang tidak membantu

Pola mencoba ulang

  1. Buat key dan simpan.
  2. Kirim POST /orders.
  3. Bila berhasil (201 atau 200), simpan items dan selesai.
  4. Bila kode termasuk daftar bagian 3.5, atau tidak ada respons, tunggu lalu ulangi dengan key yang sama. Pakai Retry-After bila ada, selain itu jeda yang membesar (misalnya 0,5; 1; 2; 4 detik) ditambah sedikit acak. Batasi jumlah percobaan, misalnya 5.
  5. Bila semua percobaan habis dan hasilnya tidak diketahui, cek GET /orders?idempotency_key=<key> sebelum menyerah. Bila order ada, ambil isinya lewat GET /orders/{id}/items.

Contoh kode ada di bagian 6.1.

3.6 GET /orders

Daftar order yang dibuat lewat API oleh akun Anda, terbaru lebih dulu, tanpa items. Order dari bot atau website tidak ikut.

ParameterKeterangan
statuspending, success, failed, atau expired. Nilai lain dijawab 422 invalid_request
from, toBatas waktu, YYYY-MM-DD atau RFC 3339, dalam UTC. Tanggal tanpa jam berarti sehari penuh, jadi to=2026-10-28 mencakup seluruh 28 Oktober. Format lain dijawab 422 invalid_request
idempotency_keyNilai Idempotency-Key yang Anda kirim saat membeli. Hasilnya 0 atau 1 order
limitBawaan 20, maksimum 100
offsetBawaan 0
Shell
curl -sS "$BASE/orders?from=2026-10-01&to=2026-10-28&limit=20" \
  -H "Authorization: Bearer $AYOSEL_API_KEY"
JSON
{
  "success": true,
  "data": [
    {
      "id": "33333333-3333-4333-8333-333333333333",
      "invoice_number": "INV-20261028-33333333",
      "status": "success",
      "product_id": "11111111-1111-4111-8111-111111111111",
      "product_name": "Akun Streaming Contoh",
      "variant_id": "22222222-2222-4222-8222-222222222222",
      "variant_name": "1 Bulan",
      "quantity": 2,
      "unit_price": 18000,
      "price_tier": "reseller",
      "bulk_discount_amount": 0,
      "final_amount": 36000,
      "currency": "IDR",
      "idempotency_key": "pesanan-20261028-0001",
      "created_at": "2026-10-28T03:15:07.482913Z",
      "paid_at": "2026-10-28T03:15:07.482913Z",
      "refunded_at": null
    }
  ],
  "meta": { "total": 1, "limit": 20, "offset": 0 }
}

Field setiap order sama dengan field order di POST /orders, tanpa terms dan items.

idempotency_key berguna bila respons POST /orders hilang di jalan:

Shell
curl -sS "$BASE/orders?idempotency_key=pesanan-20261028-0001" \
  -H "Authorization: Bearer $AYOSEL_API_KEY"

meta.total bernilai 1 bila order itu ada dan 0 bila tidak ada.

3.7 GET /orders/{id}

Detail satu order, tanpa items.

Shell
curl -sS "$BASE/orders/33333333-3333-4333-8333-333333333333" \
  -H "Authorization: Bearer $AYOSEL_API_KEY"

data berbentuk sama dengan satu elemen di GET /orders. Order yang tidak ada, milik pelanggan lain, atau dibuat lewat bot atau website dijawab 404 order_not_found, dengan jawaban yang identik untuk ketiga kasus.

3.8 GET /orders/{id}/items

Isi produk yang terkirim untuk satu order, ditambah syarat dan ketentuannya. Dipakai bila Anda perlu mengambil ulang produk, misalnya untuk pelanggan yang kehilangan produknya.

Shell
curl -sS "$BASE/orders/33333333-3333-4333-8333-333333333333/items" \
  -H "Authorization: Bearer $AYOSEL_API_KEY"
JSON
{
  "success": true,
  "data": {
    "order_id": "33333333-3333-4333-8333-333333333333",
    "terms": "Garansi 30 hari. Dilarang mengubah kata sandi.",
    "items": [
      { "content": "[email protected]|kata-sandi-contoh-1" },
      { "content": "[email protected]|kata-sandi-contoh-2" }
    ]
  }
}

Isi produk tetap bisa diambil setelah order direfund (refunded_at terisi). Pemilik order dicek seperti di GET /orders/{id}, dan jawaban bila bukan milik Anda adalah 404 order_not_found.

Butuh bantuan dengan integrasi?

Sertakan request_id dari respons galat, lalu hubungi tim Ayosel lewat Telegram atau WhatsApp, atau email [email protected].