POST /v1/actions/subscription-product/create-product
请求体
| 字段 | 类型 | 必需 | 说明 |
|---|---|---|---|
storeId | string | 是 | Store ID(STO_xxx 格式) |
name | string | 是 | 商品名称(≤ 64 字符) |
billingPeriod | string | 是 | weekly、monthly、quarterly 或 yearly |
prices | object | 是 | 多币种定价(参见 价格格式) |
description | string | null | 否 | 商品描述(支持 Markdown;传 null 或 "" 清空) |
media | array | 否 | 商品图片/视频(参见 媒体格式) |
successUrl | string | null | 否 | 成功订阅后的跳转 URL(≤ 512 字符,必须为合法 http(s) URL;传 null 或 "" 清空) |
metadata | object | 否 | 自定义键值数据。可包含 trialDays(整数,1-365)以提供免费试用期 |
请求示例
import { WaffoPancake, BillingPeriod, TaxCategory } from "@waffo/pancake-ts";
const client = new WaffoPancake({
merchantId: process.env.WAFFO_MERCHANT_ID!,
privateKey: process.env.WAFFO_PRIVATE_KEY!,
});
const { product } = await client.subscriptionProducts.create({
storeId: "STO_2D5F8G3H1K4M6N9P",
name: "Pro Plan",
billingPeriod: BillingPeriod.Monthly,
prices: {
USD: { amount: "29.00", taxIncluded: false, taxCategory: TaxCategory.SaaS },
EUR: { amount: "27.00", taxIncluded: false, taxCategory: TaxCategory.SaaS },
},
description: "Full access to all Pro features.",
successUrl: "https://example.com/welcome",
metadata: { trialDays: 14 },
});
curl -X POST https://api.waffo.ai/v1/actions/subscription-product/create-product \
-H "Content-Type: application/json" \
-H "X-Merchant-Id: MER_7K3M9P2R5T8W1Y4Z" \
-H "X-Timestamp: 2026-03-30T10:00:00Z" \
-H "X-Signature: BASE64_RSA_SIGNATURE" \
-d '{
"storeId": "STO_2D5F8G3H1K4M6N9P",
"name": "Pro Plan",
"billingPeriod": "monthly",
"prices": {
"USD": { "amount": "29.00", "taxIncluded": false, "taxCategory": "saas" },
"EUR": { "amount": "27.00", "taxIncluded": false, "taxCategory": "saas" }
},
"description": "Full access to all Pro features.",
"successUrl": "https://example.com/welcome",
"metadata": { "trialDays": 14 }
}'
import requests
# Assumes you have a sign_request() helper for RSA-SHA256 signing
headers = sign_request("POST", "/v1/actions/subscription-product/create-product", body)
body = {
"storeId": "STO_2D5F8G3H1K4M6N9P",
"name": "Pro Plan",
"billingPeriod": "monthly",
"prices": {
"USD": {"amount": "29.00", "taxIncluded": False, "taxCategory": "saas"},
"EUR": {"amount": "27.00", "taxIncluded": False, "taxCategory": "saas"},
},
"description": "Full access to all Pro features.",
"successUrl": "https://example.com/welcome",
"metadata": {"trialDays": 14},
}
response = requests.post(
"https://api.waffo.ai/v1/actions/subscription-product/create-product",
json=body,
headers=headers,
)
data = response.json()
成功响应 (200)
{
"data": {
"product": {
"id": "PROD_3F7H2J5L8N1Q4S6U",
"storeId": "STO_2D5F8G3H1K4M6N9P",
"name": "Pro Plan",
"description": "Full access to all Pro features.",
"billingPeriod": "monthly",
"prices": {
"USD": { "amount": "29.00", "taxCategory": "saas" },
"EUR": { "amount": "27.00", "taxCategory": "saas" }
},
"media": [],
"successUrl": "https://example.com/welcome",
"metadata": { "trialDays": 14 },
"status": "active",
"createdAt": "2026-03-30T10:30:00.000Z",
"updatedAt": "2026-03-30T10:30:00.000Z"
}
}
}
响应字段
响应包裹在data.product 中。商品对象是合并了商品和版本字段的扁平化详情视图。
| 字段 | 类型 | 说明 |
|---|---|---|
id | string | 商品 ID(PROD_xxx) |
storeId | string | Store ID(STO_xxx) |
name | string | 商品名称(来自当前版本) |
description | string | null | 商品描述(来自当前版本) |
billingPeriod | string | 计费周期:weekly、monthly、quarterly 或 yearly |
prices | object | 多币种定价映射(见下方) |
media | array | 媒体项(来自当前版本) |
successUrl | string | null | 成功跳转 URL(来自当前版本) |
metadata | object | 自定义元数据(来自当前版本,可包含 trialDays) |
status | string | 当前环境中的状态:active 或 inactive |
createdAt | string | 商品创建时间戳(ISO 8601) |
updatedAt | string | 商品最后更新时间戳(ISO 8601) |
| 字段 | 类型 | 说明 |
|---|---|---|
amount | string | 显示格式的价格字符串(例如 “29.00”) |
taxCategory | string | 用于税率计算的税务类别 |
错误响应
重试策略:4xx 一律不要重试 — 修正请求后重发。5xx 指数退避重试(起步 5s,最多 3 次)。
| 状态码 | errors[0].message | 含义 | 推荐处理 |
|---|---|---|---|
| 400 | Missing header: x-context-merchant-id | 商户上下文 header 未透传 | 修正 SDK 配置 |
| 400 | Missing or invalid header: x-context-environment | 环境 header 缺失或不是 test / prod | 把 X-Environment 设为 test 或 prod |
| 400 | Missing required field: storeId / Missing required field: name / Missing required field: prices (must have at least one currency) | 必填字段缺失 | 补齐字段后重发 |
| 400 | Expected format: STO_xxx, got "X" | storeId 不是合法 Short ID | 使用 STO_ 前缀的 Short ID |
| 400 | Invalid or missing billingPeriod | billingPeriod 不在 weekly / monthly / quarterly / yearly 集合 | 改为四个允许值之一 |
| 400 | Invalid currency code: "X". Must be 3 uppercase letters (e.g., "USD", "EUR", "JPY") | prices 的 key 不是合法 ISO 4217 代码 | 改为 3 位大写字母 |
| 400 | Invalid amount for X: "Y". Must be a positive number string (e.g., "9.99", "1000") | 金额字符串无法解析 | 改为正数十进制字符串 |
| 401 | Unauthorized | 认证失败 | 检查 API Key、时间戳与签名 |
| 404 | Store not found | 解码后的 storeId 不存在(外键约束) | 确认门店归属于当前商户 |
| 500 | Internal server error | 瞬时下游故障 | 指数退避重试(起步 5s,最多 3 次) |