Clicking “▶ Watch at X:XX” in an article seeks the video to that point
System Architecture
Edited
The Payments API follows an event-driven microservices pattern. The entry point is an Express gateway that validates JWT tokens and routes requests to domain services.
Domain services communicate via an internal message bus (RabbitMQ). Each service owns its own PostgreSQL schema — there is no cross-service join.
External card networks are reached through a PaymentGatewayAdapter that normalises responses from Stripe, Adyen and Razorpay into a single internal payment event.
architecturebackend
Database Schema
Three PostgreSQL schemas are in use: payments (transactions, intents, refunds), accounts (wallets, balances) and audit (append-only ledger).
The audit schema is write-only via the AuditService — no UPDATE or DELETE is ever issued. Any discrepancy between payments.transactions and the audit ledger is a data-integrity incident.
Migrations are managed with Flyway. All team members run mvn flyway:migrate before starting work.
databasearchitecture
Authentication & Token Flow
All API clients authenticate with a short-lived JWT (15-minute TTL) obtained via the shared auth service. The Payments gateway validates the token signature on every request — no session state is held server-side.
Service-to-service calls use mTLS with certificates provisioned by Vault. Public-facing calls go through the API gateway which strips internal headers before forwarding.
securityauthentication
Known Pitfalls & Gotchas
The connection pool exhausts under high concurrency if pool.max is not tuned. The recommended value for the payments service is 20; the default is 5 and was the cause of the September outage.
Never call PaymentGatewayAdapter.charge() inside a database transaction — the external call can take up to 30 seconds and will hold the DB connection open, blocking other requests.