商家转账新版接入排障:1001、NO_AUTH 与 24 小时未确认单怎么处理
一、先确认自己在哪一套接口上
“商家转账到零钱”已于 2025 年 1 月 15 日升级为新版“商家转账”。升级后两套接口并存,它们容易混淆:
旧版(需升级):
- 企业付款到零钱
/mmpaymkttransfers/promotion/transfers - 企业付款到银行卡
/mmpaysptrans/pay_bank - 现金红包
/mmpaymkttransfers/sendredpack - 小程序红包
/mmpaymkttransfers/sendminiprogramhb - 商家转账到零钱
/v3/transfer/batches
新版:
- 商家转账
/v3/fund-app/mch-transfer/transfer-bills - 发起转账并完成免确认收款授权
/v3/fund-app/mch-transfer/transfer-bills/pre-transfer-with-authorization - 先发起免确认收款授权
/v3/fund-app/mch-transfer/user-confirm-authorization - 授权后转账
/v3/fund-app/mch-transfer/transfer-bills/transfer
权限归属规则:2025-01-15 之后申请的权限默认是新版;之前已有权限的商户仍是旧版权限;旧权限因长期未使用被关闭、或商户自己关闭后重新申请,拿到的都是新版;已开通新版不支持回退旧版。
所以第一个排查动作,就是把线上调用地址和商户实际的权限版本核对一遍。调用与权限不匹配会报权限类错误:1001 权限未开通,或 NO_AUTH「当前商户号没有相关权限,暂不支持使用」,或提示“暂不支持使用升级前功能”。官方说明:若商户已有产品权限和调用接口不匹配,会报权限验证失败类错误,可结合接口 URL 自查。
新版不再提供官方 SDK,改为工具类 + 示例代码(Java、Go)。社区 wechatpay-java #330 里有人反馈接口路径换了、SDK 未同步、H5 拉起用户确认卡住。
这类错误重试没有意义。 1001 / NO_AUTH 只能走权限申请与版本确认,代码里加重试只会把失败次数刷上去。
参考资料:商家转账到零钱升级说明、商家转账产品介绍、发起转账。
二、发起转账:字段与状态机
POST https://api.mch.weixin.qq.com/v3/fund-app/mch-transfer/transfer-bills
(备域名 api2.mch.weixin.qq.com)
请求头需要 Authorization(签名)、Accept: application/json、Content-Type: application/json、Wechatpay-Serial(微信支付公钥 ID 或平台证书序列号,用于加密敏感字段)。
body 必填:
appid:与商户号有绑定关系的 appid(APP / 小程序 / 公众号 / 企业号均可)out_bill_no:商户单号,只能数字和大小写字母,商户内唯一transfer_scene_id:转账场景 ID,需在商户平台-产品中心-商家转账申请,如1000(现金营销)、1006(企业报销)openid:收款用户在该 appid 下的 openidtransfer_amount:整数,单位分transfer_remark:转账备注,用户可见,UTF8,最多 32 字符transfer_scene_report_infos[]:数组,每项info_type(≤15 字符,固定值,按转账场景报备信息字段说明传)、info_content(≤32 字符)
选填:
user_name:收款方真实姓名,需加密传入(微信支付公钥加密,推荐;或平台证书公钥)。转账金额 ≥ 2000 元时必须传;0.3 元(含)到 2000 元(不含)之间可自行决定是否传;低于 0.3 元不支持传姓名校验notify_url:回调地址,必须公网可访问的 HTTPS、不能带参数user_recv_perception:用户收款感知(不填展示场景默认内容)user_recv_style:{type: CONFIRM_PAGE | RED_PACKET},红包样式单笔金额需 ≤ 200 元,且仅部分场景可用
应答 200 OK 返回 out_bill_no、transfer_bill_no(微信转账单号)、create_time(rfc3339)、state、package_info(跳转领取页面的 package 信息)。
state 状态与处置方式:
| state | 含义 | 处置 |
|---|---|---|
ACCEPTED | 转账已受理 | 非终态,可原单重试 |
PROCESSING | 转账锁定资金中 | 非终态;长期停留该状态建议检查账户余额,余额不足可充值后原单重试 |
WAIT_USER_CONFIRM | 待用户确认,资金已锁定 | 非终态,可拉起微信收款确认页 |
TRANSFERING | 转账中 | 非终态,可拉起收款确认页再次重试确认 |
SUCCESS | 成功 | 终态 |
FAIL | 失败 | 终态,重新转账需重新生成单据 |
CANCELING | 撤销中 | 非终态 |
CANCELLED | 撤销完成 | 终态 |
三条官方结论要落到代码里:
- HTTP 200 且
state=WAIT_USER_CONFIRM才算创建转账单成功。 - HTTP 状态码不为 200 时,务必不要立即更换商户订单号重试;应按错误码处理,并用“商户单号查询转账单”或“微信单号查询转账单”查结果;确认原单明确“失败”后,再换单重试,否则有重复转账的资金风险。
- 遇到新错误码也不要换单重试,先查单。
单个商户接口频率限制 100 次/s。
也就是说,重试白名单严格限定在 ACCEPTED / PROCESSING / WAIT_USER_CONFIRM / TRANSFERING 这四个非终态、且使用原 out_bill_no;FAIL、CANCELLED 以及任何非 200 响应,一律先进查单流程,不做自动换单。
三、用户确认收款
新版必须用户主动确认才能到账(确认模式)。小程序用 wx.requestMerchantTransfer(mchId、appId、package=后端返回的 package_info);APP 与公众号 / JSAPI 有各自调起接口。
第三方记录:微信版本 ≥ 8.0.30、基础库 ≥ 3.4.5,可用 wx.canIUse('requestMerchantTransfer') 做兼容检测(未实测,需以官方文档为准)。
用户 24 小时内未确认,转账过期;官方 FAQ 明确过期会推送回调通知。这条直接决定了后面定时任务的存在必要。
免确认模式需要先完成授权(user-confirm-authorization),授权成功后长期有效,解除授权才失效;同一用户只需授权一次,勿换授权单号重复授权,否则会报“用户在当前场景下的授权数量达到限制”。撤销转账接口限频 20 次/s。
四、六个易错点
- 接口安全 IP 白名单:商家转账出资类接口必须用已配置的请求来源 IP 调用;授权、查询授权关系、查单等不出资接口不校验。未配置返回
268491067「此IP地址不允许调用接口,请按开发指引设置」/INVALID_REQUEST。常见反例是只登记了内网 IP,漏了 NAT / 负载均衡的公网出口。 - 场景:
transfer_scene_id必须是已申请的场景,硬编码示例1000会返回INVALID_REQUEST「你尚未获取该转账场景」。目前仅可申请一个场景。现金营销场景单笔最低 0.1 元,不可调整。 - 额度:旧版商家转账到零钱单笔上限 200 元且不可提额;超了明细返回
FAIL,失败原因TRANSFER_QUOTA_EXCEED。这是业务上会直接卡住的一类,属于必须改代码或改产品的,不是调参能解决的。 - 权限与证书一致性:发起转账的商户号必须有商家转账权限,商户号、API 证书序列号、签名私钥必须同一套。子商户开通、服务商号签名是典型反例。
- 回调:须配置 APIv3 密钥解密通知数据;防火墙需放通微信支付回调 IP 段(只放通单个临时出口 IP 是典型反例);未收到回调必须主动查单兜底;回调与查单都要幂等,保存
Request-ID与报文。 - 平台证书到期:到期前要做多证书验签或切换微信支付公钥,否则应答/回调验签失败,转账结果处理不了。
第 1、2、4 条属于配置侧,人工确认一次即可,重试无收益。第 3 条要么提额不成立就缩额,要么切新版。第 5、6 条是长期运行风险,需要监控。
五、从旧版直接打款切到新版的改造清单
- 数据库加“待确认收款”状态位,加
package_info存储与确认截止时间。package_info有时效,别缓存太久。 - 提现/打款接口拆成“创建转账单”和“前端拉起确认”两段,前端要有待确认列表与确认按钮。原来的同步打款语义在这里断掉了,产品文案要跟着改。
- 定时任务扫 24h 未确认单,查单后置为失败并解冻余额。扫到
WAIT_USER_CONFIRM超时的单子,靠回调通知 + 查单双通道确认状态,不要只看本地时间戳。 - 非 200 一律先查单,禁止换单重试。把这条写进公共客户端,而不是靠每个业务方自觉。
- 回调 + 主动查单双通道,幂等键用
out_bill_no。