Skip to content

Commit dd534da

Browse files
committed
fix(payments-paypal): Customer cannot purchase another product with their saved PayPal account
Because - PaypalCustomerMultipleRecordsError was being thrown if there were multiple records when it should only throw if there are multiple active records This pull request - returns the billing agreement ID of the active record and throws when there are multiple active records Closes: FXA-11772
1 parent aea08cc commit dd534da

2 files changed

Lines changed: 63 additions & 18 deletions

File tree

libs/payments/paypal/src/lib/paypalBillingAgreement.manager.spec.ts

Lines changed: 54 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -143,9 +143,8 @@ describe('PaypalBillingAgreementManager', () => {
143143
.spyOn(paypalClient, 'baUpdate')
144144
.mockResolvedValue(NVPBAUpdateTransactionResponseFactory());
145145

146-
const result = await paypalBillingAgreementManager.cancel(
147-
billingAgreementId
148-
);
146+
const result =
147+
await paypalBillingAgreementManager.cancel(billingAgreementId);
149148

150149
expect(result).toBeUndefined();
151150
expect(paypalClient.baUpdate).toBeCalledWith({
@@ -211,9 +210,8 @@ describe('PaypalBillingAgreementManager', () => {
211210
.spyOn(paypalClient, 'baUpdate')
212211
.mockResolvedValue(nvpBillingAgreementMock);
213212

214-
const result = await paypalBillingAgreementManager.retrieve(
215-
billingAgreementId
216-
);
213+
const result =
214+
await paypalBillingAgreementManager.retrieve(billingAgreementId);
217215
expect(result).toEqual({
218216
city: nvpBillingAgreementMock.CITY,
219217
countryCode: nvpBillingAgreementMock.COUNTRYCODE,
@@ -239,9 +237,8 @@ describe('PaypalBillingAgreementManager', () => {
239237
.spyOn(paypalClient, 'baUpdate')
240238
.mockResolvedValue(nvpBillingAgreementMock);
241239

242-
const result = await paypalBillingAgreementManager.retrieve(
243-
billingAgreementId
244-
);
240+
const result =
241+
await paypalBillingAgreementManager.retrieve(billingAgreementId);
245242
expect(result).toEqual({
246243
city: nvpBillingAgreementMock.CITY,
247244
countryCode: nvpBillingAgreementMock.COUNTRYCODE,
@@ -271,6 +268,33 @@ describe('PaypalBillingAgreementManager', () => {
271268
expect(result).toEqual(mockPayPalCustomer.billingAgreementId);
272269
});
273270

271+
it("returns the customer's current active PayPal billing agreement ID with multiple records", async () => {
272+
const uid = faker.string.uuid();
273+
const mockPayPalCustomerRecord1 = ResultPaypalCustomerFactory({
274+
uid,
275+
status: 'Cancelled',
276+
});
277+
const mockPayPalCustomerRecord2 = ResultPaypalCustomerFactory({
278+
uid,
279+
status: 'active',
280+
});
281+
const mockPayPalCustomerRecord3 = ResultPaypalCustomerFactory({
282+
uid,
283+
status: 'Cancelled',
284+
});
285+
286+
jest
287+
.spyOn(paypalCustomerManager, 'fetchPaypalCustomersByUid')
288+
.mockResolvedValue([
289+
mockPayPalCustomerRecord1,
290+
mockPayPalCustomerRecord2,
291+
mockPayPalCustomerRecord3,
292+
]);
293+
294+
const result = await paypalBillingAgreementManager.retrieveActiveId(uid);
295+
expect(result).toEqual(mockPayPalCustomerRecord2.billingAgreementId);
296+
});
297+
274298
it('returns undefined if no PayPal customer record', async () => {
275299
const uid = faker.string.uuid();
276300

@@ -284,7 +308,9 @@ describe('PaypalBillingAgreementManager', () => {
284308

285309
it('returns undefined if billing agreement is not active', async () => {
286310
const uid = faker.string.uuid();
287-
const mockPayPalCustomer = ResultPaypalCustomerFactory({ status: 'Cancelled' });
311+
const mockPayPalCustomer = ResultPaypalCustomerFactory({
312+
status: 'Cancelled',
313+
});
288314

289315
jest
290316
.spyOn(paypalCustomerManager, 'fetchPaypalCustomersByUid')
@@ -294,14 +320,28 @@ describe('PaypalBillingAgreementManager', () => {
294320
expect(result).toEqual(undefined);
295321
});
296322

297-
it('throws PaypalCustomerMultipleRecordsError if more than one PayPal customer found', async () => {
323+
it('throws PaypalCustomerMultipleRecordsError if more than one active PayPal customer found', async () => {
298324
const uid = faker.string.uuid();
299-
const mockPayPalCustomer1 = ResultPaypalCustomerFactory();
300-
const mockPayPalCustomer2 = ResultPaypalCustomerFactory();
325+
const mockPayPalCustomerRecord1 = ResultPaypalCustomerFactory({
326+
uid,
327+
status: 'active',
328+
});
329+
const mockPayPalCustomerRecord2 = ResultPaypalCustomerFactory({
330+
uid,
331+
status: 'active',
332+
});
333+
const mockPayPalCustomerRecord3 = ResultPaypalCustomerFactory({
334+
uid,
335+
status: 'Cancelled',
336+
});
301337

302338
jest
303339
.spyOn(paypalCustomerManager, 'fetchPaypalCustomersByUid')
304-
.mockResolvedValue([mockPayPalCustomer1, mockPayPalCustomer2]);
340+
.mockResolvedValue([
341+
mockPayPalCustomerRecord1,
342+
mockPayPalCustomerRecord2,
343+
mockPayPalCustomerRecord3,
344+
]);
305345

306346
await expect(
307347
paypalBillingAgreementManager.retrieveActiveId(uid)

libs/payments/paypal/src/lib/paypalBillingAgreement.manager.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export class PaypalBillingAgreementManager {
1515
constructor(
1616
private client: PayPalClient,
1717
private paypalCustomerManager: PaypalCustomerManager
18-
) { }
18+
) {}
1919

2020
public async retrieveOrCreateId(
2121
uid: string,
@@ -99,11 +99,16 @@ export class PaypalBillingAgreementManager {
9999
async retrieveActiveId(uid: string): Promise<string | undefined> {
100100
const paypalCustomer =
101101
await this.paypalCustomerManager.fetchPaypalCustomersByUid(uid);
102-
const firstRecord = paypalCustomer.at(0);
102+
const activeRecords = paypalCustomer.filter(
103+
(customer) => customer.status === 'active'
104+
);
103105

104-
if (!firstRecord || firstRecord?.status !== 'active') return;
105-
if (paypalCustomer.length > 1)
106+
if (activeRecords.length > 1) {
106107
throw new PaypalCustomerMultipleRecordsError(uid);
108+
}
109+
110+
const firstRecord = activeRecords.at(0);
111+
if (!firstRecord) return;
107112

108113
return firstRecord.billingAgreementId;
109114
}

0 commit comments

Comments
 (0)