How to access Stripe webhooks when using Vercel + Supabase (React JS)

3 min read 04-10-2024
How to access Stripe webhooks when using Vercel + Supabase (React JS)


Accessing Stripe Webhooks with Vercel, Supabase, and React.js

Integrating Stripe webhooks into your React.js application powered by Vercel and Supabase can be a tricky dance. This article breaks down the process, offering a clear path to successfully capturing and handling these critical events.

The Challenge

Stripe webhooks are powerful tools, enabling real-time updates on events within your Stripe account. However, receiving and processing these webhooks securely and efficiently within a serverless environment like Vercel, while storing data in Supabase, can be daunting.

Scenario

Imagine you have a React.js application that processes online payments through Stripe. You want to be notified when a customer successfully completes a payment. To achieve this, you need to configure Stripe webhooks to send data to your application when a checkout.session.completed event occurs.

Original Code (Partial)

// Stripe webhook handler (serverless function)
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);

exports.handler = async (event, context) => {
  const signature = event.headers['stripe-signature'];
  let event;
  try {
    event = stripe.webhooks.constructEvent(event.body, signature, process.env.STRIPE_WEBHOOK_SECRET);
  } catch (err) {
    console.error(err);
    return {
      statusCode: 400,
      body: JSON.stringify({ error: 'Webhook signature verification failed.' }),
    };
  }

  // Process event data based on type
  if (event.type === 'checkout.session.completed') {
    // Store data in Supabase 
  }
}

Breaking it Down

  1. Vercel Serverless Function: The code snippet defines a serverless function deployed on Vercel. This function acts as the endpoint for Stripe webhooks.
  2. Signature Verification: The stripe.webhooks.constructEvent function validates the incoming webhook data using the provided stripe-signature header and your Stripe webhook secret. This ensures the data originates from Stripe and hasn't been tampered with.
  3. Data Processing: Once verified, the webhook data is processed based on the event.type.
  4. Supabase Integration: The code snippet suggests storing the processed data in Supabase.

Key Considerations

  • Security: Verifying the webhook signature is crucial. Never trust the data directly without validation.
  • Scalability: Vercel provides a serverless environment, making it easy to scale your webhook handling infrastructure as your user base grows.
  • Data Persistence: Supabase offers a robust and flexible PostgreSQL database, allowing you to store and query webhook data efficiently.
  • Real-Time Updates: You can utilize Supabase's real-time capabilities to trigger updates in your React.js application based on changes in the webhook data.

Example Implementation

Here's a refined example showcasing the integration with Vercel, Supabase, and React.js:

1. Vercel Serverless Function

// serverless.js
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
const { createClient } = require('@supabase/supabase-js');

const supabaseUrl = process.env.SUPABASE_URL;
const supabaseKey = process.env.SUPABASE_KEY;

const supabase = createClient(supabaseUrl, supabaseKey);

exports.handler = async (event, context) => {
  const signature = event.headers['stripe-signature'];
  let event;
  try {
    event = stripe.webhooks.constructEvent(event.body, signature, process.env.STRIPE_WEBHOOK_SECRET);
  } catch (err) {
    console.error(err);
    return {
      statusCode: 400,
      body: JSON.stringify({ error: 'Webhook signature verification failed.' }),
    };
  }

  if (event.type === 'checkout.session.completed') {
    const { data: { id: sessionId } } = event.data.object;
    try {
      const { error } = await supabase
        .from('payment_events')
        .insert({ session_id: sessionId, event_type: 'checkout.session.completed' });
      if (error) {
        console.error('Error storing data in Supabase:', error);
      }
    } catch (error) {
      console.error(error);
    }
  }
  return {
    statusCode: 200,
    body: JSON.stringify({ received: true }),
  };
};

2. React.js Component

import React, { useEffect, useState } from 'react';
import { createClient } from '@supabase/supabase-js';

const supabaseUrl = process.env.REACT_APP_SUPABASE_URL;
const supabaseKey = process.env.REACT_APP_SUPABASE_KEY;

const supabase = createClient(supabaseUrl, supabaseKey);

function PaymentEvents() {
  const [paymentEvents, setPaymentEvents] = useState([]);

  useEffect(() => {
    const subscription = supabase
      .from('payment_events')
      .on('*', (payload) => {
        setPaymentEvents((prevEvents) => [...prevEvents, payload.new]);
      })
      .subscribe();

    return () => subscription.unsubscribe();
  }, []);

  return (
    <div>
      <h1>Payment Events</h1>
      <ul>
        {paymentEvents.map((event) => (
          <li key={event.id}>
            Session ID: {event.session_id}, Event Type: {event.event_type}
          </li>
        ))}
      </ul>
    </div>
  );
}

export default PaymentEvents;

3. Setting up Stripe Webhooks

  • In your Stripe dashboard, navigate to Developers > Webhooks.
  • Create a new webhook endpoint with the URL of your Vercel function.
  • Select the checkout.session.completed event, ensuring the signature is correctly configured.

Conclusion

This article provides a comprehensive guide to efficiently integrating Stripe webhooks into your Vercel and Supabase powered React.js application. By following these steps, you can receive real-time payment updates, process data securely, and utilize a powerful serverless architecture for robust and scalable webhook handling.