# Bangla QR Platform

Three independently deployable systems plus two shared libraries.
Each system is one Phoenix 1.8 / LiveView 1.2 app (Bandit, Tailwind + daisyUI,
Postgres), shaped like the owner's `product_base_app`. One database per system.

| System | Project | Port | Database (dev) | Role |
|---|---|---|---|---|
| ① Acquirer | umbrella app `apps/bqr_acquirer` | 34100 | `bqr_acquirer_dev` | merchants, QR generation, credits, merchant webhooks |
| ② Switch simulator | standalone project `bqr_switch_sim/` | 34200 | `bqr_switch_dev` | stands in for NPSB: routing, idempotency, fault injection |
| ③ Issuer | umbrella app `apps/bqr_issuer` | 34300 | `bqr_issuer_dev` | payer accounts, scan/validate, pay, hold/finalize |

Shared libraries in the umbrella (no database):

* `bqr_core` – Bangla QR v2.0 codec: TLV, CRC16, parse, validate, build, merchant PAN.
* `bqr_protocol` – JSON message contract, HMAC signing, result codes.

`bqr_switch_sim` is **not** in the umbrella. It depends only on `bqr_protocol`
(path dep `../apps/bqr_protocol`), never `bqr_core`, so it behaves like a third
party and can be moved to its own repo later.

## Status

| Phase | State |
|---|---|
| 0 Skeleton, Phoenix apps, config, `/health` | done |
| 1 `bqr_core` | done, 38 tests |
| 2 `bqr_protocol` | done, 13 tests |
| 3 acquirer: merchants, PAN/BIN allocation, static + dynamic QR, HMAC partner API | done, 90 tests; see [docs/ACQUIRER_API.md](docs/ACQUIRER_API.md) |
| 4 switch: registry, /pay, routing, idempotency, ledger; acquirer credit handler | done; see [docs/SWITCH_API.md](docs/SWITCH_API.md) |
| 5 issuer: accounts, scan page, hold, pay, finalize, payer-app API | done; see [docs/ISSUER.md](docs/ISSUER.md) |
| Tools | [HTML QR generator](tools/qr-generator.html) and the [Flutter payer app](mobile/bqr_payer/README.md); run guide: [docs/RUNNING.md](docs/RUNNING.md) |
| 6 PENDING resolution, retries, fault injection | next |
| 7 webhooks, reconciliation, dashboards | not started |

Tests: umbrella `mix test` 324 (core 38, protocol 13, acquirer 133, issuer 140); switch simulator 68; Flutter app 65; HTML tool signing 7.
Whole chain checked live with `.\scripts\e2e.ps1`.

All three systems run as separate processes and answer `/health`.

## Run

Needs local PostgreSQL (`postgres`/`postgres` on `localhost:5432`), Elixir 1.18 / OTP 27, Node 22.

```bash
mix setup                                   # umbrella: deps, create + migrate DBs, assets (acquirer + issuer)
(cd bqr_switch_sim && mix setup)            # switch simulator, separate project

mix test                                    # umbrella (bqr_core, bqr_protocol, acquirer, issuer)
(cd bqr_switch_sim && mix test)

./scripts/dev.sh                            # or scripts\dev.ps1 on Windows: three separate processes

curl localhost:34100/health                  # acquirer
curl localhost:34200/health                  # switch simulator
curl localhost:34300/health                  # issuer
```

Run one system alone: `cd apps/bqr_acquirer && mix phx.server` (same for `apps/bqr_issuer`, `bqr_switch_sim`).

Releases: `MIX_ENV=prod mix release acquirer` or `issuer` from the umbrella root;
the switch has its own release inside `bqr_switch_sim/`.
Environment: `<NAME>_DATABASE_URL`, `<NAME>_SECRET_KEY_BASE`, `<NAME>_PORT`, `<NAME>_HOST`
where NAME is `ACQUIRER`, `SWITCH` or `ISSUER`.

Notes:

* daisyUI is vendored in each app's `assets/vendor/` (the `daisyui` git dep clones to ~5 GB); heroicons stays a git dep.
* On Windows without admin rights the LiveView colocated-assets symlink warning is disabled in config.
* `/health` returns `{"status":"ok","service":"bqr_<name>","db":"up"}`, or 503 `degraded` when the DB ping fails.

## Try the codec

```elixir
cd apps/bqr_core && iex -S mix
{:ok, qr} = BqrCore.build(type: :dynamic, acquirer_type: :psp, acquirer_id: "9999",
  merchant_pan: "9025019876543210", merchant_name: "ABC Retail Store",
  merchant_city: "Dhaka", mcc: "5411", amount: "250.00", reference_label: "INV10025")
{:ok, parsed, warnings} = BqrCore.decode(qr)
```

## Assumptions to confirm with NPSB

1. TLV length counts characters, not bytes, for non-ASCII values (`BqrCore.TLV`).
2. Tag 26 GUID value (`config :bqr_core, npsb_guid`) is a placeholder.
3. Merchant PAN = 6-digit BIN + zero-padded serial, 16 characters, no check digit.
4. The wire protocol in `bqr_protocol` is invented for the simulator; NPSB's real
   interface replaces it behind each system's adapter.
5. MDR, interchange (0% from 1 Oct 2026) and settlement timing come from press
   reports; check the Bangladesh Bank circulars before coding fee logic.

See `docs/ARCHITECTURE.md` for flows, states and the build plan.
