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'=>'系统异常']);
        }
    }
相关推荐
weixin199701080161 天前
【保姆级教程】淘宝/天猫商品详情 API(item_get)接入指南:Python/Java/PHP 调用示例与 JSON 返回值解析
java·python·php
Data_Journal1 天前
使用Python lxml轻松进行网络爬取
开发语言·php
阿洛学长1 天前
CSDN、掘金、简书博客文章如何转为Markdown?
运维·数据库·架构·php·持续部署
源远流长jerry1 天前
Linux 网络虚拟化深度解析:从 veth 设备对到容器网络实战
linux·运维·服务器·网络·性能优化·php
阳明山水2 天前
零售销量预测为何选LightGBM
人工智能·机器学习·微信·微信公众平台·微信开放平台
Java源头2 天前
PHP 身份证二要素检测
开发语言·php
yoyo_zzm2 天前
PHP vs Java:后端语言终极选择指南
java·spring boot·后端·架构·php
yoyo_zzm2 天前
五大编程语言对比:PHP、C、C++、C#、易语言
c语言·c++·php
赛特·亮2 天前
利用WTAPI(WeChatapi)-robot-go 项目解析与实战指南
微信·面试·golang
largecode2 天前
打电话时,怎么让号码显示自己的品牌名称?办理号码认证服务流程
笔记·百度·微信·课程设计·微信公众平台·facebook·新浪微博