@@ -0,0 +1,106 @@
|
||||
import {useEffect, useRef, useState} from 'react';
|
||||
import {useSearchParams} from 'react-router-dom';
|
||||
import {loadStripe, SetupIntent} from '@stripe/stripe-js';
|
||||
import {message} from '../../../i18n/message';
|
||||
import {apiClient} from '../../../http/query-client';
|
||||
import {useNavigate} from '../../../utils/hooks/use-navigate';
|
||||
import {
|
||||
BillingRedirectMessage,
|
||||
BillingRedirectMessageConfig,
|
||||
} from '../../billing-redirect-message';
|
||||
import {invalidateBillingUserQuery} from '../use-billing-user';
|
||||
import {useSettings} from '../../../core/settings/use-settings';
|
||||
|
||||
const previousUrl = '/billing';
|
||||
|
||||
export function ChangePaymentMethodDone() {
|
||||
const {
|
||||
billing: {stripe_public_key},
|
||||
} = useSettings();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [params] = useSearchParams();
|
||||
const clientSecret = params.get('setup_intent_client_secret');
|
||||
|
||||
const [messageConfig, setMessageConfig] =
|
||||
useState<BillingRedirectMessageConfig>();
|
||||
|
||||
const stripeInitiated = useRef<boolean>();
|
||||
|
||||
useEffect(() => {
|
||||
if (stripeInitiated.current || !clientSecret) return;
|
||||
loadStripe(stripe_public_key!).then(stripe => {
|
||||
if (!stripe) {
|
||||
setMessageConfig(getRedirectMessageConfig());
|
||||
return;
|
||||
}
|
||||
stripe.retrieveSetupIntent(clientSecret).then(({setupIntent}) => {
|
||||
if (setupIntent?.status === 'succeeded') {
|
||||
changeDefaultPaymentMethod(setupIntent.payment_method as string).then(
|
||||
() => {
|
||||
invalidateBillingUserQuery();
|
||||
}
|
||||
);
|
||||
}
|
||||
setMessageConfig(getRedirectMessageConfig(setupIntent?.status));
|
||||
});
|
||||
});
|
||||
stripeInitiated.current = true;
|
||||
}, [stripe_public_key, clientSecret]);
|
||||
|
||||
if (!clientSecret) {
|
||||
navigate(previousUrl);
|
||||
return null;
|
||||
}
|
||||
|
||||
return <BillingRedirectMessage config={messageConfig} />;
|
||||
}
|
||||
|
||||
function getRedirectMessageConfig(
|
||||
status?: SetupIntent.Status
|
||||
): BillingRedirectMessageConfig {
|
||||
switch (status) {
|
||||
case 'succeeded':
|
||||
return {
|
||||
...redirectMessageDefaults,
|
||||
message: message('Payment method changed successfully!'),
|
||||
status: 'success',
|
||||
};
|
||||
case 'processing':
|
||||
return {
|
||||
...redirectMessageDefaults,
|
||||
message: message(
|
||||
"Your request is processing. We'll update you when your payment method is confirmed."
|
||||
),
|
||||
status: 'success',
|
||||
};
|
||||
case 'requires_payment_method':
|
||||
return {
|
||||
...redirectMessageDefaults,
|
||||
message: message(
|
||||
'Payment method confirmation failed. Please try another payment method.'
|
||||
),
|
||||
status: 'error',
|
||||
};
|
||||
default:
|
||||
return {
|
||||
...redirectMessageDefaults,
|
||||
message: message('Something went wrong'),
|
||||
status: 'error',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const redirectMessageDefaults: Omit<
|
||||
BillingRedirectMessageConfig,
|
||||
'message' | 'status'
|
||||
> = {
|
||||
link: previousUrl,
|
||||
buttonLabel: message('Go back'),
|
||||
};
|
||||
|
||||
function changeDefaultPaymentMethod(paymentMethodId: string) {
|
||||
return apiClient.post('billing/stripe/change-default-payment-method', {
|
||||
payment_method_id: paymentMethodId,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import {Fragment} from 'react';
|
||||
import {Breadcrumb} from '../../../ui/breadcrumbs/breadcrumb';
|
||||
import {useNavigate} from '../../../utils/hooks/use-navigate';
|
||||
import {BreadcrumbItem} from '../../../ui/breadcrumbs/breadcrumb-item';
|
||||
import {Trans} from '../../../i18n/trans';
|
||||
import {Outlet} from 'react-router-dom';
|
||||
|
||||
const previousUrl = '/billing';
|
||||
|
||||
export function ChangePaymentMethodLayout() {
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<Breadcrumb>
|
||||
<BreadcrumbItem isLink onSelected={() => navigate(previousUrl)}>
|
||||
<Trans message="Billing" />
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbItem>
|
||||
<Trans message="Payment method" />
|
||||
</BreadcrumbItem>
|
||||
</Breadcrumb>
|
||||
<h1 className="text-3xl font-bold my-32 md:my-64">
|
||||
<Trans message="Change payment method" />
|
||||
</h1>
|
||||
<Outlet />
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import {Trans} from '../../../i18n/trans';
|
||||
import {Button} from '../../../ui/buttons/button';
|
||||
import {Link} from 'react-router-dom';
|
||||
import {StripeElementsForm} from '../../checkout/stripe/stripe-elements-form';
|
||||
import {useSettings} from '../../../core/settings/use-settings';
|
||||
|
||||
const previousUrl = '/billing';
|
||||
|
||||
export function ChangePaymentMethodPage() {
|
||||
const {base_url} = useSettings();
|
||||
|
||||
return (
|
||||
<div className="max-w-[464px]">
|
||||
<StripeElementsForm
|
||||
type="setupIntent"
|
||||
submitLabel={<Trans message="Change" />}
|
||||
returnUrl={`${base_url}/billing/change-payment-method/done`}
|
||||
/>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="w-full mt-16"
|
||||
size="md"
|
||||
to={previousUrl}
|
||||
elementType={Link}
|
||||
type="button"
|
||||
>
|
||||
<Trans message="Go back" />
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user