Skip to content

Commit 9354bf7

Browse files
authored
Merge pull request #19430 from mozilla/PAY-3183
fix(stripe-webhook): transactionalize payment method and invoice cache
2 parents 3e3129f + 8bfe26c commit 9354bf7

5 files changed

Lines changed: 610 additions & 116 deletions

File tree

packages/fxa-auth-server/lib/payments/stripe-firestore.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ export class StripeFirestore extends StripeFirestoreBase {
106106
customer: Partial<Stripe.Customer | Stripe.DeletedCustomer>
107107
) {
108108
try {
109-
await this.retrieveCustomer({ uid });
109+
return await this.retrieveCustomer({ uid });
110110
} catch (err) {
111111
if (err.name === FirestoreStripeError.FIRESTORE_CUSTOMER_NOT_FOUND) {
112112
if (!customer.id) throw new Error('Customer ID must be provided');
@@ -115,10 +115,11 @@ export class StripeFirestore extends StripeFirestoreBase {
115115
throw err;
116116
}
117117
}
118-
return this.insertCustomerRecord(uid, customer);
119118
}
120119

121120
/**
121+
* @deprecated This method does not support transactions
122+
*
122123
* Insert a subscription record into Firestore under the customer's stripe id.
123124
*/
124125
async insertSubscriptionRecord(subscription: Partial<Stripe.Subscription>) {
@@ -141,6 +142,8 @@ export class StripeFirestore extends StripeFirestoreBase {
141142
}
142143

143144
/**
145+
* @deprecated This method does not support transactions
146+
*
144147
* Insert a subscription record into Firestore under the customer's stripe id.
145148
* If the customer does not exist, this will backfill the customer with all their
146149
* subscriptions.
@@ -160,6 +163,8 @@ export class StripeFirestore extends StripeFirestoreBase {
160163
}
161164

162165
/**
166+
* @deprecated This method does not support transactions
167+
*
163168
* Insert a payment method record into Firestore under the customer's stripe id.
164169
* If the customer does not exist, this will backfill the customer with all their
165170
* subscriptions.

packages/fxa-auth-server/lib/payments/stripe.ts

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -466,13 +466,13 @@ export class StripeHelper extends StripeHelperBase {
466466
customer.metadata.userid,
467467
customer
468468
);
469-
await this.stripeFirestore.insertPaymentMethodRecord(paymentMethod);
469+
await this.stripeFirestore.fetchAndInsertPaymentMethod(paymentMethod.id);
470470
// Try paying now instead of waiting for Stripe since this could block a
471471
// customer from finishing a payment
472472
const invoice = await this.stripe.invoices.pay(invoiceId, {
473473
expand: ['payment_intent'],
474474
});
475-
await this.stripeFirestore.insertInvoiceRecord(invoice);
475+
await this.stripeFirestore.fetchAndInsertInvoice(invoice.id);
476476
return invoice;
477477
} catch (err) {
478478
if (err.type === 'StripeCardError') {
@@ -3518,19 +3518,20 @@ export class StripeHelper extends StripeHelperBase {
35183518
* Process a invoice event that needs to be saved to Firestore.
35193519
*/
35203520
async processInvoiceEventToFirestore(event: Stripe.Event) {
3521-
const invoiceId = (event.data.object as Stripe.Invoice).id;
3521+
const eventData = event.data.object as Stripe.Invoice;
3522+
const invoiceId = eventData.id;
35223523
if (!invoiceId) throw new Error('Invoice ID must be specified');
3523-
3524-
const invoice = await this.stripe.invoices.retrieve(invoiceId);
3524+
const customerId = eventData.customer;
3525+
if (typeof customerId !== "string") throw new Error('Customer ID must be a string');
35253526

35263527
try {
3527-
await this.stripeFirestore.insertInvoiceRecord(invoice);
3528+
await this.stripeFirestore.fetchAndInsertInvoice(invoiceId);
35283529
} catch (err) {
35293530
if (err.name === FirestoreStripeError.FIRESTORE_CUSTOMER_NOT_FOUND) {
3530-
await this.stripeFirestore.retrieveAndFetchSubscription(
3531-
invoice.subscription as string
3531+
await this.stripeFirestore.fetchAndInsertCustomer(
3532+
customerId,
35323533
);
3533-
return this.stripeFirestore.insertInvoiceRecord(invoice);
3534+
return this.stripeFirestore.fetchAndInsertInvoice(invoiceId);
35343535
}
35353536
throw err;
35363537
}
@@ -3550,20 +3551,11 @@ export class StripeHelper extends StripeHelperBase {
35503551
return;
35513552
}
35523553

3553-
const paymentMethod = await this.stripe.paymentMethods.retrieve(
3554-
(event.data.object as Stripe.PaymentMethod).id
3555-
);
3556-
3557-
// If this payment method is not attached, we can't store it in firestore as
3558-
// the customer may not exist. It is possible that a payment_method.detached
3559-
// event has already been processed, detaching the payment method.
3560-
if (!paymentMethod.customer) {
3561-
return;
3562-
}
3554+
const paymentMethodId = (event.data.object as Stripe.PaymentMethod).id;
35633555

35643556
try {
3565-
await this.stripeFirestore.insertPaymentMethodRecordWithBackfill(
3566-
paymentMethod
3557+
await this.stripeFirestore.fetchAndInsertPaymentMethod(
3558+
paymentMethodId
35673559
);
35683560
} catch (err) {
35693561
if (

0 commit comments

Comments
 (0)