业务示例代码

更新时间:2025.08.22

1、分账

1.1、简介

适用于已开通连锁品牌工具箱的服务商、品牌商户和接入品牌的门店,品牌可通过供应链分账对门店、连锁业态其它参与者进行更灵活的资金管理。

1.2、使用场景

  • 品牌管理门店账期:门店独立收款,品牌可以通过供应链分账的“冻结解冻”功能实现对门店的账期管理。

  • 品牌基于门店交易抽成:门店独立收款,品牌根据门店的每笔交易在线抽成。

  • 分账给服务商、供应商、店员或其他分账方:门店独立收款,按每笔交易分账给品牌合作的服务商、供应商、门店店员、或者有品牌合作的其他分润方。

  • 品牌分账给门店:在品牌统一营销活动等场景,品牌可先收款再分账给门店。

1.3、功能特点

  • 需分账/资金管控的订单,服务商在下单时打上分账标识。

  • 周期可控+平台兜底:服务商可根据品牌与门店的协议,实时(支付成功后30s)分账,或按周期延时分账/完结分账(解冻订单未分账资金)。最长冻结周期默认180天,若超时仍未发起分账指令,该笔订单的剩余资金将自动解冻。

  • 多次分账+可分多方:支持同一笔订单最多分账50次,每次分账可最多可分给50个接收方。

  • 接口可查+支持回退:提供接口查询分账结果;若已分账的订单需要退款,对于商户类型的分账接收方,服务商可协助发起分账回退,将已分账资金回退回交易方账户。

2、目标

通过本文档的学习可以利用分账相关接口完成分账流程操作

3、业务处理流程

3.1、分账

微信订单支付成功后,平台商户可以在180天内发起分账,超过180天的订单,微信支付系统会自动把该笔订单剩余未分金额解冻给子商户

分账的流程正常是:

  1. 先通过 查询订单剩余待分金额 查询订单的剩余可分金额

  2. 通过 添加分账接收方 添加分账接收方

  3. 通过 请求分账 发起分账,分账接收方的总金额不能超过步骤1中返回的剩余可分金额,同时分账接收方的总金额不能超过这笔订单全部可分金额的30%(分账给其他商户或者其他人的,不包括解冻给子商户的资金),是受理型 请求分账 接口,即请求分账接口成功之后不代表分账成功,需要通过 查询分账结果 查询分账的最终结果

  4. 发起分账成功之后,可以通过 查询分账结果 查询分账结果
    分账单状态:

分账接收方分账结果:

1package com.java.brand.profitsharing;
2
3import com.java.demo.QueryOrderAmount; // 使用查询订单剩余待分金额接口:https://pay.weixin.qq.com/doc/v3/partner/4012467021
4import static com.java.demo.QueryOrderAmount.*;
5import com.java.demo.AddReceiver; // 使用添加分账接收方接口:https://pay.weixin.qq.com/doc/v3/partner/4012467100
6import static com.java.demo.AddReceiver.*;
7import com.java.demo.CreateOrder; // 使用请求分账接口:https://pay.weixin.qq.com/doc/v3/partner/4012692975
8import static com.java.demo.CreateOrder.*;
9import com.java.demo.QueryOrder; // 使用查询分账结果接口:https://pay.weixin.qq.com/doc/v3/partner/4012467002
10import static com.java.demo.QueryOrder.*;
11import com.java.demo.FinishOrder; // 使用请求分账完结接口:https://pay.weixin.qq.com/doc/v3/partner/4012467016
12import static com.java.demo.FinishOrder.*;
13import com.java.utils.WXPayUtility; // 引用微信支付工具库,参考:https://pay.weixin.qq.com/doc/v3/partner/4014985777
14
15import java.util.ArrayList;
16
17public class ProfitSharingDemo {
18  // TODO: 请准备商户开发必要参数,参考:https://pay.weixin.qq.com/doc/v3/partner/4013080340
19  // 商户号,是由微信支付系统生成并分配给每个商户的唯一标识符,商户号获取方式参考
20  // https://pay.weixin.qq.com/doc/v3/partner/4013080340
21  private static String mchid = "19xxxxxxxx";
22  // 商户API证书序列号,如何获取请参考 https://pay.weixin.qq.com/doc/v3/partner/4013058924
23  private static String certificateSerialNo = "1DDE55AD98Exxxxxxxxxx";
24  // 商户API证书私钥文件路径,本地文件路径
25  private static String privateKeyFilePath = "/path/to/apiclient_key.pem";
26  // 微信支付公钥ID,如何获取请参考 https://pay.weixin.qq.com/doc/v3/partner/4013038589
27  private static String wechatPayPublicKeyId = "PUB_KEY_ID_xxxxxxxxxxxxx";
28  // 微信支付公钥文件路径,本地文件路径
29  private static String wechatPayPublicKeyFilePath = "/path/to/wxp_pub.pem";
30
31  public static void main(String[] argv) {
32    String transactionId = "4208450740201411110007820472";
33    ProfitSharingDemo demo = new ProfitSharingDemo();
34
35    // 建议用户支付完成30s之后,再发起分账请求
36
37    // 1. 查询订单剩余待分金额
38    long unsplitAmount = demo.getUnsplitAmount(transactionId);
39    if (unsplitAmount <= 0) {
40      // 该笔订单没有可分金额了,不能发起分账,退出
41      return;
42    }
43
44    // 2. 添加分账接收方(若已添加过的分账接收方无需重复添加,可跳过这步骤。)
45    demo.addReceiver();
46
47    // 3. 申请分账(分账的金额一定小于等于步骤1返回的订单剩余待分金额)
48    // 商户分账单号需系统内唯一,且完结分账的商户分账单号与请求分账单号不可以一致。
49    String outOrderNo = "P20150806125346";
50    demo.applyProfitSharing(transactionId, outOrderNo);
51
52    // 4. 查询分账结果
53    // 步骤三申请分账成功,不代表分账成功,需要查询分账结果才执行,建议申请分账成功之后等5分钟之后再查询分账结果
54    demo.queryProfitSharingResult(transactionId, outOrderNo);
55
56    // 5. 完结分账(如果订单不需要分账给其他商户或者用户并且订单还有剩余可分金额时,可以请求完结分账接口,把剩余可分金额解冻给特约商户)
57    outOrderNo = "P20150806125347";
58    demo.FinishProfitSharing(transactionId, outOrderNo);
59
60    // 6. 查询完结分账结果
61    // 步骤五完结分账成功,不代表完结分账成功,需要查询分账结果才执行,建议完结分账成功之后等5分钟之后再查询分账结果
62    demo.queryProfitSharingResult(transactionId, outOrderNo);
63  }
64
65  private long getUnsplitAmount(String transactionId) {
66    QueryOrderAmount client = new QueryOrderAmount(
67        mchid,
68        certificateSerialNo,
69        privateKeyFilePath,
70        wechatPayPublicKeyId,
71        wechatPayPublicKeyFilePath);
72    QueryOrderAmountRequest request = new QueryOrderAmountRequest();
73    request.transactionId = transactionId;
74    try {
75      QueryOrderAmountResponse response = client.run(request);
76      return response.unsplitAmount;
77    } catch (WXPayUtility.ApiException e) {
78      // 异常处理逻辑
79      if (e.getErrorCode() == "SYSTEM_ERROR") {
80        // 错误:系统错误
81        // 解决方式:稍后重试
82        // 描述:微信支付系统失败,系统失败直接立即重试大概率还会是系统失败,建议等1分钟后再重试
83      } else if (e.getErrorCode() == "FREQUENCY_LIMITED") {
84        // 错误:限频报错
85        // 解决方式:稍后重试
86        // 描述: 接口频率限制,直接立即重试大概率还会是系统失败,建议等1分钟后再重试
87      } else if (e.getErrorCode() == "SIGN_ERROR") {
88        // 错误:签名错误
89        // 解决方式:检查服务商商户证书序列号,证书私钥文件,公钥ID,公钥文件,同时确认下签名过程是不是按照微信支付的签名方式
90        // 描述:签名报错,需要确认签名材料和签名流程是否正确
91      } else if (e.getErrorCode() == "PARAM_ERROR") {
92        // 错误:参数错误
93        // 解决方式:按照报错返回的message,重新输入请求参数
94        // 描述:参数的类型,长度,或者必填选项没有填写等,大概率是传的微信支付订单号是非法的
95      } else if (e.getErrorCode() == "ORDER_NOT_EXIST") {
96        // 错误:订单不存在
97        // 解决方式:查询的订单不存在,大概率是该笔订单和对应的商户不一致
98      } else if (e.getErrorCode() == "INVALID_REQUEST") {
99        // 错误:请求非法,请求参数正确,但是不符合分账业务规则
100        // 解决方式:根据具体message查看具体哪里不符合业务规则,如果可以修改参数达到符合业务规则的修改请求符合业务规则之后再原单重试
101        // 描述:不符合业务规则的场景如:
102        // -
103        // 订单还未结算完成,请等分账完成后再发起分账,一般等待2分钟即可
104        // - 非分账订单
105      } else {
106        // 其他类型错误:稍等一会后原单重试
107      }
108      throw e;
109    }
110  }
111
112  private void addReceiver() {
113    // TODO: 请准备商户开发必要参数,参考:https://pay.weixin.qq.com/doc/v3/partner/4013080340
114    AddReceiver client = new AddReceiver(
115        mchid,
116        certificateSerialNo,
117        privateKeyFilePath,
118        wechatPayPublicKeyId,
119        wechatPayPublicKeyFilePath);
120
121    AddReceiverRequest request = new AddReceiverRequest();
122    request.brandMchid = "1900000108";
123    request.appid = "wx8888888888888888";
124    request.subAppid = "wx8888888888888889";
125    request.type = "MERCHANT_ID";
126    request.account = "1900000109";
127    request.name = "示例商户全称";
128    request.relationType = "SUPPLIER";
129    try {
130      AddReceiverResponse response = client.run(request);
131      // 添加分账接收方成功
132      // 可以发起分账
133    } catch (WXPayUtility.ApiException e) {
134      // 异常处理逻辑
135      if (e.getErrorCode() == "SYSTEM_ERROR") {
136        // 错误:系统错误
137        // 解决方式:稍后重试
138        // 描述:微信支付系统失败,系统失败直接立即重试大概率还会是系统失败,建议等1分钟后再重试
139      } else if (e.getErrorCode() == "FREQUENCY_LIMITED") {
140        // 错误:限频报错
141        // 解决方式:稍后重试
142        // 描述: 接口频率限制,直接立即重试大概率还会是系统失败,建议等1分钟后再重试
143      } else if (e.getErrorCode() == "NO_AUTH") {
144        // 错误:无分账权限
145        // 解决方式:
146        // 1. 检查商户是否是被处罚:登录商户平台进入账户中心-违约记录查询是否违约记录,如果有按照上面的指引解决违约记录之后重试
147        // 2.
148        // 检查特约商户是否已授权平台分账权限:登录商户平台进入产品中心-特约商户授权产品,在特约商户列表中看看该特约商户是否存在并且授权状态为“已授权”,
149        // 如果不存在或者授权状态不是“已授权”就需要发起授权邀请,等特约商户审批之后重新查询一遍,授权状态为“已授权”之后再重试
150        // 描述:服务商商户被处罚或者服务商商户和特约商户没有父子受理关系
151      } else if (e.getErrorCode() == "SIGN_ERROR") {
152        // 错误:签名错误
153        // 解决方式:检查服务商商户证书序列号,证书私钥文件,公钥ID,公钥文件,同时确认下签名过程是不是按照微信支付的签名方式
154        // 描述:签名报错,需要确认签名材料和签名流程是否正确
155      } else if (e.getErrorCode() == "PARAM_ERROR") {
156        // 错误:参数错误
157        // 解决方式:按照报错返回的message,重新输入请求参数
158        // 描述:参数的类型,长度,或者必填选项没有填写等
159      } else if (e.getErrorCode() == "INVALID_REQUEST") {
160        // 错误:请求非法,请求参数正确,但是不符合分账业务规则
161        // 解决方式:根据具体message查看具体哪里不符合业务规则,如果可以修改参数达到符合业务规则的修改请求符合业务规则之后再原单重试
162        // 描述:不符合业务规则的场景如:
163        // - 分账接收用户没有实名
164        // - 分账接收商户不存在
165        // - 分账给用户传的appid和openid不匹配
166        // - 分账接收商户全称不匹配
167      } else {
168        // 其他类型错误:稍等一会后原单重试
169      }
170      throw e;
171    }
172  }
173
174  private void applyProfitSharing(String transactionId, String outOrderNo) {
175    CreateOrder client = new CreateOrder(
176        mchid,
177        certificateSerialNo,
178        privateKeyFilePath,
179        wechatPayPublicKeyId,
180        wechatPayPublicKeyFilePath);
181
182    CreateOrderRequest request = new CreateOrderRequest();
183    request.brandMchid = "1900000108";
184    request.subMchid = "1900000109";
185    request.appid = "wx8888888888888888";
186    request.subAppid = "wx8888888888888889";
187    request.transactionId = transactionId;
188    request.outOrderNo = outOrderNo;
189    request.receivers = new ArrayList<>();
190    // 分账给商户
191    {
192      ReceiverEntity reciever = new ReceiverEntity();
193      reciever.type = "MERCHANT_ID";
194      reciever.account = "1900000109";
195      reciever.amount = 1L;
196      reciever.description = "分给商户1900000110";
197      reciever.name = client.encrypt("商户1900000110的全称");
198      request.receivers.add(reciever);
199    }
200    // 分账给用户
201    {
202      ReceiverEntity reciever = new ReceiverEntity();
203      reciever.type = "PERSONAL_OPENID";
204      reciever.account = "oLIsd5O4GKSw4Qcsv2cQAk_mS";
205      reciever.amount = 2L;
206      reciever.description = "分给用户";
207      reciever.name = client.encrypt("用户oLIsd5O4GKSw4Qcsv2cQAk_mS的姓名");
208      request.receivers.add(reciever);
209    }
210    // 注意
211    // 上面分给商户的金额+分给用户的金额之和不能大于步骤1返回的订单剩余待分金额
212    // 同时分给商户的金额+分给用户的金额之和不能超过该笔订单的总的可分金额的30%
213    // 每次分账最多可分给50个接收方
214    // 同一个分账请求内,不支持有重复的接收方
215
216    // 是否分账完成
217    // 为true时:该笔订单剩余所有的未分金额都会自动解冻给特约商户
218    // 为false时:该笔订单剩余所有的未分金额不会解冻给特约商户,该笔订单的剩余可分金额还可以继续分账
219    // 一笔订单最多可以发起50次分账
220    request.finish = false;
221    try {
222      CreateOrderResponse response = client.run(request);
223      // 请求分账成功,但是不代表分账成功,需要通过查询分账结果来判断分账是否成功
224    } catch (WXPayUtility.ApiException e) {
225      // 异常处理逻辑
226      if (e.getErrorCode() == "SYSTEM_ERROR") {
227        // 错误:系统错误
228        // 解决方式:稍后原单重试
229        // 描述:微信支付系统失败,一定要原单重试,系统失败直接立即重试大概率还会是系统失败,建议等1分钟后再重试
230      } else if (e.getErrorCode() == "FREQUENCY_LIMITED") {
231        // 错误:限频报错
232        // 解决方式:稍后原单重试
233        // 描述: 接口频率限制,一定要原单重试,直接立即重试大概率还会是系统失败,建议等1分钟后再重试
234      } else if (e.getErrorCode() == "NO_AUTH") {
235        // 错误:无分账权限
236        // 解决方式:
237        // 1. 检查商户是否是被处罚:登录商户平台进入账户中心-违约记录查询是否违约记录,如果有按照上面的指引解决违约记录之后原单重试
238        // 2.
239        // 检查特约商户是否已授权平台分账权限:登录商户平台进入产品中心-特约商户授权产品,在特约商户列表中看看该特约商户是否存在并且授权状态为“已授权”,
240        // 如果不存在或者授权状态不是“已授权”就需要发起授权邀请,等特约商户审批之后重新查询一遍,授权状态为“已授权”之后再原单重试
241        // 描述:服务商商户被处罚或者服务商商户和特约商户没有父子受理关系
242      } else if (e.getErrorCode() == "SIGN_ERROR") {
243        // 错误:签名错误
244        // 解决方式:检查服务商商户证书序列号,证书私钥文件,公钥ID,公钥文件,同时确认下签名过程是不是按照微信支付的签名方式
245        // 描述:签名报错,需要确认签名材料和签名流程是否正确
246      } else if (e.getErrorCode() == "PARAM_ERROR") {
247        // 错误:参数错误
248        // 解决方式:按照报错返回的message,重新输入请求参数
249        // 描述:参数的类型,长度,或者必填选项没有填写等
250      } else if (e.getErrorCode() == "NOT_ENOUGH") {
251        // 错误:账户余额不足
252        // 解决方式:商户充值之后原单重试
253        // 描述:商户账户余额不足,导致退款失败,商户充值之后一定要原单重试
254      } else if (e.getErrorCode() == "INVALID_REQUEST") {
255        // 错误:请求非法,请求参数正确,但是不符合分账业务规则
256        // 解决方式:根据具体message查看具体哪里不符合业务规则,如果可以修改参数达到符合业务规则的修改请求符合业务规则之后再原单重试
257        // 描述:不符合业务规则的场景如:
258        // - 分账次数过多,该笔订单分账次数已经超过50次了,不能再发起分账了
259        // - 该笔订单还未结算完成,请等待结算完成才能发起分账
260        // - 剩余可分账金额不足
261        // - 小程序交易被冻结,在用户主动/系统自动确认收货后才进行资金结算,详细规则可查看《交易类小程序运营规范》
262        // - 订单已过期,不支持分账,请等待系统自动解冻(订单已经超过180天,不允许发起分账)
263
264      } else {
265        // 其他类型错误:稍等一会后原单重试
266      }
267      throw e;
268    }
269  }
270
271  private void queryProfitSharingResult(String transactionId, String outOrderNo) {
272    QueryOrder client = new QueryOrder(
273        mchid,
274        certificateSerialNo,
275        privateKeyFilePath,
276        wechatPayPublicKeyId,
277        wechatPayPublicKeyFilePath);
278
279    QueryOrderRequest request = new QueryOrderRequest();
280    request.subMchid = "1900000109";
281    request.transactionId = transactionId;
282    request.outOrderNo = outOrderNo;
283    try {
284      QueryOrderResponse response = client.run(request);
285      // 1. 根据分账单状态处理分账结果
286      switch (response.status) {
287        case "PROCESSING":
288          // 分账处理中
289          // 稍等2分钟之后再重新查单,直到分账单状态为FINISHED
290          break;
291        case "FINISHED":
292          // 分账完成,检查每个接收方状态
293          for (QueryOrder.ReceiverResultEntity receiver : response.receivers) {
294            switch (receiver.result) {
295              case "SUCCESS":
296                // 分账成功
297                // 处理商户自己的业务逻辑
298                break;
299              case "CLOSED":
300                // 分账失败
301                // 分账给该分账方失败了,可以通过receiver.failReason查看失败原因,对应的处理指引见:https://pay.weixin.qq.com/doc/v3/partner/4015504955
302                // 商户如果根据失败原因做了相应的处理,可以换单重新发起分账
303                break;
304              default:
305                // 非法状态,因为上面分账单已经是FINISHED状态了,所以分账接收方的状态也不会出现pending状态
306                throw new RuntimeException("非法分账接收方状态:" + receiver.result);
307            }
308          }
309          break;
310        default:
311          // 非法状态
312          throw new RuntimeException("非法分账单状态:" + response.status);
313      }
314    } catch (WXPayUtility.ApiException e) {
315      // 异常处理逻辑
316      if (e.getErrorCode() == "SYSTEM_ERROR") {
317        // 错误:系统错误
318        // 解决方式:稍后重试
319        // 描述:微信支付系统失败,系统失败直接立即重试大概率还会是系统失败,建议等1分钟后再重试
320      } else if (e.getErrorCode() == "FREQUENCY_LIMITED") {
321        // 错误:限频报错
322        // 解决方式:稍后重试
323        // 描述: 接口频率限制,直接立即重试大概率还会是系统失败,建议等1分钟后再重试
324      } else if (e.getErrorCode() == "NO_AUTH") {
325        // 错误:无分账权限
326        // 解决方式:
327        // 1. 检查商户是否是被处罚:登录商户平台进入账户中心-违约记录查询是否违约记录,如果有按照上面的指引解决违约记录之后重试
328        // 2.
329        // 检查特约商户是否已授权服务商商户分账权限:登录商户平台进入产品中心-特约商户授权产品,在特约商户列表中看看该特约商户是否存在并且授权状态为“已授权”,
330        // 如果不存在或者授权状态不是“已授权”就需要发起授权邀请,等特约商户审批之后重新查询一遍,授权状态为“已授权”之后再重试
331        // 描述:服务商商户被处罚或者服务商商户和特约商户没有父子受理关系
332      } else if (e.getErrorCode() == "SIGN_ERROR") {
333        // 错误:签名错误
334        // 解决方式:检查服务商商户证书序列号,证书私钥文件,公钥ID,公钥文件,同时确认下签名过程是不是按照微信支付的签名方式
335        // 描述:签名报错,需要确认签名材料和签名流程是否正确
336      } else if (e.getErrorCode() == "RESOURCE_NOT_EXISTS") {
337        // 错误:分账单不存在
338        // 解决方式:确定outOrderId和transactionId输入是否正确
339        // 描述:申请分账未成功
340      } else if (e.getErrorCode() == "PARAM_ERROR") {
341        // 错误:参数错误
342        // 解决方式:按照报错返回的message,重新输入请求参数
343        // 描述:参数的类型,长度,或者必填选项没有填写等
344      } else if (e.getErrorCode() == "INVALID_REQUEST") {
345        // 错误:请求非法,请求参数正确,但是不符合分账业务规则
346        // 解决方式:根据具体message查看具体哪里不符合业务规则,如果可以修改参数达到符合业务规则的修改请求符合业务规则之后再重试
347        // 描述:不符合业务规则的场景如:
348        // - 电商商户不允许调用非电商分账API
349      } else {
350        // 其他类型错误:稍等一会后原单重试
351      }
352      throw e;
353    }
354  }
355
356  private void FinishProfitSharing(String transactionId, String outOrderNo) {
357
358    FinishOrder client = new FinishOrder(
359        mchid,
360        certificateSerialNo,
361        privateKeyFilePath,
362        wechatPayPublicKeyId,
363        wechatPayPublicKeyFilePath);
364
365    FinishOrderRequest request = new FinishOrderRequest();
366    request.subMchid = "1900000109";
367    request.transactionId = transactionId;
368    request.outOrderNo = outOrderNo;
369    request.description = "分账完结";
370    try {
371      FinishOrderResponse response = client.run(request);
372      // 请求完结分账成功,但是不代表完结分账成功,需要通过查询分账结果来判断分账是否成功
373    } catch (WXPayUtility.ApiException e) {
374      // 异常处理逻辑
375      if (e.getErrorCode() == "SYSTEM_ERROR") {
376        // 错误:系统错误
377        // 解决方式:稍后原单重试
378        // 描述:微信支付系统失败,一定要原单重试,系统失败直接立即重试大概率还会是系统失败,建议等1分钟后再重试
379      } else if (e.getErrorCode() == "FREQUENCY_LIMITED") {
380        // 错误:限频报错
381        // 解决方式:稍后原单重试
382        // 描述: 接口频率限制,一定要原单重试,直接立即重试大概率还会是系统失败,建议等1分钟后再重试
383      } else if (e.getErrorCode() == "NO_AUTH") {
384        // 错误:无分账权限
385        // 解决方式:
386        // 1. 检查商户是否是被处罚:登录商户平台进入账户中心-违约记录查询是否违约记录,如果有按照上面的指引解决违约记录之后原单重试
387        // 2.
388        // 检查特约商户是否已授权平台分账权限:登录商户平台进入产品中心-特约商户授权产品,在特约商户列表中看看该特约商户是否存在并且授权状态为“已授权”,
389        // 如果不存在或者授权状态不是“已授权”就需要发起授权邀请,等特约商户审批之后重新查询一遍,授权状态为“已授权”之后再原单重试
390        // 描述:服务商商户被处罚或者服务商商户和特约商户没有父子受理关系
391      } else if (e.getErrorCode() == "SIGN_ERROR") {
392        // 错误:签名错误
393        // 解决方式:检查服务商商户证书序列号,证书私钥文件,公钥ID,公钥文件,同时确认下签名过程是不是按照微信支付的签名方式
394        // 描述:签名报错,需要确认签名材料和签名流程是否正确
395      } else if (e.getErrorCode() == "PARAM_ERROR") {
396        // 错误:参数错误
397        // 解决方式:按照报错返回的message,重新输入请求参数
398        // 描述:参数的类型,长度,或者必填选项没有填写等
399      } else if (e.getErrorCode() == "NOT_ENOUGH") {
400        // 错误:账户余额不足
401        // 解决方式:商户充值之后原单重试
402        // 描述:商户账户余额不足,导致退款失败,商户充值之后一定要原单重试
403      } else if (e.getErrorCode() == "INVALID_REQUEST") {
404        // 错误:请求非法,请求参数正确,但是不符合分账业务规则
405        // 解决方式:根据具体message查看具体哪里不符合业务规则,如果可以修改参数达到符合业务规则的修改请求符合业务规则之后再原单重试
406        // 描述:不符合业务规则的场景如:
407        // - 分账次数过多,该笔订单分账次数已经超过50次了,不能再发起分账了
408        // - 该笔订单还未结算完成,请等待结算完成才能发起分账
409        // - 剩余可分账金额不足
410        // - 小程序交易被冻结,在用户主动/系统自动确认收货后才进行资金结算,详细规则可查看《交易类小程序运营规范》
411        // - 订单已过期,不支持分账,请等待系统自动解冻(订单已经超过180天,不允许发起分账)
412
413      } else {
414        // 其他类型错误:稍等一会后原单重试
415      }
416      throw e;
417    }
418  }
419}
420

3.2、分账回退

当成功分账给商户之后(分账给用户不支持分账回退),如果因为退款需要回退分账金额时,可以请求分账回退接口退还分账给商户的资金,请求分账回退接口会交易分账订单的状态,只有发生过退款的分账单才支持分账回退

分账回退接口为同步接口,接口返回成功即代表回退到终态,但是可能是回退成功或者回退失败,需要根据回退结果来判断,如果请求回退返回报错,可以通过查询分账回退结果接口查询分账回退结果

分账回退单状态机:

1package com.java.brand.profitsharing;
2
3import com.java.demo.CreateReturnOrder; // 使用请求分账回退接口:https://pay.weixin.qq.com/doc/v3/partner/4012467097
4import static com.java.demo.CreateReturnOrder.*;
5import com.java.demo.QueryReturnOrder; // 使用查询分账回退结果接口:https://pay.weixin.qq.com/doc/v3/partner/4012467011
6import static com.java.demo.QueryReturnOrder.*;
7import com.java.utils.WXPayUtility; // 引用微信支付工具库,参考:https://pay.weixin.qq.com/doc/v3/partner/4014985777
8
9public class ProfitSharingReturnDemo {
10  // TODO: 请准备商户开发必要参数,参考:https://pay.weixin.qq.com/doc/v3/partner/4013080340
11  // 商户号,是由微信支付系统生成并分配给每个商户的唯一标识符,商户号获取方式参考
12  // https://pay.weixin.qq.com/doc/v3/partner/4013080340
13  private static String mchid = "19xxxxxxxx";
14  // 商户API证书序列号,如何获取请参考 https://pay.weixin.qq.com/doc/v3/partner/4013058924
15  private static String certificateSerialNo = "1DDE55AD98Exxxxxxxxxx";
16  // 商户API证书私钥文件路径,本地文件路径
17  private static String privateKeyFilePath = "/path/to/apiclient_key.pem";
18  // 微信支付公钥ID,如何获取请参考 https://pay.weixin.qq.com/doc/v3/partner/4013038589
19  private static String wechatPayPublicKeyId = "PUB_KEY_ID_xxxxxxxxxxxxx";
20  // 微信支付公钥文件路径,本地文件路径
21  private static String wechatPayPublicKeyFilePath = "/path/to/wxp_pub.pem";
22
23  public static void main(String[] args) {
24    ProfitSharingReturnDemo demo = new ProfitSharingReturnDemo();
25    // 发起分账回退
26    demo.returnProfitSharing();
27  }
28
29  private void returnProfitSharing() {
30    CreateReturnOrder client = new CreateReturnOrder(
31        mchid,
32        certificateSerialNo,
33        privateKeyFilePath,
34        wechatPayPublicKeyId,
35        wechatPayPublicKeyFilePath);
36
37    CreateReturnOrderRequest request = new CreateReturnOrderRequest();
38    request.subMchid = "1900000109";
39
40    // orderId和outOrderNo二选一
41    // orderId是微信支付分账单号,即请求分账返回的orderId
42    // outOrderNo是商户分账单号,即请求分账时传入的outOrderNo
43    request.orderId = "3008450740201411110007820472";
44    // request.outOrderNo = "P20150806125346";
45    request.outReturnNo = "R20190516001";
46    request.returnMchid = "86693852";
47    // 分账回退金额不能超过分账给该商户的金额
48    request.amount = 10L;
49    request.description = "分账回退";
50    try {
51      CreateReturnOrderResponse response = client.run(request);
52      // 请求分账回退成功,即代表分账回退单到终态,但是可能是回退成功,也可能回退失败
53      switch (response.result) {
54        case "SUCCESS":
55          // 分账回退成功
56          // 商户处理自己的业务逻辑
57          break;
58        case "FAILED":
59          // 分账回退失败
60          // 可以查看response.failReason查看失败原因
61          // 如果商户根据failReason做了相应的处理,可以换单重新发起分账回退,不能原单重试了
62          break;
63        default:
64          // 非法状态,PROCESSING状态也不会出现,因为分账回退接口是同步接口,接口返回成功即代表分账回退单到终态
65          throw new RuntimeException("非法分账回退状态:" + response.result);
66      }
67
68    } catch (WXPayUtility.ApiException e) {
69      // 异常处理逻辑
70      if (e.getErrorCode() == "SYSTEM_ERROR") {
71        // 错误:系统错误
72        // 解决方式:稍后原单重试,也可以通过查询分账回退结果进行处理(demo.queryProfitSharingReturnResult())
73        // 描述:微信支付系统失败,一定要原单重试,系统失败直接立即重试大概率还会是系统失败,建议等1分钟后再重试
74      } else if (e.getErrorCode() == "FREQUENCY_LIMITED") {
75        // 错误:限频报错
76        // 解决方式:稍后原单重试
77        // 描述: 接口频率限制,一定要原单重试,直接立即重试大概率还会是系统失败,建议等1分钟后再重试
78      } else if (e.getErrorCode() == "NO_AUTH") {
79        // 错误:无分账权限
80        // 解决方式:
81        // 1. 检查商户是否是被处罚:登录商户平台进入账户中心-违约记录查询是否违约记录,如果有按照上面的指引解决违约记录之后原单重试
82        // 2.
83        // 检查特约商户是否已授权平台分账权限:登录商户平台进入产品中心-特约商户授权产品,在特约商户列表中看看该特约商户是否存在并且授权状态为“已授权”,
84        // 如果不存在或者授权状态不是“已授权”就需要发起授权邀请,等特约商户审批之后重新查询一遍,授权状态为“已授权”之后再原单重试
85        // 描述:服务商商户被处罚或者服务商商户和特约商户没有父子受理关系
86      } else if (e.getErrorCode() == "SIGN_ERROR") {
87        // 错误:签名错误
88        // 解决方式:检查服务商商户证书序列号,证书私钥文件,公钥ID,公钥文件,同时确认下签名过程是不是按照微信支付的签名方式
89        // 描述:签名报错,需要确认签名材料和签名流程是否正确
90      } else if (e.getErrorCode() == "PARAM_ERROR") {
91        // 错误:参数错误
92        // 解决方式:按照报错返回的message,重新输入请求参数
93        // 描述:参数的类型,长度,或者必填选项没有填写等
94      } else if (e.getErrorCode() == "NOT_ENOUGH") {
95        // 错误:账户余额不足
96        // 解决方式:商户充值之后原单重试
97        // 描述:商户账户余额不足,导致退款失败,商户充值之后一定要原单重试
98      } else if (e.getErrorCode() == "INVALID_REQUEST") {
99        // 错误:请求非法,请求参数正确,但是不符合分账业务规则
100        // 解决方式:根据具体message查看具体哪里不符合业务规则,如果可以修改参数达到符合业务规则的修改请求符合业务规则之后再原单重试
101        // 描述:不符合业务规则的场景如:
102        // - 分账单存在,或超过分账回退时效(只能回退180天内的分账单),请检查对应的分账单
103        // - 超过分账回退最大周期(只能回退180天内的分账单)
104        // - 剩余可回退的金额不足
105        // - 当前商户号暂不能进行“资金收款类”相关业务操作,可前往“商户平台>账户中心>违约记录”或“商家助手小程序>风险处理”了解原因
106        // - 分账回退出资商户不允许和接收商户相同,请重新输入回退商户号
107
108      } else {
109        // 其他类型错误:稍等一会后原单重试,也可以通过查询分账回退结果进行处理(demo.queryProfitSharingReturnResult())
110      }
111      throw e;
112    }
113  }
114
115  private void queryProfitSharingReturnResult() {
116    QueryReturnOrder client = new QueryReturnOrder(
117        mchid,
118        certificateSerialNo,
119        privateKeyFilePath,
120        wechatPayPublicKeyId,
121        wechatPayPublicKeyFilePath);
122
123    QueryReturnOrderRequest request = new QueryReturnOrderRequest();
124    request.subMchid = "1900000109";
125    request.outReturnNo = "R20190516001";
126    // orderId和outOrderNo二选一
127    // orderId是微信支付分账单号,即请求分账返回的orderId
128    // outOrderNo是商户分账单号,即请求分账时传入的outOrderNo
129    request.orderId = "4208450740201411110007820472";
130    // request.outOrderNo = "P20190806125346";
131    try {
132      QueryReturnOrderResponse response = client.run(request);
133      // 根据response.result处理分账回退结果
134      switch (response.result) {
135        case "PROCESSING":
136          // 分账回退处理中
137          // 稍等2分钟之后再重新查单,直到分账回退单状态为SUCCESS或FAILED
138          // 也可以原单重试请求分账回退接口
139          break;
140        case "SUCCESS":
141          // 分账回退成功
142          // 商户处理自己的业务逻辑
143          break;
144        case "FAILED":
145          // 分账回退失败
146          // 可以查看response.failReason查看失败原因
147          // 如果商户根据failReason做了相应的处理,可以换单重新发起分账回退,不能原单重试了
148          break;
149        default:
150          // 非法状态
151          throw new RuntimeException("非法分账回退状态:" + response.result);
152      }
153    } catch (WXPayUtility.ApiException e) {
154      // 异常处理逻辑
155      if (e.getErrorCode() == "SYSTEM_ERROR") {
156        // 错误:系统错误
157        // 解决方式:稍后重试
158        // 描述:微信支付系统失败,系统失败直接立即重试大概率还会是系统失败,建议等1分钟后再重试
159      } else if (e.getErrorCode() == "FREQUENCY_LIMITED") {
160        // 错误:限频报错
161        // 解决方式:稍后重试
162        // 描述: 接口频率限制,直接立即重试大概率还会是系统失败,建议等1分钟后再重试
163      } else if (e.getErrorCode() == "NO_AUTH") {
164        // 错误:无分账权限
165        // 解决方式:
166        // 1. 检查商户是否是被处罚:登录商户平台进入账户中心-违约记录查询是否违约记录,如果有按照上面的指引解决违约记录之后重试
167        // 2.
168        // 检查特约商户是否已授权服务商商户分账权限:登录商户平台进入产品中心-特约商户授权产品,在特约商户列表中看看该特约商户是否存在并且授权状态为“已授权”,
169        // 如果不存在或者授权状态不是“已授权”就需要发起授权邀请,等特约商户审批之后重新查询一遍,授权状态为“已授权”之后再重试
170        // 描述:服务商商户被处罚或者服务商商户和特约商户没有父子受理关系
171      } else if (e.getErrorCode() == "SIGN_ERROR") {
172        // 错误:签名错误
173        // 解决方式:检查服务商商户证书序列号,证书私钥文件,公钥ID,公钥文件,同时确认下签名过程是不是按照微信支付的签名方式
174        // 描述:签名报错,需要确认签名材料和签名流程是否正确
175      } else if (e.getErrorCode() == "PARAM_ERROR") {
176        // 错误:参数错误
177        // 解决方式:按照报错返回的message,重新输入请求参数
178        // 描述:参数的类型,长度,或者必填选项没有填写等,大概率是传的微信支付订单号是非法的
179      } else if (e.getErrorCode() == "INVALID_REQUEST") {
180        // 错误:请求非法,请求参数正确,但是不符合分账业务规则
181        // 解决方式:根据具体message查看具体哪里不符合业务规则,如果可以修改参数达到符合业务规则的修改请求符合业务规则之后再原单重试
182      } else {
183        // 其他类型错误:稍等一会后原单重试
184      }
185      throw e;
186    }
187  }
188}
189