php微信商家转账回调通知数据解密

php 复制代码
<?php
namespace fast\wx;

class AesUtil {
    /**
     * AES key(PEM格式)
     *
     * @var string
     */
    private $aesKey;
    
    const KEY_LENGTH_BYTE = 32;
    const AUTH_TAG_LENGTH_BYTE = 16;
    
    /**
     * 错误信息
     * @var string
     */
    private $errorInfo = '';
    
    /**
     * Constructor
     * 
     * @param string $aesKey 32字节的原始AES密钥(非PEM格式)
     * @throws Exception
     */
    public function __construct($aesKey) {
        // 验证密钥是否为空
        if (empty($aesKey)) {
            throw new \Exception('AES密钥不能为空');
        }
        
        // 验证密钥长度
        $keyLength = strlen($aesKey);
        if ($keyLength != self::KEY_LENGTH_BYTE) {
            throw new \Exception('无效的ApiV3Key,长度应为' . self::KEY_LENGTH_BYTE . '个字节,实际为' . $keyLength . '个字节');
        }
        $this->aesKey = $aesKey;
    }
    
    /**
     * 获取最后发生的错误信息
     * 
     * @return string
     */
    public function getLastError() {
        return $this->errorInfo;
    }
    
    /**
     * 解密回调数据(修复密钥格式后)
     */
    public function decryptToString($associatedData, $nonceStr, $ciphertext) {
        $this->errorInfo = '';
        
        // Base64解码密文
        $decodedCiphertext = base64_decode($ciphertext);
        if ($decodedCiphertext === false) {
            $this->errorInfo = '密文Base64解码失败';
            return false;
        }
        // 分离密文和认证标签
        $ctext   = substr($decodedCiphertext, 0, -self::AUTH_TAG_LENGTH_BYTE);
        $authTag = substr($decodedCiphertext, -self::AUTH_TAG_LENGTH_BYTE);
 
        // 使用PEM格式密钥解密
        $result = openssl_decrypt(
            $ctext,
            'aes-256-gcm',
            $this->aesKey,
            OPENSSL_RAW_DATA,
            $nonceStr,
            $authTag,
            $associatedData
        );
        if ($result === false) {
            $errors = [];
            while ($error = openssl_error_string()) {
                $errors[] = $error;
            }
            $this->errorInfo = 'OpenSSL解密失败: ' . implode('; ', $errors);
            return false;
        }
        
        return $result;
    }
    

}
?>

调用

php 复制代码
public function batches_notify(){
        $encryptMsg = file_get_contents('php://input');
        // 记录原始数据
        file_put_contents(ROOT_PATH.'/runtime/batches_notify'.date('Ym').'.txt', 
            '原始数据: '.$encryptMsg.PHP_EOL.'时间: '.date('Y-m-d H:i:s').PHP_EOL.PHP_EOL, FILE_APPEND);
            
        // $encryptMsg = '{"id":"a9102053-a245-5376-804e-2ae543187dbc","create_time":"2025-09-28T13:56:55+08:00","resource_type":"encrypt-resource","event_type":"MCHTRANSFER.BATCH.FINISHED","summary":"商家转账批次完成通知","resource":{"original_type":"mch_payment","algorithm":"AEAD_AES_256_GCM","ciphertext":"2obO8Q5Jl9yJZwEo+Cv5tvH65CST1pgrPSGKafizGWTKCwdK08BJLcAyrN9/coOeeb/YTbSUovoy6kpDBv0S/w3HZa3ASj/iUexKv26WMqk7uabUsWQVHxHcs/mm5jrCTnx+4V7CYfacinUx9iLieF5pczpJ38u6sQY3YQ6x7DA5kFSmtgtfwfHEGBXG84kZmlkmYwpt778kI9h85X497o48mbb/gkpife4l9A==","associated_data":"mch_payment","nonce":"lqe0lcxxxxxx"}}';

        
        $data = json_decode($encryptMsg, true);
        if (json_last_error() !== JSON_ERROR_NONE) {
            return json(['code'=>'FAIL','message'=>'JSON解析错误']);
        }
    
        // 验证数据格式
        if (!isset($data['resource']['ciphertext'])) {
            return json(['code'=>'FAIL','message'=>'回调数据格式不正确']);
        }
        $wxpay = Config::get('site.wxpay');
        $wxpayV3Key = $wxpay['wxpayV3Key'] ?? '';//APIv3密钥
        // 解密数据
        $aesUtil = new AesUtil($wxpayV3Key);
        $decryptedData = $aesUtil->decryptToString(
            $data['resource']['associated_data'] ?? '',
            $data['resource']['nonce'] ?? '',
            $data['resource']['ciphertext']
        );
        
        if ($decryptedData === false) {
            return json(['code'=>'FAIL','message'=>'数据解密失败']);
        }
    
        // 解析业务数据
        $transferData = json_decode($decryptedData, true);
        if (json_last_error() !== JSON_ERROR_NONE) {
            return json(['code'=>'FAIL','message'=>'业务数据JSON解析错误']);
        }
        // 记录解密数据
        file_put_contents(ROOT_PATH.'/runtime/batches_notify_data'.date('Ym').'.txt', 
            '解密数据: '.$decryptedData.PHP_EOL.'时间: '.date('Y-m-d H:i:s').PHP_EOL.PHP_EOL, FILE_APPEND);
        Db::startTrans();
        try {
            $order_id = $transferData['out_batch_no'] ?? '';
            $batch_id = $transferData['batch_id'] ?? '';
            $tixian = db('tixian')->where('order_id', $order_id)->find();
            
            // 逻辑处理



            
        } catch (\Exception $e) {
            Db::rollback();
            file_put_contents(ROOT_PATH.'/runtime/batches_notify_transfer_error'.date('Ym').'.txt', 
                '错误: '.$e->getMessage().PHP_EOL.'时间: '.date('Y-m-d H:i:s').PHP_EOL.PHP_EOL, FILE_APPEND);
            return json(['code'=>'FAIL','message'=>'系统异常']);
        }
    }
相关推荐
TlSfoward17 小时前
抓包代理链路下的 TLS 指纹变化分析 TLSFOWARD抓包工具
数据库·爬虫·网络协议·搜索引擎·php
—Miss. Z—20 小时前
计算机二级MySQL——简单应用题(存储过程&存储函数)
数据库·oracle·php
故乡de云20 小时前
AWS 添加付款方式失败排查清单:卡片、银行风控与账号状态逐项定位
开发语言·php
梦梦代码精21 小时前
基于ThinkPHP6 + Vue3的家政预约系统全解析:从LBS定位到自动派单的完整实现
java·docker·开源·php·代码规范
郑州光合科技余经理1 天前
家政O2O平台解析:从0搭建上门预约小程序解决方案
android·java·开发语言·前端·小程序·架构·php
lxw18449125141 天前
PHP后端(CI框架方向)面试题库,分为基础必问、MySQL、Redis、工程运维、高阶加分、场景实操六大模块,适配该JD全部考点,附带标准答案
ci/cd·面试·php
郑州光合科技余经理1 天前
家政预约小程序后端源码深度剖析:支付回调实战
java·开发语言·前端·git·小程序·架构·php
weixin_408099672 天前
2026 图片去水印 API 接口完全指南:一键去除图片水印(附 Python/Java/PHP/C# 示例)
java·python·php·图片处理·api调用·图片去水印·石榴智能
王莎莎-MinerU2 天前
MCP 解决的是工具接入,科研 Agent 还缺的是科学证据接口标准化
开发语言·网络·人工智能·深度学习·pdf·c#·php
AC赳赳老秦2 天前
OpenClaw 采集任务日志审计:全程记录采集行为,满足合规溯源与企业审计要求
java·大数据·python·数据挖掘·数据分析·php·openclaw