Skip to content

Commit 4fbd61f

Browse files
Merge pull request #19820 from mozilla/PAY-3155
feat(payments-next): Add redirects on mismatched cart uids
2 parents 80da652 + f88ca40 commit 4fbd61f

File tree

6 files changed

+159
-17
lines changed

6 files changed

+159
-17
lines changed

apps/payments/next/app/[locale]/[offeringId]/[interval]/checkout/[cartId]/error/page.tsx

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import { headers } from 'next/headers';
66
import Image from 'next/image';
77
import Link from 'next/link';
8-
8+
import { auth } from 'apps/payments/next/auth';
99
import errorIcon from '@fxa/shared/assets/images/error.svg';
1010
import checkIcon from '@fxa/shared/assets/images/check.svg';
1111
import {
@@ -15,12 +15,12 @@ import {
1515
getErrorFtlInfo,
1616
buildPageMetadata,
1717
} from '@fxa/payments/ui/server';
18-
import {
19-
getCartOrRedirectAction,
20-
} from '@fxa/payments/ui/actions';
18+
import { getCartOrRedirectAction } from '@fxa/payments/ui/actions';
2119
import { config } from 'apps/payments/next/config';
2220
import type { Metadata } from 'next';
2321
import { CartErrorReasonId } from '@fxa/shared/db/mysql/account';
22+
import { buildRedirectUrl } from '@fxa/payments/ui';
23+
import { redirect } from 'next/navigation';
2424

2525
// forces dynamic rendering
2626
// https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config
@@ -53,15 +53,40 @@ export default async function CheckoutError({
5353
const { locale } = params;
5454
const acceptLanguage = headers().get('accept-language');
5555

56+
const sessionPromise = auth();
5657
const cartPromise = getCartOrRedirectAction(
5758
params.cartId,
5859
SupportedPages.ERROR,
5960
searchParams
6061
);
6162
const l10n = getApp().getL10n(acceptLanguage, locale);
62-
const [cart] = await Promise.all([cartPromise]);
63+
const [cart, session] = await Promise.all([cartPromise, sessionPromise]);
64+
65+
const errorReason = getErrorFtlInfo(
66+
cart.errorReasonId,
67+
params,
68+
config,
69+
searchParams
70+
);
6371

64-
const errorReason = getErrorFtlInfo(cart.errorReasonId, params, config, searchParams);
72+
if (cart.id && cart.uid !== session?.user?.id) {
73+
const redirectSearchParams: Record<string, string | string[]> =
74+
searchParams || {};
75+
delete redirectSearchParams.cartId;
76+
delete redirectSearchParams.cartVersion;
77+
const redirectTo = buildRedirectUrl(
78+
params.offeringId,
79+
params.interval,
80+
'new',
81+
'checkout',
82+
{
83+
baseUrl: config.paymentsNextHostedUrl,
84+
locale,
85+
searchParams: redirectSearchParams,
86+
}
87+
);
88+
redirect(redirectTo);
89+
}
6590

6691
return (
6792
<>
@@ -72,7 +97,7 @@ export default async function CheckoutError({
7297
{
7398
// Once more conditionals are added, move this to a separate component
7499
cart.errorReasonId ===
75-
CartErrorReasonId.CART_ELIGIBILITY_STATUS_SAME ? (
100+
CartErrorReasonId.CART_ELIGIBILITY_STATUS_SAME ? (
76101
<Image
77102
src={checkIcon}
78103
alt=""

apps/payments/next/app/[locale]/[offeringId]/[interval]/checkout/[cartId]/needs_input/page.tsx

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
LoadingSpinner,
88
StripeWrapper,
99
PaymentInputHandler,
10+
buildRedirectUrl,
1011
} from '@fxa/payments/ui';
1112
import {
1213
getApp,
@@ -17,6 +18,8 @@ import { headers } from 'next/headers';
1718
import { getCartOrRedirectAction } from '@fxa/payments/ui/actions';
1819
import type { Metadata } from 'next';
1920
import { config } from 'apps/payments/next/config';
21+
import { auth } from 'apps/payments/next/auth';
22+
import { redirect } from 'next/navigation';
2023

2124
export async function generateMetadata({
2225
params,
@@ -45,14 +48,38 @@ export default async function NeedsInputPage({
4548
const { locale } = params;
4649
const acceptLanguage = headers().get('accept-language');
4750
const l10n = getApp().getL10n(acceptLanguage, locale);
48-
const cart = await getCartOrRedirectAction(
51+
52+
const sessionPromise = auth();
53+
const cartPromise = getCartOrRedirectAction(
4954
params.cartId,
5055
SupportedPages.NEEDS_INPUT,
5156
searchParams
5257
);
58+
const [session, cart] = await Promise.all([sessionPromise, cartPromise]);
59+
5360
if (!cart.currency) {
5461
throw new Error('Currency is missing from the cart');
5562
}
63+
64+
if (!session?.user?.id ||cart.uid !== session.user.id) {
65+
const redirectSearchParams: Record<string, string | string[]> =
66+
searchParams || {};
67+
delete redirectSearchParams.cartId;
68+
delete redirectSearchParams.cartVersion;
69+
const redirectTo = buildRedirectUrl(
70+
params.offeringId,
71+
params.interval,
72+
'new',
73+
'checkout',
74+
{
75+
baseUrl: config.paymentsNextHostedUrl,
76+
locale,
77+
searchParams: redirectSearchParams,
78+
}
79+
);
80+
redirect(redirectTo);
81+
}
82+
5683
return (
5784
<section
5885
className="flex flex-col text-center text-sm"

apps/payments/next/app/[locale]/[offeringId]/[interval]/checkout/[cartId]/success/page.tsx

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import Image from 'next/image';
88
import { auth } from 'apps/payments/next/auth';
99

1010
import { SubPlatPaymentMethodType } from '@fxa/payments/customer';
11-
import { getCardIcon } from '@fxa/payments/ui';
11+
import { buildRedirectUrl, getCardIcon } from '@fxa/payments/ui';
1212
import {
1313
fetchCMSData,
1414
getCartOrRedirectAction,
@@ -20,6 +20,7 @@ import {
2020
buildPageMetadata,
2121
} from '@fxa/payments/ui/server';
2222
import { config } from 'apps/payments/next/config';
23+
import { redirect } from 'next/navigation';
2324

2425
export const dynamic = 'force-dynamic';
2526

@@ -68,6 +69,25 @@ export default async function CheckoutSuccess({
6869
sessionPromise,
6970
]);
7071

72+
if (!session?.user?.id || cart.uid !== session.user.id) {
73+
const redirectSearchParams: Record<string, string | string[]> =
74+
searchParams || {};
75+
delete redirectSearchParams.cartId;
76+
delete redirectSearchParams.cartVersion;
77+
const redirectTo = buildRedirectUrl(
78+
params.offeringId,
79+
params.interval,
80+
'new',
81+
'checkout',
82+
{
83+
baseUrl: config.paymentsNextHostedUrl,
84+
locale,
85+
searchParams: redirectSearchParams,
86+
}
87+
);
88+
redirect(redirectTo);
89+
}
90+
7191
const { successActionButtonUrl, successActionButtonLabel } =
7292
cms.commonContent.localizations.at(0) || cms.commonContent;
7393

apps/payments/next/app/[locale]/[offeringId]/[interval]/upgrade/[cartId]/(mainLayout)/error/page.tsx

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import { headers } from 'next/headers';
66
import Image from 'next/image';
77
import Link from 'next/link';
8-
8+
import { auth } from 'apps/payments/next/auth';
99
import errorIcon from '@fxa/shared/assets/images/error.svg';
1010
import {
1111
getApp,
@@ -14,11 +14,11 @@ import {
1414
getErrorFtlInfo,
1515
buildPageMetadata,
1616
} from '@fxa/payments/ui/server';
17-
import {
18-
getCartOrRedirectAction,
19-
} from '@fxa/payments/ui/actions';
17+
import { getCartOrRedirectAction } from '@fxa/payments/ui/actions';
2018
import { config } from 'apps/payments/next/config';
2119
import { Metadata } from 'next';
20+
import { buildRedirectUrl } from '@fxa/payments/ui';
21+
import { redirect } from 'next/navigation';
2222

2323
// forces dynamic rendering
2424
// https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config
@@ -51,15 +51,40 @@ export default async function UpgradeError({
5151
const { locale } = params;
5252
const acceptLanguage = headers().get('accept-language');
5353

54+
const sessionPromise = auth();
5455
const cartPromise = getCartOrRedirectAction(
5556
params.cartId,
5657
SupportedPages.ERROR,
5758
searchParams
5859
);
5960
const l10n = getApp().getL10n(acceptLanguage, locale);
60-
const [cart] = await Promise.all([cartPromise]);
61+
const [cart, session] = await Promise.all([cartPromise, sessionPromise]);
62+
63+
const errorReason = getErrorFtlInfo(
64+
cart.errorReasonId,
65+
params,
66+
config,
67+
searchParams
68+
);
6169

62-
const errorReason = getErrorFtlInfo(cart.errorReasonId, params, config, searchParams);
70+
if (cart.id && cart.uid !== session?.user?.id) {
71+
const redirectSearchParams: Record<string, string | string[]> =
72+
searchParams || {};
73+
delete redirectSearchParams.cartId;
74+
delete redirectSearchParams.cartVersion;
75+
const redirectTo = buildRedirectUrl(
76+
params.offeringId,
77+
params.interval,
78+
'new',
79+
'checkout',
80+
{
81+
baseUrl: config.paymentsNextHostedUrl,
82+
locale,
83+
searchParams: redirectSearchParams,
84+
}
85+
);
86+
redirect(redirectTo);
87+
}
6388

6489
return (
6590
<>

apps/payments/next/app/[locale]/[offeringId]/[interval]/upgrade/[cartId]/(mainLayout)/needs_input/page.tsx

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
LoadingSpinner,
88
StripeWrapper,
99
PaymentInputHandler,
10+
buildRedirectUrl,
1011
} from '@fxa/payments/ui';
1112
import {
1213
getApp,
@@ -17,6 +18,8 @@ import { headers } from 'next/headers';
1718
import { getCartOrRedirectAction } from '@fxa/payments/ui/actions';
1819
import { Metadata } from 'next';
1920
import { config } from 'apps/payments/next/config';
21+
import { auth } from 'apps/payments/next/auth';
22+
import { redirect } from 'next/navigation';
2023

2124
export async function generateMetadata({
2225
params,
@@ -45,14 +48,36 @@ export default async function NeedsInputPage({
4548
const { locale } = params;
4649
const acceptLanguage = headers().get('accept-language');
4750
const l10n = getApp().getL10n(acceptLanguage, locale);
48-
const cart = await getCartOrRedirectAction(
51+
const cartPromise = getCartOrRedirectAction(
4952
params.cartId,
5053
SupportedPages.NEEDS_INPUT,
5154
searchParams
5255
);
56+
const sessionPromise = auth();
57+
const [session, cart] = await Promise.all([sessionPromise, cartPromise]);
5358
if (!cart.currency) {
5459
throw new Error('Currency is missing from the cart');
5560
}
61+
62+
if (!session?.user?.id || cart.uid !== session.user.id) {
63+
const redirectSearchParams: Record<string, string | string[]> =
64+
searchParams || {};
65+
delete redirectSearchParams.cartId;
66+
delete redirectSearchParams.cartVersion;
67+
const redirectTo = buildRedirectUrl(
68+
params.offeringId,
69+
params.interval,
70+
'new',
71+
'checkout',
72+
{
73+
baseUrl: config.paymentsNextHostedUrl,
74+
locale,
75+
searchParams: redirectSearchParams,
76+
}
77+
);
78+
redirect(redirectTo);
79+
}
80+
5681
return (
5782
<section
5883
className="flex flex-col text-center text-sm"

apps/payments/next/app/[locale]/[offeringId]/[interval]/upgrade/[cartId]/(mainLayout)/success/page.tsx

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { Metadata } from 'next';
88
import { auth } from 'apps/payments/next/auth';
99

1010
import { SubPlatPaymentMethodType } from '@fxa/payments/customer';
11-
import { getCardIcon } from '@fxa/payments/ui';
11+
import { buildRedirectUrl, getCardIcon } from '@fxa/payments/ui';
1212
import {
1313
fetchCMSData,
1414
getCartOrRedirectAction,
@@ -20,6 +20,7 @@ import {
2020
buildPageMetadata,
2121
} from '@fxa/payments/ui/server';
2222
import { config } from 'apps/payments/next/config';
23+
import { redirect } from 'next/navigation';
2324

2425
export const dynamic = 'force-dynamic';
2526

@@ -68,6 +69,25 @@ export default async function UpgradeSuccess({
6869
sessionPromise,
6970
]);
7071

72+
if (!session?.user?.id || cart.uid !== session.user.id) {
73+
const redirectSearchParams: Record<string, string | string[]> =
74+
searchParams || {};
75+
delete redirectSearchParams.cartId;
76+
delete redirectSearchParams.cartVersion;
77+
const redirectTo = buildRedirectUrl(
78+
params.offeringId,
79+
params.interval,
80+
'new',
81+
'checkout',
82+
{
83+
baseUrl: config.paymentsNextHostedUrl,
84+
locale,
85+
searchParams: redirectSearchParams,
86+
}
87+
);
88+
redirect(redirectTo);
89+
}
90+
7191
const { successActionButtonUrl, successActionButtonLabel } =
7292
cms.commonContent.localizations.at(0) || cms.commonContent;
7393

0 commit comments

Comments
 (0)