Developer Guide

Integration Documentation

Everything you need to configure your store, manage products, and handle secure downloads.

1. Getting Started

Three commands to a working store:

# Scaffold a new AeroCart project via npm
npx aerocart my-store
# Navigate into your project
cd my-store
# Start the dev server
npm run dev

Or clone manually from GitHub:

git clone https://github.com/byronjohnson/aerocart.git my-store
cd my-store
npm install
npm run setup
npm run dev

The interactive setup wizard will:

  1. Create your .env.local from the example template
  2. Prompt for your Stripe API keys (with format validation)
  3. Ask for your AeroCart license key (optional — skip for Free tier)
  4. Check your product inventory for completeness
  5. Verify your Node.js version (18+ required)

Get your Stripe keys from the Stripe Dashboard. Use test mode keys during development.

Tip: Use Stripe test card 4242 4242 4242 4242 (any future expiry, any CVC) to test the full purchase flow without real charges.

2. Project Structure

├── setup.mjs                      # ⚡ Interactive setup wizard
├── CHANGELOG.md                   # Version history
├── TROUBLESHOOTING.md             # Common issues & fixes
├── .env.example                   # Environment variable template
└── src/
    ├── app/
    │   ├── api/
    │   │   ├── checkout/route.js      # Creates Stripe sessions
    │   │   ├── secure-download/route.js # Secure file delivery
    │   │   ├── session/route.js       # Verify purchases
    │   │   ├── products/route.js      # List all products
    │   │   └── products/[id]/route.js # Get single product
    │   ├── buy/[productId]/page.js    # Direct purchase page
    │   ├── cart/page.js               # Full cart page
    │   ├── success/page.js            # Post-payment downloads
    │   └── page.js                    # Store homepage
    ├── components/
    │   ├── CartContext.js             # Cart state (React Context)
    │   └── FloatingCart.js            # Slide-out cart drawer
    └── lib/
        ├── inventory.js               # ⭐ YOUR PRODUCTS GO HERE
        ├── license.js                 # 🔑 License validation (free/pro)
        └── stripe.js                  # Stripe client + env validation

3. Licensing (Free & Pro)

AeroCart uses a freemium model. The free tier lets you sell 1 product with all features included. Upgrade to Pro to sell unlimited products.

FeatureFreePro ($50/yr)
Products1Unlimited
Source Code✅ Full✅ Full
Stripe Integration
Secure Delivery
Updates & Support1 year

Activating Pro

After purchasing a Pro license at aerocartshop.com/pricing, you'll receive a license key on the confirmation page. Add it to your .env.local:

# Add to .env.local
AEROCART_LICENSE_KEY=AC-PRO-your-key-here

Restart your dev server — unlimited products are instantly unlocked. No code changes needed.

Note: The license key is cryptographically signed and tied to your email. It cannot be forged or modified. Keep it safe — it's your proof of purchase.

4. Defining Products

All products are defined in src/lib/inventory.js. This is the only file you need to edit to add or modify products. Every field has TODO markers to guide you.

src/lib/inventory.js

export const products = {
  'my-ebook': {
    id: 'my-ebook',                       // TODO: Your product ID
    name: 'The Ultimate Guide',           // TODO: Your product name
    description: 'A comprehensive...',    // TODO: Your description
    price: 2900,                          // TODO: Price in cents ($29.00)
    currency: 'usd',
    type: 'pdf',                          // TODO: pdf, zip, mp3, mp4
    image: '/images/ebook.jpg',           // TODO: Your product image
    downloadUrl: process.env.EBOOK_URL,   // TODO: Your file URL
  },
};

// Helper functions (already provided)
export function getAllProducts() { ... }
export function getProduct(id) { ... }
export function getDownloadUrl(id) { ... }
Tip: Use environment variables for downloadUrl to keep file URLs out of your codebase. Add them to .env.local and reference with process.env.MY_PRODUCT_URL.

The file includes commented-out examples for video courses, software tools, and music packs — uncomment and modify as needed.

5. Using the Cart

The cart is already set up in layout.js. Use the useCart hook anywhere in your app.

import { useCart } from '@/components/CartContext'; import { getAllProducts } from '@/lib/inventory'; export default function ProductCard() { const { addToCart, cart, count, total } = useCart(); const products = getAllProducts(); return ( <div> {products.map(product => ( <button key={product.id} onClick={() => addToCart(product)} > Add {product.name} to Cart </button> ))} <p>Items in cart: {count}</p> <p>Total: ${(total / 100).toFixed(2)}</p> </div> ); }

Available Cart Methods

MethodDescription
addToCart(product)Add item or increment quantity
removeFromCart(id)Remove item completely
clearCart()Empty entire cart
toggleCart()Open/close cart drawer
countTotal number of items
totalTotal price in cents
isLoadedWhether localStorage has been read (prevents flash)

6. Secure Delivery

The secure download flow is handled automatically by /api/secure-download.

How it works:

  1. User completes checkout → redirected to /success?session_id=xxx
  2. Success page calls /api/session to get purchased product IDs
  3. User clicks "Download" → hits /api/secure-download?session_id=xxx&product_id=yyy
  4. API verifies Stripe session is paid
  5. API checks product was in the purchase metadata
  6. API fetches file from your private URL and streams to user
Important: Your source files (S3, R2, etc.) should be private. Only your server-side code should have access via secret URLs.

7. API Reference

POST /api/checkout

Creates a Stripe Checkout session. Prices are validated server-side from inventory.js — tamper-proof.

// Request Body
{ "items": [{ "id": "product-id", "quantity": 1 }] }

// Response
{ "url": "https://checkout.stripe.com/..." }

GET /api/session

Returns purchased products for a session.

// Request
GET /api/session?session_id=cs_xxx

// Response
{ 
  "status": "paid",
  "products": [{ "id": "...", "name": "...", "type": "pdf" }]
}

GET /api/secure-download

Streams a purchased file to the user. Verifies session + product before delivery.

// Request
GET /api/secure-download?session_id=cs_xxx&product_id=my-ebook

// Response
Binary file stream with Content-Disposition: attachment

GET /api/products

Lists all products (download URLs stripped for security). Safe to call from any client.

// Request
GET /api/products

// Response
{ "products": [{ "id": "...", "name": "...", "price": 2900, ... }] }

// Single product
GET /api/products/my-ebook

8. Environment Variables

All configuration lives in .env.local. Run npm run setup to configure interactively, or edit the file directly.

VariableRequiredDescription
STRIPE_SECRET_KEY✅ YesServer-side key (sk_test_... or sk_live_...)
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY✅ YesClient-side key (pk_test_... or pk_live_...)
NEXT_PUBLIC_BASE_URLRecommendedYour app URL (defaults to localhost:3000)
*_DOWNLOAD_URLPer productSecure file URLs (S3, R2, Supabase Storage, etc.)
AEROCART_LICENSE_KEYOptionalPro license key — unlocks unlimited products
Validation: The app automatically validates your Stripe keys on startup. If keys are missing or misconfigured, you'll see a detailed error message with instructions to fix it.

9. Troubleshooting

Quick fixes for the most common issues:

ProblemSolution
STRIPE_SECRET_KEY is missingRun npm run setup or add the key to .env.local
Products not showingCheck src/lib/inventory.js — add your products to the products object. On Free tier, only the first product is shown.
Only 1 product visibleYou're on the Free tier. Add AEROCART_LICENSE_KEY to .env.local to unlock all products.
Download fails after purchaseVerify downloadUrl is accessible — test with curl -I YOUR_URL
Build failsCheck Node version (18+), then rm -rf node_modules && npm install
CORS errors (cross-domain)Add CORS headers to your API routes — see TROUBLESHOOTING.md

See the full TROUBLESHOOTING.md included in the project for detailed solutions with code examples.