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'=>'系统异常']);
        }
    }
相关推荐
郑州光合科技余经理6 小时前
海外版外卖加盟:总站与分站配送规则怎么分开管
java·开发语言·前端·后端·uni-app·php·ai编程
专业程序开发源6 小时前
springboot简历管理系统81389-计算机课程设计、毕业设计
java·vue.js·spring boot·后端·spring·php·课程设计
专业程序开发源6 小时前
springboot社区养老系统44071-计算机课程设计、毕业设计
vue.js·spring boot·后端·python·django·php·课程设计
星光开发者9 小时前
基于决策树随机森林算法的小说推荐系统的设计与实现-计算机毕设【课程设计】78485
vue.js·算法·微信小程序·django·php·课程设计·express
专业程序开发源12 小时前
SSM校园拍摄交流服务平台36936-计算机课程设计、毕业设计
java·spring boot·后端·python·elasticsearch·php·课程设计
星夜夏空9913 小时前
网络编程(5)—— Reactor实现(v3)
服务器·网络·php
小此方16 小时前
Linux网络(二十):TCP拥塞控制与延迟应答详解:从拥塞窗口到TCP性能优化,理解TCP与UDP的效率差异
linux·网络·php
专业程序开发源17 小时前
springboot高校学生社团管理系统74810-计算机课程设计、毕业设计
java·vue.js·spring boot·后端·spring·php·课程设计
AC赳赳老秦17 小时前
OpenClaw 与 FineBI 联动方案:公开数据自动采集与实时业务分析看板实践
大数据·开发语言·python·php·finebi·deepseek·openclaw
AIGC小尼17 小时前
8G 显存零基础本地部署 AI 漫剧全流程|ComfyUI+Wan2.2+FFmpeg 离线成片完整方案(含代码 / 指令 / 排坑)
人工智能·ffmpeg·php·comfyui·ai漫剧