Skip to content
New issue

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

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

Already on GitHub? # 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。

感谢支持!

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

No branches or pull requests

4 participants