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;}}
?>
调用
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'=>'系统异常']);}}