Skip to content

Commit 57f60c8

Browse files
authored
Merge pull request #18928 from mozilla/FXA-11699
fix(payments): Update error message for customers with active IAP subscription
2 parents c04f207 + 31c348f commit 57f60c8

9 files changed

Lines changed: 52 additions & 45 deletions

File tree

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

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,22 +27,20 @@ import { CartErrorReasonId } from '@fxa/shared/db/mysql/account';
2727
// https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config
2828
export const dynamic = 'force-dynamic';
2929

30-
export async function generateMetadata(
31-
{
32-
params,
33-
searchParams,
34-
}: {
35-
params: CheckoutParams;
36-
searchParams: Record<string, string> | undefined;
37-
},
38-
): Promise<Metadata> {
30+
export async function generateMetadata({
31+
params,
32+
searchParams,
33+
}: {
34+
params: CheckoutParams;
35+
searchParams: Record<string, string> | undefined;
36+
}): Promise<Metadata> {
3937
return buildPageMetadata({
4038
params,
4139
page: 'error',
4240
pageType: 'checkout',
4341
acceptLanguage: headers().get('accept-language'),
4442
baseUrl: config.paymentsNextHostedUrl,
45-
searchParams
43+
searchParams,
4644
});
4745
}
4846

@@ -96,7 +94,7 @@ export default async function CheckoutError({
9694
/>
9795
)
9896
}
99-
<p className="text-grey-400 max-w-sm text-sm px-7 py-0 mb-4 ">
97+
<p className="text-grey-400 max-w-sm text-sm leading-5 px-7 py-0 mb-4 ">
10098
{l10n.getString(errorReason.messageFtl, errorReason.message)}
10199
</p>
102100

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

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,22 +25,20 @@ import { Metadata } from 'next';
2525
// https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config
2626
export const dynamic = 'force-dynamic';
2727

28-
export async function generateMetadata(
29-
{
30-
params,
31-
searchParams,
32-
}: {
33-
params: CheckoutParams;
34-
searchParams: Record<string, string> | undefined;
35-
},
36-
): Promise<Metadata> {
28+
export async function generateMetadata({
29+
params,
30+
searchParams,
31+
}: {
32+
params: CheckoutParams;
33+
searchParams: Record<string, string> | undefined;
34+
}): Promise<Metadata> {
3735
return buildPageMetadata({
3836
params,
3937
page: 'error',
4038
pageType: 'upgrade',
4139
acceptLanguage: headers().get('accept-language'),
4240
baseUrl: config.paymentsNextHostedUrl,
43-
searchParams
41+
searchParams,
4442
});
4543
}
4644

@@ -78,7 +76,7 @@ export default async function UpgradeError({
7876
aria-label="Payment error"
7977
>
8078
<Image src={errorIcon} alt="" className="mt-16 mb-10" />
81-
<p className="text-grey-400 max-w-sm text-sm px-7 py-0 mb-4 ">
79+
<p className="text-grey-400 max-w-sm text-sm leading-5 px-7 py-0 mb-4 ">
8280
{l10n.getString(errorReason.messageFtl, errorReason.message)}
8381
</p>
8482

apps/payments/next/app/[locale]/en.ftl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ checkout-error-boundary-basic-error-message = Something went wrong. Please try a
44
## Error pages - /checkout and /upgrade
55
## Common strings used in multiple pages
66
next-payment-error-manage-subscription-button = Manage my subscription
7-
next-iap-upgrade-contact-support = You can still get this product — please contact support so we can help you.
7+
next-iap-blocked-contact-support = You have a mobile in-app subscription that conflicts with this product — please contact support so we can help you.
88
next-payment-error-retry-button = Try again
99
next-basic-error-message = Something went wrong. Please try again later.
1010
checkout-error-contact-support-button = Contact Support

apps/payments/next/app/[locale]/error.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ export default function Error({
8181
),
8282
}}
8383
>
84-
<p className="text-grey-400 max-w-sm text-sm px-7 py-0 mb-4 ">
84+
<p className="text-grey-400 max-w-sm text-sm leading-5 px-7 py-0 mb-4 ">
8585
Something went wrong. Please try again or
8686
<Link href={SUPPORT_URL} className="underline hover:text-grey-400">
8787
contact support.

libs/payments/cart/src/lib/cart.service.spec.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -645,7 +645,7 @@ describe('CartService', () => {
645645
eligibilityStatus: CartEligibilityStatus.BLOCKED_IAP,
646646
couponCode: args.promoCode,
647647
},
648-
CartErrorReasonId.IAP_UPGRADE_CONTACT_SUPPORT
648+
CartErrorReasonId.IAP_BLOCKED_CONTACT_SUPPORT
649649
);
650650
expect(result).toEqual(mockErrorCart);
651651
});
@@ -1280,7 +1280,7 @@ describe('CartService', () => {
12801280

12811281
const result = await cartService.getCartState(mockCart.id);
12821282
expect(result).toEqual({
1283-
state: mockCart.state
1283+
state: mockCart.state,
12841284
});
12851285

12861286
expect(cartManager.fetchCartById).toHaveBeenCalledWith(mockCart.id);
@@ -1479,9 +1479,11 @@ describe('CartService', () => {
14791479
const mockInvoicePreview = InvoicePreviewFactory();
14801480
const mockFromOfferingId = faker.string.uuid();
14811481
const mockFromPrice = StripePriceFactory({
1482-
recurring: StripePriceRecurringFactory({ interval: 'month' })
1482+
recurring: StripePriceRecurringFactory({ interval: 'month' }),
1483+
});
1484+
const mockPricingForCurrency = PricingForCurrencyFactory({
1485+
price: mockFromPrice,
14831486
});
1484-
const mockPricingForCurrency = PricingForCurrencyFactory({ price: mockFromPrice })
14851487
const mockSubscription = StripeSubscriptionFactory();
14861488

14871489
jest.spyOn(cartManager, 'fetchCartById').mockResolvedValue(mockCart);
@@ -1500,7 +1502,9 @@ describe('CartService', () => {
15001502
jest
15011503
.spyOn(subscriptionManager, 'retrieveForCustomerAndPrice')
15021504
.mockResolvedValue(mockSubscription);
1503-
jest.spyOn(priceManager, 'retrievePricingForCurrency').mockResolvedValue(mockPricingForCurrency);
1505+
jest
1506+
.spyOn(priceManager, 'retrievePricingForCurrency')
1507+
.mockResolvedValue(mockPricingForCurrency);
15041508

15051509
const result = await cartService.getCart(mockCart.id);
15061510
expect(result).toEqual({

libs/payments/cart/src/lib/cart.service.ts

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,8 @@ export class CartService {
106106
private productConfigurationManager: ProductConfigurationManager,
107107
private promotionCodeManager: PromotionCodeManager,
108108
private subscriptionManager: SubscriptionManager,
109-
@Inject(StatsDService) private statsd: StatsD,
110-
) { }
109+
@Inject(StatsDService) private statsd: StatsD
110+
) {}
111111

112112
/**
113113
* Should be used to wrap any method that mutates an existing cart.
@@ -355,7 +355,7 @@ export class CartService {
355355
if (cartEligibilityStatus === CartEligibilityStatus.BLOCKED_IAP) {
356356
return this.cartManager.createErrorCart(
357357
createCartParams,
358-
CartErrorReasonId.IAP_UPGRADE_CONTACT_SUPPORT
358+
CartErrorReasonId.IAP_BLOCKED_CONTACT_SUPPORT
359359
);
360360
}
361361

@@ -390,12 +390,12 @@ export class CartService {
390390

391391
const accountCustomer = oldCart.uid
392392
? await this.accountCustomerManager
393-
.getAccountCustomerByUid(oldCart.uid)
394-
.catch((error) => {
395-
if (!(error instanceof AccountCustomerNotFoundError)) {
396-
throw error;
397-
}
398-
})
393+
.getAccountCustomerByUid(oldCart.uid)
394+
.catch((error) => {
395+
if (!(error instanceof AccountCustomerNotFoundError)) {
396+
throw error;
397+
}
398+
})
399399
: undefined;
400400

401401
if (!(oldCart.taxAddress && oldCart.currency)) {
@@ -782,11 +782,18 @@ export class CartService {
782782
if (cartEligibilityStatus === CartEligibilityStatus.UPGRADE) {
783783
assert('fromPrice' in eligibility, 'fromPrice not present for upgrade');
784784

785-
const { price: priceForCurrency, unitAmountForCurrency } = await this.priceManager.retrievePricingForCurrency(eligibility.fromPrice.id, cart.currency);
785+
const { price: priceForCurrency, unitAmountForCurrency } =
786+
await this.priceManager.retrievePricingForCurrency(
787+
eligibility.fromPrice.id,
788+
cart.currency
789+
);
786790
assertNotNull(unitAmountForCurrency);
787791
assertNotNull(priceForCurrency.recurring);
788792

789-
const interval = getSubplatInterval(priceForCurrency.recurring.interval, priceForCurrency.recurring.interval_count);
793+
const interval = getSubplatInterval(
794+
priceForCurrency.recurring.interval,
795+
priceForCurrency.recurring.interval_count
796+
);
790797
assert(interval, 'Interval not found but is required');
791798

792799
fromPrice = {

libs/payments/cart/src/lib/cart.utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export const cartEligibilityDetailsMap: Record<
4040
[EligibilityStatus.BLOCKED_IAP]: {
4141
eligibilityStatus: CartEligibilityStatus.BLOCKED_IAP,
4242
state: CartState.FAIL,
43-
errorReasonId: CartErrorReasonId.IAP_UPGRADE_CONTACT_SUPPORT,
43+
errorReasonId: CartErrorReasonId.IAP_BLOCKED_CONTACT_SUPPORT,
4444
},
4545
[EligibilityStatus.SAME]: {
4646
eligibilityStatus: CartEligibilityStatus.INVALID,

libs/payments/ui/src/lib/utils/getErrorFtlInfo.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,14 @@ export function getErrorFtlInfo(
3939
message: 'You’ve already subscribed to this product.',
4040
messageFtl: 'checkout-error-already-subscribed',
4141
};
42-
case CartErrorReasonId.IAP_UPGRADE_CONTACT_SUPPORT:
42+
case CartErrorReasonId.IAP_BLOCKED_CONTACT_SUPPORT:
4343
return {
4444
buttonFtl: 'next-payment-error-manage-subscription-button',
4545
buttonLabel: 'Manage my subscription',
4646
buttonUrl: `${config.contentServerUrl}/subscriptions`,
4747
message:
48-
'You can still get this product — please contact support so we can help you.',
49-
messageFtl: 'next-iap-upgrade-contact-support',
48+
'You have a mobile in-app subscription that conflicts with this product — please contact support so we can help you.',
49+
messageFtl: 'next-iap-blocked-contact-support',
5050
};
5151
case CartErrorReasonId.CART_CURRENCY_NOT_DETERMINED:
5252
return {

libs/shared/db/mysql/account/src/lib/kysely-types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export enum CartState {
3333

3434
export enum CartErrorReasonId {
3535
BASIC_ERROR = 'basic-error-message',
36-
IAP_UPGRADE_CONTACT_SUPPORT = 'iap_upgrade_contact_support',
36+
IAP_BLOCKED_CONTACT_SUPPORT = 'iap_blocked_contact_support',
3737
SUCCESS_CART_MISSING_REQUIRED = 'success_cart_missing_required',
3838
CART_ELIGIBILITY_STATUS_SAME = 'cart_eligibility_status_same',
3939
CART_ELIGIBILITY_STATUS_DOWNGRADE = 'cart_eligibility_status_downgrade',

0 commit comments

Comments
 (0)