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 });
}

Related Articles