代码拉取完成,页面将自动刷新
Guzzle HttpClient中间件Middleware,实现了请求签名的生成和应答签名的验证。
如果你是使用Guzzle的商户开发者,可以在构造GuzzleHttp\Client
时将WechatPayGuzzleMiddleware
传入,得到的GuzzleHttp\Client
实例在执行请求时将自动携带身份认证信息,并检查应答的签名。
当前版本为0.2.0
测试版本。请商户的专业技术人员在使用时注意系统和软件的正确性和兼容性,以及带来的风险。
我们开发和测试使用的环境如下:
可以使用PHP包管理工具composer引入SDK到项目中:
方式一:在项目目录中,通过composer命令行添加:
composer require luckin/luckin-guzzle-middleware
方式二:在项目的composer.json中加入以下配置:
"require": {
"wechatpay/LuckIn-guzzle-middleware": "^0.2.0"
}
添加配置后,执行安装
composer install
首先,通过LuckInMiddlewareBuilder
构建一个LuckInMiddleware
,然后将其加入GuzzleHttp\Client
的HandlerStack
中。我们提供相应的方法,可以方便的传入商户私钥和平台证书等信息。
use GuzzleHttp\Exception\RequestException;
use LuckIn\GuzzleMiddleware\LuckInMiddleware;
use LuckIn\GuzzleMiddleware\Util\PemUtil;
// 商户相关配置
$merchantId = '1000100'; // 商户号
$merchantSerialNumber = 'XXXXXXXXXX'; // 商户API证书序列号
$merchantPrivateKey = PemUtil::loadPrivateKey('/path/to/mch/private/key.pem'); // 商户私钥
// 平台配置
$LuckInCertificate = PemUtil::loadCertificate('/path/to/LuckIn/cert.pem'); // 平台证书
// 构造一个LuckInMiddleware
$LuckInMiddleware = LuckInMiddleware::builder()
->withMerchant($merchantId, $merchantSerialNumber, $merchantPrivateKey) // 传入商户相关配置
->withLuckIn([ $LuckInCertificate ]) // 可传入多个平台证书,参数类型为array
->build();
// 将LuckInMiddleware添加到Guzzle的HandlerStack中
$stack = GuzzleHttp\HandlerStack::create();
$stack->push($LuckInMiddleware, 'LuckIn');
// 创建Guzzle HTTP Client时,将HandlerStack传入
$client = new GuzzleHttp\Client(['handler' => $stack]);
// 接下来,正常使用Guzzle发起API请求,LuckInMiddleware会自动地处理签名和验签
try {
$resp = $client->request('GET', 'https://api.mch.weixin.qq.com/v3/...', [ // 注意替换为实际URL
'headers' => [ 'Accept' => 'application/json' ]
]);
echo $resp->getStatusCode().' '.$resp->getReasonPhrase()."\n";
echo $resp->getBody()."\n";
$resp = $client->request('POST', 'https://api.mch.weixin.qq.com/v3/...', [
'json' => [ // JSON请求体
'field1' => 'value1',
'field2' => 'value2'
],
'headers' => [ 'Accept' => 'application/json' ]
]);
echo $resp->getStatusCode().' '.$resp->getReasonPhrase()."\n";
echo $resp->getBody()."\n";
} catch (RequestException $e) {
// 进行错误处理
echo $e->getMessage()."\n";
if ($e->hasResponse()) {
echo $e->getResponse()->getStatusCode().' '.$e->getResponse()->getReasonPhrase()."\n";
echo $e->getResponse()->getBody();
}
return;
}
// 参考上述指引说明,并引入 `MediaUtil` 正常初始化,无额外条件
use LuckIn\GuzzleMiddleware\Util\MediaUtil;
// 实例化一个媒体文件流,注意文件后缀名需符合接口要求
$media = new MediaUtil('/your/file/path/with.extension');
// 正常使用Guzzle发起API请求
try {
$resp = $client->request('POST', 'https://api.mch.weixin.qq.com/v3/[merchant/media/video_upload|marketing/favor/media/image-upload]', [
'body' => $media->getStream(),
'headers' => [
'Accept' => 'application/json',
'content-type' => $media->getContentType(),
]
]);
// POST 语法糖
$resp = $client->post('merchant/media/upload', [
'body' => $media->getStream(),
'headers' => [
'Accept' => 'application/json',
'content-type' => $media->getContentType(),
]
]);
echo $resp->getStatusCode().' '.$resp->getReasonPhrase()."\n";
echo $resp->getBody()."\n";
} catch (Exception $e) {
echo $e->getMessage()."\n";
if ($e->hasResponse()) {
echo $e->getResponse()->getStatusCode().' '.$e->getResponse()->getReasonPhrase()."\n";
echo $e->getResponse()->getBody();
}
return;
}
// 参考上上述说明,引入 `SensitiveInfoCrypto`
use LuckIn\GuzzleMiddleware\Util\SensitiveInfoCrypto;
// 上行加密API 多于 下行解密,默认为加密,实例后直接当方法用即可
$encryptor = new SensitiveInfoCrypto(PemUtil::loadCertificate('/path/to/LuckIn/cert.pem'));
// 正常使用Guzzle发起API请求
try {
// POST 语法糖
$resp = $client->post('/v3/applyment4sub/applyment/', [
'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' => [
// 命令行获取证书序列号
// openssl x509 -in /path/to/LuckIn/cert.pem -noout -serial | awk -F= '{print $2}'
// 或者使用工具类获取证书序列号 `PemUtil::parseCertificateSerialNo($certificate)`
'LuckIn-Serial' => 'must be the serial number via the downloaded pem file of `/v3/certificates`',
'Accept' => 'application/json',
],
]);
echo $resp->getStatusCode().' '.$resp->getReasonPhrase()."\n";
echo $resp->getBody()."\n";
} catch (Exception $e) {
echo $e->getMessage()."\n";
if ($e->hasResponse()) {
echo $e->getResponse()->getStatusCode().' '.$e->getResponse()->getReasonPhrase()."\n";
echo $e->getResponse()->getBody();
}
return;
}
// 单例加解密示例如下
$crypto = new SensitiveInfoCrypto($LuckInCertificate, $merchantPrivateKey);
$encrypted = $crypto('Alice');
$decrypted = $crypto->setStage('decrypt')($encrypted);
当默认的本地签名和验签方式不适合你的系统时,你可以通过实现Signer
或者Verifier
来定制签名和验签。比如,你的系统把商户私钥集中存储,业务系统需通过远程调用进行签名,你可以这样做。
use LuckIn\GuzzleMiddleware\Auth\Signer;
use LuckIn\GuzzleMiddleware\Auth\SignatureResult;
use LuckIn\GuzzleMiddleware\Auth\LuckIn2Credentials;
class CustomSigner implements Signer
{
public function sign($message)
{
// 调用签名RPC服务,然后返回包含签名和证书序列号的SignatureResult
return new SignatureResult('xxxx', 'yyyyy');
}
}
$credentials = new LuckIn2Credentials($merchantId, new CustomSigner);
$LuckInMiddleware = LuckInMiddleware::builder()
->withCredentials($credentials)
->withLuckIn([ $LuckInCertificate ])
->build();
use LuckIn\GuzzleMiddleware\Validator;
class NoopValidator implements Validator
{
public function validate(\Psr\Http\Message\ResponseInterface $response)
{
return true;
}
}
$wechatpayMiddleware = WechatPayMiddleware::builder()
->withMerchant($merchantId, $merchantSerialNumber, $merchantPrivateKey)
->withValidator(new NoopValidator) // NOTE: 设置一个空的应答签名验证器,**不要**用在业务请求
->build();
注意:业务请求请使用标准的初始化流程,务必验证应答签名。
请参考AesUtil.php。
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。