From b4fdcbff9edc373511a38c1ee4e1d08f5b0075bb Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Sat, 18 Jul 2026 11:32:35 +0000 Subject: [PATCH] fix(guest): make Swish QR scannable on guest payment page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The guest payment page (GuestPaymentRedirect.vue) still used the old non-compliant QR settings that were already fixed in PaymentRedirect.vue (commit 573153b). Desktop users scanning the QR with the Swish app could not pay via QR on the guest checkout path. Root cause: GuestPaymentRedirect.vue was not updated when the auth-path QR fix was applied. It kept: - margin: 2 (half the ISO/IEC 18004 minimum of 4 modules) - color #111827 (dark gray; Swish spec requires pure black) - width: 224 (borderline for ~80-90 char pre-fill URLs) Changes: - GuestPaymentRedirect.vue: QR options now match PaymentRedirect.vue exactly — margin 4, width 288, errorCorrectionLevel 'M', color #000000/#ffffff - CSS .payment__qr-img: 224px -> 288px to match the larger QR - GuestPaymentRedirect.spec.ts: renamed 'renders QR code after loading swish info' to 'renders QR code with spec-compliant settings' and added assertions verifying margin, width, and color options Closes #22 --- .../src/__tests__/GuestPaymentRedirect.spec.ts | 6 +++++- frontend/src/pages/GuestPaymentRedirect.vue | 16 +++++++++++----- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/frontend/src/__tests__/GuestPaymentRedirect.spec.ts b/frontend/src/__tests__/GuestPaymentRedirect.spec.ts index 7e31b4b..81a606f 100644 --- a/frontend/src/__tests__/GuestPaymentRedirect.spec.ts +++ b/frontend/src/__tests__/GuestPaymentRedirect.spec.ts @@ -124,12 +124,16 @@ describe('GuestPaymentRedirect', () => { }) }) - it('renders QR code after loading swish info', async () => { + it('renders QR code with spec-compliant settings', async () => { const { wrapper } = await mountPage() await vi.waitFor(() => { expect(wrapper.find('.payment__qr-img').exists()).toBe(true) }) expect(mockToDataURL).toHaveBeenCalledTimes(1) + const options = mockToDataURL.mock.calls[0][1] + expect(options.margin).toBe(4) + expect(options.width).toBe(288) + expect(options.color).toEqual({ dark: '#000000', light: '#ffffff' }) }) it('renders Swish payment link', async () => { diff --git a/frontend/src/pages/GuestPaymentRedirect.vue b/frontend/src/pages/GuestPaymentRedirect.vue index f3261b2..a4c8e67 100644 --- a/frontend/src/pages/GuestPaymentRedirect.vue +++ b/frontend/src/pages/GuestPaymentRedirect.vue @@ -52,9 +52,15 @@ onMounted(async () => { if (swishPaymentUrl.value) { qrDataUrl.value = await QRCode.toDataURL(swishPaymentUrl.value, { - width: 224, - margin: 2, - color: { dark: '#111827', light: '#ffffff' }, + // Swish requires a reliably scannable black-on-white QR. The previous + // settings (margin 2, #111827, 224px) produced a 2-module quiet zone + // — half the QR spec minimum — which the Swish app's scanner fails to + // read when scanning off a screen. Use the spec-compliant 4-module + // quiet zone, pure black, and larger modules. + width: 288, + margin: 4, + errorCorrectionLevel: 'M', + color: { dark: '#000000', light: '#ffffff' }, }) } } catch { @@ -269,8 +275,8 @@ async function confirmPayment() { } .payment__qr-img { - width: 224px; - height: 224px; + width: 288px; + height: 288px; border-radius: var(--radius-md); margin: 0 auto var(--space-sm); }