需要注意的地方如下:
- 需要分账的订单必须有分账标识;
- 账期控制:可以控制二级商户的资金周结或月结。系统默认最长冻结周期180天,若超时仍未发起分账指令,该笔订单的剩余资金将自动解冻。
- 订单分润:平台方可以抽取手续费,推广员/带货网红/分销员可以获得佣金,“分账完结”后订单资金全部解冻给二级商户。
- 多次分账:一笔订单最大分账20次,单次分账最多5个分账接受方。
- 已分账订单可退款。
添加分账接收方
接受方是指接受资金的一方;
电商平台默认已经添加为接收方,其他接收方由电商平台通过“添加分账接收方”接口,建立平台维度统一的“分账接收方列表”,添加成功后,所有二级商户号均可向其分账。接收方可以是微信支付商户或微信支付的个人账户。
这里的测试类使用了电商平台作为接受方,调用结果当然是{“code”:“INVALID_REQUEST”,“message”:“操作已完成,无需二次请求”}
/**
* 添加分账接收方
*/
@Test
public void addReceivers() {
JSONObject reqJsonObject = new JSONObject();
//合单商户appid
reqJsonObject.put("appid", WxPayConfig.merchantAppId);
//接收方类型
reqJsonObject.put("type", "MERCHANT_ID");
//接收方账号,商户号或者个人openId
reqJsonObject.put("account", WxPayConfig.merchantId);
//接收方名称,商户全称如:张三网络公司
reqJsonObject.put("name", "杭州张三网络有限公司");
/* 与分账方的关系类型:
SUPPLIER:供应商
DISTRIBUTOR:分销商
SERVICE_PROVIDER:服务商
PLATFORM:平台
OTHERS:其他
*/
reqJsonObject.put("relation_type", "PLATFORM");
String headerToken = WxPayUtils.getHeaderAuthorization("POST",
HttpUrl.parse(WxPayConfig.profit_sharing_add_receivers_url), reqJsonObject.toJSONString());
Map<String, String> headersMap = new HashMap<>();
headersMap.put("User-Agent", WxPayConfig.userAgent);
headersMap.put("Accept", "application/json");
headersMap.put("Authorization", headerToken);
headersMap.put("Wechatpay-Serial", WxPayConfig.api_v3_cert_serial_no);
RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), reqJsonObject.toJSONString());
ResponseAndStatusAndHeaders response = ClientUtil.getAndPostJson("POST", WxPayConfig.profit_sharing_add_receivers_url, requestBody, headersMap);
if (response.getStatus() != Status.SUCCESS || response.getResponseData() == null) {
LogUtil.printErrorLog(LogUtil.log_front_wxpay + "添加分账接收方接口请求错误,返回信息为:\n" + response.toString() + "\n请求的参数为:\n" + reqJsonObject + "\n\n");
return;
}
LogUtil.printInfoLog(LogUtil.log_front_wxpay + "添加分账接收方接口请求成功,返回信息为:\n" + response.toString() + "\n请求的参数为:\n" + reqJsonObject + "\n\n");
}
请求分账
最大分账比例为30%是指订单分账最多可以分出去30%,即保证供应商最少也有7成的收益。当然有些需求是让分账到供应商的多个账户之类,或者有多级供应商,这就导致30%分账无法满足业务。
如果要提高30%的最大分账比例,官方的回答是“联系BD”也不给联系方式…,所以目前微信根本不支持高比例分账,切实的办法还是使用三方MallBook之类的分账合作。
由于是单商户的分账,所以本次分账就已经分账完结了,分账订单完成后订单剩余资金会解冻到给二级商户。
如果是多次分账的话,每次分账,平台的分账订单号不能相同。
微信收取手续费是在分账之前,也就是先扣除手续费再算分账比例,否则会失败。
/**
* 请求分账
*/
@Test
public void order() throws Exception {
JSONObject reqJsonObject = new JSONObject();
//合单商户appid
reqJsonObject.put("appid", WxPayConfig.merchantAppId);
//合单商户,分账出资的商户号
reqJsonObject.put("sub_mchid", WxPayConfig.testMerchantIdRxz);
//微信订单号
reqJsonObject.put("transaction_id", "4325300104202005287616306099");
//电商平台 分账订单号
reqJsonObject.put("out_order_no", "20200528807139");
//是否完成分账
reqJsonObject.put("finish", true);
//分账接收方列表
JSONArray receivers = new JSONArray();
//分账接收方
JSONObject receiver1 = new JSONObject();
//分账接收商户号
receiver1.put("receiver_mchid", WxPayConfig.merchantId);
//分账金额,最大分账金额需小于全部金额的30%
receiver1.put("amount", 2);
//分账描述
receiver1.put("description", "电商平台手续费+推广员收益代收");
receivers.set(0, receiver1);
reqJsonObject.put("receivers", receivers);
String headerToken =WxPayUtils.getHeaderAuthorization("POST",
HttpUrl.parse(WxPayConfig.profit_sharing_order_url), reqJsonObject.toJSONString());
Map<String, String> headersMap = new HashMap<>();
headersMap.put("User-Agent", WxPayConfig.userAgent);
headersMap.put("Accept", "application/json");
headersMap.put("Authorization", headerToken);
headersMap.put("Wechatpay-Serial", WxPayConfig.api_v3_cert_serial_no);
RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), reqJsonObject.toJSONString());
ResponseAndStatusAndHeaders response = ClientUtil.getAndPostJson("POST", WxPayConfig.profit_sharing_order_url, requestBody, headersMap);
if (response.getStatus() != Status.SUCCESS || response.getResponseData() == null) {
LogUtil.printErrorLog(LogUtil.log_front_wxpay + "请求分账接口请求错误,返回信息为:\n" + response.toString() + "\n请求的参数为:\n" + reqJsonObject + "\n\n");
return;
}
LogUtil.printInfoLog(LogUtil.log_front_wxpay + "请求分账接口请求成功,返回信息为:\n" + response.toString() + "\n请求的参数为:\n" + reqJsonObject + "\n\n");
JSONObject jsonObject = JSON.parseObject(response.getResponseData().toString());
String order_id = jsonObject.get("order_id").toString();
System.out.println("分账成功,分账订单id为:" + order_id);
//{"order_id":"30000101372020052700873705962","out_order_no":"20200527584262","sub_mchid":"1595728911","transaction_id":"4326700103202005271848011909"}
}
分账动账结果回调通知
-
配置分账通知:
在服务商户后台【交易中心】-【分账接收设置】里可以设置分账回调地址。
配置了分账动账的回调URL后,也需要设置秘钥才能正常接受到通知,设置路径:【微信商户平台->账户中心->账户设置->API安全->密钥设置】 -
调用分账接口或分账回退接口都会触发回调通知。
-
需要在分账接受方对应的商户平台设置分账动账通知url,也只有分账接收方才能收到分账动账通知,分账方是不会有通知的。
注意:经过个人测试分账回调通知不太稳定,有时很快收到通知有时分账成功了还收不到通知,建议使用定时器轮询查询分账结果。
/**
* 微信分账回调通知
*/
@PostMapping("sharingCallback")
public JSONObject callBackNotifyForSharing(@RequestBody String requestJsonStr, @RequestHeader HttpHeaders headers, HttpServletResponse response) {
LogUtil.printErrorLog(LogUtil.log_front_wxpay + "分账动账通知接受成功,header为:" + headers + "\n body为:" + requestJsonStr);
//返回通知的应答报文,code(32):SUCCESS为清算机构接收成功;message(64):错误原因
JSONObject responseJson = new JSONObject();
responseJson.put("code", "FAIL");
//支付通知http应答码为200或204才会当作正常接收,当回调处理异常时,应答的HTTP状态码应为500,或者4xx。
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
if (!headers.containsKey("Wechatpay-Serial") || !headers.containsKey("Wechatpay-Timestamp")
|| !headers.containsKey("Wechatpay-Nonce") || !headers.containsKey("Wechatpay-Signature")) {
LogUtil.printErrorLog(LogUtil.log_front_wxpay + "回调请求header缺失");
responseJson.put("message", "回调请求header缺失");
return responseJson;
}
String WechatpaySerial = headers.getFirst("Wechatpay-Serial");//平台证书序列号
String WechatpayTimestamp = headers.getFirst("Wechatpay-Timestamp");//应答时间戳
String WechatpayNonce = headers.getFirst("Wechatpay-Nonce");//应答随机串
String WechatpaySignature = headers.getFirst("Wechatpay-Signature"); //应答签名
if (!WechatpaySerial.equals(WxPayConfig.api_v3_cert_serial_no)) {
LogUtil.printErrorLog(LogUtil.log_front_wxpay + "回调请求证书序列化不一致");
responseJson.put("message", "回调请求证书序列化不一致");
return responseJson;
}
//获取签名串,验签
String srcData = WechatpayTimestamp + "\n" + WechatpayNonce + "\n" + requestJsonStr + "\n";
if (!WxPayUtils.verify(srcData, WechatpaySignature)) {
LogUtil.printErrorLog(LogUtil.log_front_wxpay + "验签失败");
responseJson.put("message", "验签失败");
return responseJson;
}
JSONObject requestJson = JSONObject.parseObject(requestJsonStr);
/*通知的类型,:
PROFITSHARING:分账
PROFITSHARING_RETURN:分账回退
*/
String event_type = requestJson.get("event_type").toString();
//通知数据
JSONObject resource = JSONObject.parseObject(requestJson.get("resource").toString());
//数据密文,Base64编码后的开启/停用结果数据密文
String ciphertext = resource.get("ciphertext").toString();
String associated_data = resource.get("associated_data").toString();
String nonce = resource.get("nonce").toString();
//密文串进行解密
String verify = WxPayUtils.getNotifyData(associated_data, nonce, ciphertext);
JSONObject sharingSuccess = JSONObject.parseObject(verify);
//商户分账订单号
String orderNo = sharingSuccess.get("out_order_no").toString();
if (StrUtil.isBlank(orderNo)) {
responseJson.put("message", "操作失败,未接受到商户分账订单号");
return responseJson;
}
WxProfitSharingOrderDO wxProfitSharingOrder = wxProfitSharingOrderService.getById(orderNo);
if (wxProfitSharingOrder == null) {
responseJson.put("message", "操作失败,未查询到商户分账订单号");
return responseJson;
}
if (wxProfitSharingOrder.getSharingStatus() != 1) {
response.setStatus(HttpServletResponse.SC_OK);
responseJson.put("code", "SUCCESS");
responseJson.put("message", "微信分账成功,商户处理已结束,分账单号:" + wxProfitSharingOrder.getSharingOrderNo());
}
//分账成功
if (event_type.equals("PROFITSHARING")) {
boolean result = wxProfitSharingOrderService.updateSharingOrder(wxProfitSharingOrder, 2);
if (result) {
LogUtil.printErrorLog(LogUtil.log_front_wxpay + "分账结果通知:分账成功,处理成功。分账单号为:" + wxProfitSharingOrder.getSharingOrderNo());
response.setStatus(HttpServletResponse.SC_OK);
responseJson.put("code", "SUCCESS");
responseJson.put("message", "微信分账成功,商户处理成功,分账单号:" + wxProfitSharingOrder.getSharingOrderNo());
} else {
LogUtil.printErrorLog(LogUtil.log_front_wxpay + "分账结果通知:分账成功,处理失败。分账单号为:" + wxProfitSharingOrder.getSharingOrderNo());
responseJson.put("message", "微信分账成功,商户处理失败,分账单号:" + wxProfitSharingOrder.getSharingOrderNo());
}
return responseJson;
}
return responseJson;
}
查询分账结果
需要在接口后拼接上二级商户号、微信订单号和商户分账单号。
/**
* 查询分账接口
*/
@Test
public void queryOrder() {
String param = "?sub_mchid=" + WxPayConfig.testMerchantId + "&transaction_id=" + "4326700103202005271848011909" + "&out_order_no=" + "20200527584262";
String headerToken = WxPayUtils.getHeaderAuthorization("GET",
HttpUrl.parse(WxPayConfig.profit_sharing_order_query_url + param), "");
Map<String, String> headersMap = new HashMap<>();
headersMap.put("User-Agent", WxPayConfig.userAgent);
headersMap.put("Accept", "application/json");
headersMap.put("Authorization", headerToken);
headersMap.put("Wechatpay-Serial", WxPayConfig.api_v3_cert_serial_no);
ResponseAndStatusAndHeaders response = ClientUtil.getAndPostJson("GET", WxPayConfig.profit_sharing_order_query_url + param, null, headersMap);
if (response.getStatus() != Status.SUCCESS || response.getResponseData() == null) {
LogUtil.printErrorLog(LogUtil.log_front_wxpay + "查询分账接口请求错误,返回信息为:\n" + response.toString() + "\n请求的参数为:\n" + param + "\n\n");
return;
}
LogUtil.printInfoLog(LogUtil.log_front_wxpay + "查询分账接口请求成功,返回信息为:\n" + response.toString() + "\n请求的参数为:\n" + param + "\n\n");
JSONObject jsonObject = JSON.parseObject(response.getResponseData().toString());
String status = jsonObject.get("status").toString();
System.out.println("分账结果为:" + status);
//{"order_id":"30000101372020052700873705962","out_order_no":"20200527584262","receivers":[{"amount":9,"description":"电商平台手续费+推广员收益代收","finish_time":"2020-05-27T21:31:34+08:00","receiver_account":"1586786671","receiver_mchid":"1586786671","result":"SUCCESS","type":"MERCHANT_ID"},{"amount":21,"description":"解冻给分账方","finish_time":"2020-05-27T21:31:34+08:00","receiver_account":"1595728911","receiver_mchid":"1595728911","result":"SUCCESS","type":"MERCHANT_ID"}],"status":"FINISHED","sub_mchid":"1595728911","transaction_id":"4326700103202005271848011909"}
}
参考链接
很多地方官方文档没有描述清楚或者压根没写的,比如返回“系统繁忙,请稍后重试”可能是你的传参有误,接口的错误状态也是有些混乱。可以参考微信社区的回答,但是还是要自己调用接口测试下实际情况。
分账接口文档:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/ecommerce/profitsharing/chapter1_1.shtml
(建议看下)分账接口常见问题官方回复:https://developers.weixin.qq.com/community/pay/doc/000284823e8460aada68dacca5b008?blockType=8%3FblockType%3D8%3FblockType%3D8
“微信支付服务商, 分账比例目前最高30%的比例, 怎么申请可以提高分账比例”的官方回复:
https://developers.weixin.qq.com/community/develop/doc/000ca68f484cd015863951b1556000
分账时“订单处理中,暂时无法分账,请稍后重试”的官方回复:https://developers.weixin.qq.com/community/pay/doc/0006e6b6198f5085ce6a1a92551000
接受不到分账通知的官方回复:https://developers.weixin.qq.com/community/develop/doc/00040c2395c7e088af39f13a35b400?highLine=%2520%25E5%2588%2586%25E8%25B4%25A6%25E5%259B%259E%25E8%25B0%2583%25E9%2580%259A%25E7%259F%25A5