Skip to content

Commit a0724d5

Browse files
Merge pull request #2164 from appwrite/fix-add-payment-method
Feat: State selector US
2 parents 882c8e7 + 576365f commit a0724d5

File tree

10 files changed

+494
-51
lines changed

10 files changed

+494
-51
lines changed

e2e/steps/pro-project.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ export async function enterCreditCard(page: Page) {
2020
await stripe.locator('id=Field-cvcInput').fill('123');
2121
await stripe.locator('id=Field-countryInput').selectOption('DE');
2222
await dialog.getByRole('button', { name: 'Add', exact: true }).click();
23+
await page.locator('id=state-picker').click(); // open dropdown
24+
await page.getByRole('option', { name: 'Alabama' }).click();
25+
await dialog.getByRole('button', { name: 'Add', exact: true }).click();
2326
await dialog.waitFor({
2427
state: 'hidden'
2528
});

src/lib/components/billing/paymentBoxes.svelte

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
import { initializeStripe, unmountPaymentElement } from '$lib/stores/stripe';
66
import { Badge, Card, Layout } from '@appwrite.io/pink-svelte';
77
import type { PaymentMethodData } from '$lib/sdk/billing';
8+
import type { PaymentMethod } from '@stripe/stripe-js';
9+
import StatePicker from './statePicker.svelte';
810
911
export let methods: PaymentMethodData[];
1012
export let group: string;
@@ -14,6 +16,9 @@
1416
export let disabledCondition: string = null;
1517
export let setAsDefault = false;
1618
export let showSetAsDefault = false;
19+
export let showState = false;
20+
export let paymentMethod: PaymentMethod | null = null;
21+
export let state: string = '';
1722
1823
let element: HTMLDivElement;
1924
let loader: HTMLDivElement;
@@ -79,25 +84,29 @@
7984
{/each}
8085
<Card.Selector title="Add new payment method" name="$new" bind:group value="$new" />
8186
{#if group === '$new'}
82-
<InputText
83-
id="name"
84-
label="Cardholder name"
85-
placeholder="Cardholder name"
86-
bind:value={name}
87-
required
88-
autofocus={true} />
87+
{#if showState}
88+
<StatePicker card={paymentMethod} bind:state />
89+
{:else}
90+
<InputText
91+
id="name"
92+
label="Cardholder name"
93+
placeholder="Cardholder name"
94+
bind:value={name}
95+
required
96+
autofocus={true} />
8997

90-
<div class="aw-stripe-container" data-private>
91-
<div class="loader-container" bind:this={loader}>
92-
<div class="loader"></div>
98+
<div class="aw-stripe-container" data-private>
99+
<div class="loader-container" bind:this={loader}>
100+
<div class="loader"></div>
101+
</div>
102+
<div bind:this={element}></div>
93103
</div>
94-
<div bind:this={element}></div>
95-
</div>
96-
{#if showSetAsDefault}
97-
<InputChoice
98-
bind:value={setAsDefault}
99-
id="default"
100-
label="Set as default payment method for this organization" />
104+
{#if showSetAsDefault}
105+
<InputChoice
106+
bind:value={setAsDefault}
107+
id="default"
108+
label="Set as default payment method for this organization" />
109+
{/if}
101110
{/if}
102111
{/if}
103112
</Layout.Stack>

src/lib/components/billing/paymentModal.svelte

Lines changed: 51 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22
import { FakeModal } from '$lib/components';
33
import { InputText, Button } from '$lib/elements/forms';
44
import { createEventDispatcher, onMount } from 'svelte';
5-
import { initializeStripe, submitStripeCard } from '$lib/stores/stripe';
5+
import { initializeStripe, setPaymentMethod, submitStripeCard } from '$lib/stores/stripe';
66
import { invalidate } from '$app/navigation';
77
import { Dependencies } from '$lib/constants';
88
import { addNotification } from '$lib/stores/notifications';
99
import { page } from '$app/state';
1010
import { Spinner } from '@appwrite.io/pink-svelte';
11+
import type { PaymentMethod } from '@stripe/stripe-js';
12+
import StatePicker from './statePicker.svelte';
1113
1214
export let show = false;
1315
@@ -16,10 +18,36 @@
1618
let name: string;
1719
let error: string;
1820
let modal: FakeModal;
21+
let showState: boolean = false;
22+
let state: string = '';
23+
let paymentMethod: PaymentMethod | null = null;
1924
2025
async function handleSubmit() {
2126
try {
27+
if (showState && !state) {
28+
throw Error('Please select a state');
29+
}
30+
31+
if (showState) {
32+
const card = await setPaymentMethod(paymentMethod.id, name, state);
33+
modal.closeModal();
34+
await invalidate(Dependencies.PAYMENT_METHODS);
35+
dispatch('submit', card);
36+
addNotification({
37+
type: 'success',
38+
message: 'A new payment method has been added to your account'
39+
});
40+
return;
41+
}
42+
2243
const card = await submitStripeCard(name, page?.params?.organization ?? null);
44+
if (card && Object.hasOwn(card, 'id')) {
45+
if ((card as PaymentMethod).card.country === 'US') {
46+
paymentMethod = card as PaymentMethod;
47+
showState = true;
48+
return;
49+
}
50+
}
2351
modal.closeModal();
2452
await invalidate(Dependencies.PAYMENT_METHODS);
2553
dispatch('submit', card);
@@ -73,28 +101,30 @@
73101
bind:error
74102
onSubmit={handleSubmit}>
75103
<slot />
76-
<InputText
77-
id="name"
78-
required
79-
autofocus={true}
80-
bind:value={name}
81-
label="Cardholder name"
82-
placeholder="Cardholder name" />
83-
84-
<div class="aw-stripe-container" data-private>
85-
{#if isLoading}
86-
<div class="loader-element">
87-
<Spinner />
104+
{#if showState}
105+
<StatePicker card={paymentMethod} bind:state />
106+
{:else}
107+
<InputText
108+
id="name"
109+
required
110+
autofocus={true}
111+
bind:value={name}
112+
label="Cardholder name"
113+
placeholder="Cardholder name" />
114+
<div class="aw-stripe-container" data-private>
115+
{#if isLoading}
116+
<div class="loader-element">
117+
<Spinner />
118+
</div>
119+
{/if}
120+
<div
121+
style:display={isLoading ? 'none' : 'initial'}
122+
class="stripe-element"
123+
bind:this={element}>
124+
<!-- Stripe will create form elements here -->
88125
</div>
89-
{/if}
90-
91-
<div
92-
style:display={isLoading ? 'none' : 'initial'}
93-
class="stripe-element"
94-
bind:this={element}>
95-
<!-- Stripe will create form elements here -->
96126
</div>
97-
</div>
127+
{/if}
98128
<slot name="end"></slot>
99129
<svelte:fragment slot="footer">
100130
<Button secondary on:click={() => (show = false)}>Cancel</Button>

0 commit comments

Comments
 (0)