Project: Digital eBook Store with Secure Downloads
In this project, we will build a digital ebook marketplace. The site allows users to purchase using Lemon Squeezy variants, apply discount campaigns, and securely access short-lived download links after successful transactions.
1. Purchase and Download Flow
graph TD
A[User purchases Ebook] --> B[Lemon Squeezy triggers order_created]
B --> C[Verify signature and save Order info]
C --> D[Redirect User to download page]
D --> E[Server generates short-lived file download URL]2. Implementing Ebook Checkout Page
Create the UI component presenting the ebook and triggering the payment action:
// app/ebook/CheckoutSection.tsx
"use client";
import React, { useState } from "react";
export default function CheckoutSection({ variantId }: { variantId: string }) {
const [loading, setLoading] = useState(false);
async function handleBuy() {
setLoading(true);
try {
const response = await fetch("/api/checkout/create-link", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ variantId, email: "buyer@example.com" }),
});
const { url } = await response.json();
if (url) {
window.location.href = url; // Redirect to hosted checkout
}
} catch (err) {
console.error("Payment redirect failed:", err);
} finally {
setLoading(false);
}
}
return (
<div className="max-w-sm mx-auto p-6 border rounded-3xl bg-white shadow-sm mt-16 text-center">
<img src="/ebook-cover.jpg" alt="Ebook Cover" className="w-full h-48 object-cover rounded-xl" />
<h2 className="text-xl font-bold mt-4 text-gray-950">Next.js Development Handbook</h2>
<p className="text-gray-400 text-xs mt-1">Master Server Components and database integration.</p>
<div className="mt-6 flex justify-between items-center">
<span className="text-2xl font-black text-gray-900">$19.99</span>
<button
onClick={handleBuy}
disabled={loading}
className="bg-blue-600 hover:bg-blue-700 text-white font-semibold py-2 px-4 rounded-lg text-sm transition"
>
{loading ? "Redirecting..." : "Buy Ebook"}
</button>
</div>
</div>
);
}3. Serving Secure File Downloads
When the customer lands on the /download page:
- The server reads their transaction state from PostgreSQL.
- The server verifies that the order has been paid.
- The server generates a temporary SAS (shared access signature) URL for the file storage (such as AWS S3 or Supabase Storage) with an expiry of 10 minutes, protecting your file from unauthorized redistribution.
// app/api/download/route.ts
import { prisma } from "../../lib/prisma";
export async function GET(req: Request) {
const url = new URL(req.url);
const orderId = url.searchParams.get("orderId");
if (!orderId) return new Response("Order required", { status: 400 });
// 1. Verify payment status in database
const order = await prisma.order.findUnique({
where: { id: orderId },
});
if (!order || order.status !== "paid") {
return new Response("Payment not verified", { status: 403 });
}
// 2. Generate secure download link (Mock example redirect)
const secureFileUrl = "https://storage.mycompany.com/files/handbook.pdf?token=temporary-sas-key-here";
return Response.redirect(secureFileUrl);
}Published on Last updated: