Back to roadmaps lemonsqueezy Course

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:

  1. The server reads their transaction state from PostgreSQL.
  2. The server verifies that the order has been paid.
  3. 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: