Skip to content

Commit 2c8ab7b

Browse files
feat(payments-next): Cancellation flow: Create page - Interstitial Offer
Because: * We need a cancel interstitial offer page as part of the churn intervention epic. This commit: * Creates /[locale]/subscriptions/[subscription_id]/offer page Closes #PAY-3371
1 parent 087ec80 commit 2c8ab7b

File tree

9 files changed

+198
-0
lines changed

9 files changed

+198
-0
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
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 { headers } from 'next/headers';
6+
import { /*notFound,*/ redirect } from 'next/navigation';
7+
8+
//import { InterstitialOffer, InterstitialOfferProps } from '@fxa/payments/ui';
9+
import { getInterstitialOfferContentAction } from '@fxa/payments/ui/actions';
10+
import { auth } from 'apps/payments/next/auth';
11+
import { config } from 'apps/payments/next/config';
12+
import { SubplatInterval } from '@fxa/payments/customer';
13+
14+
export default async function InterstitialOfferPage({
15+
params,
16+
searchParams,
17+
}: {
18+
params: {
19+
locale: string;
20+
subscriptionId: string;
21+
};
22+
searchParams: Record<string, string> | undefined;
23+
}) {
24+
const { locale, subscriptionId } = params;
25+
const acceptLanguage = headers().get('accept-language');
26+
const session = await auth();
27+
if (!session?.user?.id) {
28+
const redirectToUrl = new URL(
29+
`${config.paymentsNextHostedUrl}/${locale}/subscriptions/landing`
30+
);
31+
redirectToUrl.search = new URLSearchParams(searchParams).toString();
32+
redirect(redirectToUrl.href);
33+
}
34+
35+
const uid = session.user.id;
36+
37+
const pageContent = await getInterstitialOfferContentAction(
38+
uid,
39+
subscriptionId,
40+
'vpn', // hardcoded offeringApiIdentifier for now
41+
SubplatInterval.Monthly, // hardcoded for now
42+
SubplatInterval.Yearly,
43+
acceptLanguage,
44+
locale
45+
);
46+
console.log('these are pageContent: ', pageContent)
47+
}

libs/payments/ui/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export * from './lib/client/components/CheckoutForm';
1414
export * from './lib/client/components/CheckoutCheckbox';
1515
export * from './lib/client/components/CouponForm';
1616
export * from './lib/client/components/Header';
17+
export * from './lib/client/components/InterstitialOffer';
1718
export * from './lib/client/components/LoadingSpinner';
1819
export * from './lib/client/components/PageNotFound';
1920
export * from './lib/client/components/PaymentStateObserver';
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
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+
'use server';
6+
7+
import { getApp } from '../nestapp/app';
8+
import { SubplatInterval } from '@fxa/payments/customer';
9+
10+
export const getInterstitialOfferContentAction = async (
11+
uid: string,
12+
subscriptionId: string,
13+
offeringApiIdentifier: string,
14+
currentInterval: SubplatInterval,
15+
upgradeInterval: SubplatInterval,
16+
acceptLanguage?: string | null,
17+
selectedLanguage?: string
18+
) => {
19+
const result = await getApp().getActionsService().getInterstitialOfferContent({
20+
uid,
21+
subscriptionId,
22+
offeringApiIdentifier,
23+
currentInterval,
24+
upgradeInterval,
25+
acceptLanguage,
26+
selectedLanguage,
27+
});
28+
29+
return result;
30+
};

libs/payments/ui/src/lib/actions/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export { determineCancellationInterventionAction } from './determineCancellation
1313
export { fetchCMSData } from './fetchCMSData';
1414
export { getCartAction } from './getCart';
1515
export { getCartOrRedirectAction } from './getCartOrRedirect';
16+
export { getInterstitialOfferContentAction } from './getInterstitialOfferContent';
1617
export { getMetricsFlowAction } from './getMetricsFlow';
1718
export { getPayPalCheckoutToken } from './getPayPalCheckoutToken';
1819
export { getPayPalBillingAgreementId } from './getPayPalBillingAgreementId';
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
## InterstitialOffer
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
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+
'use client';
6+
7+
import { SubplatInterval } from '@fxa/payments/customer';
8+
9+
export interface InterstitialOfferProps { //update pageContent properly
10+
userId: string;
11+
subscriptionId: string;
12+
offeringApiIdentifier: string;
13+
currentInterval: SubplatInterval;
14+
upgradeInterval: SubplatInterval;
15+
locale: string;
16+
pageContent: {
17+
offeringApiIdentifier: string;
18+
currentInterval: SubplatInterval;
19+
upgradeInterval: SubplatInterval;
20+
advertisedSavings: number;
21+
ctaMessage: string;
22+
modalHeading1: string;
23+
modalHeading2: string;
24+
modalMessage: string;
25+
productPageUrl: string;
26+
upgradeButtonLabel: string;
27+
upgradeButtonUrl: string;
28+
};
29+
}
30+
31+
export function InterstitialOffer({
32+
userId,
33+
subscriptionId,
34+
locale,
35+
pageContent,
36+
}: InterstitialOfferProps) {
37+
38+
}

libs/payments/ui/src/lib/nestapp/nextjs-actions.service.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ import { GetChurnInterventionDataActionArgs } from './validators/GetChurnInterve
4646
import { GetPayPalCheckoutTokenArgs } from './validators/GetPayPalCheckoutTokenArgs';
4747
import { GetSubManPageContentActionArgs } from './validators/GetSubManPageContentActionArgs';
4848
import { GetSubManPageContentActionResult } from './validators/GetSubManPageContentActionResult';
49+
import { GetInterstitialOfferContentActionArgs } from './validators/GetInterstitialOfferContentActionArgs';//
50+
import { GetInterstitialOfferContentActionResult } from './validators/GetInterstitialOfferContentActionResult';
4951
import { RestartCartActionArgs } from './validators/RestartCartActionArgs';
5052
import { SetupCartActionArgs } from './validators/SetupCartActionArgs';
5153
import { UpdateCartActionArgs } from './validators/UpdateCartActionArgs';
@@ -693,6 +695,38 @@ export class NextJSActionsService {
693695
return result;
694696
}
695697

698+
@SanitizeExceptions()
699+
@NextIOValidator(
700+
GetInterstitialOfferContentActionArgs,
701+
GetInterstitialOfferContentActionResult
702+
)
703+
@WithTypeCachableAsyncLocalStorage()
704+
@CaptureTimingWithStatsD()
705+
async getInterstitialOfferContent(args: {
706+
uid: string;
707+
subscriptionId: string;
708+
offeringApiIdentifier: string;
709+
currentInterval: SubplatInterval;
710+
upgradeInterval: SubplatInterval;
711+
acceptLanguage?: string | null;
712+
selectedLanguage?: string;
713+
}) {
714+
const result =
715+
await this.churnInterventionService.determineCancelInterstitialOfferEligibility({ //reuse? or make a separate func?
716+
uid: args.uid,
717+
subscriptionId: args.subscriptionId,
718+
offeringApiIdentifier: args.offeringApiIdentifier,
719+
currentInterval: args.currentInterval,
720+
upgradeInterval: args.upgradeInterval,
721+
acceptLanguage: args.acceptLanguage,
722+
selectedLanguage: args.selectedLanguage
723+
});
724+
725+
return {
726+
cancelInterstitialOfferResult: result.cmsCancelInterstitialOfferResult
727+
};
728+
}
729+
696730
@SanitizeExceptions()
697731
@NextIOValidator(DetermineCurrencyActionArgs, DetermineCurrencyActionResult)
698732
@WithTypeCachableAsyncLocalStorage()
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
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 { IsString, IsOptional } from 'class-validator';
6+
import { SubplatInterval } from '@fxa/payments/customer';
7+
8+
export class GetInterstitialOfferContentActionArgs {
9+
@IsString()
10+
uid!: string;
11+
12+
@IsString()
13+
subscriptionId!: string;
14+
15+
@IsString()
16+
offeringApiIdentifier!: string;
17+
18+
@IsString()
19+
currentInterval!: SubplatInterval;
20+
21+
@IsString()
22+
upgradeInterval!: SubplatInterval;
23+
24+
@IsString()
25+
@IsOptional()
26+
acceptLanguage?: string;
27+
28+
@IsString()
29+
@IsOptional()
30+
selectedLanguage?: string;
31+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
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 {
6+
ValidateNested,
7+
} from 'class-validator';
8+
import { Type } from 'class-transformer';
9+
import { CmsCancelInterstitialOfferResult } from './DetermineCancellationInterventionActionResult';
10+
11+
export class GetInterstitialOfferContentActionResult {
12+
@ValidateNested()
13+
@Type(() => CmsCancelInterstitialOfferResult)
14+
cancelInterstitialOfferResult!: CmsCancelInterstitialOfferResult | null;
15+
}

0 commit comments

Comments
 (0)