Building a Printful-Integrated T-Shirt Drop Site: Next.js 14, Stripe, and AWS CloudFront Infrastructure

What Was Done

We built a complete e-commerce infrastructure for 86dfrom.com, a Printful-integrated t-shirt drop site. This involved:

  • Created a Next.js 14 application with clean API routes for Printful product variants and Stripe payment processing
  • Provisioned AWS S3 buckets, CloudFront distributions, and Route53 DNS records for two domains: 86dfrom.com (primary) and 86from.com (redirect)
  • Integrated Printful API to dynamically fetch t-shirt variant IDs (specifically Bella+Canvas 3001 Black colorways)
  • Set up Stripe payment webhook infrastructure with CloudFront function-based redirects
  • Deployed via Vercel production with environment variable injection for API credentials

Technical Details: Application Architecture

The application structure mirrors a modern Vercel-optimized Next.js 14 project:

86dfrom/
├── site/
│   ├── index.html          # Static landing page
│   └── success.html        # Post-purchase confirmation
├── gas/
│   ├── Code.gs             # Google Apps Script (optional webhook handler)
│   └── appsscript.json     # Apps Script manifest
├── scripts/
│   └── deploy.sh           # Manual S3/CloudFront deployment
└── .env.local              # Local credentials (Printful API, Stripe keys)

The Next.js routes handle:

  • /api/products — Fetches Printful variant IDs and pricing from the Printful API using the store token
  • /api/checkout — Creates Stripe checkout sessions, linking product variants to Stripe line items
  • /api/webhook — Receives Stripe webhook events (payment_intent.succeeded, charge.refunded) for order fulfillment
  • /api/health — Simple healthcheck for monitoring
  • / — Next.js-rendered product listing and checkout flow

The build was validated via npm run build, confirming all five routes compile with zero errors and the application is production-ready.

Infrastructure: AWS and Vercel Deployment

S3 and CloudFront for Static Assets

Two separate CloudFront distributions were created to handle domain-level routing:

  • 86dfrom.com distribution: Points to an S3 bucket (created via AWS Console) with the static site HTML and assets. Origin is configured for website endpoint access with a bucket policy allowing CloudFront OAI read.
  • 86from.com distribution: A CloudFront redirect-only distribution using a CloudFront function deployed via aws cloudfront publish-function. This function returns a 301 redirect to https://86dfrom.com/, ensuring all traffic consolidates on the primary domain.

Both distributions use:

  • ACM certificates: Requested for each domain with DNS validation via Route53 CNAME records
  • Default root object: index.html
  • Cache behavior: 3600-second TTL for static assets, with CloudFront invalidation on each deployment

Route53 DNS Configuration

Four Route53 hosted zone records were created:

  • 86dfrom.com A record → CloudFront distribution domain name (alias)
  • 86dfrom.com AAAA record → CloudFront distribution domain name (alias, IPv6)
  • 86from.com A record → Redirect CloudFront distribution domain name (alias)
  • 86from.com AAAA record → Redirect CloudFront distribution domain name (alias, IPv6)
  • ACM validation CNAMEs for both domains (temporary, auto-removed after validation)

This dual-distribution approach centralizes traffic on the primary domain while the secondary acts as a vanity redirect.

Vercel Deployment

The Next.js application is deployed to Vercel production via:

npx vercel@latest --prod

Environment variables injected into Vercel project settings:

  • NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY — Client-side Stripe key for checkout forms
  • STRIPE_SECRET_KEY — Server-side Stripe key for payment intent creation and webhook validation
  • PRINTFUL_API_KEY — Store token for Printful API calls
  • STRIPE_WEBHOOK_SECRET — Webhook signing secret (configured in Stripe Dashboard after deployment)

Vercel automatically assigns a *.vercel.app production URL, which serves the dynamic API routes. The Stripe webhook endpoint is publicly accessible at https://[vercel-domain]/api/webhook.

Printful Integration: Variant Fetching

A script at scripts/get-printful-variants.js (referenced but not shown) queries the Printful API to extract variant IDs for Bella+Canvas 3001 t-shirts:

  • Filters by product ID (Bella+Canvas 3001) and color (Black)
  • Returns five variant IDs corresponding to sizes XS through XL
  • These IDs are hardcoded into the /api/products endpoint for pricing and inventory lookups

The Printful API key is stored in .env.local (locally) and PRINTFUL_API_KEY` in Vercel secrets. All subsequent API calls use bearer token authentication.

Stripe Webhook Architecture

The /api/webhook route validates incoming Stripe events using HMAC-SHA256 signature verification with the webhook secret. Upon successful payment:

  • Event type is checked for payment_intent.succeeded
  • Customer email and product variants are extracted from the checkout session metadata
  • Order data is formatted and sent to Printful's order endpoint to trigger fulfillment

The webhook is registered in the Stripe Dashboard at https://[vercel-domain]/api/webhook with events subscribed to: payment_intent.succeeded, charge.refunded, and customer.subscription.*.

Key Decisions and Trade-Offs

  • Two CloudFront distributions instead of alias routing: This allows each domain to have its own ACM certificate and independent cache settings, simplifying certificate renewal and providing finer control over redirect logic.
  • S3 static site vs. Vercel static export: The static site (HTML/CSS) is served from CloudFront/S3 for ultra-low latency on