product-bundles-kits について
EC ストアの商品バンドルやキットの設計・実装を案内するスキルです。各プラットフォームのツール、価格設定、構成品在庫、商品ページ表示、カスタムのヘッドレス構成を扱います。
主な機能
- 01固定・設定可能なバンドル
- 02バンドル価格と割引
- 03構成品単位の在庫管理
- 04商品ページでのバンドル表示
- 05販売可能性の検証案内
活用シーン
- 01割引付き商品セットの販売
- 02自由に選べるキットの作成
- 03関連商品の提案追加
- 04カスタムキット画面の構築
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ finsilabs/ awesome-ecommerce-skills --skill product-bundles-kitsインストール後、Claude Code で/product-bundles-kitsを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
tile.jsonevals/bundle-cart-line-items-and-availability/criteria.jsontask.mdevals/bundle-pdp-display-and-cart-grouping/criteria.jsontask.mdevals/bundle-pricing-calculation/criteria.jsontask.md詳細
商品バンドルとキット
概要
商品バンドルを利用すると、複数の商品を割引価格でまとめるかどうかを選択して、1つの購入単位として販売できます。これにより平均注文額が増加し、eコマースで最も効果的なアップセル手法の1つとなります。専用のバンドルアプリは、プラットフォームに標準搭載されていない在庫管理、価格設定、表示ロジックを処理します。キットの構成要件(動的な組み立て、リアルタイムの価格設定、構成商品の差し替え)がアプリの機能を超える場合に限り、カスタムのバンドルシステムを構築してください。
このスキルを使用する場面
- 「よく一緒に購入される商品」や「コーディネートを完成させる」機能を実装するとき
- 商品キット(例:カメラ本体+レンズ+バッグのバンドル)を販売するとき
- 商品セットをまとめて購入すると割引を提供するとき
- 購入者が選択肢から独自のセットを組み立てるカスタムキットビルダーを構築するとき
主な手順
ステップ1:プラットフォームを特定し、適切なツールを選ぶ
| プラットフォーム | 推奨ツール | 理由 |
|---|---|---|
| Shopify | Bundler — Product Bundles または Bundle Builder | 最も人気のあるバンドルアプリです。固定バンドル、組み合わせ自由のバンドル、構成商品ごとの在庫差し引きに対応しています |
| WooCommerce | WooCommerce Product Bundles(WooCommerce 拡張機能) | WooCommerce 公式の拡張機能です。固定バンドルと構成可能なバンドル、構成商品ごとの価格設定、在庫管理に対応しています |
| BigCommerce | Product Bundler アプリ、または標準の「よく一緒に購入される商品」 | BigCommerce App Marketplace には複数のバンドルアプリがあります。シンプルなクロスセルには標準の「よく一緒に購入される商品」を利用できます |
| カスタム/ヘッドレス | 構成商品の在庫差し引きを含むバンドルデータモデルを構築 | バンドルのロジック(リアルタイムの価格設定、動的な構成商品の差し替え)がアプリの機能を超える場合に必要です |
ステップ2:プラットフォームごとの設定
Shopify
オプションA:Bundler — Product Bundles(推奨)
- Shopify App Store から Bundler — Product Bundles をインストールします
- Bundler → Bundles → Create bundle に移動します
- バンドルの種類を選択します:
- 固定バンドル:あらかじめ定義された商品セット(例:「スキンケアスターターキット」)
- 組み合わせ自由:購入者がコレクションから X 個の商品を選びます
- ボックスを作成:購入者が自由な組み合わせでボックスを埋めます
- 構成商品を追加し、数量を設定します
- 価格を設定します:
- 割引率(例:合計価格から15%割引)
- 固定バンドル価格(例:構成商品の価格にかかわらず $49.99)
- 単品価格の合計(割引なし。まとめて購入できる利便性のみを提供)
- Bundler が在庫を管理します。バンドルが購入されると、各構成商品の在庫数が自動的に差し引かれます
「よく一緒に購入される商品」の場合:
- Code Black Belt の Frequently Bought Together(App Store)をインストールします
- アプリが注文履歴を分析し、商品詳細ページにバンドル候補の商品を自動的に提案します
- 手動で設定しなくても、そのまま利用できます
Shopify Plus での重要事項:
- バンドルをカート内で単一の明細項目として表示する必要がある場合(ロイヤルティポイントや割引対象外の設定など)は、Shopify Bundles(標準機能。管理画面の Products → Bundles から利用可能)を検討してください
- Shopify 標準の Bundles 商品タイプでは、購入者が購入する親 SKU が作成され、構成商品の在庫が自動的に差し引かれます
WooCommerce
WooCommerce Product Bundles(公式拡張機能):
- WooCommerce.com から WooCommerce Product Bundles をインストールします
- 新しい商品を作成します:Products → Add Product → Product type: Bundle
- Bundled Products タブに移動します
- 各構成商品を追加します:
- 商品を検索して選択します
- デフォルトの数量と、オプションにするかどうか(カスタマイズ可能なバンドルの場合)を設定します
- 構成商品を別々の場所から発送する場合は、Ship separately を有効にします
- General タブでバンドル価格を設定します:
- Per-item pricing:バンドル価格 = 構成商品の価格の合計(任意で割引を適用)
- Static bundle pricing:バンドルの固定価格を入力します
- 在庫:販売可能なバンドル数を制限する場合(構成商品の在庫状況に基づく)は、Manage stock? を有効にします
「よく一緒に購入される商品」の場合:
- WooCommerce Frequently Bought Together をインストールします(無料版または有料版を利用できます)
- または YITH WooCommerce Frequently Bought Together を使用します
BigCommerce
App Marketplace のバンドルアプリ:
- BigCommerce 管理画面の Apps セクションで "Bundle" を検索します
- Bold Bundles は広く利用されています。インストールして、既存の SKU に紐付けたバンドル商品を設定します
- 割引ルールを設定します(割引率、固定価格、BOGO)
- Bold Bundles は各構成商品の在庫を自動的に減算します
標準のクロスセル/「よく一緒に購入される商品」:
- Products → [Product] → Related Products tab に移動します
- 関連商品を追加します。これらは PDP におすすめ商品として表示されます
- 自動割引はありませんが、顧客は各商品を個別にカートに追加できます
カスタム/ヘッドレス
ヘッドレスストアフロントでは、構成商品を個別のカート明細として保存するバンドルモデルを構築します(在庫、税金、フルフィルメントを簡素化できます):
// lib/bundles.ts
interface BundleComponent { variantId: string; quantity: number; unitPrice: number; }
interface BundlePricing { componentSum: number; bundlePrice: number; savings: number; savingsPct: number; }
// Calculate bundle price dynamically from current component prices
export async function calculateBundlePrice(bundle: Bundle, selectedVariants: BundleComponent[]): Promise<BundlePricing> {
const componentSum = selectedVariants.reduce((total, c) => total + c.unitPrice * c.quantity, 0);
let bundlePrice: number;
switch (bundle.pricingType) {
case 'fixed': bundlePrice = bundle.pricingValue; break;
case 'discount_pct': bundlePrice = +(componentSum * (1 - bundle.pricingValue / 100)).toFixed(2); break;
case 'discount_abs': bundlePrice = Math.max(0, +(componentSum - bundle.pricingValue).toFixed(2)); break;
default: bundlePrice = componentSum;
}
const savings = +(componentSum - bundlePrice).toFixed(2);
return { componentSum, bundlePrice, savings, savingsPct: componentSum > 0 ? Math.round((savings / componentSum) * 100) : 0 };
}
// Check availability for all bundle components atomically
export async function checkBundleAvailability(selectedVariants: BundleComponent[]) {
const unavailable = [];
for (const { variantId, quantity } of selectedVariants) {
const level = await db.inventoryLevels.findFirst({ where: { variantId } });
const available = (level?.onHand ?? 0) - (level?.reserved ?? 0);
if (available < quantity) {
unavailable.push({ variantId, requested: quantity, available: Math.max(0, available) });
}
}
return { available: unavailable.length === 0, unavailable };
}
// Add bundle to cart as separate line items (grouped by bundle ID for UI display)
export async function addBundleToCart(cartId: string, bundleId: string, selectedVariants: BundleComponent[]) {
const bundle = await db.productBundles.findUnique({ where: { id: bundleId } });
const pricing = await calculateBundlePrice(bundle, selectedVariants);
const { available, unavailable } = await checkBundleAvailability(selectedVariants);
if (!available) throw new Error(`Bundle not available: ${unavailable.map(u => u.variantId).join(', ')}`);
// Create a bundle group record to keep line items visually associated
const bundleGroup = await db.cartBundleGroups.create({
data: { cartId, bundleId, bundlePrice: pricing.bundlePrice, bundleSavings: pricing.savings },
});
// Pro-rate the discount across components proportionally to their share of the total
const lineItems = selectedVariants.map(({ variantId, quantity, unitPrice }) => {
const itemSubtotal = unitPrice * quantity;
const discountShare = pricing.savings * (itemSubtotal / pricing.componentSum);
const discountedUnitPrice = +(unitPrice - discountShare / quantity).toFixed(4);
return { cartId, variantId, quantity, bundleGroupId: bundleGroup.id, unitPrice: discountedUnitPrice };
});
await db.cartItems.createMany({ data: lineItems });
return bundleGroup;
}
ステップ 3:商品ページでのバンドル表示を設定する
バンドル表示のチェックリスト:
- 割引の価値が伝わるように、「価値:$149|バンドル価格:$119 — $30 お得(20% オフ)」と表示します
- 商品画像と商品名を添えて、含まれる商品を表示します
- 在庫状況を表示します。構成商品のいずれかが在庫切れの場合は、該当商品を示し、代替商品を提案します
- カートページでは、バンドル名と合計割引額のバッジを付けた見出しの下に、バンドルの明細をまとめて表示します
**Shopify/Bundler の場合:**アプリがバンドル商品ページを自動生成します。テンプレートは Bundler → Customize でカスタマイズします
**WooCommerce Product Bundles の場合:**プラグインがバンドル専用の商品ページレイアウトを表示します。組み込みの CSS 設定または子テーマのオーバーライドを使ってスタイルを調整します
ステップ 4:在庫が正しく減算されることを確認する
バンドルの設定後、各構成商品の在庫が正しく減算されることをテストします:
- バンドルのテスト注文を行います
- 各構成商品の在庫数を確認します。それぞれ、バンドル内の構成商品の数量分だけ減っている必要があります
- 特殊なケースをテストします。ある構成商品の残り在庫がちょうど 1 個のバンドルで、バンドルを 2 つ注文すると、2 つ目の注文は失敗する必要があります
ベストプラクティス
- 一から構築するのではなく、アプリを使います — Shopify 向けの Bundler と WooCommerce Product Bundles は、正しく構築するのに数週間かかるエッジケース(分割フルフィルメント、一部のみ在庫あり、返金)に対応しています
- バンドルの在庫状況は必ずサーバー側で検証します — カートに追加する前に、すべての構成商品の在庫が十分にあることをアトミックに確認します。すべての構成商品が在庫ありの場合にのみ、バンドルを購入できます
- チェックアウト時にバンドル価格を再計算します — クライアントから送信されたバンドル価格を決して信用せず、注文時に現在の構成商品価格から再計算します
- 商品ごとの価格とバンドル価格を並べて表示します — 顧客が節約額の基準を確認できて初めて、割引に意味が生まれます
- バンドルの構成商品数は 2~5 個にします — 大きすぎるバンドルは買い物客を混乱させ、分かりやすく商品を見せるのも難しくなります
よくある落とし穴
| 問題 | 解決策 |
|---|---|
| チェックアウト時にセット割引が正しく適用されない | アプリに組み込まれた割引ロジックを使用してください。セット割引に追加の割引コードを重ねる場合は、組み合わせたときの動作をテストしてください |
| カートに入れた後、セット商品の構成商品の1つが在庫切れになる | チェックアウト時に在庫状況を再確認してください。在庫切れの構成商品を具体的に表示し、顧客が調整できるようにしてください |
| 構成商品の価格が変わってもセット価格が更新されない | 商品をカートに追加するときだけでなく、カートの更新時とチェックアウト時にもセット価格を再計算してください |
| フルフィルメントシステムがセット商品の明細を正しく処理できない | ヘッドレス構成では、各明細に標準の variant_id と quantity が設定されていることを確認してください。bundle_group_id はUIでのグループ化にのみ使用してください |
| セット商品のすべての構成商品について在庫が引き落とされない | セットアップ後は必ず実際の注文でテストし、各構成商品のSKUの在庫が正しい数量分だけ引き落とされることを確認してください |
関連スキル
- @variant-matrix
- @inventory-tracking
- @product-content-enrichment