diff --git a/src/lib/actions/analytics.ts b/src/lib/actions/analytics.ts index 181592ef0..2c1d4d119 100644 --- a/src/lib/actions/analytics.ts +++ b/src/lib/actions/analytics.ts @@ -264,6 +264,7 @@ export enum Submit { PaymentMethodUpdate = 'submit_payment_method_update', PaymentMethodDelete = 'submit_payment_method_delete', RetryPayment = 'submit_retry_payment', + VerifyPayment = 'submit_verify_payment', BillingAddressCreate = 'submit_billing_address_create', BillingAddressUpdate = 'submit_billing_address_update', BillingAddressDelete = 'submit_billing_address_delete', diff --git a/src/lib/components/billing/alerts/paymentMandate.svelte b/src/lib/components/billing/alerts/paymentMandate.svelte index d8819b492..9799ddcc3 100644 --- a/src/lib/components/billing/alerts/paymentMandate.svelte +++ b/src/lib/components/billing/alerts/paymentMandate.svelte @@ -2,28 +2,25 @@ import { page } from '$app/stores'; import { Button } from '$lib/elements/forms'; import { HeaderAlert } from '$lib/layout'; - import { actionRequiredInvoices } from '$lib/stores/billing'; + import { paymentMissingMandate } from '$lib/stores/billing'; import { organization } from '$lib/stores/organization'; - import { VARS } from '$lib/system'; + import { sdk } from '$lib/stores/sdk'; + import { confirmSetup } from '$lib/stores/stripe'; - const endpoint = VARS.APPWRITE_ENDPOINT ?? `${$page.url.origin}/v1`; + async function verifyPaymentMethod() { + const method = await sdk.forConsole.billing.setupPaymentMandate( + $organization.$id, + $paymentMissingMandate.$id + ); + await confirmSetup(method.clientSecret, method.$id); + } -{#if $actionRequiredInvoices && $actionRequiredInvoices?.invoices?.length && !$page.url.pathname.includes('/console/account')} - - Please authorize your upcoming payment for {$organization.name}. Your bank requires this - security measure to proceed with payment. +{#if $paymentMissingMandate && $paymentMissingMandate.country === 'in' && $paymentMissingMandate.mandateId === null && !$page.url.pathname.includes('/console/account')} + + The payment method for {$organization.name} needs to be verified. - - + {/if} diff --git a/src/lib/stores/billing.ts b/src/lib/stores/billing.ts index 3a20511ac..d034acd0d 100644 --- a/src/lib/stores/billing.ts +++ b/src/lib/stores/billing.ts @@ -2,7 +2,14 @@ import { page } from '$app/stores'; import { derived, get, writable } from 'svelte/store'; import { sdk } from './sdk'; import { organization, type Organization } from './organization'; -import type { InvoiceList, AddressesList, Invoice, PaymentList, PlansMap } from '$lib/sdk/billing'; +import type { + InvoiceList, + AddressesList, + Invoice, + PaymentList, + PlansMap, + PaymentMethodData +} from '$lib/sdk/billing'; import { isCloud } from '$lib/system'; import { cachedStore } from '$lib/helpers/cache'; import { Query } from '@appwrite.io/console'; @@ -14,6 +21,7 @@ import { base } from '$app/paths'; import { activeHeaderAlert } from '$routes/console/store'; import MarkedForDeletion from '$lib/components/billing/alerts/markedForDeletion.svelte'; import { BillingPlan } from '$lib/constants'; +import PaymentMandate from '$lib/components/billing/alerts/paymentMandate.svelte'; export type Tier = 'tier-0' | 'tier-1' | 'tier-2'; @@ -266,10 +274,19 @@ export function checkForMarkedForDeletion(org: Organization) { } } +export const paymentMissingMandate = writable(null); + export async function checkForMandate(org: Organization) { const paymentId = org.paymentMethodId ?? org.backupPaymentMethodId; const paymentMethod = await sdk.forConsole.billing.getPaymentMethod(paymentId); if (paymentMethod.mandateId === null && paymentMethod.country === 'in') { - const method = sdk.forConsole.billing.setupPaymentMandate(org.$id, paymentId); + headerAlert.add({ + id: 'paymentMandate', + component: PaymentMandate, + show: true, + importance: 8 + }); + activeHeaderAlert.set(headerAlert.get()); + paymentMissingMandate.set(paymentMethod); } } diff --git a/src/lib/stores/stripe.ts b/src/lib/stores/stripe.ts index 5730a4c86..31cfff63d 100644 --- a/src/lib/stores/stripe.ts +++ b/src/lib/stores/stripe.ts @@ -5,6 +5,7 @@ import { get, writable } from 'svelte/store'; import type { PaymentMethodData } from '$lib/sdk/billing'; import { Submit, trackError, trackEvent } from '$lib/actions/analytics'; import { addNotification } from './notifications'; +import { organization } from './organization'; export const stripe = writable(); let paymentMethod: PaymentMethodData; @@ -63,7 +64,8 @@ export async function submitStripeCard(name: string, urlRoute?: string) { clientSecret, confirmParams: { return_url: `${baseUrl}${ - urlRoute ?? `organization/billing?clientSecret=${clientSecret}` + urlRoute ?? + `organization-${get(organization).$id}/billing?clientSecret=${clientSecret}` }`, payment_method_data: { billing_details: { @@ -133,6 +135,45 @@ export async function confirmPayment( } } +export async function confirmSetup( + clientSecret: string, + paymentMethodId: string, + urlRoute?: string +) { + const baseUrl = 'https://cloud.appwrite.io/console/'; + + const { setupIntent, error } = await get(stripe).confirmCardSetup(clientSecret, { + payment_method: paymentMethodId, + return_url: `${baseUrl}${ + urlRoute ?? `organization-${get(organization).$id}/billing?clientSecret=${clientSecret}` + }` + }); + + if (error) { + const e = new Error(error.message); + trackError(e, Submit.VerifyPayment); + addNotification({ + message: error.message, + type: 'error' + }); + } + + if (setupIntent && setupIntent.status === 'succeeded') { + trackEvent(Submit.VerifyPayment); + addNotification({ + message: 'Payment method verified successfully', + type: 'success' + }); + } else { + const e = new Error('Something went wrong. Please try again later.'); + trackError(e, Submit.VerifyPayment); + addNotification({ + message: e.message, + type: 'error' + }); + } +} + const apperanceLight = { variables: { colorPrimary: '#606a7b',