🔴 Swish QR quiet zone too small — won't scan on desktop.
🔵 Isolate QR generation in its own try/catch.
🟡 Honor-system pay — no payment verification.
Advisory review — guest checkout (Swish Tier 0 + QR)
💡 Suggestion (low): /[\s+]/g strips every + from anywhere in the string, but the JSDoc above (L51) and the PR note describe stripping only a leading +. For valid phone numbers this is harmless (a + only ever appears as the international prefix), so no behaviour change — but the comment slightly overstates the intent. Either reword to "strips whitespace and any +", or for leading-only use replace(/\s/g, '').replace(/^\+/, ''). Either is fine.
💡 Suggestion (low): Swallowing the QR failure silently is the right UX — the Swish link + manual fallback still render. For production diagnosability, consider a console.warn(…) (or your error-telemetry hook) inside this catch so a recurring QR-library failure isn't invisible. No functional change either way.
✅ LGTM — solid, spec-compliant Swish QR fix with good test coverage. Ready to merge.
CI Investigation
You're right — I should have verified CI was green before finishing the PR. Here's what I found:
Investigation
All frontend CI steps pass locally (on the PR…
🔴 Critical — public unauthenticated create with no abuse protection. This endpoint is permitAll and writes a DB row per call with no rate limit, captcha, or pre-check. Trivial to abuse (orders-table flooding / cleanup cost). Suggest an IP rate limit + per-email throttle, requiring a successful plate/vehicle lookup first, or a lightweight captcha for the public create path.
🟠 Honor-system pay — no payment verification. confirmGuestPayment marks the order PROCESSING without confirming any Swish payment landed. Anyone with the token (or the customer) can mark an order paid without paying → BilHej mails a free letter. Acknowledged as Phase 0 and consistent with the authenticated confirmPayment, but harden with Swish Commerce callback verification before real money. Also: amountPaid / PAID status are never set on either path.
🟠 Guest token in the URL query string. The token is the customer's sole credential but here it's pushed into ?token=…, so it lands in browser history and the nginx access log (and risks Referer leakage). The magic-link /gast-order/:token is inherently URL-based, but this payment page needn't be — prefer sessionStorage / Pinia store / route state (or a #fragment) to keep the credential out of the query string.
🔵 Client email regex weaker than backend @Email. /\S+@\S+\.\S+/ lets a@b / x@.y through client-side, only to fail server-side with a confusing message. Mirror a stricter pattern or drop the client check and surface the backend @Email message.
🔵 Unused index. idx_orders_guest_email is created but findByGuestEmail was never added to OrderRepository (the PR body lists it, but only findByGuestToken is there), so there's no read path on guest_email yet. Either add the lookup now or defer the index until the email-link phase to avoid an unused schema object.
Verdict — solid, well-structured guest checkout. The security model (opaque 122-bit token, partial unique index, refuse-to-serve user-owned orders via the guest path) is thoughtful and the backend cleanly parallels OrderController without touching the JWT path. The main concerns are an unauthenticated create endpoint with no abuse protection, the guest token riding in the URL query string, and the honor-system /pay that should be hardened before real money. Advisory only — no blocking changes requested.
🔴 Critical — public unauthenticated create with no abuse protection. This endpoint is permitAll and writes a DB row per call with no rate limit, captcha, or pre-check. Trivial to abuse (orders-table flooding / cleanup cost). Suggest an IP rate limit + per-email throttle, requiring a successful plate/vehicle lookup first, or a lightweight captcha for the public create path.