```html

Building a Printful-Integrated T-Shirt E-Commerce Site with Next.js 14, Stripe, and AWS CloudFront

This post documents the end-to-end deployment of 86dfrom.com, a print-on-demand t-shirt storefront powered by Printful's API, Stripe for payments, and Google Apps Script for order fulfillment automation. The stack spans Next.js 14 on Vercel, AWS S3 + CloudFront for static assets, and Google Sheets as a lightweight fulfillment database.

Architecture Overview

The site is structured around five core Next.js routes, each handling a distinct function:

  • / – Marketing homepage with product carousel and checkout entry point
  • /api/variants – Printful API proxy; returns available t-shirt colors and sizes
  • /api/checkout – Stripe Checkout session creation
  • /api/webhook – Stripe webhook handler for payment completion and fulfillment triggers
  • /success – Post-purchase confirmation page

This separation of concerns allows the frontend to remain lightweight while centralizing payment and inventory logic on the server.

Next.js Build & Deployment Pipeline

The project lives at /Users/cb/Documents/repos/sites/86dfrom.com with the following structure:

86dfrom.com/
├── site/
│   ├── index.html
│   └── success.html
├── gas/
│   ├── Code.gs
│   └── appsscript.json
├── scripts/
│   └── deploy.sh
├── .env.local (to be generated)
├── next.config.js
├── package.json
└── pages/api/ (routes)

The Next.js build compiles cleanly with no errors. Each API route is independently compilable, ensuring modularity and ease of debugging. Environment variables are injected at deploy time via Vercel's platform, not hardcoded into the repository.

Why this structure? Separating static HTML (in site/) from Google Apps Script code (in gas/) allows us to:

  • Deploy the frontend to Vercel independently of backend automation
  • Version control and test GAS code separately via clasp (Google Apps Script CLI)
  • Keep deployment scripts (scripts/deploy.sh) DRY and reusable across multiple projects

Printful API Integration

Printful serves as the print-on-demand provider. The /api/variants endpoint fetches available t-shirt colors, sizes, and pricing directly from Printful's REST API, eliminating the need to maintain a separate product database.

The variant IDs for the core product (Bella+Canvas 3001 Black t-shirt) are:

4016 – XS
4017 – S
4018 – M
4019 – L
4020 – XL

These are hardcoded in the API route but could be externalized to a database or .env file for multi-product support. The Printful API key is stored as a Vercel environment variable and never committed to version control.

Why Printful? It abstracts away inventory management, fulfillment, and shipping. When a customer places an order, we send JSON to Printful's order endpoint; they handle printing, packing, and shipping. This eliminates the need for warehouse infrastructure.

Stripe Payment Integration

Payments are handled via Stripe Checkout. The flow is:

  1. Customer selects size/color on the homepage and clicks "Buy Now"
  2. Frontend calls POST /api/checkout with size, color, and price
  3. Backend creates a Stripe Checkout session and returns a session ID
  4. Stripe.js redirects to the Stripe-hosted checkout page
  5. On successful payment, Stripe sends a webhook to /api/webhook
  6. Webhook handler creates the Printful order and logs it to Google Sheets

The webhook endpoint must be registered manually in the Stripe Dashboard under Developers → Webhooks. The URL will be https://86dfrom.com/api/webhook once DNS is live. The webhook listens for the checkout.session.completed event.

Why Stripe Checkout? It's PCI-compliant out of the box, handles card validation, 3D Secure, and supports international cards. We avoid building our own payment form, reducing security risk and development overhead.

Google Apps Script Fulfillment Automation

When a payment succeeds, the webhook triggers a Google Apps Script function that:

  • Receives order data (customer name, email, size, color) from the webhook
  • Appends the order to a Google Sheet (86dfrom-orders)
  • Sends a confirmation email to the customer
  • Triggers a Printful order via API

The GAS code is in gas/Code.gs and deployed via clasp` to Google's Apps Script platform. The deployment ID is stored in gas/.clasp.json.

The script is exposed as a web app via appsscript.json:

{
  "timeZone": "America/Los_Angeles",
  "exceptionLogging": "STACKDRIVER",
  "runtimeVersion": "V8",
  "webapp": {
    "executeAs": "USER_DEPLOYING",
    "access": "ANYONE"
  }
}

This allows the Vercel webhook handler to POST order data to the GAS endpoint without authentication.

DNS & CDN Infrastructure

The domain 86dfrom.com uses AWS Route53 for DNS. Two CloudFront distributions were created:

  • Primary distribution (86dfrom.com): Origin: Vercel. Caches HTML, JS, and API responses with appropriate TTLs.
  • Redirect distribution (86from.com): A CloudFront function intercepts requests to the legacy domain and 301-redirects to 86dfrom.com.

An ACM certificate was requested for both domains and validated via DNS CNAME records in Route53. This ensures HTTPS is automatic and certificate renewal is handled by AWS.

Why CloudFront? It provides edge caching, DDoS protection via Shield Standard, and geographic load balancing. For a product site, this means:

  • Static assets (CSS, fonts) are cached at 255 edge locations worldwide
  • HTML is cached with a short TTL (60 seconds) to allow quick updates without purges
  • API responses bypass cache to ensure real-time inventory and pricing

Environment Variables & Secrets Management

The .env.local file (not committed to git) contains:

NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_xxx
STRIPE_SECRET_KEY=sk_xxx
PRINTFUL_API_KEY