API Distributor
Antarmuka host-to-host untuk melakukan pembelian produk digital secara otomatis dari sistem Anda sendiri. Ditujukan untuk mitra distributor yang sudah memiliki saldo di AM08SHOP.
Base URL
Produksi https://api.am08shop.com/v1 Sandbox — belum tersedia, nama sudah dicadangkan https://sandbox-api.am08shop.com/v1
Seluruh endpoint memakai metode POST dan bertukar data JSON — termasuk endpoint yang sifatnya hanya membaca, seperti cek saldo. Ini disengaja: tanda tangan permintaan menyertakan ringkasan isi body, sehingga aturannya jadi seragam dan tidak ada kasus khusus yang bisa salah diterapkan.
Prasyarat
Empat hal berikut harus terpenuhi sebelum permintaan pertama Anda diterima.
| Syarat | Keterangan |
|---|---|
| Role Distributor | Akun Anda harus berrole Distributor. Harga yang berlaku di API adalah harga tier Distributor, bukan harga etalase. |
| Saldo | Order hanya bisa dibayar dengan saldo. Tidak ada metode pembayaran lain di API — isi saldo lebih dulu melalui halaman member. |
| PIN | Akun harus sudah memiliki PIN. PIN dikirim pada setiap permintaan order. |
| Whitelist IP | Permintaan hanya diterima dari alamat IP yang Anda daftarkan di halaman Kredensial API. IP di luar daftar ditolak sebelum tanda tangan sempat diperiksa. Selama daftarnya masih kosong, semua permintaan ditolak — lihat Mengatur di halaman Kredensial API. |
Kredensial, whitelist IP, dan URL callback diatur sendiri lewat menu Kredensial API di area member Anda. Mengubah salah satunya meminta PIN.
Kredensial
Anda memegang dua nilai dengan peran yang berbeda. Membedakan keduanya penting: satu boleh terlihat orang lain, satu lagi tidak boleh sama sekali.
Menyatakan siapa yang memanggil. Dikirim apa adanya di header X-Client-Id pada setiap permintaan, jadi nilainya memang terlihat — perannya seperti username, bukan kata sandi. Aman bila tercatat di log Anda sendiri.
Dipakai hanya untuk menghitung X-Signature. Nilainya tidak pernah ikut dalam permintaan — yang terkirim hanyalah hasil perhitungannya. Perannya seperti kata sandi: siapa pun yang memegangnya bisa berpura-pura menjadi Anda.
Inilah alasan tanda tangan lebih aman daripada sekadar mengirim kunci rahasia di header: kalaupun sebuah permintaan berhasil disadap, penyadap hanya mendapat API Key dan satu tanda tangan yang hanya berlaku untuk permintaan itu saja — bukan kunci yang bisa dipakai membuat permintaan baru.
Mendapatkan dan mengganti kredensial
Keduanya diterbitkan otomatis saat Anda pertama kali membuka menu Kredensial API di area member. Tidak ada yang perlu diminta ke kami: begitu akun Anda berrole Distributor, menu itu muncul, dan kunjungan pertama ke sana yang menerbitkan kunci Anda.
Secret Key ditampilkan penuh hanya pada saat diterbitkan. Setelah Anda meninggalkan halaman itu, nilainya tidak dapat ditampilkan lagi — bukan disembunyikan, memang tidak pernah dikirim lagi. Salin dan simpan sebelum menutup tab. Bila terlewat, tekan Buat ulang untuk menerbitkan yang baru.
Generate ulang membuat Secret Key lama langsung tidak berlaku, sehingga integrasi yang masih memakainya akan gagal dengan rc 11. Siapkan penggantian di sisi Anda sebelum menekan tombolnya.
Mengatur di halaman Kredensial API
Selain menampilkan kunci, halaman itu tempat Anda mengatur dari mana permintaan boleh datang dan ke mana kami mengirim callback. Menyimpan perubahan apa pun di sini meminta PIN.
Whitelist IP
Daftar alamat IP server Anda. Permintaan dari alamat di luar daftar ditolak lebih dulu, sebelum tanda tangan sempat diperiksa.
Ini bukan kelalaian, melainkan gerbangnya sendiri. Kredensial yang baru diterbitkan selalu berdaftar kosong, jadi tidak ada permintaan yang bisa masuk sampai Anda mengisinya — kunci yang bocor sebelum Anda sempat memakainya pun tidak berguna bagi siapa pun. Efek sampingnya berguna: mengosongkan daftar adalah cara tercepat menjeda seluruh integrasi Anda sendiri tanpa menghapus apa pun.
| Aturan | Keterangan |
|---|---|
| Alamat persis | Tulis IP satu per satu. Rentang CIDR (10.0.0.0/8) dan nama host (server-saya.com) ditolak — nama host bisa berubah menunjuk alamat lain tanpa sepengetahuan kami, sehingga daftarnya berhenti menjadi jaminan. |
| Pemisah | Koma atau baris baru, boleh dicampur. 1.2.3.4, 5.6.7.8 dan satu-per-baris sama saja. |
| Maksimal 20 alamat | Daftar ini dipindai pada setiap permintaan API Anda, jadi panjangnya dibatasi agar tidak menjadi beban yang Anda bayar terus-menerus. |
| IPv6 dibakukan | IPv6 disimpan dalam bentuk baku. 2606:4700::ABCD akan tampil kembali sebagai 2606:4700::abcd, dan ::ffff:1.2.3.4 menjadi 1.2.3.4. Itu alamat yang sama — bukan perubahan, dan justru yang membuatnya cocok saat permintaan Anda tiba. |
URL callback
Ke mana kami mengirim pemberitahuan. Kosongkan bila Anda tidak memakai callback dan memilih mengambil status lewat cek status.
| Aturan | Keterangan |
|---|---|
Wajib https:// | Isi callback memuat nomor tujuan, SN, dan sisa saldo Anda. http:// ditolak agar data itu tidak melintas dalam bentuk polos. |
| Alamat publik | Alamat non-publik — localhost, 127.0.0.1, rentang privat, alamat metadata cloud — ditolak. Pemeriksaannya dilakukan ulang setiap kali kami menghubungi Anda, bukan hanya saat menyimpan. |
| Tanpa pengalihan | Balasan 3xx tidak diikuti dan dihitung gagal. Arahkan langsung ke endpoint tujuan. |
Kolomnya sudah tersedia dan boleh Anda isi sekarang, tapi belum ada yang dikirim ke sana. Callback product.changed akan mulai berjalan setelah fiturnya dirilis; sampai saat itu, ambil perubahan harga lewat daftar produk dengan parameter updated_since.
Autentikasi
Setiap permintaan membawa tiga header. Tidak ada token yang perlu disimpan atau diperbarui — tanda tangan dihitung ulang per permintaan.
| Header | Isi |
|---|---|
| X-Client-Id | API Key Anda — lihat Kredensial. |
| X-Request-Time | Waktu permintaan dalam format ISO 8601, contoh 2026-08-05T09:14:22+07:00 atau 2026-08-05T02:14:22Z. |
| X-Signature | HMAC-SHA256 dalam huruf kecil heksadesimal, dihitung dengan Secret Key. Cara membentuknya di bawah. |
Membentuk tanda tangan
- Susun body JSON, lalu buang seluruh spasi dan baris baru sehingga menjadi satu baris rapat.
- Hitung
MD5dari string body tersebut. - Rangkai empat bagian dipisah titik dua: metode, path tanpa garis miring depan, hasil MD5, dan
X-Request-Time. - Hitung
HMAC-SHA256atas rangkaian itu menggunakan Secret Key Anda sebagai kunci.
// 1 — body dirapatkan {"service_code":"ML10","reference_number":"INV-0001",...} // 2 — md5 dari body di atas ca06c494d590574127263feeed50bae5 // 3 — string yang ditandatangani POST:v1/order:ca06c494d590574127263feeed50bae5:2026-08-05T09:14:22+07:00 // 4 — hasil akhir, masuk ke header X-Signature hmac_sha256(secret, string_di_atas) // hex, huruf kecil
X-Request-Time ditolak bila selisihnya lebih dari 5 menit dari jam server kami. Pastikan jam server Anda tersinkron NTP — jam yang meleset adalah penyebab kegagalan integrasi yang paling sering terjadi dan paling membingungkan, karena tanda tangannya sendiri sudah benar.
Setiap tanda tangan hanya bisa dipakai sekali. Untuk mengirim ulang (misalnya setelah timeout), hitung ulang X-Request-Time dan tanda tangannya.
Secret Key hanya dipakai untuk menghitung tanda tangan dan tidak pernah dikirim dalam permintaan. Jangan menaruhnya di kode sisi klien, repositori publik, atau log. Bila bocor, generate ulang dari halaman API — tanda tangan lama langsung tidak berlaku.
Nilai X-Request-Time di header harus sama persis dengan yang Anda masukkan ke rangkaian tanda tangan. Hitung sekali, simpan di variabel, lalu pakai di kedua tempat. Membangkitkannya dua kali secara terpisah adalah penyebab rc 11 yang paling sulit dilacak, karena selisih satu detik saja sudah membuat tanda tangan berbeda.
Contoh kode
Fungsi penanda tangan beserta contoh pemanggilan endpoint order. Semua contoh menghasilkan tanda tangan yang identik untuk masukan yang sama.
// Node.js 18+ — tanpa dependensi eksternal const crypto = require('crypto'); const BASE_URL = 'https://api.am08shop.com'; const API_KEY = process.env.PK_API_KEY; // pk_live_… const SECRET_KEY = process.env.PK_SECRET_KEY; // sk_live_… jangan di-hardcode function buatSignature(pathname, payload, requestTime) { const md5 = crypto.createHash('md5').update(payload).digest('hex'); const raw = `POST:${pathname}:${md5}:${requestTime}`; return crypto.createHmac('sha256', SECRET_KEY).update(raw).digest('hex'); } async function panggil(pathname, body) { // JSON.stringify sudah menghasilkan string rapat tanpa spasi/baris baru. // Payload yang ditandatangani HARUS string yang sama dengan yang dikirim. const payload = JSON.stringify(body); const requestTime = new Date().toISOString(); const res = await fetch(`${BASE_URL}/${pathname}`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Client-Id': API_KEY, 'X-Request-Time': requestTime, 'X-Signature': buatSignature(pathname, payload, requestTime), }, body: payload, }); return res.json(); } // Contoh: buat order panggil('v1/order', { service_code: 'ML10', reference_number: 'INV-0001', inputs: { user_id: '12345678', zone_id: '2201' }, price: 3000, pin: process.env.PK_PIN, }).then(console.log);
// TypeScript — Node.js 18+ import { createHash, createHmac } from 'node:crypto'; const BASE_URL = 'https://api.am08shop.com'; const API_KEY = process.env.PK_API_KEY!; const SECRET_KEY = process.env.PK_SECRET_KEY!; function buatSignature(pathname: string, payload: string, requestTime: string): string { const md5 = createHash('md5').update(payload).digest('hex'); const raw = `POST:${pathname}:${md5}:${requestTime}`; return createHmac('sha256', SECRET_KEY).update(raw).digest('hex'); } interface ApiResponse<T> { rc: string; message: string; data?: T; } async function panggil<T>(pathname: string, body: unknown): Promise<ApiResponse<T>> { const payload = JSON.stringify(body); const requestTime = new Date().toISOString(); const res = await fetch(`${BASE_URL}/${pathname}`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Client-Id': API_KEY, 'X-Request-Time': requestTime, 'X-Signature': buatSignature(pathname, payload, requestTime), }, body: payload, }); return res.json() as Promise<ApiResponse<T>>; } interface Order { order_id: string; reference_number: string; status: 'PROCESSING' | 'SUCCESS' | 'FAILED' | 'REFUNDED'; sn: string; total: number; } const hasil = await panggil<Order>('v1/order', { service_code: 'ML10', reference_number: 'INV-0001', inputs: { user_id: '12345678', zone_id: '2201' }, price: 3000, pin: process.env.PK_PIN!, });
<?php const BASE_URL = 'https://api.am08shop.com'; function buatSignature(string $pathname, string $payload, string $requestTime, string $secretKey): string { $md5 = md5($payload); $raw = "POST:{$pathname}:{$md5}:{$requestTime}"; return hash_hmac('sha256', $raw, $secretKey); } function panggil(string $pathname, array $body): array { $apiKey = getenv('PK_API_KEY'); $secretKey = getenv('PK_SECRET_KEY'); // JSON_UNESCAPED_SLASHES agar payload tidak berubah bentuk; // string inilah yang ditandatangani DAN yang dikirim. $payload = json_encode($body, JSON_UNESCAPED_SLASHES); $requestTime = date('c'); // contoh: 2026-08-05T09:14:22+07:00 $ch = curl_init(BASE_URL . '/' . $pathname); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => $payload, CURLOPT_HTTPHEADER => [ 'Content-Type: application/json', 'X-Client-Id: ' . $apiKey, 'X-Request-Time: ' . $requestTime, 'X-Signature: ' . buatSignature($pathname, $payload, $requestTime, $secretKey), ], ]); $res = curl_exec($ch); curl_close($ch); return json_decode($res, true); } // Contoh: buat order $hasil = panggil('v1/order', [ 'service_code' => 'ML10', 'reference_number' => 'INV-0001', 'inputs' => ['user_id' => '12345678', 'zone_id' => '2201'], 'price' => 3000, 'pin' => getenv('PK_PIN'), ]); print_r($hasil);
package main import ( "bytes" "crypto/hmac" "crypto/md5" "crypto/sha256" "encoding/hex" "encoding/json" "fmt" "io" "net/http" "os" "time" ) const baseURL = "https://api.am08shop.com" func buatSignature(pathname, payload, requestTime, secretKey string) string { sum := md5.Sum([]byte(payload)) raw := fmt.Sprintf("POST:%s:%s:%s", pathname, hex.EncodeToString(sum[:]), requestTime) mac := hmac.New(sha256.New, []byte(secretKey)) mac.Write([]byte(raw)) return hex.EncodeToString(mac.Sum(nil)) } func panggil(pathname string, body any) (map[string]any, error) { // json.Marshal menghasilkan string rapat; string inilah yang // ditandatangani DAN yang dikirim sebagai body. payload, err := json.Marshal(body) if err != nil { return nil, err } requestTime := time.Now().Format(time.RFC3339) // 2026-08-05T09:14:22+07:00 req, err := http.NewRequest("POST", baseURL+"/"+pathname, bytes.NewReader(payload)) if err != nil { return nil, err } req.Header.Set("Content-Type", "application/json") req.Header.Set("X-Client-Id", os.Getenv("PK_API_KEY")) req.Header.Set("X-Request-Time", requestTime) req.Header.Set("X-Signature", buatSignature(pathname, string(payload), requestTime, os.Getenv("PK_SECRET_KEY"))) res, err := http.DefaultClient.Do(req) if err != nil { return nil, err } defer res.Body.Close() raw, _ := io.ReadAll(res.Body) var out map[string]any return out, json.Unmarshal(raw, &out) } func main() { hasil, err := panggil("v1/order", map[string]any{ "service_code": "ML10", "reference_number": "INV-0001", "inputs": map[string]string{"user_id": "12345678", "zone_id": "2201"}, "price": 3000, "pin": os.Getenv("PK_PIN"), }) if err != nil { panic(err) } fmt.Println(hasil) }
Tandatangani string body yang sama persis dengan yang Anda kirimkan — jangan menyusun ulang JSON untuk keperluan tanda tangan. Urutan kunci, spasi, dan cara meng-escape karakter bisa berbeda antar pemanggilan, dan perbedaan satu karakter saja membuat MD5 — dan seluruh tanda tangannya — tidak cocok. Pada semua contoh di atas, satu variabel payload dipakai untuk keduanya.
Memverifikasi tanda tangan callback
Perhitungannya sama, hanya pathname-nya diganti path endpoint callback Anda sendiri.
// Node.js — verifikasi callback masuk const diterima = req.headers['x-signature']; const harusnya = buatSignature('webhook/am08shop', rawBody, req.headers['x-request-time']); // Bandingkan dengan timingSafeEqual, bukan === , agar waktu perbandingan // tidak membocorkan informasi tentang tanda tangan yang benar. const cocok = diterima?.length === harusnya.length && crypto.timingSafeEqual(Buffer.from(diterima), Buffer.from(harusnya)); if (!cocok) return res.status(401).end();
rawBody harus berupa body mentah yang diterima, bukan hasil parse lalu stringify ulang. Di Express, gunakan express.raw() atau simpan body mentahnya lewat opsi verify sebelum JSON di-parse.
Kode respons
Setiap respons membawa rc dan message. HTTP status selalu 200 selama permintaan berhasil diterima dan diautentikasi — hasil bisnisnya dibaca dari rc, bukan dari HTTP status.
| rc | Arti | Tindakan yang disarankan |
|---|---|---|
| 00 | Sukses | — |
| 01 | Transaksi sedang diproses | Tunggu callback. Jangan kirim ulang order. |
| 02 | Transaksi gagal | Saldo sedang dikembalikan otomatis. |
| 03 | Saldo sudah dikembalikan | Status akhir. Aman untuk order ulang. |
| 10 | Client ID tidak dikenal | Periksa header X-Client-Id. |
| 11 | Tanda tangan tidak valid | Periksa perapatan body dan urutan rangkaian. |
| 12 | Waktu permintaan kedaluwarsa | Sinkronkan jam server Anda. |
| 13 | IP tidak diizinkan | Tambahkan IP di halaman API. |
| 14 | PIN salah | Hentikan pengiriman ulang. Lihat catatan di bawah. |
| 15 | Akses API nonaktif | Hubungi kami. Juga dikirim untuk order baru selama website maintenance (lihat message); kirim ulang reference_number lama tetap dijawab status ordernya. |
| 16 | Permintaan sudah pernah diterima | Tanda tangan hanya berlaku sekali. Kirim ulang dengan X-Request-Time dan tanda tangan baru. |
| 20 | Produk tidak ditemukan | Perbarui katalog Anda. |
| 21 | Produk sedang nonaktif | Coba lagi nanti atau pakai produk lain. |
| 22 | Harga tidak cocok | Ambil ulang harga dari daftar produk. |
| 30 | Saldo tidak cukup | Isi saldo. |
| 31 | Data akun tidak lengkap | Lengkapi inputs sesuai inputs_schema. |
| 40 | Nomor referensi sudah dipakai untuk order berbeda | Gunakan nomor referensi baru. |
| 99 | Kesalahan sistem | Coba lagi beberapa saat kemudian. |
PIN yang salah dikirim berkali-kali akan mengunci akses API Anda selama 15 menit. Penguncian ini khusus API dan tidak memengaruhi PIN Anda di area member. Bila mendapat rc 14, hentikan pengiriman dan perbaiki konfigurasi — mengulang otomatis hanya memperpanjang penguncian.
Saldo
Mengembalikan sisa saldo akun Anda dalam Rupiah.
Permintaan
{}
Body kosong, tetapi tetap harus dikirim sebagai {} dan ikut ditandatangani.
Respons
{
"rc": "00",
"message": "Sukses",
"data": {
"balance": 1250000,
"currency": "IDR"
}
}
Daftar produk
Mengembalikan katalog beserta harga tier Distributor Anda dan bentuk data akun yang dibutuhkan tiap produk.
Permintaan
| Field | Tipe | Keterangan |
|---|---|---|
| updated_sinceopsional | string | ISO 8601. Bila diisi, hanya produk yang berubah sejak waktu itu yang dikembalikan. Gunakan untuk sinkronisasi berkala tanpa menarik seluruh katalog. |
| category_codeopsional | string | Batasi ke satu kategori. |
Respons
{
"rc": "00",
"message": "Sukses",
"data": [
{
"service_code": "ML10",
"service_name": "10 Diamonds",
"category_code": "MLBB",
"category_name": "Mobile Legends",
"price": 3000,
"status": "active",
"updated_at": "2026-08-05T09:00:00+07:00",
"inputs_schema": [
{ "key": "user_id", "label": "User ID", "type": "number", "required": true },
{ "key": "zone_id", "label": "Zone ID", "type": "number", "required": true }
]
}
]
}
inputs_schema adalah kontrak untuk field inputs saat membuat order. Produk pulsa umumnya hanya punya satu entri; game biasanya dua. Simpan skema ini bersama katalog Anda agar validasi bisa dilakukan di sisi Anda sebelum order dikirim.
Buat order
Permintaan
| Field | Tipe | Keterangan |
|---|---|---|
| service_codewajib | string | Kode produk dari daftar produk. |
| reference_numberwajib | string | Nomor referensi milik Anda, unik per akun. Maks. 64 karakter. |
| inputswajib | object | Data akun tujuan, berkunci sesuai inputs_schema produk. |
| priceopsional | number | Harga satuan yang Anda yakini berlaku. Bila dikirim dan tidak cocok, order ditolak dengan rc 22. Bila tidak dikirim (atau 0), order ditagih dengan harga tier Distributor yang berlaku saat itu. |
| pinwajib | string | PIN 6 digit akun Anda. |
| qtytidak dipakai | number | Tidak lagi digunakan. Setiap order selalu berjumlah 1. Field ini boleh tetap dikirim — nilainya diabaikan, jadi integrasi yang sudah berjalan tidak perlu diubah. Untuk memesan beberapa unit, kirim beberapa order dengan reference_number berbeda. |
{
"service_code": "ML10",
"reference_number": "INV-0001",
"inputs": {
"user_id": "12345678",
"zone_id": "2201"
},
"price": 3000,
"pin": "123456"
}
Respons
{
"rc": "01",
"message": "Transaksi sedang diproses",
"data": {
"order_id": "TRX-20260805-A1B2C",
"reference_number": "INV-0001",
"service_code": "ML10",
"service_name": "10 Diamonds",
"status": "PROCESSING",
"price": 3000,
"qty": 1,
"total": 3000,
"sn": "",
"balance_after": 1247000,
"created_at": "2026-08-05T09:14:22+07:00"
}
}
Mengirim ulang reference_number yang sama tidak membuat order kedua — Anda menerima kembali status order yang sudah ada. Ini berlaku juga ketika koneksi Anda terputus sebelum respons diterima: kirim ulang dengan body dan reference_number yang sama, tetapi X-Request-Time dan tanda tangan baru — saldo tidak akan terpotong dua kali. Tanda tangan hanya berlaku sekali; permintaan yang dikirim ulang persis sama ditolak dengan rc 16.
Bila reference_number yang sama dikirim dengan isi berbeda, permintaan ditolak dengan rc 40.
Saldo dipotong saat order diterima. Bila order berakhir gagal, saldo dikembalikan otomatis dan status menjadi REFUNDED — Anda tidak perlu mengajukan apa pun.
Cek status
Gunakan sebagai jaring pengaman bila callback tidak diterima. Bukan pengganti callback — jangan melakukan polling ketat.
Permintaan
Isi salah satu dari dua field berikut.
| Field | Tipe | Keterangan |
|---|---|---|
| reference_number | string | Nomor referensi Anda. |
| order_id | string | Nomor order dari sistem kami. |
{ "reference_number": "INV-0001" }
Respons
Bentuknya sama persis dengan respons buat order, dengan status dan sn terkini.
Callback status transaksi
Dikirim ke URL callback transaksi Anda setiap kali sebuah order mencapai status akhir. Kami mengirim POST JSON dengan header tanda tangan yang sama seperti permintaan Anda ke kami — sehingga Anda bisa memverifikasinya dengan kode yang cara kerjanya identik.
{
"event": "transaction.status",
"data": {
"order_id": "TRX-20260805-A1B2C",
"reference_number": "INV-0001",
"service_code": "ML10",
"status": "SUCCESS",
"rc": "00",
"message": "Transaksi berhasil",
"sn": "SN-8842190",
"price": 3000,
"qty": 1,
"total": 3000,
"balance_after": 1247000,
"updated_at": "2026-08-05T09:15:04+07:00"
}
}
Balas dengan HTTP 200 untuk menandakan callback sudah diterima. Respons selain 200, atau tidak ada respons dalam 10 detik, dianggap gagal dan akan diulang setiap 60 detik, maksimal lima kali. Setelah itu callback berhenti dan Anda perlu mengambil statusnya lewat cek status.
Callback yang sama bisa terkirim lebih dari sekali — misalnya bila respons 200 Anda hilang di jalan. Pastikan pemrosesan di sisi Anda aman diulang, dengan berpatokan pada reference_number.
Callback perubahan produk
Dikirim ke URL callback produk Anda ketika ada perubahan harga atau status produk. Perubahan dikumpulkan lebih dulu selama ±20 detik, lalu dikirim sebagai satu callback berisi array.
Satu tindakan admin — misalnya mengubah margin sebuah kategori — dapat mengubah harga ratusan produk sekaligus. Bila tiap produk mengirim callback sendiri, endpoint Anda akan menerima ratusan permintaan dalam sekejap. Dengan pengumpulan, satu tindakan admin menghasilkan satu callback, dan Anda cukup memprosesnya dalam satu transaksi basis data.
{
"event": "product.changed",
"data": [
{
"service_code": "ML10",
"price": 3100,
"status": "active",
"changed": ["price"],
"updated_at": "2026-08-05T09:20:00+07:00"
},
{
"service_code": "ML86",
"price": 21000,
"status": "inactive",
"changed": ["status"],
"updated_at": "2026-08-05T09:20:00+07:00"
}
]
}
Field changed menyebutkan apa saja yang berubah, sehingga Anda bisa memperbarui seperlunya. Aturan percobaan ulang sama dengan callback transaksi.
Verifikasi tanda tangan callback
Callback dari kami membawa header X-Client-Id, X-Request-Time, dan X-Signature. Rangkaian yang ditandatangani memakai path callback Anda sendiri, tanpa garis miring depan.
// URL callback Anda: https://server-anda.com/webhook/am08shop
POST:webhook/am08shop:<md5(body dirapatkan)>:<X-Request-Time>
Tanpa verifikasi, siapa pun yang mengetahui URL callback Anda bisa mengirim notifikasi palsu — misalnya mengaku sebuah order sudah SUCCESS padahal belum. Bandingkan tanda tangan sebelum memproses isinya, dan tolak bila tidak cocok.
Status transaksi
| Status | rc | Arti |
|---|---|---|
| PROCESSING | 01 | Order diterima dan sedang diproses. Saldo sudah dipotong. Status belum final. |
| SUCCESS | 00 | Produk terkirim. sn berisi serial number bila produk memilikinya. Status akhir. |
| FAILED | 02 | Produk gagal terkirim. Pengembalian saldo sedang berjalan — status ini bersifat sementara. |
| REFUNDED | 03 | Saldo sudah kembali ke akun Anda. Status akhir. |
Anda akan menerima callback pada SUCCESS dan REFUNDED. Bila sebuah order tertahan di PROCESSING lebih lama dari biasanya, itu berarti sedang menunggu penanganan manual — jangan mengirim order pengganti, karena order aslinya masih bisa berakhir sukses.
Aturan penting
Harga diperiksa saat order
Field price bersifat opsional. Bila Anda mengirimnya, nilainya dibandingkan dengan harga tier Distributor yang berlaku saat itu. Bila berbeda, order ditolak — tidak diproses diam-diam dengan harga lain. Ini melindungi kedua pihak ketika katalog Anda tertinggal dari perubahan harga kami.
Bila price tidak dikirim, order langsung ditagih dengan harga yang berlaku saat itu. Harga yang terpotong tercantum di data.price dan data.total pada balasan. Kami menyarankan tetap mengirim price agar perubahan harga tidak terlewat.
Satu nomor referensi, satu order
reference_number harus unik di akun Anda. Ini yang membuat pengiriman ulang aman saat jaringan bermasalah.
Kegagalan selalu dikembalikan
Tidak ada order gagal yang menahan saldo Anda. Pengembalian berjalan otomatis dan tercatat di mutasi saldo.
Batas laju
Maksimal 100 permintaan per detik per akun. Melebihi batas akan mendapat rc 99 sementara.