SHOPLINE Product Discount Functions 指引

应用场景

SHOPLINE Product Discount Functions 提供了商品维度的自定义折扣能力,支持买家在购物车和结账页享受针对特定商品或商品款式的优惠。折扣形式可为自动折扣或单个折扣码。

:warning: 注意:

  • 当前不支持订单维度折扣及免运费折扣;

  • 每个店铺最多支持 25 个自定义折扣规则;

  • 每个 Function 应用最多支持 25 个自定义折扣规则。

前提条件

  • 你已经熟悉了 SHOPLINE Functions 工作流程。见文档 《概述》

  • 你已经了解如何进行 SHOPLINE Functions 开发。见文档 《快速入门》

业务流程

通过 SHOPLINE Functions 实现自定义折扣的整体流程如下:

  1. 创建并发布折扣函数;

  2. 搭建折扣函数规则的创建与详情页面,使用 Admin REST API 支持商家在应用中创建和管理 Function 折扣活动;

  3. 商家安装 Function 应用并设置自定义折扣规则;

  4. 买家访问购物车或结账页时,系统执行函数逻辑并应用对应的商品折扣。

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 实现;

  • 删除折扣码删除自动折扣 活动规则。(商户也可以在 SHOPLINE 后台折扣列表处删除和禁用自定义折扣活动)

查看完整的 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 不匹配。