async expandResource()

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);
    }
  }