From 91d30d82e8799422bbcc29a8d1bb832fc0399b36 Mon Sep 17 00:00:00 2001 From: elizabeth-ilina Date: Wed, 10 Jun 2026 17:42:06 -0400 Subject: [PATCH] feat(payments-next): [Sunset improvements] Include column in script output to denote Stripe or PayPal Because: * Accounting has requested that we amend our Mover and Cancellation scripts for use in future sunset efforts to include a column in the script output denoting if the subscription was paid via Stripe or PayPal. This commit: * Adds a paymentProvider column to the cancel-subscriptions-to-plan and move-customers-to-new-plan-v2 scripts Closes #[PAY-3465](https://mozilla-hub.atlassian.net/browse/PAY-3465) --- .../cancel-subscriptions-to-plan.spec.ts | 19 ++++++++++++---- .../cancel-subscriptions-to-plan.ts | 11 ++++++++-- .../move-customers-to-new-plan-v2.spec.ts | 22 ++++++++++++------- .../move-customers-to-new-plan-v2.ts | 14 +++++++++--- 4 files changed, 49 insertions(+), 17 deletions(-) diff --git a/packages/fxa-auth-server/scripts/cancel-subscriptions-to-plan/cancel-subscriptions-to-plan.spec.ts b/packages/fxa-auth-server/scripts/cancel-subscriptions-to-plan/cancel-subscriptions-to-plan.spec.ts index e0f3902e766..e757583c6b0 100644 --- a/packages/fxa-auth-server/scripts/cancel-subscriptions-to-plan/cancel-subscriptions-to-plan.spec.ts +++ b/packages/fxa-auth-server/scripts/cancel-subscriptions-to-plan/cancel-subscriptions-to-plan.spec.ts @@ -237,6 +237,7 @@ describe('PlanCanceller', () => { expect.objectContaining({ subscription: mockSub, customer: mockCustomer, + paymentProvider: 'Stripe', isExcluded: false, amountRefunded: 1000, isOwed: false, @@ -246,17 +247,23 @@ describe('PlanCanceller', () => { }); }); - describe('success - with refund', () => { + describe('success - PayPal subscription with refund', () => { + const paypalSub = { + ...mockSub, + collection_method: 'send_invoice', + } as unknown as Stripe.Subscription; + beforeEach(async () => { attemptFullRefundStub.mockResolvedValue(1000); - await planCanceller.processSubscription(mockSub); + await planCanceller.processSubscription(paypalSub); }); - it('writes report with refund amount', () => { + it('writes report with PayPal provider and refund amount', () => { expect(writeReportStub).toHaveBeenCalledWith( expect.objectContaining({ - subscription: mockSub, + subscription: paypalSub, customer: mockCustomer, + paymentProvider: 'PayPal', isExcluded: false, amountRefunded: 1000, isOwed: false, @@ -285,6 +292,7 @@ describe('PlanCanceller', () => { expect.objectContaining({ subscription: mockSub, customer: mockCustomer, + paymentProvider: 'Stripe', isExcluded: false, amountRefunded: 1000, isOwed: false, @@ -309,6 +317,7 @@ describe('PlanCanceller', () => { expect.objectContaining({ subscription: mockSub, customer: mockCustomer, + paymentProvider: 'Stripe', isExcluded: true, amountRefunded: null, isOwed: false, @@ -327,6 +336,7 @@ describe('PlanCanceller', () => { expect.objectContaining({ subscription: mockSub, customer: null, + paymentProvider: 'Stripe', isExcluded: false, amountRefunded: null, isOwed: false, @@ -343,6 +353,7 @@ describe('PlanCanceller', () => { expect.objectContaining({ subscription: mockSub, customer: null, + paymentProvider: 'Stripe', isExcluded: false, amountRefunded: null, isOwed: false, diff --git a/packages/fxa-auth-server/scripts/cancel-subscriptions-to-plan/cancel-subscriptions-to-plan.ts b/packages/fxa-auth-server/scripts/cancel-subscriptions-to-plan/cancel-subscriptions-to-plan.ts index 88ca556592a..ea0b0db27d4 100644 --- a/packages/fxa-auth-server/scripts/cancel-subscriptions-to-plan/cancel-subscriptions-to-plan.ts +++ b/packages/fxa-auth-server/scripts/cancel-subscriptions-to-plan/cancel-subscriptions-to-plan.ts @@ -91,6 +91,8 @@ export class PlanCanceller { typeof subscription.customer === 'string' ? subscription.customer : subscription.customer.id; + const paymentProvider = + subscription.collection_method === 'send_invoice' ? 'PayPal' : 'Stripe'; try { console.log(`Processing ${subscription.id}`); @@ -160,6 +162,7 @@ export class PlanCanceller { await this.writeReport({ subscription, customer, + paymentProvider, isExcluded, amountRefunded, approximateAmountWasOwed, @@ -176,6 +179,7 @@ export class PlanCanceller { await this.writeReport({ subscription, customer: null, + paymentProvider, isExcluded: false, amountRefunded: null, approximateAmountWasOwed: null, @@ -371,7 +375,8 @@ export class PlanCanceller { "daysUntilNextBill", "previousInvoiceAmountDue", "isOwed", - "error" + "error", + "paymentProvider" ]; const reportCSV = data.join(',') + '\n'; @@ -385,6 +390,7 @@ export class PlanCanceller { async writeReport(args: { subscription: Stripe.Subscription, customer: Stripe.Customer | null, + paymentProvider: 'Stripe' | 'PayPal', isExcluded: boolean, amountRefunded: number | null, approximateAmountWasOwed: number | null, @@ -408,7 +414,8 @@ export class PlanCanceller { args.daysUntilNextBill ?? "null", args.previousInvoiceAmountDue ?? "null", args.isOwed, - args.error + args.error, + args.paymentProvider ]; const reportCSV = data.join(',') + '\n'; diff --git a/packages/fxa-auth-server/scripts/move-customers-to-new-plan-v2/move-customers-to-new-plan-v2.spec.ts b/packages/fxa-auth-server/scripts/move-customers-to-new-plan-v2/move-customers-to-new-plan-v2.spec.ts index 8eabf803561..db4cb32b9e7 100644 --- a/packages/fxa-auth-server/scripts/move-customers-to-new-plan-v2/move-customers-to-new-plan-v2.spec.ts +++ b/packages/fxa-auth-server/scripts/move-customers-to-new-plan-v2/move-customers-to-new-plan-v2.spec.ts @@ -243,6 +243,7 @@ describe('CustomerPlanMover v2', () => { expect(writeReportStub).toHaveBeenCalledTimes(1); const reportArgs = writeReportStub.mock.calls[0][0]; expect(reportArgs.subscription.id).toBe('sub_123'); + expect(reportArgs.paymentProvider).toBe('Stripe'); expect(reportArgs.isExcluded).toBe(false); expect(reportArgs.amountRefunded).toBe(null); expect(reportArgs.approximateAmountWasOwed).toBe(null); @@ -442,8 +443,12 @@ describe('CustomerPlanMover v2', () => { }); }); - describe('success - with prorated refund', () => { + describe('success - PayPal subscription with prorated refund', () => { let attemptRefundStub: jest.Mock; + const paypalSub = { + ...mockStripeSubscription, + collection_method: 'send_invoice', + } as unknown as Stripe.Subscription; beforeEach(async () => { customerPlanMover = new CustomerPlanMover( @@ -470,20 +475,18 @@ describe('CustomerPlanMover v2', () => { stripeStub.subscriptions.update = jest .fn() - .mockResolvedValue(mockStripeSubscription); + .mockResolvedValue(paypalSub); - await customerPlanMover.convertSubscription( - mockStripeSubscription, - mockPrice - ); + await customerPlanMover.convertSubscription(paypalSub, mockPrice); }); it('attempts refund', () => { - expect(attemptRefundStub).toHaveBeenCalledWith(mockStripeSubscription); + expect(attemptRefundStub).toHaveBeenCalledWith(paypalSub); }); - it('writes report with refund amount', () => { + it('writes report with PayPal provider and refund amount', () => { const reportArgs = writeReportStub.mock.calls[0][0]; + expect(reportArgs.paymentProvider).toBe('PayPal'); expect(reportArgs.amountRefunded).toBe(500); expect(reportArgs.isOwed).toBe(false); expect(reportArgs.error).toBe(false); @@ -530,6 +533,7 @@ describe('CustomerPlanMover v2', () => { it('marks customer as owed', () => { const reportArgs = writeReportStub.mock.calls[0][0]; + expect(reportArgs.paymentProvider).toBe('Stripe'); expect(reportArgs.isOwed).toBe(true); expect(reportArgs.amountRefunded).toBe(null); expect(reportArgs.error).toBe(false); @@ -581,6 +585,7 @@ describe('CustomerPlanMover v2', () => { it('writes report marking as excluded', () => { const reportArgs = writeReportStub.mock.calls[0][0]; + expect(reportArgs.paymentProvider).toBe('Stripe'); expect(reportArgs.isExcluded).toBe(true); expect(reportArgs.error).toBe(false); expect(reportArgs.amountRefunded).toBe(null); @@ -724,6 +729,7 @@ describe('CustomerPlanMover v2', () => { expect(writeReportStub).toHaveBeenCalledTimes(1); const reportArgs = writeReportStub.mock.calls[0][0]; + expect(reportArgs.paymentProvider).toBe('Stripe'); expect(reportArgs.error).toBe(true); expect(reportArgs.customer).toBe(null); expect(reportArgs.isOwed).toBe(false); diff --git a/packages/fxa-auth-server/scripts/move-customers-to-new-plan-v2/move-customers-to-new-plan-v2.ts b/packages/fxa-auth-server/scripts/move-customers-to-new-plan-v2/move-customers-to-new-plan-v2.ts index b3e473396b0..4230e7b1973 100644 --- a/packages/fxa-auth-server/scripts/move-customers-to-new-plan-v2/move-customers-to-new-plan-v2.ts +++ b/packages/fxa-auth-server/scripts/move-customers-to-new-plan-v2/move-customers-to-new-plan-v2.ts @@ -77,6 +77,9 @@ export class CustomerPlanMover { } async convertSubscription(subscription: Stripe.Subscription, destinationPrice: Stripe.Price) { + const paymentProvider = + subscription.collection_method === 'send_invoice' ? 'PayPal' : 'Stripe'; + try { console.log(`Processing ${subscription.id}`); @@ -101,7 +104,7 @@ export class CustomerPlanMover { const isExcluded = this.isCustomerExcluded(customer.subscriptions.data); const destinationPriceCurrencyOptionForCurrency = destinationPrice.currency_options?.[subscription.currency]; - const destinationPriceUnitAmountForCurrency = + const destinationPriceUnitAmountForCurrency = destinationPriceCurrencyOptionForCurrency?.unit_amount ?? (typeof destinationPriceCurrencyOptionForCurrency?.unit_amount_decimal === "string" ? Math.round(parseFloat(destinationPriceCurrencyOptionForCurrency.unit_amount_decimal)) @@ -166,6 +169,7 @@ export class CustomerPlanMover { await this.writeReport({ subscription, customer, + paymentProvider, isExcluded, amountRefunded, approximateAmountWasOwed, @@ -182,6 +186,7 @@ export class CustomerPlanMover { await this.writeReport({ subscription, customer: null, + paymentProvider, isExcluded: false, amountRefunded: null, approximateAmountWasOwed: null, @@ -334,7 +339,8 @@ export class CustomerPlanMover { "daysSinceLastBill", "previousInvoiceAmountDue", "isOwed", - "error" + "error", + "paymentProvider" ]; const reportCSV = data.join(',') + '\n'; @@ -348,6 +354,7 @@ export class CustomerPlanMover { async writeReport(args: { subscription: Stripe.Subscription, customer: Stripe.Customer | null, + paymentProvider: 'Stripe' | 'PayPal', isExcluded: boolean, amountRefunded: number | null, approximateAmountWasOwed: number | null, @@ -371,7 +378,8 @@ export class CustomerPlanMover { args.daysSinceLastBill ?? "null", args.previousInvoiceAmountDue ?? "null", args.isOwed, - args.error + args.error, + args.paymentProvider ]; const reportCSV = data.join(',') + '\n';