API route
Createapp/api/subscription/route.ts:
Copy
Ask AI
import { NextRequest, NextResponse } from "next/server";
const CUPRICE_BASE = process.env.CUPRICE_BASE!;
const SHARE_ID = process.env.CUPRICE_SHARE_ID!;
export async function GET(req: NextRequest) {
const sessionId = req.nextUrl.searchParams.get("session_id");
if (!sessionId) {
return NextResponse.json({ error: "session_id required" }, { status: 400 });
}
// Fetch receipt from Cuprice
const res = await fetch(
`${CUPRICE_BASE}/api/stripe/receipt?session_id=${sessionId}&shareId=${SHARE_ID}`
);
const receipt = await res.json();
const metadata = receipt.metadata || {};
let features: string[] = [];
// Custom plans: reconstruct featureDetails
let json = "";
Object.keys(metadata)
.filter(k => k.startsWith("featureDetails"))
.sort()
.forEach(k => json += metadata[k]);
if (json) {
features = JSON.parse(json).map((f: { name: string }) => f.name);
}
// Regular plans: look up features by planId
if (!features.length && metadata.planId) {
const shareRes = await fetch(`${CUPRICE_BASE}/api/share/${SHARE_ID}`);
const { plans } = await shareRes.json();
const plan = plans?.find((p: any) => p.id.toString() === metadata.planId);
if (plan?.features) {
features = plan.features.filter((f: any) => f.included).map((f: any) => f.name);
}
}
return NextResponse.json({
planName: receipt.planName,
features,
customPlan: receipt.customPlan || false,
});
}
Payment handler component
Createapp/components/payment-handler.tsx:
Copy
Ask AI
"use client";
import { useSearchParams, useRouter } from "next/navigation";
import { useEffect } from "react";
export default function PaymentHandler() {
const params = useSearchParams();
const router = useRouter();
useEffect(() => {
const payment = params.get("payment");
const sessionId = params.get("session_id");
if (payment === "success" && sessionId) {
fetch(`/api/subscription?session_id=${sessionId}`)
.then(r => r.json())
.then(data => {
// Save to your database or localStorage
localStorage.setItem("plan_active", "true");
for (const name of data.features) {
const key = name.toLowerCase().replace(/\s+/g, "_");
localStorage.setItem(`feature_${key}`, "true");
}
router.push("/dashboard");
});
}
}, [params, router]);
return null;
}
Feature card component
Createapp/components/feature-card.tsx:
Copy
Ask AI
"use client";
import { useEffect, useState } from "react";
export default function FeatureCard({
featureKey,
title,
description,
}: {
featureKey: string;
title: string;
description: string;
}) {
const [enabled, setEnabled] = useState(false);
useEffect(() => {
const active = localStorage.getItem("plan_active") === "true";
const bought = localStorage.getItem(`feature_${featureKey}`) === "true";
setEnabled(active && bought);
}, [featureKey]);
return (
<div className={`p-6 border rounded ${
enabled ? "border-green-200 bg-white" : "border-gray-200 bg-gray-50 opacity-75"
}`}>
<span className={`text-xs font-bold ${enabled ? "text-green-700" : "text-gray-400"}`}>
{enabled ? "ENABLED" : "LOCKED"}
</span>
<h3 className="font-bold mt-2">{title}</h3>
<p className="text-sm text-gray-500 mt-1">{description}</p>
</div>
);
}
This example uses localStorage for simplicity. In production, store subscription data in your database and verify server-side.
