Panduan teknis menyatukan POS dan e-commerce: single source of truth stok, reservasi atomik, sinkronisasi order, hingga rekonsiliasi pembayaran.
Stok minus di toko sementara marketplace masih menerima pesanan adalah gejala klasik POS dan e-commerce yang berjalan sendiri-sendiri. Tim gudang meng-update stok manual dua kali sehari, admin marketplace mengecek satu per satu, dan saat flash sale satu SKU terjual dua kali di kanal berbeda. Untuk apotek dengan 1.200 SKU dan tiga kanal penjualan (kasir, order WhatsApp, Shopee), selisih stok 0,5% saja berarti 6 item salah jual per hari dan rekonsiliasi manual 2–3 jam setiap minggu. Integrasi omnichannel bukan sekadar menyambungkan API, melainkan menetapkan siapa pemegang kebenaran data stok, bagaimana reservasi dikunci, dan bagaimana kegagalan ditangani. Artikel ini membahas langkah praktisnya, termasuk pola kode yang kami pakai di proyek POS farmasi, retail, dan restoran di Yogyakarta maupun Magelang. Semua contoh bisa langsung diadaptasi ke stack Anda.
1. Tentukan Single Source of Truth Sebelum Menyentuh API
Kesalahan paling mahal dalam integrasi omnichannel adalah membiarkan dua sistem sama-sama boleh menulis stok. Pilih satu single source of truth (SSOT). Untuk retail dengan satu gudang toko, POS adalah SSOT karena ia yang paling cepat berubah. Untuk distributor dengan 4 gudang, ERP (misalnya Odoo 17 atau Accurate Online) yang jadi SSOT, dan POS hanya membaca cache lokal. E-commerce sebaiknya tidak pernah memegang angka stok otoritatif — ia hanya menyimpan salinan untuk ditampilkan dan memberi tahu platform saat stok berubah.
Secara arsitektur, gunakan pola event-driven. POS/ERP menerbitkan event stock.changed dan order.created ke message broker (Redis 7.2 + BullMQ 5 atau RabbitMQ 3.13). Worker terpisah mengonsumsi event dan mendorongnya ke kanal eksternal: WooCommerce REST API v3, Shopee Open Platform v2, Tokopedia API, atau TikTok Shop Partner Center. Jangan panggil API marketplace langsung dari dalam transaksi kasir — satu timeout 30 detik akan memblokir antrean pembayaran di depan.
Sisi e-commerce juga perlu reverse flow. Pesanan dari marketplace masuk melalui webhook, bukan polling. Polling tiap 1 menit untuk 500 order/jam memang terlihat aman, tapi Anda hanya mendapat data setelah pesanan dibuat, bukan saat pelanggan mengklik bayar — celah oversell tetap ada. Webhook memberi latensi 1–3 detik dan menghemat kuota rate limit API.
Terakhir, buat tabel pemetaan SKU eksplisit (sku_mapping) berisi pos_sku, channel, channel_sku, dan conversion_factor untuk produk yang dijual per dus di marketplace tapi per pcs di kasir. Tanpa tabel ini, integrasi akan gagal pada produk pertama yang punya kemasan berbeda.
2. Sinkronisasi Stok: Reservasi Atomik, Bukan Sekadar Update
Mengurangi angka stok saat pesanan masuk bukan cukup. Yang benar adalah mereservasi: stok dikurangi dari available, dipindahkan ke reserved, dan otomatis dikembalikan bila pembayaran gagal dalam 15 menit. Pola ini mencegah oversell tanpa harus menahan stok permanen. Di PostgreSQL 16, lakukan pengurangan dalam satu statement dengan klausa WHERE available >= qty supaya aman dari race condition antar-worker.
// Node.js 20 LTS + Express 4.19 — reservasi stok atomik & idempotent
app.post('/webhook/order', async (req, res) => {
const { order_id, sku, qty } = req.body;
await db.tx(async (t) => {
const r = await t.query(
`UPDATE stok SET available = available - $1
WHERE sku = $2 AND available >= $1 RETURNING available`, [qty, sku]);
if (!r.rowCount) throw new Error('STOK_KURANG');
await t.query(`INSERT INTO reservasi(order_id,sku,qty,expires_at)
VALUES ($1,$2,$3, now() + interval '15 minutes')
ON CONFLICT (order_id) DO NOTHING`, [order_id, sku, qty]);
});
res.json({ status: 'reserved' });
});
Perhatikan ON CONFLICT (order_id) DO NOTHING. Marketplace sering mengirim webhook yang sama 2–5 kali ketika timeout. Tanpa idempotency key, satu pesanan bisa mereservasi stok berkali-kali. Simpan juga X-Request-Id dari header untuk audit.
Untuk katalog besar, jangan dorong seluruh stok ke marketplace. Kirim hanya SKU yang berubah (delta sync). Pada klien retail dengan 8.000 SKU dan 3 kanal, full sync tiap 5 menit memakan 24.000 panggilan API per jam dan langsung kena rate limit Shopee. Delta sync menurunkannya ke 200–400 panggilan/jam.
Tambahkan safety buffer 2–3 unit untuk SKU fast-moving. Tujuannya menyerap selisih antara waktu klik bayar di marketplace dan waktu webhook tiba. Untuk restoran dengan penjualan 300 porsi/hari, buffer ini menghilangkan hampir semua komplain "pesanan dibatalkan karena stok habis".
3. Sinkronisasi Order, Pembayaran, dan Rekonsiliasi Harian
Alur yang kami pakai: webhook order masuk → validasi stok & reservasi → simpan order lokal berstatus pending → panggil payment gateway (Midtrans Snap atau Xendit Invoice) → setelah status settlement, dorong order ke POS/ERP untuk picking → kirim nomor resi balik ke marketplace. Setiap langkah punya status sendiri sehingga kegagalan bisa di-retry tanpa menduplikasi order.
Gunakan exponential backoff 5 percobaan (2s, 8s, 30s, 2m, 10m) lalu lempar ke dead-letter queue. Di BullMQ 5, ini cukup dikonfigurasi lewat opsi attempts dan backoff: { type: 'exponential', delay: 2000 }. Order yang gagal permanen muncul di dashboard operasional, bukan hilang diam-diam di log.
Rekonsiliasi adalah bagian yang paling sering dilupakan. Buat job harian pukul 02.00 yang membandingkan tiga sumber: total transaksi POS, total order channel, dan total settlement payment gateway. Selisih harus nol; kalau tidak, keluarkan daftar order yang tidak match beserta statusnya. Klien kami di Magelang memangkas waktu tutup buku dari 6 jam menjadi 40 menit setelah rekonsiliasi otomatis ini berjalan.
Untuk pelaporan lintas kanal, simpan satu tabel fact_sales dengan kolom channel, order_id, gross, discount, fee_channel, dan net. Biaya komisi marketplace (Shopee 6–8%, TikTok 5–8%) sering membuat margin terlihat lebih besar dari kenyataan; masukkan sebagai kolom terpisah agar laporan laba per kanal akurat.
Best Practices
- Tetapkan satu SSOT stok dan larang kanal lain menulis angka stok secara langsung.
- Selalu gunakan reservasi berbatas waktu (10–15 menit) plus job pelepas reservasi yang berjalan tiap menit.
- Wajibkan idempotency key pada semua endpoint webhook dan simpan log request selama minimal 30 hari.
- Pisahkan worker sinkronisasi dari proses transaksi kasir; gunakan antrean dengan retry dan dead-letter queue.
- Jalankan rekonsiliasi otomatis harian dan kirim ringkasan selisih ke email/WhatsApp operasional.
FAQ
Berapa lama implementasi integrasi POS–e-commerce?
Untuk satu kanal (WooCommerce atau Shopee) dan satu POS, 3–5 minggu kerja efektif sudah cukup mencakup mapping SKU, reservasi stok, sinkronisasi order, dan rekonsiliasi. Tambah 2 minggu per kanal berikutnya, terutama karena proses approval API marketplace memakan waktu.
Apakah harus pakai middleware seperti n8n atau bisa langsung?
Untuk 1–2 kanal dan volume di bawah 300 order/hari, n8n self-hosted sudah memadai. Di atas itu, atau bila butuh jaminan urutan dan retry presisi, gunakan worker kustom dengan BullMQ atau RabbitMQ plus database transaksional.
Bagaimana menangani produk yang stoknya di-share antar cabang?
Pisahkan stok per lokasi dan buat aturan alokasi eksplisit, misalnya cabang terdekat pelanggan diprioritaskan. Hindari menggabungkan stok semua cabang menjadi satu angka di marketplace karena risiko oversell di satu lokasi tetap tinggi.
Apa yang terjadi kalau marketplace sedang down saat sinkronisasi?
Order dan event tetap tersimpan di antrean lokal. Begitu API pulih, worker memproses ulang berdasarkan urutan waktu. Itulah alasan utama integrasi harus memakai antrean, bukan pemanggilan API langsung di dalam transaksi.
Integrasi omnichannel yang rapi bukan proyek sekali jadi — ia butuh SSOT yang jelas, reservasi stok yang aman, dan rekonsiliasi yang berjalan sendiri setiap hari. Jika Anda sedang menyiapkan POS, ERP, atau website e-commerce yang harus saling bicara, tim kami membantu mulai dari audit arsitektur hingga implementasi dan pendampingan operasional. Kami melayani konsultasi IT dan pengembangan aplikasi SIMRS, SIM Klinik, ERP, POS, serta website untuk klien di Yogyakarta (DIY) dan Magelang, Jawa Tengah. Hubungi Nugroho Setiawan untuk sesi konsultasi awal dan pemetaan kebutuhan sistem Anda.
Artikel ini bagian dari edukasi Nugroho Setiawan, konsultan IT Jogja. Solusi terkait: PilarPOS SIM Klinik Integrator Bridging BPJS & SatuSehat. Butuh konsultasi gratis?
Komentar
Belum ada komentar. Jadilah yang pertama!
Saya membantu konsultasi IT & pengembangan aplikasi untuk rumah sakit, klinik, dan bisnis di Yogyakarta (DIY), Magelang, dan sekitarnya.