A production-inspired, Brazil-focused payments gateway and fintech core built with NestJS. It implements digital wallets, peer-to-peer transfers, and PIX charge generation with real sandbox providers (OpenPix/Efí), emphasizing security, idempotency, and ACID transactions.
graph LR;
U[Client] -->|REST| API[NestJS API];
API --> W[Wallets];
API --> T[Transfers ACID];
API --> P[PIX];
T -->|QueryRunner| DB[(Postgres)];
W --> DB;
API -->|Idempotency-Key| R[(Redis)];
P --> OP[OpenPix/Efi Sandbox];
OP -->|Webhook| API;
API -->|Credit| W;
API --> DOCS[Swagger /docs];
- Digital Wallets: create and query user wallets with accurate decimal balances.
- Money Transfers (ACID): transactional transfer flow using TypeORM
QueryRunnerand pessimistic locks. - PIX Integration: dynamic QR code charges via OpenPix or Efí providers; webhook to credit wallets on payment confirmation.
- Idempotency:
Idempotency-Keyheader cached on Redis to avoid double-processing when users click twice. - Security: API Key guard (
x-api-key), strict validation with DTOs (class-validator), logging and error interceptors. - Documentation: Swagger UI at
/docswith security schemes. - Docker:
docker-compose.ymlspins API, Postgres, and Redis with one command. - Tests: Unit tests for core services and an E2E sanity check.
- NestJS core (
@nestjs/common,@nestjs/core) - TypeORM with Postgres (
typeorm,pg,@nestjs/typeorm) - Redis via
ioredisfor idempotency - Axios for provider HTTP calls
- Validation:
class-validatorandclass-transformer - API Docs:
@nestjs/swagger - Testing: Jest,
@nestjs/testing, Supertest
- Bootstrap and Config: main.ts, app.module.ts
- Common:
- Guards: API Key api-key.guard.ts
- Interceptors: Logging logging.interceptor.ts and Errors errors.interceptor.ts
- Idempotency: Redis provider redis.provider.ts, service idempotency.service.ts, interceptor idempotency.interceptor.ts
- Wallets: Entity wallet.entity.ts, Service wallets.service.ts, Controller wallets.controller.ts
- Transfers: Service transfers.service.ts, Controller transfers.controller.ts
- PIX Payments: Module pix.module.ts, Controller pix.controller.ts, Webhook webhook.controller.ts, Service pix.service.ts, Providers openpix.provider.ts and efi.provider.ts
- Validation: Global
ValidationPipewithwhitelist,forbidNonWhitelisted, and auto-transform to ensure DTO integrity. - Logging/Error Handling: Interceptors wrap controller responses, emitting concise logs and normalizing DB errors.
- Idempotency Strategy: For
POSTrequests withIdempotency-Key, responses are cached in Redis with TTL. Replays return the cached payload, avoiding repeated side-effects. - Transaction Safety: Transfers load both wallets under pessimistic write locks within a
QueryRunnertransaction, mutating balances and committing atomically. Failures trigger rollback. - Decimal Handling: Balances are stored in Postgres as
numeric(20,2)and represented as strings to avoid floating-point errors.
- Clone and install dependencies:
npm install
- Configure environment:
- Copy
/.env.exampleto/.envand adjust variables.
- Copy
- Run with Docker (recommended):
docker-compose up -d
- Start the API (local dev):
npm run start:dev
- Open Swagger:
http://localhost:3000/docs
- Core
PORTdefault3000API_KEYforx-api-keyguard
- Database (Postgres)
DB_HOST,DB_PORT,DB_USER,DB_PASSWORD,DB_NAME
- Redis
REDIS_HOST,REDIS_PORT
- PIX Providers
PIX_PROVIDER=openpix|efi- OpenPix:
OPENPIX_API_KEY - Efí:
EFI_CLIENT_ID,EFI_CLIENT_SECRET,EFI_CERT_PATH,EFI_BASE_URL(sandbox values)
- See /.env.example
- Health
GET /health→{ status: 'ok' }
- Wallets (requires header
x-api-key)POST /wallets{ userId }→ create walletGET /wallets/:userId→ get wallet
- Transfers (requires
x-api-key; supportsIdempotency-Key)POST /transfers{ fromUserId, toUserId, amount }→ atomic transfer
- PIX (requires
x-api-key; supportsIdempotency-Key)POST /pix/charge{ amount, recipientUserId, payerName? }→{ txid, qrCode }POST /pix/webhookprovider callback to apply wallet credit
- Docs
GET /docs→ Swagger UI
x-api-key: <your-api-key>Idempotency-Key: <unique-key-per-operation>
- Applicable to
POST /transfersandPOST /pix/charge. - Redis
SET NX EXstores the final response underidem:<key>. - Repeated requests with the same
Idempotency-Keyshort-circuit and return the cached payload.
- Reads both wallets under
pessimistic_writelock. - Checks funds and updates balances inside a single transaction.
commitpersists both updates together; errors causerollback.
- Provider selection by
PIX_PROVIDER:openpix(default) orefi. - OpenPix uses API key authentication and returns
txidand QR payload/URL. - Efí flow typically requires OAuth and mTLS certificates; the included provider demonstrates the request sequence and should be adapted with sandbox credentials.
- Webhook normalizes provider payload and credits the recipient wallet.
- For production, add webhook signature verification and idempotent event handling per provider documentation.
- Unit:
npm test- Example: transfers service spec transfers.service.spec.ts
- E2E:
npm run test:e2e- Basic health check: app.e2e-spec.ts
- Compose services:
api,db(Postgres),redis. - Start all:
docker-compose up -d - Rebuild:
docker-compose up --build -d - Env variables forwarded to the
apiservice; Postgres volume persists data (pgdata).
- Never commit secrets; use
.envand secret managers. - Keep
API_KEYconfigured in environments exposed to the internet. - Validate all incoming data via DTOs and consider rate-limiting in front of the API for added protection.
MIT