Skip to content

Commit 370ecdf

Browse files
authored
Merge pull request #18990 from mozilla/train-312-uplift-main
Train 312 uplift main
2 parents 96e6e91 + a26c695 commit 370ecdf

File tree

65 files changed

+865
-149
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

65 files changed

+865
-149
lines changed

apps/payments/next/.env

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ GLEAN_CONFIG__CHANNEL='development'
9494
GLEAN_CONFIG__LOGGER_APP_NAME='fxa-payments-next'
9595

9696
# CSP Config
97-
CSP__ACCOUNTS_STATIC_CDN=https://accounts-static.cdn.mozilla.net
97+
CSP__ACCOUNTS_STATIC_CDN=https://cdn.accounts.firefox.com
9898
CSP__PAYPAL_API='https://www.sandbox.paypal.com'
9999

100100
# Sentry Config

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

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,8 @@ export default async function Checkout({
8181
]);
8282

8383
const redirectSearchParams: Record<string, string> = searchParams || {};
84-
if (cart.taxAddress) {
85-
redirectSearchParams.countryCode = cart.taxAddress.countryCode;
86-
redirectSearchParams.postalCode = cart.taxAddress.postalCode;
87-
}
84+
redirectSearchParams.cartId = cart.id;
85+
redirectSearchParams.cartVersion = cart.version.toString();
8886

8987
const redirectTo = buildRedirectUrl(
9088
params.offeringId,

apps/payments/next/app/[locale]/[offeringId]/[interval]/new/page.tsx

Lines changed: 34 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
validateLocationAction,
99
getTaxAddressAction,
1010
setupCartAction,
11+
updateCartUidAction,
1112
} from '@fxa/payments/ui/actions';
1213
import { CartEligibilityStatus, CartState } from '@fxa/shared/db/mysql/account';
1314
import { BaseParams, buildRedirectUrl } from '@fxa/payments/ui';
@@ -96,41 +97,53 @@ export default async function New({
9697
}
9798

9899
let redirectToUrl: URL;
99-
try {
100-
const cart = await setupCartAction(
101-
interval as SubplatInterval,
102-
offeringId,
103-
taxAddress,
104-
undefined,
105-
coupon,
106-
fxaUid
100+
let cart: ResultCart;
101+
102+
if (searchParams.cartId && fxaUid && searchParams.cartVersion) {
103+
cart = await updateCartUidAction(
104+
searchParams.cartId,
105+
Number(searchParams.cartVersion),
106+
fxaUid,
107107
);
108108

109109
redirectToUrl = getRedirectToUrl(cart, params, searchParams);
110-
} catch (error) {
111-
if (error.name === 'CartInvalidPromoCodeError') {
112-
const cart = await setupCartAction(
110+
} else {
111+
try {
112+
cart = await setupCartAction(
113113
interval as SubplatInterval,
114114
offeringId,
115115
taxAddress,
116116
undefined,
117-
undefined,
117+
coupon,
118118
fxaUid
119119
);
120120

121121
redirectToUrl = getRedirectToUrl(cart, params, searchParams);
122-
} else if (
123-
error.name === 'RetrieveStripePriceInvalidOfferingError' ||
124-
error.name === 'RetrieveStripePriceNotFoundError'
125-
) {
126-
notFound();
127-
} else {
128-
throw error;
122+
} catch (error) {
123+
if (error.name === 'CartInvalidPromoCodeError') {
124+
cart = await setupCartAction(
125+
interval as SubplatInterval,
126+
offeringId,
127+
taxAddress,
128+
undefined,
129+
undefined,
130+
fxaUid
131+
);
132+
133+
redirectToUrl = getRedirectToUrl(cart, params, searchParams);
134+
} else if (
135+
error.name === 'RetrieveStripePriceInvalidOfferingError' ||
136+
error.name === 'RetrieveStripePriceNotFoundError'
137+
) {
138+
notFound();
139+
} else {
140+
throw error;
141+
}
129142
}
130143
}
131144

132-
redirectToUrl.searchParams.delete('countryCode');
133-
redirectToUrl.searchParams.delete('postalCode');
145+
redirectToUrl.searchParams.delete('cartId');
146+
redirectToUrl.searchParams.delete('cartVersion');
134147

135148
redirect(redirectToUrl.href);
136149
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
## Authentication Error page
2+
3+
auth-error-page-title = We Couldn’t Sign You In
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/* This Source Code Form is subject to the terms of the Mozilla Public
2+
* License, v. 2.0. If a copy of the MPL was not distributed with this
3+
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4+
5+
import Image from 'next/image';
6+
import errorIcon from '@fxa/shared/assets/images/error.svg';
7+
import mozillaIcon from '@fxa/shared/assets/images/moz-logo-bw-rgb.svg';
8+
import Link from 'next/link';
9+
import { cookies } from 'next/headers';
10+
import { BaseButton, ButtonVariant } from '@fxa/payments/ui';
11+
import { config } from '../../../../config';
12+
import { getApp } from '@fxa/payments/ui/server';
13+
14+
export default function AuthErrorPage({
15+
searchParams,
16+
}: {
17+
searchParams: Record<string, string>;
18+
}) {
19+
const cookieStore = cookies();
20+
const redirectUrl =
21+
cookieStore.get('__Secure-authjs.callback-url')?.value ||
22+
cookieStore.get('authjs.callback-url')?.value;
23+
const supportUrl = config.supportUrl
24+
const l10n = getApp().getL10n();
25+
getApp().getEmitterService().emit('auth', { type: 'error', errorMessage: searchParams?.error });
26+
27+
return (
28+
<>
29+
<header
30+
className="bg-white fixed flex justify-between items-center shadow h-16 left-0 top-0 mx-auto my-0 px-4 py-0 w-full z-40 tablet:h-20"
31+
role="banner"
32+
>
33+
<div className="flex items-center">
34+
<Image
35+
src={mozillaIcon}
36+
alt="Mozilla logo"
37+
className="object-contain"
38+
width={140}
39+
/>
40+
</div>
41+
</header>
42+
<section
43+
className="flex flex-col items-center text-center max-w-lg mx-auto mt-6 p-16 tablet:my-10 gap-16 bg-white shadow tablet:rounded-xl border border-transparent"
44+
aria-labelledby='unable-to-signin-heading'
45+
>
46+
<h1 id="unable-to-signin-heading" className="text-xl font-bold">
47+
{l10n.getString('auth-error-page-title', 'We Couldn’t Sign You In')}
48+
</h1>
49+
<Image src={errorIcon} alt="" />
50+
<p className="flex flex-col gap-6 items-center text-grey-400 max-w-md text-sm">
51+
<span>
52+
{l10n.getFragmentWithSource('checkout-error-boundary-basic-error-message',
53+
{
54+
elems: {
55+
contactSupportLink: (
56+
<Link href={supportUrl} className="underline hover:text-grey-400">
57+
contact support.
58+
</Link>
59+
)
60+
}
61+
}
62+
,
63+
<>
64+
Something went wrong. Please try again or{' '}
65+
<Link href={supportUrl} className="underline hover:text-grey-400">
66+
contact support.
67+
</Link>
68+
</>
69+
)}
70+
</span>
71+
{redirectUrl &&
72+
<Link href={redirectUrl}>
73+
<BaseButton
74+
variant={ButtonVariant.Primary}
75+
className="text-base"
76+
>
77+
Try again
78+
</BaseButton>
79+
</Link>
80+
}
81+
</p>
82+
</section>
83+
</>
84+
);
85+
}
86+

apps/payments/next/app/api/auth/callback/fxa/route.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { NextRequest } from 'next/server';
22
import { GET as AuthJsGET } from '../../../../../auth';
33
import { redirect } from 'next/navigation';
4+
import { getApp } from '@fxa/payments/ui/server';
45

56
export { POST } from '../../../../../auth';
67

@@ -21,6 +22,7 @@ export async function GET(request: NextRequest) {
2122
cookieStore.get('__Secure-authjs.callback-url') ||
2223
cookieStore.get('authjs.callback-url');
2324
if (redirectUrl?.value) {
25+
getApp().getEmitterService().emit('auth', { type: 'prompt_none_fail' });
2426
redirect(redirectUrl.value);
2527
}
2628
}

apps/payments/next/auth.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,18 @@
55
import NextAuth from 'next-auth';
66
import { authConfig } from './auth.config';
77
import { config } from './config';
8+
import { BaseError } from '@fxa/shared/error';
9+
10+
import { getApp } from '@fxa/payments/ui/server';
11+
12+
13+
export class AuthError extends BaseError {
14+
constructor(...args: ConstructorParameters<typeof BaseError>) {
15+
super(...args);
16+
this.name = 'AuthError';
17+
Object.setPrototypeOf(this, AuthError.prototype);
18+
}
19+
}
820

921
export const {
1022
handlers: { GET, POST },
@@ -13,6 +25,9 @@ export const {
1325
signOut,
1426
} = NextAuth({
1527
...authConfig,
28+
pages: {
29+
error: '/auth/error',
30+
},
1631
providers: [
1732
{
1833
id: 'fxa',
@@ -56,4 +71,17 @@ export const {
5671
};
5772
},
5873
},
74+
events: {
75+
async signIn() {
76+
getApp().getEmitterService().emit('auth', { type: 'signin' });
77+
},
78+
async signOut() {
79+
getApp().getEmitterService().emit('auth', { type: 'signout' });
80+
}
81+
},
82+
logger: {
83+
error(error: Error) {
84+
console.error(new AuthError(error.message, { cause: error }))
85+
}
86+
}
5987
});

apps/payments/next/next.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ const nextConfig = {
7777
remotePatterns: [
7878
{
7979
protocol: 'https',
80-
hostname: 'accounts-static.cdn.mozilla.net',
80+
hostname: 'cdn.accounts.firefox.com',
8181
port: '',
8282
pathname: '/product-icons/**',
8383
},
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/* This Source Code Form is subject to the terms of the Mozilla Public
2+
* License, v. 2.0. If a copy of the MPL was not distributed with this
3+
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4+
import { BaseError } from "@fxa/shared/error";
5+
6+
export class EmitterServiceHandleAuthError extends BaseError {
7+
constructor(...args: ConstructorParameters<typeof BaseError>) {
8+
super(...args);
9+
this.name = 'EmitterServiceHandleAuthError';
10+
Object.setPrototypeOf(this, EmitterServiceHandleAuthError.prototype);
11+
}
12+
}
13+
14+

libs/payments/events/src/lib/emitter.factories.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
AdditionalMetricsData,
77
SP3RolloutEvent,
88
SubscriptionEndedEvents,
9+
type AuthEvents,
910
} from './emitter.types';
1011
import {
1112
CancellationReason,
@@ -14,6 +15,13 @@ import {
1415
} from '@fxa/payments/metrics';
1516
import { SubplatInterval } from '@fxa/payments/customer';
1617

18+
export const AuthEventsFactory = (
19+
override?: Partial<AuthEvents>
20+
): AuthEvents => ({
21+
type: faker.helpers.arrayElement(['signin', 'signout', 'prompt_none_fail', 'error']),
22+
...override
23+
})
24+
1725
export const AdditionalMetricsDataFactory = (
1826
override?: AdditionalMetricsData
1927
): AdditionalMetricsData => ({

0 commit comments

Comments
 (0)