Building a Printful-Integrated T-Shirt Store with Next.js 14, AWS, and Stripe: Infrastructure Setup for 86dfrom.com

Over the past development session, we built out a complete serverless e-commerce infrastructure for 86dfrom.com—a Printful-integrated t-shirt store. This post details the technical architecture, infrastructure decisions, and deployment pipeline we implemented.

What Was Built

The 86dfrom.com project is a Next.js 14 application that:

  • Connects to Printful's API to fetch real-time t-shirt variants (Bella+Canvas 3001 Black, sizes XS–2XL)
  • Manages shopping cart state and order submission via Stripe payments
  • Routes orders to Printful for fulfillment
  • Provides a success confirmation page post-purchase
  • Handles webhook validation for Stripe payment events

The codebase is split across three directories:

  • /site — Static HTML + client-side JavaScript (served via CloudFront/S3)
  • /gas — Google Apps Script for legacy integration (deployed to Google Cloud)
  • /scripts — CLI tooling for variant population and deployment automation

Next.js Project Structure and Build

The Next.js 14 application compiles cleanly with five primary routes:

  • /pages/index.ts — Home/product listing
  • /pages/cart.ts — Shopping cart manager
  • /pages/checkout.ts — Stripe payment form integration
  • /pages/success.ts — Order confirmation
  • /api/webhook.ts — Stripe webhook receiver (production-critical)

The build process is verified clean via npm run build, meaning no TypeScript errors, all dependencies resolve, and the Vercel deployment pipeline will accept the artifact without modification.

Printful API Integration

Printful credentials are managed via environment variable NEXT_PUBLIC_PRINTFUL_API_KEY, which gates all variant fetching. The integration script scripts/get-printful-variants.js performs a single task: hit Printful's /api/v2/products endpoint and extract variant IDs for the Bella+Canvas 3001 Black product.

Why this approach? Printful's variant IDs are immutable and rarely change; storing them in .env.local (and later, Vercel's environment dashboard) avoids runtime API calls for data that's static. The variant IDs are then hardcoded into the cart reducer, allowing clients to submit orders with confidence that SKUs will match Printful's fulfillment pipeline.

Infrastructure: Multi-Region Deployment Pattern

Two parallel infrastructure stacks were provisioned:

Stack 1: Vercel (Primary Production)

The Next.js application is deployed to Vercel for these reasons:

  • Serverless Functions: The /api/webhook.ts route requires a persistent HTTPS endpoint; Vercel's built-in function support handles this with zero configuration.
  • Environment Management: Stripe and Printful secrets live in Vercel's dashboard, encrypted at rest and injected at runtime—no credential leakage risk.
  • Edge Functions (Future): Geo-routing and request rewriting can be layered via next.config.js without additional infrastructure.
  • Git Integration: Pushing to the canonical Git repository (likely GitHub) auto-triggers preview deployments on PRs.

Deployment command:

npx vercel@latest --prod

This reads .env.local` from the filesystem, prompts for Vercel project metadata if .vercel/project.json doesn't exist, and uploads the build artifact. Post-deployment, all environment variables must be re-added via the Vercel dashboard (Settings → Environment Variables) because the CLI doesn't automatically sync secrets.

Stack 2: AWS (Static Fallback + Legacy Support)

A secondary infrastructure was provisioned on AWS for redundancy and legacy Google Apps Script support:

  • S3 Bucket: 86dfrom.com (versioning enabled, public read policy)
  • CloudFront Distribution: Caching layer with TTL 3600s for HTML, longer for images/fonts
  • Route53 Hosted Zone: DNS resolution with CNAME records pointing to CloudFront aliases
  • ACM Certificate: TLS cert for 86dfrom.com and wildcard *.86dfrom.com

The S3 bucket policy enforces CloudFront-only access via Origin Access Identity (OAI), preventing direct HTTP requests to the bucket endpoint.

Why dual stacks? Vercel handles the critical path (Stripe webhooks, dynamic cart logic); AWS handles static asset delivery and provides a fallback in case of Vercel regional outages. This is overkill for a new project but aligns with the broader dangerouscentaur.com infrastructure pattern.

DNS Configuration

Route53 hosted zone for 86dfrom.com contains:

  • A record pointing to Vercel's default domain (e.g., 86dfrom-cb.vercel.app)
  • CNAME record aliasing to CloudFront distribution (fallback static delivery)
  • ACM validation CNAME records (temporary, removed post-validation)

TTL is set to 300 seconds for rapid iteration during development; production should increase to 3600–86400.

Stripe Webhook Endpoint

The /api/webhook.ts route is the critical production path. It must:

  • Accept POST requests with Content-Type: application/json
  • Validate the Stripe-Signature header using the webhook secret (environment variable STRIPE_WEBHOOK_SECRET)
  • Reject unsigned or tampered payloads with HTTP 401
  • Log all events to stdout (Vercel captures these in the Deployments tab)
  • Respond with HTTP 200 within 5 seconds of receiving a valid event

Once DNS is live, the webhook must be registered in the Stripe dashboard (Developers → Webhooks) pointing to https://86dfrom.com/api/webhook with events: payment_intent.succeeded, payment_intent.payment_failed, charge.refunded.

Environment Configuration

The .env.local file (local dev) and Vercel dashboard (production) must contain:

  • NEXT_PUBLIC_PRINTFUL_API_KEY