Skip to content

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

2 Commits

Folders and files

Repository files navigation

Order Processing System

A small backend that keeps orders, payments, and inventory consistent even when the payment provider fails, times out, or the server crashes mid-request.

Built with Node.js/Express + PostgreSQL, and a mock payment provider that can be told to fail, succeed, or hang on demand - so every failure mode described below can actually be demonstrated, not just claimed.

What it demonstrates

  • Idempotency keys - duplicate requests (same Idempotency-Key header) never double-charge; they replay the original result.
  • Circuit breaker (via opossum) - stops hammering a payment provider that's already failing, and recovers automatically.
  • Row-level locking - SELECT ... FOR UPDATE on inventory prevents two concurrent buyers from both getting the last item in stock.
  • Crash/timeout recovery - a background reconciliation job resolves orders left in an unknown state (payment succeeded but the app never got to record it) by asking the provider for the real status, instead of guessing.

Running it

docker compose up --build

This starts:

  • postgres on 5432 (schema auto-applied from api/migrations/001_init.sql)
  • payment-mock on 4001
  • api on 3000

Trying it out

Place an order (note the Idempotency-Key header - generate a new UUID per logical attempt, and reuse it if you retry):

curl -X POST http://localhost:3000/orders \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"userId": "user-1", "productId": "sku-001", "quantity": 1}'

Send the exact same request again with the same Idempotency-Key - you'll get the identical response back instead of a second charge.

Check an order's status:

curl http://localhost:3000/orders/<order-id>

Demoing the circuit breaker

Force every payment call to fail, then place a few orders in a row - after enough failures the breaker opens and subsequent calls fail instantly without even hitting payment-mock:

curl -X POST http://localhost:4001/admin/mode -H "Content-Type: application/json" -d '{"mode": "always_fail"}'

Watch the api container logs for [circuit-breaker] OPEN. Switch back with {"mode": "normal"} and watch it move to HALF-OPEN then CLOSED.

Demoing crash/timeout reconciliation

Force slow responses (8s, longer than the API's 4s timeout) so the API can't tell if the charge went through:

curl -X POST http://localhost:4001/admin/mode -H "Content-Type: application/json" -d '{"mode": "slow"}'

Place an order - you'll get a 202 with PENDING_RECONCILIATION. Within RECONCILIATION_INTERVAL_MS (15s by default) the background job checks the provider's real status for that idempotency key and resolves the order to PAID or FAILED automatically. Check with GET /orders/:id before and after to see the state flip without you doing anything.

Demoing the inventory race condition

The seeded product (sku-001) has 5 units in stock. Fire several concurrent requests for it with different idempotency keys and different quantities summing past 5 - only the ones that fit will succeed; the rest get a 409 Insufficient stock, never an oversold order.

What's intentionally left out

This is a portfolio piece focused on the payment-consistency problem, not a full e-commerce app - no auth, no catalog UI, no cart. A minimal Postman collection or the curl commands above are the intended way to exercise it.

Project Structure

order-processing-system/
├── docker-compose.yml
├── README.md
│
├── payment-mock/
│   ├── Dockerfile
│   ├── package.json
│   └── src/
│       └── index.js
│
└── api/
    ├── Dockerfile
    ├── package.json
    ├── .env.example
    │
    ├── migrations/
    │   └── 001_init.sql
    │
    ├── tests/
    │   ├── idempotency.test.js
    │   ├── locking.test.js
    │   └── circuitBreaker.test.js
    │
    └── src/
        ├── config/
        │   └── env.js
        │
        ├── db/
        │   ├── pool.js
        │   └── repositories/
        │       ├── orderRepository.js
        │       ├── idempotencyRepository.js
        │       ├── inventoryRepository.js
        │       └── paymentRepository.js
        │
        ├── services/
        │   ├── orderService.js
        │   ├── paymentClient.js
        │   └── reconciliationService.js
        │
        ├── routes/
        │   └── orderRoutes.js
        │
        ├── middleware/
        │   └── errorHandler.js
        │
        └── index.js

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages