Project: SaaS Pricing Cards and Checkout Flow
In this project, we will build a premium SaaS pricing cards layout. The cards will trigger the Paddle Overlay checkout window and securely pass the current user account ID as metadata.
1. Interface Design Layout
graph TD
A[Pricing Cards Options] -->|Click Pro Card| B[Inject User ID metadata]
B --> C[Launch Paddle Checkout Overlay]2. Implementing the Pricing Card Component
Create the React Client Component to render plans cards and trigger Paddle:
// app/pricing/PricingCardList.tsx
"use client";
import React from "react";
import { getPaddle } from "../../lib/paddleClient";
interface Plan {
id: string;
name: string;
price: string;
priceId: string;
features: string[];
}
const plans: Plan[] = [
{
id: "free",
name: "Starter Tier",
price: "$0",
priceId: "",
features: ["Standard Dashboard access", "5 Projects", "Community Slack support"],
},
{
id: "pro",
name: "Business Pro",
price: "$29/mo",
priceId: "pri_test_pro_plan_price_id", // Paste sandbox price ID
features: ["Advanced Metrics", "Unlimited Projects", "Premium 24/7 Email support", "API Integration"],
},
];
export default function PricingCardList({ currentUserId }: { currentUserId: string }) {
async function triggerCheckout(priceId: string) {
const paddle = await getPaddle();
if (!paddle) return;
// Launch overlay checkout
paddle.Checkout.open({
items: [{ priceId, quantity: 1 }],
customData: {
userId: currentUserId, // Attach user database ID to match in webhook
},
settings: {
theme: "dark",
successUrl: "https://mycompany.com/dashboard?status=welcome",
},
});
}
return (
<div className="flex flex-col md:flex-row gap-8 max-w-4xl mx-auto mt-12 p-4">
{plans.map((plan) => (
<div key={plan.id} className="flex-1 border p-8 rounded-3xl bg-white shadow-sm flex flex-col justify-between">
<div>
<h3 className="text-xl font-bold text-gray-950">{plan.name}</h3>
<p className="text-3xl font-extrabold mt-4 text-blue-600">{plan.price}</p>
<ul className="mt-6 space-y-3">
{plan.features.map((feat, i) => (
<li key={i} className="text-sm text-gray-500 flex items-center gap-2">
✓ {feat}
</li>
))}
</ul>
</div>
<div className="mt-8">
{plan.priceId ? (
<button
onClick={() => triggerCheckout(plan.priceId)}
className="w-full bg-blue-600 text-white font-semibold py-2.5 rounded-lg text-sm transition hover:bg-blue-700"
>
Go Business Pro
</button>
) : (
<button
disabled
className="w-full bg-gray-100 text-gray-400 font-semibold py-2.5 rounded-lg text-sm"
>
Current Tier
</button>
)}
</div>
</div>
))}
</div>
);
}3. Webhook Integration Verification
When a purchase is completed:
- Paddle triggers the webhook endpoint payload.
- The endpoint reads the
userIdcustom data parameter. - The server updates the database row
pricingTier: "pro". - Next time the user loads the dashboard, the server shows their upgraded Business Pro interface features.
Published on Last updated: