Accepting Payments in Indonesia: Midtrans, Xendit, and the Dual Gateway Pattern
Indonesia is WhatsApp's second-largest market. If you're building a SaaS platform for Indonesian businesses, you'll quickly discover a truth that Silicon Valley ignores: not everyone pays by credit card β and Stripe doesn't even support Indonesian merchant accounts.
At Gigaviz, we solved this with what we call the dual gateway pattern β two Indonesia-native payment providers in one platform, each optimized for its audience.
The Indonesian payment reality
Indonesia has over 270 million people, but credit card penetration is under 5%. Most B2B transactions happen via bank transfer (BCA, Mandiri, BRI, BNI). Mobile wallets like GoPay, OVO, and Dana are growing fast. QRIS is becoming the universal QR standard.
Meanwhile, international customers in Singapore and Malaysia expect card payments with their local currency. They want to pay in USD or SGD, not IDR.
You can't choose one over the other. You need both.
The dual gateway pattern
Gigaviz uses two Indonesia-based payment gateways, each serving a different purpose:
Midtrans (Local IDR) β Snap popup checkout for domestic Indonesian customers. Supports credit cards, bank transfer/VA (BCA, BNI, BRI, Mandiri, Permata), e-wallets (GoPay, ShopeePay, DANA, OVO, LinkAja), QRIS, and convenience stores (Alfamart, Indomaret). All in IDR.
Xendit (International Multi-Currency) β Invoice-based hosted checkout for international customers. Supports IDR, USD, and SGD. Credit/debit cards (Visa, Mastercard, Amex, JCB), bank transfer, e-wallets, QRIS, and retail outlets.
Both flows end at the same place: tokens in the workspace wallet, transaction in the ledger, audit trail complete.
How we built the Midtrans integration
Snap popup checkout
When a user selects IDR local payment, the client sends a request to our Midtrans checkout endpoint. The server:
1. Validates the request (workspace ID, token amount, price) with Zod schemas 2. Creates a payment intent in our database (status: pending, provider: midtrans) 3. Calls Midtrans Snap API to create a transaction token 4. Returns the Snap token and redirect URL
The client opens the Midtrans Snap popup β a familiar interface for Indonesian users with all local payment methods.
Webhook processing
When payment settles, Midtrans sends a notification to our webhook handler. We:
1. Verify the SHA-512 signature (reject tampering) 2. Server-to-server verify status with Midtrans Core API 3. Credit tokens to the workspace wallet 4. Update the payment status to "paid" 5. Record the transaction in the token ledger
This happens asynchronously β even if the user closes their browser, the webhook ensures tokens are credited.
How we built the Xendit integration
Invoice-based checkout
For international customers, we use Xendit's Invoice API. When a user selects USD or SGD:
1. Server creates a Xendit Invoice with the correct currency and amount 2. Records a payment intent (status: pending, provider: xendit) 3. Returns the hosted invoice URL
The client redirects to Xendit's hosted checkout page. Multi-currency pricing is built in β each plan and token package has IDR, USD, and SGD prices.
Webhook verification
Xendit uses a simpler verification model than Midtrans: an `x-callback-token` header that must match our configured secret. We also do server-to-server verification by calling `GET /v2/invoices/{id}` to double-check the payment status.
Why two gateways instead of one?
Midtrans excels at local IDR payments. The Snap popup is instantly recognizable to Indonesian users. It handles QRIS, GoPay, bank VA, and convenience store payments natively. HQ in Jakarta, support in Bahasa Indonesia.
Xendit adds multi-currency for international markets. Same Indonesian heritage (HQ Jakarta), but with USD, SGD, PHP, and MYR support. Perfect for customers in Singapore, Malaysia, and beyond.
The dual pattern scales. Both gateways use the same architecture: create payment intent β call provider API β redirect user β process webhook β credit tokens. Adding a third gateway would follow the identical pattern.
Token-based pricing: why it works for messaging SaaS
We don't charge per seat or per message. We use a token economy:
- Each action costs tokens (messaging, AI responses, content generation β each with its own token rate)
- Workspaces buy token packages (50K, 100K+bonus, 500K+bonus)
- Usage is tracked in real-time with daily breakdown charts
- Budget guards prevent overspending (configurable monthly caps, hard caps, alert thresholds)
This model works because messaging volume is unpredictable. A marketing campaign might send 10,000 messages in a day, then nothing for a week. Per-seat pricing penalizes small teams with high volume. Per-message pricing penalizes everyone. Tokens let customers buy capacity and use it however they need.
Lessons for SaaS builders in Southeast Asia
Don't assume Stripe works everywhere. Stripe doesn't support Indonesian merchant accounts. Use local gateways β Midtrans and Xendit are both excellent and Indonesia-native.
Support multi-currency from day one. Your Indonesian customers pay in IDR. Your Singapore customers pay in SGD. Build the pricing table with multiple currencies upfront β retrofitting is painful.
Track everything in one ledger. Whether payment comes from Midtrans or Xendit, it should all end up in the same token ledger with the same audit trail.
Feature-flag your payment providers. Payment systems break. Being able to disable a payment method with one environment variable (`NEXT_PUBLIC_XENDIT_ENABLED`, `NEXT_PUBLIC_MIDTRANS_ENABLED`) is worth the engineering investment.
Use the same webhook architecture. Both our gateways follow identical patterns: verify signature β check server-to-server β update payment intent β credit tokens β send email. Same code structure, different providers.
What's next
Our payment roadmap:
- Invoice generation with Indonesian tax compliance (Faktur Pajak)
- Recurring subscription automation via Xendit recurring invoices
- Additional currencies β MYR, PHP, THB for broader Southeast Asia coverage
- Revenue dashboard in ops console (Midtrans + Xendit combined metrics)
The dual gateway pattern is our foundation. Every new payment provider plugs into the same architecture: create intent, process webhook, credit tokens, log transaction.
---
*Building a SaaS for Southeast Asia? Use Indonesia-native payment gateways. Midtrans for local IDR payments, Xendit for international multi-currency. Your customers get familiar payment methods, you get paid from day one, and the architecture scales to any new provider.*