Building a Printful + Stripe T-Shirt Commerce Site: Infrastructure & Deployment for 86dfrom.com
This post documents the complete infrastructure and application setup for 86dfrom.com—a Next.js 14 t-shirt storefront with Printful print-on-demand integration and Stripe payments. We'll cover the architectural decisions, AWS/Vercel infrastructure, environment configuration strategy, and deployment pipeline.
Project Architecture Overview
The 86dfrom.com stack consists of:
- Frontend & API: Next.js 14 deployed to Vercel (production)
- Print Partner: Printful API for variant data and order fulfillment
- Payment Processing: Stripe (test and live modes supported)
- DNS & Static Assets: Route53 + S3 + CloudFront (for future static asset serving)
- Google Apps Script: Optional backend automation (gas/Code.gs)
The application structure mirrors a standard Next.js monorepo with clear separation of concerns:
86dfrom.com/
├── site/ # Static HTML + assets (pre-built option)
│ ├── index.html
│ └── success.html
├── gas/ # Google Apps Script backend (optional)
│ ├── Code.gs
│ └── appsscript.json
├── scripts/
│ ├── deploy.sh # S3 + CloudFront deployment
│ └── get-printful-variants.js # Variant ID fetcher
├── .env.local # Runtime secrets (git-ignored)
└── .clasp.json # GAS project config
API Integration: Printful Variant Population
The Printful integration centers on fetching variant IDs for the Bella+Canvas 3001 t-shirt. Rather than hardcoding variant IDs, we use the scripts/get-printful-variants.js script to query the Printful API and populate our application's product catalog.
Why this approach? Variant IDs can change when Printful updates their product database. By fetching at build/setup time, we ensure our inventory is always in sync with the print partner.
The script makes authenticated requests to the Printful API using a bearer token stored in .env.local:
// Pseudocode: variant discovery flow
const variants = await fetch('https://api.printful.com/products/{product_id}/variants', {
headers: { 'Authorization': 'Bearer <PRINTFUL_API_KEY>' }
});
// Filter to Black colorway (variants 4016–4020 across sizes XS–2XL)
const blackVariants = variants.filter(v => v.color === 'Black');
For this deployment, we targeted the standard Black Bella+Canvas 3001 across five sizes (XS, S, M, L, XL). Heather and oxblood colorways were excluded to simplify initial inventory and reduce SKU complexity.
Environment Configuration & Secrets Management
The .env.local file (git-ignored) centralizes all runtime secrets and feature flags. The structure supports both Stripe test and live modes:
# .env.local structure
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_... or pk_test_...
STRIPE_SECRET_KEY=sk_live_... or sk_test_...
PRINTFUL_API_KEY=UPQNIqzJkpoV2JPKKrhwYteCKzhipRnLHA2TxLnt
PRINTFUL_STORE_ID=<numeric store ID>
# Webhook endpoint (set after Vercel deployment)
STRIPE_WEBHOOK_SECRET=whsec_...
Why separate public and secret keys? The NEXT_PUBLIC_* prefix tells Next.js to expose these to the browser (safe for public keys like Stripe's publishable key). The secret key is server-side only, never exposed to the client.
Stripe supports parallel test/live configurations. In test mode, transactions use test card numbers (e.g., 4242 4242 4242 4242) and don't charge real cards. This allows full integration testing before going live.
Vercel Deployment Pipeline
The Next.js application is deployed to Vercel production via:
npx vercel@latest --prod
This command:
- Triggers a clean build of all 5 Next.js routes (verified locally with
npm run build) - Deploys compiled assets to Vercel's global CDN
- Provisions HTTPS automatically
- Requires Vercel project link (via
vercel linkor dashboard)
After deployment, environment variables are injected via the Vercel dashboard (Settings → Environment Variables). This keeps secrets out of git and allows rotation without code changes.
DNS & Domain Configuration
The 86dfrom.com domain uses Route53 for DNS management. After the Vercel deployment completes, we configure:
- Primary A/CNAME records: Point 86dfrom.com and www.86dfrom.com to Vercel's Edge Network
- ACM Certificate Validation: CNAME records validate SSL ownership (example:
_xxxxx.86dfrom.com → _yyyyy.acm-validations.aws.) - CloudFront Distribution (if serving static assets): Alternate domain alias for CDN distribution
Route53 hosted zone for 86dfrom.com contains these record sets (exact IDs omitted for security):
86dfrom.com A → Vercel Edge Network CNAMEwww.86dfrom.com CNAME → 86dfrom.com_acm-challenge.86dfrom.com CNAME → AWS Certificate Manager validation token
AWS Infrastructure for Static Assets (Future)
While the primary application runs on Vercel, we provisioned AWS infrastructure for optional static asset serving:
- S3 Bucket:
86dfrom.com(blocks public read; CloudFront is sole accessor) - CloudFront Distribution: Caches site/ assets globally with low latency
- ACM Certificate: TLS for
86dfrom.comalt domain - Route53 Records: Optional alias for CloudFront distribution
The scripts/deploy.sh script automates S3 uploads and CloudFront invalidation. This pattern mirrors existing deployments (e.g., dangerouscentaur.com infrastructure) but remains dormant until needed.
Stripe Webhook Configuration
After Vercel deployment goes live, the API endpoint /api/webhook receives Stripe events. Configuration steps:
- Open dashboard.stripe.com → Developers → Webhooks
- Create endpoint:
https://86dfrom.com/api/webhook