微信支付商家转账用户确认模式:状态机、PARAM_ERROR 与幂等重试
从一个业务改动说起
2025-01-15 起,微信「商家转账到零钱」升级:
- 原来:商户发起转账即受理并执行,钱直接到账。
- 升级后:新增「用户确认模式」,发起转账后需收款用户主动在微信里确认收款,确认后即时入账。
- 影响:提现审核类流程被拆成 用户申请 → 后台审核通过 → 还要触达用户回来点「确认收款」→ 用户 24 小时内不确认,单据自动关闭、资金退回商户出资账户。
- 社区里大量吐槽点:审核和到账之间出现时差;用户不点确认就收不到钱;用户取消确认后钱进入冻结/退回。
前提与不适用场景:以下内容来自微信支付官方文档 + 微信开放社区问答,字段/状态/报错原文均来自文档与社区帖,未经本地实测。适用于商家转账(用户确认模式)接入与排障,包括商户、服务商、平台收付通。不适用于旧版「发起即到账」流程;资金最终结果以查单/回调为准,不能只看发起接口 HTTP 200。限额、场景可用性以商户平台当前口径为准。
单据状态机怎么读
发起转账:
- 普通商户:
POST /v3/fund-app/mch-transfer/transfer-bills - 平台收付通/服务商:
POST /v3/ecommerce/mch-transfer/transfer-bills
关键必填:appid、out_bill_no(商户单号,数字+大小写字母,≤32)、transfer_scene_id(转账场景ID)、openid(收款用户)、transfer_amount(单位:分)、transfer_remark(≤32字符)、transfer_scene_report_infos(转账场景报备信息);notify_url 可选。
应答返回 state(单据状态)与 package_info(拉起确认收款用,仅 WAIT_USER_CONFIRM 时返回)。
状态流转:
ACCEPTED(已受理)→ PROCESSING(锁定资金中)→ WAIT_USER_CONFIRM(待收款用户确认)→ TRANSFERING(转账中,可再次拉起确认)→ SUCCESS / FAIL;CANCELING → CANCELLED。
用户确认模式判定:接口 HTTP 200 且 state=WAIT_USER_CONFIRM 才算「创建转账单成功」,可以引导用户确认收款。HTTP 200 不代表已到账。
场景报备为什么总报 PARAM_ERROR
报错原文对照:
{"code":"PARAM_ERROR","message":"需传入转账场景报备信息,请检查"}未传入完整且对应的转账场景报备信息,请根据接口文档检查你尚未获取该转账场景(错误码TRANSFER_SCENE_INVALID)→ 场景ID填错或没申请。TRANSFER_SCENE_UNAVAILABLE→ 该场景暂不可用。
要点:
transfer_scene_id必须先在「商户平台-产品中心-商家转账-产品设置」申请;每个商户最多申请 3 种场景;首个场景为默认场景,不传则用默认。transfer_scene_report_infos要按场景「固定值」填。常见坑:必须填两行(如 活动名称 + 奖励说明);info_type是固定值(≤15字符),不能自己造;info_content描述必须照文档表格口径。user_recv_perception(用户收款感知)与transfer_scene_report_infos是两码事:前者是用户看到的收款原因,不填用场景默认,填了必须匹配该场景允许的内容;社区有人把「现金奖励」硬塞进不匹配的场景,直接PARAM_ERROR。- 服务商/平台收付通场景下字段名为
transfer_scene_id+transfer_scene_report_infos,语义相同。
接口 200 不等于到账:24 小时退回、金额与限额边界
HTTP 200 只说明请求被受理,真正能引导用户确认收款的条件是 state=WAIT_USER_CONFIRM。用户 24 小时内不确认,单据自动关闭,资金退回商户出资账户。用户取消确认后,钱进入冻结/退回。
金额与姓名的边界:
- 金额 ≥2000 元:必须传
user_name(收款用户姓名),且需用微信支付公钥加密传入。微信会校验收款人与姓名是否一致。 - 金额在 0.3(含)~2000(含)元之间:可选传姓名。
- 金额 <0.3 元:不支持传姓名,传了直接报错。
限额以商户平台当前口径为准,新旧文档不一致:
- 单笔默认 200 元,可调范围 0.1–200 元(部分场景单笔上限不同,如佣金/报销类可到 20000 元)。
- 单日向同一用户默认 2000 元(有文档口径为 2 万,以平台为准)。
- 单日转账额度默认 5 万元,可申请提高。
- 单月额度默认 3000 万元,不支持修改。
- 设置路径:商户平台 > 产品中心 > 运营工具 > 商家转账 > 前往功能 > 转账额度。
- 报错原文:
{"code":"INVALID_REQUEST","message":"超过单笔转账上下限,核实产品设置是否准确"}或TRANSFER_QUOTA_EXCEED。
幂等与重试铁律
- 接口返回 HTTP 非 200 时,不要立刻换商户单号重试。换单会重复转账。
- 遇到
SYSTEM_ERROR、ACCEPTED、PROCESSING:一定要用原商户单号 + 原参数重试(例如余额不足补钱后原单重试)。 - 单据到终态(
SUCCESS/FAIL/CANCELLED)后不可再用同单号,重新转账必须换单号。 - 遇到新错误码:先用「商户单号查询转账单」查原单结果,明确为「失败」再换单重试。
- 错误码
ALREADY_EXISTS:订单已存在,说明单据已到终态,查单确认。 FREQUENCY_LIMIT_EXCEED / RATELIMIT_EXCEEDED:频率超限,降低频率后原参数重试。
撤销:CANCELING → CANCELLED 是异步的
- 用户确认收款前均可撤销:
ACCEPTED / PROCESSING / WAIT_USER_CONFIRM / TRANSFERING。 - 撤销是异步的:
CANCELING→CANCELLED,返回成功只代表撤销请求已受理,最终以查单为准。 - 用户 24 小时未领取自动过期关闭后无法撤销;终态单据不能撤销。
失败原因枚举(查单/回调里会看到)
NAME_NOT_CORRECT:收款人姓名校验不通过。TRANSFER_RISK:该笔转账可能存在风险,被微信拦截。TRANSFER_SCENE_INVALID / TRANSFER_SCENE_UNAVAILABLE:场景ID错/不可用。MERCHANT_NOT_CONFIRM:商户员工(转账验密人)超时未验密。MERCHANT_REJECT:商户员工驳回转账。MCH_CANCEL:商户撤销付款。
回调与拉起确认收款
- 发起转账时传
notify_url,单据到终态SUCCESS/FAIL/CANCELLED时微信推送回调。处理:先验签、先回 HTTP 200 再异步处理业务,避免应答超时。 - 拉起确认收款:用发起转账返回的
package_info;拉起的appid/openid必须与发起时完全一致(否则报「商家APPID错误」/「收款微信号不一致」);移动应用用 APP 调起用户确认收款,公众号/小程序用 JSAPI 调起;拉起前先查单确认state仍是WAIT_USER_CONFIRM。
官方文档链接
- 产品介绍:https://pay.weixin.qq.com/doc/v3/merchant/4012064660
- 开发指引:https://pay.weixin.qq.com/doc/v3/merchant/4012715211
- 发起转账:https://pay.weixin.qq.com/doc/v3/merchant/4012716434
- 业务示例代码:https://pay.weixin.qq.com/doc/v3/merchant/4018940876
- 常见问题:https://pay.weixin.qq.com/doc/v3/merchant/4013778940
- 设置转账额度:https://pay.weixin.qq.com/doc/v3/merchant/4013747667
- 订单失败原因说明:https://pay.wechatpay.cn/doc/v3/merchant/4013774966
- 开发安全最佳实践:https://pay.weixin.qq.com/doc/v3/merchant/4012065173
材料来源与实测状态
材料来源:微信支付官方文档 + 微信开放社区问答。以上字段、状态、报错原文、限额数字均来自上述材料,未经本地实测。限额、场景可用性、接口行为以商户平台和官方文档当前口径为准。