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) and86from.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 tohttps://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 formsSTRIPE_SECRET_KEY— Server-side Stripe key for payment intent creation and webhook validationPRINTFUL_API_KEY— Store token for Printful API callsSTRIPE_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/productsendpoint 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