Create Pricing Page

Course: Integrate Stripe Subscriptions with Next.js

Introduction

Since the focus of this course is on Stripe integration, I won't too much time on building the pricing UI. The following is a very basic pricing page with some pricing cards.

Pricing Cards

The pricing cards we will be using will have the following properties:

const tiers = [
  {
    name: "Free",
    price: "$0",
    priceId: null,
    features: ["Basic features", "Limited usage", "Community support"],
    buttonText: "Get Started",
  },
  {
    name: "Premium",
    price: "$10/mo",
    priceId: "premium",
    features: [
      "All basic features",
      "Advanced analytics",
      "Priority support",
      "Custom integrations",
    ],
    buttonText: "Upgrade to Premium",
  },
  {
    name: "Pro",
    price: "$20/mo",
    priceId: "pro",
    features: [
      "All premium features",
      "Unlimited usage",
      "24/7 support",
      "White-label options",
      "API access",
    ],
    buttonText: "Go Pro",
  },
]

Then we'll build a PricingCard component that we can pass the tiers objects into:

<div className="grid grid-cols-1 md:grid-cols-3 gap-8 w-full max-w-6xl">
      {tiers.map((tier) => (
        <div
          key={tier.name}
          className="bg-white rounded-xl shadow-lg p-6 flex flex-col justify-between hover:border border-indigo-300 transition-colors"
        >
          <div>
            <h2 className="text-xl font-semibold mb-4">{tier.name}</h2>
            <p className="text-3xl font-bold mb-6">{tier.price}</p>
            <ul className="mb-6 space-y-2">
              {tier.features.map((feature, idx) => (
                <li key={idx} className="flex items-center">
                  <span className="text-green-500 mr-2"></span>
                  {feature}
                </li>
              ))}
            </ul>
          </div>
          <button
            onClick={() => handleSubscribe(tier.priceId!)}
            className="px-4 py-2 rounded-lg  bg-indigo-600 text-white hover:bg-indigo-700 cursor-pointer"
          >
            {tier.buttonText}
          </button>
        </div>
      ))}
    </div>

Now let's create the logic for the handleSubscribe function:

const handleSubscribe = async (priceId: string) => {
    if (!priceId) return

    try {
      const response = await fetch("/api/stripe/checkout", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify({ priceId }),
      })

      const { url } = await response.json()

      if (url) {
        window.location.href = url
      } else {
        throw new Error("No checkout URL received")
      }
    } catch (error) {
      console.error("Error creating checkout session:", error)
      alert("Something went wrong. Please try again.")
    }
  }

If priceId is null (free tier) then nothing happens. If you want, you can do a redirect instead. Then in a trycatch block, we'll send the priceId to our checkout API. It's going to return a url, which we will redirect them to. This is the payment portal that is hosted by Stripe.

Now, with the front end set up, we'll go and build the checkout API endpoint.