feat(stripe): polish pr to update `prePaySteps` and `createPlainCustomer`

Because:

* We want to clean up our code and make sure our methods are created and updated appropriately.

This commit:

* Is a follow up to https://github.com/mozilla/fxa/pull/16924 comments and Slack messages between the SubPlat SWEs.
* Updates `StripeManager.createPlainCustomer`.
* Updates `CheckoutService.prePaySteps` to add `AccountCustomerManager.createAccountCustomer`.
* Updates all applicable tests.
This commit is contained in:
Meghan Sardesai 2024-05-22 15:32:58 -04:00
Родитель 257a254305
Коммит 2a0fb52594
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 9A46BEBC2E8A3934
4 изменённых файлов: 86 добавлений и 42 удалений

Просмотреть файл

@ -20,6 +20,8 @@ import {
StripeSubscriptionFactory,
TaxAddressFactory,
InvoicePreviewFactory,
AccountCustomerManager,
ResultAccountCustomerFactory,
} from '@fxa/payments/stripe';
import {
PayPalClient,
@ -68,6 +70,7 @@ describe('CheckoutService', () => {
let eligibilityService: EligibilityService;
let contentfulService: ContentfulService;
let accountManager: AccountManager;
let accountCustomerManager: AccountCustomerManager;
beforeEach(async () => {
const moduleRef = await Test.createTestingModule({
@ -81,6 +84,7 @@ describe('CheckoutService', () => {
StripeConfig,
PaypalClientConfig,
AccountManager,
AccountCustomerManager,
MockAccountDatabaseNestFactory,
CartManager,
ContentfulManager,
@ -104,6 +108,7 @@ describe('CheckoutService', () => {
stripeManager = moduleRef.get(StripeManager);
contentfulService = moduleRef.get(ContentfulService);
accountManager = moduleRef.get(AccountManager);
accountCustomerManager = moduleRef.get(AccountCustomerManager);
checkoutService = moduleRef.get(CheckoutService);
});
@ -140,6 +145,13 @@ describe('CheckoutService', () => {
})
);
const mockAccountCustomer = StripeResponseFactory(
ResultAccountCustomerFactory({
uid: uid,
stripeCustomerId: mockCart.stripeCustomerId,
})
);
const mockPrice = StripePriceFactory();
const mockPromotionCode = StripeResponseFactory(
@ -151,10 +163,13 @@ describe('CheckoutService', () => {
jest
.spyOn(stripeManager, 'createPlainCustomer')
.mockResolvedValue(mockCustomer);
jest.spyOn(cartManager, 'updateFreshCart').mockResolvedValue();
jest
.spyOn(stripeManager, 'fetchActiveCustomer')
.mockResolvedValue(mockCustomer);
jest
.spyOn(accountCustomerManager, 'createAccountCustomer')
.mockResolvedValue(mockAccountCustomer);
jest.spyOn(cartManager, 'updateFreshCart').mockResolvedValue();
jest
.spyOn(eligibilityService, 'checkEligibility')
.mockResolvedValue(EligibilityStatus.CREATE);
@ -248,6 +263,16 @@ describe('CheckoutService', () => {
stripeCustomerId: mockCart.stripeCustomerId,
});
});
it('creates an account customer', () => {
expect(
accountCustomerManager.createAccountCustomer
).toHaveBeenCalledWith({
uid: uid,
stripeCustomerId: mockCart.stripeCustomerId,
updatedAt: Date.now(),
});
});
});
describe('success - with new stripe customer stub account', () => {
@ -267,6 +292,7 @@ describe('CheckoutService', () => {
it('creates a new stripe customer stub account', () => {
expect(stripeManager.createPlainCustomer).toHaveBeenCalledWith({
uid: uid,
email: mockCart.email,
taxAddress: mockCart.taxAddress,
});

Просмотреть файл

@ -6,6 +6,7 @@ import { Injectable } from '@nestjs/common';
import { EligibilityService } from '@fxa/payments/eligibility';
import { PayPalManager, PaypalCustomerManager } from '@fxa/payments/paypal';
import {
AccountCustomerManager,
StripeClient,
StripeManager,
StripeSubscription,
@ -34,7 +35,8 @@ export class CheckoutService {
private cartManager: CartManager,
private eligibilityService: EligibilityService,
private contentfulService: ContentfulService,
private accountManager: AccountManager
private accountManager: AccountManager,
private accountCustomerManager: AccountCustomerManager
) {}
async prePaySteps(cart: ResultCart, locale: string) {
@ -45,20 +47,6 @@ export class CheckoutService {
throw new CartEmailNotFoundError(cart.id);
}
// if stripeCustomerId not found, create stub stripe account
if (!cart.stripeCustomerId) {
customer = await this.stripeManager.createPlainCustomer({
email: cart.email,
taxAddress: taxAddress,
});
stripeCustomerId = customer.id;
} else {
stripeCustomerId = cart.stripeCustomerId;
customer = await this.stripeManager.fetchActiveCustomer(stripeCustomerId);
}
// if uid not found, create stub account customer
// TODO: update hardcoded verifierVersion
// https://mozilla-hub.atlassian.net/browse/FXA-9693
@ -72,6 +60,29 @@ export class CheckoutService {
uid = cart.uid;
}
// if stripeCustomerId not found, create plain stripe account
if (!cart.stripeCustomerId) {
customer = await this.stripeManager.createPlainCustomer({
uid: uid,
email: cart.email,
taxAddress: taxAddress,
});
stripeCustomerId = customer.id;
} else {
stripeCustomerId = cart.stripeCustomerId;
customer = await this.stripeManager.fetchActiveCustomer(stripeCustomerId);
}
// create accountCustomer if it does not exist
if (!cart.uid) {
await this.accountCustomerManager.createAccountCustomer({
uid: uid,
stripeCustomerId: stripeCustomerId,
updatedAt: Date.now(),
});
}
// update cart
// TODO: update code so it's conditional only when cart data needs to be updated
// NOTE: originally done as two separate calls dependent on if

Просмотреть файл

@ -96,22 +96,11 @@ describe('StripeManager', () => {
});
describe('createPlainCustomer', () => {
it('create a stub customer from Stripe', async () => {
const mockCustomer = StripeResponseFactory(StripeCustomerFactory());
jest
.spyOn(stripeClient, 'customersCreate')
.mockResolvedValue(mockCustomer);
const result = await stripeManager.createPlainCustomer({});
expect(result).toEqual(mockCustomer);
});
it('create a stub customer with args from Stripe', async () => {
it('creates a plain customer from Stripe', async () => {
const taxAddress = TaxAddressFactory();
const mockCustomer = StripeResponseFactory(
StripeCustomerFactory({
name: faker.person.fullName(),
shipping: {
name: '',
address: {
@ -131,7 +120,9 @@ describe('StripeManager', () => {
.mockResolvedValue(mockCustomer);
const result = await stripeManager.createPlainCustomer({
uid: faker.string.uuid(),
email: faker.internet.email(),
displayName: faker.person.fullName(),
taxAddress: taxAddress,
});

Просмотреть файл

@ -76,20 +76,36 @@ export class StripeManager {
/**
* Create customer stub account
*/
async createPlainCustomer(args: { email?: string; taxAddress?: TaxAddress }) {
if (args.taxAddress) {
return this.client.customersCreate({
shipping: {
name: args.email || '',
address: {
country: args.taxAddress.countryCode,
postal_code: args.taxAddress.postalCode,
},
},
});
}
async createPlainCustomer(args: {
uid: string;
email: string;
displayName?: string;
taxAddress?: TaxAddress;
}) {
const { uid, email, displayName, taxAddress } = args;
return this.client.customersCreate();
const shipping = taxAddress
? {
name: email,
address: {
country: taxAddress.countryCode,
postal_code: taxAddress.postalCode,
},
}
: undefined;
const stripeCustomer = await this.client.customersCreate({
email,
name: displayName || '',
description: uid,
metadata: {
userid: uid,
geoip_date: new Date().toString(),
},
shipping,
});
return stripeCustomer;
}
/**