```html

Building a Printful-Integrated T-Shirt Site on Next.js 14: Infrastructure, Architecture, and Deployment Patterns

Overview: From Monorepo to Production

This post documents the complete build and deployment of 86dfrom.com, a Next.js 14 e-commerce site integrated with Printful's print-on-demand API and Stripe for payments. The project demonstrates modern serverless architecture patterns: decoupled frontend (Vercel), backend API routes (Next.js serverless functions), inventory management (Printful), and payment processing (Stripe). All infrastructure is code-driven and reproducible.

Project Structure and Setup

The project lives at /Users/cb/Documents/repos/sites/86dfrom.com with a clean directory tree:

86dfrom.com/
├── site/
│   ├── index.html          # Static landing page (pre-rendered)
│   └── success.html        # Post-purchase confirmation
├── gas/
│   ├── Code.gs             # Google Apps Script for webhook handling
│   ├── appsscript.json     # GAS manifest
│   └── .clasp.json         # Clasp project config
├── scripts/
│   ├── deploy.sh           # S3 + CloudFront deployment script
│   └── get-printful-variants.js  # Variant ID fetcher
├── .env.local              # Runtime credentials (git-ignored)
└── [Next.js app root]      # Next.js 14 app with /api routes

The monorepo structure allows us to version control static assets, deployment automation, and cloud functions alongside the Next.js application. Google Apps Script is used as a lightweight webhook receiver for asynchronous order processing, decoupling the payment flow from the fulfillment pipeline.

Build Verification and Compilation

Before any deployment, we validated the Next.js build pipeline:

cd ~/Documents/repos/sites/86dfrom.com && npm run build

The build compiled cleanly with all five API routes resolving without errors:

  • /api/products — Fetch product catalog with variant metadata from Printful
  • /api/checkout — Create Stripe checkout sessions
  • /api/webhook — Stripe webhook receiver for payment events
  • /api/orders — Order history and status
  • /api/health — Health check endpoint

This modular API design follows microservices principles: each route has a single responsibility, can be independently scaled by Vercel's serverless platform, and integrates cleanly with external services (Printful, Stripe, Google Sheets for order logging).

Printful Integration: Variant Fetching

The script scripts/get-printful-variants.js queries the Printful API to extract product variant IDs for the Bella+Canvas 3001 Black t-shirt. This is a critical step because Printful requires exact variant IDs in the fulfillment payload:

node scripts/get-printful-variants.js

The script makes authenticated calls to https://api.printful.com/ using the store API token (scoped to the "86Store" within the dangerouscentaur.com Printful account). Variant IDs returned are burned into environment variables, ensuring the checkout flow always orders the correct product configuration.

Why Printful? Print-on-demand eliminates inventory risk. Orders are created in Printful only after successful Stripe payment, ensuring zero upfront capital for stock. The API is REST-based and synchronous, making it trivial to call from Next.js serverless functions with sub-second latency.

Environment and Secrets Management

The .env.local file is never committed and is structured as:

NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_...
STRIPE_SECRET_KEY=sk_live_...
STRIPE_WEBHOOK_SECRET=whsec_...
PRINTFUL_API_TOKEN=
PRINTFUL_VARIANT_IDS=4016,4017,4018,4019,4020
GAS_WEBHOOK_URL=https://script.google.com/macros/d/.../usercontent

NEXT_PUBLIC_* prefixed variables are safe to expose to the browser (Stripe public key); others remain server-only. Vercel's UI allows environment variable injection at build and runtime, eliminating the need to commit secrets to git.

Stripe Configuration and Webhook Setup

Stripe integration follows the standard SCA (Strong Customer Authentication) flow:

  1. Frontend calls /api/checkout with cart data
  2. Backend creates a PaymentIntent and returns a client secret
  3. Frontend redirects to Stripe Checkout (managed session)
  4. Post-payment, Stripe POSTs to /api/webhook with event signed using whsec_* secret
  5. Webhook verifies signature and triggers fulfillment via Printful API

The webhook is the critical integration point. Its signature is verified server-side using the webhook secret, preventing spoofed payment events. Once verified, the handler calls Printful to create an order and optionally logs to Google Sheets via Apps Script.

Static Assets and CDN Distribution

The site/ directory contains static HTML and is served independently of the Next.js app for resilience:

bash scripts/deploy.sh

This script syncs site/ to an S3 bucket and invalidates the CloudFront cache, following the pattern established in other projects:

  • S3 bucket: 86dfrom-com-site (created during setup)
  • CloudFront distribution: Primary domain distribution for 86dfrom.com
  • Route53 hosted zone: Manages DNS for 86dfrom.com (created during initial infrastructure setup)

Static files are served with cache headers; CloudFront cache invalidation ensures updates propagate within seconds. This separation of static and dynamic content is a best practice: static assets can be cached aggressively, while API routes respond dynamically.

Vercel Deployment

The Next.js application is deployed to Vercel production:

npx vercel@latest --prod

This command builds the app, uploads it to Vercel's edge network, and assigns a unique production URL. All environment variables are injected from Vercel's dashboard (no .env.local file needed on CI/CD).

The Vercel deployment is fronted by the custom domain 86dfrom.com, configured via Route53 CNAME/alias records pointing to Vercel's nameservers. This architecture provides:

  • Global edge distribution: Requests route to nearest Vercel PoP
  • Automatic HTTPS: Vercel provisions SSL certs automatically
  • Seamless CI/CD: Git pushes trigger rebuilds (if integrated with GitHub)
  • Serverless functions