From 73dd2c2dea63710e0d988b6e5ff42b942d66a5e9 Mon Sep 17 00:00:00 2001 From: Stephen Adamson Date: Fri, 31 Jul 2026 11:53:06 +0100 Subject: [PATCH] Fix regression: UserPaymentModal 'Use a new card' overridden by auto-select effect MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the saved-card auto-select effect INTO CardSelection, where it owns both cards and showNewCardForm. The effect is guarded by !showNewCardForm so the 'Use a new card' click (selectedCardId = '') is not immediately re-set to the default card — previously the parent's unguarded effect (moved from the OLD showNewCardForm guard during the CardSelection refactor) silently charged the saved default card instead of the newly entered card. CardSelection mounts fresh each time the modal opens (conditional {#if} mounting in UserBookingModal and BookingFlow), so the auto-select fires once on load, exactly like the tip flows' one-shot load-time selection. --- .../src/lib/components/payments/CardSelection.svelte | 11 +++++++++++ .../lib/components/payments/UserPaymentModal.svelte | 8 -------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/frontend/src/lib/components/payments/CardSelection.svelte b/frontend/src/lib/components/payments/CardSelection.svelte index 3f6a9b5..3b44591 100644 --- a/frontend/src/lib/components/payments/CardSelection.svelte +++ b/frontend/src/lib/components/payments/CardSelection.svelte @@ -36,6 +36,17 @@ // When no saved cards exist the form shows by default. let showNewCardForm = $state(false); + // Auto-select the default saved card when cards first load. Guarded by + // !showNewCardForm so the "Use a new card" click (selectedCardId = '') is + // NOT immediately overridden back to the default card — which would + // silently charge the wrong card on submit. + $effect(() => { + if (cards.length > 0 && !selectedCardId && !showNewCardForm) { + const defaultCard = cards.find((c) => c.is_default) ?? cards[0]; + selectedCardId = defaultCard.id; + } + }); + // Blur-based validation state (matches the pattern used across all card flows) let cardNumberTouched = $state(false); let cardExpiryTouched = $state(false); diff --git a/frontend/src/lib/components/payments/UserPaymentModal.svelte b/frontend/src/lib/components/payments/UserPaymentModal.svelte index 93e63e5..79eeb80 100644 --- a/frontend/src/lib/components/payments/UserPaymentModal.svelte +++ b/frontend/src/lib/components/payments/UserPaymentModal.svelte @@ -46,14 +46,6 @@ let stamps = $state(0); let useLoyalty = $state(false); - // Auto-select first saved card when methods load - $effect(() => { - if (paymentMethods.length > 0 && !selectedCardId) { - const defaultCard = paymentMethods.find((m) => m.is_default) ?? paymentMethods[0]; - selectedCardId = defaultCard.id; - } - }); - // New card form fields (bound into CardSelection) let newCardNumber = $state(''); let newCardExpiry = $state('');