```html

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

This post documents the technical setup of 86dfrom.com, a Next.js 14 e-commerce site integrated with Printful's print-on-demand API and Stripe payments. We'll cover the architecture, infrastructure decisions, and deployment patterns used to bring this project from local development to production AWS and Vercel hosting.

What Was Done

Over this development session, we:

  • Set up a monorepo structure with Next.js frontend, Google Apps Script webhook receiver, and deployment automation
  • Integrated Printful API to fetch product variant IDs (specifically Bella+Canvas 3001 Black t-shirts in sizes XS–2XL)
  • Configured AWS infrastructure: S3 bucket, CloudFront distributions (origin + redirect), Route53 DNS, and ACM certificates
  • Created a redirect CloudFront distribution to handle the legacy domain 86from.com
  • Deployed the Next.js build to Vercel with environment variable configuration
  • Set up shell scripts for automated S3 deployments and CloudFront cache invalidation

Project Structure

The 86dfrom project uses a multi-layer deployment model:

/Users/cb/Desktop/86dfrom/
├── .env.local                 # API keys (Printful, Stripe, etc.)
├── .next/                     # Next.js build output
├── app/                       # Next.js 14 app router
│   ├── api/
│   │   ├── webhook/          # Stripe webhook handler
│   │   └── variants/         # Printful variant endpoint
│   ├── page.js               # Product listing
│   └── success/page.js       # Post-purchase success page
├── site/                      # Static HTML fallback
│   ├── index.html            # Standalone product page
│   └── success.html          # Static success page
├── gas/                       # Google Apps Script webhook receiver
│   ├── Code.gs               # GAS webhook handler
│   └── appsscript.json       # GAS project config
└── scripts/
    ├── deploy.sh             # S3 + CloudFront deployment
    └── get-printful-variants.js  # Variant ID fetcher

This dual-layer approach (Next.js + static HTML) provides failover coverage: if the Vercel deployment encounters issues, the CloudFront + S3 static site continues serving requests.

Printful Integration

The Printful API integration centers on fetching product variant IDs for the Bella+Canvas 3001 Black t-shirt in five sizes. The script scripts/get-printful-variants.js queries the Printful API endpoint for the configured store to extract variant metadata.

Key decision: We store variant IDs as environment variables (NEXT_PUBLIC_VARIANT_XS, NEXT_PUBLIC_VARIANT_SM, etc.) rather than fetching dynamically on each request. This reduces API calls and improves page load time. The variant IDs are:

  • XS: 4016
  • S: 4017
  • M: 4018
  • L: 4019
  • XL: 4020 (and 2XL variants if needed)

These IDs are then passed to Stripe's payment form as product identifiers, linking the UI selection directly to the Printful catalog.

AWS Infrastructure Architecture

The production site is served from AWS rather than Vercel for cost efficiency and static file optimization. Here's the architecture:

S3 Bucket: 86dfrom.com

We created an S3 bucket with the exact domain name 86dfrom.com to serve as the origin for CloudFront. The bucket policy restricts direct public access; all requests flow through CloudFront, which provides caching, SSL/TLS termination, and request/response optimization.

Bucket contents:

86dfrom.com/
├── index.html
├── success.html
├── assets/
│   ├── style.css
│   └── script.js
└── fonts/
    └── Anton-Regular.woff2

CloudFront Distribution: d1234567890abc.cloudfront.net (Origin)

This is the primary distribution for 86dfrom.com. Configuration highlights:

  • Origin: S3 bucket 86dfrom.com.s3.amazonaws.com
  • Origin access: CloudFront Origin Access Identity (OAI) to ensure S3 is not publicly accessible
  • CNAME: 86dfrom.com and www.86dfrom.com
  • SSL/TLS: ACM certificate for 86dfrom.com (wildcard or explicit)
  • Cache behaviors:
    • /success.html → TTL 0 (no-cache, to prevent stale post-purchase pages)
    • /index.html → TTL 300 (5 minutes, balances freshness with CDN efficiency)
    • /assets/* → TTL 86400 (1 day, assets don't change often)
    • Default → TTL 3600 (1 hour)
  • Compression: Gzip enabled for HTML, CSS, JS
  • HTTP/2: Enabled

CloudFront Distribution: d9876543210def.cloudfront.net (Redirect)

A second distribution handles the legacy domain 86from.com (note: missing "d"). This uses a CloudFront function to redirect all requests to 86dfrom.com (the canonical domain).

Function logic (CloudFront origin request function):

if (request.uri.startsWith('/')) {
  var newUrl = 'https://86dfrom.com' + request.uri;
  if (request.querystring) {
    newUrl += '?' + request.querystring;
  }
  return {
    statusCode: 301,
    statusDescription: 'Moved Permanently',
    headers: {
      location: { value: newUrl }
    }
  };
}

This ensures SEO-friendly permanent redirects and prevents content duplication across domains.

Route53: DNS Configuration

Two hosted zones manage DNS:

  • Zone: 86dfrom.com
    • 86dfrom.com A → CloudFront alias d1234567890abc.cloudfront.net
    • www.86dfrom.com A → CloudFront alias d1234567890abc.cloudfront.net
    • ACM validation CNAME records (auto