Allow Customer to Manage Their Subscription with Stripe Customer Portal

Jon Meyers
InstructorJon Meyers

Share this video with your friends

Send Tweet
Published a month ago
Updated 9 hours ago

We can use Stripe's Customer Portal to allow our users to manage their subscription and payment information. We will configure the Customer Portal in the Stripe Dashboard and create the required pages for /terms and /privacy.

Additionally, we create an API route to initiate our customer portal session. Similarly to our API route that charges for a subscription, we need to get the token from the cookie, add it to the Supabase session and fetch the associated profile data.

Since the updated event gets triggered for creating a new subscription, as well as updating an existing one, and we want to perform the same changes in our Supabase DB, we can replace our case statement to handle customer.subscription.updated.

Lastly, we implement a case for deleted, setting is_subscribed to false and interval to null. Since this event doesn't actually trigger until the end of the customer's billing cycle, we can manually cancel the user's subscription from the Stripe dashboard.

Instructor: [0:00] We want our users to be able to manage their own subscription from their dashboard. Let's set up a customer portal in Stripe. Let's go to Settings and then under Billing, Customer Portal. If we scroll down, we can choose what we want our customers to be able to do in their portal.

[0:14] I want customers to be able to see their invoice history and update their billing information. I also want them to be able to update their payment methods, cancel subscriptions. I want that to take place at the end of their billing cycle and I want customers to be able to switch between different pricing plans.

[0:29] I now need to choose some products that they'll be able to choose between. I'm going to choose my basic product and my pro product. We then need a link to our terms of service page and our privacy policy.

[0:40] Let's set this to http://localhost over port 3000/terms and then /privacy. We want our default redirect link to be our dashboard. Let's save these settings and confirm our password, and now we need to create those pages for terms and privacy.

[0:56] If you're going to be using this application in production, then I recommend using something like Termly to generate your terms and conditions and privacy policy. In this example, I'm just going to use the text terms and the same for our privacy policy.

[1:15] We want to add a Manage Subscription button to redirect our user to the Stripe customer portal. We also want this button to only appear once we finish loading, which means we need to wrap these two adjacent siblings in a fragment.

[1:32] We probably want to push that button down a little bit. When we click that button, we want to load Stripe's portal. We're going to want to make a request to an API route to initiate this session so we need to import Axios and use it to make a get request to /API/portal.

[1:57] This data will contain a URL that we want to redirect the user to. Let's bring in the useRouter hook from next/router and get our router instance in our component. We can redirect to that URL by calling router.push and giving it data.URL.

[2:13] Let's create this API route, which similarly to our subscription logic, we'll need to import these libraries. Get the Supabase user from our cookie. Send a failure response if we don't have a user. Pass the token out of our cookie.

[2:29] Set it to our Supabase session so we can make a request for the associated Stripe customer and then initialize a Stripe client. Let's copy this across. We're only two levels deep rather than three.

[2:45] Now we can create a new customer portal session by calling stripe.billingportal.sessions.create and passing it an object where our customer is set to our Stripe customer. Our return URL is set to http://localhost over port 3000/dashboard.

[2:58] We can then send back our response, which is an object with the key URL set to our session URL. Remember we're then taking that URL and redirecting our user to this Stripe customer portal. Let's click Manage Subscription. We can then update our plan.

[3:18] We could go to the Yearly Pro Plan and click Continue. It's going to tell us the difference that we'll need to pay for this new subscription. Now our user is on that yearly plan. However, if we go back to our dashboard, it still says that we're on the monthly plan.

[3:33] This is because we're not currently listening to webhooks about changes to a customer subscription. We're only listening for when a new subscription is created. When a customer creates or updates their subscription, we want to perform the same logic.

[3:46] We want to set the isSubscribe column to true, and update the interval. Since the updated event gets sent both when the customer creates a subscription and when they update it, we can just listen to that single event for both cases.

[3:58] Now if we manage our subscription again and set our plan back to the basic plan and then back to yearly again, we see that data reflected correctly in our dashboard. The other case we need to handle is when a user wants to cancel their plan.

[4:14] Let's add a break for the end of this case. Then let's add a case for customer.subscription.deleted. In this case, we also want to update the users profile table, but this time updating is subscribed to false, and setting our interval to null. We can add a break for the end of that case.

[4:31] If our user cancels their plan, we'll see that that's been cancelled in Stripe. Now when we head back to our dashboard, it still says we're subscribed to our yearly plan. That's because this event doesn't trigger immediately when the customer cancels their subscription.

[4:46] It triggers at the end of their current billing cycle when their subscription actually expires. We can trigger this event manually by heading over to the Stripe dashboard, finding our Pro subscriber that's already canceled, and cancelling their subscription from here.

[5:00] We can say we want this to happen immediately and click cancel subscription. If we go back to our dashboard and refresh, we'll see that our user is no longer subscribed.