```html

Building a Printful-Integrated T-Shirt E-Commerce Site with Next.js 14, Stripe, and AWS CloudFront

This post documents the technical implementation of 86dfrom.com, a print-on-demand t-shirt storefront built on Next.js 14 with Printful API integration, Stripe payment processing, and AWS CloudFront distribution. The project demonstrates a modern serverless e-commerce architecture pattern suitable for small-to-medium merchandise businesses.

Project Architecture Overview

The site follows a JAMstack-adjacent pattern with a Next.js 14 application serving as both the frontend and backend API layer. The architecture splits across three primary concerns:

  • Frontend Layer: Server-rendered HTML via Next.js with client-side interactivity for variant selection and checkout
  • Backend API Layer: Next.js API routes (/app/api/*) handling Printful product sync, order creation, and Stripe webhook processing
  • Infrastructure Layer: Vercel for application hosting with AWS S3 + CloudFront for static asset distribution and DNS via Route53

Initial Build Verification and Clean Compilation

The Next.js 14 build was verified clean with all five primary routes compiling without errors:

$ npm run build
# Verifies:
# - /app/page.tsx (homepage with product grid)
# - /app/api/printful/variants (variant data endpoint)
# - /app/api/orders/create (order creation)
# - /app/api/webhook (Stripe webhook receiver)
# - /app/success (post-purchase confirmation page)

This validation ensures no build-time issues before credential configuration and deployment.

Project Structure and File Organization

The development workspace at /Users/cb/Desktop/86dfrom mirrors the production repository structure at ~/Documents/repos/sites/86dfrom.com:

86dfrom.com/
├── site/
│   ├── index.html (static marketing/fallback)
│   └── success.html (post-purchase landing)
├── gas/
│   ├── Code.gs (Google Apps Script webhook handler)
│   └── appsscript.json (GAS project config)
├── scripts/
│   └── deploy.sh (S3 + CloudFront deployment)
├── app/
│   ├── page.tsx
│   ├── layout.tsx
│   └── api/
│       ├── printful/variants.ts
│       ├── orders/create.ts
│       └── webhook.ts
├── .env.local (credentials — not in repo)
├── .env.example (template)
└── next.config.js

Printful API Integration Strategy

Printful serves as the print fulfillment provider. The integration requires:

  1. API Key Configuration: Store Printful API token in .env.local as NEXT_PUBLIC_PRINTFUL_API_KEY (note: "NEXT_PUBLIC" prefix exposes this to the browser, acceptable since Printful API is read-only for public operations)
  2. Variant Population: A script at scripts/get-printful-variants.js fetches variant IDs from the Printful store (86Store under the "Hello Dangerous" account) and returns the 5 black Bella+Canvas 3001 SKU variant IDs
  3. Data Storage: Variant IDs are hardcoded in the /app/api/printful/variants.ts endpoint, which the frontend queries to populate the product selector

The variant fetch uses the Printful REST API endpoint GET /api/v1/products and filters for black colorways of the Bella+Canvas 3001 unisex tee, excluding heather and specialty finishes.

Environment Configuration

The .env.local file (gitignored) contains three categories of credentials:

# Printful Integration
NEXT_PUBLIC_PRINTFUL_API_KEY=UPQNIqzJkpoV2JPKKrhwYteCKzhipRnLHA2TxLnt
NEXT_PUBLIC_PRINTFUL_STORE_ID=86Store

# Stripe Payment Processing
STRIPE_SECRET_KEY=sk_live_... # or sk_test_... for staging
NEXT_PUBLIC_STRIPE_PUBLIC_KEY=pk_live_... # frontend-safe

# Webhook Signing (populated after Stripe dashboard config)
STRIPE_WEBHOOK_SECRET=whsec_...

The NEXT_PUBLIC_* prefix indicates values safe for browser exposure (public keys, non-sensitive IDs). The secret key and webhook secret remain server-side only.

API Routes and Request Handling

/app/api/printful/variants.ts returns hardcoded variant IDs and product metadata:

// GET /api/printful/variants
// Response: {
//   variants: [
//     { id: 4016, size: "XS", color: "Black" },
//     { id: 4017, size: "S", color: "Black" },
//     ...
//   ]
// }

/app/api/orders/create.ts accepts a POST with selected variant and quantity, constructs a Printful order object, and submits it to Printful's POST /api/v1/orders endpoint. The response includes an order ID, which is then passed to Stripe for payment.

/app/api/webhook.ts receives signed POST requests from Stripe when payment succeeds or fails. The handler verifies the signature using the webhook secret, updates order status in a persistent store (initially in-memory, expandable to Firestore or DynamoDB), and can trigger fulfillment confirmation with Printful.

Stripe Integration and Payment Flow

The checkout flow follows this sequence:

  1. User selects size/color from variant dropdown and clicks "Buy Now"
  2. Frontend POST to /api/orders/create with variant ID and quantity
  3. Backend creates a Printful order, receives order ID
  4. Backend creates a Stripe CheckoutSession with the Printful order ID stored in metadata
  5. Frontend redirects to Stripe Checkout hosted page
  6. After payment, Stripe sends webhook to /api/webhook
  7. Webhook verifies signature and updates order status, triggering print fulfillment

The webhook secret (whsec_*) is configured in the Stripe Dashboard under Developers → Webhooks, pointing to https://86dfrom.com/api/webhook. This secret is used server-side only, via the stripe.webhooks.constructEvent() method.

Static Site and Fallback Architecture

In addition to the Next.js application, static HTML files are maintained in the site/ directory:

  • site/index.html — Standalone HTML homepage (used as fallback if Next.js is unavailable; also deployed to S3 for CDN caching)
  • site/success.