ビルド · Shopify
Shopify向けtry-onアプリの構築
要約
Shopify try-onアプリは、Admin APIから商品画像を読み取り、PhottaにPOSTし、バックグラウンドワーカーでジョブをポーリングして、完成したURLをproductCreateMediaを通じてShopifyに渡します。
- Credits: 2Kで5 credits · 4Kで6 credits
- 所要時間の目安: 1.5–4 min
- 処理方式: 非同期、ポーリング
このページに記載されている数値はすべて以下に基づきます: Photta APIドキュメント · 確認済み
Admin APIから商品写真を読み込み、Phottaでtry-onをレンダリングして、結果を商品メディアとしてプッシュします。必要なのは4回のリクエストのみで、自前での画像処理は不要です。
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"]仕組み
商品ページからtry-on画像まで
アプリのスコープ設定、写真の読み取り、送信、リクエストパス外でのポーリング、メディアの添付。
ステップ 1
アプリのスコープ設定
try-onアプリには商品の読み取りとメディアの書き込み権限が必要です。アプリが実際に使用する商品スコープのみを要求してください—マーチャントは同意画面を確認します。
ステップ 2
Photta APIキーの取得
環境ごとに1つのキーを使用し、アプリのサーバー側設定で保持します。ストアフロントやマーチャントのブラウザに渡ることはありません。
ステップ 3
商品画像の読み取り
商品の最初の画像をクエリします。そのURLは公開されており、Photta endpointが求めている形式そのものです—再アップロードも、署名付きURLの管理も不要です。
ステップ 4
try-onのリクエスト送信
画像、mannequin、poseを指定してPOSTします。レスポンスは即座に返され、generation idが含まれます。この時点ではまだレンダリングされていません。
ステップ 5
バックグラウンドジョブでのポーリング
Shopifyはwebhookハンドラーが数秒以内に応答することを想定していますが、生成にはそれ以上の時間がかかります。即座に応答を返し、キューに追加して、ワーカーから3秒ごとにポーリングしてください。
ステップ 6
メディアの添付
完成したURLをShopifyに渡せば、Shopify自体がファイルをダウンロードします。ギャラリーが独立して存続できるよう、画像はアプリ側でも永続化してください。
エンドツーエンドの4つの呼び出し
PhottaのリクエストとポーリングはAPIドキュメントに基づいています。Shopifyの呼び出しは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" }],
},
});開発者からのよくある質問
Shopify開発者からよくある質問
Shopifyアプリ内でPhottaキーはどこに保持すべきですか?
テーマ内、アプリブロック、またはアプリプロキシのレスポンスではなく、アプリサーバーの環境内に保持してください。ストアフロントが読み取れるものは、購入者も読み取ることができます。
これにはどのShopifyスコープが必要ですか?
商品画像の読み取りと商品メディアの書き込みです。Photta側にはShopifyへのアクセス権限は一切不要です—両側と通信するのは作成するアプリのみです。
webhookの処理速度を維持するにはどうすればよいですか?
即座に応答を返し、処理をキューに追加して、ワーカーからポーリングします。生成にはwebhookハンドラーが許容する以上の時間がかかるため、完了を待つ処理をハンドラー内に含めるべきではありません。
マーチャントに対して画像ごとに課金できますか?
それは価格設定の判断次第です。Phottaは呼び出しごとにアカウントへcreditsを請求するため、レンダリング前に画像あたりのコストが確定します。そこにいくら上乗せするかは自由です。
商品に写真がない場合はどうなりますか?
プレースホルダーを送信するのではなく、スキップしてください。endpointには実際の衣服の写真が必要であり、プレースホルダーを使用すると誰も公開しない画像にcreditsを消費することになります。
インフラの構築ではなく、アプリのリリースに注力する
このページのPhottaの呼び出しは、APIドキュメントからそのまま引用しています。キーを取得すれば、数分で最初のレンダリングを実行できます。