代码 微信支付商家转账(用户确认模式):接口 200 不等于到账,场景报备 PARAM_ERROR 与幂等重试的坑

2026-10-16 09:01:54

微信支付商家转账用户确认模式:状态机、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。

官方文档链接

材料来源与实测状态

材料来源:微信支付官方文档 + 微信开放社区问答。以上字段、状态、报错原文、限额数字均来自上述材料,未经本地实测。限额、场景可用性、接口行为以商户平台和官方文档当前口径为准。

推荐文章

程序员茄子在线接单