Skip to content
All projects
Case studyCS/09
StatusPersonal project
TypeFull-stack
Year2026

Payeazie

Payment operations: async charging, recovery tools and a full audit trail

A Fastify API creates payments idempotently and hands charging to BullMQ workers. Every status change goes through one transition model with an audit record, and an internal ops dashboard lets operators retry, reconcile and refund stuck or failed payments.

  • 2 background workers
  • 5 payment states
  • Audit row per transition
  • 44 Vitest cases

§1

The problem

Charging a card inside the HTTP request makes checkout as slow as the gateway, and a timeout leaves nobody sure whether the customer paid. Payments that get stuck need a safe way for an operator to fix them, with a record of who did what.

§2

My role

Sole developer: API, workers, status model, auth (JWT, Google OAuth, password reset), the merchant UI and the internal ops dashboard.

§3

Architecture

Fig. 1 · Payeazie architecture
enqueueREACT APPmerchant + opsFASTIFY APIJWT · OAuthPOSTGRESQLpayments · auditBULLMQRedisCHARGE WORKERSKIP LOCKEDRECONCILERworker · stuck sweepGATEWAYmock
clientservicedataeventexternal

§4

Key decisions

  1. D/01

    Idempotent payment intents

    One CTE returns the existing payment for (order, idempotency key) or inserts a new one. Reusing a key with a different payload is 409; paying an order that's already processing or paid is 422.

  2. D/02

    API and workers split

    The API persists the payment as pending and enqueues a charge job (5 attempts, exponential backoff from 250 ms). The gateway call happens in a worker, never in the request.

  3. D/03

    No double charges

    The charge worker selects the payment with FOR UPDATE SKIP LOCKED, so two workers can never pick up the same payment.

  4. D/04

    One source of truth for status

    pending → processing → succeeded / failed, succeeded → refunded, failed → pending for a retry. Finality, retryability and refundability all come from the same module.

  5. D/05

    Role-aware surfaces

    Merchants see their own payments; admin and ops roles see all of them plus recovery actions, each attributed to the operator in the audit log.

§5

Challenges

  • Stuck payments: a reconcile worker sweeps payments that aren't in a final state and asks the gateway what really happened. Operators can also restart or reconcile one by hand.
  • Testing failure paths without a real gateway: a mock gateway and webhook simulator force success, failure or slow processing on demand.
  • Hardening the API with Helmet, rate limiting and secure sessions, plus health and metrics endpoints for basic operational visibility.

§6

Stack

  • Node.js
  • Fastify
  • BullMQ
  • Redis
  • PostgreSQL
  • React
  • TypeScript
  • Vitest

Next case study

MediSync

Patient-management back end as Spring Boot microservices with gRPC and Kafka