Guard checkout payment step behind login
This commit is contained in:
@@ -10,6 +10,7 @@ import { AuthStep } from "./steps/AuthStep";
|
||||
import { ConfirmationStep } from "./steps/ConfirmationStep";
|
||||
import { useAnalytics } from '@/hooks/useAnalytics';
|
||||
import { cn } from "@/lib/utils";
|
||||
import toast from 'react-hot-toast';
|
||||
|
||||
const PaymentStep = lazy(() => import('./steps/PaymentStep').then((module) => ({ default: module.PaymentStep })));
|
||||
|
||||
@@ -80,6 +81,7 @@ const WizardBody: React.FC<{
|
||||
authUser,
|
||||
isAuthenticated,
|
||||
paymentCompleted,
|
||||
goToStep,
|
||||
} = useCheckoutWizard();
|
||||
const progressRef = useRef<HTMLDivElement | null>(null);
|
||||
const hasMountedRef = useRef(false);
|
||||
@@ -114,6 +116,13 @@ const WizardBody: React.FC<{
|
||||
});
|
||||
}, [currentStep, trackEvent]);
|
||||
|
||||
useEffect(() => {
|
||||
if (currentStep === 'payment' && !isAuthenticated) {
|
||||
toast.error(t('checkout.payment_step.auth_required'));
|
||||
goToStep('auth');
|
||||
}
|
||||
}, [currentStep, goToStep, isAuthenticated, t]);
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof window === 'undefined' || !progressRef.current) {
|
||||
return;
|
||||
|
||||
@@ -83,6 +83,21 @@ describe('CheckoutWizard auth step navigation guard', () => {
|
||||
nextButtons.forEach((button) => expect(button).not.toBeDisabled());
|
||||
});
|
||||
|
||||
it('redirects to the auth step when the user is not authenticated on the payment step', async () => {
|
||||
render(
|
||||
<CheckoutWizard
|
||||
initialPackage={basePackage}
|
||||
packageOptions={[basePackage]}
|
||||
privacyHtml="<p>privacy</p>"
|
||||
initialAuthUser={null}
|
||||
initialStep="payment"
|
||||
/>,
|
||||
);
|
||||
|
||||
await screen.findByTestId('auth-step');
|
||||
expect(screen.queryByTestId('payment-step')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('only renders the next button on the payment step after the payment is completed', async () => {
|
||||
const paidPackage = { ...basePackage, id: 2, price: 99 };
|
||||
|
||||
|
||||
@@ -191,6 +191,8 @@ export const PaymentStep: React.FC = () => {
|
||||
nextStep,
|
||||
paddleConfig,
|
||||
authUser,
|
||||
isAuthenticated,
|
||||
goToStep,
|
||||
setPaymentCompleted,
|
||||
checkoutSessionId,
|
||||
setCheckoutSessionId,
|
||||
@@ -368,6 +370,13 @@ export const PaymentStep: React.FC = () => {
|
||||
}, [couponCode]);
|
||||
|
||||
const handleFreeActivation = async () => {
|
||||
if (!isAuthenticated || !authUser) {
|
||||
const message = t('checkout.payment_step.auth_required');
|
||||
toast.error(message);
|
||||
goToStep('auth');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!selectedPackage) {
|
||||
return;
|
||||
}
|
||||
@@ -420,6 +429,15 @@ export const PaymentStep: React.FC = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isAuthenticated || !authUser) {
|
||||
const message = t('checkout.payment_step.auth_required');
|
||||
setStatus('error');
|
||||
setMessage(message);
|
||||
toast.error(message);
|
||||
goToStep('auth');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!acceptedTerms) {
|
||||
setConsentError(t('checkout.legal.checkbox_terms_error'));
|
||||
return;
|
||||
@@ -466,6 +484,18 @@ export const PaymentStep: React.FC = () => {
|
||||
});
|
||||
|
||||
const rawBody = await response.text();
|
||||
if (
|
||||
response.status === 401 ||
|
||||
response.status === 419 ||
|
||||
(response.redirected && response.url.includes('/login'))
|
||||
) {
|
||||
const message = t('checkout.payment_step.auth_required');
|
||||
setStatus('error');
|
||||
setMessage(message);
|
||||
toast.error(message);
|
||||
goToStep('auth');
|
||||
return;
|
||||
}
|
||||
if (typeof window !== 'undefined') {
|
||||
|
||||
console.info('[Checkout] Hosted checkout response', { status: response.status, rawBody });
|
||||
|
||||
Reference in New Issue
Block a user