Move money, move forward.
🔗 Live demo: payflowapp.vercel.app · API: payflow-8ohe.onrender.com
Payflow is a wallet transfer app with an Express API and a Vite/React frontend. It runs with zero setup locally (a JSON data store, no database needed) and switches to Postgres automatically when a DATABASE_URL is provided — so the same code works for quick local review and for a real deployment where accounts persist.
- Node.js + Express
- Postgres when
DATABASE_URLis set, local JSON store otherwise - React + Vite
- Jest + Supertest
- Sign in with seeded local demo accounts
- View your wallet balance
- Transfer funds to another local user by email
- Review your own transaction history
- Reset the local dataset at any time
src/backend app, routes, models, middleware, local data storefrontend/React clienttests/backend request testsdata/local JSON persistence file created by the app.github/issue templates, PR template, and CIdocs/maintainer notes and Wave issue ideas
- no database required to get started — falls back to a local JSON store
- no Docker required
- contributor can run everything with terminal commands only
- add a
DATABASE_URL(e.g. a free Neon Postgres) when you want persistent accounts
That makes it a better fit for review programs and newcomer-friendly open source contribution.
npm install
cd frontend
npm installCopy .env.example to .env.
Minimal local example (JSON store, no database):
PORT=5000
JWT_SECRET=your-local-secret
CLIENT_ORIGINS=http://localhost:5173,http://127.0.0.1:5173To use Postgres instead (persistent accounts), also set:
DATABASE_URL=postgresql://user:password@host/dbname?sslmode=requireWhen DATABASE_URL is present the API creates its tables automatically on first run.
npm run seedThis resets the data store (JSON file or Postgres, whichever is configured) and restores demo accounts:
alex@payflow.localmaya@payflow.localsam@payflow.local
Shared demo password:
12345678
npm run devcd frontend
npm run devThe frontend expects the API at http://localhost:5000 unless VITE_API_URL is set.
npm run devstart API in watch modenpm run startstart API oncenpm run seedreset local demo datanpm testrun backend tests
npm run devstart Vite dev servernpm run buildbuild production assets
POST /auth/registerPOST /auth/loginGET /auth/me
GET /wallet/balancePOST /wallet/transfer
GET /transactions
All wallet and transaction routes require Authorization: Bearer <token>.
The backend stores users, wallets, and transactions in one of two interchangeable backends:
- Postgres — used when
DATABASE_URLis set. Tables are created automatically. - Local JSON — the default fallback, written to
data/payflow.json. Override the path withDATA_FILEin.env.
Transfers run as a single atomic operation (a locked DB transaction on Postgres), so balances stay consistent.
- Passwords are hashed with
bcryptjs - Transfer sender identity is derived from the signed JWT, not the request body
- Balance and transaction reads are scoped to the authenticated user
- Transfer validation uses recipient email and positive numeric amounts
See CONTRIBUTING.md for setup, branching, and review expectations.
For Wave/maintainer prep, start with docs/wave-issue-backlog.md.