Integrations
Six external partners, all wrapped in a reliability layer. Every integration goes through a CircuitBreaker. Every webhook is idempotent. Every external call has a defined fallback.
Integration map
Xendit, payments ✅
Purpose: Process all buyer payments, QRIS, BCA Virtual Account, bank transfer.
Outbound calls:
POST /v2/invoices, create payment invoiceGET /v2/invoices/:id, check invoice statusPOST /payouts, disburse to seller
Inbound webhooks:
invoice.paid, payment confirmedinvoice.expired, payment timeoutpayout.completed, disbursement done
Reliability:
- HMAC signature verification (timing-safe compare)
- Idempotency keys on all outbound calls
- Amount mismatch detection on webhooks
- 12-hour payment window enforced
Critical guards:
- Amount mismatch never auto-progresses →
PAYMENT_REVIEW - Failed HMAC → reject + log to
webhook_events - Duplicate webhook → idempotency check returns existing outcome
Files:
backend/src/infrastructure/adapters/XenditAdapter.tsbackend/src/modules/payments/(invoice + payout webhook controllers)- env:
XENDIT_API_KEY,XENDIT_WEBHOOK_VERIFICATION_TOKEN
Biteship, shipping ✅
Purpose: Aggregated shipping across all major Indonesian couriers, JNE, J&T, GoSend, AnterAja, Lion Parcel, etc.
Outbound calls:
POST /v1/rates/couriers, get shipping rate quotePOST /v1/orders, create shipment + generate labelGET /v1/trackings/:id, fetch tracking status
Inbound webhooks:
order.delivered, package deliveredorder.returned, return shipment receivedorder.failed, delivery failed
Reliability:
- Server-side rate quote (prevents client tampering)
- IDR 2,000 markup added in adapter, not exposed to client
- Rate cache for 60 seconds in
shipping_rates_cache
Status: ✅ Forward shipping live · 🔵 Reverse label for returns planned for v1
Files:
backend/src/infrastructure/adapters/BiteshipAdapter.tsbackend/src/modules/shipping/(rates, areas, tracking, label, webhook controllers)- env:
BITESHIP_API_KEY
Resend, email ✅
Purpose: Transactional email, order confirmations, KTP status updates, payout notifications, password reset, marketing broadcasts.
Outbound calls:
POST /emails, send single emailPOST /emails/batch, send batch
Templates: Stored in email_templates table. Edited via ops portal Email Templates module.
Reliability:
- Outbox pattern, email creation is async via
outbox_events - Failed sends retry every 15 minutes
- Bounce tracking (planned)
Files:
backend/src/infrastructure/adapters/ResendAdapter.ts- env:
RESEND_API_KEY
OpenAI, AI capabilities ✅
Purpose: Three use cases:
- KTP OCR, extract identity data from photo
- Help center AI bot, customer queries
- Ops AI bot, staff SOP guidance
Model: gpt-4o-mini (cost-optimized for high volume)
Reliability:
- PII redaction layer scrubs sensitive data before API call
- Rate limiting per user
- Cost monitoring with daily budget alerts
- Fallback to keyword matching when API down
KTP OCR pipeline:
Files:
backend/src/infrastructure/adapters/OpenAIAdapter.tsbackend/src/infrastructure/adapters/KtpOcrAdapter.ts- env:
OPENAI_API_KEY
Xero, accounting 🟡
Purpose (intended): Sync every order, fee, expense, and disbursement to Xero for accounting and tax compliance.
Current state: Placeholder only. The xero_synced boolean is flipped to true, but no actual Xero API call happens.
What's needed:
- OAuth 2.0 authentication flow
- Map orders to Xero invoices
- Map disbursements to Xero bills
- Map expenses to Xero expense entries
- Bidirectional sync for reconciliation
Roadmap: Build before launch (June 2026). Required for proper Indonesian tax compliance.
Files:
infrastructure/adapters/XeroAdapter.ts(stub)- env:
XERO_CLIENT_ID,XERO_CLIENT_SECRET(not yet wired)
Meta Pixel, analytics ✅
Purpose: Track marketing funnel events for Meta ads optimization.
Events fired:
PageView, every page loadViewContent, listing detail viewSearch, filter useAddToWishlist, listing likeInitiateCheckout, checkout startedAddPaymentInfo, payment method selectedPurchase, order confirmed
Privacy: Hashed user IDs only. No PII sent to Meta.
Files:
src/app/lib/analytics/meta.ts- env:
VITE_META_PIXEL_ID
Reliability layer
Three patterns wrap every external integration:
IdempotencyGuard
Prevents duplicate processing on retried operations.
const result = await idempotencyGuard.execute(
`order-create-${orderId}`,
() => xendit.createInvoice(order)
);
If the key has been seen before, returns the original result without re-executing.
Used on: Order creation, all webhooks, all outbound payment calls.
OutboxProcessor
Transactional outbox pattern — writes side effects to outbox_events table within the same transaction as business logic, then a 15-minute in-process cron processes pending events. The cron runs via @nestjs/schedule inside the NestJS backend — not an external or Vercel cron.
Why it matters: If the DB commit succeeds but the email send fails, the email will retry. No lost notifications.
Used for: Notifications, fin_audit_log entries, downstream events.
CircuitBreaker
Wraps every external API call. Opens after N consecutive failures, fails fast for cooldown period, retries.
const breaker = new CircuitBreaker('xendit', {
failureThreshold: 5,
cooldownMs: 30000
});
const result = await breaker.execute(() => xendit.api.call());
Used on: Xendit, Biteship, Resend, OpenAI, Xero adapters.
Why it matters: When Biteship is down, we fail fast with a clear error instead of cascading timeouts.
Webhook security
All inbound webhooks pass through a security pipeline:
Stored in webhook_events table for full audit trail.
Observability
Sentry is live for error tracking across all three runtimes: web (VITE_SENTRY_DSN), NestJS backend (SENTRY_DSN_SERVER), and mobile (sentry_flutter). See Observability for how to read logs and use it for debugging.
Future integrations (planned)
| Integration | Purpose | Priority |
|---|---|---|
| Mixpanel or Amplitude | Product analytics beyond Meta | Medium |
| Algolia | Search infrastructure | Medium |
| Twilio | SMS for KTP verification | Low |
| WhatsApp Business API | Native chat integration | Low |
Where to look in the codebase
| What you want | Where it lives |
|---|---|
| Adapter interfaces | backend/src/domain/ports/ |
| Adapter implementations | backend/src/infrastructure/adapters/ |
| Reliability layer | backend/src/infrastructure/reliability/ |
| Webhook controllers | backend/src/modules/payments/, backend/src/modules/shipping/, backend/src/modules/notifications/ |
| Environment variables | .env.example |
Next: Automation Roadmap →