メインコンテンツへスキップ
マルチ通貨対応の新しい単発購入商品を作成します。
POST /v1/actions/onetime-product/create-product
認証: API Key

リクエストボディ

フィールド必須説明
storeIdstringYesStore ID(Short ID フォーマット STO_xxx
namestringYes商品名(64 文字以下)
descriptionstring | nullNo商品説明(Markdown 対応;null または "" で消去可能)
pricesobjectYesマルチ通貨価格マップ(以下参照)
mediaarrayNo商品画像/動画(以下参照)
successUrlstring | nullNo購入成功後のリダイレクト URL(512 文字以下、有効な http(s) URL であること;null または "" で消去可能)
metadataobjectNoカスタムキー値データ(最大 50 キー)

価格オブジェクトフォーマット

価格は ISO 4217 通貨コードから価格設定オブジェクトへのマップです。少なくとも 1 つの通貨が必要です。
{
  "USD": {
    "amount": "29.00",
    "taxIncluded": false,
    "taxCategory": "saas"
  },
  "EUR": {
    "amount": "27.00",
    "taxIncluded": true,
    "taxCategory": "saas"
  }
}
フィールド説明
amountstring表示フォーマット文字列としての価格(例:"29.00" = $29.00)
taxIncludedboolean金額に税が含まれているかどうか
taxCategorystring税計算用のカテゴリ
サポートされる taxCategory 値:
説明
digital_goods一般的なデジタル商品
saasSoftware 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"
}
フィールド説明
typestringimage または video
urlstringメディア URL
altstringアクセシビリティ用の代替テキスト
thumbnailstringサムネイル 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 -w 0)
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 -w 0)

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 でラップされます。商品オブジェクトは、商品とバージョンのフィールドを結合したフラット化された詳細ビューです。
フィールド説明
idstring商品 ID(PROD_xxx
storeIdstringStore ID(STO_xxx
namestring商品名(現在のバージョンから)
descriptionstring | null商品説明(現在のバージョンから)
pricesobjectマルチ通貨価格マップ(以下参照)
mediaarrayメディアアイテム(現在のバージョンから)
successUrlstring | null成功時のリダイレクト URL(現在のバージョンから)
metadataobjectカスタムメタデータ(現在のバージョンから)
statusstring現在の環境でのステータス: active または inactive
createdAtstring商品作成タイムスタンプ(ISO 8601)
updatedAtstring商品最終更新タイムスタンプ(ISO 8601)
レスポンスの価格オブジェクト(通貨ごと):
フィールド説明
amountstring表示フォーマット文字列としての価格(例:"49.00"
taxCategorystring税計算用のカテゴリ
status フィールドは操作対象の環境を反映します。test 環境(デフォルト)で作成した場合、ステータスは active です。production 環境で作成した場合、production のステータスが返されます。

エラー

リトライポリシー:4xx は一切リトライしない — リクエストを修正してから再送信。5xx は指数バックオフでリトライ(5s 開始、最大 3 回)。
ステータスerrors[0].message意味推奨処理
400Missing required field: storeIdstoreId が指定されていませんボディを修正して再送信
400Invalid ID format指定された Short ID をデコードできませんでしたstoreId のフォーマットを修正して再送信
400Missing required field: namename が指定されていませんボディを修正して再送信
400Prices must have at least one currencyprices が欠落しているか空のオブジェクトです通貨を少なくとも 1 つ指定して再送信
400Invalid currency code通貨コードは ISO 4217 に準拠した3文字の大文字である必要があります(例:USDEURJPY有効な ISO 4217 コードを使用して再送信
400Invalid amount金額は正の数値文字列である必要があります(例:"9.99""100"正の数値文字列を使用して再送信
404Store not foundストアが存在しないかアクセスできませんstoreId が現在のマーチャントに属することを確認
500Internal server errorサーバ側の予期しない障害指数バックオフでリトライ(5s 開始、最大 3 回)