应用场景
SHOPLINE Product Discount Functions 提供了商品维度的自定义折扣能力,支持买家在购物车和结账页享受针对特定商品或商品款式的优惠。折扣形式可为自动折扣或单个折扣码。
注意:
-
当前不支持订单维度折扣及免运费折扣;
-
每个店铺最多支持 25 个自定义折扣规则;
-
每个 Function 应用最多支持 25 个自定义折扣规则。
前提条件
业务流程
通过 SHOPLINE Functions 实现自定义折扣的整体流程如下:
-
创建并发布折扣函数;
-
搭建折扣函数规则的创建与详情页面,使用 Admin REST API 支持商家在应用中创建和管理 Function 折扣活动;
-
商家安装 Function 应用并设置自定义折扣规则;
-
买家访问购物车或结账页时,系统执行函数逻辑并应用对应的商品折扣。
1. 创建折扣函数
折扣函数的输入主要包含两个部分:
-
当前购物车或结账页的信息,如:客户、商品、金额等信息;
-
商家配置的函数自定义活动规则,在运行函数时可获取。
折扣函数的输出将用于定义如何在购物车或结账页中应用折扣。目前支持两种形式:
-
自动折扣;
-
折扣码。
你可以通过 CLI 下载函数模板,查看 input.graphql 等字段定义。
**接下来将展示具体要如何创建和发布一个折扣自定义函数规则。**到这里,请确保你已经处于开发函数环节。
1.1.定义需要的函数输入
编辑 input.graphql 文件,声明函数所需字段:
-
根据折扣活动需要判断的条件,声明函数在购物车和结账页所需要的字段,仅声明的字段会进行返回;
-
如你需要获取客户、商品、商品规格的元字段信息,请使用 cart.customerMetafield、cart.productMetafield、cart.variantMetafield ,同时写入需要获取元字段的 key 和 namespace,否则元字段不会进行返回;
-
商家配置的折扣规则将存储于 DiscountNodes.metafield,因此需要获取该字段。
完整字段见:
query Input {
cart(customerMetafield:[{key:"customer_profession", namespace: "customers"}],
productMetafield:[{key:"product_brand", namespace: "products"}],
variantMetafield:[{key:"variant_brand", namespace: "variants"}]){
cost {
subtotalAmount {
amount
}
}
buyerIdentity {
customer {
email
id
metafields {
key
namespace
type
value
}
phone
}
email
phone
purchasingCompany {
company {
id
name
}
companyContact {
id
}
companyLocation {
id
name
}
}
}
lines {
cost {
subtotalAmount {
amount
currencyCode
}
}
id
merchandise {
productVariant {
compareAtPrice
isCustom
metafields {
key
namespace
type
value
}
price
product {
handle
id
inSalesPlanGroup
isGiftCard
metafields {
key
namespace
type
value
}
productType
title
}
requiresShipping
sku
title
weight
weightUnit
}
}
quantity
}
}
discountNodes {
metafields {
key
namespace
type
value
}
priceRuleId
}
localization {
country
language
marketId
}
}
1.2. 编写函数核心逻辑
在函数入口文件 main.go 的 ProductDiscountCustomizationFunction 方法中,编写你的函数逻辑:
func ProductDiscountCustomizationFunction(req *module.ProductDiscountCustomizationFunctionRequest) (result module.ProductDiscountCustomizationFunctionRespnse) {
function.Log(fmt.Sprintf("req: %#v", req))
//通过 DiscountNodes 判断是否有折扣信息传入
if len(req.DiscountNodes) == 0 {
return module.ProductDiscountCustomizationFunctionRespnse{}
}
function.Log("have discount nodes")
//这里获取购物车的商品列表
lines := req.Cart.Lines
// 此例子例只用到第一个折扣内容
activity := req.DiscountNodes[0]
//此例判断Input中折扣内容的元数据信息是否有值,如果没有值,则直接返回没有命中折扣优惠
if len(activity.Metafields) == 0 {
return module.ProductDiscountCustomizationFunctionRespnse{}
}
function.Log("have meta fields")
//此例遍历折扣信息中的元数据,用于下游业务代码判断
rules := make(map[string]string)
for _, mf := range activity.Metafields {
rules[mf.Key] = mf.Value
}
function.Log(fmt.Sprintf("rules: %d", len(rules)))
// 这里通过折扣元数据判断购物车中的商品,是否在元数据指定商品款式(SKU)中
effectScopes := make([]*BaseEffectScopeBo, 0)
effectScopeSkuStr := rules["effectScopeSkuList"]
effectScopeSkuList := strings.Split(effectScopeSkuStr, ",")
for i := range effectScopeSkuList {
sku := effectScopeSkuList[i]
effectScopes = append(effectScopes, &BaseEffectScopeBo{
SkuSeq: &sku,
})
}
function.Log(fmt.Sprintf("parse effect scope successfully: %d", len(effectScopes)))
lines = filterSkuScope(lines, effectScopes)
function.Log(fmt.Sprintf("lines: %d", len(lines)))
if len(lines) == 0 {
return module.ProductDiscountCustomizationFunctionRespnse{}
}
// 这里可通过元数据,获取到自定义的 function 活动优惠门槛:无条件
//var benefitEvents []*GetBenefitEventBo
//beErr := json.Unmarshal([]byte(rules["activityBenefitEventList"]), benefitEvents)
//if beErr != nil {
// return module.ProductDiscountCustomizationFunctionRespnse{}
//}
// 优惠计算: 固定金额
benefitValue := rules["benefitValue"]
function.Log(fmt.Sprintf("benefitValue: %s", benefitValue))
discountTargets := make([]*module.ProductDiscountTarget, 0, len(lines))
for _, line := range lines {
discountTargets = append(discountTargets, &module.ProductDiscountTarget{
CartLine: &module.CartLineTarget{
Id: line.Id,
Quantity: line.Quantity,
},
})
}
function.Log(fmt.Sprintf("targets:%d", len(discountTargets)))
// 如果是折扣码活动,这里可以从元数据中获取折扣码
code := rules["code"]
//组装function结果
res := module.ProductDiscountCustomizationFunctionRespnse{
Discounts: []*module.ProductDiscount{
{
Code: &code,
PriceRuleId: activity.PriceRuleId,
Targets: discountTargets,
Value: &module.ProductDiscountValue{
FixedAmount: &module.ProductDiscountFixedAmount{
Amount: &benefitValue,
},
},
},
},
}
function.Log(fmt.Sprintf("res: %#v", res))
return res
}
1.3. 发布函数
请参考文档《快速入门》中的函数发布流程,将函数部署至开发商店及应用环境中。
2. 搭建自定义折扣规则的管理页面
自定义规则页面由开发者自主搭建。商家安装 Function 应用后,可在你的应用中创建、编辑和管理自定义规则。
2.1. 自定义折扣规则的创建页面
根据实际应用场景,您可以搭建折扣规则的创建页面,支持商家设置商品维度的折扣活动规则。
商家的活动配置需通过调用 SHOPLINE 提供的 API 进行保存。支持创建自动折扣和折扣码活动。
自定义的活动规则将保存在 metafield 字段中,其中 type 和 value 可自由定义,namespace 限定为 sale。规则信息会在函数执行时,通过 input.DiscountNodes 字段传递。
应用成功创建规则后,SHOPLINE 主站的折扣活动列表中将新增一条记录,商户在后台点击后跳转回您在 Function 文件中配置的路由。
2.2. 自定义折扣规则的编辑页面
根据实际应用场景,您可以搭建折扣规则的编辑页面,支持商家编辑已创建的折扣活动规则。
根据商户的使用场景,建议在支持以下功能:
-
显示当前函数自定义规则的详细配置,可通过查询应用折扣详情的 API 实现;
-
更新自定义折扣规则,可通过 更新自动折扣函数和折扣码函数 API 实现;
查看完整的 Admin rest API 文档: SHOPLINE Product Discount Functions - Admin rest API
3. 在购物车或结账页生效自定义折扣
商家配置并启用自定义折扣规则后,买家将在购物车和结账页看到相应的优惠,包活活动名称、活动优惠金额等。
3.1. 自定义折扣的匹配逻辑
-
自定义折扣函数的执行优先于 SHOPLINE 主站默认折扣。
-
自定义折扣是否叠加主站折扣,会根据函数规则中的配置决定。当自定义折扣规则中配置了可以与主站折扣叠加,那么无论主站折扣活动的叠加设置如何,命中了自定义折扣规则的商品仍然可以继续叠加主站的折扣活动。
-
命中了自定义折扣规则的商品,无法参与 SHOPLINE 自研开发的应用活动,包括:限时促销、预售活动、组合销售(组合商品、加购品、礼品盒、售后促销)、联盟分销折扣码、弹窗推广折扣码、购物车凑单进度条折扣码、Marketing - All in one折扣码、赠品活动。
-
当用户输入了自定义活动规则的折扣码,系统才会去查询应用创建的自定义折扣码活动,用户不输入的话,只会匹配自定义折扣中的自动折扣活动。当 Output 中 返回的折扣码和用户输入的折扣码一致时,视为接受这个折扣码,否则视为不接受,用户将会被提示折扣码无效。
3.2. 函数 Input 示例
当店铺存在有效的折扣函数规则时,系统将向函数传入 input 数据。请注意,在 input 中只会接收您在输入查询中请求的字段。
根据 1.1 定义好的 input 查询,SHOPLINE 将会传入对应的字段给到函数应用,示例如下:
Input {
"cart": {
"cost": {
"subtotalAmount": {
"amount": "44.75"
}
},
"buyerIdentity": {
"customer": {
"email": "ella@shopline.com",
"id": "4600602639",
"phone": "0025115645636979"
},
"email": "ella@shopline.com",
"phone": "0025115645636979"
},
"lines": [
{
"cost": {
"subtotalAmount": {
"amount": "44.75"
}
},
"id": "0_18059066233236021651690734",
"merchandise": {
"productVariant": {
"compareAtPrice": "0",
"price": "8.95",
"product": {
"handle": "toddler-girl-floral-mesh-princess-dress-230064",
"id": "16059066233233337297070734",
"inSalesPlanGroup": false,
"isGiftCard": "false"
},
"sku": "18059066233236021651690734",
"weight": "180",
"weightUnit": "g"
}
},
"quantity": 5
}
]
},
"discountNodes": Array[1],
"localization": {
"country": "Code(isoCode=AU)",
"language": "Code(isoCode=null)"
}
}
查看完整的 input 结构文档: SHOPLINE Product Discount Functions - Input & Output 接口
3.3 函数 Output 示例
3.3.1 输出结构
折扣自定义函数需要根据活动规则和购物车或结账页实时的数据,自行计算优惠,并以 SHOPLINE 规范好的 Output 结构返回优惠内容。SHOPLINE 购物车和结账页将会根据 Output 输出的内容,应用和展示自定义折扣活动的优惠内容。以下是函数运行结果的示例(分别以折扣码和自动折扣为例):
{
"discounts": [
{
"code": "",
"priceRuleId": "7053515777396753364",
"targets": [
{
"cartLine": {
"id": "0_18059066233236021651690734",
"quantity": 5
}
}
],
"value": {
"fixedAmount": {
"amount": "8"
}
}
}
]
}
查看完整 Output 结构文档: SHOPLINE Product Discount Functions - Input & Output 接口
3.3.2 多函数处理逻辑
1. 当一个应用的多个自定义折扣作用于同一商品行时,会优先应用最近更新的规则,即一个商品只能生效一个自定义折扣规则。
2. 当多个应用同时生效自定义折扣,如果折扣分别生效在不同的商品行上,则取结果并集。
3. 若多个应用的自定义折扣对同一个商品返回优惠结果,只取第一个返回的折扣结果进行应用。
示例:购物车当前有商品 A、B,返回了 3 个自定义折扣的运算结果:
-
活动 1:商品 A 购买满1件,优惠金额为 $1
-
活动 2:商品 A 和 B 一起购买,优惠金额为 $5
-
活动 3:商品B 购买满1件,优惠金额为 $3
自定义折扣活动的更新时间顺序从近到远分别是活动 1、活动 2、活动 3,那么最终结果
-
商品 A:应用活动 1
-
商品 B:应用活动 3
活动 2 要求同时购买 A 和 B,且 商品 A 已经应用了一个自定义活动,所以活动 2 不匹配。