mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
Merge pull request #2164 from appwrite/fix-add-payment-method
Feat: State selector US
This commit is contained in:
@@ -20,6 +20,9 @@ export async function enterCreditCard(page: Page) {
|
||||
await stripe.locator('id=Field-cvcInput').fill('123');
|
||||
await stripe.locator('id=Field-countryInput').selectOption('DE');
|
||||
await dialog.getByRole('button', { name: 'Add', exact: true }).click();
|
||||
await page.locator('id=state-picker').click(); // open dropdown
|
||||
await page.getByRole('option', { name: 'Alabama' }).click();
|
||||
await dialog.getByRole('button', { name: 'Add', exact: true }).click();
|
||||
await dialog.waitFor({
|
||||
state: 'hidden'
|
||||
});
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
import { initializeStripe, unmountPaymentElement } from '$lib/stores/stripe';
|
||||
import { Badge, Card, Layout } from '@appwrite.io/pink-svelte';
|
||||
import type { PaymentMethodData } from '$lib/sdk/billing';
|
||||
import type { PaymentMethod } from '@stripe/stripe-js';
|
||||
import StatePicker from './statePicker.svelte';
|
||||
|
||||
export let methods: PaymentMethodData[];
|
||||
export let group: string;
|
||||
@@ -14,6 +16,9 @@
|
||||
export let disabledCondition: string = null;
|
||||
export let setAsDefault = false;
|
||||
export let showSetAsDefault = false;
|
||||
export let showState = false;
|
||||
export let paymentMethod: PaymentMethod | null = null;
|
||||
export let state: string = '';
|
||||
|
||||
let element: HTMLDivElement;
|
||||
let loader: HTMLDivElement;
|
||||
@@ -79,25 +84,29 @@
|
||||
{/each}
|
||||
<Card.Selector title="Add new payment method" name="$new" bind:group value="$new" />
|
||||
{#if group === '$new'}
|
||||
<InputText
|
||||
id="name"
|
||||
label="Cardholder name"
|
||||
placeholder="Cardholder name"
|
||||
bind:value={name}
|
||||
required
|
||||
autofocus={true} />
|
||||
{#if showState}
|
||||
<StatePicker card={paymentMethod} bind:state />
|
||||
{:else}
|
||||
<InputText
|
||||
id="name"
|
||||
label="Cardholder name"
|
||||
placeholder="Cardholder name"
|
||||
bind:value={name}
|
||||
required
|
||||
autofocus={true} />
|
||||
|
||||
<div class="aw-stripe-container" data-private>
|
||||
<div class="loader-container" bind:this={loader}>
|
||||
<div class="loader"></div>
|
||||
<div class="aw-stripe-container" data-private>
|
||||
<div class="loader-container" bind:this={loader}>
|
||||
<div class="loader"></div>
|
||||
</div>
|
||||
<div bind:this={element}></div>
|
||||
</div>
|
||||
<div bind:this={element}></div>
|
||||
</div>
|
||||
{#if showSetAsDefault}
|
||||
<InputChoice
|
||||
bind:value={setAsDefault}
|
||||
id="default"
|
||||
label="Set as default payment method for this organization" />
|
||||
{#if showSetAsDefault}
|
||||
<InputChoice
|
||||
bind:value={setAsDefault}
|
||||
id="default"
|
||||
label="Set as default payment method for this organization" />
|
||||
{/if}
|
||||
{/if}
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
|
||||
@@ -2,12 +2,14 @@
|
||||
import { FakeModal } from '$lib/components';
|
||||
import { InputText, Button } from '$lib/elements/forms';
|
||||
import { createEventDispatcher, onMount } from 'svelte';
|
||||
import { initializeStripe, submitStripeCard } from '$lib/stores/stripe';
|
||||
import { initializeStripe, setPaymentMethod, submitStripeCard } from '$lib/stores/stripe';
|
||||
import { invalidate } from '$app/navigation';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { page } from '$app/state';
|
||||
import { Spinner } from '@appwrite.io/pink-svelte';
|
||||
import type { PaymentMethod } from '@stripe/stripe-js';
|
||||
import StatePicker from './statePicker.svelte';
|
||||
|
||||
export let show = false;
|
||||
|
||||
@@ -16,10 +18,36 @@
|
||||
let name: string;
|
||||
let error: string;
|
||||
let modal: FakeModal;
|
||||
let showState: boolean = false;
|
||||
let state: string = '';
|
||||
let paymentMethod: PaymentMethod | null = null;
|
||||
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
if (showState && !state) {
|
||||
throw Error('Please select a state');
|
||||
}
|
||||
|
||||
if (showState) {
|
||||
const card = await setPaymentMethod(paymentMethod.id, name, state);
|
||||
modal.closeModal();
|
||||
await invalidate(Dependencies.PAYMENT_METHODS);
|
||||
dispatch('submit', card);
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: 'A new payment method has been added to your account'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const card = await submitStripeCard(name, page?.params?.organization ?? null);
|
||||
if (card && Object.hasOwn(card, 'id')) {
|
||||
if ((card as PaymentMethod).card.country === 'US') {
|
||||
paymentMethod = card as PaymentMethod;
|
||||
showState = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
modal.closeModal();
|
||||
await invalidate(Dependencies.PAYMENT_METHODS);
|
||||
dispatch('submit', card);
|
||||
@@ -73,28 +101,30 @@
|
||||
bind:error
|
||||
onSubmit={handleSubmit}>
|
||||
<slot />
|
||||
<InputText
|
||||
id="name"
|
||||
required
|
||||
autofocus={true}
|
||||
bind:value={name}
|
||||
label="Cardholder name"
|
||||
placeholder="Cardholder name" />
|
||||
|
||||
<div class="aw-stripe-container" data-private>
|
||||
{#if isLoading}
|
||||
<div class="loader-element">
|
||||
<Spinner />
|
||||
{#if showState}
|
||||
<StatePicker card={paymentMethod} bind:state />
|
||||
{:else}
|
||||
<InputText
|
||||
id="name"
|
||||
required
|
||||
autofocus={true}
|
||||
bind:value={name}
|
||||
label="Cardholder name"
|
||||
placeholder="Cardholder name" />
|
||||
<div class="aw-stripe-container" data-private>
|
||||
{#if isLoading}
|
||||
<div class="loader-element">
|
||||
<Spinner />
|
||||
</div>
|
||||
{/if}
|
||||
<div
|
||||
style:display={isLoading ? 'none' : 'initial'}
|
||||
class="stripe-element"
|
||||
bind:this={element}>
|
||||
<!-- Stripe will create form elements here -->
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div
|
||||
style:display={isLoading ? 'none' : 'initial'}
|
||||
class="stripe-element"
|
||||
bind:this={element}>
|
||||
<!-- Stripe will create form elements here -->
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<slot name="end"></slot>
|
||||
<svelte:fragment slot="footer">
|
||||
<Button secondary on:click={() => (show = false)}>Cancel</Button>
|
||||
|
||||
@@ -0,0 +1,238 @@
|
||||
export const states: Array<{ name: string; abbreviation: string }> = [
|
||||
{
|
||||
name: 'Alabama',
|
||||
abbreviation: 'AL'
|
||||
},
|
||||
{
|
||||
name: 'Alaska',
|
||||
abbreviation: 'AK'
|
||||
},
|
||||
{
|
||||
name: 'American Samoa',
|
||||
abbreviation: 'AS'
|
||||
},
|
||||
{
|
||||
name: 'Arizona',
|
||||
abbreviation: 'AZ'
|
||||
},
|
||||
{
|
||||
name: 'Arkansas',
|
||||
abbreviation: 'AR'
|
||||
},
|
||||
{
|
||||
name: 'California',
|
||||
abbreviation: 'CA'
|
||||
},
|
||||
{
|
||||
name: 'Colorado',
|
||||
abbreviation: 'CO'
|
||||
},
|
||||
{
|
||||
name: 'Connecticut',
|
||||
abbreviation: 'CT'
|
||||
},
|
||||
{
|
||||
name: 'Delaware',
|
||||
abbreviation: 'DE'
|
||||
},
|
||||
{
|
||||
name: 'District Of Columbia',
|
||||
abbreviation: 'DC'
|
||||
},
|
||||
{
|
||||
name: 'Federated States Of Micronesia',
|
||||
abbreviation: 'FM'
|
||||
},
|
||||
{
|
||||
name: 'Florida',
|
||||
abbreviation: 'FL'
|
||||
},
|
||||
{
|
||||
name: 'Georgia',
|
||||
abbreviation: 'GA'
|
||||
},
|
||||
{
|
||||
name: 'Guam',
|
||||
abbreviation: 'GU'
|
||||
},
|
||||
{
|
||||
name: 'Hawaii',
|
||||
abbreviation: 'HI'
|
||||
},
|
||||
{
|
||||
name: 'Idaho',
|
||||
abbreviation: 'ID'
|
||||
},
|
||||
{
|
||||
name: 'Illinois',
|
||||
abbreviation: 'IL'
|
||||
},
|
||||
{
|
||||
name: 'Indiana',
|
||||
abbreviation: 'IN'
|
||||
},
|
||||
{
|
||||
name: 'Iowa',
|
||||
abbreviation: 'IA'
|
||||
},
|
||||
{
|
||||
name: 'Kansas',
|
||||
abbreviation: 'KS'
|
||||
},
|
||||
{
|
||||
name: 'Kentucky',
|
||||
abbreviation: 'KY'
|
||||
},
|
||||
{
|
||||
name: 'Louisiana',
|
||||
abbreviation: 'LA'
|
||||
},
|
||||
{
|
||||
name: 'Maine',
|
||||
abbreviation: 'ME'
|
||||
},
|
||||
{
|
||||
name: 'Marshall Islands',
|
||||
abbreviation: 'MH'
|
||||
},
|
||||
{
|
||||
name: 'Maryland',
|
||||
abbreviation: 'MD'
|
||||
},
|
||||
{
|
||||
name: 'Massachusetts',
|
||||
abbreviation: 'MA'
|
||||
},
|
||||
{
|
||||
name: 'Michigan',
|
||||
abbreviation: 'MI'
|
||||
},
|
||||
{
|
||||
name: 'Minnesota',
|
||||
abbreviation: 'MN'
|
||||
},
|
||||
{
|
||||
name: 'Mississippi',
|
||||
abbreviation: 'MS'
|
||||
},
|
||||
{
|
||||
name: 'Missouri',
|
||||
abbreviation: 'MO'
|
||||
},
|
||||
{
|
||||
name: 'Montana',
|
||||
abbreviation: 'MT'
|
||||
},
|
||||
{
|
||||
name: 'Nebraska',
|
||||
abbreviation: 'NE'
|
||||
},
|
||||
{
|
||||
name: 'Nevada',
|
||||
abbreviation: 'NV'
|
||||
},
|
||||
{
|
||||
name: 'New Hampshire',
|
||||
abbreviation: 'NH'
|
||||
},
|
||||
{
|
||||
name: 'New Jersey',
|
||||
abbreviation: 'NJ'
|
||||
},
|
||||
{
|
||||
name: 'New Mexico',
|
||||
abbreviation: 'NM'
|
||||
},
|
||||
{
|
||||
name: 'New York',
|
||||
abbreviation: 'NY'
|
||||
},
|
||||
{
|
||||
name: 'North Carolina',
|
||||
abbreviation: 'NC'
|
||||
},
|
||||
{
|
||||
name: 'North Dakota',
|
||||
abbreviation: 'ND'
|
||||
},
|
||||
{
|
||||
name: 'Northern Mariana Islands',
|
||||
abbreviation: 'MP'
|
||||
},
|
||||
{
|
||||
name: 'Ohio',
|
||||
abbreviation: 'OH'
|
||||
},
|
||||
{
|
||||
name: 'Oklahoma',
|
||||
abbreviation: 'OK'
|
||||
},
|
||||
{
|
||||
name: 'Oregon',
|
||||
abbreviation: 'OR'
|
||||
},
|
||||
{
|
||||
name: 'Palau',
|
||||
abbreviation: 'PW'
|
||||
},
|
||||
{
|
||||
name: 'Pennsylvania',
|
||||
abbreviation: 'PA'
|
||||
},
|
||||
{
|
||||
name: 'Puerto Rico',
|
||||
abbreviation: 'PR'
|
||||
},
|
||||
{
|
||||
name: 'Rhode Island',
|
||||
abbreviation: 'RI'
|
||||
},
|
||||
{
|
||||
name: 'South Carolina',
|
||||
abbreviation: 'SC'
|
||||
},
|
||||
{
|
||||
name: 'South Dakota',
|
||||
abbreviation: 'SD'
|
||||
},
|
||||
{
|
||||
name: 'Tennessee',
|
||||
abbreviation: 'TN'
|
||||
},
|
||||
{
|
||||
name: 'Texas',
|
||||
abbreviation: 'TX'
|
||||
},
|
||||
{
|
||||
name: 'Utah',
|
||||
abbreviation: 'UT'
|
||||
},
|
||||
{
|
||||
name: 'Vermont',
|
||||
abbreviation: 'VT'
|
||||
},
|
||||
{
|
||||
name: 'Virgin Islands',
|
||||
abbreviation: 'VI'
|
||||
},
|
||||
{
|
||||
name: 'Virginia',
|
||||
abbreviation: 'VA'
|
||||
},
|
||||
{
|
||||
name: 'Washington',
|
||||
abbreviation: 'WA'
|
||||
},
|
||||
{
|
||||
name: 'West Virginia',
|
||||
abbreviation: 'WV'
|
||||
},
|
||||
{
|
||||
name: 'Wisconsin',
|
||||
abbreviation: 'WI'
|
||||
},
|
||||
{
|
||||
name: 'Wyoming',
|
||||
abbreviation: 'WY'
|
||||
}
|
||||
];
|
||||
@@ -0,0 +1,44 @@
|
||||
<script lang="ts">
|
||||
import { states } from './state';
|
||||
import { InputSelect } from '$lib/elements/forms';
|
||||
import type { PaymentMethod } from '@stripe/stripe-js';
|
||||
import { Alert, Card, Layout, Typography } from '@appwrite.io/pink-svelte';
|
||||
import { CreditCardBrandImage } from '../index.js';
|
||||
|
||||
export let state: string = '';
|
||||
export let card: PaymentMethod | null = null;
|
||||
</script>
|
||||
|
||||
<Layout.Stack direction="column" gap="m">
|
||||
{#if card}
|
||||
<Card.Base variant="secondary" padding="s">
|
||||
<Layout.Stack direction="row" alignItems="center" gap="s">
|
||||
<CreditCardBrandImage brand={card.card?.brand} />
|
||||
<span>ending in {card.card?.last4}</span>
|
||||
</Layout.Stack>
|
||||
<Typography.Text size="s">
|
||||
{card.card.country}
|
||||
{card.billing_details.address.postal_code}
|
||||
</Typography.Text>
|
||||
</Card.Base>
|
||||
{/if}
|
||||
|
||||
<Alert.Inline status="info" title="State is required for US payment methods">
|
||||
<Typography.Text size="s">
|
||||
To complete the billing information, select your state so we can apply the correct taxes
|
||||
and meet U.S. legal requirements.
|
||||
</Typography.Text>
|
||||
</Alert.Inline>
|
||||
|
||||
<InputSelect
|
||||
bind:value={state}
|
||||
required
|
||||
label="State"
|
||||
placeholder="Select a state"
|
||||
id="state-picker"
|
||||
options={states.map((state) => ({
|
||||
label: state.name,
|
||||
value: state.abbreviation,
|
||||
id: state.abbreviation.toLowerCase()
|
||||
}))} />
|
||||
</Layout.Stack>
|
||||
@@ -1111,7 +1111,8 @@ export class Billing {
|
||||
async setPaymentMethod(
|
||||
paymentMethodId: string,
|
||||
providerMethodId: string | PaymentMethod,
|
||||
name: string
|
||||
name: string,
|
||||
state: string | undefined = undefined
|
||||
): Promise<PaymentMethodData> {
|
||||
const path = `/account/payment-methods/${paymentMethodId}/provider`;
|
||||
const params = {
|
||||
@@ -1119,6 +1120,10 @@ export class Billing {
|
||||
providerMethodId,
|
||||
name
|
||||
};
|
||||
|
||||
if (state !== undefined) {
|
||||
params['state'] = state;
|
||||
}
|
||||
const uri = new URL(this.client.config.endpoint + path);
|
||||
return await this.client.call(
|
||||
'patch',
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
import type { Appearance, Stripe, StripeElement, StripeElements } from '@stripe/stripe-js';
|
||||
import type {
|
||||
Appearance,
|
||||
PaymentMethod,
|
||||
Stripe,
|
||||
StripeElement,
|
||||
StripeElements
|
||||
} from '@stripe/stripe-js';
|
||||
import { sdk } from './sdk';
|
||||
import { app } from './app';
|
||||
import { get, writable } from 'svelte/store';
|
||||
@@ -83,7 +89,8 @@ export async function submitStripeCard(name: string, organizationId?: string) {
|
||||
billing_details: {
|
||||
name
|
||||
}
|
||||
}
|
||||
},
|
||||
expand: ['payment_method']
|
||||
},
|
||||
redirect: 'if_required'
|
||||
});
|
||||
@@ -95,9 +102,13 @@ export async function submitStripeCard(name: string, organizationId?: string) {
|
||||
}
|
||||
|
||||
if (setupIntent && setupIntent.status === 'succeeded') {
|
||||
if ((setupIntent.payment_method as PaymentMethod).card.country === 'US') {
|
||||
// need to get state
|
||||
return setupIntent.payment_method as PaymentMethod;
|
||||
}
|
||||
const method = await sdk.forConsole.billing.setPaymentMethod(
|
||||
paymentMethod.$id,
|
||||
setupIntent.payment_method,
|
||||
(setupIntent.payment_method as PaymentMethod).id,
|
||||
name
|
||||
);
|
||||
paymentElement.destroy();
|
||||
@@ -115,6 +126,32 @@ export async function submitStripeCard(name: string, organizationId?: string) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function setPaymentMethod(providerMethodId: string, name: string, state: string) {
|
||||
if (!paymentMethod) {
|
||||
addNotification({
|
||||
title: 'Error',
|
||||
message: 'No payment method found. Please try again.',
|
||||
type: 'error'
|
||||
});
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const method = await sdk.forConsole.billing.setPaymentMethod(
|
||||
paymentMethod.$id,
|
||||
providerMethodId,
|
||||
name,
|
||||
state
|
||||
);
|
||||
paymentElement.destroy();
|
||||
isStripeInitialized.set(false);
|
||||
trackEvent(Submit.PaymentMethodCreate);
|
||||
return method;
|
||||
} catch (e) {
|
||||
trackError(e, Submit.PaymentMethodCreate);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
export async function confirmPayment(
|
||||
orgId: string,
|
||||
clientSecret: string,
|
||||
|
||||
@@ -5,12 +5,13 @@
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import type { Organization } from '$lib/stores/organization';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { submitStripeCard } from '$lib/stores/stripe';
|
||||
import { setPaymentMethod, submitStripeCard } from '$lib/stores/stripe';
|
||||
import { onMount } from 'svelte';
|
||||
import type { PaymentList } from '$lib/sdk/billing';
|
||||
import type { PaymentList, PaymentMethodData } from '$lib/sdk/billing';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
|
||||
import { PaymentBoxes } from '$lib/components/billing';
|
||||
import type { PaymentMethod } from '@stripe/stripe-js';
|
||||
|
||||
export let organization: Organization;
|
||||
export let show = false;
|
||||
@@ -20,6 +21,9 @@
|
||||
let name: string;
|
||||
let error: string;
|
||||
let selectedPaymentMethodId: string;
|
||||
let showState: boolean = false;
|
||||
let state: string = '';
|
||||
let paymentMethod: PaymentMethod | null = null;
|
||||
|
||||
onMount(async () => {
|
||||
if (!organization.paymentMethodId && !organization.backupPaymentMethodId) {
|
||||
@@ -41,7 +45,24 @@
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
if (selectedPaymentMethodId === '$new') {
|
||||
const method = await submitStripeCard(name, organization.$id);
|
||||
if (showState && !state) {
|
||||
throw Error('Please select a state');
|
||||
}
|
||||
let method: PaymentMethodData;
|
||||
if (showState) {
|
||||
method = await setPaymentMethod(paymentMethod.id, name, state);
|
||||
} else {
|
||||
const card = await submitStripeCard(name, organization.$id);
|
||||
if (card && Object.hasOwn(card, 'id')) {
|
||||
if ((card as PaymentMethod).card.country === 'US') {
|
||||
paymentMethod = card as PaymentMethod;
|
||||
showState = true;
|
||||
return;
|
||||
}
|
||||
} else if (card && Object.hasOwn(card, '$id')) {
|
||||
method = card as PaymentMethodData;
|
||||
}
|
||||
}
|
||||
selectedPaymentMethodId = method.$id;
|
||||
}
|
||||
isBackup
|
||||
@@ -97,6 +118,9 @@
|
||||
<PaymentBoxes
|
||||
methods={filteredMethods}
|
||||
bind:name
|
||||
bind:paymentMethod
|
||||
bind:showState
|
||||
bind:state
|
||||
bind:group={selectedPaymentMethodId}
|
||||
defaultMethod={organization?.paymentMethodId}
|
||||
backupMethod={organization?.backupPaymentMethodId}
|
||||
|
||||
@@ -3,11 +3,16 @@
|
||||
import { FakeModal } from '$lib/components';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import type { Invoice } from '$lib/sdk/billing';
|
||||
import type { Invoice, PaymentMethodData } from '$lib/sdk/billing';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
|
||||
import { page } from '$app/state';
|
||||
import { confirmPayment, isStripeInitialized, submitStripeCard } from '$lib/stores/stripe';
|
||||
import {
|
||||
confirmPayment,
|
||||
isStripeInitialized,
|
||||
setPaymentMethod,
|
||||
submitStripeCard
|
||||
} from '$lib/stores/stripe';
|
||||
import { organization } from '$lib/stores/organization';
|
||||
import { toLocaleDate } from '$lib/helpers/date';
|
||||
import { PaymentBoxes } from '$lib/components/billing';
|
||||
@@ -16,6 +21,7 @@
|
||||
import { getApiEndpoint, sdk } from '$lib/stores/sdk';
|
||||
import { formatCurrency } from '$lib/helpers/numbers';
|
||||
import { base } from '$app/paths';
|
||||
import type { PaymentMethod } from '@stripe/stripe-js';
|
||||
|
||||
export let show = false;
|
||||
export let invoice: Invoice;
|
||||
@@ -24,6 +30,9 @@
|
||||
let name: string;
|
||||
let paymentMethodId: string;
|
||||
let setAsDefault = false;
|
||||
let showState: boolean = false;
|
||||
let state: string = '';
|
||||
let paymentMethod: PaymentMethod | null = null;
|
||||
const endpoint = getApiEndpoint();
|
||||
|
||||
onMount(async () => {
|
||||
@@ -48,7 +57,24 @@
|
||||
try {
|
||||
if (paymentMethodId === null) {
|
||||
try {
|
||||
const method = await submitStripeCard(name, $organization.$id);
|
||||
if (showState && !state) {
|
||||
throw Error('Please select a state');
|
||||
}
|
||||
let method: PaymentMethodData;
|
||||
if (showState) {
|
||||
method = await setPaymentMethod(paymentMethod.id, name, state);
|
||||
} else {
|
||||
const card = await submitStripeCard(name, $organization.$id);
|
||||
if (card && Object.hasOwn(card, 'id')) {
|
||||
if ((card as PaymentMethod).card.country === 'US') {
|
||||
paymentMethod = card as PaymentMethod;
|
||||
showState = true;
|
||||
return;
|
||||
}
|
||||
} else if (card && Object.hasOwn(card, '$id')) {
|
||||
method = card as PaymentMethodData;
|
||||
}
|
||||
}
|
||||
const card = await sdk.forConsole.billing.getPaymentMethod(method.$id);
|
||||
if (card?.last4) {
|
||||
paymentMethodId = card.$id;
|
||||
@@ -131,6 +157,9 @@
|
||||
</Button>
|
||||
|
||||
<PaymentBoxes
|
||||
bind:paymentMethod
|
||||
bind:showState
|
||||
bind:state
|
||||
methods={filteredMethods}
|
||||
defaultMethod={$organization?.paymentMethodId}
|
||||
backupMethod={$organization?.backupPaymentMethodId}
|
||||
|
||||
+27
-3
@@ -1,17 +1,21 @@
|
||||
<script lang="ts">
|
||||
import { WizardStep } from '$lib/layout';
|
||||
import { onMount } from 'svelte';
|
||||
import type { PaymentList } from '$lib/sdk/billing';
|
||||
import type { PaymentList, PaymentMethodData } from '$lib/sdk/billing';
|
||||
import { invalidate } from '$app/navigation';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { isStripeInitialized, submitStripeCard } from '$lib/stores/stripe';
|
||||
import { isStripeInitialized, setPaymentMethod, submitStripeCard } from '$lib/stores/stripe';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { PaymentBoxes } from '$lib/components/billing';
|
||||
import { addCreditWizardStore } from '../store';
|
||||
import { organization } from '$lib/stores/organization';
|
||||
import type { PaymentMethod } from '@stripe/stripe-js';
|
||||
|
||||
let methods: PaymentList;
|
||||
let name: string;
|
||||
let showState: boolean = false;
|
||||
let state: string = '';
|
||||
let paymentMethod: PaymentMethod | null = null;
|
||||
|
||||
onMount(async () => {
|
||||
methods = await sdk.forConsole.billing.listPaymentMethods();
|
||||
@@ -21,7 +25,24 @@
|
||||
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
const method = await submitStripeCard(name, $organization.$id);
|
||||
if (showState && !state) {
|
||||
throw Error('Please select a state');
|
||||
}
|
||||
let method: PaymentMethodData;
|
||||
if (showState) {
|
||||
method = await setPaymentMethod(paymentMethod.id, name, state);
|
||||
} else {
|
||||
const card = await submitStripeCard(name, $organization.$id);
|
||||
if (card && Object.hasOwn(card, 'id')) {
|
||||
if ((card as PaymentMethod).card.country === 'US') {
|
||||
paymentMethod = card as PaymentMethod;
|
||||
showState = true;
|
||||
return;
|
||||
}
|
||||
} else if (card && Object.hasOwn(card, '$id')) {
|
||||
method = card as PaymentMethodData;
|
||||
}
|
||||
}
|
||||
$addCreditWizardStore.paymentMethodId = method.$id;
|
||||
invalidate(Dependencies.PAYMENT_METHODS);
|
||||
} catch (e) {
|
||||
@@ -45,6 +66,9 @@
|
||||
|
||||
<p class="text"><b>Payment method</b></p>
|
||||
<PaymentBoxes
|
||||
bind:paymentMethod
|
||||
bind:showState
|
||||
bind:state
|
||||
methods={filteredMethods}
|
||||
bind:name
|
||||
bind:group={$addCreditWizardStore.paymentMethodId} />
|
||||
|
||||
Reference in New Issue
Block a user