wechatpay-apiv3 / wechatpay-php

微信支付 APIv3 的官方 PHP Library,同时也支持 APIv2
Apache License 2.0
475 stars 98 forks source link

QQ小程序里调用微信支付的问题,需要同时url带参数+post数据,我的返回结果是 401 Unauthorized {} #99

Closed hetao29 closed 2 years ago

hetao29 commented 2 years ago

运行环境

- OS:Linux
- PHP:7.4.27
- wechatpay-php:1.4.5

描述你的问题现象

如果在post一个url的时候,同时带上query和post的body,文档如下:https://q.qq.com/wiki/develop/miniprogram/server/virtual-payment/wx_pay.html

$url = "https://api.q.qq.com/wxpay/v3/pay/partner/transactions/h5";
$headers = [
    'Accept' => 'application/json' ,
    "Content-Type"=>"application/json, charset=UTF-8",
];
$resp = $this->_instance->chain($url)->post([
        'query'=> [
                "appid"=>$qq_appid,
                "access_token"=>$conf['access_token'],
                "real_notify_url"=>$real_notify_url,
        ],
        'body' => $body,
        'headers' => $headers,
]);
TheNorthMemory commented 2 years ago

看了下文档,其中:

需要做自定义签名signer适配,代码就如下

use GuzzleHttp\Middleware;
use WeChatPay\Crypto\Rsa;
use WeChatPay\Formatter;

$qProxySigner = function (RequestInterface $request) use ($merchantId, $merchantSerial, $merchantPrivateKey): string {
    $nonce = Formatter::nonce();
    $timestamp = (string) Formatter::timestamp();
    $signature = Rsa::sign(Formatter::request(
        $request->getMethod(),
        \str_ireplace('/wxpay', '', $request->getUri()->getPath()),
        $timestamp,
        $nonce,
        (string) $request->getBody()
    ), $merchantPrivateKey);

    return $request->withHeader('Authorization', Formatter::authorization(
        $merchantId,
        $nonce,
        $signature,
        $timestamp,
        $merchantSerial
    ));
};

$stack = $instance->getDriver()->select()->getConfig('handler');
// 卸载SDK内置签名中间件
$stack->remove('signer');
// 注册适配q.qq.com下的支付的请求签名中间件
$stack->before('prepare_body', Middleware::mapRequest(
    static function (RequestInterface $request) use ($qProxySigner): RequestInterface {
        return $request->withHeader('Authorization', $qProxySigner($request));
    }
), 'signer');

// 然后正常chainable请求即可
$resp = $this->_instance->chain('v3/pay/partner/transactions/h5')->post([
  'base_uri' => 'https://api.q.qq.com/wxpay/',
  'query' => [
                "appid"=>$qq_appid,
                "access_token"=>$conf['access_token'],
                "real_notify_url"=>$real_notify_url,
  ],
]);
TheNorthMemory commented 2 years ago

或者直接用QQ小程序支持的微信支付APIv2接口,代码无需做适配即可使用,如下:

$resp = $this->_instance->chain('v2/wxpay/unifiedorder')->post([
    'base_uri' => 'https://api.q.qq.com/',
    'query' => [
        'appid' => $qq_appid,
        'access_token' => $conf['access_token'],
        'real_notify_url' => $real_notify_url,
    ],
    'xml' => [
        'version' => '1.0',
        'profit_sharing' => 'N',
        'appid' => 'wxd678efh567hg6787',
        'mch_id' => '1230000109',
        'device_info' => '013467007045764',
        'sign_type' => 'MD5',
        'body' => '腾讯充值中心-QQ会员充值',
        'detail' => '{"cost_price":1,"receipt_id":"wx123","goods_detail":[{"goods_id":"商品编码","wxpay_goods_id":"1001","goods_name":"iPhone6s 16G","quantity":1,"price":1},{"goods_id":"商品编码","wxpay_goods_id":"1002","goods_name":"iPhone6s 32G","quantity":1,"price":1}]}',
        'attach' => '深圳分店',
        'out_trade_no' => '20150806125346',
        'fee_type' => 'CNY',
        'total_fee' => '88',
        'spbill_create_ip' => '123.12.12.123',
        'time_start' => '20091225091010',
        'time_expire' => '20091227091010',
        'goods_tag' => 'WXG',
        'notify_url' => 'http://www.weixin.qq.com/wxpay/pay.php',
        'trade_type' => 'MWEB',
        'product_id' => '12235413214070356458058',
        'limit_pay' => 'no_credit',
        'openid' => 'oUpF8uMuAJO_M2pxb1Q9zNjWeS6o',
        'receipt' => 'Y',
        'scene_info' => '{"store_info" : {"id": "SZTX001","name": "腾大餐厅","area_code": "440305","address": "科技园中一路腾讯大厦" }}',
    ],
]);
hetao29 commented 2 years ago

最后我实现的方式,是使用 GuzzleHttp的原生Client

use GuzzleHttp\Client;
$client = new Client();
$resp = $client->request('POST', $url, [
    'query'=> [
        "appid"=>$qq_appid,
        "access_token"=>$conf['access_token'],
        "real_notify_url"=>$real_notify_url,
    ],
    'body' => $body,
    'headers' => $headers,
]);