Developer Tools
Quickstart Templates
Production-ready starter code for integrating ClapPay into your app. Copy, paste, and customize.
5 min setup per template
Next.js Starter
Complete Next.js app with ClapPay checkout, webhooks, and customer portal.
1. Create Project
bash
npx create-next-app@latest my-clappay-app --typescript cd my-clappay-app npm install @clappay/sdk @clappay/js
2. Configure Environment
env
# .env.local CLAPPAY_SECRET_KEY=sk_test_... NEXT_PUBLIC_CLAPPAY_PUBLISHABLE_KEY=pk_test_... CLAPPAY_WEBHOOK_SECRET=whsec_... NEXT_PUBLIC_APP_URL=http://localhost:3000
3. Server Code
typescript
// app/api/checkout/route.ts
import { NextResponse } from "next/server";
const API_URL = process.env.NEXT_PUBLIC_API_URL || "https://api.clappay.com";
export async function POST(req: Request) {
const { priceId, customerEmail } = await req.json();
const response = await fetch(`${API_URL}/api/v1/payment/checkout-sessions/`, {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.CLAPPAY_SECRET_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
price_id: priceId,
customer_email: customerEmail,
success_url: `${process.env.NEXT_PUBLIC_APP_URL}/success?session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${process.env.NEXT_PUBLIC_APP_URL}/pricing`,
}),
});
const session = await response.json();
return NextResponse.json({ url: session.url });
}4. Client Page
typescript
// app/pricing/page.tsx
"use client";
import { useState } from "react";
export default function PricingPage() {
const [loading, setLoading] = useState(false);
async function handleCheckout(priceId: string) {
setLoading(true);
const res = await fetch("/api/checkout", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ priceId, customerEmail: "customer@example.com" }),
});
const { url } = await res.json();
window.location.href = url;
}
return (
<div className="max-w-4xl mx-auto py-12">
<h1 className="text-3xl font-bold mb-8">Choose a Plan</h1>
<div className="grid md:grid-cols-2 gap-6">
<div className="border rounded-lg p-6">
<h2 className="text-xl font-semibold">Starter</h2>
<p className="text-3xl font-bold mt-2">$29/mo</p>
<button
onClick={() => handleCheckout("price_starter_monthly")}
disabled={loading}
className="mt-4 w-full bg-blue-600 text-white py-2 rounded hover:bg-blue-700"
>
{loading ? "Redirecting..." : "Get Started"}
</button>
</div>
<div className="border rounded-lg p-6">
<h2 className="text-xl font-semibold">Pro</h2>
<p className="text-3xl font-bold mt-2">$99/mo</p>
<button
onClick={() => handleCheckout("price_pro_monthly")}
disabled={loading}
className="mt-4 w-full bg-blue-600 text-white py-2 rounded hover:bg-blue-700"
>
{loading ? "Redirecting..." : "Get Started"}
</button>
</div>
</div>
</div>
);
}5. Webhook Handler
typescript
// app/api/webhooks/clappay/route.ts
import { NextResponse } from "next/server";
import crypto from "crypto";
export async function POST(req: Request) {
const body = await req.text();
const signature = req.headers.get("clappay-signature") || "";
// Verify webhook signature
const expected = crypto
.createHmac("sha256", process.env.CLAPPAY_WEBHOOK_SECRET!)
.update(body)
.digest("hex");
if (signature !== expected) {
return NextResponse.json({ error: "Invalid signature" }, { status: 401 });
}
const event = JSON.parse(body);
switch (event.type) {
case "checkout.session.completed":
// Fulfill the order
console.log("Payment succeeded:", event.data.id);
break;
case "subscription.updated":
// Update subscription status
console.log("Subscription updated:", event.data.id);
break;
case "invoice.payment_failed":
// Notify customer
console.log("Payment failed:", event.data.id);
break;
}
return NextResponse.json({ received: true });
}