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 天前
Obsidian + Claude Code + 微信AI,我把这三个系统缝进了一个软件
微信·obsidian·claude code·molio
Jinkey9 天前
要用户手机号真的是为了打骚扰电话吗?浅谈微信生态会员账号体系与资产合并
后端·微信·微信小程序
两个人的幸福10 天前
Windows 桌面应用自研 PHP 队列(下):完整代码与六大工程化优化
php
BingoGo12 天前
PHP 泛型之殇 泛型 RFC 提案被拒绝
后端·php
JaguarJack12 天前
PHP 泛型之殇 泛型 RFC 提案被拒绝
后端·php
用户30745969820713 天前
PHP 扩展——从入门到理解
php
鹏仔先生14 天前
拷贝漫画APP下载页PHP程序,后台带免费AI写作
php
云水一下14 天前
从零开始学 PHP 系列(一):PHP 的前世今生与开发环境搭建
开发语言·php
xingpanvip14 天前
星盘接口开发文档:本命盘接口指南
android·开发语言·css·php·lua
2601_9618451514 天前
粉笔行测题库|系统班|刷题
网络·百度·微信·微信公众平台·facebook·新浪微博