-
Notifications
You must be signed in to change notification settings - Fork 0
Installation
- 🟢 Node.js 20 LTS or higher
- 🐳 Docker + Docker Compose
- 🍃 MongoDB Atlas cluster (M10 or higher; free tier does not support Queryable Encryption)
- 🔑 AWS KMS key or
PSP_KMS_PROVIDER=localfor offline development (see §1)
The stack runs three services: the backend API (port 8081), the PSP frontend portal (port 8080), and the external merchant app (port 8082).
The repository already ships a committed .env at the root. Open it and set your values in place (there is no .env.example). Application variables use the PSP_ prefix; only MONGODB_*, AWS_*, NODE_ENV, HOST, PORT and NEXT_PUBLIC_* are unprefixed.
Set at minimum:
| Variable | Required | Notes |
|---|---|---|
MONGODB_URI |
Yes | Atlas connection string (mongodb+srv://...) |
MONGODB_DB_NAME |
Yes | e.g. fsi-psp-leafy-pay-store
|
PSP_KMS_PROVIDER |
Yes |
aws or local (offline dev) |
PSP_KMS_LOCAL_MASTER_KEY |
When PSP_KMS_PROVIDER=local
|
Generate with npm run setup:key:master (see below) |
AWS_ACCESS_KEY_ID |
When PSP_KMS_PROVIDER=aws
|
IAM user with KMS Decrypt permission |
AWS_SECRET_ACCESS_KEY |
When PSP_KMS_PROVIDER=aws
|
(same IAM user as above) |
AWS_CMK_ARN |
When PSP_KMS_PROVIDER=aws
|
ARN of your Customer Master Key |
AWS_REGION |
When PSP_KMS_PROVIDER=aws
|
e.g. us-east-1
|
PSP_JWT_SECRET |
Yes | Generate: node -e "require('crypto').randomBytes(32).toString('hex')"
|
PSP_OAUTH_KEY_PROVIDER + backend/keys/
|
Yes (auth/CIBA) | RS256 OAuth signing keys; generate with npm run setup:key:rsa
|
NEXT_PUBLIC_PSP_URL_BACKEND_PUBLIC |
Yes | Browser-facing backend URL, e.g. http://localhost:8081
|
PSP_MERCHANT_* |
For the merchant app | Merchant OAuth client id/secret, PSP URLs (see merchant/ env) |
MongoDB Queryable Encryption requires a shared library (mongo_crypt_v1) that is not bundled with the npm packages.
Step 1: Download the library:
Go to mongodb.com/try/download/enterprise
→ Select your platform → "Cryptography Library (crypt_shared)"
Step 2: Add to .env:
# Windows
MONGODB_CRYPT_SHARED_LIB_PATH=C:/Program Files/MongoDB/Shared Library/bin/mongo_crypt_v1.dll
# macOS
MONGODB_CRYPT_SHARED_LIB_PATH=/usr/local/lib/mongo_crypt_v1.dylib
# Linux
MONGODB_CRYPT_SHARED_LIB_PATH=/usr/lib/mongo_crypt_v1.soIf
MONGODB_CRYPT_SHARED_LIB_PATHis not set, the backend will try common default install paths automatically. If the library is not found anywhere, it logs a warning with download instructions.
Use the built-in generator, it produces the correct 96-byte key in a single command:
npm run setup:key:masterThe command prints the exact line ready to paste into your .env:
PSP_KMS_LOCAL_MASTER_KEY=YKwocS2+PYvY... ← copy this line
Then set:
PSP_KMS_PROVIDER=local
PSP_KMS_LOCAL_MASTER_KEY=<paste the generated value>For the OAuth/OIDC + CIBA signing keys, run npm run setup:key:rsa (writes to backend/keys/).
npm run setup
# Installs root + frontend + backend + merchant node_modulesnpm run setup:db # Creates QE collections, key vault, indexes; run once per cluster
npm run setup:seed # Inserts synthetic BIAN-compliant demo data (idempotent)
⚠️ Note:setup:dbrequires a live Atlas cluster with QE-compatible tier (M10+) and valid KMS credentials.
# Option A: Development mode with hot reload (recommended)
npm run dev
# Starts three services concurrently: backend :8081, PSP frontend :8080, merchant app :8082
# Option B: Docker Compose (full containerised stack)
docker compose upOpen http://localhost:8080 for the PSP portal, and http://localhost:8082 for the merchant app.
The test suite is organised in three levels following the IST demo testing pyramid. All tests live in test/ at the repository root.
test/
├── setup.ts ← global Vitest setup
├── backend/
│ ├── unit/services/ ← service layer, mocked DB, no Atlas required
│ └── integration/routes/ ← API routes, requires TEST_MONGODB_URI
└── frontend/
├── unit/lib/ ← auth helpers, constants, API client
└── e2e/ ← Playwright browser flows
Run against mocked dependencies. No Atlas connection required.
npm run test:unit
# Runs Vitest on test/backend/unit/ and test/frontend/unit/What is covered:
| File | What it tests |
|---|---|
test/backend/unit/services/auth.service.test.ts |
JWT signing, bcrypt compare, 401 cases, no password hash in response |
test/backend/unit/services/cardTransaction.service.test.ts |
Fraud trigger logic, threshold env var, Level-1 field stripping |
test/backend/unit/services/customerAgreement.service.test.ts |
QE field stripping from response, search predicate correctness |
test/backend/unit/services/paymentCard.service.test.ts |
Card creation, ADR-003 token stored as surrogate |
test/backend/unit/services/fraudDiagnosis.service.test.ts |
Case creation, audit log entry, pagination filters |
test/frontend/unit/lib/auth.test.ts |
Cookie read/write/clear, JWT decode, expiry check |
test/frontend/unit/lib/constants.test.ts |
All 5 demo users defined, role labels, severity/status color maps |
Spin up a real Fastify app against a test Atlas cluster with QE active.
Requires TEST_MONGODB_URI to be set. Tests skip gracefully when the variable is absent, so CI can run without Atlas.
# Set the test cluster URI (separate from your demo cluster is recommended)
export TEST_MONGODB_URI="mongodb+srv://..."
export TEST_MONGODB_DB_NAME="pci_dss_test"
npm run test:integration
# Runs Vitest on test/backend/integration/ and test/frontend/integration/What is covered:
| File | FR coverage |
|---|---|
test/backend/integration/routes/auth.test.ts |
FR-v1-05: login 201/401, user list, auth guard, public routes |
test/backend/integration/routes/cardTransaction.test.ts |
FR-v1-03/04: POST transaction, fraud auto-creation (amount + MCC), GET by ID (Level-1 projection), POST card, GET cases paginated |
npm test
# Equivalent to: npm run test:unit && npm run test:integrationnpm run test:watch
# Vitest reruns affected tests on every file saveBrowser-driven tests covering the primary demo flows. Requires the frontend dev server. Playwright starts it automatically when running locally.
# One-time browser install (first run only)
npx playwright install chromium
# Run all E2E tests
npm run test:e2e
# Interactive UI mode (recommended for debugging)
npm run test:e2e:ui
# Debug a single test with browser inspector
npm run test:e2e:debugWhat is covered:
| Spec file | Flow |
|---|---|
test/frontend/e2e/simulator-payment.spec.ts |
FR-v1-01: Simulator 3-step checkout, card masking, PCI DSS note, fraud alert |
test/frontend/e2e/simulator-investigation.spec.ts |
FR-v1-02: Search by QE field, case table, case detail, encryption badges, raw document toggle |
test/frontend/e2e/demo-auth.spec.ts |
FR-v1-05: Login, role-based redirect (analyst → investigation, customer → payment), auth guard, sign out |
test/frontend/e2e/demo-payment.spec.ts |
FR-v1-03: Authenticated checkout, fraud alert on creation, error state |
test/frontend/e2e/demo-investigation.spec.ts |
FR-v1-04: Case table, severity badges, case detail, audit log, raw document toggle |
E2E against staging:
BASE_URL=https://my-demo.staging.example.com npm run test:e2enpm test && npm run test:e2eBefore merging or releasing, confirm:
-
npm testpasses with zero failures - Every non-trivial service function has a unit test
- Every API route has an integration test covering the happy path
- Primary demo flow covered by an E2E test
- At least one error state covered by an E2E test
| Command | Description |
|---|---|
npm run setup |
Install root + frontend + backend + merchant dependencies |
npm run setup:key:master |
Generate a 96-byte PSP_KMS_LOCAL_MASTER_KEY for offline/local KMS |
npm run setup:key:rsa |
Generate the OAuth/OIDC + CIBA RS256 signing keys (backend/keys/) |
npm run setup:db |
Create QE collections, provision DEKs and indexes |
npm run setup:db:drop |
Drop the demo collections (destructive; use before a clean re-setup) |
npm run setup:check |
Validate the database/setup |
npm run setup:seed |
Insert synthetic demo data (auto-generates JSON files if missing; idempotent) |
npm run setup:generate |
Re-generate fresh JSON seed data files (use before re-seeding with new data). Manual files (authDomains.json, customerCreditRatings.json) are never overwritten. |
npm run setup:integrity-issues |
Seed deliberate integrity-issue fixtures |
npm run dev |
Start backend, PSP frontend and merchant app concurrently (hot reload) |
npm run dev:frontend |
Start only the PSP frontend (:8080) |
npm run dev:backend |
Start only the Fastify API (:8081) |
npm run dev:merchant |
Start only the merchant app (:8082) |
npm run build |
Build frontend, backend and merchant for production |
npm run test |
Run unit + integration + e2e |
npm run test:ci |
Run unit + integration (no e2e) |
npm run test:unit |
Unit tests only (Vitest), no Atlas required |
npm run test:integration |
Integration tests (Vitest), requires TEST_MONGODB_URI
|
npm run test:e2e |
Playwright E2E tests |
npm run deploy / deploy:docker / deploy:kube
|
Deploy (Kubernetes via tools/kube.ts, or docker compose) |
npm run type-check |
TypeScript type check (backend + frontend, no emit) |
VS Code debug configurations are provided in .vscode/launch.json:
| Configuration | What it does |
|---|---|
Backend: Debug (ts-node-dev) |
Launches Fastify with --inspect on port 9229 |
Frontend: Debug Next.js |
Launches Next.js with --inspect on port 9230 |
Full Stack: Backend + Frontend |
Compound: starts both simultaneously |
Backend: Run Jest Unit Tests |
Runs test:unit with the VS Code debugger attached |
Backend: Run Jest Integration Tests |
Runs test:integration with the VS Code debugger attached |
Backend: Attach to Running (port 9229) |
Attach to an already-running backend process |
Frontend: Attach to Running (port 9230) |
Attach to an already-running Next.js process |
Set breakpoints in any TypeScript source file and launch the relevant configuration from the Run and Debug panel (Ctrl+Shift+D / ⇧⌘D).
The demo ships with a lightweight data-administration panel at /admin for managing seed data and running setup operations during development. It is not part of the demo narrative and should not be shown to prospects.
Access is gated by two environment variables in .env:
PSP_ADM_USER=admin # admin username
PSP_ADM_PASS=<sha256-hash> # SHA-256 hash of the admin passwordGenerate the hash for your chosen password:
echo -n "mypassword" | sha256sum # Linux / macOS
# or
node -e "require('crypto').createHash('sha256').update('mypassword').digest('hex')" | Write-HostThese variables are optional. If absent, the admin login always returns 401.