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

支付宝支付当subject为中文时,异步通知subject乱码导致验签失败 #14

Closed
BigBigYokee opened this issue Sep 19, 2017 · 13 comments

Comments

@BigBigYokee
Copy link

支付宝异步通知数据,charset为GBK,subject为乱码,导致验签失败。当subject为英文时,验签通过。

@yansongda
Copy link
Owner

yansongda commented Sep 19, 2017

刚刚对中文问题进行了测试。但是未重现您说的验签失败问题。

测试如下:

<?php

namespace App\Http\Controllers;

use Pay;
use Log;
use Illuminate\Http\Request;

class UsersController extends Controller
{
    public function index(Request $request)
    {
        $config_biz = [
            'out_trade_no' => time(),
            'total_amount' => '1',
            'subject'      => '测试用例',
        ];

        return Pay::driver('alipay')->gateway()->pay($config_biz);
    }

    public function return(Request $request)
    {
        $o = Pay::driver('alipay')->gateway()->verify($request->all());
        dd($o);
    }

    public function notify(Request $request)
    {
        if (Pay::driver('alipay')->gateway()->verify($request->all())) {
            file_put_contents(storage_path('notify.txt'), "收到来自支付宝的异步通知\r\n", FILE_APPEND);
            file_put_contents(storage_path('notify.txt'), '订单号:' . $request->out_trade_no . "\r\n", FILE_APPEND);
            file_put_contents(storage_path('notify.txt'), '订单标题:' . $request->subject . "\r\n", FILE_APPEND);
            file_put_contents(storage_path('notify.txt'), '订单金额:' . $request->total_amount . "\r\n\r\n", FILE_APPEND);
        } else {
            file_put_contents(storage_path('notify.txt'), "收到来自异步通知,not verify\r\n", FILE_APPEND);
        }

        echo "success";
    }
}

最后 notify.txt 文件收到:

收到来自支付宝的异步通知
订单号:1505836463
订单标题:测试用例
订单金额:1.00

表明支付宝异步通知所使用的是 utf8 的编码,从同步通知结果也能看到 &charset=utf-8

麻烦您详细描述下您所使用的环境及代码,以便确定问题所在,谢谢!

感谢您的支持!

@BigBigYokee
Copy link
Author

gateway 为scan时

@shizhice
Copy link

支付宝支付,当getewayscan时,确实有这个问题。可能是因为下单时编码问题导致。

<?php
namespace Yansongda\Pay\Gateways\Alipay;

use Yansongda\Pay\Contracts\GatewayInterface;
use Yansongda\Pay\Exceptions\GatewayException;
use Yansongda\Pay\Exceptions\InvalidArgumentException;
use Yansongda\Pay\Support\Config;
use Yansongda\Pay\Traits\HasHttpRequest;

abstract class Alipay implements GatewayInterface
{
    ...

    protected function getResult($config_biz, $method)
    {
        $this->config['biz_content'] = json_encode($config_biz);
        $this->config['method'] = $method;
        $this->config['sign'] = $this->getSign();

        $method = str_replace('.', '_', $method).'_response';

        $this->gateway = $this->gateway."?charset=utf-8";

        $data = json_decode(
                    mb_convert_encoding($this->post($this->gateway, $this->config), 'utf-8', 'gb2312'),
                    true
                );

        if (!isset($data[$method]['code']) || $data[$method]['code'] !== '10000') {
            throw new GatewayException(
                'get result error:'.$data[$method]['msg'].' - '.$data[$method]['sub_code'],
                $data[$method]['code'],
                $data);
        }

        return $this->verify($data[$method], $data['sign'], true);
    }
}

在gateway后拼接?charset=utf-8后,乱码问题不在重现。

@yansongda
Copy link
Owner

yansongda commented Sep 20, 2017

翻了整个官方文档中,并未有 https://openapi.alipay.com/gateway.do?charset=utf-8 这种写法,这只是临时解决方案。

同时,post 参数中已经带有 charset,可能是支付宝为解析。

所以,为了严谨性,将不会采用这种方案。

刚刚测试了下,发现验签是通过的,但是,最后得到的 notify.txt 已经乱码,内容如下:

收到�自支付�的异步通知
订��:1505836463
订�标题:测试用例
订�金�:1.00

收到�自支付�的异步通知
订��:scan-1505873798
订å�•æ ‡é¢˜ï¼š²âÊÔÓÃÀý
订�金�:1.00

已经基本可以确定是编码问题

请问您确定验签不通过吗?

@shizhice 请问您这边也验签不通过吗?

@shizhice
Copy link

对。编码错误,导致验签失败。

@shizhice
Copy link

我查看了post参数中确实有chatset=utf-8,但是并未发现因何不起作用。我看了下其他的gateway,发现当gateway=wap时,构建的html中,发现代码如下

<form id="alipaysubmit" name="alipaysubmit" action="https://openapi.alipay.com/gateway.do?charset=utf-8" method="POST">
   ...
   <input type="hidden" name="method" value="alipay.trade.wap.pay" />
   <input type="hidden" name="format" value="json" />
   <input type="hidden" name="charset" value="utf-8" />
   <input type="hidden" name="sign_type" value="RSA2" />
   <input type="hidden" name="version" value="1.0" />
   ...
  </form>
  <script>document.forms['alipaysubmit'].submit();</script>

故而临时采用方案

$this->gateway = $this->gateway."?charset=utf-8";

@yansongda
Copy link
Owner

@shizhice 我看看官方 SDK 先,看看能不能找到解决方案,web 和 wap 的那个是官方 SDK 中的,所以就直接扣下来了。

奇怪,我这边验签居然是通过的。

感谢您的支持!

@shizhice
Copy link

@yansongda 不好意思啊,我刚才又看了一下,我这里也是验签成功的,我是将request()->all()$verifyResult = Pay::driver('alipay')->gateway()->verify($request->all())直接json_encode后打log测试的。因为编码问题,json_encode失败,故而误以为验签失败,当我Arr::except($verifyResult, ["subject"])后,可以正常输出。我的失误,请见谅。

@shizhice
Copy link

@yansongda 另外我认为有一个优化的地方,当支付宝支付成功的时候,支付宝会异步通知我们,但是支付宝会通知我们两次。

{"trade_status":"WAIT_BUYER_PAY"}

{"trade_status":"TRADE_SUCCESS"}

新手可能会忽略这个问题,望在readme中添加下说明在收到支付宝异步通知时判断下trade_status
而且在gateway为wap和web支付时候,构建的html会暴露我们的notify_url。为保证订单确实支付成功,或者其他人恶意请求notify_url。建议使用者,在接到支付宝和微信异步通知的时候进行一次主动查询。

@yansongda
Copy link
Owner

是的,扫码时,的确会异步通知两次,一般情况下,需要对官方文档熟悉,不然即使这个地方提醒了,也是不 ok 的,不过您说的对,稍后我再 readme 中做出说明。

经检查,官方 SDK 中直接将 charset 放入 get 中:

//系统参数放入GET请求串
		$requestUrl = $this->gatewayUrl . "?";
		foreach ($sysParams as $sysParamKey => $sysParamValue) {
			$requestUrl .= "$sysParamKey=" . urlencode($this->characet($sysParamValue, $this->postCharset)) . "&";
		}
		$requestUrl = substr($requestUrl, 0, -1);

不得不说,官方的文档及 SDK 真实让人抓狂~

稍后将进行修复。

感谢大家的支持!谢谢!

@shizhice
Copy link

官方的sdk如果那么优雅、实用的话,我们还造什么轮子。😁😁😁

@bqdove
Copy link

bqdove commented Oct 9, 2018

    public function pay($endpoint, array $payload): Response
    {
        $payload['method'] = $this->getMethod();
        $payload['biz_content'] = json_encode(array_merge(
            json_decode($payload['biz_content'], true),
            ['product_code' => $this->getProductCode()]
        ));
        $payload['sign'] = Support::generateSign($payload, $this->config->get('private_key'));
        Log::debug('Paying A Web/Wap Order:', [$endpoint, $payload]);
        return $this->buildPayHtml($endpoint, $payload);
    }

biz_content使用json_encode编码时,中文会被转换掉,异步通知时的中文就会乱掉,导致验签失败

下面是我打印的日志:

2018-10-09 10:30:04 > DEBUG > Paying A Web/Wap Order: ["https://openapi.alipaydev.com/gateway.do",{"app_id":"2016080700186015","method":"alipay.trade.page.pay","format":"JSON","charset":"utf-8","sign_type":"RSA2","version":"1.0","return_url":"http://www.baidu.com","notify_url":"http://open.dev.che300.com:8027/api/pay_notify/alipay","timestamp":"2018-10-09 10:30:04","sign":"mdK1CYvqr7pMYmIKRkjqKAZL1ClO5nM+7ppxufwy67FcDfFlsmzh4M/OM3i9GfdietP5BqvSb8bvMTtLrOf4NgPA2qXtxixbkwh+B6I1ZjKi7t6e2ALFDgq2JS9zkqym/wiRRakkS77CYH92l8EfTuH/XsJFTPgz/C81b8mAxTyx6W6bBxDAhepw1/VwjQGsaFLMvYcg+za8BOa3tWrXgFOhyFqrrJBerm9l8Ja3QEl9XT2WUZy5yu7Egi1byGWZKsJAUPFQjbWdB0oBvn4amm3kmtqBKgW4lzziLROHv70wNj5eNKzXFJWKjwpZh+dB7BFagkMEaBVAzg9In9Qeqg==","biz_content":"{\"out_trade_no\":\"2016080700186015_201810091030044563059\",\"subject\":\"\\u6d4b\\u8bd5\\u4e3b\\u9898\",\"body\":\"\\u6d4b\\u8bd5\\u5546\\u54c1\",\"total_amount\":\"0.01\",\"timeout_express\":\"10m\",\"passback_params\":\"channel%3D1100%26app_id%3D5%26relation_id%3D7%26scene_type%3D1102\",\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}"}] []

2018-10-09 10:30:25 > DEBUG > Receive Alipay Request: {"gmt_create":"2018-10-09 10:30:17","charset":"GBK","subject":"","sign":"DjUds3K3cVsdXzWobPoM5AJexbKhUJ2DCfxiBrttH3N3q/7Ec9o9p1BrqxVhthcs6QzgsK+jo1dLB2tHQL9VqKD9lj6P7TRcLpGl0F4dFp4FVe6QQMSwharbPnwiw+nf+eh5ZukiuZwTNyC6sTPiUerun3RJcDDHFERepIs5BEm9ZoJeCed28bhTaVfSQDOxsGeobfkW6PkNostFijTWC99crrcdMeva/7itA/vlKgukDq3JsR5yi/UU+zfLiwzfkSrkVca18r5gU5vHzRzxendjEzpM0uObIEFqlhF4RT9jOFUnz02c1pZtFKfYUoQsCR+efSVLllALwljEDBC/3A==","buyer_id":"2088102172198252","body":"品","invoice_amount":"0.01","notify_id":"d86ddcd732ec5dd6811fe0ab3d37462hxl","fund_bill_list":"[{\"amount\":\"0.01\",\"fundChannel\":\"ALIPAYACCOUNT\"}]","notify_type":"trade_status_sync","trade_status":"TRADE_SUCCESS","receipt_amount":"0.01","app_id":"2016080700186015","buyer_pay_amount":"0.01","sign_type":"RSA2","seller_id":"2088102170308480","gmt_payment":"2018-10-09 10:30:24","notify_time":"2018-10-09 10:30:25","passback_params":"channel%3D1100%26app_id%3D5%26relation_id%3D7%26scene_type%3D1102","version":"1.0","out_trade_no":"2016080700186015_201810091030044563059","total_amount":"0.01","trade_no":"2018100922001498250500405640","auth_app_id":"2016080700186015","point_amount":"0.00"} []

2018-10-09 10:30:25 > WARNING > Alipay Sign Verify FAILED {"gmt_create":"2018-10-09 10:30:17","charset":"GBK","subject":"","sign":"DjUds3K3cVsdXzWobPoM5AJexbKhUJ2DCfxiBrttH3N3q/7Ec9o9p1BrqxVhthcs6QzgsK+jo1dLB2tHQL9VqKD9lj6P7TRcLpGl0F4dFp4FVe6QQMSwharbPnwiw+nf+eh5ZukiuZwTNyC6sTPiUerun3RJcDDHFERepIs5BEm9ZoJeCed28bhTaVfSQDOxsGeobfkW6PkNostFijTWC99crrcdMeva/7itA/vlKgukDq3JsR5yi/UU+zfLiwzfkSrkVca18r5gU5vHzRzxendjEzpM0uObIEFqlhF4RT9jOFUnz02c1pZtFKfYUoQsCR+efSVLllALwljEDBC/3A==","buyer_id":"2088102172198252","body":"品","invoice_amount":"0.01","notify_id":"d86ddcd732ec5dd6811fe0ab3d37462hxl","fund_bill_list":"[{\"amount\":\"0.01\",\"fundChannel\":\"ALIPAYACCOUNT\"}]","notify_type":"trade_status_sync","trade_status":"TRADE_SUCCESS","receipt_amount":"0.01","app_id":"2016080700186015","buyer_pay_amount":"0.01","sign_type":"RSA2","seller_id":"2088102170308480","gmt_payment":"2018-10-09 10:30:24","notify_time":"2018-10-09 10:30:25","passback_params":"channel%3D1100%26app_id%3D5%26relation_id%3D7%26scene_type%3D1102","version":"1.0","out_trade_no":"2016080700186015_201810091030044563059","total_amount":"0.01","trade_no":"2018100922001498250500405640","auth_app_id":"2016080700186015","point_amount":"0.00"} []

@yansongda
Copy link
Owner

@bqdove 麻烦请升级至最新版,同时,如果仍有问题,请按照 issue 模板重新发 issue。

感谢支持!

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

4 participants