mcpField
C
commerce-js-integration

commerce-js-integration

github.com/finsilabs开发6074 次安装5 天前更新
代码仓库

关于 commerce-js-integration

面向构建无头商店的 JavaScript 开发者,介绍 Commerce.js SDK 如何通过 REST API 管理商品、购物车、结账和订单,并以 promise API 简化集成。

主要功能

  1. 01通过 SDK 获取和展示商品
  2. 02创建购物车并管理商品项
  3. 03生成结账令牌并捕获订单
  4. 04读取商品变体及选项
  5. 05查询客户订单

适用场景

  1. 01为静态网站添加电商功能
  2. 02在 React、Vue 或 Svelte 项目中构建商店
  3. 03为 MVP 或概念验证实现购物流程
  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)是一个无头商务平台,提供 JavaScript SDK 来封装其 REST API,以管理产品、购物车、结账和订单。它面向希望在任何网站或 JavaScript 框架中添加商务功能,同时无需应对完整电商平台复杂性的开发者。该 SDK 通过简洁的、基于 Promise 的 API 处理产品获取、购物车生命周期、结账令牌创建和订单捕获。

何时使用此技能

  • 想要快速为静态网站或简单的 React/Vue/Svelte 项目添加电商功能时
  • 不需要复杂的后端,并希望使用托管的商务 API,而无需编写服务器端代码时
  • 构建作品集项目、MVP 或概念验证无头商店时
  • 产品目录规模较小(少于 1,000 件产品),且不需要自定义履约逻辑时
  • 想要使用简单的 SDK,而无需管理完整的 Shopify 或 commercetools 环境时

前提条件和平台说明

此技能面向自定义/无头店面(Node.js、Python 或类似后端)。代码示例使用 TypeScript/Node.js,可适配任何技术栈。

Shopify:Shopify Hydrogen 是 Shopify 的无头框架。使用 Shopify 作为商务后端搭配自定义前端,或将 Shopify 与其他同类最佳服务混用时,适用 MACH/可组合模式。 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 和浏览器环境)进行处理
  • 为结账捕获实现错误处理——使用 try/catch 包裹 commerce.checkout.capture,并将 commerce.error 代码映射为用户易懂的提示信息

常见问题

问题解决方案
Commerce is not a constructor确保使用 import Commerce from '@chec/commerce.js' 导入(默认导入,而非命名导入)
页面加载后购物车未保留Commerce.js 会将购物车存储在 localStorage 的 chec_cart_id 中;请确保 localStorage 可用且未被阻止
结账令牌已过期结账令牌会在 7 天后过期;开始结账前,请立即从购物车生成新令牌
未应用变体选项将选项作为第三个参数传递给 commerce.cart.add():commerce.cart.add(id, qty, {variantGroupId: optionId})
未配置支付网关在 Chec Dashboard 的 Setup → Payment gateways 下启用并配置支付网关(Stripe、Square 等)

相关技能

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

评论

支持 Markdown