- Add /admin/providers LiveView for connecting and managing POD providers - Implement pagination for Printify API (handles all products, not just first page) - Add parallel processing (5 concurrent) for faster product sync - Add slug-based fallback matching when provider_product_id changes - Add error recovery with try/rescue to prevent stuck sync status - Add checksum-based change detection to skip unchanged products - Add upsert tests covering race conditions and slug matching - Add Printify provider tests - Document Printify integration research (product identity, order risks, open source vs managed hosting implications) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
324 lines
10 KiB
Markdown
324 lines
10 KiB
Markdown
# Printify Integration Research
|
|
|
|
> Research notes from investigating Printify API integration, product syncing, and order submission risks. This document captures findings for future implementation work.
|
|
|
|
## Product Identity & Matching
|
|
|
|
### What identifiers exist?
|
|
|
|
| Field | Stable? | Notes |
|
|
|-------|---------|-------|
|
|
| `id` (provider_product_id) | ❌ | Changes if product deleted & recreated |
|
|
| `sku` (variant level) | ❌ | Auto-generated per product creation |
|
|
| `blueprint_id` | ✅ | The base product type (e.g., Gildan 5000) |
|
|
| `print_provider_id` | ✅ | The fulfillment provider |
|
|
| `title` | ✅ | User-defined, stable unless edited |
|
|
|
|
### Our matching strategy
|
|
|
|
1. **Primary**: Match by `provider_product_id`
|
|
2. **Fallback**: Match by `slug` (derived from title)
|
|
3. **Potential improvement**: Match by `blueprint_id + print_provider_id + title`
|
|
|
|
The slug fallback handles cases where Printify product IDs change (e.g., product deleted and recreated with same title).
|
|
|
|
### How Shopify/Printify official integration works
|
|
|
|
- SKU is generated once when first published to Shopify
|
|
- SKU becomes the stable link for future updates
|
|
- Republishing matches by SKU
|
|
- The "publish lock" prevents editing after publishing
|
|
|
|
## Duplicate Products
|
|
|
|
### When duplicates occur
|
|
|
|
- Mock/test product generators run multiple times
|
|
- User accidentally creates products twice in Printify
|
|
- Migration scenarios with orphaned products
|
|
- Using multiple POD providers on same store
|
|
|
|
### How we detect duplicates
|
|
|
|
Same `title` (and therefore same `slug`) but different `provider_product_id`.
|
|
|
|
In our test case, Printify returned 30 products but only 16 unique titles - every product existed twice with different IDs but identical:
|
|
- `title`
|
|
- `blueprint_id`
|
|
- `print_provider_id`
|
|
|
|
### How we handle duplicates
|
|
|
|
Our `upsert_product/2` function matches by slug when provider_product_id lookup fails, treating them as the same product and updating the provider_product_id to the latest value.
|
|
|
|
### Is this a common real-world issue?
|
|
|
|
Research suggests **no** - duplicate products aren't commonly reported. Main sync issues are:
|
|
- Orders not syncing
|
|
- Store connection expiring
|
|
- Publishing failures
|
|
- Product info being overwritten on republish
|
|
|
|
The official Shopify integration handles product identity well through SKUs.
|
|
|
|
## Sales Channel Integration Options
|
|
|
|
### Option 1: Personal API Token (current)
|
|
|
|
- Pull products via API
|
|
- Works immediately, no approval needed
|
|
- We don't appear in Printify's "Publish to..." UI
|
|
- Products not "locked" after sync
|
|
|
|
### Option 2: OAuth Platform Integration
|
|
|
|
- Apply at printify.com/printify-api
|
|
- ~1 week approval process
|
|
- Merchants authorize SimpleShop via OAuth
|
|
- We appear in Printify's publishing UI alongside Shopify/Etsy
|
|
- Products get "published" TO us with lock
|
|
- Webhooks for real-time updates
|
|
|
|
**How publishing works in Option 2:**
|
|
1. Merchant clicks "Publish to SimpleShop" in Printify
|
|
2. Printify sends `product:publish:started` webhook
|
|
3. We create the product on our side
|
|
4. We call `publishing_succeeded.json` to confirm
|
|
5. Product locked in Printify, shows as "Published to SimpleShop"
|
|
|
|
**Benefits of official integration:**
|
|
- Publish lock prevents editing after publishing (data consistency)
|
|
- Real-time webhooks
|
|
- "Official" feel for merchants
|
|
- Better UX in Printify dashboard
|
|
|
|
**Downsides:**
|
|
- Approval process
|
|
- More complex OAuth auth
|
|
- Ongoing partnership relationship
|
|
|
|
## Order Submission Risks
|
|
|
|
### How order submission works
|
|
|
|
We send to Printify:
|
|
```elixir
|
|
%{
|
|
product_id: "printify_product_id",
|
|
variant_id: 12345,
|
|
quantity: 2
|
|
}
|
|
```
|
|
|
|
We **do not send price**. Printify charges their current wholesale cost.
|
|
|
|
### Risk scenarios
|
|
|
|
| Scenario | Result | Who bears cost? |
|
|
|----------|--------|-----------------|
|
|
| Wholesale cost increased | Order succeeds | Shop owner (you) |
|
|
| Wholesale cost decreased | Order succeeds | Shop owner profits more |
|
|
| Variant discontinued | Order **fails** | Customer experience |
|
|
| Product deleted | Order **fails** | Customer experience |
|
|
| Design changed | Order succeeds with new design | Customer gets wrong item |
|
|
|
|
### Why the publish lock matters
|
|
|
|
In official integrations, the publish lock prevents merchants from editing products after publishing. This ensures:
|
|
- Price consistency between storefront and fulfillment cost
|
|
- Variant availability guaranteed
|
|
- Design matches what customer saw
|
|
|
|
Without the lock (our current approach), products can change between sync and order.
|
|
|
|
### Recommended mitigations
|
|
|
|
1. **Webhooks / frequent polling**
|
|
- Subscribe to `product:updated`, `product:deleted` events
|
|
- Or poll every N minutes for changes
|
|
- Update local product data immediately
|
|
|
|
2. **Pre-checkout validation**
|
|
- Before completing checkout, call Printify API
|
|
- Verify variant still exists
|
|
- Get current cost, compare to stored cost
|
|
- Alert or block if significant difference
|
|
|
|
3. **Cost monitoring**
|
|
- Store `cost` from Printify on each variant
|
|
- During sync, compare old vs new cost
|
|
- Alert shop owner if cost increased significantly
|
|
- Consider auto-adjusting retail price
|
|
|
|
4. **Graceful order failure handling**
|
|
- Catch "variant not found" errors
|
|
- Notify customer immediately
|
|
- Offer refund or alternative
|
|
- Don't leave order in limbo
|
|
|
|
5. **Stale data warnings**
|
|
- Track `last_synced_at` per product
|
|
- Warn in admin if product not synced recently
|
|
- Consider blocking orders for very stale products
|
|
|
|
## API Rate Limits
|
|
|
|
From Printify documentation:
|
|
- 600 requests/minute global
|
|
- 100 requests/minute for catalog endpoints
|
|
- 50 products per page max
|
|
- Product publishing: 200 requests per 30 minutes
|
|
|
|
## Implementation Status
|
|
|
|
### Completed
|
|
- [x] Product sync with pagination
|
|
- [x] Parallel processing (5 concurrent)
|
|
- [x] Slug-based fallback matching
|
|
- [x] Error recovery (try/rescue)
|
|
- [x] Checksum-based change detection
|
|
|
|
### Not yet implemented
|
|
- [ ] Webhook endpoint for real-time updates
|
|
- [ ] Pre-checkout variant validation
|
|
- [ ] Cost change monitoring/alerts
|
|
- [ ] OAuth platform integration (requires Printify approval)
|
|
- [ ] `blueprint_id + print_provider_id` matching
|
|
|
|
## Open Source vs Managed Hosting Considerations
|
|
|
|
### The core tension
|
|
|
|
SimpleShop exists in two forms:
|
|
1. **Open source** - self-hosted by anyone
|
|
2. **Managed hosting** - SaaS service run by us
|
|
|
|
Printify's integration options have different implications for each.
|
|
|
|
### Personal API Token (current approach)
|
|
|
|
**How it works:**
|
|
- Each merchant creates their own Printify API token
|
|
- Token entered in SimpleShop admin
|
|
- Direct API access, no intermediary
|
|
|
|
**Open source:** ✅ Works perfectly
|
|
- Each self-hosted instance uses merchant's own token
|
|
- No central service required
|
|
- Full functionality
|
|
|
|
**Managed hosting:** ✅ Works perfectly
|
|
- Same as self-hosted
|
|
- Each tenant uses their own token
|
|
- No special relationship with Printify needed
|
|
|
|
**Downsides:**
|
|
- Merchants must manually create/manage API tokens
|
|
- No "Connect with Printify" button UX
|
|
- Products not locked after sync (data consistency risk)
|
|
- No real-time webhooks (must poll or manual sync)
|
|
|
|
### OAuth Platform Integration
|
|
|
|
**How it works:**
|
|
- Register SimpleShop as an app with Printify
|
|
- Get OAuth client ID/secret
|
|
- Merchants click "Connect" and authorize
|
|
- We receive access tokens, appear in Printify UI
|
|
|
|
**Open source:** ❌ Problematic
|
|
- OAuth requires registered callback URLs
|
|
- Can't register infinite self-hosted domains
|
|
- Would need to proxy through a central service
|
|
- Defeats the "fully self-hosted" value proposition
|
|
|
|
**Managed hosting:** ✅ Works well
|
|
- Single registered callback URL
|
|
- "Connect with Printify" button
|
|
- Appears as official integration
|
|
- Real-time webhooks
|
|
- Publish lock for data consistency
|
|
|
|
### Hybrid approach possibilities
|
|
|
|
**Option A: Token for open source, OAuth for managed**
|
|
- Open source uses personal API tokens (current)
|
|
- Managed hosting uses OAuth integration
|
|
- Two code paths, more maintenance
|
|
- Clear value differentiation
|
|
|
|
**Option B: Webhook proxy service**
|
|
- Register OAuth app
|
|
- Self-hosted instances connect through managed webhook proxy
|
|
- Proxy forwards webhooks to self-hosted URLs
|
|
- Adds dependency on central service
|
|
- Could be free tier for open source users
|
|
|
|
**Option C: OAuth with self-registration**
|
|
- Document how to register own OAuth app with Printify
|
|
- Self-hosters go through Printify approval themselves
|
|
- Complex, unlikely many would do this
|
|
- Each instance is independent
|
|
|
|
### Business model implications
|
|
|
|
| Approach | Open Source | Managed Hosting |
|
|
|----------|-------------|-----------------|
|
|
| Personal API Token | Full feature parity | No differentiation |
|
|
| OAuth (managed only) | Basic sync only | Premium "official" integration |
|
|
| Webhook proxy | Depends on proxy | Full features |
|
|
|
|
**Potential differentiation for managed hosting:**
|
|
- Official "Publish to SimpleShop" in Printify UI
|
|
- Real-time sync via webhooks
|
|
- Publish lock (data consistency guarantee)
|
|
- Pre-checkout validation (verify before order)
|
|
- No token management for merchants
|
|
|
|
**What open source keeps:**
|
|
- Full product sync (polling-based)
|
|
- Order submission
|
|
- All admin features
|
|
- Self-hosted independence
|
|
|
|
### Printify partnership considerations
|
|
|
|
**What registering as an app might require:**
|
|
- Application/approval process (~1 week)
|
|
- Technical integration review
|
|
- Ongoing partnership relationship
|
|
- Support obligations?
|
|
- Usage/volume commitments?
|
|
|
|
**Questions to research:**
|
|
- [ ] What are Printify's requirements for app partners?
|
|
- [ ] Are there fees or revenue sharing?
|
|
- [ ] Can we register once for managed hosting only?
|
|
- [ ] What support/SLA obligations exist?
|
|
- [ ] Can app be limited to specific redirect URIs (managed only)?
|
|
|
|
### Recommendation
|
|
|
|
**Phase 1 (now):** Personal API tokens for both versions
|
|
- Works everywhere
|
|
- No partnership dependencies
|
|
- Validates product-market fit first
|
|
|
|
**Phase 2 (if managed hosting gains traction):** OAuth for managed only
|
|
- Apply for Printify app registration
|
|
- Implement OAuth flow for managed platform
|
|
- Keep token-based flow for open source
|
|
- Use as value differentiator
|
|
|
|
**Phase 3 (optional):** Webhook proxy for open source
|
|
- If demand exists for real-time sync in self-hosted
|
|
- Could be free or paid add-on
|
|
- Maintains open source independence while adding capability
|
|
|
|
## References
|
|
|
|
- [Printify API Reference](https://developers.printify.com/)
|
|
- [Printify Help - Sales Channels](https://help.printify.com/hc/en-us/articles/4483630572945-Which-sales-channels-does-Printify-integrate-with)
|
|
- [Printify Partner Application](https://printify.com/become-a-partner/)
|
|
- [Shopify Product Status](https://community.shopify.com/t/what-is-the-difference-of-unpublished-product-draft-product/30964)
|