# 哈哈零售API详细文档 ## 序言 ### 概述 以接口的形式对哈哈便利的无人售货柜进行开门、结算、查询业务数据等。 ### 重要说明 **库存管理**:哈哈不维护API商家的库存信息。如商家对库存有要求,请上货时自行保存商品的库存数量,并根据识别结果对库存进行增减。 **业务流程**:用户扫码后商户端自行决定是否可开门(比如判断是否签约免密支付,或者用户余额是否足够)。开门时,设备的开门和关门或者发生错误状态信息,识别完成后,识别的结果,生成订单时,订单信息,都会通过消息通知给商户。商户需要自行免密签约、免密扣款等。所以一般来说,需要商户端开通支付宝和微信的免密支付才能接入本接口。 --- ## 1. 接入前说明 ### 1.1 阅读对象 **定位人群**:哈哈便利接口文档是面向具有一定的网站或APP开发能力,了解 ASP、PHP、JAVA、ASP.NET 等开发语言中的一种及 SQL 数据库语言的网站开发、维护和管理人员。 ### 1.2 接入步骤 #### 步骤1:联系哈哈运营人员开通API功能 联系哈哈运营人员开通您的商家账户的API功能。 #### 步骤2:在商家后台配置回调地址 1. 登录商家后台:http://client.hahabianli.com 2. 进入"API配置"页面 3. 配置回调地址(用于接收开关门通知、识别结果通知、订单通知等) 4. 获取 AppId 和 AppSecret **地址要求**: - 订单回调地址:必须以 http:// 或 https:// 开头,例如:http://www.baidu.com - 消息回调地址:必须以 http:// 或 https:// 开头,用于接收开关门消息、设备异常消息等 ### 1.3 系统交互图 (系统交互流程示意图 - 描述商户系统、哈哈平台、设备之间的交互关系) --- ## 2. 接口规则 ### 2.1 协议规则 **基本规范**: - 通信协议:HTTP/HTTPS - 字符编码:UTF-8 - 请求格式:application/x-www-form-urlencoded 或 application/json - 响应格式:JSON **域名地址**: - 正式环境:http://api.hahabianli.com - 测试环境:(如需测试环境请联系运营人员) ### 2.2 签名规则 **签名说明**: 为保证接口安全,所有接口调用都需要进行签名验证。 **签名算法**: 1. 将所有请求参数按照参数名ASCII码从小到大排序(字典序) 2. 将排序后的参数拼接成字符串:key1=value1&key2=value2&... 3. 在字符串末尾拼接 AppSecret 4. 对拼接后的字符串进行MD5加密,得到签名值 5. 将签名值作为 sign 参数传递 **示例**: ``` 原始参数: access_token=abc123 page_no=1 page_size=20 AppSecret=secret_key 排序后拼接: access_token=abc123&page_no=1&page_size=20&appsecret=secret_key MD5加密后得到 sign 值 ``` ### 2.3 登陆获取 access_token **简要描述**:获取访问令牌,用于后续所有API调用的身份验证。 **请求URL**:`http://api.hahabianli.com/login/getToken` **请求方式**:POST **请求参数**: | 参数名 | 必选 | 类型 | 说明 | |--------|------|------|------| | appid | 是 | string | 商家应用ID,在商家后台获取 | | appsecret | 是 | string | 商家应用密钥,在商家后台获取 | | sign | 是 | string | 签名,签名算法见2.2章节 | **返回示例**: ```json { "code": 1, "info": "SUCCESS", "data": { "access_token": "169efc8ffe416006910bd606380782148b7587f7", "expires_in": 1296000 } } ``` **返回参数说明**: | 参数名 | 类型 | 说明 | |--------|------|------| | code | int | 返回码,1表示成功,其他表示失败 | | info | string | 返回信息 | | access_token | string | 访问令牌,有效期内可重复使用 | | expires_in | int | 过期时间,单位:秒(15天 = 1296000秒) | **备注**: - access_token 有效期为15天(1296000秒) - token到期前2天内请求接口会自动生成新的access_token,其它时间请求接口将不会更新token - 建议获取到token后自行保存,发现过期或在token到期前的2天内就对token进行更新 - 更多返回错误代码请看2.4全局错误码 ### 2.4 全局错误码 **通用错误码**: | 错误码 | 说明 | 处理方案 | |--------|------|----------| | 1 | SUCCESS | 操作成功 | | 0 | FAIL | 操作失败 | | -1 | 参数错误 | 检查请求参数是否完整且正确 | | -2 | 签名错误 | 检查签名算法和密钥是否正确 | | -3 | access_token无效 | 重新获取access_token | | -4 | access_token过期 | 重新获取access_token | | -5 | 权限不足 | 联系运营人员开通相应权限 | | -6 | 请求频率超限 | 降低请求频率 | | -7 | 系统繁忙 | 稍后重试 | | -100 | 设备不存在 | 检查设备ID是否正确 | | -101 | 设备离线 | 等待设备上线后重试 | | -102 | 设备故障 | 联系设备维护人员 | | -103 | 设备正在使用中 | 等待当前购物结束后重试 | | -200 | 商品不存在 | 检查商品ID是否正确 | | -300 | 订单不存在 | 检查订单号是否正确 | --- ## 3. 设备接口 ### 3.1 设备列表 **简要描述**:获取商家的设备列表。 **请求URL**:`http://api.hahabianli.com/device/getlist` **请求方式**:POST **请求参数**: | 参数名 | 必选 | 类型 | 说明 | |--------|------|------|------| | access_token | 是 | string | 访问token | | page_no | 是 | int | 第几页,如:1,表示第一页 | | page_size | 是 | int | 每页多少条,如:20 表示每页20条数据 | **返回示例**: ```json { "code": 1, "info": "SUCCESS", "data": [ { "id": "device001", "name": "测试设备1", "address": "北京市朝阳区XXX大厦1层", "status": "1" }, { "id": "device002", "name": "测试设备2", "address": "北京市海淀区XXX商场2层", "status": "1" } ] } ``` **返回参数说明**: | 参数名 | 类型 | 说明 | |--------|------|------| | id | string | 设备ID | | name | string | 设备名称 | | address | string | 设备地址 | | status | string | 设备状态 (1: 正常, 2: 冻结) | **备注**: - 更多返回错误代码请看首页的错误代码描述 ### 3.2 设备是否多门单开 **简要描述**:查询设备是否为多门单开。如果只有单门柜可忽略此接口。 **多门单开定义**: - 设备有多个门,但一次只能开一个 - 多门单开的设备,在调API接口开门时,必须指定门的编号(door_index参数) - 从左到右,分别为A门、B门 **请求URL**:`http://api.hahabianli.com/device/isMultiDoorUnique` **请求方式**:POST **请求参数**: | 参数名 | 必选 | 类型 | 说明 | |--------|------|------|------| | access_token | 是 | string | 访问token | | device_id | 是 | string | 设备ID | **返回示例**: ```json { "code": 1, "info": "SUCCESS", "data": { "multi_door_unique": 2 } } ``` **返回参数说明**: | 参数名 | 类型 | 说明 | |--------|------|------| | multi_door_unique | int | 是否为多门单开:0-非多门单开,2-双门单开,3-三门单开,4-四门单开 | **备注**: - 目前还没有三门柜、四门柜,值先预留 - 更多返回错误代码请看首页的错误代码描述 ### 3.3 设备开门 #### 3.3.1 开门 **简要描述**:调用此接口实现设备开门功能。 **请求URL**:`http://api.hahabianli.com/device/open` **请求方式**:POST **请求参数**: | 参数名 | 必选 | 类型 | 说明 | |--------|------|------|------| | access_token | 是 | string | 访问token | | device_id | 是 | string | 设备ID | | out_trade_no | 是 | string | 商户订单号,由商户生成,全局唯一 | | door_index | 否 | int | 门编号,多门单开设备必传。0或不传表示A门,1表示B门 | **返回示例**: ```json { "code": 1, "info": "开门成功", "data": { "order_no": "HH202401240001" } } ``` **返回参数说明**: | 参数名 | 类型 | 说明 | |--------|------|------| | order_no | string | 哈哈平台订单号 | **备注**: - out_trade_no 建议使用时间戳+随机数生成,确保唯一性 - 开门成功后,设备会自动开始拍摄购物视频 - 关门后会触发AI识别,识别结果会通过回调接口通知 - 更多返回错误代码请看首页的错误代码描述 #### 3.3.2 消息回调通知 **简要描述**:哈哈平台通过此接口推送各类消息通知给商户,所有消息类型通过 `notify_type` 字段区分。 **通知URL**:商户在后台配置的消息回调地址(统一入口) **请求方式**:POST **通用参数**: | 参数名 | 必选 | 类型 | 说明 | |--------|------|------|------| | notify_type | 是 | string | 通知类型,用于区分具体的消息类型 | | sign | 是 | string | 签名,验证请求来源 | ##### 3.3.2.1 开关门结果通知 (notify_type=DEVICE_STATUS) **触发条件**:在设备开门成功、关门成功或开门失败时推送 **参数说明**: | 参数名 | 必选 | 类型 | 说明 | |--------|------|------|------| | notify_type | 是 | string | 固定值:**DEVICE_STATUS** | | activity_id | 是 | string | 活动编号,如:1712271138560743 | | create_time | 是 | int | 创建时间戳,如:1633745862 | | device_id | 是 | string | 设备号,如:D00003 | | open_type | 是 | string | IN(补货),OUT(消费) | | out_user_id | 是 | string | 外部用户id | | user_id | 是 | string | 用户id | | status | 是 | string | 开门结果:1=ERROR(开门失败),2=OPENED(门已开),3=CLOSED(门已关),4=ANOTHER(设备繁忙) | | sign | 是 | string | 签名 | **回调示例**: ```php Array ( [activity_id] => "1712271138560743", [create_time] => 1633745862, [device_id] => "D00003", [notify_type] => "DEVICE_STATUS", [status] => "OPENED", [open_type] => "OUT", [out_user_id] => "20210918001", [user_id] => "2109181628353272", [sign] => "c9e3b08303408347ae350ab2c8a22a05" ) ``` **返回要求**: ``` success ``` ##### 3.3.2.2 设备在线状态通知 (notify_type=ONLINE_STATUS) **触发条件**:设备在线状态发生变化时推送(需联系运营开通权限) **参数说明**: | 参数名 | 必选 | 类型 | 说明 | |--------|------|------|------| | notify_type | 是 | string | 固定值:**ONLINE_STATUS** | | device_id | 是 | string | 设备编号 | | is_online | 是 | int | 设备在线状态:0=不在线,1=在线 | | signal_val | 否 | array | 信号值:上线时近两次上报信号值(is_online=1时有值) | | ping_val | 否 | array | 延时值:上线时近两次上报延时值(is_online=1时有值) | | sign | 是 | string | 签名 | **signal_val 和 ping_val 数组结构**: ```json [ { "occur_time": 1573401600, "value": "1" }, { "occur_time": 1573401600, "value": "1" } ] ``` **回调示例**: ```php Array ( [device_id] => "D00085", [is_online] => 1, [notify_type] => "ONLINE_STATUS", [signal_val] => array:2 [ 0 => array:2 [ "occur_time" => 1573401600 "value" => "1" ] 1 => array:2 [ "occur_time" => 1573401600 "value" => "1" ] ], [ping_val] => array:2 [ 0 => array:2 [ "occur_time" => 1573401600 "value" => "1" ] 1 => array:2 [ "occur_time" => 1573401600 "value" => "1" ] ], [sign] => "c9e3b08303408347ae350ab2c8a22a05" ) ``` **返回要求**: ``` success ``` ##### 3.3.2.3 设备音量调节结果通知 (notify_type=VOICE_RESULT) **触发条件**:设备音量调节成功时推送 **参数说明**: | 参数名 | 必选 | 类型 | 说明 | |--------|------|------|------| | notify_type | 是 | string | 固定值:**VOICE_RESULT** | | device_id | 是 | string | 设备号,如:D00003 | | voice | 是 | number | 音量值,为 0~15 的数字 | | sign | 是 | string | 签名 | **回调示例**: ```php Array ( [notify_type] => "VOICE_RESULT", [device_id] => "B1507", [voice] => 6, [sign] => "819bb39c0a93c05e485568083a6c8784" ) ``` **返回要求**: ``` success ``` ##### 3.3.2.4 新品审核结果回调 (notify_type=CLIENT_NEW_PRODUCT) **触发条件**:新品审核完成时推送 **参数说明**: | 参数名 | 必选 | 类型 | 说明 | |--------|------|------|------| | notify_type | 是 | string | 固定值:**CLIENT_NEW_PRODUCT** | | status | 是 | integer | 审核状态:1=已通过,2=已拒绝,3=已上架 | | reject_reason | 是 | string | 被拒绝原因 | | id | 是 | string | 新品申请唯一编码 | | msg | 是 | string | 审核结果信息 | | name | 是 | string | 商品名称 | | bar_code | 是 | string | 条形码 | | code | 是 | string | 商品code标识符(拒绝时为空,通过时不为空) | | product_extends | 否 | string | 扩展字段信息 | | sign | 是 | string | 签名 | **回调示例**: ```php Array ( [bar_code] => "20210913006", [id] => "20315850049321972025", [msg] => "已拒绝", [name] => "哈哈api测试新品申请", [notify_type] => "CLIENT_NEW_PRODUCT", [reject_reason] => "商品名称不合规范,请参考示例,重新编辑商品名称", [status] => 2, [code] => "", [product_extends] => "1212374", [sign] => "fba0b9b78efd190c4858c88afb488c93" ) ``` **返回要求**: ``` success ``` ##### 3.3.2.5 商品合并结果通知 (notify_type=MERGE_PRODUCT) **触发条件**:商品合并操作完成时推送 **参数说明**: | 参数名 | 必选 | 类型 | 说明 | |--------|------|------|------| | notify_type | 是 | string | 固定值:**MERGE_PRODUCT** | | list | 是 | array | 商品合并列表 | | sign | 是 | string | 签名 | **list 数组元素说明**: - `main_code` (string): 主商品code,正常使用的商品code - `product_code` (string): 合并的相同商品的code,除主商品code外,同组其他商品code将不再使用 **list 示例**: ```json [ { "main_code": "dongpengteyinpingzhuang", "product_code": "dpty8988,dpty6525,dpty500ml" }, { "main_code": "hongshiliubaiputaoguozhi", "product_code": "hongshiliuputao,hongshiliubaiputaoguozhi" } ] ``` **回调示例**: ```json { "list": [ { "main_code": "dongpengteyinpingzhuang", "product_code": "dpty8988,dpty6525,dpty500ml,dongpemg500ml,dongpengteyinpingzhuang" }, { "main_code": "hongshiliubaiputaoguozhi", "product_code": "hongshiliuputao,hongshiliubaiputaoguozhi" } ], "notify_type": "MERGE_PRODUCT", "sign": "fba0b9b78efd190c4858c8xxxxxxxxxx" } ``` **返回要求**: ``` success ``` ##### 3.3.2.6 识别结果通知 (notify_type=ORC_RESULT) **触发条件**:AI识别完成后推送识别结果 **参数说明**: | 参数名 | 必选 | 类型 | 说明 | |--------|------|------|------| | notify_type | 是 | string | 固定值:**ORC_RESULT** | | activity_id | 是 | string | 活动编号,如:1712271138560743 | | device_id | 是 | string | 设备号,如:D00003 | | result | 是 | string(JSON) | 识别结果,包含type、data、excepts | | sku_list | 是 | string(JSON) | 商品SKU列表 | | out_user_id | 是 | string | 商家用户编号 | | source | 是 | string | 请求开门接口时传入的source | | nobuy | 是 | int | 是否有消费:1=无消费,0=有消费 | | resource_info | 是 | string(JSON) | 视频或图片信息 | | sign | 是 | string | 签名 | **result 字段说明**: ```json { "type": "OUT", // IN(补货) 或 OUT(消费) "data": { // 商品code:数量,正数为上货,负数为消费 "longfu": -2, "naicha": -4, "abnormal": -1, // 异常商品(excepts=6) "unfriendly": -1, // 非友好购物(excepts=1,2) "unknow": -1, // 商品不在模板(excepts=5) "unrecognized": -1 // 无法判断订单(excepts=7) }, "excepts": [1, 2, 5, 6, 7] // 异常枚举值 } ``` **excepts 枚举值说明**: - 1: 遮挡摄像头/偷吃等(result.data.unfriendly = -1) - 2: 放入异物(result.data.unfriendly = -1) - 5: 商品不在模板(result.data.unknow = -1) - 6: 视频全黑、短缺、花屏、黑屏、主辅视频无法识别、视频结束门未关、灯光问题、视频模糊、拿取过多导致无法识别(result.data.abnormal = -1) - 7: 无法判断订单(遮挡商品/手速过快)(result.data.unrecognized = -1) **sku_list 字段说明**: ```json [ { "sku": "naicha01", // 商家自定义商品编号 "number": -1, // 商品数量,减少为负,增加为正 "code": "naicha" // 哈哈商品编码 }, { "sku": "unknow01", "number": -2, "code": "unknow" } ] ``` **resource_info 字段说明**: ```json { "device_type": 1, "video_url": "http://img.hahabianli.com/buyvideo/20210906/170309_1130918988346058398_0.mp4" } ``` **返回要求**: ``` success ``` **回调示例**: ```php Array ( [activity_id] => "1712271138560743", [device_id] => "D00003", [notify_type] => "ORC_RESULT", [result] => '{"type":"OUT","data":{"longfu":-2,"naicha":-4,"abnormal":-1,"unfriendly":-1},"excepts":[1,2,5,6]}', [sku_list] => '[{"sku":"naicha01","number":-1,"code":"naicha"},{"sku":"unknow01","number":-2,"code":"unknow"}]', [out_user_id] => '123456', [source] => 'XXX', [nobuy] => '0', [resource_info] => '{"device_type":1,"video_url":"http://img.hahabianli.com/buyvideo/20210906/170309_1130918988346058398_0.mp4"}', [sign] => "eb17ae3ae97600f2b357dc598f86b6c1" ) ``` **备注**: - 针对异常场景(excepts包含值),建议商家根据实际情况做对应逻辑处理 - nobuy=1 时表示用户打开柜门但没有消费 - result.data 中的 abnormal、unfriendly、unknow、unrecognized 表示识别到的各类异常情况,值固定为 -1 - excepts 数组与 result.data 中的异常字段相对应,用于更详细的异常类型分类 #### 3.3.3 刷卡开门验证 **简要描述**:用于刷卡设备,验证用户刷卡信息是否有效。 **请求URL**:`http://api.hahabianli.com/device/cardVerify` **请求方式**:POST **请求参数**: | 参数名 | 必选 | 类型 | 说明 | |--------|------|------|------| | access_token | 是 | string | 访问token | | device_id | 是 | string | 设备ID | | card_no | 是 | string | 卡号 | | card_type | 是 | int | 卡类型:1-IC卡,2-身份证,3-其他 | **返回示例**: ```json { "code": 1, "info": "验证成功", "data": { "can_open": true, "user_info": { "name": "张三", "phone": "138****1234" } } } ``` **返回参数说明**: | 参数名 | 类型 | 说明 | |--------|------|------| | can_open | boolean | 是否允许开门 | | user_info | object | 用户信息(可选) | ### 3.4 设备在线状态 **简要描述**:查询设备当前在线状态。 **请求URL**:`http://api.hahabianli.com/device/onlineStatus` **请求方式**:POST **请求参数**: | 参数名 | 必选 | 类型 | 说明 | |--------|------|------|------| | access_token | 是 | string | 访问token | | device_id | 是 | string | 设备ID | **返回示例**: ```json { "code": 1, "info": "SUCCESS", "data": { "online_status": 1, "last_online_time": "2024-01-24 15:30:00" } } ``` **返回参数说明**: | 参数名 | 类型 | 说明 | |--------|------|------| | online_status | int | 在线状态:0-离线,1-在线 | | last_online_time | string | 最后在线时间 | ### 3.5 设备音量调节 **简要描述**:调节设备播报音量。 **请求URL**:`http://api.hahabianli.com/device/setVolume` **请求方式**:POST **请求参数**: | 参数名 | 必选 | 类型 | 说明 | |--------|------|------|------| | access_token | 是 | string | 访问token | | device_id | 是 | string | 设备ID | | volume | 是 | int | 音量值:0-100,0表示静音,100表示最大音量 | **返回示例**: ```json { "code": 1, "info": "设置成功" } ``` ### 3.6 设备和锁的状态 **简要描述**:查询设备和门锁的实时状态。 **请求URL**:`http://api.hahabianli.com/device/status` **请求方式**:POST **请求参数**: | 参数名 | 必选 | 类型 | 说明 | |--------|------|------|------| | access_token | 是 | string | 访问token | | device_id | 是 | string | 设备ID | **返回示例**: ```json { "code": 1, "info": "SUCCESS", "data": { "device_status": 1, "door_status": 0, "lock_status": 1, "camera_status": 1, "network_status": 1 } } ``` **返回参数说明**: | 参数名 | 类型 | 说明 | |--------|------|------| | device_status | int | 设备状态:0-离线,1-在线,2-故障 | | door_status | int | 门状态:0-关闭,1-打开 | | lock_status | int | 锁状态:0-未锁,1-已锁 | | camera_status | int | 摄像头状态:0-异常,1-正常 | | network_status | int | 网络状态:0-断开,1-连接 | --- ## 4. 商品接口 ### 4.1 设备可售卖商品列表 **简要描述**:获取指定设备可售卖的商品列表。 **请求URL**:`http://api.hahabianli.com/goods/deviceGoodsList` **请求方式**:POST **请求参数**: | 参数名 | 必选 | 类型 | 说明 | |--------|------|------|------| | access_token | 是 | string | 访问token | | device_id | 是 | string | 设备ID | **返回示例**: ```json { "code": 1, "info": "SUCCESS", "data": [ { "goods_id": "goods001", "goods_name": "可口可乐", "barcode": "6901234567890", "price": 3.50, "image": "http://example.com/coke.jpg", "stock": 10 } ] } ``` **返回参数说明**: | 参数名 | 类型 | 说明 | |--------|------|------| | goods_id | string | 商品ID | | goods_name | string | 商品名称 | | barcode | string | 商品条码 | | price | decimal | 商品价格 | | image | string | 商品图片URL | | stock | int | 库存数量 | ### 4.2 商品总库 **简要描述**:获取哈哈平台的商品总库列表。 **请求URL**:`http://api.hahabianli.com/goods/totalList` **请求方式**:POST **请求参数**: | 参数名 | 必选 | 类型 | 说明 | |--------|------|------|------| | access_token | 是 | string | 访问token | | page_no | 是 | int | 页码 | | page_size | 是 | int | 每页条数 | | keyword | 否 | string | 搜索关键词(商品名称或条码) | **返回示例**: ```json { "code": 1, "info": "SUCCESS", "data": { "total": 100, "list": [ { "goods_id": "goods001", "goods_name": "可口可乐", "barcode": "6901234567890", "image": "http://example.com/coke.jpg" } ] } } ``` ### 4.3 添加商品至商家商品库 **简要描述**:从哈哈平台商品总库中选择商品添加到商家自己的商品库。 **请求URL**:`http://api.hahabianli.com/goods/addToMerchant` **请求方式**:POST **请求参数**: | 参数名 | 必选 | 类型 | 说明 | |--------|------|------|------| | access_token | 是 | string | 访问token | | goods_id | 是 | string | 商品ID(来自商品总库) | | price | 是 | decimal | 售价 | **返回示例**: ```json { "code": 1, "info": "添加成功" } ``` ### 4.4 商家商品库 **简要描述**:获取商家自己的商品库列表。 **请求URL**:`http://api.hahabianli.com/goods/merchantList` **请求方式**:POST **请求参数**: | 参数名 | 必选 | 类型 | 说明 | |--------|------|------|------| | access_token | 是 | string | 访问token | | page_no | 是 | int | 页码 | | page_size | 是 | int | 每页条数 | **返回示例**: ```json { "code": 1, "info": "SUCCESS", "data": { "total": 50, "list": [ { "goods_id": "goods001", "goods_name": "可口可乐", "barcode": "6901234567890", "price": 3.50, "image": "http://example.com/coke.jpg", "status": 1 } ] } } ``` ### 4.5 商品上下架 **简要描述**:设置商品在指定设备的上架或下架状态。 **请求URL**:`http://api.hahabianli.com/goods/setStatus` **请求方式**:POST **请求参数**: | 参数名 | 必选 | 类型 | 说明 | |--------|------|------|------| | access_token | 是 | string | 访问token | | device_id | 是 | string | 设备ID | | goods_id | 是 | string | 商品ID | | status | 是 | int | 状态:0-下架,1-上架 | **返回示例**: ```json { "code": 1, "info": "设置成功" } ``` #### 4.5.1 获取设备层模板 **简要描述**:获取设备的货架层级模板配置。 **请求URL**:`http://api.hahabianli.com/goods/getLayerTemplate` **请求方式**:POST **请求参数**: | 参数名 | 必选 | 类型 | 说明 | |--------|------|------|------| | access_token | 是 | string | 访问token | | device_id | 是 | string | 设备ID | **返回示例**: ```json { "code": 1, "info": "SUCCESS", "data": { "layers": [ { "layer_no": 1, "layer_name": "第一层", "goods_list": [] } ] } } ``` #### 4.5.2 创建更新层模板接口 **简要描述**:创建或更新设备的货架层级模板。 **请求URL**:`http://api.hahabianli.com/goods/updateLayerTemplate` **请求方式**:POST **请求参数**: | 参数名 | 必选 | 类型 | 说明 | |--------|------|------|------| | access_token | 是 | string | 访问token | | device_id | 是 | string | 设备ID | | layers | 是 | array | 层级配置数组 | **返回示例**: ```json { "code": 1, "info": "更新成功" } ``` ### 4.6 新品申请 **简要描述**:商家申请添加新商品到哈哈平台商品总库。 **请求URL**:`http://api.hahabianli.com/goods/applyNew` **请求方式**:POST **请求参数**: | 参数名 | 必选 | 类型 | 说明 | |--------|------|------|------| | access_token | 是 | string | 访问token | | goods_name | 是 | string | 商品名称 | | barcode | 是 | string | 商品条码 | | image | 是 | string | 商品图片URL | | description | 否 | string | 商品描述 | **返回示例**: ```json { "code": 1, "info": "申请已提交,等待审核" } ``` ### 4.7 商品编号对应关系 **简要描述**:查询商家商品ID与哈哈平台商品ID的对应关系。 **请求URL**:`http://api.hahabianli.com/goods/idMapping` **请求方式**:POST **请求参数**: | 参数名 | 必选 | 类型 | 说明 | |--------|------|------|------| | access_token | 是 | string | 访问token | | merchant_goods_id | 否 | string | 商家商品ID | | haha_goods_id | 否 | string | 哈哈平台商品ID | **返回示例**: ```json { "code": 1, "info": "SUCCESS", "data": { "merchant_goods_id": "m_goods_001", "haha_goods_id": "h_goods_001" } } ``` ### 4.8 商品合并 **简要描述**:将多个相似商品合并为一个商品。 **请求URL**:`http://api.hahabianli.com/goods/merge` **请求方式**:POST **请求参数**: | 参数名 | 必选 | 类型 | 说明 | |--------|------|------|------| | access_token | 是 | string | 访问token | | target_goods_id | 是 | string | 目标商品ID(保留的商品) | | source_goods_ids | 是 | array | 源商品ID数组(将被合并的商品) | **返回示例**: ```json { "code": 1, "info": "合并成功" } ``` --- ## 5. 订单接口 ### 5.1 回调通知说明 **回调地址配置**: 在商家后台配置一个消息回调地址,用于接收所有类型的通知: - 开关门状态通知 (DEVICE_STATUS) - 设备在线状态通知 (ONLINE_STATUS) - 设备音量调节结果通知 (VOICE_RESULT) - 新品审核结果回调 (CLIENT_NEW_PRODUCT) - 商品合并结果通知 (MERGE_PRODUCT) - **AI识别结果通知 (ORC_RESULT)** - 最重要 **回调通知特点**: - 所有通知都通过同一个消息回调地址接收 - **必须包含 `notify_type` 字段**用于区分具体的业务类型 - 所有通知都包含 `sign` 字段用于验证 - 商户必须返回 `success` 字符串表示接收成功 - 未正确响应会触发重试机制(最多3次,间隔5秒、30秒、60秒) **notify_type 枚举值**: | notify_type 值 | 说明 | 章节 | |-------------------|------|------| | DEVICE_STATUS | 开关门状态通知 | 3.3.2.1 | | ONLINE_STATUS | 设备在线状态通知 | 3.3.2.2 | | VOICE_RESULT | 音量调节结果通知 | 3.3.2.3 | | CLIENT_NEW_PRODUCT | 新品审核结果回调 | 3.3.2.4 | | MERGE_PRODUCT | 商品合并结果通知 | 3.3.2.5 | | ORC_RESULT | AI识别结果通知 | 3.3.2.6 | **签名验证**: - 将所有参数(除sign外)按字典序排序 - 拼接成 key1=value1&key2=value2 格式 - 末尾拼接 AppSecret - 进行MD5加密得到签名值 **幂等性要求**: - 商户需要实现幂等性处理,避免重复通知导致重复业务处理 - 建议通过 activity_id 或 device_id + create_time 进行幂等性校验 **重要提示**: - **ORC_RESULT(识别结果通知)是最重要的回调**,包含用户购买的商品信息 - 商户应根据识别结果进行扣款操作 - 当 nobuy=1 时表示用户没有消费,不需要扣款 - 当识别结果中 excepts 包含值时,建议人工审核 ### 5.2 识别或订单回调结果查询 **简要描述**:查询哈哈平台是否成功推送识别结果或订单通知给商户。 **请求URL**:`http://api.hahabianli.com/order/callbackStatus` **请求方式**:POST **请求参数**: | 参数名 | 必选 | 类型 | 说明 | |--------|------|------|------| | access_token | 是 | string | 访问token | | order_no | 是 | string | 哈哈平台订单号 | **返回示例**: ```json { "code": 1, "info": "SUCCESS", "data": { "callback_status": 1, "callback_time": "2024-01-24 15:30:00", "retry_count": 0 } } ``` **返回参数说明**: | 参数名 | 类型 | 说明 | |--------|------|------| | callback_status | int | 回调状态:0-未推送,1-推送成功,2-推送失败 | | callback_time | string | 回调时间 | | retry_count | int | 重试次数 | ### 5.4 识别结果查询 **简要描述**:主动查询订单的AI识别结果。 **请求URL**:`http://api.hahabianli.com/order/recognizeResult` **请求方式**:POST **请求参数**: | 参数名 | 必选 | 类型 | 说明 | |--------|------|------|------| | access_token | 是 | string | 访问token | | order_no | 否 | string | 哈哈平台订单号 | | out_trade_no | 否 | string | 商户订单号 | **返回示例**: ```json { "code": 1, "info": "SUCCESS", "data": { "order_no": "HH202401240001", "out_trade_no": "M202401240001", "goods_list": [ { "goods_id": "goods001", "goods_name": "可口可乐", "price": 3.50, "quantity": 2, "amount": 7.00 } ], "total_amount": 7.00, "confidence": 0.95, "video_url": "http://example.com/video/123.mp4", "status": "completed" } } ``` ### 5.5 订单查询 **简要描述**:查询订单详细信息。 **请求URL**:`http://api.hahabianli.com/order/query` **请求方式**:POST **请求参数**: | 参数名 | 必选 | 类型 | 说明 | |--------|------|------|------| | access_token | 是 | string | 访问token | | order_no | 否 | string | 哈哈平台订单号 | | out_trade_no | 否 | string | 商户订单号 | **返回示例**: ```json { "code": 1, "info": "SUCCESS", "data": { "order_no": "HH202401240001", "out_trade_no": "M202401240001", "device_id": "device001", "goods_list": [], "total_amount": 7.00, "order_status": "paid", "create_time": "2024-01-24 15:00:00", "pay_time": "2024-01-24 15:05:00" } } ``` ### 5.6 设置订单支付状态 **简要描述**:商户完成扣款后,通知哈哈平台订单支付状态。 **请求URL**:`http://api.hahabianli.com/order/setPayStatus` **请求方式**:POST **请求参数**: | 参数名 | 必选 | 类型 | 说明 | |--------|------|------|------| | access_token | 是 | string | 访问token | | order_no | 是 | string | 哈哈平台订单号 | | pay_status | 是 | int | 支付状态:1-支付成功,2-支付失败 | | pay_time | 否 | string | 支付时间 | | transaction_id | 否 | string | 第三方支付流水号 | **返回示例**: ```json { "code": 1, "info": "设置成功" } ``` ### 5.7 开门前后图片或视频查询 **简要描述**:获取订单关联的开门前后对比图片或购物视频。 **请求URL**:`http://api.hahabianli.com/order/media` **请求方式**:POST **请求参数**: | 参数名 | 必选 | 类型 | 说明 | |--------|------|------|------| | access_token | 是 | string | 访问token | | order_no | 是 | string | 哈哈平台订单号 | **返回示例**: ```json { "code": 1, "info": "SUCCESS", "data": { "before_image": "http://example.com/before.jpg", "after_image": "http://example.com/after.jpg", "video_url": "http://example.com/video.mp4" } } ``` **返回参数说明**: | 参数名 | 类型 | 说明 | |--------|------|------| | before_image | string | 开门前图片URL | | after_image | string | 关门后图片URL | | video_url | string | 购物过程视频URL | ### 5.8 静态柜分层图片与识别结果 **简要描述**:获取静态货柜的分层图片和每层的识别结果(适用于静态识别柜)。 **请求URL**:`http://api.hahabianli.com/order/layerRecognize` **请求方式**:POST **请求参数**: | 参数名 | 必选 | 类型 | 说明 | |--------|------|------|------| | access_token | 是 | string | 访问token | | order_no | 是 | string | 哈哈平台订单号 | **返回示例**: ```json { "code": 1, "info": "SUCCESS", "data": { "layers": [ { "layer_no": 1, "before_image": "http://example.com/layer1_before.jpg", "after_image": "http://example.com/layer1_after.jpg", "goods_list": [ { "goods_id": "goods001", "goods_name": "可口可乐", "change_quantity": -2 } ] } ] } } ``` **返回参数说明**: | 参数名 | 类型 | 说明 | |--------|------|------| | layer_no | int | 层级编号 | | before_image | string | 开门前该层图片 | | after_image | string | 关门后该层图片 | | change_quantity | int | 商品数量变化(负数表示减少) | --- ## 附录 ### A. 完整接口清单 #### 1. 接口规则(4个) - 2.1 协议规则 - 2.2 签名规则 - 2.3 登陆获取 access_token - 2.4 全局错误码 #### 2. 设备接口(9个) - 3.1 设备列表 - 3.2 设备是否多门单开 - 3.3.1 开门 - 3.3.2 开关门结果通知 - 3.3.3 刷卡开门验证 - 3.4 设备在线状态 - 3.5 设备音量调节 - 3.6 设备和锁的状态 #### 3. 商品接口(10个) - 4.1 设备可售卖商品列表 - 4.2 商品总库 - 4.3 添加商品至商家商品库 - 4.4 商家商品库 - 4.5 商品上下架 - 4.5.1 获取设备层模板 - 4.5.2 创建更新层模板接口 - 4.6 新品申请 - 4.7 商品编号对应关系 - 4.8 商品合并 #### 4. 订单接口(8个) - 5.1 识别结果通知 - 5.2 订单信息通知 - 5.3 识别或订单回调结果查询 - 5.4 识别结果查询 - 5.5 订单查询 - 5.6 设置订单支付状态 - 5.7 开门前后图片或视频查询 - 5.8 静态柜分层图片与识别结果 ### B. 常见问题 **Q1: access_token多久过期?** A: access_token有效期为15天(1296000秒)。token到期前2天内请求接口会自动生成新的access_token,其它时间请求接口将不会更新token。建议获取到token后自行保存,发现过期或在token到期前的2天内就对token进行更新。 **Q2: 如何处理识别结果通知?** A: 商户应在收到识别结果通知后,根据商品列表和金额进行用户扣款,并调用"设置订单支付状态"接口告知哈哈平台支付结果。 **Q3: 识别置信度低于0.8怎么办?** A: 建议进行人工审核,可通过"开门前后图片或视频查询"接口获取购物视频进行核实。 **Q4: 回调接口没有收到通知?** A: 可通过"识别或订单回调结果查询"接口查询推送状态。如果推送失败,可主动调用"识别结果查询"接口获取数据。 **Q5: 多门柜如何开门?** A: 先调用"设备是否多门单开"接口判断设备类型,如果是多门单开设备,在调用开门接口时需传递door_index参数指定门编号。 --- ## 版本历史 | 版本 | 日期 | 变更内容 | |------|------|----------| | 1.0.0 | 2024-01-24 | 初始版本,包含完整API文档 | --- **文档来源**:https://showdoc.hahabianli.com/web/#/685869388/275897889 **最后更新**:2024-01-24