AIGC标识 微信支付服务商分账流程解析

 先总结:先注册企业服务号并完成微信认证(300 元),用认证后的服务号申请成为微信支付服务商(1-5 天审核 + 对公打款验证),在服务商平台一键开通「服务商分账」产品,然后添加子商户(手动进件/扫码入驻/API 进件)并完成双向授权(服务商发起邀请、子商户登录自己平台确认并签协议,子商户不确认后面必报错),再给每个子商户配置分账接收方并设置最大分账比例(默认 30%,超了要单独申请),最后把商户号、AppID、APIv3 密钥、证书四样参数交给开发写代码(下单时必须传 profit_sharing=true,不能补传),用 1 元真实跑通一笔分账就算验收完成
以下是具体流程

第一部分 必读:核心概念与角色说明

1.1 什么是服务商分账

微信支付「服务商分账」是指:服务商(即平台方)代替其旗下的特约商户(即子商户)发起支付交易后,把这笔订单的部分资金,按指定金额分给一个或多个『分账接收方』的能力。

典型场景:

● 平台抽佣:子商户收款后,平台从中抽取服务费。

● 供应链分账:把交易款分给上游供应商、下游达人、合作伙伴等。

● 多门店结算:连锁品牌总部向各分店或加盟商分账。

⚠️ 注意:微信支付『只接受按金额分账,不接受按比例分账』。若需按比例分账,请自行计算好比例对应的金额后再调用接口。比例的作用是限制『最多能分多少』,不是分账方式。

 

【分账业务流程图】以下为微信支付官方业务流程:

▲ 微信支付特约商户进件 + 分账全流程图(来源:官方文档 4012062375)

流程图官方说明:https://pay.weixin.qq.com/doc/v3/partner/4012062375

 

1.2 四大角色说明

【分账产品模型图】以下为微信支付官方产品模型图:

▲ 分账产品模型图:分账发起方 → 分账方 → 接收方(来源:官方文档 4015870957)

产品模型官方说明:https://pay.weixin.qq.com/doc/v3/partner/4015870957

角色

说明

注意

示例标识

服务商(分账发起方)

即您自己。已认证的企业服务号申请成为微信支付服务商。替子商户发起交易,并由其调用分账接口。

无独立商户号无法收款

商户号 19xxxxxxxx

特约商户(分账方/子商户)

在服务商旗下入驻的商户。每笔订单的实际收款方,分账资金从其订单中扣除。

服务商必须先有子商户才能交易

子商户号 16xxxxxxxx

分账接收方

接收分账资金的一方。可以是微信支付商户号,也可以是微信用户的个人零钱账户。

分账前必须先建立接收方关系

商户号 / openid

微信支付平台

提供分账能力、资金清算与冻结/解冻。

服务商不直接接触资金

—

 

【角色关系图解】

● 服务商 → 替子商户发起交易 → 资金进入子商户账户(被冻结)

● 服务商 → 调用分账接口 → 把冻结资金的一部分分给接收方

● 剩余资金 → 自动解冻给子商户

● 分账接收方 → 收到资金后手动提现到银行账户

1.3 整体流程总览

从 0 到代码跑通,需要依次完成下面 6 步:

1.  成为微信支付服务商(注册服务号 → 微信认证 → 申请服务商)

2.  开通『服务商分账』产品(服务商平台一键开通)

3.  添加子服务商/特约商户(手动进件或商家扫码入驻)

4.  配置分账接收方(接口添加或商户平台手动添加)

5.  设置分账比例(子商户在商户平台设置,默认最大 30%)

6.  代码开发(下单传 profit_sharing=true → 调请求分账 → 查询结果 → 完结)

ℹ️ 提示:本文档接下来按这 6 步顺序展开,每一步都标注『入口网址』『页面路径』『填写内容』『对应截图』。非技术人员请按顺序操作,遇到截图请对照屏幕找位置。

 

第二部分 第一步:成为微信支付服务商

⚠️ 注意:前提条件:你已经是『通过微信认证的企业类型服务号』。个体户、未认证的服务号无法申请。

 

【服务商申请全流程图】

▲ 服务商申请流程图(来源:微信支付官方文档 4012062375)

流程图官方说明:https://pay.weixin.qq.com/doc/v3/partner/4012062375

2.1 注册企业类型服务号

【什么是服务号?】服务号是微信公众平台上的一种账号类型,类似企业的『官方微信』,可以让企业在微信里推送消息、提供服务、接通微信支付。

【为什么必须是服务号?】订阅号不能开通微信支付,只有服务号才能申请服务商资格。

访问入口:https://mp.weixin.qq.com/

步骤 1:打开微信公众平台

7.  在浏览器地址栏输入:https://mp.weixin.qq.com/

8.  页面右上角点击『立即注册』蓝色按钮。

▲ 操作路径:打开浏览器 → 输入网址 → 点击立即注册

ℹ️ 提示:注册页实际显示请直接访问 https://mp.weixin.qq/ 点击右上角『立即注册』查看,按页面提示选择『服务号』类型。

 

步骤 2:选择账号类型

9.  在弹出的账号类型选择页,点击『服务号』图标。

10.  注意:必须选『服务号』,不要选订阅号。订阅号无法开通微信支付。

11.  为什么必须服务号?因为微信支付的功能模块只对接服务号(或小程序),订阅号没有支付接口。

▲ 操作路径:账号类型页 → 找到服务号图标 → 点击选择

 

步骤 3:填写邮箱密码

12.  填写一个『未绑定过任何微信产品』的邮箱作为登录账号。

13.  【重要】这个邮箱不能绑定过任何个人微信、公众号、小程序、开放平台账号。建议用公司新注册的企业邮箱。

14.  密码:8 位以上,含字母数字,区分大小写。

15.  点击『注册』按钮。

▲ 操作路径:填写邮箱 → 设置密码 → 点击注册

步骤 4:邮箱激活

16.  登录刚才填写的邮箱,查收来自 weixin-team@tencent.com 的激活邮件。

17.  【如果没收到邮件】检查垃圾邮件、广告邮件文件夹;或登录 mp.weixin.qq.com 重新发送激活邮件。

18.  点击邮件中的激活链接,链接会自动跳回微信公众平台继续填写资料。

19.  【链接有效期】激活链接 24 小时内有效,过期需重新发送。

▲ 操作路径:登录邮箱 → 查收激活邮件 → 点击激活链接

步骤 5:选择主体类型与注册方式

20.  主体类型选择『企业』(不要选个体工商户或政府机关)。

21.  注册方式选择『微信认证』(注册并立即申请认证)。

22.  企业全称:与营业执照上『名称』完全一致,含括号、全角字符。

23.  营业执照注册号:18 位统一社会信用代码。

▲ 操作路径:选企业 → 选微信认证 → 填写企业信息

▲ 分账产品模型图:您即将成为『分账发起方』(来源:4015870957)

步骤 6:提交主体信息登记

 

▲ 步骤三:安装证书工具,选择存储路径,点击申请证书

▲ 步骤四:将商户号和商户名称粘贴到证书工具

步骤 5-6:复制证书请求串,粘贴到商户平台

253.  在证书工具中,复制证书请求串

254.  回到商户平台

255.  将证书工具中复制的请求串粘贴到商户平台文本框中

256.  点击『下一步』

▲ 步骤五:在证书工具中复制证书请求串

▲ 步骤六:将请求串粘贴到商户平台文本框

步骤 7-8:安全验证,复制证书串

257.  安全验证:输入操作密码 + 短信验证码

258.  点击『确定』

259.  在商户平台『复制证书串』环节,点击『复制证书串』

▲ 步骤七:安全验证,输入操作密码和短信验证码

▲ 步骤八:在商户平台复制证书串

步骤 9-10:粘贴证书串到工具,完成申请

260.  将商户平台复制的证书串粘贴到证书工具中

261.  点击『下一步』

262.  申请商户API证书成功

263.  点击『查看证书文件夹』,查看已生成的证书文件

▲ 步骤九:将证书串粘贴到证书工具

▲ 步骤十:申请成功,查看证书文件夹

【证书文件说明】生成后的证书文件为压缩包,解压后有 4 个文件:

▲ 证书文件解压后的4个文件:p12格式、pem格式、私钥pem格式、说明文件

● apiclient_cert.p12:p12 格式证书(APIv2 用)

● apiclient_cert.pem:pem 格式证书

● apiclient_key.pem:证书私钥(APIv3 签名用,最重要!)

● README.md:说明文件

❌ 重要:请将生成的证书文件转交给技术人员,由技术人员将证书部署到服务器上。私钥文件 apiclient_key.pem 只能通过证书工具导出,若丢失无法找回,只能作废重新申请。

 

证书申请完整官方文档:https://pay.weixin.qq.com/doc/v3/partner/4013058924

7.2 业务流程

完整分账流程(来自微信支付官方业务流程图):

264.  下单时传 profit_sharing=true(标识为分账订单)。

265.  用户支付成功,资金进入『冻结池』(默认冻结期 30 天,电商分账 180 天)。

266.  调用『添加分账接收方』接口建立接收方关系(一次性,已添加可跳过)。

267.  调用『请求分账』接口发起分账(接口异步受理,返回 order_id 不代表成功)。

268.  调用『查询分账结果』接口轮询/查询分账最终结果。

269.  (可选)调用『完结分账』接口把剩余资金解冻给分账方。

270.  (如退款)先调用『请求分账回退』把已分资金回退,再处理退款。

7.3 接口清单

接口名称

方法

路径

用途

添加分账接收方

POST

/v3/profitsharing/receivers/add

建立分账关系

删除分账接收方

POST

/v3/profitsharing/receivers/delete

解除分账关系

请求分账

POST

/v3/profitsharing/orders

发起分账,异步受理

查询分账结果

GET

/v3/profitsharing/orders/{out_order_no}

查询分账最终结果

完结分账

POST

/v3/profitsharing/orders/{out_order_no}/finish

解冻剩余资金给分账方

查询订单剩余待分金额

GET

/v3/profitsharing/transactions/{transaction_id}/amounts

查可分余额

请求分账回退

POST

/v3/profitsharing/returns

已分资金回退

查询分账回退结果

GET

/v3/profitsharing/returns/{out_return_no}

查回退结果

分账动账通知

POST(回调)

商户配置的 notify_url

分账/回退结果异步通知

 

7.4 完整 Java 代码示例

以下代码基于微信支付 APIv3 + Apache HttpClient,可直接放进 Spring Boot 项目。参数需替换为您自己的商户号、证书路径等。

ℹ️ 提示:代码来源:微信支付官方示例 https://pay.weixin.qq.com/doc/v3/partner/4015870957,已整理为可执行片段。

 

▲ 分账产品模型图:分账发起方 → 分账方 → 接收方(来源:官方文档 4015870957)

▲ 代码对接全流程时序图(来源:微信支付官方文档 4012062375)

7.4.1 创建 HttpClient(带签名验证)

import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;

import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;

import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;

import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;

import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;

import org.apache.http.impl.client.CloseableHttpClient;

 

import java.io.FileInputStream;

import java.security.PrivateKey;

 

public class WxPayClient {

// ===== 替换成自己的参数 =====

private static final String MCH_ID = "1900000108"; // 服务商商户号

private static final String MCH_SERIAL = "xxxxxxxxxxxx"; // 商户API证书序列号

private static final String API_V3_KEY = "xxxxxxxxxxxx"; // APIv3 密钥

private static final String KEY_PATH = "C:/cert/apiclient_key.pem"; // 商户私钥文件

 

private static CloseableHttpClient httpClient;

 

static {

try {

PrivateKey privateKey = PemUtil.loadPrivateKey(

new FileInputStream(KEY_PATH));

httpClient = WechatPayHttpClientBuilder.create()

.withMerchant(MCH_ID, MCH_SERIAL, privateKey)

.withValidator(new WechatPay2Validator(

// 自动获取并校验微信支付平台证书

CertManagerSingleton.getCertManager(API_V3_KEY).getVerifier()))

.build();

} catch (Exception e) {

throw new RuntimeException("初始化微信支付 HttpClient 失败", e);

}

}

 

public static CloseableHttpClient getClient() { return httpClient; }

}

 

7.4.2 添加分账接收方

添加接收方接口文档:https://pay.weixin.qq.com/doc/v3/partner/4012477758

▲ 接收方添加成功后可在商户平台查看(来源:官方文档 4013058924)

import org.apache.http.client.methods.HttpPost;

import org.apache.http.entity.StringEntity;

import org.apache.http.impl.client.CloseableHttpResponse;

import org.apache.http.util.EntityUtils;

 

public class AddReceiver {

/**

* 添加分账接收方

* 接口:POST /v3/profitsharing/receivers/add

* 文档:https://pay.weixin.qq.com/doc/v3/partner/4012690944

*/

public static String addReceiver(String subMchid, String receiverAccount) throws Exception {

String url = "https://api.mch.weixin.qq.com/v3/profitsharing/receivers/add";

 

// 请求体 JSON

String reqdata = "{"

+ "\"sub_mchid\":\"" + subMchid + "\","

+ "\"appid\":\"wx8888888888888888\"," // 服务商 AppID

+ "\"receivers\":[{"

+ "\"type\":\"MERCHANT_ID\"," // 接收方类型:商户号

+ "\"account\":\"" + receiverAccount + "\"," // 接收方商户号

+ "\"name\":\"某某合作公司\"," // 接收方名称(需用微信支付公钥加密)

+ "\"relation_type\":\"PARTNER\" // 关系类型:合作伙伴

+ "}]"

+ "}";

 

HttpPost httpPost = new HttpPost(url);

httpPost.addHeader("Accept", "application/json");

httpPost.addHeader("Content-Type", "application/json");

// Wechatpay-Serial 头:填微信支付公钥ID或平台证书序列号

httpPost.addHeader("Wechatpay-Serial", "PUB_KEY_ID_xxx");

httpPost.setEntity(new StringEntity(reqdata, "utf-8"));

 

try (CloseableHttpResponse response = WxPayClient.getClient().execute(httpPost)) {

int code = response.getStatusLine().getStatusCode();

String body = EntityUtils.toString(response.getEntity());

if (code == 200) {

System.out.println("添加分账接收方成功:" + body);

return body;

} else {

throw new RuntimeException("添加失败:" + code + " " + body);

}

}

}

}

 

7.4.3 请求分账

请求分账接口文档:https://pay.weixin.qq.com/doc/v3/partner/4012691594

▲ 分账产品模型图:请求分账对应图中『分账发起方 → 接收方』的资金流向(来源:4015870957)

public class CreateProfitSharing {

/**

* 请求分账

* 接口:POST /v3/profitsharing/orders

* 文档:https://pay.weixin.qq.com/doc/v3/partner/4012692975

*/

public static String create(String subMchid, String transactionId, String outOrderNo,

String receiverAccount, int amount) throws Exception {

String url = "https://api.mch.weixin.qq.com/v3/profitsharing/orders";

 

// 关键:分账金额单位是「分」,100 = 1 元

// receivers.amount 总和 ≤ 订单金额 × 子商户设置的最大分账比例

String reqdata = "{"

+ "\"sub_mchid\":\"" + subMchid + "\","

+ "\"appid\":\"wx8888888888888888\","

+ "\"transaction_id\":\"" + transactionId + "\"," // 微信支付订单号

+ "\"out_order_no\":\"" + outOrderNo + "\"," // 商户分账单号,商户系统内唯一

+ "\"receivers\":[{"

+ "\"type\":\"MERCHANT_ID\","

+ "\"account\":\"" + receiverAccount + "\"," // 接收方商户号

+ "\"amount\":" + amount + "," // 分账金额,单位:分

+ "\"description\":\"订单" + transactionId + "分账给" + receiverAccount + "\""

+ "}],"

+ "\"unfreeze_unsplit\":false" // false=不解冻剩余;true=完结分账

+ "}";

 

HttpPost httpPost = new HttpPost(url);

httpPost.addHeader("Accept", "application/json");

httpPost.addHeader("Content-Type", "application/json");

httpPost.addHeader("Wechatpay-Serial", "PUB_KEY_ID_xxx");

httpPost.setEntity(new StringEntity(reqdata, "utf-8"));

 

try (CloseableHttpResponse response = WxPayClient.getClient().execute(httpPost)) {

int code = response.getStatusLine().getStatusCode();

String body = EntityUtils.toString(response.getEntity());

// 202 = 受理成功(不是最终分账结果!需后续查询)

if (code == 200 || code == 202) {

System.out.println("分账受理成功:" + body);

return body;

} else {

throw new RuntimeException("分账失败:" + code + " " + body);

}

}

}

}

 

7.6.7 查询分账结果

查询分账结果接口文档:https://pay.weixin.qq.com/doc/v3/partner/4012477734

▲ 查询分账结果对应流程图中『查询』环节(来源:4012062375)

import org.apache.http.client.methods.HttpGet;

 

public class QueryProfitSharing {

/**

* 查询分账结果

* 接口:GET /v3/profitsharing/orders/{out_order_no}?sub_mchid=xxx

* 文档:https://pay.weixin.qq.com/doc/v3/partner/4012467002

*/

public static String query(String subMchid, String outOrderNo) throws Exception {

String url = "https://api.mch.weixin.qq.com/v3/profitsharing/orders/"

+ outOrderNo + "?sub_mchid=" + subMchid;

 

HttpGet httpGet = new HttpGet(url);

httpGet.addHeader("Accept", "application/json");

 

try (CloseableHttpResponse response = WxPayClient.getClient().execute(httpGet)) {

int code = response.getStatusLine().getStatusCode();

String body = EntityUtils.toString(response.getEntity());

if (code == 200) {

// 解析 receivers[i].result 字段:

// SUCCESS = 分账成功

// CLOSED = 已关闭(看 receivers[i].fail_reason)

// PENDING = 处理中

System.out.println("查询分账结果:" + body);

return body;

} else {

throw new RuntimeException("查询失败:" + code + " " + body);

}

}

}

}

 

7.6.8 完结分账(解冻剩余资金)

完结分账接口文档:https://pay.weixin.qq.com/doc/v3/partner/4012477745

▲ 完结分账:剩余资金自动解冻给分账方(来源:4015870957)

public class FinishProfitSharing {

/**

* 完结分账

* 接口:POST /v3/profitsharing/orders/{out_order_no}/finish

* 文档:https://pay.weixin.qq.com/doc/v3/partner/4012467016

* 用途:把剩余未分资金一次性解冻给分账方(子商户)

*/

public static String finish(String subMchid, String transactionId,

String outOrderNo) throws Exception {

String url = "https://api.mch.weixin.qq.com/v3/profitsharing/orders/"

+ outOrderNo + "/finish";

 

String reqdata = "{"

+ "\"sub_mchid\":\"" + subMchid + "\","

+ "\"transaction_id\":\"" + transactionId + "\"," // 微信支付订单号

+ "\"out_order_no\":\"" + outOrderNo + "\"," // 完结用的分账单号,不能与请求分账时相同

+ "\"sub_appid\":\"wx8888888888888889\"" // 子商户 AppID(如有)

+ "}";

 

HttpPost httpPost = new HttpPost(url);

httpPost.addHeader("Accept", "application/json");

httpPost.addHeader("Content-Type", "application/json");

httpPost.addHeader("Wechatpay-Serial", "PUB_KEY_ID_xxx");

httpPost.setEntity(new StringEntity(reqdata, "utf-8"));

 

try (CloseableHttpResponse response = WxPayClient.getClient().execute(httpPost)) {

int code = response.getStatusLine().getStatusCode();

String body = EntityUtils.toString(response.getEntity());

if (code == 200 || code == 202) {

System.out.println("完结分账受理成功:" + body);

return body;

} else {

throw new RuntimeException("完结失败:" + code + " " + body);

}

}

}

}

 

7.4.6 下单时传分账标识(关键步骤)

下单时必须在 settle_info 里传 profit_sharing=true,否则后续无法分账。示例(JSAPI 统一下单):

// 统一下单接口:POST /v3/pay/transactions/jsapi

// 关键字段:settle_info.profit_sharing = true

String reqdata = "{"

+ "\"sub_mchid\":\"1900000109\"," // 子商户号

+ "\"appid\":\"wx8888888888888888\"," // 服务商 AppID

+ "\"sub_appid\":\"wx8888888888888889\"," // 子商户 AppID(如有)

+ "\"out_trade_no\":\"ORDER20260914001\","

+ "\"description\":\"测试商品\","

+ "\"amount\":{\"total\":1000,\"currency\":\"CNY\"}," // 1000 分 = 10 元

+ "\"payer\":{\"openid\":\"oUpF8xxxxxx\"}," // 用户 openid(子商户 AppID 下的)

+ "\"settle_info\":{\"profit_sharing\":true}" // ★ 关键:分账标识

+ "}";

 

❌ 重要:profit_sharing=true 必须在下单时传,已支付订单不能补传。如果忘了,该订单无法分账。

 

7.5 常见错误码速查

来源:https://pay.weixin.qq.com/doc/v3/merchant/4020807103

错误码

含义

排查步骤

271643173

超过分账限制的最大比例

子商户在商户平台 → 产品中心 → 授权的产品 → 分账授权 中提高比例,或减少本次分账金额。

271645722

无分账权限

1) 检查服务商是否开通分账;2) 检查子商户是否已授权;3) 检查请求参数是否用了普通商户文档(应该用服务商文档)。

271643175

不是分账订单

下单时未传 settle_info.profit_sharing=true,或 transaction_id 写错。

271643176

剩余可分金额为 0

1) 已全额退款;2) 已被解冻;3) 已分完。调用『查询订单剩余待分金额』接口确认。

271644678

分账关系非法

未先调用『添加分账接收方』建立关系,或商户接收方未『同意分账』。

271644831

订单未结算,交易投诉冻结

登录商户平台 → 交易中心/消费者投诉 2.0,处理投诉后等资金解冻。

268588428

分账单已完结

已经调过完结分账(unfreeze_unsplit=true),不能再次分账。

268588427

分账单已逾期解冻

超过 180 天冻结期,剩余资金已自动解冻给分账方,无法再分。

271644182

回退方没有开通分账回退功能

接收方商户登录商户平台 → 交易中心 → 分账 → 分账接收设置 → 开启『同意分账回退』。

271644737

微信用户没有实名

接收方是个人零钱时,用户未实名认证。请用户在微信 → 我 → 服务 → 右上角三点 → 实名认证。

 

7.6 完整工程实践(Spring Boot 项目结构)

本节给出一个可直接放进 Spring Boot 项目的完整分账对接方案,包含:

● Maven 依赖(pom.xml 片段)

● application.yml 配置参数

● WxPayConfig 配置类(自动初始化 HttpClient)

● WxPayUtil 工具类(V3 请求 + 签名 + 验签)

● ProfitSharingService 完整业务 Service(含所有分账接口)

● ProfitSharingController 回调接收 Controller(验签 + 解密 + 业务处理)

● SensitiveDataUtil 敏感字段加密(name 字段必须用微信支付公钥加密)

● 定时任务 QueryScheduler(异步查询分账最终结果)

▲ 完整工程实践对应业务时序图(来源:微信支付官方文档 4012062375)

7.6.1 Maven 依赖(pom.xml)

<!-- ============ 微信支付 APIv3 官方 SDK ============ -->

<dependency>

<groupId>com.github.wechatpay-apiv3</groupId>

<artifactId>wechatpay-java</artifactId>

<version>0.2.14</version>

</dependency>

 

<!-- 兼容老版本 Apache HttpClient 风格(如果用 wechatpay-java 就不需要下面这个) -->

<dependency>

<groupId>com.github.wechatpay-apiv3</groupId>

<artifactId>wechatpay-apache-httpclient</artifactId>

<version>0.4.18</version>

</dependency>

 

<!-- JSON 处理 -->

<dependency>

<groupId>com.google.code.gson</groupId>

<artifactId>gson</artifactId>

<version>2.10.1</version>

</dependency>

 

<!-- Spring Boot Web(接收回调) -->

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-web</artifactId>

</dependency>

 

<!-- Lombok(可选) -->

<dependency>

<groupId>org.projectlombok</groupId>

<artifactId>lombok</artifactId>

<optional>true</optional>

</dependency>

 

7.6.2 application.yml 配置

# ============ 微信支付服务商参数 ============

wxpay:

# 服务商商户号

mch-id: "1900000108"

# 服务商 AppID

appid: "wx8888888888888888"

# 商户 API 证书序列号(40 位十六进制)

mch-serial: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

# APIv3 密钥(32 位字符串,自己设置的)

api-v3-key: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

# 商户私钥文件路径(apiclient_key.pem)

key-path: "C:/cert/apiclient_key.pem"

# 微信支付公钥 ID(2025 年后新商户号用公钥模式)

wechatpay-serial: "PUB_KEY_ID_xxxxxxxx"

# 回调地址(必须 https,公网可访问)

notify-url: "https://your-domain.com/api/wxpay/profit-sharing/notify"

 

# 子商户默认值(可被订单覆盖)

sub:

default-sub-mchid: "1900000109"

default-sub-appid: "wx8888888888888889"

 

ℹ️ 提示:代码使用 wechatpay-java 官方 SDK(推荐方式),底层自动处理签名、证书、平台证书自动更新。比手写 HttpClient 更安全、更省心。

 

7.6.3 WxPayConfig 配置类(自动初始化 SDK)

package com.example.wxpay.config;

 

import com.wechat.pay.java.core.Config;

import com.wechat.pay.java.core.RSAAutoUpdateConfig;

import com.wechat.pay.java.service.profitsharing.ProfitSharingService;

import com.wechat.pay.java.service.profitsharing.ProfitSharingService.Builder;

import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Value;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

 

@Slf4j

@Configuration

public class WxPayConfig {

 

@Value("${wxpay.mch-id}") private String mchId;

@Value("${wxpay.appid}") private String appid;

@Value("${wxpay.mch-serial}") private String mchSerial;

@Value("${wxpay.api-v3-key}") private String apiV3Key;

@Value("${wxpay.key-path}") private String keyPath;

@Value("${wxpay.wechatpay-serial}") private String wechatpaySerial;

 

/**

* 自动更新微信支付平台证书的 Config

* SDK 内部会定时下载并缓存平台证书,无需手动管理

*/

@Bean

public Config wxPayConfig() {

log.info("初始化微信支付 Config,商户号={}, 证书序列号={}", mchId, mchSerial);

return new RSAAutoUpdateConfig.Builder()

.merchantId(mchId)

.privateKeyFromPath(keyPath)

.merchantSerialNumber(mchSerial)

.apiV3Key(apiV3Key)

.build();

}

 

/**

* 分账 Service(官方 SDK 封装)

*/

@Bean

public ProfitSharingService profitSharingService(Config wxPayConfig) {

return new ProfitSharingService.Builder().config(wxPayConfig).build();

}

 

public String getMchId() { return mchId; }

public String getAppid() { return appid; }

public String getWechatpaySerial() { return wechatpaySerial; }

}

 

7.6.4 下单接口(必须传 profit_sharing=true)

▲ 下单传分账标识对应流程图中的『下单』环节(来源:4012062375)

下单使用服务商模式 JSAPI 统一下单:POST /v3/pay/partner/transactions/jsapi

❌ 重要:下单时必须传 settle_info.profit_sharing=true,否则后续无法分账。已支付订单不能补传。

 

package com.example.wxpay.service;

 

import com.google.gson.Gson;

import com.google.gson.GsonBuilder;

import com.wechat.pay.java.core.Config;

import com.wechat.pay.java.service.payments.jsapi.JsapiService;

import com.wechat.pay.java.service.payments.jsapi.model.*;

import com.wechat.pay.java.service.payments.model.Payer;

import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.beans.factory.annotation.Value;

import org.springframework.stereotype.Service;

 

@Slf4j

@Service

public class PartnerPayService {

 

@Autowired private Config wxPayConfig;

@Value("${wxpay.mch-id}") private String mchId;

@Value("${wxpay.appid}") private String appid;

 

/**

* 服务商模式 JSAPI 下单(带分账标识)

* 接口:POST /v3/pay/partner/transactions/jsapi

* 文档:https://pay.weixin.qq.com/doc/v3/partner/4012741851

*/

public PrepayWithRequestJsapiResponse createOrderWithProfitSharing(

String subMchid, String subAppid, String openid,

String outTradeNo, String description, int totalAmount) {

 

JsapiService jsapiService = new JsapiService.Builder().config(wxPayConfig).build();

 

PrepayWithRequestJsapiRequest request = new PrepayWithRequestJsapiRequest();

request.setSubMchid(subMchid);

request.setSubAppid(subAppid);

request.setAppid(appid);

request.setMchid(mchId);

request.setDescription(description);

request.setOutTradeNo(outTradeNo);

 

Amount amount = new Amount();

amount.setTotal(totalAmount); // 单位:分,1000=10 元

amount.setCurrency("CNY");

request.setAmount(amount);

 

Payer payer = new Payer();

payer.setSubOpenid(openid); // 子商户 AppID 下的 openid

request.setPayer(payer);

 

// ★ 关键:分账标识,必须传 true

SettleInfo settleInfo = new SettleInfo();

settleInfo.setProfitSharing(true);

request.setSettleInfo(settleInfo);

 

log.info("下单参数:{}", new Gson().toJson(request));

return jsapiService.prepayWithRequestJsapi(request);

}

}

 

7.6.5 添加分账接收方(含敏感字段加密)

添加接收方接口文档:https://pay.weixin.qq.com/doc/v3/partner/4012477758

▲ 添加接收方对应产品模型中『建立接收方列表』(来源:4015870957)

name 字段是个人姓名或商户全称,必须用微信支付公钥加密后传输。

package com.example.wxpay.service;

 

import com.wechat.pay.java.core.Config;

import com.wechat.pay.java.service.profitsharing.ProfitSharingService;

import com.wechat.pay.java.service.profitsharing.model.*;

import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.beans.factory.annotation.Value;

import org.springframework.stereotype.Service;

 

@Slf4j

@Service

public class ProfitSharingService {

 

@Autowired private ProfitSharingService profitSharingService;

@Autowired private Config wxPayConfig;

@Value("${wxpay.appid}") private String appid;

 

/**

* 添加分账接收方

* 接口:POST /v3/profitsharing/receivers/add

* 文档:https://pay.weixin.qq.com/doc/v3/partner/4012690944

*/

public AddReceiverResponse addReceiver(String subMchid, String receiverType,

String receiverAccount, String receiverName,

String relationType) {

 

AddReceiverRequest request = new AddReceiverRequest();

request.setSubMchid(subMchid);

request.setAppid(appid);

 

ReceiverDetail receiver = new ReceiverDetail();

receiver.setType(receiverType); // MERCHANT_ID / PERSONAL_OPENID / PERSONAL_SUB_OPENID

receiver.setAccount(receiverAccount);

 

// ★ name 字段处理:

// - MERCHANT_ID 类型必填商户全称(小微/个体户填开户人姓名)

// - 个人类型选填(如填,必须用微信支付公钥加密,且校验实名一致)

if (receiverName != null && !receiverName.isEmpty()) {

// SDK 会自动用微信支付公钥加密敏感字段

receiver.setName(receiverName);

}

receiver.setRelationType(relationType); // PARTNER / DISTRIBUTOR / SUPPLIER / OTHERS 等

 

request.setReceivers(java.util.Collections.singletonList(receiver));

 

log.info("添加分账接收方:subMchid={}, account={}", subMchid, receiverAccount);

return profitSharingService.addReceiver(request);

}

 

/**

* 删除分账接收方

* 接口:POST /v3/profitsharing/receivers/delete

*/

public DeleteReceiverResponse deleteReceiver(String subMchid,

String receiverType, String receiverAccount) {

DeleteReceiverRequest request = new DeleteReceiverRequest();

request.setSubMchid(subMchid);

request.setAppid(appid);

 

ReceiverDetail receiver = new ReceiverDetail();

receiver.setType(receiverType);

receiver.setAccount(receiverAccount);

request.setReceivers(java.util.Collections.singletonList(receiver));

 

log.info("删除分账接收方:account={}", receiverAccount);

return profitSharingService.deleteReceiver(request);

}

}

 

7.6.6 请求分账(核心接口)

请求分账接口文档:https://pay.weixin.qq.com/doc/v3/partner/4012691594

▲ 分账产品模型图:请求分账是整个流程的核心(来源:4015870957)

/**

* 请求分账

* 接口:POST /v3/profitsharing/orders

* 文档:https://pay.weixin.qq.com/doc/v3/partner/4012692975

*

* 关键规则:

* 1. 分账金额单位是「分」,100=1 元

* 2. receivers.amount 总和 ≤ 订单金额 × 子商户设置的最大分账比例

* 3. 接口是异步受理,返回 order_id 不代表分账成功,需后续查询

* 4. 同一笔订单最多分账 50 次,每次最多 50 个接收方

* 5. unfreeze_unsplit=true 表示本次是最后一次分账并解冻剩余资金

*/

public CreateOrderResponse createProfitSharing(

String subMchid, String transactionId, String outOrderNo,

List<Receiver> receivers, boolean unfreezeUnsplit) {

 

CreateOrderRequest request = new CreateOrderRequest();

request.setSubMchid(subMchid);

request.setAppid(appid);

request.setTransactionId(transactionId); // 微信支付订单号

request.setOutOrderNo(outOrderNo); // 商户分账单号,商户系统内唯一,6-64 位

request.setReceivers(receivers);

request.setUnfreezeUnsplit(unfreezeUnsplit); // true=本次分账同时完结,false=不解冻

 

log.info("请求分账:outOrderNo={}, receivers={}", outOrderNo, receivers.size());

return profitSharingService.createOrder(request);

}

 

/**

* 构造分账接收方列表(示例:分给商户)

*/

public List<Receiver> buildReceivers(String receiverAccount, int amountFen, String desc) {

Receiver r = new Receiver();

r.setType("MERCHANT_ID");

r.setAccount(receiverAccount); // 接收方商户号

r.setAmount(amountFen); // 分账金额,单位:分

r.setDescription(desc); // 分账描述,最多 80 字

r.setName("某某合作公司"); // 商户类型必填全称

return java.util.Collections.singletonList(r);

}

 

/**

* 构造分账给个人零钱的接收方

*/

public List<Receiver> buildPersonalReceivers(String openid, int amountFen, String desc, String userName) {

Receiver r = new Receiver();

r.setType("PERSONAL_OPENID"); // 或 PERSONAL_SUB_OPENID

r.setAccount(openid); // 用户 openid

r.setAmount(amountFen);

r.setDescription(desc);

// 个人类型 name 选填,填了会校验实名一致性

if (userName != null) r.setName(userName);

return java.util.Collections.singletonList(r);

}

 

7.6.7 查询分账结果

/**

* 查询分账结果

* 接口:GET /v3/profitsharing/orders/{out_order_no}?sub_mchid=xxx

* 文档:https://pay.weixin.qq.com/doc/v3/partner/4012467002

*

* receivers[i].result 取值:

* SUCCESS = 分账成功

* CLOSED = 已关闭(看 receivers[i].fail_reason)

* PENDING = 处理中

*/

public QueryOrderResponse queryProfitSharing(String subMchid, String outOrderNo) {

QueryOrderRequest request = new QueryOrderRequest();

request.setSubMchid(subMchid);

request.setOutOrderNo(outOrderNo);

 

QueryOrderResponse response = profitSharingService.queryOrder(request);

log.info("查询分账结果:outOrderNo={}, state={}", outOrderNo, response.getState());

 

// 遍历每个接收方的分账结果

if (response.getReceivers() != null) {

for (ReceiverResult r : response.getReceivers()) {

log.info("接收方 {} 分账结果:{},失败原因:{}",

r.getAccount(), r.getResult(), r.getFailReason());

}

}

return response;

}

 

/**

* 查询订单剩余待分金额

* 接口:GET /v3/profitsharing/transactions/{transaction_id}/amounts?sub_mchid=xxx

*/

public QueryOrderAmountsResponse queryUnsplitAmount(String subMchid, String transactionId) {

QueryOrderAmountsRequest request = new QueryOrderAmountsRequest();

request.setSubMchid(subMchid);

request.setTransactionId(transactionId);

return profitSharingService.queryOrderAmounts(request);

}

 

7.6.8 完结分账(解冻剩余资金)

/**

* 完结分账

* 接口:POST /v3/profitsharing/orders/{out_order_no}/finish

* 文档:https://pay.weixin.qq.com/doc/v3/partner/4012467016

*

* 用途:把剩余未分资金一次性解冻给分账方(子商户)

* 注意:out_order_no 必须与请求分账时的 out_order_no 不同!

*/

public FinishOrderResponse finishProfitSharing(String subMchid,

String transactionId, String outOrderNo, String subAppid) {

FinishOrderRequest request = new FinishOrderRequest();

request.setSubMchid(subMchid);

request.setTransactionId(transactionId);

request.setOutOrderNo(outOrderNo); // 完结用的单号,必须不同于请求分账的单号

if (subAppid != null) request.setSubAppid(subAppid);

 

log.info("完结分账:transactionId={}, outOrderNo={}", transactionId, outOrderNo);

return profitSharingService.finishOrder(request);

}

 

7.6.9 请求分账回退(已分资金回退)

分账回退接口文档:https://pay.weixin.qq.com/doc/v3/partner/4012477747

▲ 分账回退:已分资金回退给分账方(来源:4015870957)

退款流程:先调用本接口把已分资金回退给分账方 → 再调用普通退款接口退款。

/**

* 请求分账回退

* 接口:POST /v3/profitsharing/returns

* 文档:https://pay.weixin.qq.com/doc/v3/partner/4012467097

*

* 前置条件:接收方商户登录商户平台 → 交易中心 → 分账 → 分账接收设置 → 开启『同意分账回退』

* 否则报错 271644182

*/

public ReturnResponse createReturn(String subMchid, String orderId,

String outReturnNo, String outOrderNo,

String receiverAccount, String receiverType,

int returnAmount, String description) {

CreateReturnRequest request = new CreateReturnRequest();

request.setSubMchid(subMchid);

request.setOrderId(orderId); // 微信分账单号(请求分账返回的 order_id)

request.setOutReturnNo(outReturnNo); // 商户回退单号,商户系统内唯一

request.setOutOrderNo(outOrderNo); // 原分账单号

request.setReturnAccountType(receiverType); // MERCHANT_ID

request.setReturnAccount(receiverAccount);

request.setReturnAmount(returnAmount);

request.setDescription(description);

 

log.info("请求分账回退:outReturnNo={}, amount={}", outReturnNo, returnAmount);

return profitSharingService.createReturn(request);

}

 

/**

* 查询分账回退结果

* 接口:GET /v3/profitsharing/returns/{out_return_no}

* ?sub_mchid=xxx&out_order_no=xxx

*/

public QueryReturnResponse queryReturn(String subMchid, String outReturnNo, String outOrderNo) {

QueryReturnRequest request = new QueryReturnRequest();

request.setSubMchid(subMchid);

request.setOutReturnNo(outReturnNo);

request.setOutOrderNo(outOrderNo);

return profitSharingService.queryReturn(request);

}

 

7.6.10 回调 Controller(核心:验签 + 解密 + 业务处理)

分账动账通知官方文档:https://pay.weixin.qq.com/doc/v3/partner/4012467002

▲ 回调通知在流程图中的位置:分账成功后异步通知商户(来源:4012062375)

微信支付在分账/回退成功后会主动 POST 通知商户,需要:

● 1) 验签(确认请求来自微信支付,防伪造)

● 2) 解密 resource.ciphertext(AES-256-GCM 解密,密钥是 APIv3 密钥)

● 3) 业务处理(更新订单状态)

● 4) 返回 200 + {code: SUCCESS},否则微信会重试

package com.example.wxpay.controller;

 

import com.google.gson.Gson;

import com.google.gson.GsonBuilder;

import com.wechat.pay.java.core.notification.NotificationParser;

import com.wechat.pay.java.service.profitsharing.model.*;

import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.web.bind.annotation.*;

 

@Slf4j

@RestController

@RequestMapping("/api/wxpay/profit-sharing")

public class ProfitSharingController {

 

@Autowired private NotificationParser notificationParser;

@Autowired private OrderService orderService;

 

/**

* 分账动账通知回调

* 路径需与下单时 notify_url 一致(https://your-domain.com/api/wxpay/profit-sharing/notify)

* 文档:https://pay.weixin.qq.com/doc/v3/partner/4012075400

*

* SDK 自动完成:

* - 验签(用微信支付平台证书)

* - 解密(AES-256-GCM,密钥=APIv3 密钥)

* - 反序列化为 ProfitSharingNotifySource 对象

*/

@PostMapping("/notify")

public String profitSharingNotify(@RequestBody String body,

@RequestHeader("Wechatpay-Timestamp") String timestamp,

@RequestHeader("Wechatpay-Nonce") String nonce,

@RequestHeader("Wechatpay-Signature") String signature,

@RequestHeader("Wechatpay-Serial") String serial) {

log.info("收到分账回调:serial={}", serial);

 

try {

// 构造 RequestParam

com.wechat.pay.java.core.notification.RequestParam requestParam =

new com.wechat.pay.java.core.notification.RequestParam.Builder()

.serialNumber(serial)

.nonce(nonce)

.timestamp(timestamp)

.signature(signature)

.body(body)

.build();

 

// SDK 自动验签 + 解密,得到通知内容

ProfitSharingNotifySource notify =

notificationParser.parse(requestParam, ProfitSharingNotifySource.class);

 

log.info("分账回调解析成功:outOrderNo={}, state={}",

notify.getOutOrderNo(), notify.getState());

 

// ===== 业务处理 =====

// notify.getState():FINISHED / PROCESSING

// notify.getReceivers():每个接收方的最终结果

for (ReceiverResult r : notify.getReceivers()) {

if ("SUCCESS".equals(r.getResult())) {

// 分账成功:更新订单状态、给接收方加余额、触发下游业务

orderService.markReceiverSuccess(

notify.getOutOrderNo(),

r.getAccount(),

r.getAmount());

} else if ("CLOSED".equals(r.getResult())) {

// 分账失败:记录失败原因、通知运营处理

log.error("分账失败:account={}, reason={}", r.getAccount(), r.getFailReason());

orderService.markReceiverFailed(

notify.getOutOrderNo(),

r.getAccount(),

r.getFailReason());

}

}

 

// 必须返回 SUCCESS,否则微信会重试 8 次

return "{\"code\": \"SUCCESS\", \"message\": \"成功\"}";

 

} catch (Exception e) {

log.error("分账回调处理失败", e);

// 返回失败,微信会重试

return "{\"code\": \"FAIL\", \"message\": \"" + e.getMessage() + "\"}";

}

}

}

 

⚠️ 注意:回调必须返回 200 + {"code":"SUCCESS"}。否则微信会按 15s/15s/30s/3m/10m/20m/30m/30m/30m/60m 节奏重试 8 次。

 

7.6.11 NotificationParser Bean 配置

在 WxPayConfig 里追加:

@Bean

public NotificationParser notificationParser(Config wxPayConfig) {

// SDK 自动用 APIv3 密钥解密 + 用平台证书验签

return new NotificationParser(wxPayConfig);

}

 

7.6.12 定时任务:异步查询分账结果(兜底)

回调可能丢失,所以定时任务定期主动查询未完成的分账单:

package com.example.wxpay.scheduler;

 

import com.example.wxpay.service.ProfitSharingService;

import com.example.wxpay.entity.ProfitSharingOrder;

import com.example.wxpay.repository.ProfitSharingOrderRepository;

import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.scheduling.annotation.Scheduled;

import org.springframework.stereotype.Component;

 

import java.time.LocalDateTime;

import java.util.List;

 

@Slf4j

@Component

public class ProfitSharingQueryScheduler {

 

@Autowired private ProfitSharingService profitSharingService;

@Autowired private ProfitSharingOrderRepository orderRepository;

 

/**

* 每 5 分钟查询一次状态为 PENDING 的分账单

* 兜底回调丢失的情况

*/

@Scheduled(fixedRate = 5 * 60 * 1000)

public void queryPendingProfitSharing() {

LocalDateTime deadline = LocalDateTime.now().minusMinutes(10); // 只查 10 分钟前发起的

List<ProfitSharingOrder> pendingOrders =

orderRepository.findByStateAndUpdateTimeBefore("PENDING", deadline);

 

log.info("定时查询分账结果,待查单数:{}", pendingOrders.size());

 

for (ProfitSharingOrder order : pendingOrders) {

try {

QueryOrderResponse response = profitSharingService.queryProfitSharing(

order.getSubMchid(), order.getOutOrderNo());

 

// 更新订单状态

order.setState(response.getState());

if (response.getReceivers() != null) {

for (ReceiverResult r : response.getReceivers()) {

order.setReceiverResult(r.getAccount(),

r.getResult(), r.getFailReason());

}

}

orderRepository.save(order);

 

log.info("定时查询完成:outOrderNo={}, state={}",

order.getOutOrderNo(), response.getState());

 

} catch (Exception e) {

log.error("查询分账结果失败:outOrderNo={}",

order.getOutOrderNo(), e);

}

}

}

}

 

7.6.13 完整业务调用示例

▲ 完整业务调用对应的全流程时序图(来源:4012062375)

一个完整的分账流程调用示例(用户支付成功后自动分账):

package com.example.wxpay.service;

 

import com.example.wxpay.entity.Order;

import com.example.wxpay.repository.OrderRepository;

import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Service;

 

@Slf4j

@Service

public class ProfitSharingFacadeService {

 

@Autowired private ProfitSharingService profitSharingService;

@Autowired private OrderRepository orderRepository;

 

/**

* 完整分账流程(订单支付成功后调用)

*/

public void doProfitSharing(String outTradeNo) {

// 1. 查订单信息

Order order = orderRepository.findByOutTradeNo(outTradeNo);

if (order == null) throw new RuntimeException("订单不存在");

 

// 2. 查询订单剩余可分金额(重要!避免超额分账报错)

QueryOrderAmountsResponse amounts =

profitSharingService.queryUnsplitAmount(

order.getSubMchid(), order.getTransactionId());

int unsplitAmount = amounts.getUnsplitAmount();

 

if (unsplitAmount <= 0) {

log.warn("订单无可分金额:{}", outTradeNo);

return;

}

 

// 3. 计算本次分账金额

// 例:订单总额 100 元,服务商抽 10% = 10 元 = 1000 分

int profitAmount = order.getTotalAmount() / 10; // 服务商分 10%

// 检查不超过剩余可分金额

if (profitAmount > unsplitAmount) {

profitAmount = unsplitAmount;

}

 

// 4. 构造分账接收方(分给服务商自己的商户号)

List<Receiver> receivers = profitSharingService.buildReceivers(

"1900000110", // 接收方商户号(服务商自己)

profitAmount, // 分账金额(分)

"订单" + outTradeNo + "分给服务商" // 描述

);

 

// 5. 请求分账

String outOrderNo = "PS" + System.currentTimeMillis();

CreateOrderResponse response = profitSharingService.createProfitSharing(

order.getSubMchid(),

order.getTransactionId(),

outOrderNo,

receivers,

false // false=不解冻剩余,让子商户拿到大头;true=本次即完结

);

 

log.info("分账受理成功:orderId={}, outOrderNo={}",

response.getOrderId(), outOrderNo);

 

// 6. 保存分账单到数据库(等回调或定时任务更新结果)

orderRepository.saveProfitSharingOrder(

outOrderNo, order.getTransactionId(),

order.getSubMchid(), profitAmount, "PENDING");

 

// 注:剩余资金(订单金额-分账金额)默认自动解冻给子商户

// 如需主动解冻可调用 finishProfitSharing,但只有『最后一笔分账』才需要传 unfreeze_unsplit=true

}

 

/**

* 退款流程:先回退分账,再退款

*/

public void refundWithProfitSharing(String outTradeNo, int refundAmount) {

Order order = orderRepository.findByOutTradeNo(outTradeNo);

 

// 1. 先调用分账回退接口,把已分给服务商的资金回退给子商户

String outReturnNo = "RT" + System.currentTimeMillis();

profitSharingService.createReturn(

order.getSubMchid(),

order.getProfitSharingOrderId(), // 微信分账单号

outReturnNo,

order.getOutOrderNo(), // 原分账单号

"1900000110", // 回退给谁(原分账接收方)

"MERCHANT_ID",

refundAmount,

"退款回退分账资金"

);

 

// 2. 等回退成功(可监听回调或定时查询)后,再调用普通退款接口

// ... 此处省略退款接口调用

}

}

 

7.6.14 敏感字段手动加密(如不用 SDK)

如果直接用 HttpClient 不用官方 SDK,name 字段必须手动用微信支付公钥 RSA-OAEP 加密:

import javax.crypto.Cipher;

import javax.crypto.spec.OAEPParameterSpec;

import javax.crypto.spec.PSource;

import java.security.KeyFactory;

import java.security.PublicKey;

import java.security.spec.X509EncodedKeySpec;

import java.util.Base64;

 

public class SensitiveDataUtil {

 

/**

* 用微信支付公钥加密敏感字段

* 算法:RSA/ECB/OAEPWithSHA-1AndMGF1Padding

*/

public static String encrypt(String plaintext, String publicKeyBase64) throws Exception {

byte[] keyBytes = Base64.getDecoder().decode(publicKeyBase64);

X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);

KeyFactory kf = KeyFactory.getInstance("RSA");

PublicKey pubKey = kf.generatePublic(spec);

 

Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding");

OAEPParameterSpec oaepParams = new OAEPParameterSpec(

"SHA-1", "MGF1",

java.security.spec.MGF1ParameterSpec.SHA1,

PSource.PSpecified.DEFAULT

);

cipher.init(Cipher.ENCRYPT_MODE, pubKey, oaepParams);

byte[] encrypted = cipher.doFinal(plaintext.getBytes("UTF-8"));

return Base64.getEncoder().encodeToString(encrypted);

}

}

 

// 调用示例:

String encryptedName = SensitiveDataUtil.encrypt("某某公司", wxPayPublicKeyBase64);

// 然后传给接口的 receivers[i].name 字段

 

7.6.15 项目目录结构建议

▲ 项目结构对应分账产品模型(来源:4015870957)

src/main/java/com/example/wxpay/

├── config/

│ └── WxPayConfig.java # SDK 配置 + Bean 注入

├── controller/

│ ├── PartnerPayController.java # 下单 + 支付回调

│ └── ProfitSharingController.java # 分账回调(验签+解密+业务)

├── service/

│ ├── PartnerPayService.java # 下单(含 profit_sharing=true)

│ ├── ProfitSharingService.java # 分账接口封装

│ │ ├── addReceiver()

│ │ ├── deleteReceiver()

│ │ ├── createProfitSharing()

│ │ ├── queryProfitSharing()

│ │ ├── finishProfitSharing()

│ │ ├── createReturn() # 分账回退

│ │ └── queryReturn()

│ ├── ProfitSharingFacadeService.java # 业务编排(完整流程)

│ └── OrderService.java # 订单状态管理

├── scheduler/

│ └── ProfitSharingQueryScheduler.java # 定时查询兜底

├── entity/

│ ├── Order.java

│ └── ProfitSharingOrder.java

├── repository/

│ ├── OrderRepository.java

│ └── ProfitSharingOrderRepository.java

└── util/

└── SensitiveDataUtil.java # 敏感字段加密

 

src/main/resources/

├── application.yml # 微信支付参数

└── cert/

└── apiclient_key.pem # 商户私钥文件(保密)

 

7.6.16 测试用例(小额跑通)

▲ 测试用例覆盖的完整流程(来源:4012062375)

上线前用 1 元跑通一笔分账:

// 1. 下单(带分账标识)

PrepayWithRequestJsapiResponse resp = partnerPayService.createOrderWithProfitSharing(

"1900000109", // 子商户号

"wx8888888888888889", // 子商户 AppID

"oUpF8xxxxxx", // 用户 openid

"TEST" + System.currentTimeMillis(), // 商户订单号

"测试商品",

100 // 100 分 = 1 元

);

String transactionId = ... // 等支付成功回调获取

 

// 2. 查询可分金额

QueryOrderAmountsResponse amounts =

profitSharingService.queryUnsplitAmount("1900000109", transactionId);

// 应返回 unsplitAmount ≈ 100(扣手续费后约 99)

 

// 3. 添加接收方(一次性,已添加可跳过)

profitSharingService.addReceiver(

"1900000109",

"MERCHANT_ID",

"1900000110", // 接收方商户号(服务商自己)

"某某公司",

"PARTNER"

);

 

// 4. 请求分账(分 10 分 = 0.1 元 给服务商)

List<Receiver> receivers = profitSharingService.buildReceivers(

"1900000110", 10, "测试分账"

);

CreateOrderResponse resp = profitSharingService.createProfitSharing(

"1900000109", transactionId,

"PS" + System.currentTimeMillis(),

receivers, false

);

// 返回 orderId,state=ACCEPTED(已受理)

 

// 5. 查询分账结果(约 1 分钟后变 SUCCESS)

Thread.sleep(60 * 1000);

QueryOrderResponse query = profitSharingService.queryProfitSharing(

"1900000109", outOrderNo

);

// query.getReceivers()[0].getResult() = "SUCCESS"

 

// 6. 完结分账(剩余 89 分解冻给子商户)

profitSharingService.finishProfitSharing(

"1900000109", transactionId,

"FIN" + System.currentTimeMillis(),

"wx8888888888888889"

);

 

ℹ️ 提示:测试建议金额 1 元(100 分),避免大额分账出错。先跑通单接口,再跑通完整流程。最后接入回调处理。

 

第八部分 验收清单与官方资源

8.1 上线前检查清单(非技术人员对照打勾)

请逐项打勾,全部完成才可上线:

▲ 完整流程回顾图(来源:微信支付官方文档 4012062375)

已做

检查项

□

已注册企业服务号且通过微信认证

□

已申请成为微信支付服务商,获得服务商商户号

□

服务商平台 → 产品中心 → 特约商户授权产品 → 服务商分账 已开通

□

已发起分账授权邀请给每个子商户

□

每个子商户已在商户平台 → 产品中心 → 我授权的产品 中确认授权

□

所有子商户已完成进件(含打款验证)

□

子商户已关联小程序/公众号 AppID(如使用)

□

已通过接口或商户平台添加了分账接收方

□

子商户已在商户平台 → 产品中心 → 授权的产品 → 分账授权 设置最大分账比例

□

服务商平台 → 账户中心 → API 安全 已下载 API 证书、设置 APIv3 密钥

□

已记录服务商商户号、AppID、证书序列号、子商户号等开发参数

□

代码已实现:下单传 profit_sharing=true

□

代码已实现:请求分账、查询分账结果、完结分账

□

已用小额(如 1 元)跑通一笔分账测试

□

分账动账通知回调地址(notify_url)已配置并能正确接收

 

8.2 关键客服与咨询渠道

▲ 全流程回顾:从服务商申请到分账完成(来源:4012062375)

渠道

联系方式 / 网址

用途

微信支付客服

95017

提高分账比例申请、其他业务咨询

服务商 BD 邮箱

weixinpayBD@tencent.com

大客户合作

官方文档总站

https://pay.weixin.qq.com/

所有产品文档入口

服务商平台

https://pay.weixin.qq.com/index.php/partner/public/home

服务商登录与操作

商户平台

https://pay.weixin.qq.com/

子商户登录与操作

公众平台

https://mp.weixin.qq.com/

服务号、小程序后台

 

附录 A 全部官方文档链接

本文档所有截图、步骤、参数均来自以下官方文档页面:

● 开发接入准备:https://pay.weixin.qq.com/doc/v3/partner/4012072589

● 对接步骤(V2):https://pay.weixin.qq.com/doc/v2/partner/4011984375

● 开发指引(连锁品牌):https://pay.weixin.qq.com/doc/v3/partner/4012072637

● 添加分账接收方接口:https://pay.weixin.qq.com/doc/v3/partner/4012690944

● 请求分账接口:https://pay.weixin.qq.com/doc/v3/partner/4012692975

● 查询分账结果接口:https://pay.weixin.qq.com/doc/v3/partner/4012467002

● 完结分账接口:https://pay.weixin.qq.com/doc/v3/partner/4012467016

● 查询订单剩余待分金额:https://pay.weixin.qq.com/doc/v3/partner/4012457927

● 分账动账通知:https://pay.weixin.qq.com/doc/v3/partner/4012075400

● 分账失败处理指引:https://pay.weixin.qq.com/doc/v3/partner/4015504918

● 分账常见问题:https://pay.weixin.qq.com/doc/v3/partner/4014547107

● 分账错误码:https://pay.weixin.qq.com/doc/v3/merchant/4020807103

● 业务示例代码:https://pay.weixin.qq.com/doc/v3/partner/4015870957

● 商家注册小程序指引:https://pay.weixin.qq.com/static/help_guide/business_registration.shtml

● 微信支付服务商接入指引 PDF:https://gtimg.wechatpay.cn/resource/xres/mmpaydoc/static/attachment/d7ed63166bac0051d3b2c13ea0fc36dc/服务商接入指引-大交通中心(2020新版).pdf

● 服务商申请入口:https://pay.weixin.qq.com/index.php/partner/public/home

● 公众平台:https://mp.weixin.qq.com/

● 微信支付商户平台:https://pay.weixin.qq.com/

24.  按要求填写营业执照信息、法人/经营者身份证等信息。

25.  管理员信息:姓名、身份证号、手机号(管理员需绑定本人实名微信)。

26.  等待审核,一般 1-5 个工作日。

27.  审核结果会通过邮件和管理员微信通知。

▲ 操作路径:填营业执照 → 填法人信息 → 填管理员信息 → 提交等待

2.2 完成微信认证

【为什么必须认证?】服务号只有通过微信认证后,才能申请微信支付服务商资格。未认证的服务号只能做基础信息展示,不能开通任何支付功能。

如果注册时没有立即做认证,需要单独发起:

访问入口:https://mp.weixin.qq.com/

步骤 1:发起认证

28.  使用服务号账号登录 mp.weixin.qq.com。

29.  左侧菜单:『设置』 → 『微信认证』 → 点击『开通』。

30.  按页面指引填写认证资料,缴纳 300 元/次审核费。

31.  【费用说明】300 元是第三方审核公司收的审核服务费,不退(无论是否通过)。腾讯不收此费。

32.  等待第三方审核公司来电回访,一般 3-5 个工作日出结果。

33.  审核公司会电话核对填写的资料是否真实,请保持手机畅通。

▲ 操作路径:登录 → 设置 → 微信认证 → 开通 → 缴费

ℹ️ 提示:认证通过后,服务号才具备申请微信支付服务商的资格。请务必完成。

 

▲ 认证完成后签署各类协议(示意)

2.3 申请成为微信支付服务商

服务号认证通过后,即可发起服务商申请:

访问入口:https://mp.weixin.qq.com/

步骤 1:进入服务商申请页

34.  登录 mp.weixin.qq.com。

35.  左侧菜单:『微信支付』 → 『服务商申请』。

36.  (如果没有此菜单,说明服务号还没通过认证,请回到 2.2 完成。)

37.  【提示】部分新账号可能没有『微信支付』菜单,请先点击『添加功能插件』找到『微信支付』并开通。

▲ 操作路径:登录 → 微信支付菜单 → 服务商申请

步骤 2:填写服务商资料

38.  公司联系方式:联系人姓名、联系手机、联系邮箱(建议用企业邮箱)。

39.  客服电话:对外服务的客服座机或手机(消费者可拨打)。

40.  公司对公账户信息:开户行省市、开户账号。

41.  【重要】对公账户户名必须与服务商主体(营业执照上的公司名)完全一致,不能用个人账户。

42.  营业执照信息:营业执照号、有效期、高清扫描件(PDF/JPG/PNG,单张 ≤2M)。

43.  组织机构代码证:组织机构代码、有效期、高清扫描件(已三证合一的提供统一社会信用代码即可)。

44.  业务经办人或法人证件:身份证或护照正反面。

45.  【常见坑】身份证照片要清晰,4 个角都要露出来,不能有反光、阴影。

▲ 操作路径:填联系方式 → 填对公账户 → 上传营业执照 → 上传法人证件

步骤 3:提交并等待审核

46.  资料填写完成后点击『提交』。

47.  等待 1-5 个工作日审核。

48.  审核通过后会收到邮件和公众号通知,同时获得『服务商商户号』(10 位数字)。

49.  【服务商商户号】请妥善保存,后续代码开发会用到,登录服务商平台也用它。

▲ 操作路径:提交 → 等待审核 → 收到通知 → 获得商户号

▲ 服务商申请全流程图(来源:4012062375)

步骤 4:账户验证(打款验证)

50.  微信支付会向您填写的对公账户打一笔随机金额(0.01~1 元)的款项。

51.  财付通在 T+1 日原路退回。

52.  登录服务商平台 pay.weixin.qq.com/index.php/partner/public/home,在『待办事项』中回填收款金额。

53.  【操作位置】登录后页面顶部会显示红色提示『待办事项:1 项』,点击进入回填页面。

54.  数额正确即通过验证。

55.  【如果金额填错】有 3 次机会,3 次都错会被冻结,需要联系微信支付客服 95017 解冻。

▲ 操作路径:收到打款 → 登录平台 → 点击待办事项 → 回填金额 → 验证通过

步骤 5:在线签署协议

56.  验证通过后,在服务商平台点击『签署协议』。

57.  在线阅读并同意《微信支付服务商合作协议》。

58.  签约完成,正式成为微信支付服务商。

59.  【签约完成后】服务号会收到微信支付发来的『服务商入驻成功』通知,请保存好。

▲ 操作路径:点击签署协议 → 阅读协议 → 同意 → 成为服务商

✓ 建议:完成以上步骤后,您将拥有:1) 服务号 + 2) 服务商商户号 + 3) 服务商平台登录权限。这是后续所有操作的基础。

 

第三部分 第二步:开通服务商分账产品

成为服务商后,分账功能不是默认开通的,需要单独在服务商平台开通『服务商分账』产品。

【什么是分账?】消费者在您的平台下单支付 100 元后,资金先进入子商户的微信支付账户,但被『冻结』。您作为服务商可按约定比例,将其中一部分(如 10 元)自动分到您的商户号,剩余 90 元解冻给子商户。这就是分账。

【为什么要单独开通?】微信支付把分账作为独立产品,需要服务商和子商户分别开通并互相授权后才能用,目的是保护商户资金安全。

✓ 建议:好消息:现在分账产品已支持『后台直接开通,即时生效』,无需再像早年那样发邮件审核。但前提是您已是合规服务商且已绑定子商户。

 

3.1 开通前的5个前置条件(缺一不可)

【自检方法】对照下表逐条确认,全部满足才能继续下一步。任何一条不满足,开通按钮会是灰色或不可点。

序号

条件

如何确认已满足

1

服务商商户号已申请成功

在 pay.weixin.qq.com/index.php/partner/public/home 能正常登录

2

已绑定至少一个子商户

在『服务商功能 → 特约商户管理』能看到子商户列表

3

子商户已完成打款验证

子商户状态为『已验证』而非『待验证』

4

子商户已关联 AppID(如使用)

在『AppID 账号管理』里能看到绑定记录

5

服务商已配置 APIv3 密钥与证书

用于后续接口调用,分账产品开通不强制,但代码阶段必须

 

3.2 服务商侧开通分账产品(官方5步流程)

微信支付官方文档(https://pay.weixin.qq.com/doc/v2/partner/4011984344)明确的5步流程:

步骤

动作

备注

1

服务商在产品中心-特约商户授权产品中开通服务商分账

点击后即时生效,无需审核

2

(产品已开通,此步为确认)

状态显示『已开通』

3

服务商邀请特约商户授权:①邀请特约商户授权 ②填写功能使用场景及上传资料

需上传分账场景说明

4

特约商户授权:①收到通知后,在产品中心授权 ②签署协议 ③设置允许服务商分账的最大比例

默认30%

5

添加分账接收方

通过 API 接口添加

 

访问入口:https://pay.weixin.qq.com/index.php/partner/public/home

⚠️ 注意:登录角色:请用『超级管理员』扫码登录。普通操作员看不到产品中心开通入口。

 

【如何确认谁是超级管理员?】登录服务商平台后,在右上角账号信息里能看到『超级管理员』标识。如果显示『操作员』,需要联系超级管理员扫码登录,或让超管给操作员开通权限。

3.2.1 详细操作截图指引

【操作路径】服务商平台首页 → 左侧菜单『产品中心』 → 『特约商户授权产品』 → 找到『服务商分账』 → 点击『开通』。

步骤 1:登录服务商平台

60.  浏览器打开 https://pay.weixin.qq.com/index.php/partner/public/home

61.  使用超级管理员微信扫码登录。

62.  【扫码没反应?】请确认微信已更新到最新版本,且该微信号是服务商平台的超级管理员或操作员。

▲ 操作路径:打开浏览器 → 输入 pay.weixin.qq.com/partner/public/home → 超级管理员扫码登录

步骤 2:进入产品中心

63.  登录后,在页面左侧菜单找到『产品中心』,点击展开。

64.  在产品中心下找到『特约商户授权产品』分类。

65.  继续找到『服务商分账』子项。

66.  完整路径:产品中心 → 特约商户授权产品 → 服务商分账(位于『资金解决方案』分组下)。

67.  【找不到?】如果产品中心下没有『特约商户授权产品』分类,请确认账号已通过服务商审核;如果是新账号,开通入口可能直接显示在产品中心首页。

▲ 操作路径:左侧菜单产品中心 → 找到特约商户授权产品 → 找到服务商分账

▲ 官方文档截图:服务商平台 → 产品中心 → 特约商户授权产品 → 服务商分账

开通入口官方说明:https://pay.weixin.qq.com/doc/v3/partner/4012072589

步骤 3:点击开通

68.  在『服务商分账』右侧点击蓝色『开通』按钮。

69.  弹出协议窗口,阅读《分账产品服务协议》。

70.  【建议】把协议从头到尾读一遍,重点关注『资金责任』『退款责任』『违约条款』。

71.  勾选『我已阅读并同意』。

72.  点击『确认开通』。

73.  开通成功后,状态会从『未开通』变为『已开通』,按钮变为『管理』。

▲ 操作路径:找到服务商分账 → 点击开通 → 阅读协议 → 勾选同意 → 确认开通

ℹ️ 提示:此步骤『即时生效』,不需要审核等待。开通后才能向子商户发起授权邀请。

 

3.2.2 子商户侧开通分账(同步动作)

子商户也需要在自己的商户平台开通分账产品(与服务商侧开通同步进行)。

【为什么要子商户也开通?】因为分账涉及子商户的资金,子商户必须主动开通并授权,否则服务商无权操作子商户的资金。

子商户访问入口:https://pay.weixin.qq.com/

步骤 1:子商户登录商户平台

74.  打开 https://pay.weixin.qq.com/,扫码登录。

75.  【登录角色】子商户的超级管理员微信扫码,操作员可能无权限开通产品。

76.  左侧菜单:『产品中心』。

77.  找到『分账』产品项。

78.  点击『开通』,按提示签署协议。

79.  完整路径:产品中心 → 分账 → 开通。

80.  【如果看不到『分账』】可能是子商户还没被服务商绑定,请先完成第四部分『添加子服务商』。

▲ 操作路径:子商户打开网址 → 超级管理员扫码登录 → 产品中心 → 找到分账 → 点击开通

▲ 子商户商户平台:产品中心 → 分账 → 开通

3.3 服务商邀请子商户授权绑定分账

服务商侧、子商户侧都开通分账产品后,还需要建立『授权关系』:服务商邀请 → 子商户同意。

【这一步是必须的!】即使服务商和子商户都开通了分账,没有授权关系,服务商调用接口时会报错『无分账权限』。

❌ 重要:这一步必须做!否则服务商无法代子商户调用分账接口,会报错『无分账权限』。

 

步骤 1:发起邀请

81.  在服务商分账开通成功页面(或『产品中心 → 服务商分账 → 管理』),点击『邀请子商户授权』按钮。

82.  在弹窗中勾选要邀请的子商户(即您旗下的特约商户),可多选。

83.  点击『下一步』。

84.  【找不到子商户?】子商户必须已完成打款验证,状态为『已验证』才能被邀请。请先在『特约商户管理』里确认状态。

▲ 操作路径:进入分账管理页 → 点击邀请子商户授权 → 勾选子商户 → 点击下一步

邀请授权官方说明:https://pay.weixin.qq.com/doc/v3/partner/4012072589

步骤 2:填写场景描述(重要,决定审核通过率)

85.  在『场景描述』输入框中,用一句话描述分账业务场景。

86.  示例填写内容(推荐模板):『我司作为平台方,为旗下餐饮门店提供订单系统服务,需将消费者支付金额按约定比例分给门店经营者,作为营业款结算。』

87.  字数建议:50~200 字。

88.  必须包含三要素:① 谁分给谁 ② 为什么分 ③ 资金用途。

89.  可选:上传业务证明资料(合同、平台与商户的协议截图等)。

90.  点击『提交邀请』。

▲ 操作路径:填写场景描述 → 写50-200字含三要素 → 可选上传资料 → 提交邀请

ℹ️ 提示:场景描述示例请参照官方文档:https://pay.weixin.qq.com/doc/v3/partner/4012072589

 

⚠️ 注意:场景描述是审核依据,请如实填写。写得太笼统(如『分账业务』『平台抽佣』)可能被驳回,重新提交会延误时间。建议参照官方模板详细写。

 

3.4 子商户侧确认授权

服务商发起邀请后,子商户需要在自己的商户平台主动确认授权,并设置最大分账比例。

【关键点】子商户不确认,授权就不会生效。请提前与子商户沟通好,让对方尽快操作。

子商户访问入口:https://pay.weixin.qq.com/

步骤 1:子商户登录商户平台

91.  子商户在浏览器打开 https://pay.weixin.qq.com/

92.  使用子商户的超级管理员微信扫码登录。

93.  【提示】子商户会收到微信支付公众号的『待办通知』,点开通知会自动跳转到授权页面。

▲ 操作路径:子商户打开网址 → 超级管理员扫码登录

步骤 2:进入授权列表

94.  左侧菜单:『产品中心』。

95.  在产品中心下找到『我授权的产品』(部分版本叫『授权管理』)。

96.  在列表中找到来自您(服务商)发起的『分账』授权邀请。

97.  完整路径:产品中心 → 我授权的产品 → 找到分账授权邀请。

98.  【如果列表是空的】可能是服务商没发起邀请(回 3.3 重新发起),或子商户登录的账号不是被邀请的账号。

▲ 操作路径:左侧产品中心 → 找到我授权的产品 → 找到分账授权邀请

授权确认官方说明:https://pay.weixin.qq.com/doc/v3/partner/4012072589

步骤 3:确认授权 + 签署协议

99.  点击对应邀请右侧的『查看详情』。

100.  阅读服务商信息、分账场景描述。

101.  点击『同意授权』。

102.  弹出《分账服务协议》,阅读后点击『签署协议』。

103.  【签署方式】部分协议需要法人微信扫码签署,部分直接点击『签署』即可。

104.  页面弹出『授权成功』提示。

▲ 操作路径:点击查看详情 → 阅读服务商信息 → 同意授权 → 阅读协议 → 签署协议

▲ 签署《微信支付商户号与APPID授权协议》

步骤 4:设置最大分账比例(关键)

105.  签署完成后,页面会跳转到『分账授权』设置页。

106.  在『允许服务商分账的最大比例』输入框填数字(0-30,单位 %)。

107.  【建议】填 30(默认上限),这样服务商有灵活空间,实际分账比例由代码控制。

108.  默认值 30,表示服务商最多能从每笔订单分走 30%。

109.  点击『保存』。

110.  完整路径:产品中心 → 授权的产品 → 分账授权 → 设置比例。

▲ 操作路径:跳转分账授权设置页 → 填写最大比例 → 建议填30 → 点击保存

ℹ️ 提示:30% 是『服务商最多能分走』的上限,不是『子商户只能拿 30%』。子商户默认自留 70%-100%。

 

【举例】订单金额 100 元,子商户设置最大比例 30%。您可以在代码中分 5%、10%、20%、30% 都可以,但不能超过 30%。如果想分 50%,需要子商户在第六部分介绍的方法中提高上限。

ℹ️ 提示:必须子商户确认后,分账产品才算真正生效。如果子商户长期不确认,可让子商户登录后按上述路径操作,或服务商重新发起邀请。

 

第四部分 第三步:添加子服务商(特约商户)

如果您还没有子商户,需要先添加。如果已经有子商户并已授权分账,可跳过本部分。

4.1 子商户进件的3种方式

微信支付提供3种添加子商户的方式,按业务量与开发能力选择:

方式

适用场景

推荐度

入口

服务商平台手动进件

免开发,单个/少量商户入驻

推荐新手

无

商家扫码自助入驻

免开发,批量商户入驻

推荐大批量

下载『渠道商拓展二维码』

进件 API 接口对接

有开发能力,批量+自动化

推荐中大型平台

https://pay.weixin.qq.com/doc/v3/partner/4012719997

 

ℹ️ 提示:无论哪种方式,都需准备子商户的『主体类型对应资料』。各主体类型所需资料见下表。

 

4.2 主体类型与所需资料(5类)

来源:微信支付官方文档 https://pay.weixin.qq.com/doc/v3/partner/4012062365

主体类型

定义

所需资料

小微

无营业执照、免办理工商注册登记的实体商户

小微经营者个人身份证、负责人身份证、辅助证明(门店场所或流动经营证明)

个体工商户

营业执照主体为个体户、个体工商户、个体经营

营业执照、经营者身份证、结算银行卡

企业

营业执照主体为有限公司、有限责任公司

营业执照、法人身份证、组织机构代码证(未三证合一需提供)、结算银行账户

党政、机关及事业单位

政府机构、事业单位(公安/司法/医疗/教育等)

登记证书、法人身份证、组织机构代码证(未三证合一需提供)、结算银行账户

其他组织

社会团体、民办非企业、基金会(需有组织机构代码证)

登记证书(如民办非企业单位登记证书)、法人身份证、组织机构代码证

 

⚠️ 注意:资料要求:①所有材料原件拍照或复印件加盖公章;②身份证正反面均需加盖公章或财务章(部门章不支持);③影像资料不支持截图、翻拍或镜像照片。

 

4.3 方式一:服务商平台手动进件

访问入口:https://pay.weixin.qq.com/index.php/partner/public/home

▲ 特约商户进件流程图(来源:官方文档 4012062375)

进件流程官方说明:https://pay.weixin.qq.com/doc/v3/partner/4012062375

 

 

步骤 1:进入特约商户管理

111.  服务商平台左侧菜单:『服务商功能』 → 『特约商户管理』。

112.  页面右侧点击『添加特约商户』绿色按钮。

113.  完整路径:服务商功能 → 特约商户管理 → 添加特约商户。

▲ 操作路径:登录服务商平台 → 服务商功能 → 特约商户管理 → 添加特约商户

添加特约商户官方说明:https://pay.weixin.qq.com/doc/v3/partner/4012064824

步骤 2:选择主体类型

114.  在『主体类型』下拉框选择对应类型(个体工商户/企业/党政机关/其他组织)。

115.  注意:手动进件不支持小微商户,小微必须用扫码入驻或 API。

116.  对应资料按 4.2 准备。

▲ 操作路径:主体类型下拉框 → 选择对应类型

步骤 3:填写主体资料(详细字段)

117.  商户名称:与营业执照上『名称』完全一致(含括号、全角字符)。

118.  商户简称:用于账单显示,建议 4-16 字符。

119.  营业执照号:18 位统一社会信用代码。

120.  营业期限:按营业执照有效期填写,长期填『长期』。

121.  注册地址:营业执照上的注册地址(一字不差)。

122.  法人/经营者姓名:与营业执照一致。

123.  身份证号:法人/经营者身份证号。

124.  身份证有效期:按证件有效期填写,长期填『长期』。

125.  上传附件:营业执照照片、身份证正反面照片(JPG/PNG/单张 ≤2M)。

▲ 操作路径:填商户简称 → 填营业执照 → 填法人信息 → 上传营业执照扫描件

步骤 4:填写经营资料

126.  经营场景:勾选『线下门店』『公众号』『小程序』『网站』『APP』等(可多选)。

127.  经营类目:按主营选(如餐饮美食、零售超市、生活服务等),特殊类目需资质。

128.  门店名称:用于支付完成页显示。

129.  门店地址:实际经营地址。

130.  客服电话:对外服务的客服座机或手机。

131.  若使用小程序/公众号支付:填写要绑定的 AppID(一次性绑定,省去后续单独关联步骤)。

▲ 操作路径:填经营场景 → 填门店地址 → 上传门店照片

步骤 5:填写结算资料

132.  结算银行卡户名:必须与营业执照主体一致(小微可以是法人个人卡)。

133.  开户银行:选银行总行或分行(如『工商银行深圳福田支行』)。

134.  开户行省市:银行所在省市。

135.  银行账号:14-23 位数字。

136.  结算周期:T+1(次工作日到账)或 D+1(当天到账),按类目开放。

137.  费率:默认 0.6%,可与服务商商务洽谈降低。

▲ 操作路径:选择开户银行 → 填银行账号 → 确认户名=主体名称

步骤 6:提交审核与验证

138.  提交资料后等待审核(1-3 个工作日)。

139.  审核通过后,微信支付向子商户银行卡打一笔随机金额(0.01~1 元)。

140.  子商户登录商户平台 pay.weixin.qq.com 回填收款金额完成验证。

141.  若超级管理员为法人/经营者:可使用法人微信扫码验证,免打款。

142.  若为负责人:需法人微信扫码 或 汇款验证(二选一)。

143.  验证通过后,子商户号正式启用。

▲ 操作路径:检查资料 → 点击提交 → 等待1-3工作日 → 子商户完成打款验证

ℹ️ 提示:快捷验证:个体工商户、企业类型,若超级管理员为法人/经营者本人,无需账户验证;若为负责人,支持法人微信扫码验证或汇款验证。

 

4.4 方式二:商家扫码自助入驻(推荐批量)

服务商下载『渠道商拓展二维码』,让商家微信扫码自助提交资料。适合几十到几百个商家入驻。

官方文档:https://pay.weixin.qq.com/doc/v3/partner/4012064824

▲ 商家注册小程序流程图

步骤 1:服务商下载渠道二维码

144.  服务商平台左侧:『服务商功能』 → 『特约商户管理』。

145.  页面右上角点击『下载渠道商拓展二维码』。

146.  保存二维码图片,发给商家联系人。

147.  完整路径:服务商功能 → 特约商户管理 → 下载拓展二维码。

▲ 操作路径:登录服务商平台 → 服务商功能/特约商户管理 → 下载拓展二维码 → 保存图片

▲ 服务商平台:服务商功能 → 特约商户管理 → 下载拓展二维码

步骤 2:商家联系人扫码

148.  商家联系人(可以是店员、财务、业务负责人、法人本人)用微信扫一扫二维码。

149.  点击『启用该功能』。

150.  微信推送一条模板消息,点击进入『商家注册』小程序。

151.  点击『去提交』核实商家身份。

152.  注意:核实渠道码右上角 ID 是否与进件时一致。

▲ 操作路径:商家联系人微信扫一扫 → 扫描渠道二维码 → 进入商家入驻小程序

▲ 商家扫渠道码认证

步骤 3:选择主体类型 + 填写联系人

153.  在小程序中选择商家主体类型(小微/个体工商户/企业/党政机关/其他组织)。

154.  填写联系人姓名、手机号,接收短信验证码。

155.  点击『下一步』。

▲ 操作路径:选主体类型 → 填联系人姓名 → 填手机号 → 接收短信验证码

▲ 商家选择主体类型 + 填写联系人

步骤 4:提交商家资料

156.  按主体类型上传对应资料(参考 4.2)。

157.  上传营业执照、法人/经营者身份证正反面。

158.  上传行业资质(特殊行业,如医疗、教育等;非特殊行业可选『未涉及特殊行业,无需提交资质』)。

159.  所有材料原件拍照或复印件加盖公章,不支持截图、翻拍或镜像。

160.  点击『提交』。

▲ 操作路径:上传营业执照 → 上传法人身份证 → 上传门店照片 → 点击提交

▲ 商家提交资料示例

步骤 5:记录申请单号(备查)

161.  提交后会触发模板消息通知,生成『申请单编号』。

162.  建议记录单号,后续申请有问题可提供给微信支付工作人员协查。

▲ 操作路径:提交后生成申请单编号 → 记录单号备查

▲ 申请单编号示例

步骤 6:法人授权(重要)

163.  联系人提交资料后,需要法人本人确认授权。

164.  方式 a:联系人点击『向 XXX 发送验证邀请』,拉起微信消息列表,转发给法人。

165.  方式 b:法人微信扫码授权(已绑卡)。

166.  方式 c(仅企业):对公账户汇款验证。微信支付指定银行卡,汇 0.01~1 元,T+1 原路退回。

167.  法人授权后申请单进入『审核中』。

▲ 操作路径:法人收到通知 → 扫码进入授权页 → 确认商家资料 → 完成法人授权

⚠️ 注意:不同主体授权方式不同:①个体工商户=法人微信授权;②企业=法人微信 或 对公汇款;③小微=法人微信;④党政机关=微信客服回访;⑤其他组织=对公汇款。

 

步骤 7:商家预授权商户号(推荐)

168.  前置条件:商家申请单进入『审核中』或『待账户验证』状态。

169.  商家点击页面下方『进行预授权』,进入授权页。

170.  查看渠道商信息,勾选商户号,授权服务商提供服务。

171.  审核通过后无需再次授权。

▲ 操作路径:法人授权完成 → 进入预授权商户号页 → 设置商户号信息 → 完成预授权

ℹ️ 提示:只有商户全称与所注册商家名称一致的商户号才能进行授权。

 

4.5 方式三:进件 API 接口对接

适合有开发能力、需批量自动化入驻的服务商。

接口文档:https://pay.weixin.qq.com/doc/v3/partner/4012719997

进件 API 核心接口:

接口名称

方法

路径

用途

提交申请单

POST

/v3/applyment4sub/applyment/

服务商提交特约商户进件资料

查询申请单状态

GET

/v3/applyment4sub/applyment/{applyment_id}

通过申请单号查审核结果

通过业务申请编号查询

GET

/v3/applyment4sub/applyment/business_number/{business_code}

通过业务编号反查

修改结算账户

POST

/v3/applyment4sub/sub_merchants/{sub_mchid}/modify-settlement

协助子商户改结算账户

 

ℹ️ 提示:小微主体进件仅对进件质量符合条件的服务商开放,属于该接口下的高级能力(需单独申请)。

 

▲ 超管核对联系信息(来源:官方文档 4012062375)

▲ 微信支付商家助手公众号推送申请进展通知

▲ 超管完成账户验证(来源:官方文档 4012062375)

▲ 超管完成签约(来源:官方文档 4012062375)

4.6 各主体类型资料详细对照(截图)

以下为官方文档中各主体类型的资料填写示意:

▲ 商家主体类型与所需资料总览

小微商户、个体工商户、企业资料填写示意请参考官方文档(含截图):

官方资料示意:https://pay.weixin.qq.com/doc/v3/partner/4012064824

▲ 商家注册全流程示意

▲ 商家主体类型与所需资料(旧版参考)

4.7 关联小程序/公众号 AppID

若子商户要在小程序/公众号里发起支付,必须把 AppID 关联到子商户号上。

方式一:在服务商后台关联(推荐)

步骤 1:进入开发参数配置

172.  服务商平台左侧:『合作伙伴功能』 → 『开发参数配置』 → 『开发配置』。

173.  找到『关联 AppID』或『AppID 账号管理』。

174.  点击『新增关联』。

175.  完整路径:合作伙伴功能 → 开发参数配置 → 开发配置 → 关联 AppID。

▲ 操作路径:登录服务商平台 → 产品中心 → 开发参数配置

▲ 服务商平台:合作伙伴功能 → 开发参数配置 → 关联 AppID

▲ 关联 AppID 操作页

步骤 2:填写 AppID

176.  输入要关联的小程序/公众号 AppID(如 wx 开头 18 位)。

177.  选择对应的子商户号。

178.  AppID 类型:小程序/公众号/APP(按实际选)。

179.  点击『提交关联请求』。

▲ 操作路径:填入AppID → 点击提交 → 去小程序/公众号后台确认授权

方式二:在服务商后台添加子商户时,直接在『经营信息』里填写 AppID(一步到位)。

▲ 添加子商户时直接填写 AppID

方式三:通过 AppID 账号管理菜单单独关联(部分老版本入口)

▲ 产品中心 → AppID 账号管理 → 关联 AppID

▲ AppID 账号管理 - 关联操作

确认授权:小程序后台确认

步骤 1:小程序后台确认

180.  登录小程序/公众号后台 mp.weixin.qq.com。

181.  左侧菜单:『微信支付』。

182.  在『待关联商户号』列表中找到服务商发起的关联请求。

183.  点击『确认关联』。

184.  完整路径:mp.weixin.qq.com → 微信支付 → 待关联商户号 → 确认关联。

▲ 操作路径:登录mp.weixin.qq.com → 设置/基本配置 → 找到微信支付商户号 → 点击确认授权

▲ 小程序后台:微信支付 → 待关联商户号 → 确认

▲ 小程序后台确认关联

第五部分 第四步:配置分账接收方

『分账接收方』就是收钱的那一方。在调用分账接口之前,必须先建立『分账方 → 接收方』的关联关系。

⚠️ 注意:特别注意:每个子商户都要单独添加自己的接收方。即使服务商旗下有 b、c 两个子商户,b 要分给 c,c 也要在 b 这里被加为接收方,反过来也一样。

 

【分账接收方添加流程图】

▲ 分账接收方配置流程图(来源:微信支付官方文档 4012062375)

流程图官方说明:https://pay.weixin.qq.com/doc/v3/partner/4012062375

5.1 接收方类型

类型值

含义

适用场景

示例

MERCHANT_ID

商户号

分账给微信支付商户(如平台方、供应商公司)。最常用。

1900000110

PERSONAL_OPENID

个人 openid

分账给个人微信零钱。需用『父商户 AppID』转换出的 openid。

oUpF8xxxxxx

PERSONAL_SUB_OPENID

个人 sub_openid

分账给个人微信零钱。需用『子商户 AppID』转换出的 sub_openid。

oUpF8xxxxxx

 

5.2 子商户在商户平台手动添加(无需开发)

子商户访问入口:https://pay.weixin.qq.com/

步骤 1:子商户登录商户平台

185.  子商户打开 https://pay.weixin.qq.com/,扫码登录。

186.  左侧菜单:『交易中心』。

187.  在交易中心下找到『管理分账接收方』。

188.  完整路径:交易中心 → 管理分账接收方。

▲ 操作路径:子商户打开网址 → 超级管理员扫码登录

▲ 子商户:交易中心 → 管理分账接收方

步骤 2:添加分账接收方

189.  点击页面右上角『添加分账接收方』按钮。

190.  选择对应的服务商(即您自己)。

191.  填写接收方类型:MERCHANT_ID 或 PERSONAL_OPENID。

192.  填写接收方账号(商户号或 openid)。

193.  填写接收方名称(与营业执照主体一致或个人实名)。

194.  点击『提交』。

▲ 操作路径:交易中心 → 分账接收设置 → 添加分账接收方 → 填写信息 → 保存

步骤 3:接收方确认(仅商户类型需要)

195.  提交后,被添加的接收方商户会收到一条『同意分账』的待办。

196.  接收方登录自己的商户平台 → 交易中心 → 分账接收设置 → 点击『同意』。

197.  同意后关系才正式生效。

▲ 操作路径:接收方收到通知 → 登录商户平台 → 交易中心/分账接收设置 → 点击同意

▲ 接收方在商户平台签署分账接收协议

▲ 接收方管理列表(已同意后的状态)

5.3 通过接口添加(推荐,适合批量)

接口地址:

官方文档:https://pay.weixin.qq.com/doc/v3/partner/4012690944

接口路径:POST /v3/profitsharing/receivers/add

请求体关键字段:

字段名称

是否必填

填写说明

示例值

sub_mchid

是

子商户号(即特约商户号)

1900000109

appid

是

服务商 AppID(与 sub_mchid 有绑定关系)

wx8888888888888888

receivers.type

是

接收方类型:MERCHANT_ID/PERSONAL_OPENID/PERSONAL_SUB_OPENID

MERCHANT_ID

receivers.account

是

接收方账号(商户号或 openid)

1900000110

receivers.name

商户类型必填

接收方名称(需用微信支付公钥加密)

某某有限公司

receivers.relation_type

是

关系类型:PARTNER/PARTNER_PLATFORM/PLATFORM/DISTRIBUTOR/SUPPLIER/CUSTOM/OTHERS

PARTNER

 

代码示例见第七部分 7.4。

第六部分 第五步:设置分账比例

分账比例由『子商户』在自己商户平台设置,限制『服务商最多能从每笔订单中分走多少』。

【什么是分账比例?】子商户告诉微信支付:『我允许服务商最多从我每笔订单里分走多少百分比』。这是子商户对服务商的让渡,由子商户自己设置。

【为什么需要这一步?】保护子商户资金安全。即使服务商调用接口想分 50%,子商户只授权了 30%,微信支付会拒绝超额分账。

ℹ️ 提示:关键认知:30% 是『服务商最多能分走』的上限,不是『子商户只能拿 30%』。子商户默认自留 70%-100%。

 

【举例说明】消费者支付 100 元,子商户设置最大比例 30%:

● 服务商想分 10 元 → 成功,子商户拿 90 元

● 服务商想分 30 元 → 成功(刚好到上限),子商户拿 70 元

● 服务商想分 50 元 → 失败!报错『分账金额超出最大分账比例』

6.1 四种业务模式的入口路径(必看,避免找错地方)

微信支付有4种分账业务模式,对应4个不同的设置入口。请先确认您的业务模式,再找对应入口:

【如何判断我的业务模式?】

● 普通直连商户分账:您自己开店自己收款,没有服务商,没有子商户关系。

● 普通服务商分账(最常用):您是服务商,旗下有多个子商户(特约商户),从每笔订单抽佣。这是本手册重点讲的模式。

● 电商收付通分账:您是电商平台,二级商户在您平台开店,资金先入您账户再分给二级商户。

● 连锁品牌分账:品牌总部+门店模式,品牌主从门店订单中抽成。

业务模式

设置路径

入口

适用场景

普通直连商户分账

登陆商户平台 → 产品中心 → 分账 → 分账管理比例

https://pay.weixin.qq.com/

适用自己开店自己收款

普通服务商分账(最常用)

子商户登录商户平台 → 产品中心 → 授权的产品 → 分账授权

https://pay.weixin.qq.com/

本手册主要讲这个

电商收付通分账

登陆服务商商户平台 → 产品中心 → 我的工具箱 → 电商收付通 → 供应链分账设置

https://pay.weixin.qq.com/

电商平台二级商户

连锁品牌分账

登陆服务商平台 → 产品中心 → 合作工具箱 → 连锁品牌工具箱 → 品牌专区 → 品牌交易 → 品牌供应链分账 → 供应链分账管理

https://pay.weixin.qq.com/partner/public/home

品牌+门店连锁

 

ℹ️ 提示:本手册以最常用的『普通服务商分账』为例。其他模式路径略有不同,但操作逻辑一致。

 

6.2 普通服务商分账比例设置(详细步骤)

【操作人】子商户的超级管理员(不是服务商操作)

【时间】签署授权协议后立即可以设置

子商户访问入口:https://pay.weixin.qq.com/

步骤 1:子商户登录商户平台

198.  子商户打开 https://pay.weixin.qq.com/,扫码登录。

199.  使用『超级管理员』微信扫码(普通操作员看不到设置入口)。

200.  【如何确认是不是超管?】登录后右上角账号信息显示『超级管理员』。如果不是,需要联系超管扫码或开通权限。

▲ 操作路径:子商户打开网址 → 超级管理员扫码登录

步骤 2:进入分账授权页

201.  左侧菜单:『产品中心』。

202.  在产品中心下找到『授权的产品』(部分版本叫『我授权的产品』)。

203.  找到『分账授权』(或叫『分账』)项,点击『管理』或『产品设置』。

204.  完整路径:产品中心 → 授权的产品 → 分账授权 → 设置比例。

205.  【找不到『授权的产品』?】可能是子商户还没收到服务商的邀请(回 3.3 重新发起),或子商户开通分账产品失败(回 3.2.2 重新开通)。

▲ 操作路径:左侧产品中心 → 找到授权的产品 → 找到分账授权 → 点击管理或产品设置

步骤 3:设置最大分账比例

206.  在『允许服务商分账的最大比例』输入框中填写数字(0-30 之间,单位 %)。

207.  默认值:30%。

208.  示例:填 20 表示服务商最多从每笔订单分走 20%。

209.  【建议】直接填 30(默认值),给服务商留足灵活空间,实际分多少由代码控制。

210.  点击『保存』或『提交』。

211.  保存即时生效,无需审核。

▲ 操作路径:找到最大比例输入框 → 填数字0-30 → 建议填30 → 点击保存

▲ 子商户设置分账比例(新版入口位置)

▲ 分账比例设置页(旧版示意)

⚠️ 注意:此比例设置完成后,调用请求分账接口时,分账总金额不能超过:订单金额 × 最大分账比例。否则会报错『分账金额超出最大分账比例』(错误码 271643173)。

 

【举例】订单金额 100 元,子商户设置最大比例 30%,那么服务商调用接口时,分账总金额不能超过 100×30%=30 元。

6.3 如何提高分账比例(>30%)

如果业务需要分账比例超过 30%(如 50%、100%),按以下方式申请:

【什么场景需要 >30%?】

● 品牌统收分账:消费者付款给品牌总部,总部 100% 收款后分给门店

● 供应链分账:上游供应商从下游门店抽大头(如 60%)

● 电商平台抽佣:平台从订单抽 40% 服务费

⚠️ 注意:申请主体:由『子商户』主动申请(不是服务商代申请),因为限制是子商户对服务商的让渡。

 

6.3.1 第一步:在线申请 30% → 50%(无需邮件)

【适用场景】需要分账比例 30%-50% 之间。

【操作人】子商户超级管理员

步骤 1:子商户登录商户平台

212.  https://pay.weixin.qq.com/ → 扫码登录。

213.  产品中心 → 授权的产品 → 分账授权 → 设置比例。

214.  【前提】已经签署过分账授权协议,且当前比例为 30%。

▲ 操作路径:扫码登录 → 产品中心/授权的产品/分账授权/设置比例

步骤 2:点击『申请调高比例』

215.  在『最大分账比例』输入框右侧,点击『申请调高』链接。

216.  【如果没有此链接】说明当前商户号不支持在线申请,请走 6.3.3 客服通道。

217.  填写申请理由(50-200 字,说明为什么需要更高比例)。

218.  上传补充材料:

▲ 操作路径:找到最大比例输入框右侧 → 点击申请调高 → 填写理由50-200字 → 上传补充材料

- 营业执照(彩色扫描件或清晰照片)

- 与分账接收方的合作协议(盖章版)

- 分账业务场景说明(谁分给谁、为什么分、金额计算规则)

- 行业特殊资质(如医疗/教育等)

步骤 3:提交等待审核

219.  点击『提交申请』。

220.  审核周期:约 7 个工作日。

221.  通过后会在原设置位置看到新的比例上限(50%)。

222.  调用接口时按新上限校验。

223.  【审核期间怎么办?】仍按 30% 上限调用接口,通过后再调高。

▲ 操作路径:点击提交申请 → 等待约7工作日 → 通过后看到新比例上限50%

6.3.2 第二步:邮件申请 50% → 100%(需邮件)

如果 50% 仍无法满足业务需求(如品牌统收分账给门店场景需要 100%),需要单独邮件申请:

【100% 分账含义】消费者付款 100 元,服务商/品牌主可以分走全部 100 元,再自己分配给各接收方。

项目

内容

说明

申请邮箱

WeChatPayFunds@tencent.com

微信支付业务专用邮箱

邮件主题

【微信支付品牌高比例分账申请】+ 服务商公司全称

必须按此格式

申请方

由服务商提交

服务商代商户提交材料

比例范围

50% 和 100% 需分别申请

100% 审核更严格

额外材料(100%)

资金监管协议

证明资金安全监管机制

审核周期

7-15 个工作日

比 50% 申请更久

 

ℹ️ 提示:申请参考文档:分账资料包-第四部分 https://docs.qq.com/doc/DR0lVQkRXdHNlbHJx

 

⚠️ 注意:重要提示:现有及后续新增的收款方,只要占比超过 30%,均需单独通过邮件申请并上传合同协议。意味着多个接收方都要重新签订合同。

 

6.3.3 客服咨询通道(无法找到入口时)

如果在商户平台找不到『申请调高比例』入口(部分老商户号未开放在线申请):

步骤 1:拨打微信支付客服

224.  电话:95017。

225.  转接『商户服务』或按语音提示选择『分账业务』。

226.  说明:『我是特约商户 XXX,需要申请提高分账比例,请告知流程』。

227.  【工作时间】9:00-22:00(全年无休)

▲ 操作路径:拨打95017 → 转接商户服务 → 说明申请提高分账比例

步骤 2:提供商户信息

228.  商户号:16xxxxxxxx 或 19xxxxxxxx(10 位数字)。

229.  商户全称:营业执照上的公司全称。

230.  法人姓名:法人身份证姓名。

231.  客服会通过邮箱发送申请指引。

232.  【提示】客服不能直接帮你调高比例,只能告诉你申请流程。真正的审核还是走邮件。

▲ 操作路径:提供商户号 → 提供商户全称 → 提供法人姓名 → 客服发送申请指引邮件

6.4 连锁品牌分账比例设置(品牌供应链分账专属)

如果您的业务是『品牌+门店』模式,需要走品牌供应链分账,路径略有不同:

【什么是连锁品牌分账?】品牌总部(如某奶茶品牌)有多家门店,消费者在门店付款后,资金先进入品牌总账户,再由品牌总部按约定比例分给各门店。

服务商访问入口:https://pay.weixin.qq.com/partner/public/home

步骤 1:服务商开通连锁品牌工具箱

233.  服务商登录服务商平台 https://pay.weixin.qq.com/partner/public/home。

234.  左侧:『合作工具箱』 → 找到『连锁品牌工具箱』 → 点击『开通』。

235.  按提示完成品牌申请、邀请品牌总部授权、品牌门店管理等操作。

236.  【前提】服务商已开通分账产品(第三部分),且有至少一个品牌商户授权。

▲ 操作路径:服务商登录服务商平台 → 左侧合作工具箱 → 找到连锁品牌工具箱 → 点击开通

▲ 服务商平台:合作工具箱 → 连锁品牌工具箱 → 开通

步骤 2:品牌主设置分账比例

237.  品牌主登录商户平台 https://pay.weixin.qq.com/。

238.  进入『品牌专区』 → 『品牌交易』 → 『品牌供应链分账』 → 『供应链分账管理』。

239.  在此页面可调整品牌主与门店之间的分账比例。

240.  基础分账比例默认 30%,可在线申请调高到 50%(品牌主申请后品牌主和门店均适用)。

▲ 操作路径:品牌主登录商户平台 → 进入品牌专区 → 品牌交易/品牌供应链分账 → 供应链分账管理/调整比例

▲ 品牌供应链分账关系示意(品牌/门店/合作伙伴/导购)

步骤 3:50% → 100% 品牌统收分账(特殊场景)

241.  若品牌主有营销场景(如品牌营销售券,再分账给门店),可申请『品牌统收分账』。

242.  申请入口:品牌专区 → 品牌交易 → 品牌供应链分账 → 供应链分账管理 → 申请。

243.  开通后品牌主可直接 100% 分账给门店。

244.  若 100% 仍不够,邮件申请:WeChatPayFunds@tencent.com,主题『微信支付品牌高比例分账申请 + 服务商公司全称』。

▲ 操作路径:品牌专区/品牌交易/品牌供应链分账 → 供应链分账管理/申请 → 申请品牌统收分账100%

▲ 品牌统收分账申请 100% 入口

6.5 分账比例的核心规则(必须知道)

【非技术人员必读】以下 8 条规则决定您的分账业务能否正常运转:

规则

说明

重要程度

按金额分账

微信支付只接受按金额分账,不接受按比例分账。比例的作用是限制『最多能分多少』,不是分账方式。

重要

默认值 30%

所有分账模式初始默认最大比例为 30%。

通用

单笔订单校验

分账总金额 ≤ 订单金额 × 最大分账比例。

接口调用时校验

冻结期 30/180 天

普通服务商分账订单冻结 30 天,电商/品牌分账订单冻结 180 天。超时未分则自动解冻给分账方。

时效

多次分账

同一笔订单最多分账 50 次,每次最多分给 50 个接收方。

频次

剩余资金

服务商只需传入分给他方的账号和金额,订单剩余未分账资金平台默认都属于分账方,处理分账时直接解冻。

结算

分账失败处理

接收方账户不可用导致的失败,资金自动转回分账方。

异常

接收方资金提现

分账接收方收到资金后,需要手动提现到银行账户(不会自动提现)。

到账

 

【规则1详解】按金额分账,不是按比例分账。意思是:调用接口时,必须传具体金额(如 10 元=1000 分),不能传比例(如 10%)。

【规则4详解】冻结期:消费者付款后,资金先被冻结。普通服务商分账订单冻结 30 天,电商/品牌分账订单冻结 180 天。在冻结期内必须完成分账,否则过期自动解冻给子商户,无法再分。

【规则6详解】剩余资金:如订单 100 元,服务商分走 10 元,剩余 90 元会自动解冻给子商户,不需要再调用接口。

❌ 重要:下单时未传 profit_sharing=true 的订单,不能分账。下单后不能补传分账标识,请默认开启。

 

第七部分 第六步:代码开发接入

前置页面操作全部完成后(服务商开通分账 + 子商户授权 + 添加接收方 + 设置比例),即可进入代码开发阶段。

【非技术人员必读】从这一部分开始,需要后端开发人员接手。但请非技术人员也读一下 7.1 和 7.2,了解需要给开发同事提供哪些参数和资料。

【交接清单】非技术人员在交接给开发同事前,请准备好以下资料:

● 1) 服务商商户号(10 位数字,登录服务商平台后右上角可见)

● 2) 服务商 AppID(在公众平台 → 设置 → 基本配置 可见)

● 3) 商户 API 证书序列号 + apiclient_key.pem 私钥文件(在服务商平台 → 账户中心 → API 安全 下载)

● 4) APIv3 密钥(自己设置的 32 位字符串,请妥善保存)

● 5) 子商户号(特约商户的 10 位商户号)

● 6) 子商户 AppID(如果用子商户自己的小程序/公众号支付)

● 7) 微信支付平台证书(可让开发同事通过接口自动获取,或手动下载)

✓ 建议:把这些资料交给后端开发同事,对方就能根据本部分 7.4-7.6 的代码完成对接。

 

7.1 开发前准备清单

开发前请确认以下资料已准备齐全:

序号

参数

在哪里获取

示例值

1

服务商商户号 mch_id

10 位数字,登录服务商平台首页可见

1900000108

2

服务商 AppID

在 mp.weixin.qq.com → 设置 → 开发设置 可见

wx8888888888888888

3

子商户号 sub_mchid

10 位数字,在服务商平台 → 特约商户管理 可见

1900000109

4

子商户 AppID

子商户的小程序/公众号 AppID(如使用)

wx8888888888888889

5

APIv3 密钥

32 位字符串,在服务商平台 → 账户中心 → API 安全 → 设置 APIv3 密钥

abcdefghijklmnopqrstuvwxyz123456

6

商户证书序列号

40 位十六进制字符串,下载 API 证书后可见

01234ABCD56789...

7

商户私钥文件

下载 API 证书后获得的 apiclient_key.pem 文件

C:/cert/apiclient_key.pem

8

微信支付平台证书

可让 SDK 自动下载(推荐),或手动从接口获取

自动管理

9

回调 URL

您的服务器公网地址 + /api/wxpay/profit-sharing/notify,必须 https

https://your-domain.com/api/wxpay/profit-sharing/notify

 

ℹ️ 提示:【如何下载 API 证书】登录服务商平台 → 账户中心 → API 安全 → 申请 API 证书 → 下载 apiclient_key.pfx 和 apiclient_key.pem 文件。请妥善保存,不要泄露。

 

⚠️ 注意:【如何设置 APIv3 密钥】登录服务商平台 → 账户中心 → API 安全 → 设置 APIv3 密钥 → 输入 32 位字符串(自己设置,含字母数字,区分大小写)。请保存好,无法找回只能重置。

 

【代码开发业务流程图】

▲ 分账全流程业务时序图(来源:微信支付官方文档 4012062375)

流程图官方说明:https://pay.weixin.qq.com/doc/v3/partner/4012062375

上图展示了完整的分账时序:下单→支付→添加接收方→请求分账→查询结果→完结分账→(如退款)分账回退。

7.1.1 如何申请商户API证书(10步截图)

商户API证书是调用微信支付所有接口的必备条件。以下为官方文档的10步申请流程截图:

申请证书官方文档:https://pay.weixin.qq.com/doc/v3/partner/4013058924

【操作路径】服务商平台 → 账户中心 → 账户设置 → API 安全 → 商户API证书 → 申请证书

步骤 1-2:登录商户平台,下载证书工具

245.  登录 pay.weixin.qq.com → 账户中心 → API 安全 → 点击『申请证书』

246.  在弹出窗口内点击『下载证书工具』按钮

247.  复制商户号和商户名称

▲ 步骤一:登录商户平台,点击账户中心-API安全-申请证书

▲ 步骤二:下载证书工具,复制商户号和商户名称

步骤 3-4:安装证书工具,填写商户信息

248.  安装证书工具并打开

249.  选择证书存储路径

250.  点击『申请证书』

251.  将复制的商户号和商户名称粘贴到证书工具中

252.  点击『下一步』



 

▲ 步骤三:安装证书工具,选择存储路径,点击申请证书

▲ 步骤四:将商户号和商户名称粘贴到证书工具

步骤 5-6:复制证书请求串,粘贴到商户平台

253.  在证书工具中,复制证书请求串

254.  回到商户平台

255.  将证书工具中复制的请求串粘贴到商户平台文本框中

256.  点击『下一步』

▲ 步骤五:在证书工具中复制证书请求串

▲ 步骤六:将请求串粘贴到商户平台文本框

步骤 7-8:安全验证,复制证书串

257.  安全验证:输入操作密码 + 短信验证码

258.  点击『确定』

259.  在商户平台『复制证书串』环节,点击『复制证书串』

▲ 步骤七:安全验证,输入操作密码和短信验证码

▲ 步骤八:在商户平台复制证书串

步骤 9-10:粘贴证书串到工具,完成申请

260.  将商户平台复制的证书串粘贴到证书工具中

261.  点击『下一步』

262.  申请商户API证书成功

263.  点击『查看证书文件夹』,查看已生成的证书文件

▲ 步骤九:将证书串粘贴到证书工具

▲ 步骤十:申请成功,查看证书文件夹

【证书文件说明】生成后的证书文件为压缩包,解压后有 4 个文件:

▲ 证书文件解压后的4个文件:p12格式、pem格式、私钥pem格式、说明文件

● apiclient_cert.p12:p12 格式证书(APIv2 用)

● apiclient_cert.pem:pem 格式证书

● apiclient_key.pem:证书私钥(APIv3 签名用,最重要!)

● README.md:说明文件

❌ 重要:请将生成的证书文件转交给技术人员,由技术人员将证书部署到服务器上。私钥文件 apiclient_key.pem 只能通过证书工具导出,若丢失无法找回,只能作废重新申请。

 

证书申请完整官方文档:https://pay.weixin.qq.com/doc/v3/partner/4013058924

7.2 业务流程

完整分账流程(来自微信支付官方业务流程图):

264.  下单时传 profit_sharing=true(标识为分账订单)。

265.  用户支付成功,资金进入『冻结池』(默认冻结期 30 天,电商分账 180 天)。

266.  调用『添加分账接收方』接口建立接收方关系(一次性,已添加可跳过)。

267.  调用『请求分账』接口发起分账(接口异步受理,返回 order_id 不代表成功)。

268.  调用『查询分账结果』接口轮询/查询分账最终结果。

269.  (可选)调用『完结分账』接口把剩余资金解冻给分账方。

270.  (如退款)先调用『请求分账回退』把已分资金回退,再处理退款。

7.3 接口清单

接口名称

方法

路径

用途

添加分账接收方

POST

/v3/profitsharing/receivers/add

建立分账关系

删除分账接收方

POST

/v3/profitsharing/receivers/delete

解除分账关系

请求分账

POST

/v3/profitsharing/orders

发起分账,异步受理

查询分账结果

GET

/v3/profitsharing/orders/{out_order_no}

查询分账最终结果

完结分账

POST

/v3/profitsharing/orders/{out_order_no}/finish

解冻剩余资金给分账方

查询订单剩余待分金额

GET

/v3/profitsharing/transactions/{transaction_id}/amounts

查可分余额

请求分账回退

POST

/v3/profitsharing/returns

已分资金回退

查询分账回退结果

GET

/v3/profitsharing/returns/{out_return_no}

查回退结果

分账动账通知

POST(回调)

商户配置的 notify_url

分账/回退结果异步通知

 

7.4 完整 Java 代码示例

以下代码基于微信支付 APIv3 + Apache HttpClient,可直接放进 Spring Boot 项目。参数需替换为您自己的商户号、证书路径等。

ℹ️ 提示:代码来源:微信支付官方示例 https://pay.weixin.qq.com/doc/v3/partner/4015870957,已整理为可执行片段。

 

▲ 分账产品模型图:分账发起方 → 分账方 → 接收方(来源:官方文档 4015870957)

▲ 代码对接全流程时序图(来源:微信支付官方文档 4012062375)

7.4.1 创建 HttpClient(带签名验证)

import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;

import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;

import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;

import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;

import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;

import org.apache.http.impl.client.CloseableHttpClient;

 

import java.io.FileInputStream;

import java.security.PrivateKey;

 

public class WxPayClient {

// ===== 替换成自己的参数 =====

private static final String MCH_ID = "1900000108"; // 服务商商户号

private static final String MCH_SERIAL = "xxxxxxxxxxxx"; // 商户API证书序列号

private static final String API_V3_KEY = "xxxxxxxxxxxx"; // APIv3 密钥

private static final String KEY_PATH = "C:/cert/apiclient_key.pem"; // 商户私钥文件

 

private static CloseableHttpClient httpClient;

 

static {

try {

PrivateKey privateKey = PemUtil.loadPrivateKey(

new FileInputStream(KEY_PATH));

httpClient = WechatPayHttpClientBuilder.create()

.withMerchant(MCH_ID, MCH_SERIAL, privateKey)

.withValidator(new WechatPay2Validator(

// 自动获取并校验微信支付平台证书

CertManagerSingleton.getCertManager(API_V3_KEY).getVerifier()))

.build();

} catch (Exception e) {

throw new RuntimeException("初始化微信支付 HttpClient 失败", e);

}

}

 

public static CloseableHttpClient getClient() { return httpClient; }

}

 

7.4.2 添加分账接收方

添加接收方接口文档:https://pay.weixin.qq.com/doc/v3/partner/4012477758

▲ 接收方添加成功后可在商户平台查看(来源:官方文档 4013058924)

import org.apache.http.client.methods.HttpPost;

import org.apache.http.entity.StringEntity;

import org.apache.http.impl.client.CloseableHttpResponse;

import org.apache.http.util.EntityUtils;

 

public class AddReceiver {

/**

* 添加分账接收方

* 接口:POST /v3/profitsharing/receivers/add

* 文档:https://pay.weixin.qq.com/doc/v3/partner/4012690944

*/

public static String addReceiver(String subMchid, String receiverAccount) throws Exception {

String url = "https://api.mch.weixin.qq.com/v3/profitsharing/receivers/add";

 

// 请求体 JSON

String reqdata = "{"

+ "\"sub_mchid\":\"" + subMchid + "\","

+ "\"appid\":\"wx8888888888888888\"," // 服务商 AppID

+ "\"receivers\":[{"

+ "\"type\":\"MERCHANT_ID\"," // 接收方类型:商户号

+ "\"account\":\"" + receiverAccount + "\"," // 接收方商户号

+ "\"name\":\"某某合作公司\"," // 接收方名称(需用微信支付公钥加密)

+ "\"relation_type\":\"PARTNER\" // 关系类型:合作伙伴

+ "}]"

+ "}";

 

HttpPost httpPost = new HttpPost(url);

httpPost.addHeader("Accept", "application/json");

httpPost.addHeader("Content-Type", "application/json");

// Wechatpay-Serial 头:填微信支付公钥ID或平台证书序列号

httpPost.addHeader("Wechatpay-Serial", "PUB_KEY_ID_xxx");

httpPost.setEntity(new StringEntity(reqdata, "utf-8"));

 

try (CloseableHttpResponse response = WxPayClient.getClient().execute(httpPost)) {

int code = response.getStatusLine().getStatusCode();

String body = EntityUtils.toString(response.getEntity());

if (code == 200) {

System.out.println("添加分账接收方成功:" + body);

return body;

} else {

throw new RuntimeException("添加失败:" + code + " " + body);

}

}

}

}

 

7.4.3 请求分账

请求分账接口文档:https://pay.weixin.qq.com/doc/v3/partner/4012691594

▲ 分账产品模型图:请求分账对应图中『分账发起方 → 接收方』的资金流向(来源:4015870957)

public class CreateProfitSharing {

/**

* 请求分账

* 接口:POST /v3/profitsharing/orders

* 文档:https://pay.weixin.qq.com/doc/v3/partner/4012692975

*/

public static String create(String subMchid, String transactionId, String outOrderNo,

String receiverAccount, int amount) throws Exception {

String url = "https://api.mch.weixin.qq.com/v3/profitsharing/orders";

 

// 关键:分账金额单位是「分」,100 = 1 元

// receivers.amount 总和 ≤ 订单金额 × 子商户设置的最大分账比例

String reqdata = "{"

+ "\"sub_mchid\":\"" + subMchid + "\","

+ "\"appid\":\"wx8888888888888888\","

+ "\"transaction_id\":\"" + transactionId + "\"," // 微信支付订单号

+ "\"out_order_no\":\"" + outOrderNo + "\"," // 商户分账单号,商户系统内唯一

+ "\"receivers\":[{"

+ "\"type\":\"MERCHANT_ID\","

+ "\"account\":\"" + receiverAccount + "\"," // 接收方商户号

+ "\"amount\":" + amount + "," // 分账金额,单位:分

+ "\"description\":\"订单" + transactionId + "分账给" + receiverAccount + "\""

+ "}],"

+ "\"unfreeze_unsplit\":false" // false=不解冻剩余;true=完结分账

+ "}";

 

HttpPost httpPost = new HttpPost(url);

httpPost.addHeader("Accept", "application/json");

httpPost.addHeader("Content-Type", "application/json");

httpPost.addHeader("Wechatpay-Serial", "PUB_KEY_ID_xxx");

httpPost.setEntity(new StringEntity(reqdata, "utf-8"));

 

try (CloseableHttpResponse response = WxPayClient.getClient().execute(httpPost)) {

int code = response.getStatusLine().getStatusCode();

String body = EntityUtils.toString(response.getEntity());

// 202 = 受理成功(不是最终分账结果!需后续查询)

if (code == 200 || code == 202) {

System.out.println("分账受理成功:" + body);

return body;

} else {

throw new RuntimeException("分账失败:" + code + " " + body);

}

}

}

}

 

7.6.7 查询分账结果

查询分账结果接口文档:https://pay.weixin.qq.com/doc/v3/partner/4012477734

▲ 查询分账结果对应流程图中『查询』环节(来源:4012062375)

import org.apache.http.client.methods.HttpGet;

 

public class QueryProfitSharing {

/**

* 查询分账结果

* 接口:GET /v3/profitsharing/orders/{out_order_no}?sub_mchid=xxx

* 文档:https://pay.weixin.qq.com/doc/v3/partner/4012467002

*/

public static String query(String subMchid, String outOrderNo) throws Exception {

String url = "https://api.mch.weixin.qq.com/v3/profitsharing/orders/"

+ outOrderNo + "?sub_mchid=" + subMchid;

 

HttpGet httpGet = new HttpGet(url);

httpGet.addHeader("Accept", "application/json");

 

try (CloseableHttpResponse response = WxPayClient.getClient().execute(httpGet)) {

int code = response.getStatusLine().getStatusCode();

String body = EntityUtils.toString(response.getEntity());

if (code == 200) {

// 解析 receivers[i].result 字段:

// SUCCESS = 分账成功

// CLOSED = 已关闭(看 receivers[i].fail_reason)

// PENDING = 处理中

System.out.println("查询分账结果:" + body);

return body;

} else {

throw new RuntimeException("查询失败:" + code + " " + body);

}

}

}

}

 

7.6.8 完结分账(解冻剩余资金)

完结分账接口文档:https://pay.weixin.qq.com/doc/v3/partner/4012477745

▲ 完结分账:剩余资金自动解冻给分账方(来源:4015870957)

public class FinishProfitSharing {

/**

* 完结分账

* 接口:POST /v3/profitsharing/orders/{out_order_no}/finish

* 文档:https://pay.weixin.qq.com/doc/v3/partner/4012467016

* 用途:把剩余未分资金一次性解冻给分账方(子商户)

*/

public static String finish(String subMchid, String transactionId,

String outOrderNo) throws Exception {

String url = "https://api.mch.weixin.qq.com/v3/profitsharing/orders/"

+ outOrderNo + "/finish";

 

String reqdata = "{"

+ "\"sub_mchid\":\"" + subMchid + "\","

+ "\"transaction_id\":\"" + transactionId + "\"," // 微信支付订单号

+ "\"out_order_no\":\"" + outOrderNo + "\"," // 完结用的分账单号,不能与请求分账时相同

+ "\"sub_appid\":\"wx8888888888888889\"" // 子商户 AppID(如有)

+ "}";

 

HttpPost httpPost = new HttpPost(url);

httpPost.addHeader("Accept", "application/json");

httpPost.addHeader("Content-Type", "application/json");

httpPost.addHeader("Wechatpay-Serial", "PUB_KEY_ID_xxx");

httpPost.setEntity(new StringEntity(reqdata, "utf-8"));

 

try (CloseableHttpResponse response = WxPayClient.getClient().execute(httpPost)) {

int code = response.getStatusLine().getStatusCode();

String body = EntityUtils.toString(response.getEntity());

if (code == 200 || code == 202) {

System.out.println("完结分账受理成功:" + body);

return body;

} else {

throw new RuntimeException("完结失败:" + code + " " + body);

}

}

}

}

 

7.4.6 下单时传分账标识(关键步骤)

下单时必须在 settle_info 里传 profit_sharing=true,否则后续无法分账。示例(JSAPI 统一下单):

// 统一下单接口:POST /v3/pay/transactions/jsapi

// 关键字段:settle_info.profit_sharing = true

String reqdata = "{"

+ "\"sub_mchid\":\"1900000109\"," // 子商户号

+ "\"appid\":\"wx8888888888888888\"," // 服务商 AppID

+ "\"sub_appid\":\"wx8888888888888889\"," // 子商户 AppID(如有)

+ "\"out_trade_no\":\"ORDER20260914001\","

+ "\"description\":\"测试商品\","

+ "\"amount\":{\"total\":1000,\"currency\":\"CNY\"}," // 1000 分 = 10 元

+ "\"payer\":{\"openid\":\"oUpF8xxxxxx\"}," // 用户 openid(子商户 AppID 下的)

+ "\"settle_info\":{\"profit_sharing\":true}" // ★ 关键:分账标识

+ "}";

 

❌ 重要:profit_sharing=true 必须在下单时传,已支付订单不能补传。如果忘了,该订单无法分账。

 

7.5 常见错误码速查

来源:https://pay.weixin.qq.com/doc/v3/merchant/4020807103

错误码

含义

排查步骤

271643173

超过分账限制的最大比例

子商户在商户平台 → 产品中心 → 授权的产品 → 分账授权 中提高比例,或减少本次分账金额。

271645722

无分账权限

1) 检查服务商是否开通分账;2) 检查子商户是否已授权;3) 检查请求参数是否用了普通商户文档(应该用服务商文档)。

271643175

不是分账订单

下单时未传 settle_info.profit_sharing=true,或 transaction_id 写错。

271643176

剩余可分金额为 0

1) 已全额退款;2) 已被解冻;3) 已分完。调用『查询订单剩余待分金额』接口确认。

271644678

分账关系非法

未先调用『添加分账接收方』建立关系,或商户接收方未『同意分账』。

271644831

订单未结算,交易投诉冻结

登录商户平台 → 交易中心/消费者投诉 2.0,处理投诉后等资金解冻。

268588428

分账单已完结

已经调过完结分账(unfreeze_unsplit=true),不能再次分账。

268588427

分账单已逾期解冻

超过 180 天冻结期,剩余资金已自动解冻给分账方,无法再分。

271644182

回退方没有开通分账回退功能

接收方商户登录商户平台 → 交易中心 → 分账 → 分账接收设置 → 开启『同意分账回退』。

271644737

微信用户没有实名

接收方是个人零钱时,用户未实名认证。请用户在微信 → 我 → 服务 → 右上角三点 → 实名认证。

 

7.6 完整工程实践(Spring Boot 项目结构)

本节给出一个可直接放进 Spring Boot 项目的完整分账对接方案,包含:

● Maven 依赖(pom.xml 片段)

● application.yml 配置参数

● WxPayConfig 配置类(自动初始化 HttpClient)

● WxPayUtil 工具类(V3 请求 + 签名 + 验签)

● ProfitSharingService 完整业务 Service(含所有分账接口)

● ProfitSharingController 回调接收 Controller(验签 + 解密 + 业务处理)

● SensitiveDataUtil 敏感字段加密(name 字段必须用微信支付公钥加密)

● 定时任务 QueryScheduler(异步查询分账最终结果)

▲ 完整工程实践对应业务时序图(来源:微信支付官方文档 4012062375)

7.6.1 Maven 依赖(pom.xml)

<!-- ============ 微信支付 APIv3 官方 SDK ============ -->

<dependency>

<groupId>com.github.wechatpay-apiv3</groupId>

<artifactId>wechatpay-java</artifactId>

<version>0.2.14</version>

</dependency>

 

<!-- 兼容老版本 Apache HttpClient 风格(如果用 wechatpay-java 就不需要下面这个) -->

<dependency>

<groupId>com.github.wechatpay-apiv3</groupId>

<artifactId>wechatpay-apache-httpclient</artifactId>

<version>0.4.18</version>

</dependency>

 

<!-- JSON 处理 -->

<dependency>

<groupId>com.google.code.gson</groupId>

<artifactId>gson</artifactId>

<version>2.10.1</version>

</dependency>

 

<!-- Spring Boot Web(接收回调) -->

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-web</artifactId>

</dependency>

 

<!-- Lombok(可选) -->

<dependency>

<groupId>org.projectlombok</groupId>

<artifactId>lombok</artifactId>

<optional>true</optional>

</dependency>

 

7.6.2 application.yml 配置

# ============ 微信支付服务商参数 ============

wxpay:

# 服务商商户号

mch-id: "1900000108"

# 服务商 AppID

appid: "wx8888888888888888"

# 商户 API 证书序列号(40 位十六进制)

mch-serial: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

# APIv3 密钥(32 位字符串,自己设置的)

api-v3-key: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

# 商户私钥文件路径(apiclient_key.pem)

key-path: "C:/cert/apiclient_key.pem"

# 微信支付公钥 ID(2025 年后新商户号用公钥模式)

wechatpay-serial: "PUB_KEY_ID_xxxxxxxx"

# 回调地址(必须 https,公网可访问)

notify-url: "https://your-domain.com/api/wxpay/profit-sharing/notify"

 

# 子商户默认值(可被订单覆盖)

sub:

default-sub-mchid: "1900000109"

default-sub-appid: "wx8888888888888889"

 

ℹ️ 提示:代码使用 wechatpay-java 官方 SDK(推荐方式),底层自动处理签名、证书、平台证书自动更新。比手写 HttpClient 更安全、更省心。

 

7.6.3 WxPayConfig 配置类(自动初始化 SDK)

package com.example.wxpay.config;

 

import com.wechat.pay.java.core.Config;

import com.wechat.pay.java.core.RSAAutoUpdateConfig;

import com.wechat.pay.java.service.profitsharing.ProfitSharingService;

import com.wechat.pay.java.service.profitsharing.ProfitSharingService.Builder;

import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Value;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

 

@Slf4j

@Configuration

public class WxPayConfig {

 

@Value("${wxpay.mch-id}") private String mchId;

@Value("${wxpay.appid}") private String appid;

@Value("${wxpay.mch-serial}") private String mchSerial;

@Value("${wxpay.api-v3-key}") private String apiV3Key;

@Value("${wxpay.key-path}") private String keyPath;

@Value("${wxpay.wechatpay-serial}") private String wechatpaySerial;

 

/**

* 自动更新微信支付平台证书的 Config

* SDK 内部会定时下载并缓存平台证书,无需手动管理

*/

@Bean

public Config wxPayConfig() {

log.info("初始化微信支付 Config,商户号={}, 证书序列号={}", mchId, mchSerial);

return new RSAAutoUpdateConfig.Builder()

.merchantId(mchId)

.privateKeyFromPath(keyPath)

.merchantSerialNumber(mchSerial)

.apiV3Key(apiV3Key)

.build();

}

 

/**

* 分账 Service(官方 SDK 封装)

*/

@Bean

public ProfitSharingService profitSharingService(Config wxPayConfig) {

return new ProfitSharingService.Builder().config(wxPayConfig).build();

}

 

public String getMchId() { return mchId; }

public String getAppid() { return appid; }

public String getWechatpaySerial() { return wechatpaySerial; }

}

 

7.6.4 下单接口(必须传 profit_sharing=true)

▲ 下单传分账标识对应流程图中的『下单』环节(来源:4012062375)

下单使用服务商模式 JSAPI 统一下单:POST /v3/pay/partner/transactions/jsapi

❌ 重要:下单时必须传 settle_info.profit_sharing=true,否则后续无法分账。已支付订单不能补传。

 

package com.example.wxpay.service;

 

import com.google.gson.Gson;

import com.google.gson.GsonBuilder;

import com.wechat.pay.java.core.Config;

import com.wechat.pay.java.service.payments.jsapi.JsapiService;

import com.wechat.pay.java.service.payments.jsapi.model.*;

import com.wechat.pay.java.service.payments.model.Payer;

import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.beans.factory.annotation.Value;

import org.springframework.stereotype.Service;

 

@Slf4j

@Service

public class PartnerPayService {

 

@Autowired private Config wxPayConfig;

@Value("${wxpay.mch-id}") private String mchId;

@Value("${wxpay.appid}") private String appid;

 

/**

* 服务商模式 JSAPI 下单(带分账标识)

* 接口:POST /v3/pay/partner/transactions/jsapi

* 文档:https://pay.weixin.qq.com/doc/v3/partner/4012741851

*/

public PrepayWithRequestJsapiResponse createOrderWithProfitSharing(

String subMchid, String subAppid, String openid,

String outTradeNo, String description, int totalAmount) {

 

JsapiService jsapiService = new JsapiService.Builder().config(wxPayConfig).build();

 

PrepayWithRequestJsapiRequest request = new PrepayWithRequestJsapiRequest();

request.setSubMchid(subMchid);

request.setSubAppid(subAppid);

request.setAppid(appid);

request.setMchid(mchId);

request.setDescription(description);

request.setOutTradeNo(outTradeNo);

 

Amount amount = new Amount();

amount.setTotal(totalAmount); // 单位:分,1000=10 元

amount.setCurrency("CNY");

request.setAmount(amount);

 

Payer payer = new Payer();

payer.setSubOpenid(openid); // 子商户 AppID 下的 openid

request.setPayer(payer);

 

// ★ 关键:分账标识,必须传 true

SettleInfo settleInfo = new SettleInfo();

settleInfo.setProfitSharing(true);

request.setSettleInfo(settleInfo);

 

log.info("下单参数:{}", new Gson().toJson(request));

return jsapiService.prepayWithRequestJsapi(request);

}

}

 

7.6.5 添加分账接收方(含敏感字段加密)

添加接收方接口文档:https://pay.weixin.qq.com/doc/v3/partner/4012477758

▲ 添加接收方对应产品模型中『建立接收方列表』(来源:4015870957)

name 字段是个人姓名或商户全称,必须用微信支付公钥加密后传输。

package com.example.wxpay.service;

 

import com.wechat.pay.java.core.Config;

import com.wechat.pay.java.service.profitsharing.ProfitSharingService;

import com.wechat.pay.java.service.profitsharing.model.*;

import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.beans.factory.annotation.Value;

import org.springframework.stereotype.Service;

 

@Slf4j

@Service

public class ProfitSharingService {

 

@Autowired private ProfitSharingService profitSharingService;

@Autowired private Config wxPayConfig;

@Value("${wxpay.appid}") private String appid;

 

/**

* 添加分账接收方

* 接口:POST /v3/profitsharing/receivers/add

* 文档:https://pay.weixin.qq.com/doc/v3/partner/4012690944

*/

public AddReceiverResponse addReceiver(String subMchid, String receiverType,

String receiverAccount, String receiverName,

String relationType) {

 

AddReceiverRequest request = new AddReceiverRequest();

request.setSubMchid(subMchid);

request.setAppid(appid);

 

ReceiverDetail receiver = new ReceiverDetail();

receiver.setType(receiverType); // MERCHANT_ID / PERSONAL_OPENID / PERSONAL_SUB_OPENID

receiver.setAccount(receiverAccount);

 

// ★ name 字段处理:

// - MERCHANT_ID 类型必填商户全称(小微/个体户填开户人姓名)

// - 个人类型选填(如填,必须用微信支付公钥加密,且校验实名一致)

if (receiverName != null && !receiverName.isEmpty()) {

// SDK 会自动用微信支付公钥加密敏感字段

receiver.setName(receiverName);

}

receiver.setRelationType(relationType); // PARTNER / DISTRIBUTOR / SUPPLIER / OTHERS 等

 

request.setReceivers(java.util.Collections.singletonList(receiver));

 

log.info("添加分账接收方:subMchid={}, account={}", subMchid, receiverAccount);

return profitSharingService.addReceiver(request);

}

 

/**

* 删除分账接收方

* 接口:POST /v3/profitsharing/receivers/delete

*/

public DeleteReceiverResponse deleteReceiver(String subMchid,

String receiverType, String receiverAccount) {

DeleteReceiverRequest request = new DeleteReceiverRequest();

request.setSubMchid(subMchid);

request.setAppid(appid);

 

ReceiverDetail receiver = new ReceiverDetail();

receiver.setType(receiverType);

receiver.setAccount(receiverAccount);

request.setReceivers(java.util.Collections.singletonList(receiver));

 

log.info("删除分账接收方:account={}", receiverAccount);

return profitSharingService.deleteReceiver(request);

}

}

 

7.6.6 请求分账(核心接口)

请求分账接口文档:https://pay.weixin.qq.com/doc/v3/partner/4012691594

▲ 分账产品模型图:请求分账是整个流程的核心(来源:4015870957)

/**

* 请求分账

* 接口:POST /v3/profitsharing/orders

* 文档:https://pay.weixin.qq.com/doc/v3/partner/4012692975

*

* 关键规则:

* 1. 分账金额单位是「分」,100=1 元

* 2. receivers.amount 总和 ≤ 订单金额 × 子商户设置的最大分账比例

* 3. 接口是异步受理,返回 order_id 不代表分账成功,需后续查询

* 4. 同一笔订单最多分账 50 次,每次最多 50 个接收方

* 5. unfreeze_unsplit=true 表示本次是最后一次分账并解冻剩余资金

*/

public CreateOrderResponse createProfitSharing(

String subMchid, String transactionId, String outOrderNo,

List<Receiver> receivers, boolean unfreezeUnsplit) {

 

CreateOrderRequest request = new CreateOrderRequest();

request.setSubMchid(subMchid);

request.setAppid(appid);

request.setTransactionId(transactionId); // 微信支付订单号

request.setOutOrderNo(outOrderNo); // 商户分账单号,商户系统内唯一,6-64 位

request.setReceivers(receivers);

request.setUnfreezeUnsplit(unfreezeUnsplit); // true=本次分账同时完结,false=不解冻

 

log.info("请求分账:outOrderNo={}, receivers={}", outOrderNo, receivers.size());

return profitSharingService.createOrder(request);

}

 

/**

* 构造分账接收方列表(示例:分给商户)

*/

public List<Receiver> buildReceivers(String receiverAccount, int amountFen, String desc) {

Receiver r = new Receiver();

r.setType("MERCHANT_ID");

r.setAccount(receiverAccount); // 接收方商户号

r.setAmount(amountFen); // 分账金额,单位:分

r.setDescription(desc); // 分账描述,最多 80 字

r.setName("某某合作公司"); // 商户类型必填全称

return java.util.Collections.singletonList(r);

}

 

/**

* 构造分账给个人零钱的接收方

*/

public List<Receiver> buildPersonalReceivers(String openid, int amountFen, String desc, String userName) {

Receiver r = new Receiver();

r.setType("PERSONAL_OPENID"); // 或 PERSONAL_SUB_OPENID

r.setAccount(openid); // 用户 openid

r.setAmount(amountFen);

r.setDescription(desc);

// 个人类型 name 选填,填了会校验实名一致性

if (userName != null) r.setName(userName);

return java.util.Collections.singletonList(r);

}

 

7.6.7 查询分账结果

/**

* 查询分账结果

* 接口:GET /v3/profitsharing/orders/{out_order_no}?sub_mchid=xxx

* 文档:https://pay.weixin.qq.com/doc/v3/partner/4012467002

*

* receivers[i].result 取值:

* SUCCESS = 分账成功

* CLOSED = 已关闭(看 receivers[i].fail_reason)

* PENDING = 处理中

*/

public QueryOrderResponse queryProfitSharing(String subMchid, String outOrderNo) {

QueryOrderRequest request = new QueryOrderRequest();

request.setSubMchid(subMchid);

request.setOutOrderNo(outOrderNo);

 

QueryOrderResponse response = profitSharingService.queryOrder(request);

log.info("查询分账结果:outOrderNo={}, state={}", outOrderNo, response.getState());

 

// 遍历每个接收方的分账结果

if (response.getReceivers() != null) {

for (ReceiverResult r : response.getReceivers()) {

log.info("接收方 {} 分账结果:{},失败原因:{}",

r.getAccount(), r.getResult(), r.getFailReason());

}

}

return response;

}

 

/**

* 查询订单剩余待分金额

* 接口:GET /v3/profitsharing/transactions/{transaction_id}/amounts?sub_mchid=xxx

*/

public QueryOrderAmountsResponse queryUnsplitAmount(String subMchid, String transactionId) {

QueryOrderAmountsRequest request = new QueryOrderAmountsRequest();

request.setSubMchid(subMchid);

request.setTransactionId(transactionId);

return profitSharingService.queryOrderAmounts(request);

}

 

7.6.8 完结分账(解冻剩余资金)

/**

* 完结分账

* 接口:POST /v3/profitsharing/orders/{out_order_no}/finish

* 文档:https://pay.weixin.qq.com/doc/v3/partner/4012467016

*

* 用途:把剩余未分资金一次性解冻给分账方(子商户)

* 注意:out_order_no 必须与请求分账时的 out_order_no 不同!

*/

public FinishOrderResponse finishProfitSharing(String subMchid,

String transactionId, String outOrderNo, String subAppid) {

FinishOrderRequest request = new FinishOrderRequest();

request.setSubMchid(subMchid);

request.setTransactionId(transactionId);

request.setOutOrderNo(outOrderNo); // 完结用的单号,必须不同于请求分账的单号

if (subAppid != null) request.setSubAppid(subAppid);

 

log.info("完结分账:transactionId={}, outOrderNo={}", transactionId, outOrderNo);

return profitSharingService.finishOrder(request);

}

 

7.6.9 请求分账回退(已分资金回退)

分账回退接口文档:https://pay.weixin.qq.com/doc/v3/partner/4012477747

▲ 分账回退:已分资金回退给分账方(来源:4015870957)

退款流程:先调用本接口把已分资金回退给分账方 → 再调用普通退款接口退款。

/**

* 请求分账回退

* 接口:POST /v3/profitsharing/returns

* 文档:https://pay.weixin.qq.com/doc/v3/partner/4012467097

*

* 前置条件:接收方商户登录商户平台 → 交易中心 → 分账 → 分账接收设置 → 开启『同意分账回退』

* 否则报错 271644182

*/

public ReturnResponse createReturn(String subMchid, String orderId,

String outReturnNo, String outOrderNo,

String receiverAccount, String receiverType,

int returnAmount, String description) {

CreateReturnRequest request = new CreateReturnRequest();

request.setSubMchid(subMchid);

request.setOrderId(orderId); // 微信分账单号(请求分账返回的 order_id)

request.setOutReturnNo(outReturnNo); // 商户回退单号,商户系统内唯一

request.setOutOrderNo(outOrderNo); // 原分账单号

request.setReturnAccountType(receiverType); // MERCHANT_ID

request.setReturnAccount(receiverAccount);

request.setReturnAmount(returnAmount);

request.setDescription(description);

 

log.info("请求分账回退:outReturnNo={}, amount={}", outReturnNo, returnAmount);

return profitSharingService.createReturn(request);

}

 

/**

* 查询分账回退结果

* 接口:GET /v3/profitsharing/returns/{out_return_no}

* ?sub_mchid=xxx&out_order_no=xxx

*/

public QueryReturnResponse queryReturn(String subMchid, String outReturnNo, String outOrderNo) {

QueryReturnRequest request = new QueryReturnRequest();

request.setSubMchid(subMchid);

request.setOutReturnNo(outReturnNo);

request.setOutOrderNo(outOrderNo);

return profitSharingService.queryReturn(request);

}

 

7.6.10 回调 Controller(核心:验签 + 解密 + 业务处理)

分账动账通知官方文档:https://pay.weixin.qq.com/doc/v3/partner/4012467002

▲ 回调通知在流程图中的位置:分账成功后异步通知商户(来源:4012062375)

微信支付在分账/回退成功后会主动 POST 通知商户,需要:

● 1) 验签(确认请求来自微信支付,防伪造)

● 2) 解密 resource.ciphertext(AES-256-GCM 解密,密钥是 APIv3 密钥)

● 3) 业务处理(更新订单状态)

● 4) 返回 200 + {code: SUCCESS},否则微信会重试

package com.example.wxpay.controller;

 

import com.google.gson.Gson;

import com.google.gson.GsonBuilder;

import com.wechat.pay.java.core.notification.NotificationParser;

import com.wechat.pay.java.service.profitsharing.model.*;

import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.web.bind.annotation.*;

 

@Slf4j

@RestController

@RequestMapping("/api/wxpay/profit-sharing")

public class ProfitSharingController {

 

@Autowired private NotificationParser notificationParser;

@Autowired private OrderService orderService;

 

/**

* 分账动账通知回调

* 路径需与下单时 notify_url 一致(https://your-domain.com/api/wxpay/profit-sharing/notify)

* 文档:https://pay.weixin.qq.com/doc/v3/partner/4012075400

*

* SDK 自动完成:

* - 验签(用微信支付平台证书)

* - 解密(AES-256-GCM,密钥=APIv3 密钥)

* - 反序列化为 ProfitSharingNotifySource 对象

*/

@PostMapping("/notify")

public String profitSharingNotify(@RequestBody String body,

@RequestHeader("Wechatpay-Timestamp") String timestamp,

@RequestHeader("Wechatpay-Nonce") String nonce,

@RequestHeader("Wechatpay-Signature") String signature,

@RequestHeader("Wechatpay-Serial") String serial) {

log.info("收到分账回调:serial={}", serial);

 

try {

// 构造 RequestParam

com.wechat.pay.java.core.notification.RequestParam requestParam =

new com.wechat.pay.java.core.notification.RequestParam.Builder()

.serialNumber(serial)

.nonce(nonce)

.timestamp(timestamp)

.signature(signature)

.body(body)

.build();

 

// SDK 自动验签 + 解密,得到通知内容

ProfitSharingNotifySource notify =

notificationParser.parse(requestParam, ProfitSharingNotifySource.class);

 

log.info("分账回调解析成功:outOrderNo={}, state={}",

notify.getOutOrderNo(), notify.getState());

 

// ===== 业务处理 =====

// notify.getState():FINISHED / PROCESSING

// notify.getReceivers():每个接收方的最终结果

for (ReceiverResult r : notify.getReceivers()) {

if ("SUCCESS".equals(r.getResult())) {

// 分账成功:更新订单状态、给接收方加余额、触发下游业务

orderService.markReceiverSuccess(

notify.getOutOrderNo(),

r.getAccount(),

r.getAmount());

} else if ("CLOSED".equals(r.getResult())) {

// 分账失败:记录失败原因、通知运营处理

log.error("分账失败:account={}, reason={}", r.getAccount(), r.getFailReason());

orderService.markReceiverFailed(

notify.getOutOrderNo(),

r.getAccount(),

r.getFailReason());

}

}

 

// 必须返回 SUCCESS,否则微信会重试 8 次

return "{\"code\": \"SUCCESS\", \"message\": \"成功\"}";

 

} catch (Exception e) {

log.error("分账回调处理失败", e);

// 返回失败,微信会重试

return "{\"code\": \"FAIL\", \"message\": \"" + e.getMessage() + "\"}";

}

}

}

 

⚠️ 注意:回调必须返回 200 + {"code":"SUCCESS"}。否则微信会按 15s/15s/30s/3m/10m/20m/30m/30m/30m/60m 节奏重试 8 次。

 

7.6.11 NotificationParser Bean 配置

在 WxPayConfig 里追加:

@Bean

public NotificationParser notificationParser(Config wxPayConfig) {

// SDK 自动用 APIv3 密钥解密 + 用平台证书验签

return new NotificationParser(wxPayConfig);

}

 

7.6.12 定时任务:异步查询分账结果(兜底)

回调可能丢失,所以定时任务定期主动查询未完成的分账单:

package com.example.wxpay.scheduler;

 

import com.example.wxpay.service.ProfitSharingService;

import com.example.wxpay.entity.ProfitSharingOrder;

import com.example.wxpay.repository.ProfitSharingOrderRepository;

import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.scheduling.annotation.Scheduled;

import org.springframework.stereotype.Component;

 

import java.time.LocalDateTime;

import java.util.List;

 

@Slf4j

@Component

public class ProfitSharingQueryScheduler {

 

@Autowired private ProfitSharingService profitSharingService;

@Autowired private ProfitSharingOrderRepository orderRepository;

 

/**

* 每 5 分钟查询一次状态为 PENDING 的分账单

* 兜底回调丢失的情况

*/

@Scheduled(fixedRate = 5 * 60 * 1000)

public void queryPendingProfitSharing() {

LocalDateTime deadline = LocalDateTime.now().minusMinutes(10); // 只查 10 分钟前发起的

List<ProfitSharingOrder> pendingOrders =

orderRepository.findByStateAndUpdateTimeBefore("PENDING", deadline);

 

log.info("定时查询分账结果,待查单数:{}", pendingOrders.size());

 

for (ProfitSharingOrder order : pendingOrders) {

try {

QueryOrderResponse response = profitSharingService.queryProfitSharing(

order.getSubMchid(), order.getOutOrderNo());

 

// 更新订单状态

order.setState(response.getState());

if (response.getReceivers() != null) {

for (ReceiverResult r : response.getReceivers()) {

order.setReceiverResult(r.getAccount(),

r.getResult(), r.getFailReason());

}

}

orderRepository.save(order);

 

log.info("定时查询完成:outOrderNo={}, state={}",

order.getOutOrderNo(), response.getState());

 

} catch (Exception e) {

log.error("查询分账结果失败:outOrderNo={}",

order.getOutOrderNo(), e);

}

}

}

}

 

7.6.13 完整业务调用示例

▲ 完整业务调用对应的全流程时序图(来源:4012062375)

一个完整的分账流程调用示例(用户支付成功后自动分账):

package com.example.wxpay.service;

 

import com.example.wxpay.entity.Order;

import com.example.wxpay.repository.OrderRepository;

import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Service;

 

@Slf4j

@Service

public class ProfitSharingFacadeService {

 

@Autowired private ProfitSharingService profitSharingService;

@Autowired private OrderRepository orderRepository;

 

/**

* 完整分账流程(订单支付成功后调用)

*/

public void doProfitSharing(String outTradeNo) {

// 1. 查订单信息

Order order = orderRepository.findByOutTradeNo(outTradeNo);

if (order == null) throw new RuntimeException("订单不存在");

 

// 2. 查询订单剩余可分金额(重要!避免超额分账报错)

QueryOrderAmountsResponse amounts =

profitSharingService.queryUnsplitAmount(

order.getSubMchid(), order.getTransactionId());

int unsplitAmount = amounts.getUnsplitAmount();

 

if (unsplitAmount <= 0) {

log.warn("订单无可分金额:{}", outTradeNo);

return;

}

 

// 3. 计算本次分账金额

// 例:订单总额 100 元,服务商抽 10% = 10 元 = 1000 分

int profitAmount = order.getTotalAmount() / 10; // 服务商分 10%

// 检查不超过剩余可分金额

if (profitAmount > unsplitAmount) {

profitAmount = unsplitAmount;

}

 

// 4. 构造分账接收方(分给服务商自己的商户号)

List<Receiver> receivers = profitSharingService.buildReceivers(

"1900000110", // 接收方商户号(服务商自己)

profitAmount, // 分账金额(分)

"订单" + outTradeNo + "分给服务商" // 描述

);

 

// 5. 请求分账

String outOrderNo = "PS" + System.currentTimeMillis();

CreateOrderResponse response = profitSharingService.createProfitSharing(

order.getSubMchid(),

order.getTransactionId(),

outOrderNo,

receivers,

false // false=不解冻剩余,让子商户拿到大头;true=本次即完结

);

 

log.info("分账受理成功:orderId={}, outOrderNo={}",

response.getOrderId(), outOrderNo);

 

// 6. 保存分账单到数据库(等回调或定时任务更新结果)

orderRepository.saveProfitSharingOrder(

outOrderNo, order.getTransactionId(),

order.getSubMchid(), profitAmount, "PENDING");

 

// 注:剩余资金(订单金额-分账金额)默认自动解冻给子商户

// 如需主动解冻可调用 finishProfitSharing,但只有『最后一笔分账』才需要传 unfreeze_unsplit=true

}

 

/**

* 退款流程:先回退分账,再退款

*/

public void refundWithProfitSharing(String outTradeNo, int refundAmount) {

Order order = orderRepository.findByOutTradeNo(outTradeNo);

 

// 1. 先调用分账回退接口,把已分给服务商的资金回退给子商户

String outReturnNo = "RT" + System.currentTimeMillis();

profitSharingService.createReturn(

order.getSubMchid(),

order.getProfitSharingOrderId(), // 微信分账单号

outReturnNo,

order.getOutOrderNo(), // 原分账单号

"1900000110", // 回退给谁(原分账接收方)

"MERCHANT_ID",

refundAmount,

"退款回退分账资金"

);

 

// 2. 等回退成功(可监听回调或定时查询)后,再调用普通退款接口

// ... 此处省略退款接口调用

}

}

 

7.6.14 敏感字段手动加密(如不用 SDK)

如果直接用 HttpClient 不用官方 SDK,name 字段必须手动用微信支付公钥 RSA-OAEP 加密:

import javax.crypto.Cipher;

import javax.crypto.spec.OAEPParameterSpec;

import javax.crypto.spec.PSource;

import java.security.KeyFactory;

import java.security.PublicKey;

import java.security.spec.X509EncodedKeySpec;

import java.util.Base64;

 

public class SensitiveDataUtil {

 

/**

* 用微信支付公钥加密敏感字段

* 算法:RSA/ECB/OAEPWithSHA-1AndMGF1Padding

*/

public static String encrypt(String plaintext, String publicKeyBase64) throws Exception {

byte[] keyBytes = Base64.getDecoder().decode(publicKeyBase64);

X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);

KeyFactory kf = KeyFactory.getInstance("RSA");

PublicKey pubKey = kf.generatePublic(spec);

 

Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding");

OAEPParameterSpec oaepParams = new OAEPParameterSpec(

"SHA-1", "MGF1",

java.security.spec.MGF1ParameterSpec.SHA1,

PSource.PSpecified.DEFAULT

);

cipher.init(Cipher.ENCRYPT_MODE, pubKey, oaepParams);

byte[] encrypted = cipher.doFinal(plaintext.getBytes("UTF-8"));

return Base64.getEncoder().encodeToString(encrypted);

}

}

 

// 调用示例:

String encryptedName = SensitiveDataUtil.encrypt("某某公司", wxPayPublicKeyBase64);

// 然后传给接口的 receivers[i].name 字段

 

7.6.15 项目目录结构建议

▲ 项目结构对应分账产品模型(来源:4015870957)

src/main/java/com/example/wxpay/

├── config/

│ └── WxPayConfig.java # SDK 配置 + Bean 注入

├── controller/

│ ├── PartnerPayController.java # 下单 + 支付回调

│ └── ProfitSharingController.java # 分账回调(验签+解密+业务)

├── service/

│ ├── PartnerPayService.java # 下单(含 profit_sharing=true)

│ ├── ProfitSharingService.java # 分账接口封装

│ │ ├── addReceiver()

│ │ ├── deleteReceiver()

│ │ ├── createProfitSharing()

│ │ ├── queryProfitSharing()

│ │ ├── finishProfitSharing()

│ │ ├── createReturn() # 分账回退

│ │ └── queryReturn()

│ ├── ProfitSharingFacadeService.java # 业务编排(完整流程)

│ └── OrderService.java # 订单状态管理

├── scheduler/

│ └── ProfitSharingQueryScheduler.java # 定时查询兜底

├── entity/

│ ├── Order.java

│ └── ProfitSharingOrder.java

├── repository/

│ ├── OrderRepository.java

│ └── ProfitSharingOrderRepository.java

└── util/

└── SensitiveDataUtil.java # 敏感字段加密

 

src/main/resources/

├── application.yml # 微信支付参数

└── cert/

└── apiclient_key.pem # 商户私钥文件(保密)

 

7.6.16 测试用例(小额跑通)

▲ 测试用例覆盖的完整流程(来源:4012062375)

上线前用 1 元跑通一笔分账:

// 1. 下单(带分账标识)

PrepayWithRequestJsapiResponse resp = partnerPayService.createOrderWithProfitSharing(

"1900000109", // 子商户号

"wx8888888888888889", // 子商户 AppID

"oUpF8xxxxxx", // 用户 openid

"TEST" + System.currentTimeMillis(), // 商户订单号

"测试商品",

100 // 100 分 = 1 元

);

String transactionId = ... // 等支付成功回调获取

 

// 2. 查询可分金额

QueryOrderAmountsResponse amounts =

profitSharingService.queryUnsplitAmount("1900000109", transactionId);

// 应返回 unsplitAmount ≈ 100(扣手续费后约 99)

 

// 3. 添加接收方(一次性,已添加可跳过)

profitSharingService.addReceiver(

"1900000109",

"MERCHANT_ID",

"1900000110", // 接收方商户号(服务商自己)

"某某公司",

"PARTNER"

);

 

// 4. 请求分账(分 10 分 = 0.1 元 给服务商)

List<Receiver> receivers = profitSharingService.buildReceivers(

"1900000110", 10, "测试分账"

);

CreateOrderResponse resp = profitSharingService.createProfitSharing(

"1900000109", transactionId,

"PS" + System.currentTimeMillis(),

receivers, false

);

// 返回 orderId,state=ACCEPTED(已受理)

 

// 5. 查询分账结果(约 1 分钟后变 SUCCESS)

Thread.sleep(60 * 1000);

QueryOrderResponse query = profitSharingService.queryProfitSharing(

"1900000109", outOrderNo

);

// query.getReceivers()[0].getResult() = "SUCCESS"

 

// 6. 完结分账(剩余 89 分解冻给子商户)

profitSharingService.finishProfitSharing(

"1900000109", transactionId,

"FIN" + System.currentTimeMillis(),

"wx8888888888888889"

);

 

ℹ️ 提示:测试建议金额 1 元(100 分),避免大额分账出错。先跑通单接口,再跑通完整流程。最后接入回调处理。

 

第八部分 验收清单与官方资源

8.1 上线前检查清单(非技术人员对照打勾)

请逐项打勾,全部完成才可上线:

▲ 完整流程回顾图(来源:微信支付官方文档 4012062375)

已做

检查项

□

已注册企业服务号且通过微信认证

□

已申请成为微信支付服务商,获得服务商商户号

□

服务商平台 → 产品中心 → 特约商户授权产品 → 服务商分账 已开通

□

已发起分账授权邀请给每个子商户

□

每个子商户已在商户平台 → 产品中心 → 我授权的产品 中确认授权

□

所有子商户已完成进件(含打款验证)

□

子商户已关联小程序/公众号 AppID(如使用)

□

已通过接口或商户平台添加了分账接收方

□

子商户已在商户平台 → 产品中心 → 授权的产品 → 分账授权 设置最大分账比例

□

服务商平台 → 账户中心 → API 安全 已下载 API 证书、设置 APIv3 密钥

□

已记录服务商商户号、AppID、证书序列号、子商户号等开发参数

□

代码已实现:下单传 profit_sharing=true

□

代码已实现:请求分账、查询分账结果、完结分账

□

已用小额(如 1 元)跑通一笔分账测试

□

分账动账通知回调地址(notify_url)已配置并能正确接收

 

8.2 关键客服与咨询渠道

▲ 全流程回顾:从服务商申请到分账完成(来源:4012062375)

渠道

联系方式 / 网址

用途

微信支付客服

95017

提高分账比例申请、其他业务咨询

服务商 BD 邮箱

weixinpayBD@tencent.com

大客户合作

官方文档总站

https://pay.weixin.qq.com/

所有产品文档入口

服务商平台

https://pay.weixin.qq.com/index.php/partner/public/home

服务商登录与操作

商户平台

https://pay.weixin.qq.com/

子商户登录与操作

公众平台

https://mp.weixin.qq.com/

服务号、小程序后台

 

注:全部官方文档链接

本文档所有截图、步骤、参数均来自以下官方文档页面:

● 开发接入准备:https://pay.weixin.qq.com/doc/v3/partner/4012072589

● 对接步骤(V2):https://pay.weixin.qq.com/doc/v2/partner/4011984375

● 开发指引(连锁品牌):https://pay.weixin.qq.com/doc/v3/partner/4012072637

● 添加分账接收方接口:https://pay.weixin.qq.com/doc/v3/partner/4012690944

● 请求分账接口:https://pay.weixin.qq.com/doc/v3/partner/4012692975

● 查询分账结果接口:https://pay.weixin.qq.com/doc/v3/partner/4012467002

● 完结分账接口:https://pay.weixin.qq.com/doc/v3/partner/4012467016

● 查询订单剩余待分金额:https://pay.weixin.qq.com/doc/v3/partner/4012457927

● 分账动账通知:https://pay.weixin.qq.com/doc/v3/partner/4012075400

● 分账失败处理指引:https://pay.weixin.qq.com/doc/v3/partner/4015504918

● 分账常见问题:https://pay.weixin.qq.com/doc/v3/partner/4014547107

● 分账错误码:https://pay.weixin.qq.com/doc/v3/merchant/4020807103

● 业务示例代码:https://pay.weixin.qq.com/doc/v3/partner/4015870957

● 商家注册小程序指引:https://pay.weixin.qq.com/static/help_guide/business_registration.shtml

● 微信支付服务商接入指引 PDF:https://gtimg.wechatpay.cn/resource/xres/mmpaydoc/static/attachment/d7ed63166bac0051d3b2c13ea0fc36dc/服务商接入指引-大交通中心(2020新版).pdf

● 服务商申请入口:https://pay.weixin.qq.com/index.php/partner/public/home

● 公众平台:https://mp.weixin.qq.com/

● 微信支付商户平台:https://pay.weixin.qq.com/



posted @ 2026-09-14 21:23  好运的菜狗  阅读(128)  评论(0)    收藏  举报