product-catalog-management について
product-catalog-management は、Dodo Payments SDK で商品カタログを管理する開発者向けのスキルです。複数の料金モデル、商品ライフサイクル、アドオン、コレクション、デジタル配信を扱い、価格変更やアーカイブの制約も説明します。
主な機能
- 01買い切り、定期、従量課金商品の作成
- 02商品の一覧、取得、更新、アーカイブ
- 03商品画像と短縮リンクの管理
- 04アドオンと商品コレクションの作成
- 05エンタイトルメントによるデジタルファイル配信
活用シーン
- 01異なる料金モデルの商品カタログ構築
- 02商品の価格変更またはアーカイブ
- 03商品コレクションと任意のアドオンの作成
- 04顧客へのダウンロード可能なデジタルファイル提供
MCP 対応
入力には MCP との関連について記載がありません。
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ dodopayments/ skills --skill product-catalog-managementインストール後、Claude Code で/product-catalog-managementを入力して読み込まれたか確認します。
詳細
商品カタログ管理
このスキルでは、価格モデルを設定した商品の作成、アドオンやコレクションの管理、商品画像のアップロード、デジタルファイルの顧客への提供まで、商品ライフサイクル全体を扱います。
このスキルを使用する場面
- 1回限り、継続課金、または従量課金の価格設定で商品を作成または更新する必要がある場合。
- 商品コレクションやストアフロントを構築する場合。
- 商品画像をアップロードしたり、デジタルファイルを顧客に提供したりする必要がある場合。
- アドオンや商品バリエーションを管理する場合。
- 商品の更新が失敗した理由や、商品の価格を変更する方法を確認する必要がある場合。
主な概念
商品モデル: 各商品には、作成時に選択した価格モデルが1つだけ設定されます。モデルは次の3種類です。
one_time_price:購入ごとに1回請求します。recurring_price:設定可能な決済頻度とサブスクリプション期間に基づいて請求します。usage_based_price:消費された単位ごとに請求します(メーターの設定についてはusage-based-billingスキルを参照してください)。
価格設定は商品オブジェクト内にネストされています。トップレベルの独立したPriceリソースはありません。
価格設定の構造: 各価格オブジェクトには次の項目が含まれます。
type:上記3種類のいずれかのモデル。currency:ISO 4217コード(例:USD、AED、INR)。price:最小通貨単位での金額(USDの場合はセント)。discount:同じ単位で指定する任意の割引額。
税区分: 商品の作成時に必須です。Dodoはこれを使用して売上税を計算し、徴収します。対応する値はdigital_products、saas、e_book、edtechです。
ライフサイクル: 商品ではlist、retrieve、update、archive/unarchiveを使用できます。削除用のエンドポイントはありません。アーカイブされた商品は履歴に残りますが、新しいチェックアウトには表示されません。
画像: 署名付きアップロードURLの有効期限は60秒です。URLをリクエストしたら、すぐにアップロードしてください。
デジタル商品の提供: エンタイトルメントにより、顧客はファイルにアクセスできます。ダウンロードURLの有効期限は約15分です。
商品の作成
すべての商品には、名前、価格モデル、税区分が必要です。
import DodoPayments from 'dodopayments';
const client = new DodoPayments({
bearerToken: process.env.DODO_PAYMENTS_API_KEY,
environment: 'test_mode',
});
// One-time purchase
const product = await client.products.create({
name: 'Pro Bundle',
tax_category: 'digital_products',
price: {
type: 'one_time_price',
currency: 'USD',
price: 9900, // $99.00
discount: 0,
purchasing_power_parity: false,
},
});
console.log(product.product_id); // pdt_...
継続課金商品では、請求サイクルを指定します。
const subscription = await client.products.create({
name: 'Premium Plan',
tax_category: 'saas',
price: {
type: 'recurring_price',
currency: 'USD',
price: 2999, // $29.99/month
discount: 0,
payment_frequency_count: 1,
payment_frequency_interval: 'Month',
subscription_period_count: 1,
subscription_period_interval: 'Month',
purchasing_power_parity: false,
},
});
このスキルは、商品の作成リクエストの形式に関する正規の参照元です。従量課金商品を作成するには、usage-based-billingスキルの説明に従ってメーターを作成し、単数形のprice.meters配列を通じて関連付けます。
const metered = await client.products.create({
name: 'API Calls',
tax_category: 'saas',
price: {
type: 'usage_based_price',
currency: 'USD',
discount: 0,
fixed_price: 0, // No fixed monthly charge; amounts use the smallest currency unit
payment_frequency_count: 1,
payment_frequency_interval: 'Month',
subscription_period_count: 1,
subscription_period_interval: 'Month',
purchasing_power_parity: false,
meters: [
{
meter_id: 'mtr_api_calls',
price_per_unit: '0.01',
free_threshold: 1000,
},
],
},
});
price_per_unitは、設定された通貨の最小単位を表す10進数文字列です。任意のfree_thresholdでは、従量課金が始まる前に、集計された単位数から指定した数を除外します。
商品の一覧表示と取得
// List all products
const products = await client.products.list();
// Retrieve a single product
const product = await client.products.retrieve('pdt_pro_bundle');
商品の更新
PATCH /products/{id}(client.products.update)では、名前、説明、メタデータのほか、price、tax_category、pricing_modeを指定できます。すべてのフィールドは任意です。省略したフィールドは変更されません。
2つの制限があります。価格モデルは変更できません。金額は編集できますが、1回限りの商品をサブスクリプション商品や従量課金商品に変更することはできず、その逆もできません。代わりに新しい商品を作成してください。また、価格を編集して変更されるのは新規顧客に適用される価格のみです。既存のサブスクリプションには作成時の価格が維持されるため、Change Plan(client.subscriptions.changePlan(...))を使って明示的に移行してください。
await client.products.update('pdt_pro_bundle', {
name: 'Pro Bundle (Updated)',
description: 'Includes all pro features',
metadata: {
category: 'software',
tier: 'premium',
},
});
アーカイブとアーカイブ解除
商品をアーカイブすると、削除せずに新しいチェックアウトで非表示にできます。
await client.products.archive('pdt_pro_bundle');
// Restore it later
await client.products.unarchive('pdt_pro_bundle');
商品画像
署名付きURLを使用して商品画像をアップロードします。URLの有効期限は60秒です。
import fs from 'node:fs';
// Request a presigned upload URL
const uploadUrl = await client.products.images.update('pdt_pro_bundle', {
force_update: true,
});
// Upload immediately (within 60 seconds)
const response = await fetch(uploadUrl.url, {
method: 'PUT',
body: fs.readFileSync('./product.png'),
headers: { 'Content-Type': 'image/png' },
});
if (!response.ok) {
throw new Error('Image upload failed');
}
アドオン
アドオンは、顧客が商品と一緒に購入できる任意の追加商品です。アドオンを個別に作成し、チェックアウト時に参照します。
// Create an add-on
const addon = await client.addons.create({
name: 'Priority Support',
tax_category: 'saas',
currency: 'USD',
price: 4900, // $49.00
});
// Upload an image for the add-on
const addonImageUrl = await client.addons.updateImages(addon.id);
// List add-ons
const addons = await client.addons.list();
商品コレクション
テーマ別のチェックアウト用に、商品をコレクションにまとめます。コレクションには、ネストされたグループとアイテムがあります。
// Create a collection
const collection = await client.productCollections.create({
name: 'Starter Bundle',
description: 'Everything you need to get started',
groups: [
{
group_name: 'Core Tools',
products: [
{ product_id: 'pdt_tool_a' },
{ product_id: 'pdt_tool_b' },
],
},
],
});
// Add another group later
await client.productCollections.groups.create(collection.id, {
group_name: 'Optional Tools',
products: [
{ product_id: 'pdt_tool_c' },
{ product_id: 'pdt_tool_d' },
],
});
// Server route (e.g. POST /api/checkout): create the checkout session and return its URL
export async function POST() {
const session = await client.checkoutSessions.create({
product_collection_id: collection.id,
product_cart: [], // Required: pass an empty array for collection checkout
return_url: 'https://yoursite.com/return',
});
return Response.json({ checkout_url: session.checkout_url });
}
ブラウザーコードからSDKを呼び出したり、APIキーを公開したりしないでください。ブラウザーが受け取るのはサーバールートからのURLのみです。そのURLにリダイレクトします。
// Browser
const res = await fetch('/api/checkout', { method: 'POST' });
const { checkout_url } = (await res.json()) as { checkout_url: string };
window.location.href = checkout_url;
デジタル商品の提供
エンタイトルメントにより、顧客はダウンロード可能なファイルにアクセスできます。エンタイトルメントを作成し、ファイルをアップロードして、ダウンロードリンクを生成します。
// openAsBlob requires Node.js 20 or later
import { openAsBlob } from 'node:fs';
// Create an entitlement
const entitlement = await client.entitlements.create({
name: 'Pro Bundle Files',
integration_type: 'digital_files',
integration_config: {
digital_file_ids: [],
},
});
// Upload and attach a file as multipart/form-data (append `filename` before `file`)
const form = new FormData();
form.append('filename', 'pro-bundle.zip');
form.append('file', await openAsBlob('./pro-bundle.zip'), 'pro-bundle.zip');
const { file_id } = await client.entitlements.files.upload(entitlement.id, { body: form });
// Attach the entitlement to a product.
// `entitlements` REPLACES every entitlement already attached to the product:
// include all of them, send [] to remove all, or omit the field to leave them unchanged.
await client.products.update('pdt_pro_bundle', {
entitlements: [{ entitlement_id: entitlement.id }],
});
// A grant is produced by fulfillment when the customer purchases the product.
// Grants can be listed, revoked, or fulfilled with a manually managed license key;
// they cannot be created directly through the SDK.
// List grants for a customer
const grants = await client.customers.listEntitlementGrants('cus_customer_123');
// Generate a download URL (valid for roughly 15 minutes)
const grant = grants.items[0];
const downloadUrl = grant?.digital_product_delivery?.files[0]?.download_url;
短縮リンク
商品への短縮可能な共有リンクを作成します。
const shortLink = await client.products.shortLinks.create('pdt_pro_bundle', {
slug: 'pro-bundle-deal',
});
console.log(shortLink.short_url); // https://dodo.link/pro-bundle-deal
ローカライズされた価格設定
国や通貨に応じた価格設定には、localized-pricing スキルを使用します。クライアントに価格をハードコードしないでください。
よくある間違い
削除エンドポイントがあると思い込む: 商品には削除メソッドがありません。代わりにアーカイブしてください。アーカイブ済みの商品は、照合のために履歴に残ります。
価格モデルを変更しようとする: 商品の価格モデルを、買い切り、サブスクリプション、従量課金の間で切り替えることはできません(たとえば、one_time_price を recurring_price に変更することはできません)。必要なモデルで新しい商品を作成してください。
価格を編集すると既存のサブスクライバーにも新価格が適用されると思い込む: client.products.update(...) で変更される金額は、新規顧客にのみ適用されます。既存のサブスクリプションは作成時の価格で更新されます。新しいプランに移行するには、client.subscriptions.changePlan(...) を使用してください。
クライアントに価格をハードコードする: 商品カタログは常に API から取得してください。価格は変わるため、クライアントコードが古くなります。client.products.retrieve(id) を読み込んで、現在の価格を取得してください。
税区分を指定し忘れる: すべての商品に tax_category が必要です。Dodo はこれを使って売上税を計算し、納付します。省略すると、作成リクエストは失敗します。
60 秒の有効期間を過ぎてから画像をアップロードする: 署名付き URL をリクエストし、すぐにアップロードしてください。URL の有効期限が切れた場合は、新しい URL をリクエストしてください。
コレクションのチェックアウトで空ではない product_cart を渡す: コレクションのチェックアウトには、product_cart: [] が必要です。カートの内容はコレクション自体で定義します。
購買力平価とローカライズされた価格設定を混同する: PPP はネイティブ機能です。price.purchasing_power_parity: true を設定すると、事業全体に適用される価格の国別割合で請求できます(デフォルトは false)。この機能には Adaptive Currency が有効になっている必要があり、一致するローカライズ価格がある場合は常に PPP より優先されます。localized-pricing スキルを参照してください。