Build · Shopify
Xây dựng ứng dụng try-on cho Shopify
Tóm tắt trong một câu
Một ứng dụng Shopify try-on đọc ảnh sản phẩm từ Admin API, POST tới Photta, polling job trong background worker, sau đó chuyển URL hoàn chỉnh cho Shopify thông qua productCreateMedia.
- Credits: 5 credits ở 2K · 6 credits ở 4K
- Thời gian điển hình: 1.5–4 min
- Xử lý: async, polling
Mọi số liệu trên trang này đều lấy từ tài liệu Photta API · đã kiểm tra
Đọc ảnh sản phẩm từ Admin API, render try-on bằng Photta và đẩy kết quả ngược lại làm media sản phẩm. Bốn lệnh gọi, không cần tự xử lý hình ảnh.
RESPONSE=$(curl -s -X POST https://api.photta.app/api/v1/tryon/apparel \
-H "Authorization: Bearer $PHOTTA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"product_type": "top",
"product_images": [
"https://cdn.yourstore.com/products/shirt-front.jpg"
],
"product_back_image": "https://cdn.yourstore.com/products/shirt-back.jpg",
"product_category": "t_shirt",
"mannequin_id": "mnk_f3a9b1",
"pose_id": "pose_c2d4e5",
"background": "https://cdn.yourstore.com/backgrounds/studio-loft.jpg",
"lock_background": true,
"resolution": "2K",
"aspect_ratio": "3:4",
"body_size": "M",
"expression": "neutral",
"user_instruction": "Keep the collar pattern crisp and centered."
}')
ID=$(echo "$RESPONSE" | jq -r '.data.id')const res = await fetch("https://api.photta.app/api/v1/tryon/apparel", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.PHOTTA_API_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({
"product_type": "top",
"product_images": [
"https://cdn.yourstore.com/products/shirt-front.jpg"
],
"product_back_image": "https://cdn.yourstore.com/products/shirt-back.jpg",
"product_category": "t_shirt",
"mannequin_id": "mnk_f3a9b1",
"pose_id": "pose_c2d4e5",
"background": "https://cdn.yourstore.com/backgrounds/studio-loft.jpg",
"lock_background": true,
"resolution": "2K",
"aspect_ratio": "3:4",
"body_size": "M",
"expression": "neutral",
"user_instruction": "Keep the collar pattern crisp and centered."
}),
});
const { data } = await res.json(); // data.id, data.statusimport os, requests
res = requests.post(
"https://api.photta.app/api/v1/tryon/apparel",
headers={"Authorization": f"Bearer {os.environ['PHOTTA_API_KEY']}"},
json={
"product_type": "top",
"product_images": [
"https://cdn.yourstore.com/products/shirt-front.jpg"
],
"product_back_image": "https://cdn.yourstore.com/products/shirt-back.jpg",
"product_category": "t_shirt",
"mannequin_id": "mnk_f3a9b1",
"pose_id": "pose_c2d4e5",
"background": "https://cdn.yourstore.com/backgrounds/studio-loft.jpg",
"lock_background": True,
"resolution": "2K",
"aspect_ratio": "3:4",
"body_size": "M",
"expression": "neutral",
"user_instruction": "Keep the collar pattern crisp and centered."
},
)
data = res.json()["data"] # data["id"], data["status"]Cơ chế hoạt động
Từ trang sản phẩm đến hình ảnh try-on
Xác định scope cho app, đọc ảnh, gửi request, poll tách biệt khỏi luồng request chính, gắn media.
Bước 1
Xác định scope cho app
Ứng dụng try-on cần quyền đọc sản phẩm và ghi media. Chỉ yêu cầu các product scope mà app thực sự dùng và không thêm gì khác — merchants sẽ đọc màn hình cấp quyền.
Bước 2
Lấy API key của Photta
Một key cho mỗi môi trường, lưu trong cấu hình server-side của app. Key không bao giờ được gửi tới storefront hay trình duyệt của merchant.
Bước 3
Đọc ảnh sản phẩm
Query sản phẩm để lấy ảnh đầu tiên. URL đó ở chế độ công khai, chính xác là thứ Photta endpoint cần — không cần upload lại, không cần quản lý signed URL.
Bước 4
Gửi yêu cầu try-on
POST ảnh kèm mannequin và pose. Response trả về ngay lập tức chứa generation id; chưa có nội dung nào được render tại thời điểm này.
Bước 5
Polling trong background job
Shopify yêu cầu webhook handler phản hồi trong vài giây, trong khi quá trình generation mất nhiều thời gian hơn thế. Hãy phản hồi xác nhận, đưa vào queue và poll mỗi 3 giây từ worker.
Bước 6
Gắn media
Chuyển URL hoàn chỉnh cho Shopify và hệ thống sẽ tự tải file về. Bạn cũng nên lưu ảnh phía server của mình để gallery vẫn tồn tại độc lập.
Bốn lệnh gọi, từ đầu đến cuối
Request và poll tới Photta được trích xuất từ tài liệu API; các lệnh gọi Shopify sử dụng trực tiếp Admin API.
// Shopify Admin GraphQL — the product's first image is your source photo
const query = `query($id: ID!) {
product(id: $id) { title images(first: 1) { nodes { url } } }
}`;
const res = await admin.graphql(query, { variables: { id: productGid } });
const { data } = await res.json();
const productImage = data.product.images.nodes[0].url;const res = await fetch("https://api.photta.app/api/v1/tryon/apparel", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.PHOTTA_API_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({
"product_type": "top",
"product_images": [
"https://cdn.yourstore.com/products/shirt-front.jpg"
],
"product_back_image": "https://cdn.yourstore.com/products/shirt-back.jpg",
"product_category": "t_shirt",
"mannequin_id": "mnk_f3a9b1",
"pose_id": "pose_c2d4e5",
"background": "https://cdn.yourstore.com/backgrounds/studio-loft.jpg",
"lock_background": true,
"resolution": "2K",
"aspect_ratio": "3:4",
"body_size": "M",
"expression": "neutral",
"user_instruction": "Keep the collar pattern crisp and centered."
}),
});
const { data } = await res.json(); // data.id, data.statusasync function waitFor(id) {
for (let attempt = 0; attempt < 120; attempt++) {
const res = await fetch(`https://api.photta.app/api/v1/tryon/apparel/${id}`, { headers: { Authorization: `Bearer ${process.env.PHOTTA_API_KEY}` } });
const { data } = await res.json();
if (data.status === "completed") return data.output_url;
if (data.status === "failed") throw new Error(data.error_message ?? "generation failed");
await new Promise((r) => setTimeout(r, 3000));
}
throw new Error("timed out after 6 minutes");
}// Shopify downloads the URL itself, so hand it the Photta output_url
const mutation = `mutation($id: ID!, $media: [CreateMediaInput!]!) {
productCreateMedia(productId: $id, media: $media) {
mediaUserErrors { field message }
}
}`;
await admin.graphql(mutation, {
variables: {
id: productGid,
media: [{ originalSource: outputUrl, mediaContentType: "IMAGE", alt: "Virtual try-on" }],
},
});Câu hỏi thường gặp từ developer
Câu hỏi lập trình viên Shopify thường gặp
Photta key được lưu ở đâu trong ứng dụng Shopify?
Trong biến môi trường trên server của app, tuyệt đối không đặt trong theme, app block hay response của app proxy. Bất cứ thứ gì storefront đọc được thì người mua hàng cũng đọc được.
Ứng dụng này cần những scope nào của Shopify?
Quyền đọc ảnh sản phẩm và ghi product media. Photta hoàn toàn không cần quyền truy cập vào Shopify — ứng dụng của bạn là thành phần duy nhất giao tiếp với cả hai bên.
Làm thế nào để giữ webhook phản hồi nhanh?
Xác nhận phản hồi ngay lập tức, đưa tác vụ vào queue và poll từ worker. Quá trình generation mất nhiều thời gian hơn ngưỡng cho phép của webhook handler, vì vậy không được đặt tác vụ chờ đợi vào handler.
Tôi có thể tính phí merchant theo từng ảnh không?
Đó là quyết định định giá của bạn. Photta trừ credits vào tài khoản của bạn theo từng lượt gọi, vì vậy chi phí trên mỗi ảnh đã được biết trước khi render; bạn tính thêm bao nhiêu là tùy bạn.
Nếu sản phẩm không có ảnh thì sao?
Hãy bỏ qua thay vì gửi ảnh giữ chỗ (placeholder). Endpoint cần ảnh chụp trang phục thực tế, và ảnh placeholder sẽ làm lãng phí credits vào một bức ảnh không ai sử dụng.
Các trang liên quan
Tài liệu liên quan
Tính năng
Virtual try-on API cho trang phục
REST API giúp mặc trang phục lên mannequin AI. Bất đồng bộ, thời gian tạo 1.5–4 phút, 5 credits ở 2K, 6 ở 4K. Kèm ví dụ cURL, Node.js và Python.
ĐọcBắt đầu nhanh
Tạo ảnh Photta đầu tiên trong vòng chưa đầy năm phút.
Đăng ký, tạo API key, chạy lệnh cURL đầu tiên, polling cho đến khi hoàn thành, lưu trữ kết quả. Năm bước, có tài liệu hướng dẫn trọn gói từ đầu đến cuối.
ĐọcNgôn ngữ
Node.js walkthrough
ĐọcRate limits
Đọc
Mọi số liệu trên trang này đều lấy từ tài liệu Photta API · đã kiểm tra
Phát hành ứng dụng, không tốn công dựng hạ tầng
Các lệnh gọi Photta trên trang này được lấy trực tiếp từ tài liệu API. Lấy key và bạn sẽ có bản render đầu tiên chỉ sau vài phút.