Dalam integrasi distributor–reseller, masalah terbesar biasanya bukan sekadar koneksi API, tetapi kontrak data yang ambigu. Ketika harga, stok, wilayah penjualan, MAP/MSRP, atau hak jual tidak didefinisikan dengan jelas, reseller bisa menampilkan harga yang salah, menerima order yang seharusnya dilarang, atau menjual produk di wilayah yang tidak diizinkan.
Konteks bisnis seperti sengketa Thomann vs Fender berguna sebagai pengingat bahwa aturan distribusi bukan detail administratif semata. Untuk engineer, pelajarannya adalah: aturan bisnis distribusi harus diperlakukan sebagai bagian inti dari kontrak API, bukan asumsi yang disembunyikan di email, spreadsheet, atau percakapan account manager. Artikel ini membahas cara merancang API distributor yang tegas pada kontrak harga, aman untuk retry, dan konsisten lewat idempotensi.
Mengapa kontrak API distributor sering gagal
Integrasi distributor–reseller sering dimulai dari kebutuhan yang terlihat sederhana: ambil katalog, sinkron stok, sinkron harga, lalu kirim order. Namun di lapangan, beberapa istilah yang tampak sederhana justru punya banyak makna operasional.
- Harga: apakah ini harga beli reseller, harga rekomendasi publik, atau harga minimum iklan?
- Stok: apakah stok global, stok per gudang, stok yang bisa dipesan, atau stok yang sudah dikurangi alokasi?
- Wilayah: apakah reseller boleh menjual ke negara tertentu, hanya boleh mengirim dari gudang tertentu, atau hanya boleh menampilkan katalog di region tertentu?
- Hak jual: apakah reseller boleh menjual semua SKU, hanya kategori tertentu, atau hanya brand/sub-brand tertentu?
- Order update: apakah retry dari client boleh membuat order ganda?
Kalau API tidak memodelkan aturan ini secara eksplisit, integrasi akan bergantung pada interpretasi. Di sinilah konflik bisnis berubah menjadi bug teknis.
Prinsip desain kontrak API distributor–reseller
1. Definisikan istilah bisnis sebagai field eksplisit
Jangan kirim satu field price lalu berharap semua pihak memahami artinya. Untuk distributor, lebih aman memisahkan beberapa tipe harga.
{
"sku": "STRAT-PLAYER-BLK",
"currency": "EUR",
"prices": {
"dealer_net": 799.00,
"msrp": 999.00,
"map": 949.00,
"advertised_price_floor": 949.00
},
"pricing_policy": {
"map_enforced": true,
"tax_included": false,
"valid_from": "2026-07-01T00:00:00Z",
"valid_until": null
}
}Keuntungan pendekatan ini:
- Client tidak perlu menebak arti harga.
- Validasi bisnis bisa diterapkan secara otomatis.
- Audit perubahan harga lebih mudah.
Kalau distributor memang tidak memiliki salah satu jenis harga, lebih baik field tersebut nullable atau tidak dikirim dengan dokumentasi yang jelas, daripada mengisinya dengan angka yang artinya berbeda.
2. Pisahkan hak akses katalog dari ketersediaan stok
SKU yang ada di katalog belum tentu boleh dijual oleh semua reseller. API sebaiknya membedakan:
- catalog visibility: SKU terlihat atau tidak.
- sales authorization: SKU boleh dijual atau tidak.
- fulfillment availability: SKU bisa dipenuhi dari gudang mana.
{
"sku": "STRAT-PLAYER-BLK",
"status": "active",
"visibility": {
"catalog": true,
"searchable": true
},
"sales_rights": {
"authorized": true,
"allowed_countries": ["DE", "FR", "NL"],
"blocked_marketplaces": ["example-marketplace-a"],
"reason_code": null
},
"inventory": {
"available_to_promise": 12,
"warehouses": [
{"code": "DE-1", "qty": 8},
{"code": "NL-1", "qty": 4}
]
}
}Dengan model ini, client tidak menyamakan qty > 0 dengan “boleh dijual”. Ini penting untuk kasus pembatasan wilayah, channel, atau lisensi brand.
3. Gunakan kode alasan, bukan hanya boolean
Boolean seperti authorized: false sering tidak cukup untuk debugging. Tambahkan reason_code yang stabil.
REGION_RESTRICTEDMAP_POLICY_BLOCKDEALER_TIER_NOT_ELIGIBLESKU_DISCONTINUEDCHANNEL_NOT_ALLOWED
Client dapat menampilkan pesan yang tepat ke sistem internal atau tim operasional tanpa parsing teks bebas.
Desain endpoint yang praktis
Untuk kebanyakan integrasi distributor–reseller, endpoint berikut sudah mencakup kebutuhan inti.
Endpoint katalog dan harga
GET /v1/catalog/products?updated_since=2026-07-01T00:00:00Z&limit=100
GET /v1/catalog/products/{sku}
GET /v1/prices?sku=STRAT-PLAYER-BLK&country=DE&channel=web
GET /v1/inventory?sku=STRAT-PLAYER-BLK&warehouse_scope=authorizedCatatan desain:
updated_sincemembantu sinkron bertahap.- Parameter
countrydanchannelpenting karena harga dan hak jual bisa berbeda per konteks. - Jangan gabungkan semua domain ke satu endpoint raksasa jika siklus perubahannya berbeda. Harga dan stok biasanya berubah lebih sering daripada metadata produk.
Endpoint order dengan idempotency
POST /v1/orders
Idempotency-Key: 9a7d9ab5-5a12-4c2d-a0f4-ff9fbf7c9fd1Contoh payload:
{
"external_order_id": "RSL-2026-000184",
"reseller_id": "reseller-berlin-01",
"currency": "EUR",
"country": "DE",
"channel": "web",
"items": [
{
"sku": "STRAT-PLAYER-BLK",
"qty": 1,
"unit_price": 799.00
}
],
"ship_to": {
"name": "Max Mustermann",
"country": "DE",
"postal_code": "10115"
}
}Pada sisi server, pasangan Idempotency-Key dan identitas pemanggil harus disimpan. Jika request yang sama dikirim ulang karena timeout atau gangguan jaringan, server mengembalikan hasil yang sama tanpa membuat order baru.
Endpoint update order yang aman
Perubahan order setelah dibuat perlu dibatasi. Jangan izinkan PATCH bebas untuk semua field bila status order sudah masuk proses gudang.
POST /v1/orders/{order_id}/cancel
POST /v1/orders/{order_id}/addresses/change
POST /v1/orders/{order_id}/items/replacePendekatan command-style seperti ini lebih jelas daripada PATCH /orders/{id} untuk semua kemungkinan perubahan. Setiap aksi bisa memiliki aturan bisnis, validasi, dan audit trail yang spesifik.
Versioning: ubah kontrak tanpa merusak integrasi
API distributor biasanya hidup lama dan dipakai banyak partner. Karena itu, versioning harus konservatif.
Kapan perlu versi baru
- Mengubah arti field yang sudah ada.
- Menghapus field.
- Mengubah enum sehingga client lama bisa salah interpretasi.
- Mengubah perilaku validasi secara inkompatibel.
Menambah field baru biasanya kompatibel, selama client tidak bergantung pada whitelist ketat tanpa toleransi. Versi dapat diletakkan di path seperti /v1 agar mudah dipahami partner eksternal.
Praktik yang aman
- Dokumentasikan field yang bisa bertambah.
- Jangan ubah arti
pricediam-diam; tambah field baru jika perlu. - Gunakan enum yang terdokumentasi dan stabil.
- Sediakan periode deprecation yang jelas.
Idempotensi dan retry aman
Mengapa idempotensi wajib untuk order
Dalam jaringan nyata, client bisa mengalami timeout setelah server sebenarnya berhasil memproses request. Tanpa idempotensi, retry akan membuat order ganda. Ini salah satu sumber insiden paling mahal dalam integrasi B2B.
Pola implementasi server
- Client mengirim
Idempotency-Keyunik untuk setiap intent bisnis. - Server menyimpan key bersama hash request, status pemrosesan, dan respons akhir.
- Jika key yang sama datang lagi dengan payload identik, server kembalikan respons sebelumnya.
- Jika key yang sama datang dengan payload berbeda, server kembalikan error konflik.
{
"error": {
"code": "IDEMPOTENCY_KEY_REUSED_WITH_DIFFERENT_PAYLOAD",
"message": "Idempotency-Key sudah dipakai untuk payload lain"
}
}Hal penting:
- Scope key sebaiknya per partner atau per credential, bukan global lintas tenant.
- Simpan hasil respons final, bukan hanya penanda “sudah pernah”.
- Tentukan retensi key yang sesuai dengan pola retry client dan risiko duplikasi.
Retry yang aman
Tidak semua operasi aman untuk di-retry. Pedoman praktis:
- Aman di-retry: GET, webhook acknowledgement tertentu, POST idempoten dengan key, operasi asynchronous yang punya deduplication.
- Berisiko jika tanpa proteksi: create order, reserve stock, issue refund, create shipment.
Pada client, gunakan exponential backoff dengan jitter. Hindari retry agresif serentak karena bisa memperburuk gangguan dan memicu duplikasi pada server yang belum idempoten penuh.
Retry aman bukan hanya soal jaringan. Ia harus didukung desain kontrak, penyimpanan deduplication, dan observabilitas di kedua sisi.
Webhook untuk perubahan katalog, harga, dan stok
Polling berkala berguna, tetapi perubahan harga dan stok sering perlu didorong lebih cepat. Webhook cocok untuk event seperti:
catalog.product.updatedprice.changedinventory.changedsales_rights.changedorder.status.changed
Contoh payload webhook
{
"event_id": "evt_01JXYZ9B9A",
"event_type": "price.changed",
"occurred_at": "2026-07-02T10:15:43Z",
"tenant_id": "distributor-eu",
"data": {
"sku": "STRAT-PLAYER-BLK",
"country": "DE",
"channel": "web",
"currency": "EUR",
"prices": {
"dealer_net": 789.00,
"msrp": 999.00,
"map": 939.00
},
"effective_at": "2026-07-03T00:00:00Z"
}
}Signature verification
Webhook harus ditandatangani agar receiver bisa memverifikasi bahwa payload benar berasal dari distributor dan tidak diubah di tengah jalan. Implementasi umum:
- Server pengirim menghitung HMAC dari body mentah dengan secret bersama.
- Signature dikirim di header, misalnya
X-Signature. - Receiver menghitung ulang HMAC dari body mentah dan membandingkannya secara constant-time.
POST /webhooks/distributor
X-Signature: sha256=ab12cd34...
X-Timestamp: 1751451343
X-Event-Id: evt_01JXYZ9B9AJangan memverifikasi signature dari JSON yang sudah diparse lalu diserialisasi ulang, karena perubahan whitespace atau urutan properti bisa membuat hasil berbeda. Gunakan body mentah sebagaimana diterima.
Replay protection
Signature saja tidak cukup. Payload valid bisa diputar ulang oleh pihak yang menyadap traffic atau oleh sistem yang salah konfigurasi. Tambahkan:
- Timestamp header dan toleransi waktu yang terbatas.
- Event ID unik yang disimpan untuk deduplication.
- Reject duplicate jika
event_idsudah diproses.
Dengan ini, receiver tidak memproses event yang sama berkali-kali walaupun webhook dikirim ulang.
Audit trail dan jejak keputusan
Dalam konflik harga atau hak jual, pertanyaan yang muncul biasanya bukan “apakah API hidup?”, melainkan:
- Harga apa yang berlaku saat order dibuat?
- Apakah reseller saat itu berhak menjual SKU tersebut di negara tujuan?
- Siapa yang mengubah aturan? Kapan? Melalui API, panel admin, atau import batch?
Karena itu, sistem perlu menyimpan audit trail yang minimal memuat:
- identitas actor atau sistem
- waktu perubahan
- field sebelum dan sesudah
- sumber perubahan
- correlation ID atau request ID
Untuk order, simpan pula snapshot aturan penting saat order diterima, misalnya harga dealer, MAP yang berlaku, serta evaluasi hak jual. Jangan hanya bergantung pada state terkini katalog, karena state itu bisa berubah setelah order masuk.
Race condition yang sering terjadi
1. Harga berubah saat order dibuat
Client mengambil harga pukul 10:00, user checkout pukul 10:03, distributor mengganti harga pukul 10:02. Jika server order hanya percaya payload client, order bisa masuk dengan harga lama yang sudah tidak berlaku.
Mitigasi:
- Sertakan
price_versionataueffective_atpada response harga. - Server order memvalidasi bahwa versi harga masih berlaku.
- Jika tidak cocok, kembalikan error yang eksplisit.
2. Stok terjual bersamaan
Dua reseller melihat stok 1 unit lalu sama-sama memesan. Tanpa reservasi atomik, kedua order bisa lolos.
Mitigasi:
- Gunakan transaksi dan pengurangan stok atomik.
- Pisahkan available to promise dari stok fisik jika sistem mendukung alokasi.
- Jika arsitektur asynchronous, pastikan ada kompensasi saat over-allocation terjadi.
3. Hak jual berubah di tengah sinkronisasi
SKU terlihat aktif di katalog, tetapi hak jual untuk negara tertentu baru saja dicabut. Reseller yang hanya sinkron sebagian bisa tetap menampilkan produk itu.
Mitigasi:
- Kirim event
sales_rights.changedterpisah. - Gunakan polling berkala sebagai jaring pengaman untuk webhook yang terlewat.
- Pastikan keputusan final tetap divalidasi saat create order.
Contoh respons error yang membantu debugging
Error yang baik harus bisa ditindaklanjuti oleh sistem client, bukan sekadar dibaca manusia.
{
"error": {
"code": "REGION_RESTRICTED",
"message": "SKU tidak boleh dijual ke negara tujuan",
"details": {
"sku": "STRAT-PLAYER-BLK",
"country": "DE",
"allowed_countries": ["FR", "NL"]
},
"request_id": "req_01JXYZA8Q2"
}
}Sertakan request_id agar log server dan client mudah dikorelasikan saat investigasi.
Edge case yang sering terlewat
- MAP berlaku hanya untuk channel tertentu, misalnya marketplace tetapi bukan toko fisik.
- MSRP ada, tetapi tidak relevan untuk semua negara.
- Stok negatif sementara akibat retur, penyesuaian gudang, atau koreksi batch.
- Harga efektif di masa depan, bukan langsung aktif saat event diterima.
- Satu SKU aktif di katalog tetapi diblokir untuk reseller tier tertentu.
- Order retry setelah respons 500, padahal order sebenarnya sudah tercipta.
- Webhook terkirim tidak berurutan.
- Event lama datang terlambat setelah event baru sudah diproses.
- Satu reseller memiliki beberapa credential dengan scope berbeda.
- Currency mismatch antara kontrak harga dan order.
Anti-pattern yang sebaiknya dihindari
- Satu field harga untuk semua makna. Ini sumber ambigu paling umum.
- Mengandalkan deskripsi teks bebas untuk aturan wilayah atau channel.
- Menganggap stok > 0 berarti boleh dijual.
- Retry POST tanpa idempotency key.
- Webhook tanpa signature dan deduplication.
- Mengubah arti field di versi yang sama.
- PATCH generik untuk order tanpa aturan state machine yang jelas.
- Tidak menyimpan snapshot keputusan bisnis saat order dibuat.
Strategi testing kontrak API distributor
1. Contract testing
Buat contoh request/response yang disepakati kedua pihak, lalu validasi otomatis dalam CI. Fokus bukan hanya struktur JSON, tetapi juga aturan enum, field wajib, dan skenario error.
Hal yang perlu diuji:
- Field harga apa saja yang wajib muncul.
- Bagaimana respons jika reseller tidak berhak menjual.
- Bagaimana respons untuk retry dengan idempotency key yang sama.
- Bagaimana format event webhook dan header verifikasinya.
2. Consumer-driven contract
Jika banyak reseller berbeda memakai API yang sama, pendekatan consumer-driven contract membantu menangkap asumsi client sebelum perubahan dirilis. Ini berguna terutama saat distributor ingin menambah field atau memperketat validasi.
3. Replay test untuk webhook
Uji bahwa receiver:
- menerima event valid,
- menolak signature salah,
- menolak timestamp kadaluarsa,
- mengabaikan
event_idduplikat, - tetap benar bila event datang tidak berurutan.
4. Race-condition test
Buat skenario konkuren untuk create order pada SKU dengan stok terbatas. Validasi bahwa hanya jumlah order yang sesuai stok yang diterima, sisanya gagal dengan error yang dapat dipahami.
5. Backward compatibility test
Simpan fixture dari versi lama dan jalankan pengujian regresi saat ada perubahan respons. Ini penting agar penambahan field atau perubahan enum tidak merusak parser client lama.
Checklist implementasi
- Definisikan arti setiap field harga: dealer_net, MSRP, MAP, dan aturan pajak.
- Modelkan hak jual secara eksplisit: negara, channel, tier, marketplace.
- Bedakan katalog, otorisasi jual, dan ketersediaan stok.
- Gunakan reason code yang stabil untuk penolakan.
- Terapkan versioning yang jelas dan kebijakan deprecation.
- Wajibkan Idempotency-Key untuk create order dan command penting lain.
- Simpan hasil idempotensi beserta hash payload dan respons.
- Terapkan retry dengan backoff dan jitter pada client.
- Sediakan webhook untuk perubahan harga, stok, katalog, dan hak jual.
- Verifikasi signature webhook dari raw body.
- Tambahkan replay protection dengan timestamp dan event ID unik.
- Simpan audit trail dan snapshot keputusan bisnis pada order.
- Uji race condition, duplicate event, out-of-order event, dan timeout retry.
- Pastikan final validation dilakukan saat create order, bukan hanya saat sinkron katalog.
Penutup
Merancang API distributor yang baik berarti menerjemahkan aturan distribusi ke kontrak teknis yang tegas. Harga harus jelas jenisnya, hak jual harus eksplisit, order harus idempoten, retry harus aman, dan perubahan penting harus bisa didorong lewat webhook yang tervalidasi serta tahan replay.
Pelajaran praktis dari konteks bisnis seperti sengketa distributor dan brand adalah sederhana: bila aturan penjualan hanya hidup di dokumen terpisah atau asumsi manusia, konflik cepat berubah menjadi insiden sistem. Sebaliknya, ketika kontrak API mendefinisikan harga, wilayah, MAP/MSRP, stok, dan hak jual secara eksplisit, integrasi reseller menjadi lebih dapat diaudit, lebih aman terhadap duplikasi, dan jauh lebih mudah dioperasikan.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!