评论

APIv3版的 wechatpay-php 新包v1.1迁移指南

wechatpay-php 版本最低v1.1要求为7.2.5,迁移至新包后,Chainable、PromiseA+以及强劲的PHP8运行时,均可愉快地调用微信支付官方接口了。

从~0.2 迁移至 ^1.0

变更历史 所述,本类库自1.0不兼容wechatpay/wechatpay-guzzle-middleware:~0.2,原因如下:

  1. 升级Guzzle大版本至7, Guzzle7做了许多不兼容更新,相关讨论可见Laravel8依赖变更Guzzle7要求PHP最低版本为7.2.5,重要特性是加入了函数参数类型签名以及函数返回值类型签名功能,从开发语言层面,使类库健壮性有了显著提升;
  2. 重构并修正了原敏感信息加解密过度设计问题;
  3. 重新设计了类库函数及方案,以提供回调通知签名所需方法;
  4. 调整composer.json移动guzzlehttp/guzzlerequire-dev弱依赖至require强依赖,开发者无须再手动添加;
  5. 缩减初始化手动拼接客户端参数至Builder::factory,统一由SDK来构建客户端;
  6. 新增链式调用封装器,原生提供对APIv3的链式调用;
  7. 新增APIv2支持,推荐商户可以先升级至本类库支持的APIv2能力,然后再按需升级至相对应的APIv3能力;
  8. 增加类库单元测试覆盖Linux,macOSWindows运行时;
  9. 调整命名空间namespaceWeChatPay;

迁移指南

PHP版本最低要求为7.2.5,请商户的技术开发人员先评估运行时环境是否支持再决定按如下步骤迁移。

composer.json 调整

依赖调整

     "require": {
-         "guzzlehttp/guzzle": "^6.3",
-         "wechatpay/wechatpay-guzzle-middleware": "^0.2.0"
+         "wechatpay/wechatpay": "^1.0"
     }

初始化方法调整

 use GuzzleHttp\Exception\RequestException;
- use WechatPay\GuzzleMiddleware\WechatPayMiddleware;
+ use WeChatPay\Builder;
- use WechatPay\GuzzleMiddleware\Util\PemUtil;
+ use WeChatPay\Util\PemUtil;

 $merchantId = '1000100';
 $merchantSerialNumber = 'XXXXXXXXXX';
 $merchantPrivateKey = PemUtil::loadPrivateKey('/path/to/mch/private/key.pem');
 $wechatpayCertificate = PemUtil::loadCertificate('/path/to/wechatpay/cert.pem');
+$wechatpayCertificateSerialNumber = PemUtil::parseCertificateSerialNo($wechatpayCertificate);

- $wechatpayMiddleware = WechatPayMiddleware::builder()
-     ->withMerchant($merchantId, $merchantSerialNumber, $merchantPrivateKey)
-     ->withWechatPay([ $wechatpayCertificate ])
-     ->build();
- $stack = GuzzleHttp\HandlerStack::create();
- $stack->push($wechatpayMiddleware, 'wechatpay');
- $client = new GuzzleHttp\Client(['handler' => $stack]);
+ $instance = Builder::factory([
+     'mchid' => $merchantId,
+     'serial' => $merchantSerialNumber,
+     'privateKey' => $merchantPrivateKey,
+     'certs' => [$wechatpayCertificateSerialNumber => $wechatpayCertificate],
+ ]);

调用方法调整

GET请求

可以使用本SDK提供的语法糖,缩减请求代码结构如下:

 try {
-    $resp = $client->request('GET', 'https://api.mch.weixin.qq.com/v3/...', [
+    $resp = $instance->chain('v3/...')->get([
-         'headers' => [ 'Accept' => 'application/json' ]
     ]);
 } catch (RequestException $e) {
     //do something
 }

POST请求

缩减请求代码如下:

 try {
-    $resp = $client->request('POST', 'https://api.mch.weixin.qq.com/v3/...', [
+    $resp = $instance->chain('v3/...')->post([
          'json' => [ // JSON请求体
              'field1' => 'value1',
              'field2' => 'value2'
          ],
-         'headers' => [ 'Accept' => 'application/json' ]
     ]);
 } catch (RequestException $e) {
     //do something
 }

上传媒体文件

- use WechatPay\GuzzleMiddleware\Util\MediaUtil;
+ use WeChatPay\Util\MediaUtil;
 $media = new MediaUtil('/your/file/path/with.extension');
 try {
-     $resp = $client->request('POST', 'https://api.mch.weixin.qq.com/v3/[merchant/media/video_upload|marketing/favor/media/image-upload]', [
+     $resp = $instance->chain('v3/marketing/favor/media/image-upload')->post([
         'body'    => $media->getStream(),
         'headers' => [
-             'Accept'       => 'application/json',
             'content-type' => $media->getContentType(),
         ]
     ]);
 } catch (Exception $e) {
     // do something
 }
 try {
-     $resp = $client->post('merchant/media/upload', [
+     $resp = $instance->chain('v3/merchant/media/upload')->post([
         'body'    => $media->getStream(),
         'headers' => [
-             'Accept'       => 'application/json',
             'content-type' => $media->getContentType(),
         ]
     ]);
 } catch (Exception $e) {
     // do something
 }

敏感信息加/解密

- use WechatPay\GuzzleMiddleware\Util\SensitiveInfoCrypto;
+ use WeChatPay\Crypto\Rsa;
- $encryptor = new SensitiveInfoCrypto(PemUtil::loadCertificate('/path/to/wechatpay/cert.pem'));
+ $encryptor = function($msg) use ($wechatpayCertificate) { return Rsa::encrypt($msg, $wechatpayCertificate); };

 try {
-     $resp = $client->post('/v3/applyment4sub/applyment/', [
+     $resp = $instance->chain('v3/applyment4sub/applyment/')->post([
         'json' => [
             'business_code' => 'APL_98761234',
             'contact_info'  => [
                 'contact_name'      => $encryptor('value of `contact_name`'),
                 'contact_id_number' => $encryptor('value of `contact_id_number'),
                 'mobile_phone'      => $encryptor('value of `mobile_phone`'),
                 'contact_email'     => $encryptor('value of `contact_email`'),
             ],
             //...
         ],
         'headers' => [
-             'Wechatpay-Serial' => 'must be the serial number via the downloaded pem file of `/v3/certificates`',
+             'Wechatpay-Serial' => $wechatpayCertificateSerialNumber,
-             'Accept'           => 'application/json',
         ],
     ]);
 } catch (Exception $e) {
     // do something
 }

平台证书下载工具

在第一次下载平台证书时,本类库充分利用了\GuzzleHttp\HandlerStack中间件管理器能力,按照栈执行顺序,在返回结果验签中间件verifier之前注册certsInjector,之后注册certsRecorder“解开” "死循环"问题。
本类库提供的下载工具未改变 返回结果验签 逻辑,完整实现可参考bin/CertificateDownloader.php

AesGcm平台证书解密

- use WechatPay\Util\AesUtil;
+ use WeChatPay\Crypto\AesGcm;
- $decrypter = new AesUtil($opts['key']);
- $plain = $decrypter->decryptToString($encCert['associated_data'], $encCert['nonce'], $encCert['ciphertext']);
+ $plain = AesGcm::decrypt($encCert['ciphertext'], $opts['key'], $encCert['nonce'], $encCert['associated_data']);

至此,迁移后,ChainablePromiseA+以及强劲的PHP8运行时,均可愉快地调用微信支付官方接口了。

最后一次编辑于  2021-12-26  
点赞 1
收藏
评论
登录 后发表内容