Lazada Push Mechanism 是Lazada开放平台向服务商(ISV)/卖家提供订单消息状态推送服务。目前Lazada的订单同步方式采用ISV轮询的机制,存在被限流,低效率浪费资源等问题。配置Lazada Push Mechanism后,订单状态变更会以Http报文的形式推送到ISV设置的回调URL上。
可以理解为,以前是App不停的问开放平台有没有订单,有没有订单,然后有的话就给你特定时间的所有单号。 LPM接入后改成 :App坐着等待,当有你的订单(订单状态有更新)的时候,开放平台会把你的订单送到你的回调地址上面。
看到这里你有可能有的问题:
Lazada推送的状态可以参考Lazada Seller Center 的订单ASC状态。一般比较关心的状态有 : shipped
delivered, pending, ready_to_ship, unpaid, canceled。 当状态有改变的时候就会推送至少一条消息。
是的。
除了订单正常的正向推进和逆向推进之外,不要试图使用此消息捕捉其他信息 : 比如订单信息变化,物流转包等。
如果你还有其他的问题的话,我们不妨带着问题往下看,文末的常见一定会有。
这部分内容包括以下内容:
这里你可能有的问题是:
Lazop提供了请求签名验证来防止回调接口来防止被灌数据,如仍需过滤IP等方式过滤流量,请自行联系阿里云新加坡寻找机房域名/IP, 平台不提供支持。
下面的实例是两条消息,一条正向,一条逆向。第一条消息体从第4行到第21行,第二条消息从第25行到第44行。
#Header:Header中关注Authorization的部分,具体内容下面会说 #1.正向交易信息示例(下单,付款,发货,收货等正向交易行为产生) POST /example/uri HTTP/1.1 Host: www.example.com Content-Type: application/json Content-Length: 1238 Authorization: 34f7b258df045d4ed9341850ca85b866f34828fd7d51862f11137216294a894c #正向交易消息体 { "seller_id":"1234567", #卖家ID "message_type":0, "data":{ "order_status":"unpaid", #订单状态 "trade_order_id":"260422900198363", #主单Id "trade_order_line_id":"260422900298363", #子单id "status_update_time":1603698638 #订单状态更新时间 时间戳 }, "timestamp":1603766859530, #推送时间 时间戳 "site":"lazada_vn" # 站点信息 } #2.逆向交易信息示例(取消,退货退款,等逆向交易行为产生) POST /example/uri HTTP/1.1 Host: www.example.com Content-Type: application/json Content-Length: 1238 Authorization: 34f7b258df045d4ed9341850ca85b866f34828fd7d51862f11137216294a894c #逆向交易消息体 { "seller_id":"1000114855", "message_type":0, "data":{ "order_status":"canceled", "reverse_order_id":"501977696648153", #逆向主单id "reverse_order_line_id":"502491640048153", #逆向子单id "status_update_time":1603703663, "trade_order_id":"252883361348153", #关联正向单id "trade_order_line_id":"252883361948153" #关联正向子单id }, "timestamp":1603715010436, "site":"lazada_vn" }
你可能会问:
签名由HMAC-SHA256算法基于AppKey,AppSecret和消息体生成。用于校验消息的来源以及消息完整性。要求接入方校验签名。
生成后进行HEX编码生成的字符串,接收方应该校验签名,确认消息的完整性:
/* 其中Base = {your_app_key} + "{message_body_you_receieved}" Secret = {your_app_Secret}; */ Authorization = HEX_ENCODE(HMAC-SHA256(Base, Secret));
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; /** * 签名工具类 * @author jianyan * @date 2020/12/01 */ public class SignatureUtil { private static final String HMAC_SHA256 = "HmacSHA256"; private static final Logger LOGGER = LoggerFactory.getLogger(SignatureUtil.class); /** * 产生基于Hmac-SHA256,并经过16进制编码的签名。 * @param base {AppKey} + {messageBody} * @param secret {AppSecret} * E.g.: AppKey = 123456, AppSecret = 3412gyo124goi3124 * 收到的消息体Json :{"seller_id":"1234567", "message_type":0, "data":{...}..} * * base = "123456" + "{\"seller_id\":\"1234567\", \"message_type\":0, "data":{...}..}" * secret = 3412gyo124goi3124 * signature = getSignature(base, secret); * signature = f3d2ca947f16a50b577c036adecd18bec126ea19cadedd59816e255d3b6104ab * @return 签名 */ public static String getSignature(String base, String secret) { try { Mac sha256Hmac = Mac.getInstance(HMAC_SHA256); SecretKeySpec secretKey = new SecretKeySpec(secret.getBytes(), HMAC_SHA256); sha256Hmac.init(secretKey); return byteArraytoHexString(sha256Hmac.doFinal(base.getBytes())); } catch (Exception e) { LOGGER.error("Failed to generate signature"); } return null; } /** * 十六进制Encode * @param bytes * @return */ private static String byteArraytoHexString(byte[] bytes) { if(bytes == null) { return null; } StringBuilder sb = new StringBuilder(); String stmp; for (byte aByte : bytes) { stmp = Integer.toHexString(aByte & 0XFF); if (stmp.length() == 1) { sb.append('0'); } sb.append(stmp); } return sb.toString().toLowerCase(); } }
看到这里你可能会问 :
如果你是Java 的话,直接复制代码收到请求后,用这个代码按照说明生成签名,与Header中的Authorization进行比较。
要求客户端接收到数据返回200.OK(HTTP状态码,不是Body内容)确认消息收到。 如果超过500ms服务端未收到200确认,服务端则认为消息推送失败。失败后消息会在半个小时后重试推送,一直重试12次直到彻底失败放弃推送。
由于失败重试会放大消息量消耗更多的系统资源,请接入的APP在接收后做好buffer,不要直接处理,减少失败。如果一个APP推送失败率超过50%,推送机会停止对该地址的推送。
看到这里你可能问:
登入open.lazada.com, 登录后进入Push Mechanism选项卡。
1.填入回调地址,点击Verify。 Verify会自动发送一条测试消息到填入的地址,如果返回符合预期则提示成功。
注:接入人请手动进入后台确认:测试消息,包括确认测试内容,确认签名等,此处的签名是真实的。
2.验证成功后,选择订阅的消息类型,并单击右上角的保存。
某ERP 服务商,现有的订单同步方式是通过每一个小时对服务的所有卖家进行一次/orders/get接口获取订单信息,然后再调用/order/items/get获取订单item详情。效率低,API接口经常被限流,API返回有时候还是空,莫名其妙调用失败, 失败重试又被限流, 陷入绝望。
配置回调联调上线之后,现已形成:
先消费消息,获取订单号后,对订单调用/order/get 接口查询订单详情,后用订单号调用/order/items/get获取item详情后,再进行业务处理。 每隔6小时进行一次补拉,调用/orders/get接口获取全量订单,比对有消息已经处理过的,将消息中的漏单,进行统一处理。
回顾整个过程,该ERP服务商采取的是逐步放量策略 : 现有拉取频率不降,把消息消费融入业务逻辑中。 即消费消息,也进行拉取。
之后再逐步将补拉时间下降: 从1小时一拉,变成2小时一拉,逐步减少拉取频率。
另一ERP服务商听说后,也火速接入了推送,采取了不同的灰度策略 : 选取部分店铺,他们的订单直接采取消费推送+低频补拉的方式。观察一段时间没有问题后,再进行灰度店铺的不断放量最终到全量店铺。
推送机在推送前会检查该SellerId 和APPKey在平台的授权关系是否有效,如果出现某个SellerId 没有收到推送的情况,验证一下该Seller和App的Token的有效性。
单一AppKey只能绑定一个推送地址。 如果Seller订阅了不止一个APP,并且都持有有效授权的话,所有App都可以收到该商家的订单。
目前上线的只有订单消息,未来几月内会陆续上线,商品审核消息,履约消息,库存更新消息,商家增长消息等。
首先推送对于国内主流云服务器99.8%的单子在1次推送成功,100%的单子在3次内推送成功。没有12次失败的极端情况,由于十二次推送跨越6个小时的时间,及时是突然的网络抖动,也有足够的时间进行恢复,所以12次推送全部都失败的几率非常低。
要是万一呢? 对于这种方式,我们仍然提供拉取接口给到App,可以进行定时补拉校对。但是对于其QPS的限制要比之前纯拉的时候更加严格。总而言之,我们推荐的消费方式是:以推为主,以拉兜底。
不要同步消费订单消息,存入buffer中再消费。
基于目前观察,国内主流云服务器RT在200ms以内。对于抖动情况,有12次重试补偿机制。
会,目前初定时间在春节后。
是可以的,目前查询接口以及item查询接口都是用主单id
首先回调地址可以改。测试回调的必要性不大,因为你们订单现有链路是以拉取的方式进行,与这个回调完全隔离,可以直接使用一个回调地址。
如果你发现了大量重复,请核对一下重复消息中的trade_order_line_id字段是否一致。因为推送是以子单维度区分的。
推送服务是:至少一次投递 的,少量重复属于正常,请幂等消费。
很多童鞋都反应我们给的sample code有问题。如果下面所有的方式你都自查过了,仍然有问题的再来和我们交流。
这种情况有可能是接口延迟造成的失败,数据大量补偿丢失实时性的情况。部分国内部署的服务器存在这种问题,可以考虑转发。
属性 |
值 |
解释 |
msg_type |
3 |
消息体中用于区分消息类型的字段 |
重试次数 |
12 |
推送失败后重试的次数 |
重试间隔 |
30分钟 |
每次重试的间隔 |
{ seller_id : "1234567", //卖家id message_type : 3, //消息类型 data :{ "item_id": "232611001", //product id "sku_list": [ { "shop_sku": "232611001_SGAMZ-356805001", "seller_sku": "api-create-test-111", "sku_id": "356805001" }, { "shop_sku": "232611001_SGAMZ-356805001", "seller_sku": "api-create-test-111", "sku_id": "356805001" } ]}, "action":"EDITED_UPDATED", //操作类型 PUBLISHED(商品发布), EDITED_INSERTED(已有商品增加SKU) "status_update_time":1623238942696, "timestamp" : 1606130634, "site" : "lazada-sg" }
#:action字段是指本次新增的渠道标记。 在商品新增中一共有两种渠道标分别是 : PUBLISHED (商品发布渠道),EDITED_INSERTED (已有商品新增SKU)
属性 |
值 |
解释 |
msg_type |
5 |
消息体中用于区分消息类型的字段 |
重试次数 |
12 |
推送失败后重试的次数 |
重试间隔 |
30分钟 |
每次重试的间隔 |
{ "seller_id": "100056775", "message_type": 5, "data": { "item_id": 1807508328, "sku_list": [{ "shop_sku": "1807508328_SGAMZ-9541954106", "seller_sku": "11111111", "sku_id": 9541954106 }, { "shop_sku": "1807508328_SGAMZ-9542176118", "seller_sku": "22222223", "sku_id": 9542176118 }, { "shop_sku": "1807508328_SGAMZ-9542176119", "seller_sku": "33333333", "sku_id": 9542176119 }], "action": "EDITED_DELETED", //操作类型 DELETED(商品删除) EDITED_DELETED(已有商品删除sku) "status_update_time": 1623230820094 }, "timestamp": 1623230820, "site": "LAZADA_SG" }
#:action字段是指本次新增的渠道标记。 在商品新增中一共有两种渠道标分别是 : DELETED (商品删除),EDITED_DELETED (已有商品删除SKU)
属性 |
值 |
解释 |
msg_type |
5 |
消息体中用于区分消息类型的字段 |
重试次数 |
12 |
推送失败后重试的次数 |
重试间隔 |
30分钟 |
每次重试的间隔 |
{ "seller_id": "100072361", "message_type": 4, "data": { "item_id": 1760954113, "sku_list": [{ "shop_sku": "1760954113_SGAMZ-8975502010", "seller_sku": "1760954113-1621404701289-0", "sku_id": 8975502010 }], "action": "EDITED_UPDATED",//除sku新增删除之外的一切变更 "status_update_time": 1623232569146 }, "timestamp": 1623232569, "site": "LAZADA_SG" }
#action只有一种
#除商品新增和商品删除之外的所有商品更新都会触发此消息