Pemeriksaan batas kredit yang hanya ada di frontend dapat dilewati oleh klien yang canggih dengan memanggil API secara langsung — hal ini terjadi pada implementasi awal di Commsult. Dengan menerapkan aturan ini di lapisan NestJS service, aturan bisnis tetap berlaku apa pun cara portal diakses. UI kemudian berfungsi sebagai lapisan kemudahan di atas aturan server-side tersebut.
Integrasi menggunakan kombinasi database trigger dan NestJS event emitter. Ketika status Sales Order berubah di ERP, sebuah event dipancarkan yang memperbarui tabel status yang menghadap portal, sehingga pelanggan dapat melihat progres pesanan dan pengiriman secara real-time. Pelanggan juga dapat berlangganan notifikasi email untuk setiap perubahan status.
Maksimal 5 menit adalah TTL yang disarankan untuk data harga, atau implementasikan event invalidasi cache dari ERP ke portal via webhook. Penggunaan TTL 24 jam menyebabkan insiden di mana klien mendapat harga lama selama satu bulan penuh setelah daftar harga diperbarui di ERP. TTL yang lebih pendek atau invalidasi berbasis event mencegah harga usang sampai ke pelanggan.
Midtrans, payment gateway terkemuka di Indonesia, diintegrasikan untuk pembayaran faktur online. Handler webhook yang mencatat pembayaran harus bersifat idempoten, karena Midtrans dapat mengirim webhook duplikat — memproses webhook yang sama dua kali akan salah mengosongkan saldo faktur. Pemeriksaan idempoten di handler pembayaran NestJS mencegah hal ini.
Setelah enam bulan, 73% pesanan ditempatkan melalui portal (naik dari 0%), waktu pemrosesan pesanan rata-rata turun dari 4 jam menjadi 15 menit, dan panggilan layanan pelanggan turun 55%. Hasil ini dicapai dengan menggantikan proses intake manual via telepon dan email dengan sinkronisasi ERP otomatis dan akses dokumen swalayan.
Pada 2025, 85% perusahaan B2B menawarkan portal pelanggan online, naik dari 68% tahun sebelumnya. Perusahaan yang mengadopsi saluran B2B digital melaporkan rata-rata peningkatan pendapatan penjualan 41%. Untuk bisnis B2B Indonesia yang melayani klien korporat, portal pelanggan mengubah proses pemesanan via email dan telepon menjadi pengalaman swalayan yang berkembang tanpa pertumbuhan headcount yang proporsional.
Portal pelanggan B2B bukan toko ecommerce. Klien B2B membutuhkan: memesan berdasarkan daftar harga yang dinegosiasikan, melacak status pesanan dan pengiriman secara real-time, mengakses dan membayar faktur mereka, melihat riwayat transaksi mereka, dan berkomunikasi tentang pesanan tertentu.
Dalam B2B Indonesia, setiap klien utama memiliki daftar harga yang dinegosiasikan. Portal ini membaca harga dari tabel customer_price_list ERP: setiap klien memiliki price list ID, dan ketika mereka menelusuri produk, mereka melihat harga kontrak mereka, bukan harga publik. Portal juga menampilkan batas kredit tersedia dan saldo outstanding mereka saat ini.
B2B Customer Portal Architecture
Customer (browser / mobile)
│
▼
┌─────────────────────────────────┐
│ Next.js Customer Portal │ (deployed: Vercel)
│ │
│ 🛒 Place Order (vs. price list)│
│ 📦 Track Shipment Status │
│ 🧾 View & Pay Invoices │
│ 📄 Download e-Faktur PDF │
└──────────────┬──────────────────┘
│ customer-scoped JWT
▼
┌─────────────────────────────────┐
│ NestJS API (shared) │
│ │
│ CustomerGuard → customer_id │
│ PostgreSQL RLS enforcement │
│ │
│ /pricing → customer_price_list │
│ /orders → sales_orders │
│ /invoices → ar_invoices │
│ /pay → Midtrans gateway │
└──────────────┬──────────────────┘
│
┌──────────────┴──────────────────┐
│ Internal ERP (Next.js) │
│ Sales Order sync ↔ portal │
│ Shipment updates → portal │
│ Invoice status → portal │
└─────────────────────────────────┘Dari pengalaman saya membangun sistem ERP sebagai konsultan ERP lepas: bangun pemeriksaan kredit di tingkat API, bukan hanya frontend. Saya memiliki implementasi awal di mana pemeriksaan batas kredit hanya ada di UI React — klien yang canggih dapat melewatinya dengan memanggil API langsung. Selalu paksakan aturan bisnis (batas kredit, minimum kuantitas pesanan, produk terbatas) di lapisan NestJS service.
Alur pesanan: pelanggan membuat pesanan di portal → pesanan disinkronkan ke ERP sebagai Sales Order → staf gudang ERP memilih dan mengirim → pembaruan pengiriman mengalir kembali ke portal → pelanggan melihat status real-time. Sinkronisasi bidireksional ini adalah tantangan integrasi inti.
Klien B2B perlu mengunduh dokumen: faktur pajak (PDF e-Faktur untuk rekonsiliasi PPN), Surat Jalan (untuk proses penerimaan barang mereka), dan Sertifikat Analisis untuk pemasok bahan baku. Semua ini dihasilkan di ERP dan disimpan dalam object storage dengan signed URL. Portal mengekspos pusat dokumen per pesanan dan per faktur.
// NestJS: Midtrans webhook handler (idempotent)
@Post('/webhooks/midtrans')
async handleMidtransWebhook(@Body() body: MidtransNotification) {
// 1. Validate Midtrans signature
const expectedSig = crypto
.createHash('sha512')
.update(body.order_id + body.status_code + body.gross_amount
+ process.env.MIDTRANS_SERVER_KEY)
.digest('hex');
if (expectedSig !== body.signature_key) {
throw new UnauthorizedException('Invalid webhook signature');
}
// 2. Idempotency check — skip if already processed
const alreadyProcessed = await this.paymentRepo.existsByMidtransId(
body.transaction_id
);
if (alreadyProcessed) {
return { message: 'Already processed — skipping' };
}
// 3. Only process successful payments
if (body.transaction_status === 'settlement'
|| body.transaction_status === 'capture') {
await this.arService.recordPayment({
invoiceId: body.order_id.replace('INV-', ''),
amount: parseFloat(body.gross_amount),
paymentDate: new Date(body.settlement_time),
reference: body.transaction_id,
method: body.payment_type,
});
}
return { message: 'OK' };
}Portal pelanggan adalah aplikasi Next.js terpisah yang diautentikasi melalui JWT khusus pelanggan. API NestJS memiliki konteks pelanggan yang membatasi setiap query ke data pelanggan yang terautentikasi — semua pesanan, faktur, dan pengiriman difilter berdasarkan customer_id di tingkat database menggunakan Row Level Security PostgreSQL.
Daftar harga pelanggan berubah — kontrak baru ditandatangani, harga promosi berakhir, biaya produk berubah. Jika Anda meng-cache daftar harga di lapisan aplikasi portal (misalnya di Redis untuk performa), Anda harus membatalkan cache ketika daftar harga diperbarui di ERP. Saya mengalami insiden memalukan di mana klien mendapat harga lama selama satu bulan penuh karena TTL cache diatur 24 jam. Untuk data harga, gunakan TTL yang jauh lebih pendek (maksimal 5 menit).
Saya mengintegrasikan Midtrans (payment gateway terkemuka Indonesia) untuk pembayaran faktur online. Klien dapat membayar faktur outstanding langsung dari portal. Alur pembayaran: klien memilih faktur yang akan dibayar → halaman pembayaran Midtrans terbuka → pada sukses, Midtrans mengirim webhook ke handler pembayaran NestJS → pembayaran dicatat di modul AR → status faktur diperbarui menjadi 'Dibayar'.
Setelah 6 bulan portal pelanggan di klien saya: 73% pesanan ditempatkan melalui portal (naik dari 0%), waktu pemrosesan pesanan rata-rata turun dari 4 jam menjadi 15 menit, dan panggilan layanan pelanggan turun 55%. Metrik yang penting untuk keberhasilan portal B2B: tingkat adopsi portal per akun pelanggan, pesanan melalui portal vs. saluran tradisional, tingkat pembayaran faktur secara online vs. transfer bank, dan waktu dari pesanan ke konfirmasi pesanan ERP.