Vexor

Subscriptions

Subscriptions allow you to set up recurring payments for your customers. This guide will walk you through creating a basic subscription system using Vexor.

Setup

Create a project

Login to your dashboard, go to the Projects section and click on Create Project:

Make sure to select the appropiate environment for your project. If you select Production you should use the production credentials for the payment provider. Using sandbox credentials in production will cause errors. Same as the other way around, sandbox provider credentials should only be used in a project with the environment set to Sandbox.

Add credentials

Go to the project settings and choose the providers you want to use. For now, we don't need the webhook_secret, we'll show you how to use it later. For this demo, we'll use Stripe.

Where to get the credentials:

We assume you already have an account created within Stripe and that you understand how to setup and differentiate between sandbox and production configurations. If not, you can read more here.

  1. Go to your Stripe dashboard and select the account you want to use.
  2. Go to Developers > API Keys and click on API Keys.
  3. Copy the Publishable Key and Secret Key and paste them in the corresponding fields in the Vexor dashboard in the Project details as shown before.

As you can see in the video, we have selected Sandbox as the environment (a sandbox account). So our Vexor project will use the sandbox credentials for Stripe. If you select Production you should use the production credentials for Stripe.



Let's code

Create the app

In this example we will implement Vexor subscription in a Next.js bare project but the same logic can be applied to any other framework.

Run:

npx create-next-app@latest

to create a new Next.js project.

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" 

Install the SDK and instantiate it

Run:

npm install vexor

to install the SDK.

Create a file, typically named vexor.ts instantiate it as follows:

import { Vexor } from "vexor";
 
export const vexor = Vexor.fromEnv();

As a convention, it is recommended to place this file inside a lib folder, located inside the src folder as shown below.

Let's build a simple component for the subscription plan

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

// src/components/subscription-card.tsx
 
"use client"; // Mark it as a client component
 
import { VexorSubscriptionBody } from "vexor";
 
const SubscriptionCard: React.FC<{ subscription: VexorSubscriptionBody }> = ({ subscription }) => {
 
    const handleSubscribe = async () => {
        console.log(subscription); // We will complete this function in the next steps
    };
 
    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">{subscription.name}</h2>
                <span className="text-7xl font-black">${subscription.price}</span>
                <p className="text-gray-500 mt-1">{subscription.description}</p>
            </div>
            <div className="space-y-4">
                <button
                    onClick={handleSubscribe}
                    className="w-full bg-foreground text-background py-2 px-4 rounded-md disabled:opacity-50 disabled:cursor-not-allowed"
                >
                    "Subscribe"
                </button>
            </div>
        </div>
    );
};
 
export default SubscriptionCard;

Import the component in the page and define a subscription plan

Ideally this component will be used in a page that lists all the subscription plans. For demonstration purposes, we will just define a subscription plan in the main page.tsx file.

Remove the default boilerplate code from the page.tsx file and replace it with the following code:

// src/app/page.tsx
 
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} />
      </main>
    </div>
  );
}

Run your app locally, you should see something like this:

subscriptions plan ui

Great! ✨ Now let's complete the subscription logic...

Use the Vexor SDK to create the actual subscription

In our subscription-card.tsx file, we have a handleSubscribe function that is currently empty:

const handleSubscribe = async () => {
    console.log(subscription); // This is where we will use the Vexor SDK in this step
};

Import vexor, VexorSubscriptionResponse and useTransition hook

The useTransition hook from react is a good approach to handle the logic and loading state of the purchase.

// src/components/subscription-card.tsx
 
"use client"; // Mark it as a client component
 
import { vexor } from "@/lib/vexor";
import { useTransition } from "react";
import { VexorSubscriptionBody, VexorSubscriptionResponse } from "vexor";
// ... rest of the file

Complete the handleSubscribe function

Let's complete the handleSubscribe function to create the subscription and redirect the user to the payment page. As you can see, we need to use isPending and startTransition from the useTransition hook to handle the loading state and the subscription logic.

     const [isPending, startTransition] = useTransition();
 
    const handleSubscribe = async () => {
        startTransition(async () => {
            try {
                const response : VexorSubscriptionResponse = await vexor.subscribe.stripe(subscription)
 
                window.location.href = response.payment_url;
            } catch (error) {
                console.log(error);
            }
        });
    }

And also let's make the button disabled when the purchase is pending and show a loading message:

  <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 ? 'Subscribing...' : 'Subscribe'}
  </button>

The vexor.subscribe.stripe function is used to create the subscription. You can use any other provider by calling the appropriate function, for example vexor.subscribe.mercadopago or vexor.subscribe.paypal. Or the generic vexor.subscribe method. You can read more about the subscribe methods here. We use stripe as the payment provider in this guide, but you can use any other provider. Just make sure to complete the provider credentials in your Vexor project.

That's it! Your subscription-card.tsx file should now look like this:

// src/components/subscription-card.tsx
 
"use client"; // Mark it as a client component
 
import { vexor } from "@/lib/vexor";
import { useTransition } from "react";
import { VexorSubscriptionBody, VexorSubscriptionResponse } from "vexor";
 
const SubscriptionCard: React.FC<{ subscription: VexorSubscriptionBody }> = ({ subscription }) => {
    const [isPending, startTransition] = useTransition();
 
    const handleSubscribe = async () => {
        startTransition(async () => {
            try {
                const response : VexorSubscriptionResponse = await vexor.subscribe.stripe(subscription)
 
                window.location.href = response.payment_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">{subscription.name}</h2>
                <span className="text-7xl font-black">${subscription.price}</span>
                <p className="text-gray-500 mt-1">{subscription.description}</p>
            </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 ? 'Subscribing...' : 'Subscribe'}
                </button>
            </div>
        </div>
    )
}
 
export default SubscriptionCard;

Test the subscription

Run your app locally and test subscribing to a plan by clicking the "Subscribe" button. You should be redirected to the payment page for the subscription.

Congratulations! 🎉

You have now a basic subscription working. You can extend this logic to more complex scenarios.

Is this all we need? Yes and no. We have a working subscription, the user can now subscribe to a plan and we get the recurring payments after the subscription, but we don't have a way to know if the subscription was successful or not. There are two more things that we recommend you to do:

  1. Create the success and failure pages. Define the redirect urls in the subscription body and build the pages you want to redirect to. You can read more about redirect urls here.

  2. Add a webhook (Yes, this is important 🫡) to your project to know when the subscription is successful or not. Also this webhook will be triggered when the subscription is created, updated, renewed or cancelled. You can read more about webhooks here.

Here you have a video showing the complete process we have done in the last steps of this guide to build the UI of the checkout:

👀 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.

NOTE: Check the docs of the provider you are using to know more about test cards. In our example we are using Stripe, you can read more about Stripe test cards here.

On this page