Pagos

Local-first personal finance PWA for Peru: log payments, income and expenses in two taps, never miss a due date, and see where your money goes — even offline.

Pagos — Personal finance PWA

Sobre el Proyecto

Pagos is a personal finance PWA built for the Peruvian context: log payments, income, and expenses in two taps, get reminded before every due date, and see clearly where the household's money goes.

As Full-Stack Lead, I designed the entire architecture: a Next.js 16 frontend with Dexie (IndexedDB) for a local-first model that always reads and writes to the device's local mirror, and a NestJS 12 backend with a cursor-based sync protocol (push/pull) that resolves conflicts with last-write-wins and never hard-deletes data.

The system supports shared households with capability-based roles (manage/write/read), multi-currency support with a daily SUNAT exchange rate, scheduled payments with push reminders, credit cards with statement/due dates, installments, loans between people, flexible budgets with goals, and automatic detection of recurring expenses.

Detalles Técnicos

  • RoleFull-Stack Lead
  • Year2026
  • FrontendNext.js 16, Dexie (IndexedDB)
  • BackendNestJS 12, Prisma 7
  • StatusIn Production
  • ArchitectureLocal-First PWA

STACK TECNOLÓGICO —

Next.js 16
TypeScript
Tailwind CSS 4
NestJS 12
Prisma 7
PostgreSQL 16
Redis 7
Installable PWA

Stack Completo:

Frontend:

Next.js 16, Dexie (IndexedDB), TanStack Query, shadcn/ui

Backend:

NestJS 12 (ESM), Prisma 7, Zod, BullMQ

Sync:

Cursor-based push/pull protocol, last-write-wins conflict resolution

Auth:

Google OAuth, local PIN, and passkeys (@simplewebauthn)

FUNCIONALIDADES —

Two-Tap Logging

Adding payments, income, and expenses is optimized to complete in two taps, with templates for frequent movements.

Local-First with Sync

The app always reads and writes to a local mirror (IndexedDB); a cursor-based sync engine pushes and pulls changes without blocking the UI, even offline.

Shared Household

Invitations, capability-based permissions (manage/write/read), and private transactions marked as 'mine' that don't affect the shared total.

Scheduled Payments

Push reminders before every due date, a payments calendar, projected end-of-month balance, and credit cards with statement/due dates.

Budget and Goals

Flexible per-category budget, savings goals with a target date, alerts at 80% and 100% of a limit, and an inline warning on likely duplicates.

Automation

Automatic categorization rules and recurring-expense detection after 3 similar occurrences, creating the scheduled payment in one tap.

Desafíos Técnicos y Soluciones

01. Conflict-Free Local-First Sync

I designed an idempotent cursor-based sync protocol (POST /sync/push, GET /sync/pull) where the client always reads from its local mirror and conflicts resolve by last-write-wins, with no hard deletes (deletedAt).

02. Integer Money, Never Float

Every amount is handled as amountMinor (cents) with its ISO 4217 currency, completely avoiding floating-point rounding errors in split, installment, and multi-currency balance calculations.

03. Capabilities Instead of Fixed Roles

The system checks for capabilities (household.manage | write | read) instead of fixed role names, letting each household name its own roles without touching code.

IMPACTO —

195+
Automated E2E tests
8/8
Product phases closed
PWA
Installable & Offline-First
amountMinor
Monetary precision
3
Occurrences to detect recurring expenses
Passkeys
Biometric unlock
Turborepo
pnpm monorepo

Notas Técnicas

pnpm + Turborepo monorepo (apps/web, apps/api, packages/shared). Next.js 16 frontend with Dexie (IndexedDB) for the local mirror and TanStack Query; NestJS 12 (ESM) backend with Prisma 7 over PostgreSQL 16, WebSockets via Socket.IO, queues with BullMQ and Redis 7. Authentication with Google OAuth, local PIN, and passkeys via @simplewebauthn. Deployed in production on a dedicated VPS behind Cloudflare and Caddy, with CI/CD through GitHub Actions.

YOUR FINANCES, IN ORDER.