mcpField
S
shopify-checkout-extensions

shopify-checkout-extensions

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

关于 shopify-checkout-extensions

这是面向 Shopify 应用开发者的结账扩展指南,介绍如何在结账界面添加 UI 区块,以及用 Shopify Functions 自定义后台规则。内容包括搭建、配置、测试与部署流程,可用于替代已弃用的 checkout.liquid 定制方式。

主要功能

  1. 01生成 Checkout UI 扩展
  2. 02使用 Shopify Functions 实现规则
  3. 03配置扩展目标与设置
  4. 04本地预览和部署扩展
  5. 05处理结账沙箱与执行限制

适用场景

  1. 01在结账页添加礼品留言字段
  2. 02按购物车内容筛选配送方式
  3. 03为订单实现自定义折扣
  4. 04在结账完成前验证订单内容

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/finsilabs/awesome-ecommerce-skills --skill shopify-checkout-extensions

安装后在 Claude Code 中输入/shopify-checkout-extensions确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

tile.json
evals/checkout-metafield-persistence-with-ui-e/
criteria.json
task.md
evals/configurable-ui-extension-with-settings-/
criteria.json
task.md
evals/shopify-function-configuration-and-wasm-/
criteria.json
task.md

详细介绍

Shopify Checkout Extensions

概述

Shopify Checkout Extensions 允许应用在 Shopify 结账流程中呈现自定义 UI 区块,而无需分叉结账模板。Shopify Functions 可让你使用在 Shopify 基础设施中运行的自定义 WebAssembly 模块替换后端逻辑——包括折扣、配送、付款方式和订单验证。两者结合后,可替代已弃用的 checkout.liquid 自定义方式,并适用于 Shopify Plus 和非 Plus 商店(UI 扩展),或仅适用于 Plus 商店(部分 Functions 目标)。

何时使用此技能

  • 在结账流程中添加自定义 UI 区块(追加销售、信任徽章、礼品留言字段、保修选项)
  • 实现超出 Shopify 原生折扣规则的自定义折扣逻辑(例如,阶梯折扣、B2B 定价)
  • 根据购物车内容创建自定义配送方式筛选或重命名逻辑
  • 构建付款方式自定义功能,为特定客户隐藏或重命名付款方式
  • 在结账完成前验证订单内容(例如,数量限制、区域限制)
  • 为 Shopify Plus 商店替换已弃用的 checkout.liquid 自定义配置

核心说明

  1. 使用 Shopify CLI 搭建扩展

    # Inside an existing Shopify app directory
    shopify app generate extension
    # Choose: Checkout UI extension OR Shopify Function
    # Name: my-checkout-extension
    

    这会创建一个 extensions/my-checkout-extension/ 目录,其中包含 src/index.tsx(UI)或 src/index.ts(Function)。

  2. 构建 Checkout UI 扩展

    Checkout UI 扩展使用来自 @shopify/ui-extensions-react/checkout 的类 React 组件 API:

    // extensions/order-upsell/src/index.tsx
    import {
      reactExtension,
      useCartLines,
      useApplyCartLinesChange,
      useSettings,
      BlockStack,
      Button,
      Image,
      Text,
      InlineStack,
      Divider,
    } from "@shopify/ui-extensions-react/checkout";
    
    const CheckoutBlock = reactExtension(
      "purchase.checkout.block.render",
      () => <OrderUpsell />
    );
    export { CheckoutBlock };
    
    function OrderUpsell() {
      const cartLines = useCartLines();
      const applyCartLinesChange = useApplyCartLinesChange();
      const { upsell_variant_id: upsellVariantId } = useSettings();
    
      // Only show upsell if a variant is configured and cart doesn't already contain it
      if (!upsellVariantId) return null;
    
      const alreadyInCart = cartLines.some(
        (line) => line.merchandise.id === upsellVariantId
      );
    
      if (alreadyInCart) return null;
    
      const handleAddUpsell = async () => {
        await applyCartLinesChange({
          type: "addCartLine",
          merchandiseId: upsellVariantId,
          quantity: 1,
        });
      };
    
      return (
        <BlockStack spacing="base">
          <Divider />
          <InlineStack blockAlignment="center" spacing="base">
            <Image source="https://cdn.shopify.com/s/files/..." aspectRatio={1} />
            <BlockStack>
              <Text emphasis="bold">Add a gift bag for $3.99</Text>
              <Text appearance="subdued">Beautiful packaging for your order</Text>
            </BlockStack>
            <Button onPress={handleAddUpsell}>Add</Button>
          </InlineStack>
        </BlockStack>
      );
    }
    
  3. 在 shopify.extension.toml 中配置扩展

    api_version = "2025-01"
    
    [[extensions]]
    type = "ui_extension"
    name = "Order Upsell"
    handle = "order-upsell"
    
    [[extensions.targeting]]
    module = "./src/index.tsx"
    target = "purchase.checkout.block.render"
    
    [extensions.settings]
    [[extensions.settings.fields]]
    key = "upsell_variant_id"
    type = "variant_reference"
    name = "Upsell Product Variant"
    
  4. 构建用于自定义折扣的 Shopify Function

    Shopify Functions 会编译为 WebAssembly。使用 Rust 或 JavaScript:

    // extensions/volume-discount/src/index.ts
    import type {
      RunInput,
      FunctionRunResult,
      CartLineInput,
    } from "../generated/api";
    
    const NO_CHANGES: FunctionRunResult = { discounts: [], discountApplicationStrategy: "FIRST" };
    
    export function run(input: RunInput): FunctionRunResult {
      const { cart } = input;
    
      // Calculate total quantity across all lines
      const totalQuantity = cart.lines.reduce(
        (sum, line) => sum + line.quantity,
        0
      );
    
      // Tiered volume discount
      let discountPercent = 0;
      if (totalQuantity >= 20) discountPercent = 20;
      else if (totalQuantity >= 10) discountPercent = 10;
      else if (totalQuantity >= 5) discountPercent = 5;
    
      if (discountPercent === 0) return NO_CHANGES;
    
      return {
        discounts: [
          {
            value: {
              percentage: { value: discountPercent.toString() },
            },
            targets: [{ orderSubtotal: { excludedVariantIds: [] } }],
            message: `${discountPercent}% volume discount (${totalQuantity} items)`,
          },
        ],
        discountApplicationStrategy: "FIRST",
      };
    }
    

    Function 的 shopify.extension.toml:

    api_version = "2025-01"
    
    [[extensions]]
    type = "function"
    name = "Volume Discount"
    handle = "volume-discount"
    runtime = "javascript"
    
    [[extensions.input.variables]]
    name = "cart"
    type = "Cart"
    
    [extensions.build]
    command = "npm run build"
    path = "dist/index.wasm"
    
  5. 测试并部署扩展

    # Run local dev preview (UI extension hot-reloads in checkout)
    shopify app dev
    # Open the checkout preview URL shown in terminal
    
    # Deploy all extensions to Shopify
    shopify app deploy
    

    部署后,前往 Admin → Checkout → Customize,将 UI 扩展区块添加到结账模板。要激活 Functions,请在 Admin → Discounts 中创建一个使用该 Function 的折扣。

示例

使用 useApplyMetafieldsChange 的礼品留言字段
import {
  reactExtension,
  TextField,
  useApplyMetafieldsChange,
  useMetafield,
  BlockStack,
  Text,
} from "@shopify/ui-extensions-react/checkout";

export default reactExtension(
  "purchase.checkout.shipping-option-list.render-after",
  () => <GiftMessage />
);

function GiftMessage() {
  const giftMessage = useMetafield({ namespace: "custom", key: "gift_message" });
  const applyMetafieldsChange = useApplyMetafieldsChange();

  return (
    <BlockStack spacing="tight">
      <Text emphasis="bold">Gift message (optional)</Text>
      <TextField
        label="Message"
        value={giftMessage?.value ?? ""}
        multiline={3}
        onChange={(value) =>
          applyMetafieldsChange({
            type: "updateMetafield",
            namespace: "custom",
            key: "gift_message",
            valueType: "string",
            value,
          })
        }
      />
    </BlockStack>
  );
}
付款自定义 Function(针对国际订单隐藏货到付款)
// extensions/payment-customization/src/index.ts
import type { RunInput, FunctionRunResult } from "../generated/api";

export function run(input: RunInput): FunctionRunResult {
  const country = input.cart.buyerIdentity?.countryCode;

  // Hide "Cash on Delivery" for non-domestic orders
  const hideOperations = input.paymentMethods
    .filter((pm) => pm.name.toLowerCase().includes("cash on delivery") && country !== "US")
    .map((pm) => ({
      hide: { paymentMethodId: pm.id },
    }));

  return { operations: hideOperations };
}

最佳实践

  • 使用 purchase.checkout.block.render 目标以获得最大的放置灵活性——商家可以在结账编辑器中将区块拖放到任意位置
  • 将 Function 执行时间控制在 5ms 以内——Shopify 会严格限制执行时间;避免在 Functions 中进行网络调用(使用元字段或 Function 输入变量进行配置)
  • 使用 useSettings() 钩子读取商家在扩展设置架构中配置的值,避免在扩展代码中硬编码 ID
  • 在 UI 扩展中切勿读取 DOM 或使用浏览器 API——它们运行在沙盒化的 Worker 环境中,无法访问 DOM
  • 仅使用 @shopify/ui-extensions-react/checkout 组件——扩展沙盒中不支持原生 HTML 和其他 UI 库
  • 使用真实结账会话测试付款和配送 Functions——本地开发预览仅适用于 UI 扩展;必须部署 Functions 才能进行测试
  • 固定扩展 API 版本——递增 shopify.extension.toml 中的 api_version 以使用新 API,同时保持向后兼容性
  • 为异步操作处理加载状态——useApplyCartLinesChange 是异步的;在变更进行期间显示加载指示器

常见问题

问题解决方案
扩展程序未显示在结账编辑器中确保已部署扩展程序(shopify app deploy),并在 Admin → Checkout 中选择了正确的结账模板
函数返回 FUNCTION_EXECUTION_TIMEOUT将配置移出运行时逻辑,放入 Function 输入元字段;避免在大型目录上执行复杂循环
更新购物车后,useCartLines 返回的数据已过时使用 applyCartLinesChange 返回的 promise,等待结账重新计算后再读取购物车行项目
扩展程序因“Cannot use browser APIs”而崩溃移除 document、window、localStorage 引用——扩展程序运行在 Worker 沙盒中
折扣 Function 未生效在 Admin → Discounts 中确认基于 Function 的折扣处于启用状态,并确认客户符合任何适用资格规则
结账 UI 扩展程序设置未保存设置字段需要 handle 值,且这些值必须与扩展程序代码中 useSettings() 引用的键相匹配

相关技能

  • @shopify-app-development
  • @shopify-storefront-api
  • @shopify-metafields
  • @checkout-flow-optimization
  • @shopify-admin-api

评论

支持 Markdown