业务示例代码

更新时间:2025.09.26

1. 目标

通过本教程的学习,你应该可以通过微信支付补差的API完成补差业务,API操作包括:

2. 业务处理流程

2.1 请求补差

2.1.1 请求时机

平台商户下单的时候带上分账和补差标识,微信订单支付成功并结算完成后,发起分账前,调用这个接口进行补差。

2.1.2 请求补差条件

  • 电商平台下单时传入补差金额

  • 账户余额充足

  • 补差金额需要和下单的时候的补差金额保持一致(当订单发生用户退款时,该金额可以小于下单填写的补差金额)。

  • 一笔订单只能补差一笔

补差单的状态机:

1package com.java.demo.subsidy;
2
3import com.java.demo.CreateSubsidy; // 使用请求补差接口:https://pay.weixin.qq.com/doc/v3/partner/4012477631
4import static com.java.demo.CreateSubsidy.*;
5import com.java.utils.WXPayUtility; // 引用微信支付工具库,参考:https://pay.weixin.qq.com/doc/v3/partner/4014985777
6
7public class CreateSubsidyDemo {
8
9  public static void main(String[] argv) {
10    // TODO: 请准备商户开发必要参数,参考:https://pay.weixin.qq.com/doc/v3/partner/4013080340
11    CreateSubsidy client = new CreateSubsidy(
12      "19xxxxxxxx",                    // 商户号,是由微信支付系统生成并分配给每个商户的唯一标识符,商户号获取方式参考 https://pay.weixin.qq.com/doc/v3/partner/4013080340
13      "1DDE55AD98Exxxxxxxxxx",         // 商户API证书序列号,如何获取请参考 https://pay.weixin.qq.com/doc/v3/partner/4013058924
14      "/path/to/apiclient_key.pem",    // 商户API证书私钥文件路径,本地文件路径
15      "PUB_KEY_ID_xxxxxxxxxxxxx",      // 微信支付公钥ID,如何获取请参考 https://pay.weixin.qq.com/doc/v3/partner/4013038589
16      "/path/to/wxp_pub.pem"          // 微信支付公钥文件路径,本地文件路径
17    );
18
19    SubsidiesCreateRequest request = new SubsidiesCreateRequest();
20    // 二级子商户
21    request.subMchid = "1900000109";
22    // 微信支付订单号,即电商平台下单返回的微信支付订单号
23    request.transactionId = "4208450740201411110007820472";
24    // 补差金额,默认需要和下单时的补差金额一致,如果该笔订单存在退款,这个金额可以小于下单时的补差金额
25    request.amount = 10L;
26    // 补差描述信息
27    request.description = "测试备注";
28    // 商户补差单号,要求平台商户下唯一
29    request.outSubsidyNo = "P20150806125347";
30    try {
31      SubsidiesCreateEntity response = client.run(request);
32      // 请求成功,根据返回的状态处理业务逻辑
33      if (response.result == CreateStatus.SUCCESS) {
34        // 补差成功,商户可以更新自己的业务单据状态
35        // ....
36      } else if (response.result == CreateStatus.FAIL) {
37        // 补差失败,这个非终态,微信支付系统会最终扭转到REFUND状态
38        // 商户如果还需要发起补差,可以换单(换outSubsidyNo)重试
39      } else if (response.result == CreateStatus.REFUND) {
40        // 补差失败,并且已经回退成功
41        // 改状态为终态,商户如果没有换单(outSubsidyNo)直接重试,那么还是一直返回REFUND状态
42        // 商户如果还需要发起补差,需要换单(换outSubsidyNo)重试
43      } else {
44        // 非法状态
45        throw new RuntimeException("非法的补差单状态");
46      }
47    } catch (WXPayUtility.ApiException e) {
48      // 异常处理逻辑
49      if (e.getErrorCode().equals("SYSTEM_ERROR")) {
50        // 错误:系统错误
51        // 解决方式:稍后原单重试
52        // 描述:微信支付系统失败,一定要原单重试,系统失败直接立即重试大概率还会是系统失败,建议等1分钟后再原单重试
53      } else if (e.getErrorCode().equals("FREQUENCY_LIMITED")) {
54        // 错误:限频报错
55        // 解决方式:稍后原单重试
56        // 描述: 接口频率限制,直接立即重试大概率还会是系统失败,建议等1分钟后再原单重试
57      } else if (e.getErrorCode().equals("NO_AUTH")) {
58        // 错误:无分账权限
59        // 解决方式:
60        //    1. 检查商户是否是被处罚:登录商户平台进入账户中心-违约记录查询是否违约记录,如果有按照上面的指引解决违约记录之后重试
61        //    2. 检查二级子商户是否已授权平台商户分账权限:登录商户平台进入产品中心-特约商户授权产品,在特约商户列表中看看该二级商户是否存在并且授权状态为“已授权”,
62        //       如果不存在或者授权状态不是“已授权”就需要发起授权邀请,等二级商户审批之后重新查询一遍,授权状态为“已授权”之后再重试
63        // 描述:平台商户被处罚或者平台商户和二级子商户没有父子受理关系
64      } else if (e.getErrorCode().equals("SIGN_ERROR")) {
65        // 错误:签名错误
66        // 解决方式:检查平台商户证书序列号,证书私钥文件,公钥ID,公钥文件,同时确认下签名过程是不是按照微信支付的签名方式
67        // 描述:签名报错,需要确认签名材料和签名流程是否正确
68      } else if (e.getErrorCode().equals("PARAM_ERROR")) {
69        // 错误:参数错误
70        // 解决方式:按照报错返回的message,重新输入请求参数
71        // 描述:参数的类型,长度,或者必填选项没有填写等,大概率是传的微信支付订单号是非法的
72      } else if (e.getErrorCode().equals("NOT_ENOUGH")) {
73        // 错误:账户余额不足
74        // 解决方式:商户充值之后原单重试
75        // 描述:商户账户余额不足,导致退款失败,商户充值之后一定要原单重试
76      } else if (e.getErrorCode().equals("ORDER_NOT_EXIST")) {
77        // 错误:微信支付订单号不存在
78        // 解决方式:确认请求参数里面的微信支付订单号,并且这个订单号是属于请求里的二级子商户
79        // 描述:微信支付订单号不存在
80      } else if (e.getErrorCode().equals("INVALID_REQUEST")) {
81        // 错误:请求非法,请求参数正确,但是不符合补差业务规则
82        // 解决方式:根据具体message查看具体哪里不符合业务规则,如果可以修改参数达到符合业务规则的修改请求符合业务规则之后再原单重试
83        // 描述:不符合业务规则的场景如:
84        //        - 补差单创建超过180天了,还在一直重试请求补差
85        //        - 该笔订单非分账订单或者非补差订单,不能请求补差
86        //        - 该笔订单已经补差过了,不能再进行补差了
87        //        - 该商户没有分账权限,不能请求补差
88        //        - 该笔订单还未完成结算,需要等一会(建议5分钟)原单重试
89        //        - 已经存在了一笔商户补差单号对应的补差单,但是补差单的信息和此次请求的参数不一致
90        //        - 请求中的补差金额超过了下单时的补差金额
91        //        - 订单没有退款,补差金额不能小于下单时金额
92      } else {
93        // 其他类型错误:稍等一会后原单重试
94      }
95      throw e;
96    }
97  }
98}

2.2 取消补差

2.2.1 取消补差的时机

对带有补差标识的订单,如果不需要补差,可在发起发起分账前,可调用这个接口进行取消补差

此接口采用同步处理模式,即在接收到商户请求后,会实时返回处理结果。

2.2.2 取消补差的条件

订单发生过退款

1package com.java.demo.subsidy;
2
3import com.java.demo.CancelSubsidy; // 使用取消补差接口:https://pay.weixin.qq.com/doc/v3/partner/4012477639
4import static com.java.demo.CancelSubsidy.*;
5import com.java.utils.WXPayUtility; // 引用微信支付工具库,参考:https://pay.weixin.qq.com/doc/v3/partner/4014985777
6
7public class CancelSubsidyDemo {
8
9  public static void main(String[] argv) {
10    // TODO: 请准备商户开发必要参数,参考:https://pay.weixin.qq.com/doc/v3/partner/4013080340
11    CancelSubsidy client = new CancelSubsidy(
12      "19xxxxxxxx",                    // 商户号,是由微信支付系统生成并分配给每个商户的唯一标识符,商户号获取方式参考 https://pay.weixin.qq.com/doc/v3/partner/4013080340
13      "1DDE55AD98Exxxxxxxxxx",         // 商户API证书序列号,如何获取请参考 https://pay.weixin.qq.com/doc/v3/partner/4013058924
14      "/path/to/apiclient_key.pem",    // 商户API证书私钥文件路径,本地文件路径
15      "PUB_KEY_ID_xxxxxxxxxxxxx",      // 微信支付公钥ID,如何获取请参考 https://pay.weixin.qq.com/doc/v3/partner/4013038589
16      "/path/to/wxp_pub.pem"          // 微信支付公钥文件路径,本地文件路径
17    );
18
19    SubsidiesCancelRequest request = new SubsidiesCancelRequest();
20    // 二级子商户
21    request.subMchid = "1900013401";
22    // 微信支付订单号,即电商平台下单返回的微信支付订单号
23    request.transactionId = "4208450740201411110007820472";
24    // 取消补差描述
25    request.description = "订单退款";
26    try {
27      SubsidiesCancelEntity response = client.run(request);
28      // 请求取消补差成功之后,返回的result理论上只有SUCCESS,表示取消补差成功,该笔订单可以发起分账了
29      // 如果返回的result不是SUCCESS认为是微信支付系统失败,可以找微信支付助手帮忙定位
30    } catch (WXPayUtility.ApiException e) {
31      // 异常处理逻辑
32      if (e.getErrorCode().equals("SYSTEM_ERROR")) {
33        // 错误:系统错误
34        // 解决方式:稍后重试
35        // 描述:微信支付系统失败,系统失败直接立即重试大概率还会是系统失败,建议等1分钟后再重试
36      } else if (e.getErrorCode().equals("FREQUENCY_LIMITED")) {
37        // 错误:限频报错
38        // 解决方式:稍后原单重试
39        // 描述: 接口频率限制,直接立即重试大概率还会是系统失败,建议等1分钟后再重试
40      } else if (e.getErrorCode().equals("NO_AUTH")) {
41        // 错误:无分账权限
42        // 解决方式:
43        //    1. 检查商户是否是被处罚:登录商户平台进入账户中心-违约记录查询是否违约记录,如果有按照上面的指引解决违约记录之后重试
44        //    2. 检查二级子商户是否已授权平台商户分账权限:登录商户平台进入产品中心-特约商户授权产品,在特约商户列表中看看该二级商户是否存在并且授权状态为“已授权”,
45        //       如果不存在或者授权状态不是“已授权”就需要发起授权邀请,等二级商户审批之后重新查询一遍,授权状态为“已授权”之后再重试
46        // 描述:平台商户被处罚或者平台商户和二级子商户没有父子受理关系
47      } else if (e.getErrorCode().equals("SIGN_ERROR")) {
48        // 错误:签名错误
49        // 解决方式:检查平台商户证书序列号,证书私钥文件,公钥ID,公钥文件,同时确认下签名过程是不是按照微信支付的签名方式
50        // 描述:签名报错,需要确认签名材料和签名流程是否正确
51      } else if (e.getErrorCode().equals("PARAM_ERROR")) {
52        // 错误:参数错误
53        // 解决方式:按照报错返回的message,重新输入请求参数
54        // 描述:参数的类型,长度,或者必填选项没有填写等,大概率是传的微信支付订单号是非法的
55      } else if (e.getErrorCode().equals("ORDER_NOT_EXIST")) {
56        // 错误:微信支付订单号不存在
57        // 解决方式:确认请求参数里面的微信支付订单号,并且这个订单号是属于请求里的二级子商户
58        // 描述:微信支付订单号不存在
59      } else if (e.getErrorCode().equals("INVALID_REQUEST")) {
60        // 错误:请求非法,请求参数正确,但是不符合补差业务规则
61        // 解决方式:根据具体message查看具体哪里不符合业务规则,如果可以修改参数达到符合业务规则的修改请求符合业务规则之后再原单重试
62        // 描述:不符合业务规则的场景如:
63        //        - 该笔订单非分账订单或者非补差订单,不能请求取消补差
64        //        - 订单不能取消补差,未发生过退款或者已经补差完成了
65      } else {
66        // 其他类型错误:稍等一会后原单重试
67      }
68      throw e;
69    }
70  }
71}

2.3 补差回退

订单发送退款的时候,可以对补差成功的补差单发起回退。

  • 补差回退以原补差单位依据,支持多次回退,申请回退总金额不能超过补差金额。

  • 此接口采用同步处理模式,即在接收到商户请求后,会实时返回处理结果。

  • 一笔微信退款单只允许补差回退一次。

  • 一笔微信支付订单最多只能补差回退500次

  • 一笔补差单最多只能补差回退50次

补差回退单状态:

1package com.java.demo.subsidy;
2
3import com.java.demo.CancelSubsidy; // 使用取消补差接口:https://pay.weixin.qq.com/doc/v3/partner/4012477639
4import static com.java.demo.CancelSubsidy.*;
5import com.java.utils.WXPayUtility; // 引用微信支付工具库,参考:https://pay.weixin.qq.com/doc/v3/partner/4014985777
6
7public class CancelSubsidyDemo {
8
9  public static void main(String[] argv) {
10    // TODO: 请准备商户开发必要参数,参考:https://pay.weixin.qq.com/doc/v3/partner/4013080340
11    CancelSubsidy client = new CancelSubsidy(
12      "19xxxxxxxx",                    // 商户号,是由微信支付系统生成并分配给每个商户的唯一标识符,商户号获取方式参考 https://pay.weixin.qq.com/doc/v3/partner/4013080340
13      "1DDE55AD98Exxxxxxxxxx",         // 商户API证书序列号,如何获取请参考 https://pay.weixin.qq.com/doc/v3/partner/4013058924
14      "/path/to/apiclient_key.pem",    // 商户API证书私钥文件路径,本地文件路径
15      "PUB_KEY_ID_xxxxxxxxxxxxx",      // 微信支付公钥ID,如何获取请参考 https://pay.weixin.qq.com/doc/v3/partner/4013038589
16      "/path/to/wxp_pub.pem"          // 微信支付公钥文件路径,本地文件路径
17    );
18
19    SubsidiesCancelRequest request = new SubsidiesCancelRequest();
20    // 二级子商户
21    request.subMchid = "1900013401";
22    // 微信支付订单号,即电商平台下单返回的微信支付订单号
23    request.transactionId = "4208450740201411110007820472";
24    // 取消补差描述
25    request.description = "订单退款";
26    try {
27      SubsidiesCancelEntity response = client.run(request);
28      // 请求取消补差成功之后,返回的result理论上只有SUCCESS,表示取消补差成功,该笔订单可以发起分账了
29      // 如果返回的result不是SUCCESS认为是微信支付系统失败,可以找微信支付助手帮忙定位
30    } catch (WXPayUtility.ApiException e) {
31      // 异常处理逻辑
32      if (e.getErrorCode().equals("SYSTEM_ERROR")) {
33        // 错误:系统错误
34        // 解决方式:稍后重试
35        // 描述:微信支付系统失败,系统失败直接立即重试大概率还会是系统失败,建议等1分钟后再重试
36      } else if (e.getErrorCode().equals("FREQUENCY_LIMITED")) {
37        // 错误:限频报错
38        // 解决方式:稍后原单重试
39        // 描述: 接口频率限制,直接立即重试大概率还会是系统失败,建议等1分钟后再重试
40      } else if (e.getErrorCode().equals("NO_AUTH")) {
41        // 错误:无分账权限
42        // 解决方式:
43        //    1. 检查商户是否是被处罚:登录商户平台进入账户中心-违约记录查询是否违约记录,如果有按照上面的指引解决违约记录之后重试
44        //    2. 检查二级子商户是否已授权平台商户分账权限:登录商户平台进入产品中心-特约商户授权产品,在特约商户列表中看看该二级商户是否存在并且授权状态为“已授权”,
45        //       如果不存在或者授权状态不是“已授权”就需要发起授权邀请,等二级商户审批之后重新查询一遍,授权状态为“已授权”之后再重试
46        // 描述:平台商户被处罚或者平台商户和二级子商户没有父子受理关系
47      } else if (e.getErrorCode().equals("SIGN_ERROR")) {
48        // 错误:签名错误
49        // 解决方式:检查平台商户证书序列号,证书私钥文件,公钥ID,公钥文件,同时确认下签名过程是不是按照微信支付的签名方式
50        // 描述:签名报错,需要确认签名材料和签名流程是否正确
51      } else if (e.getErrorCode().equals("PARAM_ERROR")) {
52        // 错误:参数错误
53        // 解决方式:按照报错返回的message,重新输入请求参数
54        // 描述:参数的类型,长度,或者必填选项没有填写等,大概率是传的微信支付订单号是非法的
55      } else if (e.getErrorCode().equals("ORDER_NOT_EXIST")) {
56        // 错误:微信支付订单号不存在
57        // 解决方式:确认请求参数里面的微信支付订单号,并且这个订单号是属于请求里的二级子商户
58        // 描述:微信支付订单号不存在
59      } else if (e.getErrorCode().equals("INVALID_REQUEST")) {
60        // 错误:请求非法,请求参数正确,但是不符合补差业务规则
61        // 解决方式:根据具体message查看具体哪里不符合业务规则,如果可以修改参数达到符合业务规则的修改请求符合业务规则之后再原单重试
62        // 描述:不符合业务规则的场景如:
63        //        - 该笔订单非分账订单或者非补差订单,不能请求取消补差
64        //        - 订单不能取消补差,未发生过退款或者已经补差完成了
65      } else {
66        // 其他类型错误:稍等一会后原单重试
67      }
68      throw e;
69    }
70  }
71}