账户中心 -> API安全 查询到 $platformCertificateSerial = config("wechatpay.payment.platform_cert_serial"); // // 从本地文件中加载「微信支付公钥」,用来验证微信支付应答的签名 // $platformPublicKeyFilePath = 'file://' . config("wechatpay.payment.public_key_path"); // $twoPlatformPublicKeyInstance = Rsa::from($platformPublicKeyFilePath, Rsa::KEY_TYPE_PUBLIC); // // 「微信支付公钥」的「微信支付公钥ID」 // // 需要在 商户平台 -> 账户中心 -> API安全 查询 // $platformPublicKeyId = config("wechatpay.payment.public_key_id"); // 构造一个 APIv3 客户端实例 $instance = Builder::factory([ 'mchid' => $merchantId, 'serial' => $merchantCertificateSerial, 'privateKey' => $merchantPrivateKeyInstance, 'certs' => [ $platformCertificateSerial => $onePlatformPublicKeyInstance, // $platformPublicKeyId => $twoPlatformPublicKeyInstance, ], "secret" => config("wechatpay.payment.api3_key") ]); return $instance; } /** * 商家转账 * @return void */ public function mchTransfer(string $trade_no, string $scene_id, string $openid, int $amount, string $remark, string $notify_url, array $transfer_scene_report_infos = []): array { // 发送请求 if (empty(count($transfer_scene_report_infos))) { $transfer_scene_report_infos = [ [ 'info_type' => '岗位类型', // 固定值 'info_content' => '商家/商户', // 示例值 ], [ 'info_type' => '报酬说明', // 固定值 'info_content' => '商家/商户提现', // 示例值 ], ]; } // try { $appid = config("wechat.official_account.default.app_id"); $data = [ "appid" => $appid, "out_bill_no" => $trade_no, "transfer_scene_id" => (string) $scene_id, "openid" => $openid, "transfer_amount" => $amount, "transfer_remark" => $remark, "notify_url" => $notify_url, "transfer_scene_report_infos" => $transfer_scene_report_infos ]; Log::info("转账数据", $data); $instance = $this->newClient(); $resp = $instance->chain('v3/fund-app/mch-transfer/transfer-bills')->post([ "json" => $data ]); $res = json_decode($resp->getBody(), true); return ["code" => 0, "err_msg" => "", "data" => $res]; // } catch (\Exception $e) { // // 进行异常捕获并进行错误判断处理 // Log::info($e->getMessage()); // if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) { // $r = $e->getResponse(); // $res = json_decode($r->getBody()); // return ["code" => 1, "err_msg" => $r->getBody(), "data" => null]; // } // return ["code" => 1, "err_msg" => $e->getMessage(), "data" => null]; // } } /** * 商家转账回调 */ public function mchTransferCallback($headers, $rawBody) { // if (!$this->verifySignature($headers, $rawBody)) { // Log::error('微信回调验签失败', ['headers' => $headers]); // return null; // 返回 500 让微信重试 // } $decryptData = $this->decryptToString($rawBody); // 4. 解密回调数据 // $decryptData = $this->decryptNotifyData($rawBody); // if (!$decryptData) { // Log::error('数据解密失败'); // return null; // } return $decryptData; } /** * 验证签名(RSA-SHA256) */ private function verifySignature(array $headers, array $body): bool { // 检查必要头是否存在 if ( empty($headers['wechatpay-timestamp']) || empty($headers['wechatpay-nonce']) || empty($headers['wechatpay-signature']) || empty($headers['wechatpay-serial']) ) { Log::error('请求头参数缺失'); return false; } // 检查序列号是否匹配(可选的额外校验) // $expectedSerial = $this->getPlatformCertSerial(); // if ($headers['serial'] !== $expectedSerial) { // return false; // } // 构造验签名串:timestamp + \n + nonce + \n + body + \n $signStr = $headers['wechatpay-timestamp'] . "\n" . $headers['wechatpay-nonce'] . "\n" . json_encode($body) . "\n"; // 加载微信支付平台公钥 $publicKey = openssl_pkey_get_public('file://' . config("wechatpay.payment.platform_cert_path")); if (!$publicKey) { Log::error('加载平台证书失败'); return false; } // Base64 解码签名 $signature = base64_decode($headers['wechatpay-signature']); // 验证签名 $result = openssl_verify($signStr, $signature, $publicKey, OPENSSL_ALGO_SHA256); Log::info("验签结果", ["res" => $result]); return $result === 1; } /** * AES-256-GCM 解密回调数据 */ private function decryptNotifyData(array $data): ?array { // $data = json_decode($rawBody, true); if (!isset($data['resource'])) { Log::info("缺少参数 resource"); return null; } $resource = $data['resource']; $nonce = $resource['nonce']; $associatedData = $resource['associated_data'] ?? ''; // 1. 检查原始 ciphertext 的类型和长度(它应该是 Base64 字符串) $originalCiphertext = $resource['ciphertext']; Log::info('原始 ciphertext 信息', [ 'type' => gettype($originalCiphertext), // 应该是 string 'length' => strlen($originalCiphertext), // 通常是一个正整数,如 344 'is_base64' => base64_encode(base64_decode($originalCiphertext, true)) === $originalCiphertext, // 应该是 true ]); // 2. 检查一次解码后的二进制数据长度 $binaryCiphertext = base64_decode($originalCiphertext); Log::info('解码后二进制密文长度', [ 'binary_length' => strlen($binaryCiphertext), // 通常会是 16 的倍数 ]); // 3. 再次确认你的 APIv3 密钥是否是 32 字节 $apiV3Key = config("wechatpay.payment.api3_key"); Log::info('APIv3 密钥长度', [ 'key_length' => strlen($apiV3Key), // 必须输出 32 ]); Log::info("解密数据", ["ciphertext" => $binaryCiphertext, "apiV3Key" => $apiV3Key, "nonce" => $nonce, "associatedData" => $associatedData]); // PHP 7.1+ 原生支持 AES-256-GCM $decrypted = AesGcm::decrypt( $binaryCiphertext, // Base64解码后的密文(包含tag) $apiV3Key, $nonce, $associatedData ); if ($decrypted === false) { return null; } return json_decode($decrypted, true); } public function decryptToString($data) { try { $resource = $data['resource']; $nonceStr = $resource['nonce']; $ciphertext = $resource['ciphertext']; $associatedData = $resource['associated_data'] ?? ''; $apiV3Key = config("wechatpay.payment.api3_key"); $ciphertext = base64_decode($ciphertext); if (strlen($ciphertext) <= 16) { return false; } if (in_array('aes-256-gcm', openssl_get_cipher_methods())) { $ctext = substr($ciphertext, 0, -16); $authTag = substr($ciphertext, -16); $res = openssl_decrypt( $ctext, 'aes-256-gcm', $apiV3Key, OPENSSL_RAW_DATA, $nonceStr, $authTag, $associatedData ); return json_decode($res, true); } return null; } catch (Exception $e) { $this->getError($e); return null; } } /** * 商家批量转账到零钱 */ public function transferBatches(string $trade_no, string $trade_no2, string $openid, int $amount, string $remark): array { // try { $appid = config("wechat.official_account.default.app_id"); $data = [ "appid" => $appid, "out_batch_no" => $trade_no, "batch_name" => $remark, "batch_remark" => $remark, "total_amount" => $amount, "total_num" => 1, "transfer_detail_list" => [ [ "out_detail_no" => $trade_no2, "transfer_amount" => $amount, "transfer_remark" => $remark, "openid" => $openid, ] ] ]; Log::info("转账到零钱数据", $data); $instance = $this->newClient(); $resp = $instance->chain('v3/transfer/batches')->post([ "json" => $data ]); $res = json_decode($resp->getBody(), true); dd($res); // } catch (\Exception $e) { // // 进行异常捕获并进行错误判断处理 // Log::info($e->getMessage()); // if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) { // $r = $e->getResponse(); // $res = json_decode($r->getBody()); // return ["code" => 1, "err_msg" => $r->getBody()]; // } // return ["code" => 1, "err_msg" => $e->getMessage()]; // } } /** * 转账银行卡 * @return void */ public function bankTransfer() { } }