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:
- Query the Printful API endpoint
https://api.printful.com/stores/[STORE_ID]/products - Filter for Bella+Canvas 3001 variants
- Extract variant IDs and size mappings
- Output them for hardcoding into
.env.localor 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:
- Primary (86dfrom.com): Origin points to S3 bucket. Uses wildcard TLS cert (ACM, validated via Route53 CNAME).
- 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:
Arecord (apex): points to CloudFront distribution aliaswwwCNAME: aliases to distributionCNAMEvalidation 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.completedevents - On success: creates a Printful order and logs to database (TBD)
- Returns
200 OKto 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