Peter Bonnebaigt
Peter Bonnebaigt

Reputation: 157

React integration with Stripe customer portal

I am trying to get an integration with the stripe customer portal working in my react app so that when the user clicks a button it opens the customer portal and they are able to manage their subscriptions.

Currently I have the following set up to achieve this:

const stripe = require('stripe')("pk_live_xxxxxxxxxxxxxxxxxxxxx");



  const [session, setSession] = useState();
  useEffect(() => {
    console.log('loading in stripe')
    const doIt = async () => {
      const session = await stripe.billingPortal.sessions.create({
        customer: stripeId,
    
      });
      console.log("session is ", session)
      setSession(session);
    };
    doIt();
  }, []);

And the button to open the portal is as follows:

return (

  <>
      {session && session.url &&
      <form method="POST" action={session.url}>
        <button type="submit" className="Button">
          Membership
        </button>
      </form>}

  </>

);

The issue I am having is that the button doesn't show on the page so I am unable to click it to go to the customer portal.

Upvotes: 1

Views: 795

Answers (2)

Parth Shinde
Parth Shinde

Reputation: 1

@Peter Bonnebaigt

Mistake: You are using a client-side publishable key (pk_live_...) to call stripe.billingPortal.sessions.create(), which requires a server-side secret key (sk_live_...). This is both invalid (publishable keys cannot perform API actions) and unsafe (secret keys must never be exposed in client-side code).

Solution:

Step 1: Create a Backend Endpoint Use a server (e.g., Node.js/Express) to securely create the Stripe Billing Portal session:

// Server (e.g., Node.js/Express)
const express = require('express');
const stripe = require('stripe')('sk_live_xxxxxxxxxxxxx'); // Use your secret key

const app = express();
app.use(express.json());

app.post('/create-portal-session', async (req, res) => {
  try {
    const session = await stripe.billingPortal.sessions.create({
      customer: req.body.customerId, // Pass customer ID from client
      return_url: 'https://your-website.com/dashboard' // Optional
    });
    res.json({ url: session.url });
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

Step 2: Update Your React Component Fetch the portal URL from your backend instead of using Stripe directly in the client:

    // React Component
import React, { useState, useEffect } from 'react';

const PortalButton = ({ stripeId }) => {
  const [portalUrl, setPortalUrl] = useState(null);

  useEffect(() => {
    const fetchPortalUrl = async () => {
      try {
        const response = await fetch('/create-portal-session', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ customerId: stripeId }), // Send Stripe customer ID
        });
        const data = await response.json();
        setPortalUrl(data.url);
      } catch (err) {
        console.error('Failed to create portal session:', err);
      }
    };

    if (stripeId) fetchPortalUrl();
  }, [stripeId]);

  return (
    portalUrl && (
      <a href={portalUrl} className="Button">
        Manage Membership
      </a>
    )
  );
};

Upvotes: 0

hanzo
hanzo

Reputation: 844

It could be due to your conditional failing. Can you log your session object and see if it indeed has the values that you're looking for?

Upvotes: 0

Related Questions