Cart Flex Function

该能力目前处于灰度阶段,如需使用请联系您的客户经理

简介

Cart Flex Function 允许开发者更改购物车&结账中商品的价格和展示方式。例如更新标题和图片、更改价格以及组合商品或者扩展商品。
Shopline Functions 允许您自定义 Shopline 的后端逻辑。Cart Flex Function 将此逻辑集成到结账流程中。
支持的操作:

  • Expand操作:将购物车&结账的某一个商品行扩展为多个商品。
  • Merge操作:将多行购物车&结账的商品合并为一个代表捆绑商品的行。
  • Update操作:更新购物车&结账中商品行的显示方式,以覆盖其价格、标题或图片。

Expand、Merge操作的套装,在结账页展示的效果如下:

开发并发布 Cart Flex Function

要求

备注

  1. 在搭建和推送扩展的环节,你需要在同一个 开发商店 完成以下几个步骤。
  2. 若在创建应用及函数开发存在疑问,可点击查看 相关文档 快速入门。

步骤一:账号登录

在终端执行sl login登录到开发商店。

  1. 在终端指定要登录的商店。
sl login --store={example.myshopline.com}
  1. 根据浏览器提示完成登录操作。

步骤二:拉取 Cart Flex Function 模版

如果你是首次创建 cart flex function,需要先拉取 Cart Flex Function 模版:

  1. 在终端输入该命令 sl extension create。
user % sl extension create 
?  What type of extension are you creating?

   (1) Theme App Extension
   (2) Checkout UI Customizer
   (3) Post Purchase Customizer
   (4) Shopline Pixels Extension
   (5) Shopline Shopper Extension
   (6) payment-customizations - Function
   (7) discounts-customizations - Function
   (8) product-discount-customizations - Function
>  (9) cart-flex - Function

Press ↑↓ arrows to select, enter to confirm
  1. 选择 cart-flex - Function
  2. 选择SHOPLINE合作伙伴组织账号。
  3. 在终端展示的应用列表中,选择需要关联的应用。
?  What type of extension are you creating?
✔  cart-flex - Function


?  Select partner organization?
✔  SHOPLINE-partner-name


?  Which app would like to register this extension with?
✔  YouAppName
  1. 等待依赖安装完毕则可进行开发。
✔ Dependencies installed
✔ .env saved to project root
╭─ success ────────────────────────────────────────────────────────────────────╮
│                                                                              │
│  Your extension was created in cart-flex.                                    │
│                                                                              │
╰──────────────────────────────────────────────────────────────────────────────╯

步骤三:开发 Function

目录结构

Function 主要由以下部分组成:

cart-flex
  +-- module 模型定义,input/output go 结构定义此处
  |
  +-- .env.prod 环境信息
  |
  +-- input.graphql input声明文件,声明需要哪些输入
  |
  +-- main.go function 核心逻辑入口,函数的业务逻辑要写在这里
  |
  +-- schema.gql 完整 input 的结构定义

定义输入

在 input.graphql 中声明你的函数期望的输入,可定义的输入内容需要是 schema.gql 中定义的结构的子集。只有在 input.graphql 中定义有定义的字段,才能在函数执行时获取到;

备注

为保证 Function 执行的性能,请尽量只声明你的 Function 中会消费的字段;

Function 逻辑

在 main.go 中编写你的 Cart Flex Function 的核心业务逻辑。

func CartFlexFunction(req *module.CartFlexFunctionRequest) (result module.CartFlexFunctionResponse) {
    // 在这里编写你的核心业务逻辑,通过具体某个购物车/结账的信息,判断是否需要调整商品行
    // req 中,只有你在 input.graphql 有声明的字段,才有值
    // 如果需要调整,返回具体的调整操作
    return module.CartFlexFunctionResponse{}
}

var _ = fmt.Printf
var _ = function.Log
var _ = easyjson.Marshal

关键信息:

  • CartFlexFunction方法:在此处编写你的核心逻辑,根据 购物车/结账的信息,判断是否需要调整商品行信息:

    1. 函数入参:只有你在 input.graphql 有声明的字段,才有值。
    2. 函数返回:如果需要调整,返回具体的调整操作。
  • function.Log方法:使用该方法打印日志的日志信息,最终会在 「SHOPLINE 合作伙伴后台-应用-应用详情-函数日志」中展示:

  • easyjson.Marshal:函数中,只支持使用 easyjson 进行 json 操作。

步骤四:推送 Function

当你编写完你的业务逻辑后,在 cart-flex 目录下,使用 sl extension push 命令将 Cart Flex Function 推送到 SHOPLINE:

user % sl extension push
╭─ success ────────────────────────────────────────────────────────────────────╮
│                                                                              │
│  Pushed cart-flex to Shopline!                                               │
│                                                                              │
╰──────────────────────────────────────────────────────────────────────────────╯

推送成功后,.env.prod中将回填 SHOPLINE 为该函数分配的 functionId:

PARTNER_ID=yourPartnerId
SHOPLINE_APP_KEY=yourAppKey
SHOPLINE_APP_SECRET=yourAppSecret
EXTENSION_TITLE=cart-flex
EXTENSION_UUID=${functionId}

步骤五:发布 Function

在 cli 将 Cart Flex Function 推送到 SHOPLINE 后,需要去 「SHOPLINE合作伙伴后台-应用-应用详情-应用版本」点击「创建版本」 「创建并提交审核」
待 cart-flex 审核通过后,再点击「发布」,最新的 cart flex function 代码即可生效

Cart Flex Function 业务逻辑开发

本节只说明 main.go 中业务逻辑需要消费的输入、需要返回的输出,以及常见操作写法。扩展创建、目录结构、推送发布、日志和 JSON 工具在上一节已经说明,这里不再重复。

业务逻辑入口

开发者只需要实现模板中的入口函数:

func CartFlexFunction(req *module.CartFlexFunctionRequest) (result module.CartFlexFunctionResponse) {
    return module.CartFlexFunctionResponse{}
}

req 是当前购物车/结账上下文。result 是本次函数希望平台执行的商品行操作。没有命中业务规则时,返回 module.CartFlexFunctionResponse{},或返回 Operations 为空的 response。

Function Input

只有在 input.graphql 中声明的字段,运行时才会出现在 req 中。建议只声明业务判断真正需要的字段。
输入对象的主要层级是:CartFlexFunctionRequest 包含购物车、实例配置、本地化、汇率和店铺信息;购物车包含商品行、买家身份、购物车自定义属性和元数据;商品行再关联金额、商品变体、商品和商品行自定义属性。

元数据声明方式

元数据不是默认全量传入的字段。开发者必须先在 input.graphql 的 cart(…) 查询参数中声明要读取哪些实体的元数据,运行时对应实体的 metafields 字段才会携带这些元数据。
schema.gql 中 cart 支持的元数据查询参数如下:

查询参数 类型 对应返回位置 业务含义
cartMetafields []MetafieldInput cart.metafields 查询购物车级元数据。
customerMetafields []MetafieldInput cart.buyerIdentity.customer.metafields 查询客户级元数据。
companyMetafields []MetafieldInput cart.buyerIdentity.purchasingCompany.company.metafields 查询 B2B 公司级元数据。
productMetafields []MetafieldInput cart.lines[].merchandise.product.metafields 查询商品级元数据。
productVariantMetafields []MetafieldInput cart.lines[].merchandise.metafields 查询商品变体级元数据。

MetafieldInput 每一项包含 namespace 和 key。只声明需要消费的元数据,避免无关数据进入 Function 输入。

query Input {
  cart(
    customerMetafields: [
      { namespace: "loyalty", key: "member_level" }
    ],
    productMetafields: [
      { namespace: "cart_flex", key: "bundle_config" }
    ]
  ) {
    buyerIdentity {
      customer {
        metafields {
          key
          namespace
          type
          value
        }
      }
    }
    lines {
      id
      merchandise {
        product {
          metafields {
            key
            namespace
            type
            value
          }
        }
      }
    }
  }
}

业务代码中也只能消费已声明并返回的元数据。例如读取客户会员等级和商品套装配置:

var memberLevel string
for _, m := range req.GetCart().GetBuyerIdentity().GetCustomer().GetMetafields() {
    if m.Key == "member_level" && m.Namespace != nil && *m.Namespace == "loyalty" {
        memberLevel = m.Value
        break
    }
}

var bundleConfig string
for _, line := range req.GetCart().GetLines() {
    for _, m := range line.Merchandise.Product.GetMetafields() {
        if m.Key == "bundle_config" && m.Namespace != nil && *m.Namespace == "cart_flex" {
            bundleConfig = m.Value
            break
        }
    }
    if bundleConfig != "" {
        break
    }
}

_ = memberLevel
_ = bundleConfig

cartFlex.metafield 不是通过这些查询参数声明的实体元数据,它来自 CartFlex 规则的配置,会在 Function 执行时随 CartFlex 入参传入。

CartFlexFunctionRequest

字段 类型 必填 业务含义
cart Cart 当前购物车/结账上下文,是判断商品行、买家、购物车自定义属性和购物车级配置的主要来源。
cartFlex CartFlex 当前 Cart Flex Function 实例配置,可用于读取商家在实例上保存的规则。
localization Localization 当前购物车的国家地区与语言,可用于区域和语言差异化规则。
presentmentCurrencyRate ExchangeRate 店铺币种到展示币种的汇率,可用于按展示币种计算固定价或阈值。
shop Shop 店铺上下文,目前包含店铺时区下的本地时间,可用于限时活动。

Cart

字段 类型 必填 业务含义
lines []LineItem 当前所有商品行。多数业务会遍历该数组,找到要展开、合并或更新的行。
buyerIdentity BuyerIdentity 买家身份信息,可用于会员、邮箱、手机号、B2B 公司等分群判断。
attributes []Attribute 购物车级自定义属性,可承载渠道、活动标记或前端传入的业务开关。
metafields []Metafield 购物车级元数据,可读取购物车维度的扩展配置。

LineItem

字段 类型 必填 业务含义
id ID 商品行 ID。返回商品行展开、合并或更新操作时使用这个值定位原商品行。
quantity Int 当前商品行数量,可用于判断是否达到套装、批量购买或阶梯价规则。
cost LineItemCost 商品行金额信息,可用于按单价、小计、总价或最终价判断规则。
merchandise ProductVariant 商品变体信息,用于识别 SKU、变体 ID、所属商品和变体级元数据。
properties []Property 商品行维度的自定义属性,用于承载外部写入的扩展属性信息,例如定制文本、图片、链接等,并在购物车、结账、订单链路中传递。

LineItemCost

字段 类型 必填 业务含义
price Money 商品行单价,可用于原始价格判断。
subtotalAmount Money 商品行小计金额,可用于按行小计判断门槛。
totalAmount Money 商品行总金额,可用于按总金额判断规则。
finalPrice Money 商品行最终价格,可用于判断最终展示价相关规则。

Money

字段 类型 必填 业务含义
amount String 金额值,使用字符串承载,避免浮点精度问题。
currencyCode String 币种代码。

ProductVariant

字段 类型 必填 业务含义
id ID 商品变体 ID,用于识别当前变体,也可作为输出中变体 ID 的来源。
product Product 所属商品信息,用于按商品维度继续匹配规则。
requiresShipping Boolean 是否需要发货,可用于区分虚拟商品、服务商品和实物商品。
sku String SKU,可用于匹配业务规则。
title String 变体标题,可用于展示判断或生成新的标题。
price Money 变体当前售价,可用于价格计算。
compareAtPrice Money 变体划线价,可用于促销展示逻辑。
weight Float 变体重量,可用于重量相关规则。
weightUnit String 变体重量单位。
isCustom Boolean 是否自定义商品,可用于定制品规则。
metafields []Metafield 变体级元数据,可存放套装组成、展示标题、价格规则等。

Product

字段 类型 必填 业务含义
id ID 商品 ID,可用于匹配指定商品。
handle String 商品 handle,可用于稳定识别商品。
isGiftCard Boolean 是否礼品卡,可用于排除或特殊处理礼品卡。
category Int 商品类目编号,可用于按标准类目生效。
customCategory String 自定义商品类目,可用于按商家自定义类目生效。
title String 商品标题,可用于生成展示标题。
inSalesPlanGroup Boolean 是否在订阅计划组,可用于订阅商品规则。
metafields []Metafield 商品级元数据,常用于保存套装、合并、改价等业务规则。

BuyerIdentity

字段 类型 必填 业务含义
customer Customer 登录客户信息,可用于会员身份、客户标签或客户元数据判断。
email String 买家邮箱,可用于特定客户或渠道识别。
phone String 买家手机号,可用于特定客户或渠道识别。
purchasingCompany PurchasingCompany B2B 公司身份,可用于公司、联系人、地点维度的价格或展示规则。

Customer

字段 类型 必填 业务含义
id ID 客户 ID,可用于识别登录客户。
displayName String 客户展示名。
email String 客户邮箱。
phone String 客户手机号。
metafields []Metafield 客户级元数据,可用于会员等级、偏好、客群等规则。

PurchasingCompany

字段 类型 必填 业务含义
company Company B2B 公司信息。
companyContact CompanyContact B2B 公司联系人。
companyLocation CompanyLocation B2B 公司地点。

Company

字段 类型 必填 业务含义
id ID 公司 ID,可用于按公司匹配规则。
name String 公司名称。
metafields []Metafield 公司级元数据,可用于 B2B 专属规则。

CompanyContact

字段 类型 必填 业务含义
id ID 公司联系人 ID。

CompanyLocation

字段 类型 必填 业务含义
id ID 公司地点 ID。
name String 公司地点名称。

Attribute

字段 类型 必填 业务含义
key String 购物车自定义属性 key。
value String 购物车自定义属性值。

CartFlex

字段 类型 必填 业务含义
metafield Metafield Function 实例级元数据,适合保存商家在后台配置的 JSON 规则。

Metafield

字段 类型 必填 业务含义
key String 元数据 key,用于定位具体配置项。
namespace String 元数据命名空间,用于区分不同业务域的配置。
type String 元数据类型,例如 JSON 或文本类型。
value String 元数据内容,常见为 JSON 字符串。

Localization

字段 类型 必填 业务含义
country String 国家地区代码,用于区域化规则。
language String 语言代码,用于本地化标题、图片或文案。

ExchangeRate

字段 类型 必填 业务含义
currencyCode String 展示币种代码。
rate String 店铺币种到展示币种的汇率,用于币种换算。

Shop

字段 类型 必填 业务含义
localTime String 店铺本地时间,可用于按日期、星期、小时控制规则。

Property

properties 是商品行维度的扩展属性信息,用于承载外部写入的定制信息,并在购物车、结账、订单链路中传递。Cart Flex Function 读取输入或在输出中生成 properties 时,都应遵循同一套字段含义。

字段 类型 必填 业务含义
name String 自定义属性名称,例如定制项名称、礼品卡接收人、祝福语、套装组件标识等。
value String 自定义属性值,用于承载该定制项的具体内容。
type String 定制信息类型。有效值包括 text、picture、link。
urls []String URL 资源列表。type 为 picture 时可用于渲染缩略图;type 为 link 时通常使用第一个 URL 作为超链接。
show Boolean 旧版可见性字段。仅当 roleVisibility 为空或不是有效枚举值时作为降级判断;true 表示买家和商家均可见,false 表示买家不可见、商家可见。
roleVisibility String 定制信息可见对象。有效值包括 customer、merchant、all、none,分别表示买家侧可见、商家侧可见、双方可见、双方都不可见。
additional String 定制商品附加字段,可保存定制项的补充信息。例如 name 表示文字定制时,这里可保存字号、字体、颜色等。
extInfo String 扩展字段,用于保存业务自定义的附加信息。

Function Output

输出对象的主要层级是:CartFlexFunctionResponse 包含操作列表;每个 LineOperation 只选择一种操作;具体操作再引用商品行 ID、变体 ID、价格调整、图片和自定义属性。

CartFlexFunctionResponse

字段 类型 必填 业务含义
operations []LineOperation 希望平台执行的商品行操作列表。数组为空表示不调整购物车。

LineOperation

字段 类型 必填 业务含义
lineExpand LineExpandOperation 将一个商品行展开为多个子商品行,适合展示套装、组合装明细。
linesMerge LinesMergeOperation 将多个商品行合并为一个父商品行,适合把多件商品展示成一个套装。
lineUpdate LineUpdateOperation 更新单个商品行的标题、图片或价格。

每个 LineOperation 只设置一种操作。不要在同一个元素里同时设置 lineExpand、linesMerge 和 lineUpdate。

LineExpandOperation

字段 类型 必填 业务含义
lineItemId String 被展开的商品行 ID,必须来自输入中的商品行 ID。
expandedLineItems []ExpandedLineItem 展开后展示的子商品行列表。
image Image 展开后父商品行展示图。
price PriceAdjustment 展开后父商品行价格调整。
title String 展开后父商品行展示标题。

ExpandedLineItem

字段 类型 必填 业务含义
variantId String 子商品变体 ID。
quantity Int 子商品数量。
price PriceAdjustment 子商品价格调整。
properties []Property 展开出的子商品行自定义属性,遵循商品行维度 properties 规范,可传递子商品定制信息、资源链接或可见性设置。

LinesMergeOperation

字段 类型 必填 业务含义
lineItems []MergeLineItem 参与合并的商品行和数量。
parentVariantId String 合并后父商品对应的商品变体 ID。
properties []Property 合并后父商品行自定义属性,遵循商品行维度 properties 规范,可传递合并套装的定制信息、资源链接或可见性设置。
image Image 合并后父商品行展示图。
price PriceAdjustment 合并后父商品行价格调整。
title String 合并后父商品行展示标题。

MergeLineItem

字段 类型 必填 业务含义
lineItemId String 参与合并的商品行 ID,必须来自输入中的商品行 ID。
quantity Int 该商品行参与合并的数量规则。

LineUpdateOperation

字段 类型 必填 业务含义
lineItemId String 被更新的商品行 ID,必须来自输入中的商品行 ID。
image Image 更新后的展示图片。
price PriceAdjustment 更新后的价格。
title String 更新后的展示标题。

Image

字段 类型 必填 业务含义
url String 展示图片地址。仅接受shopline CDN的图片,不可识别的图片链接将不会展示。 支持的域名:“.cloudfront.”, “img.myshopline.com”, “img-.*.myshopline.com”

PriceAdjustment

字段 类型 必填 业务含义
percentageAdjustment PercentageAdjustment 百分比调价。
fixedPricePerUnit FixedPricePerUnit 固定单价调价。

percentageAdjustment 和 fixedPricePerUnit 只能二选一,不要同时返回。

PercentageAdjustment

字段 类型 必填 业务含义
value String 百分比值,调价范围(-100,200]。正数表示涨价,例如 30 表示价格乘以 130%;负数表示降价,例如 -20 表示价格乘以 80%。

FixedPricePerUnit

字段 类型 必填 业务含义
amount String 固定单价金额,调价范围(0,999999999]。

JSON 示例

下面的示例直接对应 module.CartFlexFunctionRequest 和 module.CartFlexFunctionResponse。示例尽量覆盖当前 schema 中的可用字段;实际运行时,未在 input.graphql 声明的字段不会出现在输入中,未命中的规则也不需要在返回中输出对应操作。

全量输入示例

{
  "cart": {
    "attributes": [
      {
        "key": "campaign",
        "value": "summer_bundle"
      }
    ],
    "buyerIdentity": {
      "customer": {
        "displayName": "Alice Chen",
        "email": "alice@example.com",
        "id": "123",
        "metafields": [
          {
            "key": "member_level",
            "namespace": "loyalty",
            "type": "single_line_text_field",
            "value": "gold"
          }
        ],
        "phone": "+8613800000000"
      },
      "email": "alice@example.com",
      "phone": "+8613800000000",
      "purchasingCompany": {
        "company": {
          "id": "123",
          "metafields": [
            {
              "key": "contract_tier",
              "namespace": "b2b",
              "type": "single_line_text_field",
              "value": "enterprise"
            }
          ],
          "name": "Example Trading Co."
        },
        "companyContact": {
          "id": "123"
        },
        "companyLocation": {
          "id": "123",
          "name": "Shanghai Office"
        }
      }
    },
    "lines": [
      {
        "cost": {
          "finalPrice": {
            "amount": "89.00",
            "currencyCode": "USD"
          },
          "price": {
            "amount": "99.00",
            "currencyCode": "USD"
          },
          "subtotalAmount": {
            "amount": "198.00",
            "currencyCode": "USD"
          },
          "totalAmount": {
            "amount": "178.00",
            "currencyCode": "USD"
          }
        },
        "id": "123",
        "merchandise": {
          "compareAtPrice": {
            "amount": "129.00",
            "currencyCode": "USD"
          },
          "id": "123",
          "isCustom": false,
          "metafields": [
            {
              "key": "variant_badge",
              "namespace": "cart_flex",
              "type": "single_line_text_field",
              "value": "limited"
            }
          ],
          "price": {
            "amount": "99.00",
            "currencyCode": "USD"
          },
          "product": {
            "category": 101,
            "customCategory": "bundle",
            "handle": "travel-bundle",
            "id": "123",
            "inSalesPlanGroup": false,
            "isGiftCard": false,
            "metafields": [
              {
                "key": "bundle_config",
                "namespace": "cart_flex",
                "type": "json",
                "value": "{\"children\":[{\"variantId\":\"123\",\"quantity\":1},{\"variantId\":\"123\",\"quantity\":2}]}"
              }
            ],
            "title": "Travel Bundle"
          },
          "requiresShipping": true,
          "sku": "TRAVEL-BUNDLE",
          "title": "Default Title",
          "weight": 1.2,
          "weightUnit": "kg"
        },
        "properties": [
          {
            "additional": "{\"fontSize\":\"14px\",\"font\":\"Inter\",\"color\":\"#333333\"}",
            "extInfo": "{\"source\":\"theme\"}",
            "name": "gift_message",
            "roleVisibility": "customer",
            "show": true,
            "type": "text",
            "value": "Happy trip"
          },
          {
            "name": "gift_preview",
            "roleVisibility": "all",
            "show": true,
            "type": "picture",
            "urls": [
              "https://cdn.shoplineapp.com/files/custom-preview.png"
            ],
            "value": "Gift preview"
          }
        ],
        "quantity": 2
      }
    ],
    "metafields": [
      {
        "key": "cart_rule",
        "namespace": "cart_flex",
        "type": "json",
        "value": "{\"enabled\":true}"
      }
    ]
  },
  "cartFlex": {
    "metafield": {
      "key": "cart_flex_config",
      "namespace": "cart_flex",
      "type": "json",
      "value": "{\"bundleRules\":[{\"handle\":\"travel-bundle\"}]}"
    }
  },
  "localization": {
    "country": "US",
    "language": "en"
  },
  "presentmentCurrencyRate": {
    "currencyCode": "USD",
    "rate": "1.0000"
  },
  "shop": {
    "localTime": "2026-05-15T10:30:00+08:00"
  }
}

返回示例

{
  "operations": [
    {
      "lineUpdate": {
        "lineItemId": "123",
        "image": {
          "url": "https://img.myshopline.com/files/travel-bundle-cover.png"
        },
        "price": {
          "percentageAdjustment": {
            "value": "-10"
          }
        },
        "title": "Gold Member Travel Bundle"
      }
    },
    {
      "lineExpand": {
        "lineItemId": "123",
        "expandedLineItems": [
          {
            "properties": [
              {
                "name": "bundle_component",
                "roleVisibility": "all",
                "show": true,
                "type": "text",
                "value": "Travel Pouch"
              }
            ],
            "variantId": "123",
            "price": {
              "fixedPricePerUnit": {
                "amount": "19.90"
              }
            },
            "quantity": 1
          },
          {
            "variantId": "123",
            "quantity": 2
          }
        ],
        "image": {
          "url": "https://cdn.shopline.com/files/travel-bundle-cover.png"
        },
        "price": {
          "percentageAdjustment": {
            "value": "-10"
          }
        },
        "title": "Travel Bundle"
      }
    },
    {
      "linesMerge": {
        "lineItems": [
          {
            "lineItemId": "123",
            "quantity": 1
          },
          {
            "lineItemId": "123",
            "quantity": 1
          }
        ],
        "properties": [
          {
            "name": "bundle_name",
            "roleVisibility": "all",
            "show": true,
            "type": "text",
            "value": "City Commute Set"
          }
        ],
        "parentVariantId": "123",
        "image": {
          "url": "https://cdn.shopline.com/files/city-commute-set.png"
        },
        "price": {
          "fixedPricePerUnit": {
            "amount": "149.00"
          }
        },
        "title": "City Commute Set"
      }
    }
  ]
}

返回示例同时展示三类操作,实际业务按命中的规则返回需要的 operation 即可。不要在同一个 LineOperation 中同时设置 lineUpdate、lineExpand、linesMerge,也不要在同一个 price 中同时设置 percentageAdjustment 和 fixedPricePerUnit。

业务示例

仅更新商品行展示

适用于按商品、SKU、自定义属性或元数据命中规则后,只改标题、图片或价格。

func CartFlexFunction(req *module.CartFlexFunctionRequest) (result module.CartFlexFunctionResponse) {
    if req == nil || req.Cart == nil {
        return module.CartFlexFunctionResponse{}
    }

    title := "会员专享套装价"
    discount := "-10"
    operations := make([]*module.LineOperation, 0)

    for _, line := range req.Cart.Lines {
        if line == nil || line.Merchandise.Sku == nil || *line.Merchandise.Sku != "VIP-BUNDLE" {
            continue
        }

        operations = append(operations, &module.LineOperation{
            LineUpdate: &module.LineUpdateOperation{
                LineItemId: line.Id,
                Title:      &title,
                Price: &module.PriceAdjustment{
                    PercentageAdjustment: &module.PercentageAdjustment{Value: &discount},
                },
            },
        })
    }

    return module.CartFlexFunctionResponse{Operations: operations}
}

展开套装商品

适用于一个父商品行代表一个套装,需要在购物车/结账中展示套装包含的子商品。

func CartFlexFunction(req *module.CartFlexFunctionRequest) (result module.CartFlexFunctionResponse) {
    if req == nil || req.Cart == nil {
        return module.CartFlexFunctionResponse{}
    }

    title := "旅行套装"
    fixedPrice := "19.90"

    for _, line := range req.Cart.Lines {
        if line == nil || line.Merchandise.Product.Handle != "travel-bundle" {
            continue
        }

        return module.CartFlexFunctionResponse{Operations: []*module.LineOperation{
            {
                LineExpand: &module.LineExpandOperation{
                    LineItemId: line.Id,
                    Title:      &title,
                    ExpandedLineItems: []*module.ExpandedLineItem{
                        {
                            VariantId: "gid://shopline/ProductVariant/child-variant-1",
                            Quantity:  1,
                            Price: &module.PriceAdjustment{
                                FixedPricePerUnit: &module.FixedPricePerUnit{Amount: &fixedPrice},
                            },
                        },
                        {
                            VariantId: "gid://shopline/ProductVariant/child-variant-2",
                            Quantity:  2,
                        },
                    },
                },
            },
        }}
    }

    return module.CartFlexFunctionResponse{}
}

合并多个商品行

适用于购物车里同时存在多件指定商品时,将它们展示为一个组合商品。

func CartFlexFunction(req *module.CartFlexFunctionRequest) (result module.CartFlexFunctionResponse) {
    if req == nil || req.Cart == nil {
        return module.CartFlexFunctionResponse{}
    }

    var topLine, bottomLine *module.LineItem
    for _, line := range req.Cart.Lines {
        if line == nil || line.Merchandise.Sku == nil {
            continue
        }
        switch *line.Merchandise.Sku {
        case "SET-TOP":
            topLine = line
        case "SET-BOTTOM":
            bottomLine = line
        }
    }

    if topLine == nil || bottomLine == nil {
        return module.CartFlexFunctionResponse{}
    }

    title := "上下装组合"
    return module.CartFlexFunctionResponse{Operations: []*module.LineOperation{
        {
            LinesMerge: &module.LinesMergeOperation{
                ParentVariantId: "gid://shopline/ProductVariant/parent-set-variant",
                Title:           &title,
                LineItems: []*module.MergeLineItem{
                    {LineItemId: topLine.Id, Quantity: 1},
                    {LineItemId: bottomLine.Id, Quantity: 1},
                },
            },
        },
    }}
}

注意事项

  • lineItemId 必须使用输入中的购物车商品行 ID,不是商品 ID,也不是商品变体 ID。
  • variantId 和 parentVariantId 使用商品变体 ID。
  • 如果某个字段没有在 input.graphql 中声明,业务逻辑中不要依赖它有值。
  • properties 是商品行维度的自定义属性。返回 properties 时建议同时提供 name 和 type;type 使用 text、picture、link,可见性优先使用 roleVisibility,仅在缺省或无效时由 show 兜底。

Cart Flex 规则管理

Cart Flex Function代码发布后,还需要创建 Cart Flex 规则,结算流程才会执行对应的 function。
Cart Flex 实例管理支持开发者为商家提供可视化界面,由商家自主配置结算环节的商品组合规则与页面展示样式,助力商家灵活制定各类商品营销玩法。
典型场景:商家可以在应用程序内创建一个Cart Flex 实例,当客户将相关的产品(如手机、蓝牙耳机和充电器)添加到购物车时,自动将他们组合成一个捆绑包行项目。
核心规则:

  • 只有某个 function_id 存在对应 CartFlex 规则时,该 function_id 在购物车/结算流程中才会被执行。
  • CartFlex 规则中的 metafield 会在函数入参 CartFlex.metafield 中传入。
  • metafield 用于保存开发者/商家在具体店铺下的配置。SHOPLINE 不关心其中的业务内容和格式,只负责保存并在执行 Function 时透传给函数。

接口公共说明

以下接口用于管理 CartFlex 规则。调用时需要使用店铺访问令牌,并在请求头中传入:

请求头 类型 必填 说明
Content-Type String 固定为 application/json; charset=utf-8。
Authorization String 店铺访问令牌,格式为 Bearer {access_token}。

接口路径中的 {handle} 是店铺域名前缀,例如店铺域名为 open001.myshopline.com,则 {handle} 为 open001。{version} 使用当前开放接口版本。

CartFlexCreate

创建接口用于为指定 function_id 创建 CartFlex 规则,并写入该规则的元数据配置。创建成功后,只要该规则存在,对应 function_id 才会进入购物车/结算执行链路。

POST https://{handle}.myshopline.com/admin/openapi/{version}/cartflex.json

权限点:write_cart_flex
请求体:

字段 类型 必填 业务含义
function_id String Function ID。通常是推送 Function 扩展后由 SHOPLINE 分配的函数标识。
metafield MetafieldInput 要保存到该规则上的元数据配置。

MetafieldInput:

字段 类型 必填 业务含义
key String 元数据 key,用于标识配置项。
namespace String 元数据命名空间,用于区分不同业务域。
type String 元数据类型。若配置内容是 JSON,建议使用 JSON 类型。
value String 元数据值。SHOPLINE 不解析业务含义,只在 Function 执行时透传。

请求示例:

{
  "function_id": "{function_id}",
  "metafield": {
    "key": "cart_flex_config",
    "namespace": "cart_flex",
    "type": "json",
    "value": "{\"rules\":[] }"
  }
}

响应体:

字段 类型 必填 业务含义
cart_flex_info CartFlexInfo 创建成功后的 CartFlex 规则信息。

CartFlexQuery

查询接口用于确认某个 function_id 或规则 ID 是否已经存在对应 CartFlex 规则,也可用于读取该规则当前保存的元数据。

GET https://{handle}.myshopline.com/admin/openapi/{version}/cartflex.json

权限点:read_cart_flex
请求体:

字段 类型 必填 业务含义
function_id String Function ID。用于查询某个函数在当前店铺下是否已有规则。
id String CartFlex 规则 ID。用于查询指定规则。

请求示例:

{
  "function_id": "{function_id}",
  "id": "{cart_flex_customizition_id}"
}

响应体:

字段 类型 必填 业务含义
cart_flex_info CartFlexInfo 查询到的 CartFlex 规则信息。未查询到时表示该函数当前不会因为该规则被执行。

CartFlexDelete

删除接口用于删除指定 CartFlex 规则。删除后,该规则不再为对应 function_id 提供启用关系和元数据配置;如果该 function_id 不再存在对应规则,则购物车/结算流程不会执行该 function。

DELETE https://{handle}.myshopline.com/admin/openapi/{version}/cartflex/delete.json

权限点:write_cart_flex
请求体:

字段 类型 必填 业务含义
id String 要删除的 CartFlex 规则 ID。

请求示例:

{
  "id": "{cart_flex_customizition_id}"
}

响应体:

字段 类型 必填 业务含义
id String 已删除的 CartFlex 规则 ID。

响应对象

CartFlexInfo

字段 类型 必填 业务含义
function_id String Function ID。
id String CartFlex 规则 ID。后续查询或删除规则时使用。
metafield Metafield 该规则当前保存的元数据。Function 执行时会作为 CartFlex.metafield 传入。

Metafield

字段 类型 必填 业务含义
id String 元数据 ID。
key String 元数据 key。
namespace String 元数据命名空间。
type String 元数据类型。
value String 元数据值。SHOPLINE 不解析业务含义,只透传给 Function。

配置传递示例

创建规则时保存的元数据:

{
  "key": "cart_flex_config",
  "namespace": "cart_flex",
  "type": "json",
  "value": "{\"bundleRules\":[{\"handle\":\"travel-bundle\"}]}"
}

Function 执行时,会在 req.CartFlex.Metafield 中读取到同一份配置。开发者可以自行解析 value 中的 JSON,并按规则返回 operations。

func CartFlexFunction(req *module.CartFlexFunctionRequest) (result module.CartFlexFunctionResponse) {
    if req == nil || req.CartFlex == nil || req.CartFlex.Metafield == nil {
        return module.CartFlexFunctionResponse{}
    }

    config := req.CartFlex.Metafield.Value
    _ = config

    return module.CartFlexFunctionResponse{}
}

使用建议

  • 先推送并发布 Cart Flex Function,拿到 function_id 后,再创建 CartFlex 规则。
  • 应用后台可为商家提供配置页面,将商家配置序列化后写入规则的元数据 value。
  • 不要把密钥、访问令牌、支付凭证等敏感信息写入元数据。
  • 删除规则会影响对应 Function 是否进入购物车/结算执行链路,建议在应用后台提供明确的停用确认。