Vexor

Portals

Portals are the simplest way to let your users manage their billing and subscriptions. In this guide you'll learn how to create a basic portal for the most common use case. You can then extend it to more complex scenarios.

Since portals are used to manage billing and subscriptions, you need to have an app that handles billing and subscription system in place. If you don't have one, you can follow the subscriptions guide guide to set up a basic subscription system. Also we recommend to follow the webhooks guide guide to set up the webhooks to handle the events triggered by the payment provider.

In this example, we will use the same project we created in the subscriptions guide to which we added the webhooks in the webhooks guide.

The application

  1. Clone the webhooks branch of the subscriptions repo:
git clone -b webhooks https://github.com/Vectrals-Software/vexor-subscriptions-nextjs.git

This branch contains the implementation of subscriptions and webhooks, without portals so we can add the portals implementation in the next steps.

  1. Once you have cloned the repo, open it on your code editor and install the dependencies:
npm install

Environment variables

Add the necessary environment variables to your project:

NEXT_PUBLIC_VEXOR_PROJECT="YOUR_PROJECT_ID"
NEXT_PUBLIC_VEXOR_PUBLISHABLE_KEY="YOUR_PUBLIC_KEY"
VEXOR_SECRET_KEY="YOUR_SECRET_KEY" 

Let's build a simple component for the billing portal

Create a file named portal-card.tsx inside a src/components folder and add the following code:

// src/components/portal-card.tsx
 
"use client"; // Mark it as a client component
 
import { vexor } from "@/lib/vexor";
import { useTransition } from "react";
import { VexorPortalResponse } from "vexor";
 
const PortalCard: React.FC = () => {
 
    const [isPending, startTransition] = useTransition();
 
    const handleSubscribe = async () => {
        startTransition(async () => {
            try {
                // You can also use directly vexor.portal.stripe if you want. This time we're going to use the generic vexor.portal method and pass the platform in the parameters.
                const response : VexorPortalResponse = await vexor.portal({
                    platform: 'stripe',
                    identifier: 'abc-123-def-456',
                    returnUrl: 'http://localhost:3000'
                })
 
                window.location.href = response.portal_url
 
            } catch (error) {
                console.log(error);
            }
        });
    }
 
    return (
        <div className="w-[500px] max-w-full border rounded-lg p-6">
            <div className="mb-6 gap-3 flex flex-col justify-center items-center">
                <h2 className="text-2xl font-semibold">Subscriptions & Billing Management</h2>
            </div>
            <div className="space-y-4">
                <button
                    onClick={handleSubscribe}
                    disabled={isPending}
                    className="w-full bg-foreground text-background py-2 px-4 rounded-md disabled:opacity-50 disabled:cursor-not-allowed">
                    {isPending ? 'Loading...' : 'Manage'}
                </button>
            </div>
        </div>
    );
};
 
export default PortalCard;

Import the component in the page

Go to the main page.tsx file and import the PortalCard component:

// src/app/page.tsx
 
import PortalCard from "@/components/portal-card";

Then add the PortalCard component to the page, you can do it anywhere in the page. In this example we will add it below the SubscriptionCard component.

Your page.tsx file should look like this:

// src/app/page.tsx
 
import PortalCard from "@/components/portal-card";
import SubscriptionCard from "@/components/subscription-card";
import { VexorSubscriptionBody } from "vexor";
 
const subscriptionPlan: VexorSubscriptionBody = {
  name: 'Awesome Subscription Plan',
  description: 'Cool description for the plan that customers will see 😎',
  interval: 'month',
  price: 99,
  currency: 'USD',
  successRedirect: 'http://localhost:3000/success',
  customer: {
      email: 'test_customer@email.com',
      name: 'Test Customer'
  }
}
 
export default function Home() {
  return (
    <div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]">
      <main className="flex flex-col gap-8 row-start-2 items-center w-full max-w-4xl">
        <h1 className="text-3xl font-bold mb-8">Subscribe to our plan</h1>
          <SubscriptionCard subscription={subscriptionPlan} />
          <h1 className="text-3xl font-bold mb-8 mt-24">Manage your subscription</h1>
          <PortalCard/>
      </main>
    </div>
  );
}

If you run the project now with npm run dev, you will see the PortalCard component with a button to manage the subscription. You should see something like this:

portal card ui

Configure the webhooks

We will need the identifier to create the portal. To get the identifier we need to receive the webhook event when the subscription is created.

Please refer to the webhooks guide to learn how to set up the webhooks. You can also check the vexor.webhook reference to learn more about the webhook object.

Make sure to have webhooks configured, and... that's it!. You can now let your users manage their billing and subscriptions through the portal.

Test the portal

For testing the portal we will need to follow these three steps:

  1. Create a subscription

  2. Receive the webhook event with the identifier

  3. Pass the identifier to the portal method

We will show you how to do this step by step in the video below.

In a real world scenario, when you create a subscription and receive the webhook event with the identifier, you should save the identifier in your database. In this example we will hardcode the identifier for simplicity purposes.

πŸ‘‰πŸ» If you're using Stripe, the first time you try to create a portal you may get an error response from Vexor with the following message in your console:

Error: Portal request failed: You can’t create a portal session in test mode until you save your customer portal settings in test mode at "https://dashboard.stripe.com/test/settings/billing/portal". β€” 

The error message is self-explanatory. You just need to go to the Stripe dashboard and set up the customer portal settings in test mode in the Billing Portal Settings. If you don't want to customize the portal, you can use the default one, just click on the "Activate" button and thats it. You can try again after setting up the customer portal settings and the error should be gone.

πŸ‘€ Example videos of the complete process may be outdated and you may find slight differences in the syntax, don't worry, the logic is the same... but now it's better πŸš€ We'll update the videos soon.

On this page