Building a Printful-Integrated T-Shirt Store on Next.js 14: Infrastructure, Stripe Integration, and Multi-Domain Deployment
What Was Done
We built a complete e-commerce storefront for 86dfrom.com (with redirect from 86from.com) using Next.js 14, Printful's API for inventory management, and Stripe for payment processing. The project spans frontend (React components + HTML/CSS), backend (Google Apps Script for webhook processing), and infrastructure (AWS S3, CloudFront, Route53, ACM certificates, and Vercel for the main app).
This session focused on establishing the deployment pipeline, configuring DNS across multiple domains, and preparing the Printful API integration to power dynamic product variants.
Project Structure & Architecture
The codebase lives at ~/Documents/repos/sites/86dfrom.com/ with the following layout:
/site/— Static HTML landing page (served via S3 + CloudFront for 86from.com redirect)/gas/— Google Apps Script webhook handler (Code.gs,appsscript.json)/scripts/— Deployment automation, includingdeploy.sh- Next.js 14 app root — API routes at
/api/webhook,/api/variants, etc.
The Next.js build compiles cleanly with all 5 core routes verified:
npm run build # Confirms zero errors
Infrastructure: S3, CloudFront & Route53 Configuration
Primary Domain (86dfrom.com)
We created a dedicated S3 bucket 86dfrom.com to serve static assets and created a CloudFront distribution with origin pointing to the S3 bucket. The distribution was configured with:
- Origin: S3 bucket with origin access identity (OAI) for encrypted access
- Caching policy: Default CloudFront managed policy (TTL: 24 hours for HTML, 365 days for versioned assets)
- Compression: Enabled for HTML, CSS, JavaScript
- TLS version: TLSv1.2_2021 minimum
ACM certificate for 86dfrom.com was requested with DNS validation via Route53. We added CNAME records provided by AWS ACM into the hosted zone:
aws route53 change-resource-record-sets \
--hosted-zone-id Z... \
--change-batch file://validation.json
After validation completed, Route53 A record was created pointing to the CloudFront distribution's alias target.
Redirect Domain (86from.com)
A second S3 bucket 86from.com was created with HTTP redirect enabled (301 permanent redirect to 86dfrom.com
ACM certificate for 86from.com was validated via DNS in Route53, and a Route53 A record was created pointing to the redirect CloudFront distribution.
Both distributions use CloudFront Functions for request rewriting where needed, keeping origin logic clean and cache-friendly.
Deployment Pipeline
Static Site Deployment (S3 + CloudFront)
The scripts/deploy.sh script automates pushing the /site directory to S3 and invalidating the CloudFront cache:
#!/bin/bash
aws s3 sync ./site s3://86dfrom.com --delete
aws cloudfront create-invalidation --distribution-id E... --paths "/*"
This approach ensures atomic updates: S3 files are uploaded atomically, then CloudFront's edge caches are purged immediately, guaranteeing global CDN freshness within seconds.
Next.js App Deployment (Vercel)
The main application is deployed to Vercel for serverless function execution and integrated CI/CD:
npx vercel@latest --prod --token $VERCEL_TOKEN
Environment variables (Printful API key, Stripe keys, webhook secrets) are managed via Vercel's dashboard, not committed to version control. The .env.local file (which contains development secrets) is gitignored.
Printful API Integration
Printful serves as the inventory and fulfillment backend. The 86Store account (scoped to the dangerouscentaur.com parent account) exposes Bella+Canvas 3001 Black t-shirts in variants across 4 colors and 5 sizes (20 total SKUs).
A Node.js script scripts/get-printful-variants.js queries the Printful API to fetch all variant IDs:
// Example fetch structure (credentials passed via env vars)
const variants = await fetch('https://api.printful.com/store/86Store/products', {
headers: { 'Authorization': `Bearer ${PRINTFUL_API_KEY}` }
});
Variant IDs (4016–4020) are stored in the Next.js app's environment and referenced in API routes that calculate pricing, inventory status, and shipping estimates. This decouples inventory logic from the frontend, allowing real-time stock updates.
Stripe Payment Integration
Stripe is configured for payment collection. The architecture includes:
- Public key: Embedded in frontend for Stripe.js tokenization (PCI-DSS compliance)
- Secret key: Stored in Vercel environment, used only in
/api/webhookand checkout routes - Webhook endpoint:
https://86dfrom.com/api/webhooklistens forcharge.succeeded,charge.failedevents
The webhook secret (obtained after setting up Stripe's endpoint in the Stripe dashboard) is stored in Vercel as STRIPE_WEBHOOK_SECRET and validated on each incoming request to prevent replay attacks.
Google Apps Script Integration
A secondary webhook handler in Google Apps Script (deployed from /gas/Code.gs) provides fallback order notification and Sheets logging. The appsscript.json manifest configures:
- Script ID: Unique identifier for deployment versioning
- Permissions: Google Sheets, Gmail for order summaries
- Timeouts: 30-second maximum execution (constraint of Apps Script)
This script is triggered as a secondary webhook (fire-and-forget pattern) and logs orders to a spreadsheet for audit purposes, decoupled from the critical checkout path.
Key Technical Decisions
- CloudFront Functions vs. Lambda@Edge: We chose CloudFront Functions for the 86from.com redirect (301 rewrite) due to lower latency (executes