非小程序及代扣投诉订单如何接入并通过API监测?
结论
非小程序支付和代扣交易产生的投诉,只要已经进入微信支付的“支付交易投诉”体系,通常都可以通过“消费者投诉 2.0”接口监测,无需针对小程序、H5、Native、App 或代扣场景分别接入。
建议同时使用两种方式:
- 配置投诉通知回调,实时接收新增投诉和状态变更。
- 定时调用投诉单列表接口进行补偿查询,避免因回调失败而漏单。
需要注意,页面中可见的数据未必全部向接口开放。如果某类投诉没有出现在 GET /v3/merchant-service/complaints-v2 的查询结果中,应先确认该投诉是否属于消费者投诉 2.0 的开放范围、是否归属于当前商户号,以及当前账号是否具备相应的接口权限。对于尚未开放 API 的页面数据,修改查询参数也无法获取,不建议通过抓取管理后台页面解决。
接入前提
接入前需要准备:
- 商户号
mchid - 商户 API 证书及私钥
- API v3 密钥
- 微信支付平台证书或平台公钥
- 可通过公网 HTTPS 访问的回调地址
- 当前商户号对应的投诉接口权限
如果使用服务商模式,还要确认投诉实际归属于服务商商户号还是特约商户号。调用查询接口时使用的身份必须与投诉归属关系一致,否则可能出现“页面可见、接口查不到”的情况。
相关接口
消费者投诉 2.0 的常用接口包括:
GET /v3/merchant-service/complaints-v2
GET /v3/merchant-service/complaints-v2/{complaint_id}
POST /v3/merchant-service/complaint-notifications
DELETE /v3/merchant-service/complaint-notifications
这些接口分别用于:
- 分页查询投诉单
- 查询投诉单详情
- 创建投诉通知回调地址
- 删除投诉通知回调地址
具体的请求参数和字段应以接入时的微信支付官方 API 文档为准。需要特别确认日期范围、分页限制,以及服务商模式下的商户号参数。
第一步:配置投诉通知回调
先向微信支付登记一个 HTTPS 回调地址:
POST /v3/merchant-service/complaint-notifications
Authorization: WECHATPAY2-SHA256-RSA2048 ...
Accept: application/json
Content-Type: application/json
Wechatpay-Serial: PLATFORM_CERTIFICATE_SERIAL
{
"url": "https://merchant.example.com/wechatpay/complaint/notify"
}
对应的 curl 请求如下:
curl -X POST \
'https://api.mch.weixin.qq.com/v3/merchant-service/complaint-notifications' \
-H 'Authorization: WECHATPAY2-SHA256-RSA2048 ...' \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-H 'Wechatpay-Serial: PLATFORM_CERTIFICATE_SERIAL' \
-d '{
"url": "https://merchant.example.com/wechatpay/complaint/notify"
}'
Authorization 不能直接写死,需要使用商户私钥,按照微信支付 API v3 的签名规则动态生成。生产环境中更适合使用微信支付官方 SDK 完成签名和验签。
第二步:接收并处理通知
收到投诉通知后,应按以下流程处理:
- 使用应答头中的签名、时间戳、随机串和原始请求体进行验签。
- 使用 API v3 密钥解密通知中的
resource。 - 提取投诉单号等必要信息。
- 调用投诉详情接口获取完整数据。
- 按投诉单号幂等写入。
- 返回成功响应。
通知密文采用 AES-256-GCM 加密。下面仅展示解密部分,不能代替通知签名验证:
import crypto from 'node:crypto';
function decryptResource(apiV3Key, resource) {
const key = Buffer.from(apiV3Key, 'utf8');
const nonce = Buffer.from(resource.nonce, 'utf8');
const associatedData = Buffer.from(resource.associated_data || '', 'utf8');
const ciphertext = Buffer.from(resource.ciphertext, 'base64');
const authTag = ciphertext.subarray(ciphertext.length - 16);
const encryptedData = ciphertext.subarray(0, ciphertext.length - 16);
const decipher = crypto.createDecipheriv('aes-256-gcm', key, nonce);
decipher.setAAD(associatedData);
decipher.setAuthTag(authTag);
const plaintext = Buffer.concat([
decipher.update(encryptedData),
decipher.final()
]);
return JSON.parse(plaintext.toString('utf8'));
}
不要在验签前信任回调内容或将其写入数据库。即使解密成功,也不能证明请求一定来自微信支付。
第三步:定时补偿查询
回调可能因网络故障、服务发布或处理超时而失败,因此还需要定时查询投诉列表:
GET /v3/merchant-service/complaints-v2?begin_date=2026-09-13&end_date=2026-09-14&limit=50&offset=0
Authorization: WECHATPAY2-SHA256-RSA2048 ...
Accept: application/json
示例:
curl -X GET \
'https://api.mch.weixin.qq.com/v3/merchant-service/complaints-v2?begin_date=2026-09-13&end_date=2026-09-14&limit=50&offset=0' \
-H 'Authorization: WECHATPAY2-SHA256-RSA2048 ...' \
-H 'Accept: application/json'
轮询程序需要处理分页,并在本地保存同步游标或最近一次成功同步的时间。为避免因时间边界而漏单,可以让相邻查询窗口保留少量重叠,再按投诉单号去重。
监测流程可以设计为:
投诉通知到达
-> 验签并解密
-> 获取投诉单号
-> 查询投诉详情
-> 幂等写入数据库
-> 触发告警或工单
定时补偿任务
-> 查询最近一段时间的投诉列表
-> 遍历所有分页
-> 按投诉单号去重
-> 更新投诉状态
-> 补发遗漏告警
接口查不到时如何排查
如果后台页面中存在投诉,但列表接口没有返回,可以依次检查以下几项。
商户号是否一致
支付交易可能由另一个商户号、特约商户号或服务商体系发起。页面账号可以查看投诉,不代表当前用于签名的商户号具有相同的数据权限。
是否属于同一投诉产品
确认页面展示的是“支付交易投诉”,而不是小程序平台申诉、违规处理、风险处置或其他业务模块。不同产品后台的数据不一定共用消费者投诉接口。
查询条件是否覆盖
检查日期范围、分页偏移量,以及服务商场景下的被诉商户条件。不能只查询第一页,也不能仅按原支付订单的时间判断投诉发生时间。
接口权限是否开通
确认商户平台已经开通消费者投诉相关接口权限,并检查商户证书、API v3 密钥和平台证书是否配置正确。
是否确实未开放 API
如果投诉归属的商户号、查询条件和接口权限都没有问题,但该投诉仍然只能在页面中看到,应向微信支付商户支持确认这类页面数据是否开放 API。如果尚未开放,只能通过管理后台处理,无法用消费者投诉接口替代。
注意事项
- 不能只依赖回调,需要保留定时补偿查询。
- 投诉通知可能重复发送,数据库写入和告警都要具备幂等性。
- API v3 密钥必须为 32 字节,并应保存在密钥管理系统中。
- 日志中不要记录 API v3 密钥、商户私钥、完整身份证号、手机号或完整的回调明文。
- 平台证书可能轮换,验签程序需要支持证书更新。
- 投诉单状态可能多次变化,已经入库不等于已经处理完成。
- API 返回的支付场景、订单号等字段可能因投诉来源不同而缺失,进行业务关联时应允许字段为空。
- 不建议通过自动登录、页面抓取或模拟管理后台接口来监测投诉,这些方式在稳定性和合规性方面都有风险。
实际接入可以采用“通知回调实时发现、列表接口定时补偿、详情接口更新状态”的方式。如果经过核实,特定的非小程序或代扣投诉不在消费者投诉 2.0 的开放范围内,就无法只依靠公开 API 完成监测,需要继续通过商户平台查看,或等待相应能力开放。
备注:内容仅供参考。
