Vexor

Checkouts

Chekouts are the simplest way to collect payments. They are used for one-time payments, such as a purchase of a product or service or donations. In this guide you'll learn how to create a basic checkout for the most common use case. You can then extend it to more complex scenarios.

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 Checkout 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 products

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

// src/components/product-card.tsx
 
"use client"; // Mark it as a client component
 
// Define a simple product interface
export interface Product {
    id: string;
    title: string;
    description: string;
    quantity: number;
    unit_price: number;
}
 
 
const ProductCard: React.FC<{ product: Product }> = ({ product }) => {
 
    const handlePurchase = async () => {
        console.log(product); // We will complete this function in the next step
    };
 
    return (
        <div className="border rounded-lg p-4 shadow-sm">
            <h2 className="text-xl font-bold mb-2">{product.title}</h2>
            <p className="text-primary-foreground mb-4">{product.description}</p>
            <p className="mb-2">Quantity: {product.quantity}</p>
            <p className="mb-4">Price: ${product.unit_price.toFixed(2)}</p>
            <button
                onClick={handlePurchase}
                className="bg-foreground text-background px-4 py-2 rounded w-full"
            >
                "Buy"
            </button>
        </div>
    );
};
 
export default ProductCard;

Import the component in the page and define some products

Ideally this component will be used in a page that lists all the products. For demonstration purposes, we will just define some products 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 ProductCard, { Product } from "@/components/product-card";
 
const products: Product[] = [
  {
      id: "item-1",
      title: "Premium Widget",
      description: "High-quality widget with advanced features",
      quantity: 2,
      unit_price: 2
  },
  {
      id: "item-2",
      title: "Standard Gadget",
      description: "Reliable gadget for everyday use",
      quantity: 1,
      unit_price: 10
  }
];
 
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 sm:items-start w-full max-w-4xl">
        <h1 className="text-3xl font-bold mb-8">Our Products</h1>
        <div className="grid grid-cols-1 sm:grid-cols-2 gap-8 w-full">
          {products.map((product) => (
            <ProductCard key={product.id} product={product} />
          ))}
        </div>
      </main>
    </div>
  );
}

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

checkout products ui

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

Use the Vexor SDK to create the purchase

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

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

Import vexor, VexorPaymentResponse and useTransition hook

The useTransition hook from react is a good approach to handle the logic and loading state of the purchase. We will also import the VexorPaymentResponse type to handle the response of the purchase (recommended).

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

Complete the handlePurchase function

Let's complete the handlePurchase function to create the purchase 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 purchase logic.

    const [isPending, startTransition] = useTransition();
 
    const handlePurchase = async () => {
        startTransition(async () => {
            try {
                const response : VexorPaymentResponse = await vexor.pay.stripe({
                    items: [product],
                });
 
                window.location.href = response.payment_url;
            } catch (error) {
                console.error("Purchase failed:", error);
                // Handle error (e.g., show an error message to the user)
            }
        });
    };

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

<button
    onClick={handlePurchase}
    className="bg-foreground text-background px-4 py-2 rounded w-full"
    disabled={isPending}
>
    {isPending ? "Loading..." : "Buy"}
</button>

The vexor.pay.stripe function is used to create the purchase. You can use any other provider by calling the appropriate function, for example vexor.pay.mercadopago, vexor.pay.talo or vexor.pay.paypal. Or the generic vexor.pay method. You can read more about the pay 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 product-card.tsx file should now look like this:

// src/components/product-card.tsx
 
"use client";
 
import { vexor } from "@/lib/vexor";
import { VexorPaymentResponse } from "vexor";
import { useTransition } from "react";
 
export interface Product {
    id: string;
    title: string;
    description: string;
    quantity: number;
    unit_price: number;
}
 
const ProductCard: React.FC<{ product: Product }> = ({ product }) => {
    const [isPending, startTransition] = useTransition();
 
    const handlePurchase = async () => {
        startTransition(async () => {
            try {
                const response : VexorPaymentResponse = await vexor.pay.stripe({
                    items: [product],
                });
 
                window.location.href = response.payment_url;
            } catch (error) {
                console.error("Purchase failed:", error);
                // Handle error (e.g., show an error message to the user)
            }
        });
    };
 
    return (
        <div className="border rounded-lg p-4 shadow-sm">
            <h2 className="text-xl font-bold mb-2">{product.title}</h2>
            <p className="text-primary-foreground mb-4">{product.description}</p>
            <p className="mb-2">Quantity: {product.quantity}</p>
            <p className="mb-4">Price: ${product.unit_price.toFixed(2)}</p>
            <button
                onClick={handlePurchase}
                className="bg-foreground text-background px-4 py-2 rounded w-full"
                disabled={isPending}
            >
                {isPending ? "Loading..." : "Buy"}
            </button>
        </div>
    );
};
 
export default ProductCard;
 

Test the purchase

Run your app locally and test the purchase by clicking on the "Buy" button. You should be redirected to the payment page.

Congratulations! 🎉

You have now a basic checkout working. You can extend this logic to more complex scenarios, for example, by adding a cart and a checkout page.

Is this all we need? Yes and no. We have a working checkout, the user can now pay for the products and we get the money after the purchase, but we don't have a way to know if the purchase was successful or not. There are two more things that we recommend you to do:

  1. Create the success and failure pages. By default, vexor will redirect to yourapp.com/success or yourapp.com/failure based on the purchase result. You can build a UI for this pages or use the default ones. If you want to customize the redirect urls, you can read more about redirect urls here.

  2. Add a webhook (Yes, this is important 🫡) to your project to know when the purchase is successful or not. 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.

On this page