rswag-online/backend/app/pod/prodigi_client.py

130 lines
4.1 KiB
Python

"""Prodigi Print-on-Demand API client (v4).
Handles order submission, product specs, and quotes.
Sandbox: https://api.sandbox.prodigi.com/v4.0/
Production: https://api.prodigi.com/v4.0/
"""
import logging
import httpx
from app.config import get_settings
logger = logging.getLogger(__name__)
settings = get_settings()
SANDBOX_URL = "https://api.sandbox.prodigi.com/v4.0"
PRODUCTION_URL = "https://api.prodigi.com/v4.0"
class ProdigiClient:
"""Client for the Prodigi v4 Print API."""
def __init__(self):
self.api_key = settings.prodigi_api_key
self.base_url = SANDBOX_URL if settings.pod_sandbox_mode else PRODUCTION_URL
self.enabled = bool(self.api_key)
@property
def _headers(self) -> dict:
return {
"X-API-Key": self.api_key,
"Content-Type": "application/json",
}
async def create_order(
self,
items: list[dict],
recipient: dict,
shipping_method: str = "Budget",
metadata: dict | None = None,
) -> dict:
"""Create a Prodigi print order.
Args:
items: List of items, each with:
- sku: Prodigi SKU (e.g., "GLOBAL-STI-KIS-4X4")
- copies: Number of copies
- sizing: "fillPrintArea" | "fitPrintArea" | "stretchToPrintArea"
- assets: [{"printArea": "default", "url": "https://..."}]
recipient: Shipping address with:
- name: Recipient name
- email: Email (optional)
- address: {line1, line2, townOrCity, stateOrCounty, postalOrZipCode, countryCode}
shipping_method: "Budget" | "Standard" | "Express"
metadata: Optional key/value metadata
"""
if not self.enabled:
raise ValueError("Prodigi API key not configured")
payload = {
"shippingMethod": shipping_method,
"recipient": recipient,
"items": items,
}
if metadata:
payload["metadata"] = metadata
async with httpx.AsyncClient(timeout=30.0) as client:
resp = await client.post(
f"{self.base_url}/Orders",
headers=self._headers,
json=payload,
)
resp.raise_for_status()
result = resp.json()
logger.info(f"Prodigi order created: {result.get('id')}")
return result
async def get_order(self, order_id: str) -> dict:
"""Get order details by Prodigi order ID."""
async with httpx.AsyncClient(timeout=15.0) as client:
resp = await client.get(
f"{self.base_url}/Orders/{order_id}",
headers=self._headers,
)
resp.raise_for_status()
return resp.json()
async def get_product(self, sku: str) -> dict:
"""Get product specifications (dimensions, print areas, etc.)."""
async with httpx.AsyncClient(timeout=15.0) as client:
resp = await client.get(
f"{self.base_url}/products/{sku}",
headers=self._headers,
)
resp.raise_for_status()
return resp.json()
async def get_quote(
self,
items: list[dict],
shipping_method: str = "Budget",
destination_country: str = "US",
) -> dict:
"""Get a pricing quote before ordering.
Args:
items: List with sku, copies, sizing, assets
shipping_method: Shipping tier
destination_country: 2-letter country code
"""
payload = {
"shippingMethod": shipping_method,
"destinationCountryCode": destination_country,
"items": [
{"sku": item["sku"], "copies": item.get("copies", 1)}
for item in items
],
}
async with httpx.AsyncClient(timeout=15.0) as client:
resp = await client.post(
f"{self.base_url}/quotes",
headers=self._headers,
json=payload,
)
resp.raise_for_status()
return resp.json()