← All case studies
Backend APIs & Payments

Checkout & Payment Webhook Performance

Checkout went from 5–7 seconds to about half a second, and a paid status no longer waits on analytics.

  • Request
  • Payment
  • Deploy
  • Event
  • AI

Context

Every order triggers emails, WhatsApp messages, subscriptions, and analytics events, and the order page polls its status every 3 seconds right after payment.

Problem

Checkout took 5–7 seconds from clicking Pay to seeing payment instructions: the request sent the confirmation email and WhatsApp message synchronously, and order endpoints loaded full entity graphs with N+1 queries. Later, the payment webhooks from Midtrans and Xendit still called Mixpanel and GA/BigQuery synchronously in one shared finish-payment step, with a new HTTP client per GA call. Every payment spent at least 327 ms there (median 521 ms), 1 in 100 took over 10 seconds, and the slowest took 139 seconds.

What I did

  • Measured the checkout and webhook paths step by step: gateway calls, notifications, analytics, database queries, and frontend polling.
  • Made the order-confirmation email and WhatsApp messages asynchronous, so checkout returns as soon as the gateway responds.
  • Fixed N+1 queries on order history and the order-status lookup with JOIN FETCH and lazy associations, added a DTO projection and an index for order lists, and tuned the Hikari connection pool.
  • Moved Mixpanel and GA/BigQuery calls out of the shared finish-payment step, so every Midtrans and Xendit channel benefits.
  • Gave background work a bounded executor (8–32 threads, queue of 500, caller-runs when full) and the analytics integration one shared OkHttp client with 5-second connect and 10-second read timeouts.

Next case study

Kubernetes Platform: CI/CD & Monitoring for 15+ Applications