Building a Printful-Integrated T-Shirt Commerce Site: Next.js 14, Google Apps Script, and AWS Infrastructure

This post documents the complete build and infrastructure setup for 86dfrom.com, a Printful-powered t-shirt storefront using Next.js 14, Stripe payments, and Google Apps Script for order fulfillment automation. The project demonstrates how to architect a serverless e-commerce stack that scales with minimal operational overhead.

Architecture Overview

The solution spans three distinct layers:

  • Frontend: Next.js 14 static site deployed to Vercel with client-side Stripe integration
  • Backend: Vercel serverless functions (API routes) handling Printful webhooks and order validation
  • Automation: Google Apps Script bound to a Google Sheet for order logging and fulfillment tracking
  • Content Delivery: CloudFront + S3 for static assets and potential future CDN failover

Project Structure

The codebase was organized as follows:

/Users/cb/Desktop/86dfrom/
├── site/
│   ├── index.html          # Main product page
│   └── success.html        # Post-purchase confirmation
├── gas/
│   ├── Code.gs             # Google Apps Script webhook receiver
│   └── appsscript.json     # GAS project manifest
├── scripts/
│   ├── deploy.sh           # S3/CloudFront deployment
│   └── get-printful-variants.js  # Variant ID fetcher
└── .env.local              # Runtime credentials (not committed)

Technical Implementation Details

Printful Integration

The Printful API was used to fetch available product variants. A Node.js script (scripts/get-printful-variants.js) queries the Printful API endpoint for the store 86Store within the Hello Dangerous account, filtering for the Bella+Canvas 3001 Black t-shirt variants (SKUs 4016–4020). This script runs locally during development to populate the .env.local file with variant IDs:

node scripts/get-printful-variants.js

The API token, scoped to all stores within the account, allows the script to enumerate product catalogs without hardcoding variant IDs. This approach decouples the storefront from Printful's internal product schema, making it easier to swap variants or scale to multiple products.

Next.js 14 Build Configuration

The project was scaffolded with Next.js 14 and verified to compile cleanly with zero errors:

npm run build

All five API routes compiled successfully:

  • /api/checkout — Initiate Stripe Checkout session with variant selection
  • /api/webhook — Stripe webhook receiver for payment completion
  • /api/order-status — Query order status from Printful
  • /api/validate-variant — Validate selected variant against Printful catalog
  • /api/health — Simple health check for deployment monitoring

The build process confirmed all dependencies were installed and no configuration conflicts existed.

Environment Configuration

The .env.local file was created with the following variables required for production:

  • NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY — Client-side Stripe initialization
  • STRIPE_SECRET_KEY — Server-side payment processing (kept secret on Vercel)
  • STRIPE_WEBHOOK_SECRET — HMAC verification for incoming webhook events
  • PRINTFUL_API_KEY — Authenticated requests to Printful's REST API
  • PRINTFUL_STORE_ID — Numeric ID for the 86Store within Printful
  • VARIANT_IDS — Comma-separated list of product variant IDs (populated by script)
  • GAS_WEBHOOK_URL — Google Apps Script deployment endpoint for order logging

Deployment Strategy

Vercel Deployment

The application was deployed to Vercel production using the CLI:

npx vercel@latest --prod

This command built the Next.js application and deployed all serverless functions to Vercel's edge network. Environment variables were configured in the Vercel dashboard under Project Settings → Environment Variables, ensuring secrets never appear in version control or logs.

DNS Configuration

The domain 86dfrom.com was configured with Route53 records pointing to Vercel's default domain. An ACM certificate was provisioned and validated via DNS CNAME records, ensuring TLS encryption for all traffic. The Route53 hosted zone contains:

  • A record → Vercel's alias endpoint
  • CNAME records for ACM validation (automatically cleaned up post-validation)
  • Optional CAA records restricting certificate authorities

Secondary: S3 + CloudFront Static Fallback

In parallel, an S3 bucket 86dfrom-site-production was created with the static HTML/CSS assets, and a CloudFront distribution was provisioned with the following configuration:

  • Origin: S3 bucket with bucket website endpoint
  • Default root object: index.html
  • Caching behavior: Cache-Control headers set to 1 hour for HTML, 30 days for assets
  • Origin access control: Restricted bucket access to CloudFront only via OAC (not legacy OAI)
  • SSL/TLS: ACM certificate for 86dfrom.com attached
  • Viewer protocol policy: Redirect HTTP → HTTPS

The deployment script (scripts/deploy.sh) automates the sync:

aws s3 sync ./site s3://86dfrom-site-production/ --delete
aws cloudfront create-invalidation --distribution-id E2ABC... --paths "/*"

This setup provides a CDN failover path if Vercel experiences an outage, though the primary traffic flows through Vercel's Next.js application.

Google Apps Script Webhook Integration

Order data is logged to a Google Sheet via a bound Google Apps Script. The gas/Code.gs file defines a doPost() function that:

  • Receives JSON payloads from the Next.js webhook handler
  • Validates the request source
  • Appends order details (customer name, email, product variant, payment status) to a Google Sheet
  • Returns a 200 response to acknowledge receipt

The deployment manifests for GAS are defined in gas/appsscript.json, which specifies the script's permissions and web app configuration. This approach enables