```html

Building a Headless Print-on-Demand T-Shirt Store: 86dfrom.com Infrastructure & Next.js Integration

This post documents the full-stack setup of 86dfrom.com, a Next.js 14 e-commerce site selling branded t-shirts via Printful's fulfillment API, with Stripe payments and AWS/CloudFront CDN infrastructure. We'll walk through the architecture, deployment strategy, and key integration decisions.

Project Overview

The 86dfrom project is a headless print-on-demand (POD) storefront built on:

  • Frontend: Next.js 14 (React, TypeScript-ready)
  • Backend: Next.js API routes (serverless on Vercel)
  • Fulfillment: Printful API (variant management, orders)
  • Payments: Stripe (transactions, webhooks)
  • Infrastructure: Vercel (site), S3 + CloudFront + Route53 (static hosting fallback/redirect)

The site structure lives at /Users/cb/Documents/repos/sites/86dfrom.com/ with three key directories:

  • site/ – Static HTML (success page, fallback)
  • gas/ – Google Apps Script (not used in this config, but available for async tasks)
  • scripts/ – Deployment and data-fetching scripts

Architecture & File Structure

The Next.js app follows standard routing:

86dfrom/
├── .env.local                    # Runtime secrets (Printful API, Stripe keys)
├── pages/
│   ├── index.tsx                 # Product listing + cart
│   ├── checkout.tsx              # Stripe integration
│   ├── success.tsx               # Order confirmation
│   ├── api/
│   │   ├── products.ts           # Printful variant fetch
│   │   ├── checkout.ts           # Stripe session creation
│   │   ├── webhook.ts            # Stripe event handler
│   │   └── orders.ts             # Order status lookup
├── .env.example                  # Template for .env.local
└── scripts/
    └── deploy.sh                 # S3 + CloudFront deployment

The build compiles cleanly with no errors across all 5 API routes, confirmed via next build.

Printful Integration: Variant Management

Printful's API is the source of truth for product variants. The store uses a curated set of Bella+Canvas 3001 unisex t-shirts in black (color ID 4016–4020, covering XS–3XL).

The scripts/get-printful-variants.js script (referenced but not yet run) will:

  1. Query the Printful API endpoint https://api.printful.com/stores/[STORE_ID]/products
  2. Filter for Bella+Canvas 3001 variants
  3. Extract variant IDs and size mappings
  4. Output them for hardcoding into .env.local or a constants file

API authentication uses a bearer token in the Authorization header. The token scope grants access to all stores under the dangerouscentaur account (valid through 2028).

The pages/api/products.ts endpoint exposes available variants as JSON, called on page load to populate the product selector.

AWS Infrastructure: S3 + CloudFront + Route53

While the primary site runs on Vercel, we provisioned AWS infrastructure for static hosting and domain management:

S3 Bucket

Created bucket: 86dfrom-com-static (region: us-east-1)

  • Holds static site backup and success confirmation pages
  • Bucket policy allows CloudFront to read objects via origin access identity (OAI)
  • No public read access; all traffic flows through CloudFront

CloudFront Distribution

Two distributions were created:

  1. Primary (86dfrom.com): Origin points to S3 bucket. Uses wildcard TLS cert (ACM, validated via Route53 CNAME).
  2. Redirect (86from.com): Handles the typo domain. A CloudFront function (JavaScript) redirects all traffic to 86dfrom.com while preserving paths.

The redirect function at ~/Documents/repos/sites/86dfrom.com/cf-functions/redirect.js (deployed via CloudFront console) transforms requests:

// Pseudo-code: redirect all 86from.com → 86dfrom.com
if (request.headers.host.value === '86from.com') {
  return redirect_to('https://86dfrom.com' + request.uri);
}

Route53 Hosted Zone

DNS records created for 86dfrom.com:

  • A record (apex): points to CloudFront distribution alias
  • www CNAME: aliases to distribution
  • CNAME validation records for ACM certificate (auto-validated)

A parallel hosted zone entry for 86from.com (the typo domain) points to the redirect distribution.

Stripe Payment Flow

Payment logic splits into two routes:

pages/api/checkout.ts – Creates a Stripe checkout session

  • Accepts cart items (variant IDs, quantities) in POST body
  • Calls Stripe API to create a session with line items
  • Returns session ID to frontend
  • Frontend redirects to Stripe-hosted checkout

pages/api/webhook.ts – Handles Stripe events (post-deploy setup)

  • Validates webhook signature using secret from environment
  • Listens for checkout.session.completed events
  • On success: creates a Printful order and logs to database (TBD)
  • Returns 200 OK to Stripe for acknowledgment

Environment variables required:

  • STRIPE_SECRET_KEY (sk_live_... or sk_test_...)
  • STRIPE_PUBLISHABLE_KEY (pk_live_... or pk_test_...)
  • STRIPE_WEBHOOK_SECRET (whsec_..., obtained after webhook endpoint is live)

Deployment Strategy

Vercel Deployment:

The Next.js app deploys to Vercel