# 附录
# 附录1、请求签名算法
所有由开发者发起到快手平台的接口请求,都需要使用开发者的 app_secret 签名,以保证请求来源和请求数据完整性。
具体的参数签名方式如下:
- 参加签名的字段:① URL中的请求参数;② POST 的 body 参数字段。以下两种字段不参与签名:
- 特定字段名:sign、access_token。
- 值为空的字段。
将第1步收集的字段(K)和对应的字段值(V),放到 Map<K, V> 中,并按照字段的 ASCII 码升序排列,按照下述方式连接,组装成参数字符串:
将开发者的 app_secret 拼接在参数字符串后面,使用MD5计算。
# 请求示例(如何获取签名字段)
请求信息:
curl --location --request POST 'https://open.kuaishou.com/openapi/mp/developer/epay/create_order?app_id=ks707065143182423884&access_token=ChFvYXV0aC5hY2Nlc3NUb2tlbhIw0FKGX_0HpB-TDCypkcRcw3o_KHSda-Vy5hK89LPgfTa_XBClfLUgotP1YGpX0lD9hTzMiIHyaI8naVBqJZm6kCdtxZB7IBIpdenq8m9SqxQ1Py7U0KAUwAQ'
--header 'Content-Type: application/json'
--data-raw '{
"open_id":"5b748c61ef2901405450656638e8f702d3",
"out_order_no":"kdj1231113454676",
"total_amount":100,
"subject":"肯德基10元代金券",
"type":1,
"detail":"详情介绍",
"expire_time":3600,
"notify_url":"https://xxxx.kuaishou.com/zeus/epay/notify",
"sign":"dfb2a4b482d4f9a0cb4a60ad7fbe839e"
}'
签名参数信息:
Map<String, Object> signParamMap = new HashMap();
signParamMap.put("app_id", "ks707065143182423884");
signParamMap.put("open_id", "5b748c61ef2901405450656638e8f702d3");
signParamMap.put("out_order_no", "kdj1231113454676");
signParamMap.put("total_amount", 100);
signParamMap.put("subject", "肯德基10元代金券");
signParamMap.put("type", 1);
signParamMap.put("detail", "详情介绍");
signParamMap.put("expire_time", 3600);
signParamMap.put("notify_url", "https://xxxx.kuaishou.com/zeus/epay/notify");
待签名字符串
app_id=ks707065143182423884&detail=详情介绍&expire_time=3600¬ify_url=https://xxxx.kuaishou.com/zeus/epay/notify&open_id=5b748c61ef2901405450656638e8f702d3&out_order_no=kdj1231113454676&subject=肯德基10元代金券&total_amount=100&type=1your_app_secret
# 签名示例代码
Java 示例代码
/**
* 小程序 app_secret
*/
private static final String APP_SECRET = "your_app_secret";
/**
* 获取参数 Map 的签名结果
*
* @param signParamsMap 含义见上述示例
* @return 返回签名结果
*/
public static String calcSign(Map<String, Object> signParamsMap) {
// 去掉 value 为空的
Map<String, Object> trimmedParamMap = signParamsMap.entrySet()
.stream()
.filter(item -> !Strings.isNullOrEmpty(item.getValue().toString()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
// 按照字母排序
Map<String, Object> sortedParamMap = trimmedParamMap.entrySet()
.stream()
.sorted(Map.Entry.comparingByKey())
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(oldValue, newValue) -> oldValue, LinkedHashMap::new));
// 组装成待签名字符串。(注,引用了guava工具)
String paramStr = Joiner.on("&").withKeyValueSeparator("=").join(sortedParamMap.entrySet());
String signStr = paramStr + APP_SECRET;
// 生成签名返回。(注,引用了commons-codec工具)
return DigestUtils.md5Hex(signStr);
}
# 附录2、回调方式和策略
在支付、退款、结算流程中,如果流程结束,小程序平台通过POST方式回调开发者提供的URL地址,以通知开发者流程状态和信息。请求内容使用app_secret 签名,需要开发者对接收到的请求内容验签。
# 请求的内容
header
参数 | 描述 | 示例 |
---|---|---|
kwaisign | 对本次请求的签名 | e10adc3949ba59abbe56e057f20f883e |
body
{
"data": {
"xxxx1": "0007",
"xxxx2": 1234,
"xxxx3": "this is some attach",
},
"message_id": "61901a3a-2b1f-40b6-af14-de34660d7541",
"biz_type": "REFUND",
"app_id": "ks656399649443988986",
"timestamp": 1625728322061
}
具体的字段说明
字段名 | 类型 | 说明 |
---|---|---|
data | json string | 业务状态和信息,具体内容参考具体流程的回调接口说明。 |
message_id | string | 当前回调消息的唯一ID,在同一个消息多次通知时,保持一致。 |
biz_type | string | 业务类型。取值:PAYMENT-支付 |
app_id | string | 当前小程序的AppID |
timestamp | number | 流程变动的时间戳 |
签名方式
取出http body中的原始字符串拼接app_secret,然后使用MD5进行签名:MD5(${http_body_string} + ${app_secret})
签名示例
- 请求如下:
curl --location --request POST
'https://yourdomain.com/kuaishou/pay/event_push' \
--header 'kwaisign: e10adc3949ba59abbe56e057f20f883e' \
--header 'Content-Type: application/json' \
--data-raw '{"data":{"out_refund_no":null,"settle_amount":null,"channel":"WECHAT","out_order_no":"2021091314414946589","out_settle_no":null,"refund_amount":null,"attach":"自定义消息","status":"SUCCESS"},"biz_type":"PAYMENT","message_id":"76a50e0c-a843-492b-9bc6-463c1b178a9c","app_id":"ks696650570360602063","timestamp":1631515320564}'
- 签名验证:
appSecret = "Xgm23lSgws235hlgK"; // 小程序密钥
toSignStr = ${http_body} + ${appSecret} // 待签名字符串
即:toSignStr = {"data":{"out_refund_no":null,"settle_amount":null,"channel":"WECHAT","out_order_no":"2021091314414946589","out_settle_no":null,"refund_amount":null,"attach":"自定义消息","status":"SUCCESS"},"biz_type":"PAYMENT","message_id":"76a50e0c-a843-492b-9bc6-463c1b178a9c","app_id":"ks696650570360602063","timestamp":1631515320564}Xgm23lSgws235hlgK
sign = MD5(toSignStr) // 签名结果
boolean result = sign.equals(kwaisign); // 验证是否相同
# 开发者返回
开发者在接收到回调消息,并正确处理后,需要返回以下内容格式,以通知小程序平台不再持续回调:
{
"result" : 1, //必填。 1-成功,其他-失败。失败小程序平台会尝试重推此消息
"message_id" : "ChFvYXV0aC5hY2Nlc3NUb2tlbhJQvpR51x8In46B1sDB" //当前消息的message_id
}
如果开发者没有返回或者返回的result不等于1,则小程序平台会尝试重复推送此消息,开发者务必做好消息的幂等处理!
# 回调重试策略
对于未收到成功返回的通知,消息最大重试次数为 16 次,重试次数和初次发送回调消息时间的延迟关系如下:
重试次数 | 距初次发送回调消息的延迟时间 |
---|---|
1 | 10s |
2 | 30s |
3 | 1m |
4 | 2m |
5 | 3m |
6 | 4m |
7 | 5m |
8 | 6m |
9 | 7m |
10 | 8m |
11 | 9m |
12 | 10m |
13 | 11m |
14 | 12m |
15 | 1h |
16 | 2h |
# 附录3、手续费/分佣金额计算规则
平台服务费 = floor((订单总金额-已退款金额)*0.02),平台服务费后续将根据商品类目调整。
平台服务费计算需要根据当前用户可操作金额(订单总金额-已退款或结算金额)计算,对乘费率后的金额向下取整并保留两位小数,即为该笔交易的手续费。
平台服务费会在调用结算时扣减,若该笔交易在结算后产生退款等行为,平台服务费不会回退。
服务商分佣金额以及达人分佣金额,均按照:当前用户可操作金额*分佣比例后,向下取整并保留两位小数 的规则计算。
# 附录4、错误码和常见问题
# 错误码
错误码 | 含义 |
---|---|
10000011 | token 过期 |
10000200 | 参数有误,需要检查参数是否为空或者格式错误。 |
10000302 | 请求频率太快,被限速。 |
10000303 | 命中反垃圾策略 |
10000500 | 系统故障 |
10000501 | 稍后重试 |
10000601 | 订单不存在 |
10000602 | 订单信息不一致 |
10000603 | 订单过期 |
10000604 | 订单状态不正确 |
10000605 | 回调地址异常 |
10000606 | 接口参数签名错误 |
10000607 | 不合理的订单金额 |
10000608 | 不支持的service_id |
10000609 | 非法的orderInfo |
10000610 | 重复下单 |
10000611 | 查询订单信息失败 |
10000612 | 费用信息错误 |
10000621 | 支付中台验签失败 |
10000623 | 校验订单金额失败 |
10000624 | 支付订单回调失败 |
10000625 | 退款回调消息解析失败 |
10000626 | 结算回调消息解析失败 |
10000627 | 退款调用异常 |
10000628 | 结算调用异常 |
10000631 | 配置错误 |
10000632 | 内部错误 |
10000633 | 支付中心异常 |
10000634 | 发送webhook失败 |
10000641 | 帐户id已绑定到别的小程序 |
10000642 | 帐户已绑定进件人,无法重复绑定 |
10000643 | 帐户尚未绑定进件人 |
10000644 | 回调域名设置有误 |
10000645 | 商家提现次数限制 |
10200501 | OAuth验签失败 |
10200502 | 查询条件无效 |
10000681 | 类目被封禁 |
10000682 | 当前未到可结算时间,请在支付成功7日后发起结算 |
10000683 | 订单未支付,无可结算金额 |
10000684 | 该单已处理完成 |
10000685 | 当前订单未到可结算时间,请在核销后3天发起结算。如满足结算条件请查看商户是否被处罚。 |
10000686 | 当前有正在结算中的订单 |
10000687 | 当前有正在退款中的订单 |
10000689 | 保证金账户余额不足 |
10000690 | 保证金账户不存在 |
MD5(参数字符串 + app_secret)
K1=V1&K2=V2&...&Kn=Vn // 参数字符串