业务示例代码

更新时间:2025.09.26

1. 分账

通过分账接口,根据实际业务场景将交易款项分账到其他业务参与方的账户(如:平台抽取佣金),目前默认最高分账比例30%;同时通过该接口,实现合单交易冻结资金的解冻,从而实现对二级商户的账期管理和资金分配。

适用于已开通平台收付通的平台及二级商户,平台可通过供应链分账实现对平台二级商户的账期管理和资金分配。

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.ecommerce.profitsharing;
2
3import com.java.demo.QueryOrderAmount; // 使用查询订单剩余待分金额接口:https://pay.weixin.qq.com/doc/v3/partner/4012477751
4import static com.java.demo.QueryOrderAmount.*;
5import com.java.demo.AddReceiver; // 使用添加分账接收方接口:https://pay.weixin.qq.com/doc/v3/partner/4012477758
6import static com.java.demo.AddReceiver.*;
7import com.java.demo.CreateOrder; // 使用请求分账接口:https://pay.weixin.qq.com/doc/v3/partner/4012691594
8import static com.java.demo.CreateOrder.*;
9import com.java.demo.QueryOrder; // 使用查询分账结果接口:https://pay.weixin.qq.com/doc/v3/partner/4012477734
10import static com.java.demo.QueryOrder.*;
11import com.java.demo.FinishOrder; // 使用请求分账完结接口:https://pay.weixin.qq.com/doc/v3/partner/4012477745
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().equals("SYSTEM_ERROR")) {
80        // 错误:系统错误
81        // 解决方式:稍后重试
82        // 描述:微信支付系统失败,系统失败直接立即重试大概率还会是系统失败,建议等1分钟后再重试
83      } else if (e.getErrorCode().equals("FREQUENCY_LIMITED")) {
84        // 错误:限频报错
85        // 解决方式:稍后重试
86        // 描述: 接口频率限制,直接立即重试大概率还会是系统失败,建议等1分钟后再重试
87      } else if (e.getErrorCode().equals("SIGN_ERROR")) {
88        // 错误:签名错误
89        // 解决方式:检查平台商户证书序列号,证书私钥文件,公钥ID,公钥文件,同时确认下签名过程是不是按照微信支付的签名方式
90        // 描述:签名报错,需要确认签名材料和签名流程是否正确
91      } else if (e.getErrorCode().equals("PARAM_ERROR")) {
92        // 错误:参数错误
93        // 解决方式:按照报错返回的message,重新输入请求参数
94        // 描述:参数的类型,长度,或者必填选项没有填写等,大概率是传的微信支付订单号是非法的
95      } else if (e.getErrorCode().equals("ORDER_NOT_EXIST")) {
96        // 错误:订单不存在
97        // 解决方式:查询的订单不存在,大概率是该笔订单和对应的商户不一致
98      } else if (e.getErrorCode().equals("INVALID_REQUEST")) {
99        // 错误:请求非法,请求参数正确,但是不符合分账业务规则
100        // 解决方式:根据具体message查看具体哪里不符合业务规则,如果可以修改参数达到符合业务规则的修改请求符合业务规则之后再原单重试
101        // 描述:不符合业务规则的场景如:
102        // - 订单还未结算完成,请等分账完成后再发起分账,一般等待2分钟即可
103        // - 非分账订单
104      } else {
105        // 其他类型错误:稍等一会后原单重试
106      }
107      throw e;
108    }
109  }
110
111  private void addReceiver() {
112    // TODO: 请准备商户开发必要参数,参考:https://pay.weixin.qq.com/doc/v3/partner/4013080340
113    AddReceiver client = new AddReceiver(
114        mchid,
115        certificateSerialNo,
116        privateKeyFilePath,
117        wechatPayPublicKeyId,
118        wechatPayPublicKeyFilePath);
119
120    AddReceiverRequest request = new AddReceiverRequest();
121    request.appid = "wx8888888888888888";
122    request.type = "MERCHANT_ID";
123    request.account = "190001001";
124    request.name = "示例商户全称";
125    request.relationType = "SUPPLIER";
126    try {
127      AddReceiverResponse response = client.run(request);
128      // 添加分账接收方成功
129      // 可以发起分账
130    } catch (WXPayUtility.ApiException e) {
131      // 异常处理逻辑
132      if (e.getErrorCode().equals("SYSTEM_ERROR")) {
133        // 错误:系统错误
134        // 解决方式:稍后重试
135        // 描述:微信支付系统失败,系统失败直接立即重试大概率还会是系统失败,建议等1分钟后再重试
136      } else if (e.getErrorCode().equals("FREQUENCY_LIMITED")) {
137        // 错误:限频报错
138        // 解决方式:稍后重试
139        // 描述: 接口频率限制,直接立即重试大概率还会是系统失败,建议等1分钟后再重试
140      } else if (e.getErrorCode().equals("NO_AUTH")) {
141        // 错误:无分账权限
142        // 解决方式: 检查商户是否是被处罚:登录商户平台进入账户中心-违约记录查询是否违约记录,如果有按照上面的指引解决违约记录之后重试
143        // 描述:平台商户被处罚或者平台商户和二级商户没有父子受理关系
144      } else if (e.getErrorCode().equals("SIGN_ERROR")) {
145        // 错误:签名错误
146        // 解决方式:检查平台商户证书序列号,证书私钥文件,公钥ID,公钥文件,同时确认下签名过程是不是按照微信支付的签名方式
147        // 描述:签名报错,需要确认签名材料和签名流程是否正确
148      } else if (e.getErrorCode().equals("PARAM_ERROR")) {
149        // 错误:参数错误
150        // 解决方式:按照报错返回的message,重新输入请求参数
151        // 描述:参数的类型,长度,或者必填选项没有填写等
152      } else if (e.getErrorCode().equals("INVALID_REQUEST")) {
153        // 错误:请求非法,请求参数正确,但是不符合分账业务规则
154        // 解决方式:根据具体message查看具体哪里不符合业务规则,如果可以修改参数达到符合业务规则的修改请求符合业务规则之后再原单重试
155        // 描述:不符合业务规则的场景如:
156        // - 分账接收用户没有实名
157        // - 分账接收商户不存在
158        // - 分账给用户传的appid和openid不匹配
159        // - 分账接收商户全称不匹配
160      } else {
161        // 其他类型错误:稍等一会后原单重试
162      }
163      throw e;
164    }
165  }
166
167  private void applyProfitSharing(String transactionId, String outOrderNo) {
168    CreateOrder client = new CreateOrder(
169        mchid,
170        certificateSerialNo,
171        privateKeyFilePath,
172        wechatPayPublicKeyId,
173        wechatPayPublicKeyFilePath);
174
175    CreateOrderRequest request = new CreateOrderRequest();
176    request.appid = "wx8888888888888888";
177    request.subMchid = "1900000109";
178    request.transactionId = transactionId;
179    request.outOrderNo = outOrderNo;
180    request.receivers = new ArrayList<>();
181    // 分账给商户
182    {
183      CreateOrderReceiver reciever = new CreateOrderReceiver();
184      reciever.type = "MERCHANT_ID";
185      reciever.receiverAccount = "1900000109";
186      reciever.receiverMchid = "1900000110";
187      reciever.amount = 1L;
188      reciever.description = "分给商户1900000110";
189      reciever.receiverName = client.encrypt("商户1900000110的全称");
190      request.receivers.add(reciever);
191    }
192    // 分账给用户
193    {
194      CreateOrderReceiver reciever = new CreateOrderReceiver();
195      reciever.type = "PERSONAL_OPENID";
196      reciever.receiverAccount = "oLIsd5O4GKSw4Qcsv2cQAk_mS";
197      reciever.amount = 2L;
198      reciever.description = "分给用户";
199      reciever.receiverName = client.encrypt("用户oLIsd5O4GKSw4Qcsv2cQAk_mS的姓名");
200      request.receivers.add(reciever);
201    }
202    // 注意
203    // 上面分给商户的金额+分给用户的金额之和不能大于步骤1返回的订单剩余待分金额
204    // 同时分给商户的金额+分给用户的金额之和不能超过该笔订单的总的可分金额的30%
205    // 每次分账最多可分给50个接收方
206    // 同一个分账请求内,不支持有重复的接收方
207
208    // 是否分账完成
209    // 为true时:该笔订单剩余所有的未分金额都会自动解冻给二级商户
210    // 为false时:该笔订单剩余所有的未分金额不会解冻给二级商户,该笔订单的剩余可分金额还可以继续分账
211    // 一笔订单最多可以发起50次分账
212    request.finish = false;
213    try {
214      CreateOrderResponse response = client.run(request);
215      // 请求分账成功,但是不代表分账成功,需要通过查询分账结果来判断分账是否成功
216    } catch (WXPayUtility.ApiException e) {
217      // 异常处理逻辑
218      if (e.getErrorCode().equals("SYSTEM_ERROR")) {
219        // 错误:系统错误
220        // 解决方式:稍后原单重试
221        // 描述:微信支付系统失败,一定要原单重试,系统失败直接立即重试大概率还会是系统失败,建议等1分钟后再重试
222      } else if (e.getErrorCode().equals("FREQUENCY_LIMITED")) {
223        // 错误:限频报错
224        // 解决方式:稍后原单重试
225        // 描述: 接口频率限制,一定要原单重试,直接立即重试大概率还会是系统失败,建议等1分钟后再重试
226      } else if (e.getErrorCode().equals("NO_AUTH")) {
227        // 错误:无分账权限
228        // 解决方式: 检查商户是否是被处罚:登录商户平台进入账户中心-违约记录查询是否违约记录,如果有按照上面的指引解决违约记录之后原单重试
229        // 描述:平台商户被处罚或者平台商户和二级商户没有父子受理关系
230      } else if (e.getErrorCode().equals("SIGN_ERROR")) {
231        // 错误:签名错误
232        // 解决方式:检查平台商户证书序列号,证书私钥文件,公钥ID,公钥文件,同时确认下签名过程是不是按照微信支付的签名方式
233        // 描述:签名报错,需要确认签名材料和签名流程是否正确
234      } else if (e.getErrorCode().equals("PARAM_ERROR")) {
235        // 错误:参数错误
236        // 解决方式:按照报错返回的message,重新输入请求参数
237        // 描述:参数的类型,长度,或者必填选项没有填写等
238      } else if (e.getErrorCode().equals("NOT_ENOUGH")) {
239        // 错误:账户余额不足
240        // 解决方式:商户充值之后原单重试
241        // 描述:商户账户余额不足,导致退款失败,商户充值之后一定要原单重试
242      } else if (e.getErrorCode().equals("INVALID_REQUEST")) {
243        // 错误:请求非法,请求参数正确,但是不符合分账业务规则
244        // 解决方式:根据具体message查看具体哪里不符合业务规则,如果可以修改参数达到符合业务规则的修改请求符合业务规则之后再原单重试
245        // 描述:不符合业务规则的场景如:
246        // - 分账次数过多,该笔订单分账次数已经超过50次了,不能再发起分账了
247        // - 该笔订单还未结算完成,请等待结算完成才能发起分账
248        // - 剩余可分账金额不足
249        // - 小程序交易被冻结,在用户主动/系统自动确认收货后才进行资金结算,详细规则可查看《交易类小程序运营规范》
250        // - 订单已过期,不支持分账,请等待系统自动解冻(订单已经超过180天,不允许发起分账)
251
252      } else {
253        // 其他类型错误:稍等一会后原单重试
254      }
255      throw e;
256    }
257  }
258
259  private void queryProfitSharingResult(String transactionId, String outOrderNo) {
260    QueryOrder client = new QueryOrder(
261        mchid,
262        certificateSerialNo,
263        privateKeyFilePath,
264        wechatPayPublicKeyId,
265        wechatPayPublicKeyFilePath);
266
267    QueryOrderRequest request = new QueryOrderRequest();
268    request.subMchid = "1900000109";
269    request.transactionId = transactionId;
270    request.outOrderNo = outOrderNo;
271    try {
272      QueryOrderResponse response = client.run(request);
273      // 1. 根据分账单状态处理分账结果
274      switch (response.status) {
275        case "PROCESSING":
276          // 分账处理中
277          // 稍等2分钟之后再重新查单,直到分账单状态为FINISHED
278          break;
279        case "FINISHED":
280          // 分账完成,检查每个接收方状态
281          for (QueryOrder.OrderReceiverDetail receiver : response.receivers) {
282            switch (receiver.result) {
283              case "SUCCESS":
284                // 分账成功
285                // 处理商户自己的业务逻辑
286                break;
287              case "CLOSED":
288                // 分账失败
289                // 分账给该分账方失败了,可以通过receiver.failReason查看失败原因,对应的处理指引见:https://pay.weixin.qq.com/doc/v3/partner/4015504955
290                // 商户如果根据失败原因做了相应的处理,可以换单重新发起分账
291                break;
292              default:
293                // 非法状态,因为上面分账单已经是FINISHED状态了,所以分账接收方的状态也不会出现pending状态
294                throw new RuntimeException("非法分账接收方状态:" + receiver.result);
295            }
296          }
297          break;
298        default:
299          // 非法状态
300          throw new RuntimeException("非法分账单状态:" + response.status);
301      }
302    } catch (WXPayUtility.ApiException e) {
303      // 异常处理逻辑
304      if (e.getErrorCode().equals("SYSTEM_ERROR")) {
305        // 错误:系统错误
306        // 解决方式:稍后重试
307        // 描述:微信支付系统失败,系统失败直接立即重试大概率还会是系统失败,建议等1分钟后再重试
308      } else if (e.getErrorCode().equals("FREQUENCY_LIMITED")) {
309        // 错误:限频报错
310        // 解决方式:稍后重试
311        // 描述: 接口频率限制,直接立即重试大概率还会是系统失败,建议等1分钟后再重试
312      } else if (e.getErrorCode().equals("NO_AUTH")) {
313        // 错误:无分账权限
314        // 解决方式:检查商户是否是被处罚:登录商户平台进入账户中心-违约记录查询是否违约记录,如果有按照上面的指引解决违约记录之后重试
315        // 描述:平台商户被处罚或者平台商户和二级商户没有父子受理关系
316      } else if (e.getErrorCode().equals("SIGN_ERROR")) {
317        // 错误:签名错误
318        // 解决方式:检查平台商户证书序列号,证书私钥文件,公钥ID,公钥文件,同时确认下签名过程是不是按照微信支付的签名方式
319        // 描述:签名报错,需要确认签名材料和签名流程是否正确
320      } else if (e.getErrorCode().equals("RESOURCE_NOT_EXISTS")) {
321        // 错误:分账单不存在
322        // 解决方式:确定outOrderId和transactionId输入是否正确
323        // 描述:申请分账未成功
324      } else if (e.getErrorCode().equals("PARAM_ERROR")) {
325        // 错误:参数错误
326        // 解决方式:按照报错返回的message,重新输入请求参数
327        // 描述:参数的类型,长度,或者必填选项没有填写等
328      } else if (e.getErrorCode().equals("INVALID_REQUEST")) {
329        // 错误:请求非法,请求参数正确,但是不符合分账业务规则
330        // 解决方式:根据具体message查看具体哪里不符合业务规则,如果可以修改参数达到符合业务规则的修改请求符合业务规则之后再重试
331        // 描述:不符合业务规则的场景如:
332        // - 电商商户不允许调用非电商分账API
333      } else {
334        // 其他类型错误:稍等一会后原单重试
335      }
336      throw e;
337    }
338  }
339
340  private void FinishProfitSharing(String transactionId, String outOrderNo) {
341
342    FinishOrder client = new FinishOrder(
343        mchid,
344        certificateSerialNo,
345        privateKeyFilePath,
346        wechatPayPublicKeyId,
347        wechatPayPublicKeyFilePath);
348
349    FinishOrderRequest request = new FinishOrderRequest();
350    request.subMchid = "1900000109";
351    request.transactionId = transactionId;
352    request.outOrderNo = outOrderNo;
353    request.description = "分账完结";
354    try {
355      FinishOrderResponse response = client.run(request);
356      // 请求完结分账成功,但是不代表完结分账成功,需要通过查询分账结果来判断分账是否成功
357    } catch (WXPayUtility.ApiException e) {
358      // 异常处理逻辑
359      if (e.getErrorCode().equals("SYSTEM_ERROR")) {
360        // 错误:系统错误
361        // 解决方式:稍后原单重试
362        // 描述:微信支付系统失败,一定要原单重试,系统失败直接立即重试大概率还会是系统失败,建议等1分钟后再重试
363      } else if (e.getErrorCode().equals("FREQUENCY_LIMITED")) {
364        // 错误:限频报错
365        // 解决方式:稍后原单重试
366        // 描述: 接口频率限制,一定要原单重试,直接立即重试大概率还会是系统失败,建议等1分钟后再重试
367      } else if (e.getErrorCode().equals("NO_AUTH")) {
368        // 错误:无分账权限
369        // 解决方式:检查商户是否是被处罚:登录商户平台进入账户中心-违约记录查询是否违约记录,如果有按照上面的指引解决违约记录之后原单重试
370        // 描述:平台商户被处罚或者平台商户和二级商户没有父子受理关系
371      } else if (e.getErrorCode().equals("SIGN_ERROR")) {
372        // 错误:签名错误
373        // 解决方式:检查平台商户证书序列号,证书私钥文件,公钥ID,公钥文件,同时确认下签名过程是不是按照微信支付的签名方式
374        // 描述:签名报错,需要确认签名材料和签名流程是否正确
375      } else if (e.getErrorCode().equals("PARAM_ERROR")) {
376        // 错误:参数错误
377        // 解决方式:按照报错返回的message,重新输入请求参数
378        // 描述:参数的类型,长度,或者必填选项没有填写等
379      } else if (e.getErrorCode().equals("NOT_ENOUGH")) {
380        // 错误:账户余额不足
381        // 解决方式:商户充值之后原单重试
382        // 描述:商户账户余额不足,导致退款失败,商户充值之后一定要原单重试
383      } else if (e.getErrorCode().equals("INVALID_REQUEST")) {
384        // 错误:请求非法,请求参数正确,但是不符合分账业务规则
385        // 解决方式:根据具体message查看具体哪里不符合业务规则,如果可以修改参数达到符合业务规则的修改请求符合业务规则之后再原单重试
386        // 描述:不符合业务规则的场景如:
387        // - 分账次数过多,该笔订单分账次数已经超过50次了,不能再发起分账了
388        // - 该笔订单还未结算完成,请等待结算完成才能发起分账
389        // - 剩余可分账金额不足
390        // - 小程序交易被冻结,在用户主动/系统自动确认收货后才进行资金结算,详细规则可查看《交易类小程序运营规范》
391        // - 订单已过期,不支持分账,请等待系统自动解冻(订单已经超过180天,不允许发起分账)
392
393      } else {
394        // 其他类型错误:稍等一会后原单重试
395      }
396      throw e;
397    }
398  }
399}
400

3.2 分账回退

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

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

分账回退单状态机:

1package com.java.ecommerce.profitsharing;
2
3import com.java.demo.CreateReturnOrder; // 使用请求分账回退接口:https://pay.weixin.qq.com/doc/v3/partner/4012477737
4import static com.java.demo.CreateReturnOrder.*;
5import com.java.demo.QueryReturnOrder; // 使用查询分账回退结果接口:https://pay.weixin.qq.com/doc/v3/partner/4012477740
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    request.transactionId = "4208450740201411110007820472";
51    try {
52      CreateReturnOrderResponse response = client.run(request);
53      // 请求分账回退成功,即代表分账回退单到终态,但是可能是回退成功,也可能回退失败
54      switch (response.result) {
55        case "SUCCESS":
56          // 分账回退成功
57          // 商户处理自己的业务逻辑
58          break;
59        case "FAILED":
60          // 分账回退失败
61          // 可以查看response.failReason查看失败原因
62          // 如果商户根据failReason做了相应的处理,可以换单重新发起分账回退,不能原单重试了
63          break;
64        default:
65          // 非法状态,PROCESSING状态也不会出现,因为分账回退接口是同步接口,接口返回成功即代表分账回退单到终态
66          throw new RuntimeException("非法分账回退状态:" + response.result);
67      }
68
69    } catch (WXPayUtility.ApiException e) {
70      // 异常处理逻辑
71      if (e.getErrorCode().equals("SYSTEM_ERROR")) {
72        // 错误:系统错误
73        // 解决方式:稍后原单重试,也可以通过查询分账回退结果进行处理(demo.queryProfitSharingReturnResult())
74        // 描述:微信支付系统失败,一定要原单重试,系统失败直接立即重试大概率还会是系统失败,建议等1分钟后再重试
75      } else if (e.getErrorCode().equals("FREQUENCY_LIMITED")) {
76        // 错误:限频报错
77        // 解决方式:稍后原单重试
78        // 描述: 接口频率限制,一定要原单重试,直接立即重试大概率还会是系统失败,建议等1分钟后再重试
79      } else if (e.getErrorCode().equals("NO_AUTH")) {
80        // 错误:无分账权限
81        // 解决方式:检查商户是否是被处罚:登录商户平台进入账户中心-违约记录查询是否违约记录,如果有按照上面的指引解决违约记录之后原单重试
82        // 描述:平台商户被处罚或者平台商户和二级商户没有父子受理关系
83      } else if (e.getErrorCode().equals("SIGN_ERROR")) {
84        // 错误:签名错误
85        // 解决方式:检查平台商户证书序列号,证书私钥文件,公钥ID,公钥文件,同时确认下签名过程是不是按照微信支付的签名方式
86        // 描述:签名报错,需要确认签名材料和签名流程是否正确
87      } else if (e.getErrorCode().equals("PARAM_ERROR")) {
88        // 错误:参数错误
89        // 解决方式:按照报错返回的message,重新输入请求参数
90        // 描述:参数的类型,长度,或者必填选项没有填写等
91      } else if (e.getErrorCode().equals("NOT_ENOUGH")) {
92        // 错误:账户余额不足
93        // 解决方式:商户充值之后原单重试
94        // 描述:商户账户余额不足,导致退款失败,商户充值之后一定要原单重试
95      } else if (e.getErrorCode().equals("INVALID_REQUEST")) {
96        // 错误:请求非法,请求参数正确,但是不符合分账业务规则
97        // 解决方式:根据具体message查看具体哪里不符合业务规则,如果可以修改参数达到符合业务规则的修改请求符合业务规则之后再原单重试
98        // 描述:不符合业务规则的场景如:
99        // - 分账单存在,或超过分账回退时效(只能回退180天内的分账单),请检查对应的分账单
100        // - 超过分账回退最大周期(只能回退180天内的分账单)
101        // - 剩余可回退的金额不足
102        // - 当前商户号暂不能进行“资金收款类”相关业务操作,可前往“商户平台>账户中心>违约记录”或“商家助手小程序>风险处理”了解原因
103        // - 分账回退出资商户不允许和接收商户相同,请重新输入回退商户号
104
105      } else {
106        // 其他类型错误:稍等一会后原单重试,也可以通过查询分账回退结果进行处理(demo.queryProfitSharingReturnResult())
107      }
108      throw e;
109    }
110  }
111
112  private void queryProfitSharingReturnResult() {
113    QueryReturnOrder client = new QueryReturnOrder(
114        mchid,
115        certificateSerialNo,
116        privateKeyFilePath,
117        wechatPayPublicKeyId,
118        wechatPayPublicKeyFilePath);
119
120    QueryReturnOrderRequest request = new QueryReturnOrderRequest();
121    request.subMchid = "1900000109";
122    request.outReturnNo = "R20190516001";
123    request.orderId = "4208450740201411110007820472";
124    request.outOrderNo = "P20190806125346";
125    try {
126      QueryReturnOrderResponse response = client.run(request);
127      // 根据response.result处理分账回退结果
128      switch (response.result) {
129        case "PROCESSING":
130          // 分账回退处理中
131          // 稍等2分钟之后再重新查单,直到分账回退单状态为SUCCESS或FAILED
132          // 也可以原单重试请求分账回退接口
133          break;
134        case "SUCCESS":
135          // 分账回退成功
136          // 商户处理自己的业务逻辑
137          break;
138        case "FAILED":
139          // 分账回退失败
140          // 可以查看response.failReason查看失败原因
141          // 如果商户根据failReason做了相应的处理,可以换单重新发起分账回退,不能原单重试了
142          break;
143        default:
144          // 非法状态
145          throw new RuntimeException("非法分账回退状态:" + response.result);
146      }
147    } catch (WXPayUtility.ApiException e) {
148      // 异常处理逻辑
149      if (e.getErrorCode().equals("SYSTEM_ERROR")) {
150        // 错误:系统错误
151        // 解决方式:稍后重试
152        // 描述:微信支付系统失败,系统失败直接立即重试大概率还会是系统失败,建议等1分钟后再重试
153      } else if (e.getErrorCode().equals("FREQUENCY_LIMITED")) {
154        // 错误:限频报错
155        // 解决方式:稍后重试
156        // 描述: 接口频率限制,直接立即重试大概率还会是系统失败,建议等1分钟后再重试
157      } else if (e.getErrorCode().equals("NO_AUTH")) {
158        // 错误:无分账权限
159        // 解决方式:检查商户是否是被处罚:登录商户平台进入账户中心-违约记录查询是否违约记录,如果有按照上面的指引解决违约记录之后重试
160        // 描述:平台商户被处罚或者平台商户和二级商户没有父子受理关系
161      } else if (e.getErrorCode().equals("SIGN_ERROR")) {
162        // 错误:签名错误
163        // 解决方式:检查平台商户证书序列号,证书私钥文件,公钥ID,公钥文件,同时确认下签名过程是不是按照微信支付的签名方式
164        // 描述:签名报错,需要确认签名材料和签名流程是否正确
165      } else if (e.getErrorCode().equals("PARAM_ERROR")) {
166        // 错误:参数错误
167        // 解决方式:按照报错返回的message,重新输入请求参数
168        // 描述:参数的类型,长度,或者必填选项没有填写等,大概率是传的微信支付订单号是非法的
169      } else if (e.getErrorCode().equals("INVALID_REQUEST")) {
170        // 错误:请求非法,请求参数正确,但是不符合分账业务规则
171        // 解决方式:根据具体message查看具体哪里不符合业务规则,如果可以修改参数达到符合业务规则的修改请求符合业务规则之后再原单重试
172      } else {
173        // 其他类型错误:稍等一会后原单重试
174      }
175      throw e;
176    }
177  }
178}
179