commerce-js-integration について
Commerce.js でヘッドレスストアを構築する JavaScript 開発者向けのスキルです。REST API を利用し、Promise ベースの SDK で商品、カート、チェックアウト、注文を管理します。
主な機能
- 01SDK による商品の取得と表示
- 02カートの作成と商品管理
- 03チェックアウトトークンの生成と注文確定
- 04商品バリエーションとオプションの参照
- 05顧客注文の取得
活用シーン
- 01静的サイトへのコマース機能追加
- 02React、Vue、Svelte でのストア構築
- 03MVP や概念実証での購入フロー実装
- 04フロントエンドへの商品・カート・決済導線の統合
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ finsilabs/ awesome-ecommerce-skills --skill commerce-js-integrationインストール後、Claude Code で/commerce-js-integrationを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
tile.jsonevals/cart-management-with-react-context-and-v/criteria.jsontask.mdevals/checkout-flow-with-token-generation-and-/criteria.jsontask.mdevals/product-catalog-rendering-with-safe-html/criteria.jsontask.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)
基本手順
-
SDK をインストールしてクライアントを初期化する
npm install @chec/commerce.jsimport 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 modeChec Dashboard の Developer → API keys から公開 API キーを取得してください。
-
商品を取得して表示する
// 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> ); } -
カートを管理する
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); -
チェックアウトトークンを生成して注文を確定する
// 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(); -
商品バリエーションを処理する
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); -
注文を一覧表示する
// 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 constructor | import 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