mcpField
W
woocommerce

woocommerce

github.com/alinaqi開発711389 回インストール3 週間前に更新
リポジトリ

woocommerce について

REST API を使って WooCommerce ストアと連携するための開発ガイドです。商品、注文、顧客、Webhook などを扱い、Node.js と Python の例を示します。

主な機能

  1. 01REST API 認証の設定
  2. 02商品と在庫の操作
  3. 03注文と顧客の管理
  4. 04Webhook の作成と署名検証
  5. 05クーポン、レポート、ページネーション
  6. 06Node.js と Python の例

活用シーン

  1. 01アプリケーションと WooCommerce ストアの接続
  2. 02API による商品と在庫の管理
  3. 03注文と顧客データの処理
  4. 04ストアの Webhook の受信と検証

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/alinaqi/maggy --skill woocommerce

インストール後、Claude Code で/woocommerceを入力して読み込まれたか確認します。

詳細

WooCommerce 開発スキル

REST API を介して WooCommerce ストアと連携するためのガイドです。商品、注文、顧客、Webhook、カスタム拡張機能を扱います。

ソース: WooCommerce REST API | 開発者向けドキュメント


前提条件

ストアの要件
# WooCommerce store must have:
# 1. WordPress with WooCommerce plugin installed
# 2. HTTPS enabled (required for API auth)
# 3. Permalinks set to anything except "Plain"
#    WordPress Admin → Settings → Permalinks → Post name (recommended)
API キーを生成する
  1. WooCommerce → 設定 → 高度な設定 → REST API に移動します
  2. キーを追加 をクリックします
  3. 説明、ユーザー(管理者)、権限(読み取り/書き込み)を設定します
  4. API キーを生成 をクリックします
  5. Consumer Key と Consumer Secret をコピーします(この画面で一度だけ表示されます)

API の基本

ベース URL
https://your-store.com/wp-json/wc/v3/
認証
// Node.js - Basic Auth (recommended)
const WooCommerceRestApi = require("@woocommerce/woocommerce-rest-api").default;

const api = new WooCommerceRestApi({
  url: "https://your-store.com",
  consumerKey: process.env.WC_CONSUMER_KEY,
  consumerSecret: process.env.WC_CONSUMER_SECRET,
  version: "wc/v3"
});
# Python
from woocommerce import API

wcapi = API(
    url="https://your-store.com",
    consumer_key=os.environ["WC_CONSUMER_KEY"],
    consumer_secret=os.environ["WC_CONSUMER_SECRET"],
    version="wc/v3"
)
クエリ文字列による認証(代替手段)
# Only use if Basic Auth fails (some hosting configurations)
curl https://your-store.com/wp-json/wc/v3/products \
  ?consumer_key=ck_xxx&consumer_secret=cs_xxx

インストール

Node.js
npm install @woocommerce/woocommerce-rest-api
// lib/woocommerce.ts
import WooCommerceRestApi from "@woocommerce/woocommerce-rest-api";

const api = new WooCommerceRestApi({
  url: process.env.WC_STORE_URL!,
  consumerKey: process.env.WC_CONSUMER_KEY!,
  consumerSecret: process.env.WC_CONSUMER_SECRET!,
  version: "wc/v3",
  queryStringAuth: false, // Set true for HTTP (dev only)
});

export default api;
Python
pip install woocommerce
# lib/woocommerce.py
import os
from woocommerce import API

wcapi = API(
    url=os.environ["WC_STORE_URL"],
    consumer_key=os.environ["WC_CONSUMER_KEY"],
    consumer_secret=os.environ["WC_CONSUMER_SECRET"],
    version="wc/v3",
    timeout=30
)

商品

商品一覧の取得
// Node.js
async function getProducts(page = 1, perPage = 20) {
  const response = await api.get("products", {
    page,
    per_page: perPage,
    status: "publish",
  });
  return response.data;
}

// With filters
async function searchProducts(search: string, category?: number) {
  const response = await api.get("products", {
    search,
    category: category || undefined,
    orderby: "popularity",
    order: "desc",
  });
  return response.data;
}
# Python
def get_products(page=1, per_page=20):
    response = wcapi.get("products", params={
        "page": page,
        "per_page": per_page,
        "status": "publish"
    })
    return response.json()
単一商品の取得
async function getProduct(productId: number) {
  const response = await api.get(`products/${productId}`);
  return response.data;
}
商品の作成
async function createProduct(data: ProductInput) {
  const response = await api.post("products", {
    name: data.name,
    type: "simple", // simple, variable, grouped, external
    regular_price: data.price.toString(),
    description: data.description,
    short_description: data.shortDescription,
    categories: data.categoryIds.map(id => ({ id })),
    images: data.images.map(url => ({ src: url })),
    manage_stock: true,
    stock_quantity: data.stockQuantity,
    status: "publish",
  });
  return response.data;
}
商品の更新
async function updateProduct(productId: number, data: Partial<ProductInput>) {
  const response = await api.put(`products/${productId}`, data);
  return response.data;
}

// Update stock only
async function updateStock(productId: number, quantity: number) {
  const response = await api.put(`products/${productId}`, {
    stock_quantity: quantity,
  });
  return response.data;
}
商品の削除
async function deleteProduct(productId: number, force = false) {
  // force: true = permanent delete, false = move to trash
  const response = await api.delete(`products/${productId}`, {
    force,
  });
  return response.data;
}
バリエーションのある商品
// Create variable product
async function createVariableProduct(data: VariableProductInput) {
  // 1. Create product with type "variable"
  const product = await api.post("products", {
    name: data.name,
    type: "variable",
    attributes: [
      {
        name: "Size",
        visible: true,
        variation: true,
        options: ["Small", "Medium", "Large"],
      },
      {
        name: "Color",
        visible: true,
        variation: true,
        options: ["Red", "Blue"],
      },
    ],
  });

  // 2. Create variations
  for (const variant of data.variants) {
    await api.post(`products/${product.data.id}/variations`, {
      regular_price: variant.price.toString(),
      stock_quantity: variant.stock,
      attributes: [
        { name: "Size", option: variant.size },
        { name: "Color", option: variant.color },
      ],
    });
  }

  return product.data;
}

// Get variations
async function getVariations(productId: number) {
  const response = await api.get(`products/${productId}/variations`);
  return response.data;
}

注文

注文一覧の取得
async function getOrders(params: OrderQueryParams = {}) {
  const response = await api.get("orders", {
    page: params.page || 1,
    per_page: params.perPage || 20,
    status: params.status || "any", // pending, processing, completed, etc.
    after: params.after, // ISO date string
    before: params.before,
  });
  return response.data;
}

// Get recent orders
async function getRecentOrders(days = 7) {
  const after = new Date();
  after.setDate(after.getDate() - days);

  const response = await api.get("orders", {
    after: after.toISOString(),
    orderby: "date",
    order: "desc",
  });
  return response.data;
}
単一注文の取得
async function getOrder(orderId: number) {
  const response = await api.get(`orders/${orderId}`);
  return response.data;
}
注文の作成
async function createOrder(data: OrderInput) {
  const response = await api.post("orders", {
    payment_method: "stripe",
    payment_method_title: "Credit Card",
    set_paid: false,
    billing: {
      first_name: data.customer.firstName,
      last_name: data.customer.lastName,
      email: data.customer.email,
      phone: data.customer.phone,
      address_1: data.billing.address1,
      city: data.billing.city,
      state: data.billing.state,
      postcode: data.billing.postcode,
      country: data.billing.country,
    },
    shipping: {
      first_name: data.customer.firstName,
      last_name: data.customer.lastName,
      address_1: data.shipping.address1,
      city: data.shipping.city,
      state: data.shipping.state,
      postcode: data.shipping.postcode,
      country: data.shipping.country,
    },
    line_items: data.items.map(item => ({
      product_id: item.productId,
      variation_id: item.variationId,
      quantity: item.quantity,
    })),
    shipping_lines: [
      {
        method_id: "flat_rate",
        method_title: "Flat Rate",
        total: data.shippingCost.toString(),
      },
    ],
  });
  return response.data;
}
注文ステータスの更新
async function updateOrderStatus(orderId: number, status: OrderStatus) {
  const response = await api.put(`orders/${orderId}`, {
    status, // pending, processing, on-hold, completed, cancelled, refunded, failed
  });
  return response.data;
}

// Add order note
async function addOrderNote(orderId: number, note: string, customerNote = false) {
  const response = await api.post(`orders/${orderId}/notes`, {
    note,
    customer_note: customerNote, // true = visible to customer
  });
  return response.data;
}
注文ステータス
ステータス説明
pending支払い待ち
processing支払い済み、処理待ち
on-hold対応待ち(在庫、支払い確認)
completed注文の処理が完了
cancelled管理者または顧客によってキャンセル
refunded返金済み
failed支払い失敗

顧客

顧客一覧の取得
async function getCustomers(params: CustomerQueryParams = {}) {
  const response = await api.get("customers", {
    page: params.page || 1,
    per_page: params.perPage || 20,
    role: "customer",
    orderby: "registered_date",
    order: "desc",
  });
  return response.data;
}

// Search customers
async function searchCustomers(email: string) {
  const response = await api.get("customers", {
    email,
  });
  return response.data;
}
顧客の作成
async function createCustomer(data: CustomerInput) {
  const response = await api.post("customers", {
    email: data.email,
    first_name: data.firstName,
    last_name: data.lastName,
    username: data.email.split("@")[0],
    billing: {
      first_name: data.firstName,
      last_name: data.lastName,
      email: data.email,
      phone: data.phone,
      address_1: data.address1,
      city: data.city,
      state: data.state,
      postcode: data.postcode,
      country: data.country,
    },
    shipping: {
      // Same as billing or different
    },
  });
  return response.data;
}
顧客の更新
async function updateCustomer(customerId: number, data: Partial<CustomerInput>) {
  const response = await api.put(`customers/${customerId}`, data);
  return response.data;
}

Webhook

Webhook の作成
async function createWebhook(topic: string, deliveryUrl: string) {
  const response = await api.post("webhooks", {
    name: `Webhook for ${topic}`,
    topic, // order.created, order.updated, product.created, etc.
    delivery_url: deliveryUrl,
    status: "active",
    secret: process.env.WC_WEBHOOK_SECRET,
  });
  return response.data;
}
Webhook トピック
トピックトリガー
order.created新しい注文が行われたとき
order.updated注文のステータスまたは詳細が変更されたとき
order.deleted注文が削除されたとき
product.created新しい商品が作成されたとき
product.updated商品が更新されたとき
product.deleted商品が削除されたとき
customer.created新しい顧客が登録されたとき
customer.updated顧客が更新されたとき
coupon.created新しいクーポンが作成されたとき
Webhook 署名の検証
// Express.js webhook handler
import crypto from "crypto";

function verifyWooCommerceWebhook(req: Request): boolean {
  const signature = req.headers["x-wc-webhook-signature"] as string;
  const payload = JSON.stringify(req.body);

  const expectedSignature = crypto
    .createHmac("sha256", process.env.WC_WEBHOOK_SECRET!)
    .update(payload)
    .digest("base64");

  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expectedSignature)
  );
}

// Route handler
app.post("/webhooks/woocommerce", (req, res) => {
  if (!verifyWooCommerceWebhook(req)) {
    return res.status(401).json({ error: "Invalid signature" });
  }

  const topic = req.headers["x-wc-webhook-topic"];
  const payload = req.body;

  switch (topic) {
    case "order.created":
      handleNewOrder(payload);
      break;
    case "order.updated":
      handleOrderUpdate(payload);
      break;
    // ... other topics
  }

  res.status(200).json({ received: true });
});
# Python/Flask webhook handler
import hmac
import hashlib
import base64

@app.route("/webhooks/woocommerce", methods=["POST"])
def woocommerce_webhook():
    signature = request.headers.get("X-WC-Webhook-Signature")
    payload = request.get_data()

    expected = base64.b64encode(
        hmac.new(
            os.environ["WC_WEBHOOK_SECRET"].encode(),
            payload,
            hashlib.sha256
        ).digest()
    ).decode()

    if not hmac.compare_digest(signature, expected):
        return {"error": "Invalid signature"}, 401

    topic = request.headers.get("X-WC-Webhook-Topic")
    data = request.json

    if topic == "order.created":
        handle_new_order(data)
    elif topic == "order.updated":
        handle_order_update(data)

    return {"received": True}, 200

カテゴリーとタグ

カテゴリー一覧の取得
async function getCategories() {
  const response = await api.get("products/categories", {
    per_page: 100,
    orderby: "name",
  });
  return response.data;
}

// Create category
async function createCategory(name: string, parentId?: number) {
  const response = await api.post("products/categories", {
    name,
    parent: parentId || 0,
  });
  return response.data;
}
タグ一覧の取得
async function getTags() {
  const response = await api.get("products/tags", {
    per_page: 100,
  });
  return response.data;
}

クーポン

クーポンの作成
async function createCoupon(data: CouponInput) {
  const response = await api.post("coupons", {
    code: data.code,
    discount_type: data.type, // percent, fixed_cart, fixed_product
    amount: data.amount.toString(),
    individual_use: true,
    exclude_sale_items: false,
    minimum_amount: data.minimumAmount?.toString(),
    maximum_amount: data.maximumAmount?.toString(),
    usage_limit: data.usageLimit,
    usage_limit_per_user: 1,
    date_expires: data.expiresAt, // ISO date string
  });
  return response.data;
}

レポート

売上レポート
async function getSalesReport(period = "month") {
  const response = await api.get("reports/sales", {
    period, // day, week, month, year
  });
  return response.data;
}

// Top sellers
async function getTopSellers(period = "month") {
  const response = await api.get("reports/top_sellers", {
    period,
  });
  return response.data;
}

ページネーション

大規模なデータセットの処理
async function getAllProducts() {
  const allProducts = [];
  let page = 1;
  const perPage = 100;

  while (true) {
    const response = await api.get("products", {
      page,
      per_page: perPage,
    });

    allProducts.push(...response.data);

    // Check headers for total pages
    const totalPages = parseInt(response.headers["x-wp-totalpages"]);
    if (page >= totalPages) break;

    page++;
  }

  return allProducts;
}
ページネーションヘッダー
ヘッダー説明
X-WP-Total項目の合計数
X-WP-TotalPagesページの合計数

エラー処理

import WooCommerceRestApi from "@woocommerce/woocommerce-rest-api";

async function safeApiCall<T>(
  operation: () => Promise<{ data: T }>
): Promise<T> {
  try {
    const response = await operation();
    return response.data;
  } catch (error: any) {
    if (error.response) {
      // API returned an error
      const { status, data } = error.response;

      switch (status) {
        case 400:
          throw new Error(`Bad request: ${data.message}`);
        case 401:
          throw new Error("Invalid API credentials");
        case 404:
          throw new Error("Resource not found");
        case 429:
          // Rate limited - wait and retry
          await new Promise(r => setTimeout(r, 5000));
          return safeApiCall(operation);
        default:
          throw new Error(`API error: ${data.message}`);
      }
    }
    throw error;
  }
}

// Usage
const products = await safeApiCall(() => api.get("products"));

環境変数

# .env
WC_STORE_URL=https://your-store.com
WC_CONSUMER_KEY=ck_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
WC_CONSUMER_SECRET=cs_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
WC_WEBHOOK_SECRET=your_webhook_secret

credentials.md に追加します:

'WC_CONSUMER_KEY': r'ck_[a-f0-9]{40}',
'WC_CONSUMER_SECRET': r'cs_[a-f0-9]{40}',

チェックリスト

連携前
  • WooCommerce プラグインがインストールされ、有効化されている
  • ストアで HTTPS が有効になっている
  • パーマリンクが「基本」以外に設定されている
  • 適切な権限を持つ API キーが生成されている
  • Webhook シークレットが設定されている
セキュリティ
  • API キーが環境変数に保存されている
  • Webhook 署名が検証されている
  • すべての API 呼び出しで HTTPS が使用されている
  • レート制限に対応している
テスト
  • API 接続をテストする
  • 商品の CRUD 操作をテストする
  • 注文の作成と更新をテストする
  • Webhook の配信をテストする
  • 大規模なデータセットでページネーションをテストする

避けるべきパターン

  • 基本パーマリンク - パーマリンクを適切に設定しないと API は動作しません
  • 本番環境での HTTP - 必ず HTTPS を使用してください
  • レート制限を無視する - WooCommerce によってリクエストが制限される場合があります
  • 単一リクエストで大量のデータを取得する - 一括操作ではページネーションを使用してください
  • コード内にキーを保存する - 環境変数を使用してください
  • Webhook の検証を省略する - 必ず署名を検証してください

コメント