从~0.2 迁移至 ^1.0
如 变更历史 所述,本类库自1.0不兼容wechatpay/wechatpay-guzzle-middleware:~0.2
,原因如下:
- 升级
Guzzle
大版本至7
,Guzzle7
做了许多不兼容更新,相关讨论可见Laravel8依赖变更;Guzzle7
要求PHP最低版本为7.2.5
,重要特性是加入了函数参数类型签名
以及函数返回值类型签名
功能,从开发语言层面,使类库健壮性有了显著提升; - 重构并修正了原敏感信息加解密过度设计问题;
- 重新设计了类库函数及方案,以提供回调通知签名所需方法;
- 调整
composer.json
移动guzzlehttp/guzzle
从require-dev
弱依赖至require
强依赖,开发者无须再手动添加; - 缩减初始化手动拼接客户端参数至
Builder::factory
,统一由SDK来构建客户端; - 新增链式调用封装器,原生提供对
APIv3
的链式调用; - 新增
APIv2
支持,推荐商户可以先升级至本类库支持的APIv2
能力,然后再按需升级至相对应的APIv3
能力; - 增加类库单元测试覆盖
Linux
,macOS
及Windows
运行时; - 调整命名空间
namespace
为WeChatPay
;
迁移指南
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']);
至此,迁移后,Chainable
、PromiseA+
以及强劲的PHP8
运行时,均可愉快地调用微信支付官方接口了。