Search code examples
firebasestripe-paymentsfirebase-extensions

Run Function when a Customer is charged using Stripe's Subscription Firebase Extension


I want to run a Firebase cloud function everytime a user is charged by Stripe's subscription extension. Is there any event that is generated by that extension that can trigger a cloud function, such as a write to Firestore?

My scenario is that I want to generate a new document in an Orders collection whenever a user is successfully charged that references the corresponding User document.


Solution

  • I was able to solve this myself by borrowing some code from the official repo for Stripe's Firebase extension here: https://github.com/stripe/stripe-firebase-extensions/blob/next/firestore-stripe-subscriptions/functions/src/index.ts

    I made my own Firebase function and called it handleInvoiceWebhook that validated and constructed a Stripe Invoice object, mapped it to a custom Invoice interface I made with the fields I cared about, then saved that to an invoices collection.

    export const handleInvoiceWebhook = functions.https.onRequest(
      async (req: functions.https.Request, resp) => {
        let event: Stripe.Event;
        try {
          event = stripe.webhooks.constructEvent(
            req.rawBody,
            req.headers['stripe-signature'] || '',
            functions.config().stripe.secret
          );
        } catch (error) {
          resp.status(401).send('Webhook Error: Invalid Secret');
          return;
        }
        const invoice = event.data.object as Stripe.Invoice;
        const customerId= invoice.customer as string;
    
        await insertInvoiceRecord(invoice, customerId);
        resp.status(200).send(invoice.id);
      }
    );
    
    /**
     * Create an Invoice record in Firestore when a customer's monthly subscription payment succeeds.
     */
    const insertInvoiceRecord = async (
      invoice: Stripe.Invoice,
      customerId: string
    ): Promise<void> => {
      // Invoice is an interface with only fields I care about
      const invoiceData: Invoice = {
        invoiceId: invoice.id,
        ...map invoice data here
      };
      await admin
        .firestore()
        .collection('invoices')
        .doc(invoice.id)
        .set(invoiceData);
    };
    

    Once deployed, I went to the Stripe developer dashboard (https://dashboard.stripe.com/test/webhooks) and added a new Webhook listening for the Event type invoice.payment_succeeded and with the url being the Firebase function I just made.

    NOTE: I had to deploy my Stripe API key and Webhook Secret as enviroment variables for my Firebase function with the following command: firebase functions:config:set stripe.key="sk_test_123" stripe.secret="whsec_456"