mcpField
C
commerce-js-integration

commerce-js-integration

github.com/finsilabs開発6074 回インストール6 日前に更新
リポジトリ

commerce-js-integration について

Commerce.js でヘッドレスストアを構築する JavaScript 開発者向けのスキルです。REST API を利用し、Promise ベースの SDK で商品、カート、チェックアウト、注文を管理します。

主な機能

  1. 01SDK による商品の取得と表示
  2. 02カートの作成と商品管理
  3. 03チェックアウトトークンの生成と注文確定
  4. 04商品バリエーションとオプションの参照
  5. 05顧客注文の取得

活用シーン

  1. 01静的サイトへのコマース機能追加
  2. 02React、Vue、Svelte でのストア構築
  3. 03MVP や概念実証での購入フロー実装
  4. 04フロントエンドへの商品・カート・決済導線の統合

インストール

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

$npx skills add https://github.com/finsilabs/awesome-ecommerce-skills --skill commerce-js-integration

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

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

tile.json
evals/cart-management-with-react-context-and-v/
criteria.json
task.md
evals/checkout-flow-with-token-generation-and-/
criteria.json
task.md
evals/product-catalog-rendering-with-safe-html/
criteria.json
task.md

詳細

Commerce.js インテグレーション

概要

Commerce.js(Chec)は、商品、カート、チェックアウト、注文を管理するための REST API をラップする JavaScript SDK を提供するヘッドレスコマースプラットフォームです。フル機能の e コマースプラットフォームの複雑さを伴わずに、あらゆるウェブサイトや JavaScript フレームワークにコマース機能を追加したい開発者向けに設計されています。SDK は、商品の取得、カートのライフサイクル管理、チェックアウトトークンの作成、注文の確定を、シンプルな Promise ベースの API で処理します。

このスキルを使う場面

  • 静的サイトやシンプルな React/Vue/Svelte プロジェクトに、すばやく e コマース機能を追加したい場合
  • 複雑なバックエンドは不要で、サーバーサイドコードを使わずにマネージドコマース API を利用したい場合
  • ポートフォリオプロジェクト、MVP、または概念実証用のヘッドレスストアを構築する場合
  • カタログの規模が小さく(商品数 1,000 件未満)、カスタムのフルフィルメントロジックが不要な場合
  • Shopify や commercetools の環境全体を管理せずに、シンプルな SDK を使いたい場合

前提条件とプラットフォームに関する注意事項

このスキルは、カスタム/ヘッドレスのストアフロント(Node.js、Python、または類似のバックエンド)向けに書かれています。コード例では TypeScript/Node.js を使用していますが、あらゆるスタックに合わせて調整できます。

Shopify:Shopify Hydrogen は Shopify のヘッドレスフレームワークです。MACH/コンポーザブルパターンは、Shopify をコマースバックエンドとしてカスタムフロントエンドを使用する場合や、Shopify を他の各分野で最適なサービスと組み合わせる場合に適用できます。 WooCommerce:WooCommerce は、REST API と WPGraphQL を介してヘッドレスバックエンドとして利用できます。これらのパターンは、フロントエンドを WordPress から分離する場合に適用できます。 Magento:Magento の GraphQL API と PWA Studio は、ヘッドレスアーキテクチャをサポートしています。これらのコンポーザブルパターンは、MACH スタックのバックエンドサービスとして Magento を利用する場合に適用できます。

必要なもの:

  • Node.js 18+(またはバックエンドの言語に合わせて調整してください)
  • Stripe アカウントと API キー
  • メール送信サービス(SendGrid、AWS SES、または Postmark)

基本手順

  1. SDK をインストールしてクライアントを初期化する

    npm install @chec/commerce.js
    
    import Commerce from '@chec/commerce.js';
    
    // Public key is safe to expose in the browser
    const commerce = new Commerce(process.env.NEXT_PUBLIC_CHEC_PUBLIC_KEY, true); // true = debug mode
    

    Chec Dashboard の Developer → API keys から公開 API キーを取得してください。

  2. 商品を取得して表示する

    // Fetch all products
    const {data: products} = await commerce.products.list({
      limit: 20,
      page: 1,
      sort_by: 'created',
      sort_direction: 'desc',
    });
    
    // Fetch a single product by permalink (slug)
    const product = await commerce.products.retrieve('my-product-slug', {type: 'permalink'});
    
    // Products include structured data
    console.log({
      id: product.id,
      name: product.name,
      price: product.price.formatted_with_symbol, // "$29.99"
      description: product.description,           // HTML string from Chec CMS
      image: product.image?.url,
      variants: product.variants,                 // Size, color options
    });
    

    React コンポーネントの例:

    import {useEffect, useState} from 'react';
    import Commerce from '@chec/commerce.js';
    
    const commerce = new Commerce(process.env.NEXT_PUBLIC_CHEC_PUBLIC_KEY!);
    
    export function ProductGrid() {
      const [products, setProducts] = useState<any[]>([]);
      const [loading, setLoading] = useState(true);
    
      useEffect(() => {
        commerce.products.list()
          .then(({data}) => setProducts(data))
          .finally(() => setLoading(false));
      }, []);
    
      if (loading) return <div>Loading...</div>;
    
      return (
        <div className="grid grid-cols-3 gap-6">
          {products.map(product => (
            <div key={product.id} className="border rounded-lg p-4">
              <img src={product.image?.url} alt={product.name} className="w-full h-48 object-cover" />
              <h2 className="mt-2 font-semibold">{product.name}</h2>
              <p className="text-gray-600">{product.price.formatted_with_symbol}</p>
              <AddToCartButton productId={product.id} />
            </div>
          ))}
        </div>
      );
    }
    
  3. カートを管理する

    Commerce.js はカート ID を自動的に localStorage に保存します:

    // Get or create the current cart
    const cart = await commerce.cart.retrieve();
    
    // Add a product to the cart
    const updatedCart = await commerce.cart.add(productId, quantity, {
      // Optional: specify variant selections
      variantId: 'variant_id_here',
      optionId: 'option_id_here',
    });
    
    // Update line item quantity
    await commerce.cart.update(lineItemId, {quantity: 3});
    
    // Remove a line item
    await commerce.cart.remove(lineItemId);
    
    // Empty the cart
    await commerce.cart.empty();
    
    // Refresh the cart
    const refreshedCart = await commerce.cart.refresh();
    

    React Context によるカート状態の管理:

    // context/cart-context.tsx
    import {createContext, useContext, useState, useEffect} from 'react';
    import Commerce from '@chec/commerce.js';
    
    const commerce = new Commerce(process.env.NEXT_PUBLIC_CHEC_PUBLIC_KEY!);
    const CartContext = createContext<any>(null);
    
    export function CartProvider({children}: {children: React.ReactNode}) {
      const [cart, setCart] = useState<any>(null);
    
      useEffect(() => { commerce.cart.retrieve().then(setCart); }, []);
    
      const addToCart = async (productId: string, quantity = 1) => {
        const {cart: updated} = await commerce.cart.add(productId, quantity);
        setCart(updated);
      };
    
      const removeFromCart = async (lineItemId: string) => {
        const {cart: updated} = await commerce.cart.remove(lineItemId);
        setCart(updated);
      };
    
      return (
        <CartContext.Provider value={{cart, addToCart, removeFromCart}}>
          {children}
        </CartContext.Provider>
      );
    }
    
    export const useCart = () => useContext(CartContext);
    
  4. チェックアウトトークンを生成して注文を確定する

    // Step 1: Generate a checkout token from the cart
    const checkoutToken = await commerce.checkout.generateToken(cart.id, {type: 'cart'});
    
    // Step 2: Get live checkout information (shipping options, taxes)
    const checkoutData = await commerce.checkout.getLive(checkoutToken.id);
    
    // Step 3: Capture the order
    const order = await commerce.checkout.capture(checkoutToken.id, {
      customer: {
        firstname: 'Jane',
        lastname: 'Doe',
        email: '[email protected]',
      },
      shipping: {
        name: 'Jane Doe',
        street: '123 Main Street',
        town_city: 'San Francisco',
        county_state: 'US-CA',
        postal_zip_code: '94103',
        country: 'US',
      },
      fulfillment: {
        shipping_method: checkoutData.shipping.available_options[0].id,
      },
      payment: {
        gateway: 'stripe',
        stripe: {
          payment_method_id: stripePaymentMethodId, // from Stripe.js
        },
      },
    });
    
    console.log(`Order #${order.customer_reference} placed!`);
    // Clear the cart after successful order
    await commerce.cart.refresh();
    
  5. 商品バリエーションを処理する

    const product = await commerce.products.retrieve(productId);
    
    // Variants are organized as variant groups (e.g., "Size") with options (e.g., "S", "M", "L")
    product.variants.forEach(variantGroup => {
      console.log(`Variant group: ${variantGroup.name}`);
      variantGroup.options.forEach(option => {
        console.log(`  - ${option.name}: +${option.price.formatted_with_symbol}`);
      });
    });
    
    // When adding to cart, provide the full variant selection
    const selections = {
      [sizeVariantGroupId]: selectedSizeOptionId,
      [colorVariantGroupId]: selectedColorOptionId,
    };
    
    await commerce.cart.add(product.id, 1, selections);
    
  6. 注文を一覧表示する

    // Requires a customer JWT (obtained via commerce.customer.login)
    const customerToken = await commerce.customer.login(email, password);
    
    const {data: orders} = await commerce.orders.getAllForCustomer({
      customer_token: customerToken.token,
    });
    
    orders.forEach(order => {
      console.log({
        reference: order.customer_reference,
        status: order.status,
        total: order.order_value.formatted_with_symbol,
        items: order.order.line_items.map(item => item.product_name),
      });
    });
    

例

Next.js の商品ページ全体
// app/products/[permalink]/page.tsx
import Commerce from '@chec/commerce.js';
import DOMPurify from 'isomorphic-dompurify';

// Server-side: use secret key to fetch product at build time
const commerce = new Commerce(process.env.CHEC_SECRET_KEY!);

export async function generateStaticParams() {
  const {data: products} = await commerce.products.list({limit: 200});
  return products.map((p: any) => ({permalink: p.permalink}));
}

export default async function ProductPage({params}: {params: {permalink: string}}) {
  const product = await commerce.products.retrieve(params.permalink, {type: 'permalink'});
  // Sanitize HTML from Chec CMS before rendering
  const safeDescription = DOMPurify.sanitize(product.description ?? '');

  return (
    <main>
      <img src={product.image?.url} alt={product.name} />
      <h1>{product.name}</h1>
      {/* Sanitized HTML rendered safely */}
      <div dangerouslySetInnerHTML={{__html: safeDescription}} />
      <p className="text-xl font-bold">{product.price.formatted_with_symbol}</p>
    </main>
  );
}
カートの商品数バッジ
export function CartBadge() {
  const {cart} = useCart();
  const totalItems = cart?.total_unique_items ?? 0;

  return (
    <button className="relative">
      <ShoppingCartIcon />
      {totalItems > 0 && (
        <span className="absolute -top-2 -right-2 bg-red-500 text-white text-xs rounded-full w-5 h-5 flex items-center justify-center">
          {totalItems}
        </span>
      )}
    </button>
  );
}

ベストプラクティス

  • クライアントでは公開キー、サーバーではシークレットキーを使用する — 公開キーでは商品の読み取りとカートの管理のみが可能です。シークレットキーでは注文も管理できますが、ブラウザーのコードに公開してはいけません
  • Commerce クライアントは一度だけ初期化する — モジュールレベルの定数としてインスタンスを一つだけ作成してインポートし、レンダリングのたびに新しいインスタンスを作成しないでください
  • 注文自動化のために webhook イベントを処理する — ダッシュボードで Chec webhook を設定し、注文の作成、更新、返金時にサーバーへ POST するようにしてください
  • チェックアウトにはカート ID ではなくチェックアウトトークンを使用する — generateToken は有効期限付きのチェックアウト専用トークンを作成します。capture には必ずこのトークンを渡し、生のカート ID は渡さないでください
  • 注文確定後にカートを更新する — 注文が正常に処理された後で commerce.cart.refresh() を呼び出すと、新しい空のカートが作成されます。以前のカート ID は無効になります
  • 商品説明の HTML は表示前にサニタイズする — 商品説明は Chec CMS の HTML です。表示前に必ず DOMPurify(isomorphic-dompurify は Node.js とブラウザーの両方で動作します)を通してください
  • チェックアウト確定時のエラー処理を実装する — commerce.checkout.capture を try/catch で囲み、commerce.error コードをユーザーにわかりやすいメッセージに対応付けてください

よくある落とし穴

問題解決策
Commerce is not a constructorimport Commerce from '@chec/commerce.js' のようにインポートしてください(名前付きインポートではなくデフォルトインポートです)。
ページを再読み込みするとカートが保持されないCommerce.js は chec_cart_id というキーでカートを localStorage に保存します。localStorage が利用可能で、ブロックされていないことを確認してください。
チェックアウトトークンの有効期限が切れるチェックアウトトークンの有効期限は7日間です。チェックアウトを開始する直前に、カートから新しいトークンを生成してください。
バリエーションの選択が適用されない選択内容を commerce.cart.add() の第3引数として渡してください:commerce.cart.add(id, qty, {variantGroupId: optionId})
決済ゲートウェイが設定されていないChec Dashboard の Setup → Payment gateways で、決済ゲートウェイ(Stripe、Square など)を有効にして設定してください。

関連スキル

  • @jamstack-storefront
  • @shopify-hydrogen
  • @secure-checkout
  • @webhook-architecture

コメント