[PK-34] BE-6 payment QR endpoint + fictitious IBAN dev defaults #2

Merged
forge-admin merged 1 commit from feature/PK-34-payment-qr-bysquare into main 2026-05-16 22:42:43 +00:00
Owner

What

GET /api/orders/:id/payment-qr — pay-by-square QR generator pre potvrdzovaciu obrazovku objednávky.

Response 200 JSON:

{
  "order_id": "...",
  "amount_cents": 1599,
  "currency": "EUR",
  "variable_symbol": "1234567890",
  "specific_symbol": null,
  "iban": "SK68...",
  "recipient_name": "MASOX s.r.o.",
  "payment_string": "<bysquare encoded>",
  "qr_data_url": "data:image/png;base64,..."
}

Error shape:

  • 503 {error:"payment_not_configured"}PAYMENT_IBAN v env je prázdny.
  • 404 {error:"not_found"} — neplatné UUID, neexistujúca objednávka, alebo caller nemá prístup (žiadne leakovanie existencie objednávky).
  • 500 {error:"internal_error" | "invalid_iban" | ...} — interná chyba generátora (logované s req_id a order_id, žiadne PII).

Auth

  • Prihlásený user, ktorého userId matchuje order.userId, ALEBO
  • Guest s validným HMAC-SHA256 tokenom v ?token= (signed cez ORDER_TOKEN_SECRET, base64url).

Cudzí caller → 404. To je zhodné s plánovaným pravidlom v GET /api/orders/:id (BE-5).

Env

PAYMENT_IBAN=SK6811000000002612345678   # DEV ONLY — fiktívny IBAN
PAYMENT_RECIPIENT_NAME=MASOX s.r.o.
PAYMENT_SS=
ORDER_TOKEN_SECRET=...                  # zdieľaný s BE-2 / BE-5

503 fallback (keď je IBAN prázdny) ponechaný — len v defaultnom .env.example IBAN nastavený je, takže docker compose up z čistého stavu vracia 200 (reálny QR) pre pending objednávku.

Files

  • be/app/api/orders/[id]/payment-qr/route.ts — handler
  • be/lib/pay-by-square.ts — pure service (IBAN mod-97, bysquare payload, qrcode render)
  • be/lib/order-access.ts — auth adapter (Prisma Order lookup, camelCase fields)
  • be/lib/session.ts — cookie session lookup (Prisma Session model)
  • be/lib/order-tokens.ts — HMAC sign/verify (shared s BE-2/BE-5)
  • be/lib/payment-config.ts — env → PaymentConfig
  • be/__tests__/*.test.ts — 19 vitest testov (route 5 / pay-by-square 10 / tokens 4)
  • be/.env.example — fiktívny IBAN + komentár pre prod replacement
  • be/next.config.jsserverComponentsExternalPackages: ['bysquare']
  • be/package.jsonbysquare, qrcode, @types/qrcode, vitest
  • be/.gitignore — pridané *.tsbuildinfo

Tests

npm test
✓ __tests__/order-tokens.test.ts (4)
✓ __tests__/payment-qr-route.test.ts (5)
✓ __tests__/pay-by-square.test.ts (10)
Test Files  3 passed (3)
Tests       19 passed (19)

tsc --noEmit čisté.

Závislosti / heads-up

  • Beží nad BE-1 Prisma schémou (camelCase polia userId, guestEmail, variableSymbol, expiresAt, ...). OrderForPayment façade interne ponecháva snake_case pre čitateľnosť route handleru.
  • BE-5 pri vytvorení objednávky pre guest má v response zahrnúť qr_link = /api/orders/<id>/payment-qr?token=<signOrderToken(order.id)>signOrderToken exportované z lib/order-tokens.ts.
  • BE-2 cookie meno: masox_session (lookup do Session.id). Ak BE-2 vyberie iný názov, prosím zarovnáme.

Issue: PK-34

## What `GET /api/orders/:id/payment-qr` — pay-by-square QR generator pre potvrdzovaciu obrazovku objednávky. Response 200 JSON: ```json { "order_id": "...", "amount_cents": 1599, "currency": "EUR", "variable_symbol": "1234567890", "specific_symbol": null, "iban": "SK68...", "recipient_name": "MASOX s.r.o.", "payment_string": "<bysquare encoded>", "qr_data_url": "data:image/png;base64,..." } ``` Error shape: - `503 {error:"payment_not_configured"}` — `PAYMENT_IBAN` v env je prázdny. - `404 {error:"not_found"}` — neplatné UUID, neexistujúca objednávka, alebo caller nemá prístup (žiadne leakovanie existencie objednávky). - `500 {error:"internal_error" | "invalid_iban" | ...}` — interná chyba generátora (logované s `req_id` a `order_id`, žiadne PII). ## Auth - Prihlásený user, ktorého `userId` matchuje `order.userId`, ALEBO - Guest s validným HMAC-SHA256 tokenom v `?token=` (signed cez `ORDER_TOKEN_SECRET`, base64url). Cudzí caller → 404. To je zhodné s plánovaným pravidlom v `GET /api/orders/:id` (BE-5). ## Env ``` PAYMENT_IBAN=SK6811000000002612345678 # DEV ONLY — fiktívny IBAN PAYMENT_RECIPIENT_NAME=MASOX s.r.o. PAYMENT_SS= ORDER_TOKEN_SECRET=... # zdieľaný s BE-2 / BE-5 ``` 503 fallback (keď je IBAN prázdny) ponechaný — len v defaultnom `.env.example` IBAN nastavený **je**, takže `docker compose up` z čistého stavu vracia 200 (reálny QR) pre `pending` objednávku. ## Files - `be/app/api/orders/[id]/payment-qr/route.ts` — handler - `be/lib/pay-by-square.ts` — pure service (IBAN mod-97, bysquare payload, qrcode render) - `be/lib/order-access.ts` — auth adapter (Prisma `Order` lookup, camelCase fields) - `be/lib/session.ts` — cookie session lookup (Prisma `Session` model) - `be/lib/order-tokens.ts` — HMAC sign/verify (shared s BE-2/BE-5) - `be/lib/payment-config.ts` — env → `PaymentConfig` - `be/__tests__/*.test.ts` — 19 vitest testov (route 5 / pay-by-square 10 / tokens 4) - `be/.env.example` — fiktívny IBAN + komentár pre prod replacement - `be/next.config.js` — `serverComponentsExternalPackages: ['bysquare']` - `be/package.json` — `bysquare`, `qrcode`, `@types/qrcode`, `vitest` - `be/.gitignore` — pridané `*.tsbuildinfo` ## Tests ``` npm test ✓ __tests__/order-tokens.test.ts (4) ✓ __tests__/payment-qr-route.test.ts (5) ✓ __tests__/pay-by-square.test.ts (10) Test Files 3 passed (3) Tests 19 passed (19) ``` `tsc --noEmit` čisté. ## Závislosti / heads-up - Beží nad BE-1 Prisma schémou (camelCase polia `userId`, `guestEmail`, `variableSymbol`, `expiresAt`, ...). `OrderForPayment` façade interne ponecháva snake_case pre čitateľnosť route handleru. - BE-5 pri vytvorení objednávky pre guest má v response zahrnúť `qr_link = /api/orders/<id>/payment-qr?token=<signOrderToken(order.id)>` — `signOrderToken` exportované z `lib/order-tokens.ts`. - BE-2 cookie meno: `masox_session` (lookup do `Session.id`). Ak BE-2 vyberie iný názov, prosím zarovnáme. Issue: PK-34
Adds GET /api/orders/:id/payment-qr that returns a pay-by-square QR
(payload + PNG data URL) for the order owner (session) or a guest with a
HMAC-signed token query param. Falls back to 503 when PAYMENT_IBAN is
unset.

- lib/pay-by-square.ts: pure service (IBAN mod-97 check, bysquare payload,
  qrcode render) with explicit input validation (amount, VS, SS, message).
- lib/order-access.ts: auth adapter, queries the BE-1 Order model via Prisma
  (camelCase fields) and exposes a snake_case facade to the route.
- lib/session.ts: cookie-based session lookup against BE-1 Session model.
- lib/order-tokens.ts: HMAC-SHA256 sign/verify for guest URL tokens (shared
  with BE-2/BE-5 via ORDER_TOKEN_SECRET).
- lib/payment-config.ts: env-driven PaymentConfig + UUID guard.
- app/api/orders/[id]/payment-qr/route.ts: Next.js App Router handler with
  structured JSON logging and 404/503/500 error shaping.
- __tests__: 19 vitest tests (route 5, pay-by-square 10, order-tokens 4).
- .env.example: fictitious IBAN SK6811000000002612345678 +
  PAYMENT_RECIPIENT_NAME=MASOX s.r.o. (DEV ONLY; replace before prod). 503
  fallback retained for the empty-IBAN case.
- next.config.js: serverComponentsExternalPackages: ['bysquare'].
- package.json: bysquare, qrcode, @types/qrcode, vitest + test scripts.
forge-admin force-pushed feature/PK-34-payment-qr-bysquare from 71bac896de to 58d75b86d8 2026-05-16 22:42:33 +00:00 Compare
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
forge-admin/masox-eshop!2
No description provided.