From 0be5b76b814ff65921c5053f462c36f2b4e5e216 Mon Sep 17 00:00:00 2001 From: Harsh Mahajan <127186841+HarshMN2345@users.noreply.github.com> Date: Thu, 22 Jan 2026 15:10:54 +0530 Subject: [PATCH 1/3] feat: require state for existing US payment methods --- .../billing/updateStateModal.svelte | 105 ++++++++++++++++++ src/lib/sdk/billing.ts | 1 + .../account/payments/paymentMethods.svelte | 27 +++++ .../billing/paymentMethods.svelte | 27 +++++ 4 files changed, 160 insertions(+) create mode 100644 src/lib/components/billing/updateStateModal.svelte diff --git a/src/lib/components/billing/updateStateModal.svelte b/src/lib/components/billing/updateStateModal.svelte new file mode 100644 index 0000000000..f7804964d9 --- /dev/null +++ b/src/lib/components/billing/updateStateModal.svelte @@ -0,0 +1,105 @@ + + + + + State information is required for US payment methods to apply correct taxes and meet U.S. + legal requirements. + + + + {#if paymentMethod} + + + + ending in {paymentMethod.last4} + + + {paymentMethod.country} + + + {/if} + + + + To complete the billing information, select your state so we can apply the correct + taxes and meet U.S. legal requirements. + + + + ({ + label: stateOption.name, + value: stateOption.abbreviation, + id: stateOption.abbreviation.toLowerCase() + }))} /> + + + + + + diff --git a/src/lib/sdk/billing.ts b/src/lib/sdk/billing.ts index 5ee5232633..048dbb3d12 100644 --- a/src/lib/sdk/billing.ts +++ b/src/lib/sdk/billing.ts @@ -22,6 +22,7 @@ export type PaymentMethodData = { name: string; mandateId?: string; lastError?: string; + state?: string; }; export type PaymentList = { diff --git a/src/routes/(console)/account/payments/paymentMethods.svelte b/src/routes/(console)/account/payments/paymentMethods.svelte index 8e53a8d3da..9caea2e1f5 100644 --- a/src/routes/(console)/account/payments/paymentMethods.svelte +++ b/src/routes/(console)/account/payments/paymentMethods.svelte @@ -9,6 +9,7 @@ import DeletePaymentModal from './deletePaymentModal.svelte'; import { hasStripePublicKey, isCloud } from '$lib/system'; import PaymentModal from '$lib/components/billing/paymentModal.svelte'; + import UpdateStateModal from '$lib/components/billing/updateStateModal.svelte'; import { IconDotsHorizontal, IconInfo, @@ -33,6 +34,8 @@ let selectedLinkedOrgs: Organization[] = []; let showDelete = false; let showEdit = false; + let showUpdateState = false; + let paymentMethodNeedingState: PaymentMethodData | null = null; let isLinked = false; $: orgList = $organizationList.teams as unknown as Organization[]; @@ -49,6 +52,27 @@ ); $: hasLinkedOrgs = filteredMethods.some((method) => linkedMethodIds.has(method.$id)); $: hasPaymentError = filteredMethods.some((method) => method?.lastError || method?.expired); + + // Check for US payment methods without state + $: { + if ($paymentMethods?.paymentMethods && !showUpdateState && !paymentMethodNeedingState) { + const usMethodWithoutState = $paymentMethods.paymentMethods.find( + (method: PaymentMethodData) => + method?.country?.toLowerCase() === 'us' && + (!method.state || method.state.trim() === '') && + !!method.last4 + ); + if (usMethodWithoutState) { + paymentMethodNeedingState = usMethodWithoutState; + showUpdateState = true; + } + } + } + + // Reset when modal is closed + $: if (!showUpdateState && paymentMethodNeedingState) { + paymentMethodNeedingState = null; + } @@ -170,3 +194,6 @@ bind:showDelete linkedOrgs={selectedLinkedOrgs} /> {/if} +{#if showUpdateState && paymentMethodNeedingState && isCloud && hasStripePublicKey} + +{/if} diff --git a/src/routes/(console)/organization-[organization]/billing/paymentMethods.svelte b/src/routes/(console)/organization-[organization]/billing/paymentMethods.svelte index 445c65d107..449f165d48 100644 --- a/src/routes/(console)/organization-[organization]/billing/paymentMethods.svelte +++ b/src/routes/(console)/organization-[organization]/billing/paymentMethods.svelte @@ -13,6 +13,7 @@ import ReplaceCard from './replaceCard.svelte'; import EditPaymentModal from '$routes/(console)/account/payments/editPaymentModal.svelte'; import PaymentModal from '$lib/components/billing/paymentModal.svelte'; + import UpdateStateModal from '$lib/components/billing/updateStateModal.svelte'; import { user } from '$lib/stores/user'; import { ActionMenu, @@ -44,6 +45,8 @@ let showEdit = false; let showDelete = false; let showReplace = false; + let showUpdateState = false; + let paymentMethodNeedingState: PaymentMethodData | null = null; let isSelectedBackup = false; async function addPaymentMethod(paymentMethodId: string) { @@ -96,6 +99,27 @@ primaryMethod?.expired || backupMethod?.lastError || backupMethod?.expired; + + // Check for US payment methods without state + $: { + if (methods?.paymentMethods && !showUpdateState && !paymentMethodNeedingState) { + const usMethodWithoutState = methods.paymentMethods.find( + (method: PaymentMethodData) => + method?.country?.toLowerCase() === 'us' && + (!method.state || method.state.trim() === '') && + !!method.last4 + ); + if (usMethodWithoutState) { + paymentMethodNeedingState = usMethodWithoutState; + showUpdateState = true; + } + } + } + + // Reset when modal is closed + $: if (!showUpdateState && paymentMethodNeedingState) { + paymentMethodNeedingState = null; + } @@ -323,3 +347,6 @@ isBackup={isSelectedBackup} disabled={organization?.billingPlan !== BillingPlan.FREE && !hasOtherMethod} /> {/if} +{#if showUpdateState && paymentMethodNeedingState && isCloud && hasStripePublicKey} + +{/if} From 998cea38010bba1de2c2e8895a167c4069486541 Mon Sep 17 00:00:00 2001 From: Harsh Mahajan <127186841+HarshMN2345@users.noreply.github.com> Date: Thu, 22 Jan 2026 15:55:19 +0530 Subject: [PATCH 2/3] Update src/lib/components/billing/updateStateModal.svelte Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- src/lib/components/billing/updateStateModal.svelte | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/lib/components/billing/updateStateModal.svelte b/src/lib/components/billing/updateStateModal.svelte index f7804964d9..b431a8b1ad 100644 --- a/src/lib/components/billing/updateStateModal.svelte +++ b/src/lib/components/billing/updateStateModal.svelte @@ -23,6 +23,11 @@ let isSubmitting = $state(false); let error = $state(null); + $: if (!show) { + selectedState = ''; + error = null; + } + async function handleSubmit() { if (!selectedState) { error = 'Please select a state'; From 5b8c0a55dd3f59c210e417668ae36501e7fd42c6 Mon Sep 17 00:00:00 2001 From: Harsh Mahajan <127186841+HarshMN2345@users.noreply.github.com> Date: Thu, 22 Jan 2026 16:01:03 +0530 Subject: [PATCH 3/3] one comment --- src/lib/components/billing/updateStateModal.svelte | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/lib/components/billing/updateStateModal.svelte b/src/lib/components/billing/updateStateModal.svelte index b431a8b1ad..603b09bf4b 100644 --- a/src/lib/components/billing/updateStateModal.svelte +++ b/src/lib/components/billing/updateStateModal.svelte @@ -23,10 +23,12 @@ let isSubmitting = $state(false); let error = $state(null); - $: if (!show) { - selectedState = ''; - error = null; - } + $effect(() => { + if (!show) { + selectedState = ''; + error = null; + } + }); async function handleSubmit() { if (!selectedState) {