退款异常(REFUND.ABNORMAL)通知(JSON)
退款异常,退款到银行发现用户的卡作废或者冻结了,导致原路退款银行卡失败,可前往【平台—>交易中心】,手动处理此笔退款
注意:
- 同样的通知可能会多次发送给商户系统。商户系统必须能够正确处理重复的通知。 推荐的做法是,当商户系统收到通知进行处理时,先检查对应业务数据的状态,并判断该通知是否已经处理。如果未处理,则再进行处理;如果已处理,则直接返回结果成功。在对业务数据进行状态检查和处理之前,要采用数据锁进行并发控制,以避免函数重入造成的数据混乱。
- 如果在所有通知频率后没有收到微信侧回调,商户应调用查询订单接口确认订单状态。
- 特别提醒:商户系统对于开启结果通知的内容一定要做签名验证,并校验通知的信息是否与商户侧的信息一致,防止数据泄露导致出现“假通知”,造成资金损失。
- 商户退款完成后,微信会把相关退款结果和用户信息发送给清算机构,清算机构需要接收处理后返回应答成功,然后继续给异步通知到下游从业机构。
- 对后台通知交互时,如果微信收到商户的应答不符合规范或超时,微信认为通知失败,微信会通过一定的策略定期重新发起通知,尽可能提高通知的成功率,但微信不保证通知最终能成功。(通知频率为15s/15s/30s/3m/10m/20m/30m/30m/30m/60m/3h/3h/3h/6h/6h - 总计 24h4m)
请求参数 | 类型 | 描述 |
---|---|---|
headers | object | 通知的头参数 |
Content-Type | string | application/json |
Request-ID | string | 通知的唯一标识 |
Wechatpay-Nonce | string | 数据签名使用的随机串 |
Wechatpay-Serial | string | 微信支付公钥ID/平台证书序列号 |
Wechatpay-Signature | string | 签名串 |
Wechatpay-Signature-Type | string | 签名算法WECHATPAY2-SHA256-RSA2048 枚举值 |
Wechatpay-Timestamp | string | 时间戳 |
body | object | 通知的JSON 数据结构 |
id | string | 通知的唯一ID |
create_time | string | 通知创建的时间 |
event_type | string | 通知的类型REFUND.ABNORMAL 枚举值 |
resource_type | string | 通知的资源数据类型 |
summary | string | 回调摘要 |
resource | object | 通知资源数据 |
algorithm | string | 对数据进行加密的加密算法AEAD_AES_256_GCM 枚举值 |
associated_data | string | 数据加密的附加数据 |
nonce | string | 加密使用的随机串 |
ciphertext | string | 加密后的密文数据 |
original_type | string | 原始回调类型 |
sp_mchid | string | 服务商户号,由微信支付生成并下发 。 |
sub_mchid | string | 子商户的商户号,由微信支付生成并下发。 |
out_trade_no | string | 返回的商户订单号 |
transaction_id | string | 微信支付订单号 |
out_refund_no | string | 商户退款单号 |
refund_id | string | 微信退款单号 |
refund_status | string | 退款状态SUCCESS | CLOSED | ABNORMAL 枚举值之一 |
success_time | string | 1、退款成功时间 |
user_received_account | string | 取当前退款单的退款入账方。 1、退回银行卡:{银行名称}{卡类型}{卡尾号} 2、退回支付用户零钱: 支付用户零钱 3、退还商户: 商户基本账户、商户结算银行账户 4、退回支付用户零钱通:支付用户零钱通 5、退回用户经营账户:用户经营账户 6、退回支付用户银行电子账户:支付用户银行电子账户 7、退回支付用户零花钱:支付用户零花钱 8、退回支付用户来华零钱包:支付用户来华零钱包 9、退回企业支付商户:企业支付商户 |
amount | object | 金额信息 |
total | number | 订单总金额,单位为分,只能为整数 |
refund | number | 退款金额,币种的最小单位,只能为整数,不能超过原订单支付金额,如果有使用券,后台会按比例退。 |
payer_total | number | 用户实际支付金额,单位为分,只能为整数 |
payer_refund | number | 退款给用户的金额,不包含所有优惠券金额 |
mchid | string | 普通商户的商户号,由微信支付生成并下发。 |
refund_account | string | 电商平台垫资退款专用参数REFUND_SOURCE_PARTNER_ADVANCE | REFUND_SOURCE_SUB_MERCHANT 枚举值之一 |
individual_auth_id | string | 个人收款的微信支付账户,微信用户在该平台的标志 |
- 平台收付通-个人收款场景会返回individual_auth_id字典
php
// 使用Psr标准规范,示例如何处理(取值、验签、解密)「回调通知」事件,WebServer不同,用法略有差异,供参考实现。
function webhookProcessor(\Psr\Http\Message\RequestInterface $request,
array $platformPublicKeyMap, string $apiv3Key): array {
if (!\count($platformPublicKeyMap)) {
throw new \WeChatPay\Exception\InvalidArgumentException('平台证书或者平台公钥数组不能为空');
}
if (\strlen($apiv3Key) !== 32) {
throw new \WeChatPay\Exception\InvalidArgumentException('APIV3密钥为32字节,长度不对');
}
if (!($request->hasHeader(\WeChatPay\WechatpayNonce)
&& $request->hasHeader(\WeChatPay\WechatpaySerial)
&& $request->hasHeader(\WeChatPay\WechatpaySignature)
&& $request->hasHeader(\WeChatPay\WechatpayTimestamp))) {
throw new \WeChatPay\Exception\InvalidArgumentException('通知的头参数缺失必要参数');
}
// 检查通知的时间偏移量,允许5分钟之内的偏移
[$inWechatpayTimestamp] = $request->getHeader(\WeChatPay\WechatpayTimestamp);
if (\WeChatPay\MAXIMUM_CLOCK_OFFSET < \abs(
\WeChatPay\Formatter::timestamp() - (int)$inWechatpayTimestamp)) {
throw new \WeChatPay\Exception\InvalidArgumentException('通知头参数的时间偏移量超过可信阈值');
}
// 检查通知的平台证书/平台公钥实例是否存在
[$inWechatpaySerial] = $request->getHeader(\WeChatPay\WechatpaySerial);
if (!\array_key_exists($inWechatpaySerial, $platformPublicKeyMap)) {
throw new \WeChatPay\Exception\InvalidArgumentException('通知头参数的证书序列号/公钥ID本地不存在');
}
// 验证通知的数据签名
[$inWechatpaySignature] = $request->getHeader(\WeChatPay\WechatpaySignature);
[$inWechatpayNonce] = $request->getHeader(\WeChatPay\WechatpayNonce);
$inBody = (string)$request->getBody();
if (!\WeChatPay\Crypto\Rsa::verify(
\WeChatPay\Formatter::joinedByLineFeed($inWechatpayTimestamp, $inWechatpayNonce, $inBody),
$inWechatpaySignature, $platformPublicKeyMap[$inWechatpaySerial]
)) {
throw new \WeChatPay\Exception\InvalidArgumentException('通知头参数的数据签名校验未通过');
}
// 转换通知的JSON文本消息为PHP Array数组
$inBodyArray = (array)\json_decode($inBody, true);
// 使用PHP7+的数据解构语法,从Array中解构并赋值变量
['resource' => [
'ciphertext' => $ciphertext,
'nonce' => $nonce,
'associated_data' => $aad
]] = $inBodyArray;
// 加密文本消息解密,有可能解密异常(eg: 平台探测流量)会抛 \UnexpectedValueException
$inBodyResource = \WeChatPay\Crypto\AesGcm::decrypt($ciphertext, $apiv3Key, $nonce, $aad);
// 把解密后的文本转换为PHP Array数组
$inBodyResourceArray = (array)\json_decode($inBodyResource, true);
// 把解密后的数组定义为'original_data'函数返回
$inBodyArray['resource']['original_data'] = $inBodyResourceArray;
return [
'headers' => $request->getHeaders(),
'body' => $inBodyArray
];
}
// do your business
// ...
// ...
$json = \json_encode([
'code' => 'SUCCESS',
'message' => 'OK'
]);
应答规范 | 类型 | 描述 |
---|---|---|
status | number | HTTP 状态码20X 4XX 5XX 枚举值之一 |
body | object | 应答的JSON 数据结构 |
code | string | 业务处理状态码SUCCESS | FAIL 枚举值之一 |
message | string | 业务处理附加信息 |
参阅 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档 官方文档