Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

支付回调getRequestMessage()报Invalid request body错误 #2737

Closed
angelofan opened this issue Aug 26, 2023 · 17 comments
Closed

支付回调getRequestMessage()报Invalid request body错误 #2737

angelofan opened this issue Aug 26, 2023 · 17 comments

Comments

@angelofan
Copy link

我用的环境

  • PHP 版本:8.2
  • overtrue/wechat 版本:"overtrue/laravel-wechat": "^7.2"
  • 是否使用了框架?框架名称:"laravel/framework": "^10.10",

问题及现象

支付回调 getRequestMessage()Invalid request body 错误,从 {laravel_path}\\vendor\\w7corp\\easywechat\\src\\Pay\\Server.php(117) 抛出,此错误可被 try catch 捕获,但是 return ['code' => 'SUCCESS', 'message' => '成功']; 后微信仍然会一直重新发起通知。

BeforeMiddleware中打印的请求日志:

[2023-08-26 10:30:49] local.DEBUG: 请求日志 v1/callback/pay {"ip":"175.24.214.208","url":"{domain}/v1/callback/pay","method":"POST","data":[]} 

抛出的错误日志

日志:

[2023-08-26 10:30:49] local.ERROR: 微信支付回调 自定义处理事件时 {"msg":"Invalid request body.","code":0,"file":"{laravel_path}\\vendor\\w7corp\\easywechat\\src\\Pay\\Server.php","line":117}

堆栈跟踪:

#0 {laravel_path}\\app\\Http\\Controllers\\WeChatController.php(68): EasyWeChat\\Pay\\Server->getRequestMessage()
#1 {laravel_path}\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Controller.php(54): App\\Http\\Controllers\\WeChatController->pay_callback()
#2 {laravel_path}\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\ControllerDispatcher.php(43): Illuminate\\Routing\\Controller->callAction()
#3 {laravel_path}\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Route.php(259): Illuminate\\Routing\\ControllerDispatcher->dispatch()
#4 {laravel_path}\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Route.php(205): Illuminate\\Routing\\Route->runController()
#5 {laravel_path}\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php(799): Illuminate\\Routing\\Route->run()
#6 {laravel_path}\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(141): Illuminate\\Routing\\Router->Illuminate\\Routing\\{closure}()
#7 {laravel_path}\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Middleware\\SubstituteBindings.php(50): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#8 {laravel_path}\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(180): Illuminate\\Routing\\Middleware\\SubstituteBindings->handle()
#9 {laravel_path}\\vendor\\laravel\\framework\\src\\Illuminate\\View\\Middleware\\ShareErrorsFromSession.php(49): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#10 {laravel_path}\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(180): Illuminate\\View\\Middleware\\ShareErrorsFromSession->handle()
#11 {laravel_path}\\vendor\\laravel\\framework\\src\\Illuminate\\Session\\Middleware\\StartSession.php(121): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#12 {laravel_path}\\vendor\\laravel\\framework\\src\\Illuminate\\Session\\Middleware\\StartSession.php(64): Illuminate\\Session\\Middleware\\StartSession->handleStatefulRequest()
#13 {laravel_path}\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(180): Illuminate\\Session\\Middleware\\StartSession->handle()
#14 {laravel_path}\\vendor\\laravel\\framework\\src\\Illuminate\\Cookie\\Middleware\\AddQueuedCookiesToResponse.php(37): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#15 {laravel_path}\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(180): Illuminate\\Cookie\\Middleware\\AddQueuedCookiesToResponse->handle()
#16 {laravel_path}\\vendor\\laravel\\framework\\src\\Illuminate\\Cookie\\Middleware\\EncryptCookies.php(67): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#17 {laravel_path}\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(180): Illuminate\\Cookie\\Middleware\\EncryptCookies->handle()
#18 {laravel_path}\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(116): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#19 {laravel_path}\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php(798): Illuminate\\Pipeline\\Pipeline->then()
#20 {laravel_path}\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php(777): Illuminate\\Routing\\Router->runRouteWithinStack()
#21 {laravel_path}\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php(741): Illuminate\\Routing\\Router->runRoute()
#22 {laravel_path}\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php(730): Illuminate\\Routing\\Router->dispatchToRoute()
#23 {laravel_path}\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Kernel.php(200): Illuminate\\Routing\\Router->dispatch()
#24 {laravel_path}\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(141): Illuminate\\Foundation\\Http\\Kernel->Illuminate\\Foundation\\Http\\{closure}()
#25 {laravel_path}\\app\\Http\\Middleware\\BeforeMiddleware.php(27): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#26 {laravel_path}\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(180): App\\Http\\Middleware\\BeforeMiddleware->handle()
#27 {laravel_path}\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#28 {laravel_path}\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\TrimStrings.php(40): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle()
#29 {laravel_path}\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(180): Illuminate\\Foundation\\Http\\Middleware\\TrimStrings->handle()
#30 {laravel_path}\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize.php(27): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#31 {laravel_path}\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(180): Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize->handle()
#32 {laravel_path}\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\PreventRequestsDuringMaintenance.php(86): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#33 {laravel_path}\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(180): Illuminate\\Foundation\\Http\\Middleware\\PreventRequestsDuringMaintenance->handle()
#34 {laravel_path}\\vendor\\laravel\\framework\\src\\Illuminate\\Http\\Middleware\\HandleCors.php(49): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#35 {laravel_path}\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(180): Illuminate\\Http\\Middleware\\HandleCors->handle()
#36 {laravel_path}\\vendor\\laravel\\framework\\src\\Illuminate\\Http\\Middleware\\TrustProxies.php(39): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#37 {laravel_path}\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(180): Illuminate\\Http\\Middleware\\TrustProxies->handle()
#38 {laravel_path}\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(116): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#39 {laravel_path}\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Kernel.php(175): Illuminate\\Pipeline\\Pipeline->then()
#40 {laravel_path}\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Kernel.php(144): Illuminate\\Foundation\\Http\\Kernel->sendRequestThroughRouter()
#41 {laravel_path}\\public\\index.php(51): Illuminate\\Foundation\\Http\\Kernel->handle()
#42 {main}"

相关代码块

{laravel_path}\vendor\w7corp\easywechat\src\Pay\Server.php(117)

/**
     * @throws InvalidArgumentException
     * @throws RuntimeException
     */
    public function getRequestMessage(?ServerRequestInterface $request = null): \EasyWeChat\Kernel\Message|Message
    {
        $originContent = (string) ($request ?? $this->request)->getBody();
        $attributes = json_decode($originContent, true);

        if (! is_array($attributes)) {
            throw new RuntimeException('Invalid request body.'); //  错误日志定位 -> Server.php(117)
        }

        if (empty($attributes['resource']['ciphertext'])) {
            throw new RuntimeException('Invalid request.');
        }

        $attributes = json_decode(
            AesGcm::decrypt(
                $attributes['resource']['ciphertext'],
                $this->merchant->getSecretKey(),
                $attributes['resource']['nonce'],
                $attributes['resource']['associated_data'],
            ),
            true
        );

        if (! is_array($attributes)) {
            throw new RuntimeException('Failed to decrypt request message.');
        }

        return new Message($attributes, $originContent);
    }

{laravel_path}\app\Http\Controllers\WeChatController.php(68): EasyWeChat\Pay\Server->getRequestMessage()

public function pay_callback()
    {
        $server = app('easywechat.pay')->getServer();

        // 自定义处理事件
        try {
            $message = $server->getRequestMessage(); // 错误日志定位 -> WeChatController.php(68)
            // $message->getEventType() 事件类型
            // - TRANSACTION.SUCCESS 支付成功
            // - REFUND.SUCCESS:退款成功通知
            // - REFUND.ABNORMAL:退款异常通知
            // - REFUND.CLOSED:退款关闭通知
            // $message->out_trade_no 商户订单号
            $evevt_type = $message->getEventType();
            $order_id = $message->out_trade_no;
            Log::info('微信支付回调 自定义处理事件', [
                'evevt_type' => $evevt_type,
                'order_id' => $order_id,
            ]);

            self::handlePayCallback($evevt_type, $order_id); // 处理订单
    
            // 默认返回 ['code' => 'SUCCESS', 'message' => '成功']
            return $server->serve();
        } catch (\Throwable $th) {
            //throw $th;
            $error_info = [
                'msg' => $th->getMessage(),
                'code' => $th->getCode(),
                'file' => $th->getFile(),
                'line' => $th->getLine(),
                'trace' => $th->getTraceAsString(),
            ];
            Log::error('微信支付回调 自定义处理事件时', $error_info);

            // 默认返回 ['code' => 'SUCCESS', 'message' => '成功']
            // 这里不能使用 $server->serve() 返回,否则仍然会报错
            return ['code' => 'SUCCESS', 'message' => '成功'];
        }
    }

BeforeMiddleware

/**
     * Handle an incoming request.
     *
     * @param  \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse)  $next
     * @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
     */
    public function handle(Request $request, Closure $next)
    {
        //记录请求日志
        Log::debug('请求日志 '.$request->path(), [
            'ip' => $request->ip(),
            'url' => $request->url(),
            'method' => $request->method(),
            'data' => $request->all(),
        ]);

        return $next($request);
    }
@overtrue
Copy link
Collaborator

你接收到的推送请求可能不来自微信,而是一些恶意请求

@angelofan
Copy link
Author

退款成功的回调地址设置有两种途径:

  1. 在退款api发出时指定。
  2. 在微信商户后台的【交易中心】-【退款管理】-【退款配置】-【退款结果回调通知】里面设置(如下图)。
    d010a4589079ff8a9bd74a6342861a7

我是通过微信商户平台里面处理的退款,不是api发起的,api退款还没有开始写。

如果没有在商户后台配置回调地址,就收不到这种途径发起的退款结果通知。

后台设置了退款回调地址后,收到的回调通知就100%都会报这个错,是因为微信没有给这种途径的推送放数据进去?我看到打印的 $request->all() 是空的。

@angelofan
Copy link
Author

image

这个链接跳转到了 https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_16 这个页面,而商户中心前台文档中心里面进去的是这个链接 https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_11.shtml ,看到区别是前者是 api,后者是 apiv3 ,打开的文档页面前者是 xml,后者是 JSON

@angelofan
Copy link
Author

在微信开放社区里面搜到了相关问题:通过微信支付商户平台设置的退款结果回调通知走的是APIV2协议?

@overtrue
Copy link
Collaborator

记录一下这个值:https://github.com/w7corp/easywechat/blob/6.x/src/Pay/Server.php#L114 看看是啥

@angelofan
Copy link
Author

输出(appid、mch_id、nonce_str 已脱敏处理)

[2023-08-26 22:31:38] local.ERROR: <xml><return_code>SUCCESS</return_code><appid><![CDATA[wxc******a9700c7fa]]></appid><mch_id><![CDATA[1616******]]></mch_id><nonce_str><![CDATA[bcced3cce6a9d03ef8cb******e14c0f]]></nonce_str><req_info><![CDATA[WsZuZPAevvi/qeYHIHOjJmzgd5fUTTBojngCRnsPJBSJ3WkJ3UBWbxD+H5woEeYXgCXhAfxIzEgmprdiGiZscdO1/DL74T04yWTBCcFQjBsgpsmkCKUpZUdemb51Aah+hImfgEyLrOLl0aPUoCzHCq3MlbRyPX84EsB2kcEkyQ8J0Qx3bSMDDOr+EDPEDiqYag2l5IajvHce8Jsli6k6HKMgK07TReOiRTj58cRkj5VlnVuFENa1YpHMoqRNwF64UEBhu0tMBLD3wv6yBdReAYXXDqzedVUj/Gbjuk82/OHG82gMpB02XTm0jV0JKC4lB6q+56+ReHOGvHnlAz4eyDFCLQNnUQXbFGrydkvHHZsvwHNrd6ol6A8zngDivOxBfb0N2Q047HaKWOjtJLt/+CiXInF2Y2GzDxa4won14b13h0khkejvPjZiIuD21c7mIMp1uGjDONvqjf0B/hv5wW7Bch+PnVzlXST9ozN/i4zMslK6mxw7Y50hII1zGZ1Qchm81agzs9Fwi09QuT59XJLRdAcvwVUdubxmS9EtC7DGY3BOxHbWAf2g6ZyOTpLBkaO3sBsUYj83uvznwrwPwhaHLY+UnONANpYsH6rsVzDZrCaIbC9TJ9e0v3l+hoYQg+e3IHnmzv/PbFjEp9ycy9VeAiMcQ1iMJNl8d7TBWsGfTEZEv2O/naSgsRug8zEraf6xJtQQJNMiwsSgVh6f2l3hg3DPdN4WVUSYludSDhE0E5H8SGrQB71L1C3bepaWIPIXPwbW3dFtSznXPwlHLIkeUCB5td3vR57k6s+4n1tp0degO0i3KxqiObZYuWcdxCx2HMnY5JzPv8hLIHEiIwshF0qRhU/fszYJ/fVs/mMMZiZKaoV7vCDoILRZxqw72ZX5e6mKogMe5soq0aW2se2gL7N6hbD7gTZoj1hTZFYqyqH2VCkxD+BeZ8GBrZfOVIKMOqDXJ5lgoN/f6PPzh/yQpS9nMMtWYDo3TnuaNrYDDW3gx0vY1M7YNdiwbrlwyDj8GlMcuIVow/TzPbINi6DLH5kk5D2Fc1w5Hu69sJgEpSm4qv04OrWIG8FWWYza9tc9N1aWZa1X8BlLwZoBe2eD1970Kk9Lgp0oqnwzio3Xy4at/U6PqRTJAasLpR0QKd+Ff3lBKSi1Jk2ztvjhIoUo49e7yHBTs1/jUE4f5zo=]]></req_info></xml> {"exception":"[object] (EasyWeChat\\Kernel\\Exceptions\\RuntimeException(code: 0): <xml><return_code>SUCCESS</return_code><appid><![CDATA[wxc******a9700c7fa]]></appid><mch_id><![CDATA[1616******]]></mch_id><nonce_str><![CDATA[bcced3cce6a9d03ef8cb******e14c0f]]></nonce_str><req_info><![CDATA[WsZuZPAevvi/qeYHIHOjJmzgd5fUTTBojngCRnsPJBSJ3WkJ3UBWbxD+H5woEeYXgCXhAfxIzEgmprdiGiZscdO1/DL74T04yWTBCcFQjBsgpsmkCKUpZUdemb51Aah+hImfgEyLrOLl0aPUoCzHCq3MlbRyPX84EsB2kcEkyQ8J0Qx3bSMDDOr+EDPEDiqYag2l5IajvHce8Jsli6k6HKMgK07TReOiRTj58cRkj5VlnVuFENa1YpHMoqRNwF64UEBhu0tMBLD3wv6yBdReAYXXDqzedVUj/Gbjuk82/OHG82gMpB02XTm0jV0JKC4lB6q+56+ReHOGvHnlAz4eyDFCLQNnUQXbFGrydkvHHZsvwHNrd6ol6A8zngDivOxBfb0N2Q047HaKWOjtJLt/+CiXInF2Y2GzDxa4won14b13h0khkejvPjZiIuD21c7mIMp1uGjDONvqjf0B/hv5wW7Bch+PnVzlXST9ozN/i4zMslK6mxw7Y50hII1zGZ1Qchm81agzs9Fwi09QuT59XJLRdAcvwVUdubxmS9EtC7DGY3BOxHbWAf2g6ZyOTpLBkaO3sBsUYj83uvznwrwPwhaHLY+UnONANpYsH6rsVzDZrCaIbC9TJ9e0v3l+hoYQg+e3IHnmzv/PbFjEp9ycy9VeAiMcQ1iMJNl8d7TBWsGfTEZEv2O/naSgsRug8zEraf6xJtQQJNMiwsSgVh6f2l3hg3DPdN4WVUSYludSDhE0E5H8SGrQB71L1C3bepaWIPIXPwbW3dFtSznXPwlHLIkeUCB5td3vR57k6s+4n1tp0degO0i3KxqiObZYuWcdxCx2HMnY5JzPv8hLIHEiIwshF0qRhU/fszYJ/fVs/mMMZiZKaoV7vCDoILRZxqw72ZX5e6mKogMe5soq0aW2se2gL7N6hbD7gTZoj1hTZFYqyqH2VCkxD+BeZ8GBrZfOVIKMOqDXJ5lgoN/f6PPzh/yQpS9nMMtWYDo3TnuaNrYDDW3gx0vY1M7YNdiwbrlwyDj8GlMcuIVow/TzPbINi6DLH5kk5D2Fc1w5Hu69sJgEpSm4qv04OrWIG8FWWYza9tc9N1aWZa1X8BlLwZoBe2eD1970Kk9Lgp0oqnwzio3Xy4at/U6PqRTJAasLpR0QKd+Ff3lBKSi1Jk2ztvjhIoUo49e7yHBTs1/jUE4f5zo=]]></req_info></xml> at {laravel_path}\\vendor\\w7corp\\easywechat\\src\\Pay\\Server.php:115)

easywechat/src/Pay/Server.php

 /**
     * @throws InvalidArgumentException
     * @throws RuntimeException
     */
    public function getRequestMessage(ServerRequestInterface $request = null): \EasyWeChat\Kernel\Message|Message
    {
        $originContent = (string) ($request ?? $this->request)->getBody();
        throw new RuntimeException($originContent); // 直接抛出$originContent
        $attributes = json_decode($originContent, true);

        if (! is_array($attributes)) {
            throw new RuntimeException('Invalid request body.');
        }

        if (empty($attributes['resource']['ciphertext'])) {
            throw new RuntimeException('Invalid request.');
        }

        $attributes = json_decode(
            AesGcm::decrypt(
                $attributes['resource']['ciphertext'],
                $this->merchant->getSecretKey(),
                $attributes['resource']['nonce'],
                $attributes['resource']['associated_data'],
            ),
            true
        );

        if (! is_array($attributes)) {
            throw new RuntimeException('Failed to decrypt request message.');
        }

        return new Message($attributes, $originContent);
    }

WeChatController

public function pay_callback(Request $request)
    {
        if(true||count($request->all()) > 0){ // 强制进入此代码块,故意抛出调试的数据
            $server = app('easywechat.pay')->getServer();
            $message = $server->getRequestMessage();
            self::handlePayCallback($message);
    
            // 默认返回 ['code' => 'SUCCESS', 'message' => '成功']
            return $server->serve();
        }else{

            // 默认返回 ['code' => 'SUCCESS', 'message' => '成功']
            return ['code' => 'SUCCESS', 'message' => '成功'];
        }
    }

@overtrue
Copy link
Collaborator

所以它妈的,这帮人真的推送的XML……

@angelofan
Copy link
Author

所以它妈的,这帮人真的推送的XML……

我也是看得简直无语了,开放社区提出来都快两年半了,还没搞一个APIV3的填写框,报错调了一整天各种蒙圈,我还一直在想如果后台手动处理的退款该怎么去推到系统里。原来是微信那边这个回调推送还是用的APIV2的XML,服了服了

@overtrue
Copy link
Collaborator

改一下试试:

        if (str_starts_with($originContent, '<xml')) {
            $attributes = Xml::parse($originContent);
        } else {
            $attributes = json_decode($originContent, true);
        }

@overtrue overtrue reopened this Aug 26, 2023
@angelofan

This comment was marked as resolved.

@angelofan

This comment was marked as resolved.

@angelofan

This comment was marked as resolved.

@angelofan
Copy link
Author

angelofan commented Aug 26, 2023

到这里卡住了,不知道怎么解决了。

[2023-08-27 01:23:56] local.ERROR: openssl_decrypt(): IV passed is 32 bytes long which is longer than the 0 expected by selected cipher, truncating {"exception":"[object] (ErrorException(code: 0): openssl_decrypt(): IV passed is 32 bytes long which is longer than the 0 expected by selected cipher, truncating at {laravel_path}\vendor\w7corp\easywechat\src\Kernel\Support\AesEcb.php:35)

相关代码:

Server.php

    /**
     * @throws InvalidArgumentException
     * @throws RuntimeException
     */
    public function getRequestMessage(ServerRequestInterface $request = null): \EasyWeChat\Kernel\Message|Message
    {
        $originContent = (string) ($request ?? $this->request)->getBody();
        
        $is_xml = str_starts_with($originContent, '<xml');

        if ($is_xml) {
            $attributes = Xml::parse($originContent);
        } else {
            $attributes = json_decode($originContent, true);
        }

        if (! is_array($attributes)) {
            throw new RuntimeException('Invalid request body.');
        }

        if($is_xml){
            if (empty($attributes['req_info'])) {
                throw new RuntimeException('Invalid request.');
            }

            $attributes = json_decode(
                AesEcb::decrypt(
                    $attributes['req_info'],
                    $this->merchant->getV2SecretKey(),
                    $attributes['nonce_str'],
                ),
                true
            );
        }else{
            if (empty($attributes['resource']['ciphertext'])) {
                throw new RuntimeException('Invalid request.');
            }

            $attributes = json_decode(
                AesGcm::decrypt(
                    $attributes['resource']['ciphertext'],
                    $this->merchant->getSecretKey(),
                    $attributes['resource']['nonce'],
                    $attributes['resource']['associated_data'],
                ),
                true
            );
        }

        if (! is_array($attributes)) {
            throw new RuntimeException('Failed to decrypt request message.');
        }

        return new Message($attributes, $originContent);
    }

EasyWeChat\Kernel\Support\AesEcb

    /**
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
     */
    public static function decrypt(string $ciphertext, string $key, string $iv = null): string
    {
        $plaintext = openssl_decrypt( // <<< - 报错位置
            base64_decode($ciphertext, true) ?: '',
            'aes-256-ecb',
            $key,
            OPENSSL_RAW_DATA,
            (string) $iv
        );

        if (false === $plaintext) {
            throw new InvalidArgumentException(openssl_error_string() ?: 'Decrypt AES ECB failed.');
        }

        return $plaintext;
    }

好奇怪为什么是预期0长度,网上搜到的openssl_decrypt是预期16长度,也试了stackoverflow 这个 hex2bin 方案,依然报 openssl_decrypt(): IV passed is 16 bytes long which is longer than the 0 expected by selected cipher 错误,这里这个openssl_decrypt为什么要求0长度的IV值?我如果IV值传空字符串的话,解密就失败了。

@overtrue
Copy link
Collaborator

@overtrue
Copy link
Collaborator

AesEcb::decrypt(
                    $attributes['req_info'],
                    md5($this->merchant->getV2SecretKey()),
                ),

这样试试?

@angelofan
Copy link
Author

AesEcb::decrypt(
                    $attributes['req_info'],
                    md5($this->merchant->getV2SecretKey()),
                ),

这样试试?

[2023-08-27 13:39:32] local.ERROR: error:1C800064:Provider routines::bad decrypt {"exception":"[object] (EasyWeChat\Kernel\Exceptions\InvalidArgumentException(code: 0): error:1C800064:Provider routines::bad decrypt at {laravel_path}\vendor\w7corp\easywechat\src\Kernel\Support\AesEcb.php:44)

/**
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
     */
    public static function decrypt(string $ciphertext, string $key, string $iv = null): string
    {
        $plaintext = openssl_decrypt(
            base64_decode($ciphertext, true) ?: '',
            'aes-256-ecb',
            $key,
            OPENSSL_RAW_DATA,
            (string) $iv
        );

        if (false === $plaintext) {
            throw new InvalidArgumentException(openssl_error_string() ?: 'Decrypt AES ECB failed.'); // 报错位置
        }

        return $plaintext;
    }

@overtrue
Copy link
Collaborator

解决了,最终解决方案是:

// 建议使用单独的路由处理退款!
$server = $app->getServer();

// 推送消息,已解密
// 结构参考:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_16&index=10
$message = $server->getReqeustMessage();

// 你的逻辑...

// 返回 SUCCESS 或者 FAIL 等其他状态
return new \Nyholm\Psr7\Response(
        200, [],
      \EasyWeChat\Kernel\Support\Xml::build([
        'return_code' => 'SUCCESS',
        'return_msg' => 'OK'
      ])
);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants