构建 · 移动端试穿
为 iOS 或 Android 应用添加虚拟试穿功能
一句话总结
将 Photta API 密钥保留在后端;暴露一个代理 endpoint(/mobile/try-on),接收商品图片和模特 ID,转发至 /api/v1/tryon/apparel,将 generation ID 与用户关联存储,并暴露一个状态 endpoint 供移动端每 3 秒轮询一次,直到图片生成完毕。手机端完全感知不到 Photta 密钥的存在。
- Credits: 2K 分辨率 5 credits · 4K 分辨率 6 credits
- 常规耗时: 1.5–4 min
- 处理方式: 异步,轮询
本页所有数据均来自 Photta API 文档 · 已验证
设备永远不要直接与 Photta 通信——它只与你的后端通信,再由你的后端与 Photta 通信。这层边界能确保 API 密钥不暴露在用户的手机上,让你自主掌控 rate limit,并保证应用能顺利通过各大应用商店的审核。
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"]工作原理
设备、后端、Photta——三层架构,统一链路
在首次提交 App Store 审核前,你需要明确的 5 个关键决策。
步骤 1
将密钥保留在服务器端
切勿将 Photta 密钥打包进应用二进制文件、info.plist、AndroidManifest 或任何构建期环境变量文件中——反编译二进制文件轻而易举,密钥必然泄露。你的后端是存放密钥的唯一安全位置。
步骤 2
设计清晰的代理 endpoint
向应用暴露 /mobile/try-on。使用你自己的用户会话进行请求鉴权——不要通过代理传递原始 API 密钥。随后由代理代表该已鉴权用户调用 Photta。
步骤 3
在数据库中镜像 Photta 的任务状态
当 Photta 返回 generation ID 时,在你自己的 jobs 表中插入一行记录,并以 user_id 作为键。移动端轮询的是你自己的 /mobile/try-on/:id——而不是 Photta 的——这样 rate limit 仅受你自己的套餐方案限制,与具体用户的设备无关。
步骤 4
缓存结果 URL
当 Photta 返回 completed 状态时,将 output_url 写入你的 jobs 记录行,并可选择将文件字节流转存至你自己的 CDN。移动端用户按 MB 消耗流量——不要在商详页(PDP)的整个生命周期内一直直链 Photta CDN。
步骤 5
优雅处理离线状态
移动网络在轮询过程中可能随时中断。应展示“正在重试”状态而非直接抛出硬报错,采用指数退避机制进行重试,并在本地持久化保存 job ID,以便在冷启动时能无缝恢复上次中断的会话。
集成的三个层级
App 契约、后端代理、客户端轮询——精准契合你所需的架构边界。
// Never ship your PHOTTA_API_KEY to the device. The mobile app
// calls YOUR API; your API calls Photta. Simple, safe, billable.
POST /mobile/try-on
{
"productImageUrl": "...",
"mannequinId": "...",
"poseId": "..."
}
// → returns { jobId } immediatelyconst 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.status// Store { id: data.id, user_id, status: "processing" } in your jobs table
// and return { jobId: data.id } to the device.
// App polls YOUR /mobile/try-on/:id — you poll Photta server-side
// and mirror status. This way rate limits are YOUR problem, not
// each device's, and you can add caching + analytics in one place.
async function pollJob(jobId: string) {
while (true) {
const { status, imageUrl } = await api.get(`/mobile/try-on/${jobId}`);
if (status === "completed") return imageUrl;
if (status === "failed") throw new Error("Try-on failed");
await sleep(3000);
}
}开发者常见问题
发布基于 Photta 的移动端功能
我可以从设备端直接调用 Photta API 吗?
技术上可行——但你必须将 API 密钥硬编码打包进应用安装包中,这意味着密钥会被分发到安装该应用的每一台设备上。请务必通过你自己的后端进行路由,将密钥保留在受控的服务器上。
如何应对不稳定的移动网络?
注意两点。第一:由后端提交任务,而不是设备端——手机只需将商品图片上传到你自己的 API。第二:以 3 秒间隔轮询你自己的状态 endpoint,并在失败时引入指数退避。如果用户切换应用或信号丢失,将 job ID 保存在本地存储中,以便用户返回时能够继续轮询。
Photta 支持 webhook 吗?
不支持。轮询是该 API 提供的唯一状态完成信号,因此请保留 worker 循环——并在你自己的后端捕获任务完成的瞬间,立即向手机端推送一条服务端发送事件(Server-Sent Events)。
我可以在 React Native 或 Flutter 中使用 Photta 吗?
可以——在 JavaScript / Dart 侧,你调用的只是你自己的后端 API。Photta 的底层实现细节完全不会穿透到客户端。Flutter 应用 fetch /mobile/try-on 的方式与 React Native 或原生 SwiftUI / Jetpack Compose 应用完全一致。
Apple 审核团队会在意这种集成方式吗?
不会——因为你的应用并未直接调用 Photta。它调用的是你自己的后端,使用你自己的鉴权体系。在 Apple 看来,这就是一个标准的 REST 架构应用。Google Play 和华为应用市场同理。