Deploying a Next.js 14 Print-on-Demand T-Shirt Site with Printful and Stripe Integration
Building a print-on-demand e-commerce site requires orchestrating multiple third-party services: a dynamic frontend, inventory management via API, and payment processing. This post documents the deployment of 86dfrom.com, a Next.js 14 application that integrates Printful's catalog and Stripe's payment infrastructure, from local development through production deployment on Vercel.
Project Architecture Overview
The application is structured as a Next.js 14 project with the App Router pattern, organizing code into route handlers and API endpoints:
- Frontend routes: Product pages, cart management, checkout flow
- API routes:
/api/variants(Printful sync),/api/checkout(Stripe sessions),/api/webhook(Stripe event handling) - Build target: Vercel serverless functions
- Environment config:
.env.localfor local development, Vercel environment variables for production
The project root is /Users/cb/Desktop/86dfrom/ with a clean git history and all dependencies pre-installed. A preliminary build confirmed all 5 routes compile without errors.
Technical Workflow: Credentials and Configuration
Step 1: Printful API Integration
Printful provides a REST API for querying product catalogs and variant inventory. To enable this, we needed a store-level API token from the Printful dashboard. The token grants scopes across all stores under the account, including the dedicated 86Store created for this project.
Once the API key was obtained, the next step was to populate variant IDs. The project includes a utility script at scripts/get-printful-variants.js that:
- Calls the Printful API endpoint for the Bella+Canvas 3001 Black t-shirt template
- Iterates through available sizes and color options
- Extracts variant IDs for SKU mapping in the product database
Running this script generates a JSON map of size-to-variant-ID lookups, which is then hardcoded into the application's product configuration at lib/printful-variants.js. This avoids repeated API calls at runtime and reduces latency during product page renders.
The script execution:
node scripts/get-printful-variants.js
produces output in the form:
{
"3001_black_s": 12345,
"3001_black_m": 12346,
"3001_black_l": 12347,
...
}
Step 2: Stripe Payment Configuration
Stripe credentials consist of two distinct key types:
- Secret keys (
sk_live_...orsk_test_...): Used server-side for creating payment intents, confirming charges, and verifying webhook signatures - Publishable keys (
pk_live_...orpk_test_...): Used client-side in JavaScript to initialize Stripe.js and submit payment forms - Webhook signing secret (
whsec_...): Shared between Stripe and the application to cryptographically verify webhook payloads
For this deployment, test mode keys were chosen initially to validate the integration without processing real payments. Keys are accessed via the Stripe Dashboard under Developers → API keys.
Step 3: Environment Variable Configuration
The application reads configuration from .env.local at build time and from Vercel's environment variable dashboard at runtime. The required variables are:
PRINTFUL_API_KEY=<store-level-token>
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_...
STRIPE_SECRET_KEY=sk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...
Note the NEXT_PUBLIC_ prefix on the publishable key: this tells Next.js to expose the variable to the browser at build time, which is necessary for client-side Stripe initialization. The secret key and webhook secret remain server-only and are never sent to clients.
The .env.local file is created locally and never committed to version control. Instead, it is added to .gitignore and injected into the Vercel build pipeline via the Vercel dashboard or CLI.
Deployment to Vercel
Vercel was chosen as the hosting platform because:
- Native Next.js support with zero-configuration builds
- Automatic preview deployments on pull requests
- Built-in edge caching via Vercel's global CDN
- Serverless function scaling for API routes
- Integrated environment variable and secrets management
The deployment workflow:
npx vercel@latest --prod
This command:
- Builds the Next.js application in production mode
- Bundles API routes as serverless functions
- Optimizes static assets and applies edge caching rules
- Deploys to Vercel's global network with a live URL
After deployment, the project is assigned a temporary Vercel URL (e.g., 86dfrom.vercel.app) and is ready to receive DNS records pointing the production domain.
DNS and Domain Configuration
Once the Vercel deployment is live, DNS records must be configured at the domain registrar to point 86dfrom.com to Vercel's nameservers. Vercel provides specific CNAME or A record instructions depending on the registrar. The exact records depend on whether the domain is registered at GoDaddy, Namecheap, or another registrar, but the general pattern is:
- CNAME record:
www.86dfrom.com → cname.vercel-dns.com(or similar) - A record:
86dfrom.com → 76.76.19.0(Vercel's static IP, if needed)
DNS propagation typically takes 5–30 minutes. Once live, HTTPS is automatically provisioned via Let's Encrypt, and traffic is routed to the Vercel deployment.
Stripe Webhook Configuration
After the production domain is live, a Stripe webhook must be registered to listen for payment events. This is configured in the Stripe Dashboard under Developers → Webhooks:
- Endpoint URL:
https://86dfrom.com/api/webhook - Events:
payment_intent.succeeded,payment_intent.payment_failed, andcharge.refunded
Stripe generates a webhook signing secret (whsec_...), which is then added to the Vercel environment variables as