How I wired Printify's API to a PayPal-only checkout for a print-on-demand shop (no Shopify, no Stripe)
Most print-on-demand tutorials assume you're building on Shopify with the Printify Shopify app, or on WooCommerce, or accepting Stripe. I wanted none of those — a static storefront, PayPal as the only payment rail, and Printify purely as a fulfillment API, no platform lock-in.
Here's the actual wiring behind Inkwell & Co, a small graphic-tee shop built this way.
Why skip the Shopify/Printify app integration
The standard Printify + Shopify integration is genuinely easy to set up, which is exactly why I didn't want it for this project — it couples your storefront to Shopify's checkout, theme system, and monthly fee regardless of order volume. For a single small catalog:
- Printify's REST API (
api.printify.com/v1) exposes products, variants, and order creation directly — no app-store middleman needed. - PayPal's REST API (Orders v2 + webhooks) handles checkout and capture without needing a full commerce platform underneath it.
- A static frontend + a couple of serverless functions is enough glue code to connect the two.
The order flow
customer checks out via PayPal (Orders v2 create + capture)
→ PayPal webhook fires on PAYMENT.CAPTURE.COMPLETED
→ webhook handler POSTs an order to Printify (/shops/{id}/orders.json)
with the captured shipping address + line items
→ Printify handles print + fulfillment + shipping
→ order status polled back for customer-facing tracking
The part that actually took iteration wasn't the happy path — it was getting the PayPal capture payload's shipping address mapped correctly onto Printify's expected order schema (Printify wants a fairly specific address object shape, and PayPal's capture response nests it differently depending on whether the buyer used a saved address or typed one in at checkout). Test both paths explicitly if you're building something similar; they don't look the same in the response.
What I'd do differently starting over
- Log the full raw PayPal webhook payload before transforming it, not after. The one bug that cost real time was an address field silently going empty for guest checkouts specifically — invisible until you have the raw payload to diff against.
- Printify's variant IDs are size/color-specific and not obvious from the product API alone — pull the full variants list once at setup and cache it, don't try to guess the mapping.
- A webhook retry from PayPal (they do retry on non-2xx) can create a duplicate Printify order unless the handler is idempotent on PayPal's capture ID. Worth building that guard in from the start rather than after the first duplicate print run.
Live shop: https://pod-shop.pages.dev/
If you've wired Printify to a non-Shopify checkout and hit different edge cases, I'd like to compare notes in the comments.
Top comments (0)