in packages/fxa-shared/payments/stripe.ts [535:644]
async expandResource<T>(
resource: string | T,
resourceType: (typeof VALID_RESOURCE_TYPES)[number],
statusFilter?: Stripe.Subscription.Status[]
): Promise<T> {
if (typeof resource !== 'string') {
return resource;
}
if (!VALID_RESOURCE_TYPES.includes(resourceType)) {
const errorMsg = `stripeHelper.expandResource was provided an invalid resource type: ${resourceType}`;
const error = new Error(errorMsg);
this.log.error(`stripeHelper.expandResource.failed`, { error });
throw error;
}
switch (resourceType) {
case CUSTOMER_RESOURCE:
const customer = await this.stripeFirestore.retrieveAndFetchCustomer(
resource,
true
);
if (customer?.deleted) {
// There are no subscriptions for deleted customers on the customer object.
// @ts-ignore
return customer;
}
const subscriptions =
await this.stripeFirestore.retrieveCustomerSubscriptions(
resource,
statusFilter
);
(customer as any).subscriptions = {
data: subscriptions as any,
has_more: false,
};
// @ts-ignore
return customer;
case SUBSCRIPTIONS_RESOURCE:
// @ts-ignore
return this.stripeFirestore.retrieveAndFetchSubscription(
resource,
true
);
case INVOICES_RESOURCE:
try {
// TODO we could remove the getInvoiceWithDiscount method if we add logic
// here to check if the discounts field is expanded but it would mean
// adding another stipe call to get discounts even when unnecessary
const invoice = await this.stripeFirestore.retrieveInvoice(resource);
// @ts-ignore
return invoice;
} catch (err) {
if (err.name === FirestoreStripeError.FIRESTORE_INVOICE_NOT_FOUND) {
const invoice = await this.stripe.invoices.retrieve(resource, {
expand: ['discounts'],
});
await this.stripeFirestore.retrieveAndFetchCustomer(
invoice.customer as string,
true
);
await this.stripeFirestore.insertInvoiceRecord(invoice, true);
// @ts-ignore
return invoice;
}
throw err;
}
case PAYMENT_METHOD_RESOURCE:
try {
const paymentMethod =
await this.stripeFirestore.retrievePaymentMethod(resource);
// @ts-ignore
return paymentMethod;
} catch (err) {
if (
err.name === FirestoreStripeError.FIRESTORE_PAYMENT_METHOD_NOT_FOUND
) {
const paymentMethod = await this.stripe.paymentMethods.retrieve(
resource
);
// Payment methods may not be attached to customers, in which case we
// cannot store it in Firestore.
if (paymentMethod.customer) {
await this.stripeFirestore.retrieveAndFetchCustomer(
paymentMethod.customer as string,
true
);
await this.stripeFirestore.insertPaymentMethodRecord(
paymentMethod,
true
);
}
// @ts-ignore
return paymentMethod;
}
throw err;
}
case PRODUCT_RESOURCE:
const products = await this.allProducts();
// @ts-ignore
return products.find((p) => p.id === resource);
case PLAN_RESOURCE:
const plans = await this.allPlans();
// @ts-ignore
return plans.find((p) => p.id === resource);
default:
// @ts-ignore
return this.stripe[resourceType].retrieve(resource);
}
}