在微信支付的生态中,服务商模式是一种常见的接入方式:服务商为特约商户提供支付能力,交易资金先进入特约商户号,再按约定比例分给其他参与方。分账功能正是实现这一诉求的关键能力。本文将围绕公众号支付场景,完整讲解服务商模式下分账功能的实现流程,包括添加分账接收方、发起分账、查询分账结果以及分账回退等环节。

一、分账的基本原理与前期准备
分账的本质是在订单支付成功后,将订单金额中的一部分资金从特约商户号划拨给其他商户或个人。在服务商模式下,整个链路涉及三个角色:服务商(service)、特约商户(sub_mchid)和分账接收方(receiver)。用户支付的货款先进入特约商户的商户号,服务商调用分账接口后,微信会将指定金额划转给接收方。
前期准备有几个关键点。第一,特约商户号必须开通分账功能,这需要服务商在服务商平台为特约商户申请开通分账权限,或特约商户自行申请。第二,分账比例默认上限为30%,如需更高比例需向微信申请。第三,下单时必须传入profit_sharing参数并设为true,表示该笔订单参与分账,否则后续无法发起分账操作。
下面是公众号支付统一下单时的关键参数示例(V3接口):
import requests
import json
# V3 统一下单(JSAPI方式,公众号支付)
url = "https://api.mch.weixin.qq.com/v3/pay/partner/transactions/jsapi"
data = {
"sp_appid": "服务商的appid",
"sp_mchid": "服务商商户号",
"sub_appid": "子商户appid,可与sp_appid相同",
"sub_mchid": "特约商户号",
"description": "测试商品",
"out_trade_no": "ORDER20240101001",
"notify_url": "https://www.ipipp.com/notify",
"amount": {
"total": 100, # 单位:分
"currency": "CNY"
},
"payer": {
"sp_openid": "用户在服务商appid下的openid"
},
"settle_info": {
"profit_sharing": True # 关键:标记该笔订单允许分账
}
}
注意settle_info.profit_sharing字段,如果下单时未设置该参数为true,订单资金会直接结算给特约商户,之后调用分账接口会返回错误。这是实际开发中最常见的踩坑点之一。
二、添加分账接收方
分账接收方是资金的接收者,可以是其他商户号,也可以是个人(通过openid)。在发起分账之前,必须先将接收方与特约商户绑定,这一步通过分账接收方添加接口完成。绑定的含义是:特约商户明确授权某个商户或个人可以接收来自自己的分账资金,微信借此保证资金安全。
接口地址为POST /v3/profitsharing/receivers/add,服务商需要使用自己的商户私钥对请求签名。关键参数包括:sub_mchid(特约商户号)、app_id、type(接收方类型,MERCHANT_ID表示商户号,PERSONAL_OPENID表示个人)以及account(具体的商户号或openid)。可选参数relation_type用于说明分账关系,如服务商、门店、合作伙伴等,使用PERSONAL_OPENID时还可以传name字段并配合加密处理。
import json
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import padding
import requests
import time
MCH_PRIVATE_KEY = "服务商商户私钥内容"
MCH_ID = "服务商商户号"
SERIAL_NO = "证书序列号"
def build_auth_header(method, url_path, body=""):
timestamp = str(int(time.time()))
nonce = "randomstr123456"
message = f"{method}\n{url_path}\n{timestamp}\n{nonce}\n{body}\n"
private_key = serialization.load_pem_private_key(
MCH_PRIVATE_KEY.encode(), password=None
)
signature = private_key.sign(
message.encode(),
padding.PKCS1v15(),
hashes.SHA256()
)
import base64
sign_b64 = base64.b64encode(signature).decode()
return (
f'WECHATPAY2-SHA256-RSA2048 mchid="{MCH_ID}",'
f'nonce_str="{nonce}",signature="{sign_b64}",'
f'timestamp="{timestamp}",serial_no="{SERIAL_NO}"'
)
# 添加分账接收方
path = "/v3/profitsharing/receivers/add"
body = json.dumps({
"sub_mchid": "特约商户号",
"app_id": "服务商appid",
"type": "MERCHANT_ID",
"account": "接收方商户号",
"name": "接收方商户名称",
"relation_type": "SERVICE_PROVIDER"
})
headers = {
"Authorization": build_auth_header("POST", path, body),
"Content-Type": "application/json",
"Accept": "application/json"
}
resp = requests.post("https://api.mch.weixin.qq.com" + path,
headers=headers, data=body.encode("utf-8"))
print(resp.status_code, resp.text)
添加成功后返回HTTP 200,可以调用删除分账接收方接口解绑。需要特别注意的是,如果添加的是个人类型的接收方且传了name字段,该字段需要使用微信支付平台证书加密后再传输,明文传输会直接报错。如果没有加密条件,可以先不传name字段,微信会在首次分账后校验收款人实名信息。
另一个常见报错是NO_AUTH,表示该接收方未开通分账接收功能或类型不匹配,此时应检查接收方商户号是否已经在微信侧开通了接收分账的权限。
三、执行分账操作与结果处理
订单支付成功后(以支付回调为准),服务商即可发起分账。分账接口为POST /v3/profitsharing/orders,核心参数是transaction_id(微信支付订单号,注意不能用商户订单号)、out_order_no(商户分账单号,需保证唯一)以及receivers数组,数组中每个元素指定一个接收方及其分账金额。
# 发起分账请求
path = "/v3/profitsharing/orders"
body = json.dumps({
"sub_mchid": "特约商户号",
"app_id": "服务商appid",
"transaction_id": "4200001234202401010123456",
"out_order_no": "PSON20240101001",
"receivers": [
{
"type": "MERCHANT_ID",
"account": "接收方商户号",
"amount": 30, # 分账金额,单位分
"description": "平台服务费分账"
}
]
})
headers = {
"Authorization": build_auth_header("POST", path, body),
"Content-Type": "application/json"
}
resp = requests.post("https://api.mch.weixin.qq.com" + path,
headers=headers, data=body.encode("utf-8"))
print(resp.status_code, resp.text)
关于分账金额,有两条规则必须遵守。一是所有接收方的分账金额之和不能超过订单金额乘以分账比例上限(默认30%);二是剩余资金默认冻结在特约商户号中,需要调用分账完结接口(POST /v3/profitsharing/orders/unfinish)将剩余资金解冻给特约商户。也就是说,一次完整的分账流程通常包含两步:请求分账加完结分账。如果希望资金自动解冻,也可以在下单时传finish相关配置或在分账请求中做相应处理。
分账接口返回的是处理状态,最终结果建议通过两个途径确认:一是查询分账结果接口GET /v3/profitsharing/orders/{out_order_no},二是配置分账动账通知。查询接口的返回中,state字段为SUCCESS表示分账成功,如果某笔分账失败会附上失败原因。对于已经成功分出去的资金,如需退回,可以调用分账回退接口,但分账回退只能在分账成功之后的一定期限内操作,且接收方账户余额必须充足。
最后总结一下完整的业务闭环:统一下单时开启profit_sharing,支付成功回调中记录transaction_id,添加分账接收方,调用请求分账接口,查询或等待回调确认分账结果,最后调用分账完结解冻剩余资金。把这几步串起来,服务商模式下的公众号支付分账功能就完整落地了。开发过程中建议全程记录请求报文和返回报文,出现签名错误或参数错误时,对照微信官方接口文档逐项排查,效率会高很多。