Skip to content

Commit 068b162

Browse files
authored
Merge pull request #18948 from mozilla/FXA-11770
fix(payments): Customers with saved card and no active subs should see PayPal privacy policy link on Checkout
2 parents 90dc8f3 + 9436f0d commit 068b162

5 files changed

Lines changed: 78 additions & 3 deletions

File tree

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

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1427,6 +1427,73 @@ describe('CartService', () => {
14271427
);
14281428
});
14291429

1430+
it('returns cart with success state', async () => {
1431+
const mockCart = ResultCartFactory({
1432+
state: CartState.SUCCESS,
1433+
stripeSubscriptionId: mockSubscription.id,
1434+
});
1435+
const mockCustomer = StripeResponseFactory(StripeCustomerFactory());
1436+
const mockPrice = StripePriceFactory();
1437+
const mockUpcomingInvoicePreview = InvoicePreviewFactory();
1438+
const mockLatestInvoicePreview = InvoicePreviewFactory();
1439+
const mockPaymentMethod = StripeResponseFactory(
1440+
StripePaymentMethodFactory({})
1441+
);
1442+
jest.spyOn(eligibilityService, 'checkEligibility').mockResolvedValue({
1443+
subscriptionEligibilityResult: EligibilityStatus.CREATE,
1444+
});
1445+
1446+
jest.spyOn(cartManager, 'fetchCartById').mockResolvedValue(mockCart);
1447+
jest
1448+
.spyOn(productConfigurationManager, 'retrieveStripePrice')
1449+
.mockResolvedValue(mockPrice);
1450+
jest.spyOn(customerManager, 'retrieve').mockResolvedValue(mockCustomer);
1451+
jest
1452+
.spyOn(invoiceManager, 'previewUpcoming')
1453+
.mockResolvedValue(mockUpcomingInvoicePreview);
1454+
jest
1455+
.spyOn(invoiceManager, 'preview')
1456+
.mockResolvedValue(mockLatestInvoicePreview);
1457+
jest
1458+
.spyOn(paymentMethodManager, 'retrieve')
1459+
.mockResolvedValue(mockPaymentMethod);
1460+
1461+
const result = await cartService.getCart(mockCart.id);
1462+
expect(result).toEqual({
1463+
...mockCart,
1464+
upcomingInvoicePreview: mockUpcomingInvoicePreview,
1465+
latestInvoicePreview: mockLatestInvoicePreview,
1466+
metricsOptedOut: false,
1467+
paymentInfo: {
1468+
type: mockPaymentMethod.type,
1469+
last4: mockPaymentMethod.card?.last4,
1470+
brand: mockPaymentMethod.card?.brand,
1471+
customerSessionClientSecret: mockCustomerSession.client_secret,
1472+
},
1473+
hasActiveSubscriptions: true,
1474+
});
1475+
expect(
1476+
'latestInvoicePreview' in result && result.latestInvoicePreview
1477+
).not.toEqual(result.upcomingInvoicePreview);
1478+
1479+
expect(cartManager.fetchCartById).toHaveBeenCalledWith(mockCart.id);
1480+
expect(
1481+
productConfigurationManager.retrieveStripePrice
1482+
).toHaveBeenCalledWith(mockCart.offeringConfigId, mockCart.interval);
1483+
expect(customerManager.retrieve).toHaveBeenCalledWith(
1484+
mockCart.stripeCustomerId
1485+
);
1486+
expect(invoiceManager.previewUpcoming).toHaveBeenCalledWith({
1487+
priceId: mockPrice.id,
1488+
currency: mockCart.currency,
1489+
customer: mockCustomer,
1490+
taxAddress: mockCart.taxAddress,
1491+
});
1492+
expect(invoiceManager.preview).toHaveBeenCalledWith(
1493+
mockSubscription.latest_invoice
1494+
);
1495+
});
1496+
14301497
it('returns cart and upcomingInvoicePreview if customer is undefined', async () => {
14311498
const mockCart = ResultCartFactory({
14321499
stripeCustomerId: null,

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -789,6 +789,7 @@ export class CartService {
789789
metricsOptedOut,
790790
latestInvoicePreview,
791791
paymentInfo,
792+
hasActiveSubscriptions: !!subscriptions.length,
792793
};
793794
}
794795

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ export type SuccessCartDTO = BaseCartDTO & {
9191
state: CartState.SUCCESS;
9292
latestInvoicePreview: Invoice;
9393
paymentInfo: PaymentInfo;
94+
hasActiveSubscriptions: boolean;
9495
};
9596

9697
export type NeedsInputCartDTO = BaseCartDTO & {

libs/payments/ui/src/lib/server/components/TermsAndPrivacy/index.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ export interface TermsAndPrivacyProps {
6868
privacyNoticeUrl: string;
6969
contentServerUrl: string;
7070
showFXALinks?: boolean;
71+
hasActiveSubscriptions?: boolean;
7172
}
7273

7374
export async function TermsAndPrivacy({
@@ -79,9 +80,10 @@ export async function TermsAndPrivacy({
7980
privacyNoticeUrl,
8081
contentServerUrl,
8182
showFXALinks = false,
83+
hasActiveSubscriptions,
8284
}: TermsAndPrivacyProps) {
8385
const terms: GenericTermItem[] = [
84-
...buildPaymentTerms(paymentInfo),
86+
...buildPaymentTerms(paymentInfo, hasActiveSubscriptions),
8587
...buildFirefoxAccountsTerms(showFXALinks, contentServerUrl),
8688
...buildProductTerms(
8789
productName,

libs/payments/ui/src/lib/utils/terms-and-privacy.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,14 @@ export const PaymentProviders = {
2929
export type PaymentProvidersType = typeof PaymentProviders;
3030
export type PaymentProvider = PaymentProvidersType[keyof PaymentProvidersType];
3131

32-
export function buildPaymentTerms(provider?: PaymentInfo): GenericTermItem[] {
32+
export function buildPaymentTerms(
33+
provider?: PaymentInfo,
34+
hasActiveSubscriptions?: boolean
35+
): GenericTermItem[] {
3336
let providerString = '';
3437
let titleLocalizationId = '';
35-
const providerType = provider ? provider.type : undefined;
38+
const providerType =
39+
hasActiveSubscriptions && provider ? provider.type : undefined;
3640
const items: GenericTermsListItem[] = [];
3741
switch (providerType) {
3842
case PaymentProviders.stripe:

0 commit comments

Comments
 (0)