业务示例代码
更新时间:2025.08.221、分账
1.1、简介
适用于已开通连锁品牌工具箱的服务商、品牌商户和接入品牌的门店,品牌可通过供应链分账对门店、连锁业态其它参与者进行更灵活的资金管理。
1.2、使用场景
品牌管理门店账期:门店独立收款,品牌可以通过供应链分账的“冻结解冻”功能实现对门店的账期管理。
品牌基于门店交易抽成:门店独立收款,品牌根据门店的每笔交易在线抽成。
分账给服务商、供应商、店员或其他分账方:门店独立收款,按每笔交易分账给品牌合作的服务商、供应商、门店店员、或者有品牌合作的其他分润方。
品牌分账给门店:在品牌统一营销活动等场景,品牌可先收款再分账给门店。
1.3、功能特点
需分账/资金管控的订单,服务商在下单时打上分账标识。
周期可控+平台兜底:服务商可根据品牌与门店的协议,实时(支付成功后30s)分账,或按周期延时分账/完结分账(解冻订单未分账资金)。最长冻结周期默认180天,若超时仍未发起分账指令,该笔订单的剩余资金将自动解冻。
多次分账+可分多方:支持同一笔订单最多分账50次,每次分账可最多可分给50个接收方。
接口可查+支持回退:提供接口查询分账结果;若已分账的订单需要退款,对于商户类型的分账接收方,服务商可协助发起分账回退,将已分账资金回退回交易方账户。
2、目标
通过本文档的学习可以利用分账相关接口完成分账流程操作
3、业务处理流程
3.1、分账
微信订单支付成功后,平台商户可以在180天内发起分账,超过180天的订单,微信支付系统会自动把该笔订单剩余未分金额解冻给子商户
分账的流程正常是:
先通过 查询订单剩余待分金额 查询订单的剩余可分金额
通过 添加分账接收方 添加分账接收方
通过 请求分账 发起分账,分账接收方的总金额不能超过步骤1中返回的剩余可分金额,同时分账接收方的总金额不能超过这笔订单全部可分金额的30%(分账给其他商户或者其他人的,不包括解冻给子商户的资金),是受理型 请求分账 接口,即请求分账接口成功之后不代表分账成功,需要通过 查询分账结果 查询分账的最终结果
发起分账成功之后,可以通过 查询分账结果 查询分账结果
分账单状态:
分账接收方分账结果:
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