wechatpay-apiv3 / wechatpay-php

微信支付 APIv3 的官方 PHP Library,同时也支持 APIv2

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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

hetao29 opened this issue · comments

commented

运行环境

- 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,
]);

看了下文档,其中:

  • 注意事项
    • 微信支付 V3 版 API 的签名信息包含了 URL,这里签名时应使用微信支付原版 URL。不要携带 appid, access_token, real_notify_url 这些参数。
      • 如H5下单API(直连模式) 下,参与签名的 URL 应为 /v3/pay/transactions/h5
  • QQ小程序后台仅对统一下单和支付回调两种接口进行透明转发。建议先直接请求微信支付后台调试接口,待接口调试成功后再转到QQ小程序后台代理接口。

需要做自定义签名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,
  ],
]);

或者直接用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": "科技园中一路腾讯大厦" }}',
    ],
]);
commented

最后我实现的方式,是使用 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,
]);