POST /v1/actions/onetime-product/create-product
リクエストボディ
| フィールド | 型 | 必須 | 説明 |
|---|---|---|---|
storeId | string | Yes | Store ID(Short ID フォーマット STO_xxx) |
name | string | Yes | 商品名(64 文字以下) |
description | string | null | No | 商品説明(Markdown 対応;null または "" で消去可能) |
prices | object | Yes | マルチ通貨価格マップ(以下参照) |
media | array | No | 商品画像/動画(以下参照) |
successUrl | string | null | No | 購入成功後のリダイレクト URL(512 文字以下、有効な http(s) URL であること;null または "" で消去可能) |
metadata | object | No | カスタムキー値データ(最大 50 キー) |
価格オブジェクトフォーマット
価格は ISO 4217 通貨コードから価格設定オブジェクトへのマップです。少なくとも 1 つの通貨が必要です。{
"USD": {
"amount": "29.00",
"taxIncluded": false,
"taxCategory": "saas"
},
"EUR": {
"amount": "27.00",
"taxIncluded": true,
"taxCategory": "saas"
}
}
| フィールド | 型 | 説明 |
|---|---|---|
amount | string | 表示フォーマット文字列としての価格(例:"29.00" = $29.00) |
taxIncluded | boolean | 金額に税が含まれているかどうか |
taxCategory | string | 税計算用のカテゴリ |
taxCategory 値:
| 値 | 説明 |
|---|---|
digital_goods | 一般的なデジタル商品 |
saas | Software as a Service |
software | ダウンロード可能なソフトウェア |
ebook | 電子書籍 |
online_course | オンラインコースと教育 |
consulting | コンサルティングサービス |
professional_service | プロフェッショナルサービス |
メディアアイテムフォーマット
{
"type": "image",
"url": "https://example.com/product.png",
"alt": "Product screenshot",
"thumbnail": "https://example.com/product-thumb.png"
}
| フィールド | 型 | 説明 |
|---|---|---|
type | string | image または video |
url | string | メディア URL |
alt | string | アクセシビリティ用の代替テキスト |
thumbnail | string | サムネイル URL(画像はオプション、動画は推奨) |
リクエスト例
import { WaffoPancake, 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.onetimeProducts.create({
storeId: "STO_2aUyqjCzEIiEcYMKj7TZtw",
name: "Premium Template Pack",
description: "50 premium design templates for your next project.",
prices: {
USD: { amount: "49.00", taxIncluded: false, taxCategory: TaxCategory.DigitalGoods },
EUR: { amount: "45.00", taxIncluded: true, taxCategory: TaxCategory.DigitalGoods },
},
media: [
{ type: "image", url: "https://example.com/templates-preview.png", alt: "Template preview" },
],
successUrl: "https://example.com/thank-you",
metadata: { category: "design", fileCount: "50" },
});
MERCHANT_ID="MER_2aUyqjCzEIiEcYMKj7TZtw"
TIMESTAMP=$(date +%s)
BODY='{
"storeId": "STO_2aUyqjCzEIiEcYMKj7TZtw",
"name": "Premium Template Pack",
"description": "50 premium design templates for your next project.",
"prices": {
"USD": { "amount": "49.00", "taxIncluded": false, "taxCategory": "digital_goods" },
"EUR": { "amount": "45.00", "taxIncluded": true, "taxCategory": "digital_goods" }
},
"successUrl": "https://example.com/thank-you"
}'
BODY_HASH=$(echo -n "$BODY" | openssl dgst -sha256 -binary | base64 | tr -d '\n')
CANONICAL_REQUEST="POST
/v1/actions/onetime-product/create-product
$TIMESTAMP
$BODY_HASH"
SIGNATURE=$(echo -n "$CANONICAL_REQUEST" | openssl dgst -sha256 -sign private_key.pem | base64 | tr -d '\n')
curl -X POST "https://api.waffo.ai/v1/actions/onetime-product/create-product" \
-H "Content-Type: application/json" \
-H "X-Merchant-Id: $MERCHANT_ID" \
-H "X-Timestamp: $TIMESTAMP" \
-H "X-Signature: $SIGNATURE" \
-d "$BODY"
import hashlib, time, base64, json, requests
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import padding
merchant_id = "MER_2aUyqjCzEIiEcYMKj7TZtw"
timestamp = str(int(time.time()))
body = {
"storeId": "STO_2aUyqjCzEIiEcYMKj7TZtw",
"name": "Premium Template Pack",
"description": "50 premium design templates for your next project.",
"prices": {
"USD": {"amount": "49.00", "taxIncluded": False, "taxCategory": "digital_goods"},
"EUR": {"amount": "45.00", "taxIncluded": True, "taxCategory": "digital_goods"},
},
"successUrl": "https://example.com/thank-you",
}
body_json = json.dumps(body, separators=(",", ":"))
body_hash = base64.b64encode(hashlib.sha256(body_json.encode()).digest()).decode()
path = "/v1/actions/onetime-product/create-product"
canonical = f"POST\n{path}\n{timestamp}\n{body_hash}"
with open("private_key.pem", "rb") as f:
private_key = serialization.load_pem_private_key(f.read(), password=None)
signature = base64.b64encode(
private_key.sign(canonical.encode(), padding.PKCS1v15(), hashes.SHA256())
).decode()
resp = requests.post(
f"https://api.waffo.ai{path}",
headers={
"Content-Type": "application/json",
"X-Merchant-Id": merchant_id,
"X-Timestamp": timestamp,
"X-Signature": signature,
},
data=body_json,
)
print(resp.json())
成功レスポンス (200)
{
"data": {
"product": {
"id": "PROD_3kF9mNpQrStUvWxYz1A2bC",
"storeId": "STO_2aUyqjCzEIiEcYMKj7TZtw",
"name": "Premium Template Pack",
"description": "50 premium design templates for your next project.",
"prices": {
"USD": { "amount": "49.00", "taxCategory": "digital_goods" },
"EUR": { "amount": "45.00", "taxCategory": "digital_goods" }
},
"media": [
{ "type": "image", "url": "https://example.com/templates-preview.png", "alt": "Template preview" }
],
"successUrl": "https://example.com/thank-you",
"metadata": { "category": "design", "fileCount": "50" },
"status": "active",
"createdAt": "2026-01-15T10:30:00.000Z",
"updatedAt": "2026-01-15T10:30:00.000Z"
}
}
}
レスポンスフィールド
レスポンスはdata.product でラップされます。商品オブジェクトは、商品とバージョンのフィールドを結合したフラット化された詳細ビューです。
| フィールド | 型 | 説明 |
|---|---|---|
id | string | 商品 ID(PROD_xxx) |
storeId | string | Store ID(STO_xxx) |
name | string | 商品名(現在のバージョンから) |
description | string | null | 商品説明(現在のバージョンから) |
prices | object | マルチ通貨価格マップ(以下参照) |
media | array | メディアアイテム(現在のバージョンから) |
successUrl | string | null | 成功時のリダイレクト URL(現在のバージョンから) |
metadata | object | カスタムメタデータ(現在のバージョンから) |
status | string | 現在の環境でのステータス: active または inactive |
createdAt | string | 商品作成タイムスタンプ(ISO 8601) |
updatedAt | string | 商品最終更新タイムスタンプ(ISO 8601) |
| フィールド | 型 | 説明 |
|---|---|---|
amount | string | 表示フォーマット文字列としての価格(例:"49.00") |
taxCategory | string | 税計算用のカテゴリ |
status フィールドは操作対象の環境を反映します。test 環境(デフォルト)で作成した場合、ステータスは active です。production 環境で作成した場合、production のステータスが返されます。エラー
リトライポリシー:4xx は一切リトライしない — リクエストを修正してから再送信。5xx は指数バックオフでリトライ(5s 開始、最大 3 回)。
| ステータス | errors[0].message | 意味 | 推奨処理 |
|---|---|---|---|
| 400 | Missing required field: storeId | storeId が指定されていません | ボディを修正して再送信 |
| 400 | Invalid ID format | 指定された Short ID をデコードできませんでした | storeId のフォーマットを修正して再送信 |
| 400 | Missing required field: name | name が指定されていません | ボディを修正して再送信 |
| 400 | Prices must have at least one currency | prices が欠落しているか空のオブジェクトです | 通貨を少なくとも 1 つ指定して再送信 |
| 400 | Invalid currency code | 通貨コードは ISO 4217 に準拠した3文字の大文字である必要があります(例:USD、EUR、JPY) | 有効な ISO 4217 コードを使用して再送信 |
| 400 | Invalid amount | 金額は正の数値文字列である必要があります(例:"9.99"、"100") | 正の数値文字列を使用して再送信 |
| 404 | Store not found | ストアが存在しないかアクセスできません | storeId が現在のマーチャントに属することを確認 |
| 500 | Internal server error | サーバ側の予期しない障害 | 指数バックオフでリトライ(5s 開始、最大 3 回) |