{"id":19499638,"url":"https://github.com/wechatpay-apiv3/wechatpay-guzzle-middleware","last_synced_at":"2025-04-04T12:09:59.632Z","repository":{"id":49541755,"uuid":"202261040","full_name":"wechatpay-apiv3/wechatpay-guzzle-middleware","owner":"wechatpay-apiv3","description":"微信支付 APIv3 Guzzle HTTP Client中间件（middleware）","archived":false,"fork":false,"pushed_at":"2021-12-24T12:26:35.000Z","size":64,"stargazers_count":206,"open_issues_count":5,"forks_count":46,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-03-27T11:44:27.904Z","etag":null,"topics":["guzzle-middleware","php","wechat","wechatpay","wechatpay-apiv3"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/wechatpay-apiv3.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-08-14T02:44:41.000Z","updated_at":"2025-03-17T08:53:22.000Z","dependencies_parsed_at":"2022-08-20T08:40:37.543Z","dependency_job_id":null,"html_url":"https://github.com/wechatpay-apiv3/wechatpay-guzzle-middleware","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wechatpay-apiv3%2Fwechatpay-guzzle-middleware","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wechatpay-apiv3%2Fwechatpay-guzzle-middleware/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wechatpay-apiv3%2Fwechatpay-guzzle-middleware/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wechatpay-apiv3%2Fwechatpay-guzzle-middleware/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/wechatpay-apiv3","download_url":"https://codeload.github.com/wechatpay-apiv3/wechatpay-guzzle-middleware/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247174455,"owners_count":20896078,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["guzzle-middleware","php","wechat","wechatpay","wechatpay-apiv3"],"created_at":"2024-11-10T22:05:10.429Z","updated_at":"2025-04-04T12:09:59.615Z","avatar_url":"https://github.com/wechatpay-apiv3.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# wechatpay-guzzle-middleware\n\n## 概览\n\n[微信支付API v3](https://wechatpay-api.gitbook.io/wechatpay-api-v3/)的[Guzzle HttpClient](http://docs.guzzlephp.org/)中间件Middleware，实现了请求签名的生成和应答签名的验证。\n\n如果你是使用Guzzle的商户开发者，可以在构造`GuzzleHttp\\Client`时将`WechatPayGuzzleMiddleware`传入，得到的`GuzzleHttp\\Client`实例在执行请求时将自动携带身份认证信息，并检查应答的微信支付签名。\n\n\n\n## 项目状态\n\n当前版本为`0.2.0`测试版本。请商户的专业技术人员在使用时注意系统和软件的正确性和兼容性，以及带来的风险。\n\n本项目处于**维护**状态，我们推荐所有的开发者优先使用微信支付新的 PHP 开发库 [wechatpay-php](https://github.com/wechatpay-apiv3/wechatpay-php)。\n\n\n## 环境要求\n\n我们开发和测试使用的环境如下：\n\n+ PHP 5.5+ / PHP 7.0+\n+ guzzlehttp/guzzle ^6.3\n\n备注：不支持 guzzle7 的具体原因可以见 [#54](https://github.com/wechatpay-apiv3/wechatpay-guzzle-middleware/issues/54) 的[讨论](https://github.com/wechatpay-apiv3/wechatpay-guzzle-middleware/issues/54#issuecomment-879602192)。依赖 guzzle7 的开发者请使用 [wechatpay-php](https://github.com/wechatpay-apiv3/wechatpay-php)。\n\n## 安装\n\n可以使用PHP包管理工具composer引入SDK到项目中：\n\n#### Composer\n\n方式一：在项目目录中，通过composer命令行添加：\n```shell\ncomposer require wechatpay/wechatpay-guzzle-middleware\n```\n\n\n方式二：在项目的composer.json中加入以下配置：\n\n```json\n    \"require\": {\n        \"wechatpay/wechatpay-guzzle-middleware\": \"^0.2.0\"\n    }\n```\n添加配置后，执行安装\n```shell\ncomposer install\n```\n\n\n\n## 开始\n\n首先，通过`WechatPayMiddlewareBuilder`构建一个`WechatPayMiddleware`，然后将其加入`GuzzleHttp\\Client`的`HandlerStack`中。我们提供相应的方法，可以方便的传入商户私钥和微信支付平台证书等信息。\n\n```php\nuse GuzzleHttp\\Exception\\RequestException;\nuse WechatPay\\GuzzleMiddleware\\WechatPayMiddleware;\nuse WechatPay\\GuzzleMiddleware\\Util\\PemUtil;\n\n// 商户相关配置\n$merchantId = '1000100'; // 商户号\n$merchantSerialNumber = 'XXXXXXXXXX'; // 商户API证书序列号\n$merchantPrivateKey = PemUtil::loadPrivateKey('/path/to/mch/private/key.pem'); // 商户私钥\n// 微信支付平台配置\n$wechatpayCertificate = PemUtil::loadCertificate('/path/to/wechatpay/cert.pem'); // 微信支付平台证书\n\n// 构造一个WechatPayMiddleware\n$wechatpayMiddleware = WechatPayMiddleware::builder()\n    -\u003ewithMerchant($merchantId, $merchantSerialNumber, $merchantPrivateKey) // 传入商户相关配置\n    -\u003ewithWechatPay([ $wechatpayCertificate ]) // 可传入多个微信支付平台证书，参数类型为array\n    -\u003ebuild();\n\n// 将WechatPayMiddleware添加到Guzzle的HandlerStack中\n$stack = GuzzleHttp\\HandlerStack::create();\n$stack-\u003epush($wechatpayMiddleware, 'wechatpay');\n\n// 创建Guzzle HTTP Client时，将HandlerStack传入\n$client = new GuzzleHttp\\Client(['handler' =\u003e $stack]);\n\n\n// 接下来，正常使用Guzzle发起API请求，WechatPayMiddleware会自动地处理签名和验签\ntry {\n    $resp = $client-\u003erequest('GET', 'https://api.mch.weixin.qq.com/v3/...', [ // 注意替换为实际URL\n        'headers' =\u003e [ 'Accept' =\u003e 'application/json' ]\n    ]);\n\n    echo $resp-\u003egetStatusCode().' '.$resp-\u003egetReasonPhrase().\"\\n\";\n    echo $resp-\u003egetBody().\"\\n\";\n\n    $resp = $client-\u003erequest('POST', 'https://api.mch.weixin.qq.com/v3/...', [\n        'json' =\u003e [ // JSON请求体\n            'field1' =\u003e 'value1',\n            'field2' =\u003e 'value2'\n        ],\n        'headers' =\u003e [ 'Accept' =\u003e 'application/json' ]\n    ]);\n\n    echo $resp-\u003egetStatusCode().' '.$resp-\u003egetReasonPhrase().\"\\n\";\n    echo $resp-\u003egetBody().\"\\n\";\n} catch (RequestException $e) {\n    // 进行错误处理\n    echo $e-\u003egetMessage().\"\\n\";\n    if ($e-\u003ehasResponse()) {\n        echo $e-\u003egetResponse()-\u003egetStatusCode().' '.$e-\u003egetResponse()-\u003egetReasonPhrase().\"\\n\";\n        echo $e-\u003egetResponse()-\u003egetBody();\n    }\n    return;\n}\n```\n\n### 上传媒体文件\n\n```php\n// 参考上述指引说明，并引入 `MediaUtil` 正常初始化，无额外条件\nuse WechatPay\\GuzzleMiddleware\\Util\\MediaUtil;\n// 实例化一个媒体文件流，注意文件后缀名需符合接口要求\n$media = new MediaUtil('/your/file/path/with.extension');\n\n// 正常使用Guzzle发起API请求\ntry {\n    $resp = $client-\u003erequest('POST', 'https://api.mch.weixin.qq.com/v3/[merchant/media/video_upload|marketing/favor/media/image-upload]', [\n        'body'    =\u003e $media-\u003egetStream(),\n        'headers' =\u003e [\n            'Accept'       =\u003e 'application/json',\n            'content-type' =\u003e $media-\u003egetContentType(),\n        ]\n    ]);\n    // POST 语法糖\n    $resp = $client-\u003epost('merchant/media/upload', [\n        'body'    =\u003e $media-\u003egetStream(),\n        'headers' =\u003e [\n            'Accept'       =\u003e 'application/json',\n            'content-type' =\u003e $media-\u003egetContentType(),\n        ]\n    ]);\n    echo $resp-\u003egetStatusCode().' '.$resp-\u003egetReasonPhrase().\"\\n\";\n    echo $resp-\u003egetBody().\"\\n\";\n} catch (Exception $e) {\n    echo $e-\u003egetMessage().\"\\n\";\n    if ($e-\u003ehasResponse()) {\n        echo $e-\u003egetResponse()-\u003egetStatusCode().' '.$e-\u003egetResponse()-\u003egetReasonPhrase().\"\\n\";\n        echo $e-\u003egetResponse()-\u003egetBody();\n    }\n    return;\n}\n```\n\n### 敏感信息加/解密\n\n```php\n// 参考上上述说明，引入 `SensitiveInfoCrypto`\nuse WechatPay\\GuzzleMiddleware\\Util\\SensitiveInfoCrypto;\n// 上行加密API 多于 下行解密，默认为加密，实例后直接当方法用即可\n$encryptor = new SensitiveInfoCrypto(PemUtil::loadCertificate('/path/to/wechatpay/cert.pem'));\n\n// 正常使用Guzzle发起API请求\ntry {\n    // POST 语法糖\n    $resp = $client-\u003epost('/v3/applyment4sub/applyment/', [\n        'json' =\u003e [\n            'business_code' =\u003e 'APL_98761234',\n            'contact_info'  =\u003e [\n                'contact_name'      =\u003e $encryptor('value of `contact_name`'),\n                'contact_id_number' =\u003e $encryptor('value of `contact_id_number'),\n                'mobile_phone'      =\u003e $encryptor('value of `mobile_phone`'),\n                'contact_email'     =\u003e $encryptor('value of `contact_email`'),\n            ],\n            //...\n        ],\n        'headers' =\u003e [\n            // 命令行获取证书序列号\n            // openssl x509 -in /path/to/wechatpay/cert.pem -noout -serial | awk -F= '{print $2}'\n            // 或者使用工具类获取证书序列号 `PemUtil::parseCertificateSerialNo($certificate)`\n            'Wechatpay-Serial' =\u003e 'must be the serial number via the downloaded pem file of `/v3/certificates`',\n            'Accept'           =\u003e 'application/json',\n        ],\n    ]);\n    echo $resp-\u003egetStatusCode().' '.$resp-\u003egetReasonPhrase().\"\\n\";\n    echo $resp-\u003egetBody().\"\\n\";\n} catch (Exception $e) {\n    echo $e-\u003egetMessage().\"\\n\";\n    if ($e-\u003ehasResponse()) {\n        echo $e-\u003egetResponse()-\u003egetStatusCode().' '.$e-\u003egetResponse()-\u003egetReasonPhrase().\"\\n\";\n        echo $e-\u003egetResponse()-\u003egetBody();\n    }\n    return;\n}\n\n// 单例加解密示例如下\n$crypto = new SensitiveInfoCrypto($wechatpayCertificate, $merchantPrivateKey);\n$encrypted = $crypto('Alice');\n$decrypted = $crypto-\u003esetStage('decrypt')($encrypted);\n```\n\n## 定制\n\n当默认的本地签名和验签方式不适合你的系统时，你可以通过实现`Signer`或者`Verifier`来定制签名和验签。比如，你的系统把商户私钥集中存储，业务系统需通过远程调用进行签名，你可以这样做。\n\n```php\nuse WechatPay\\GuzzleMiddleware\\Auth\\Signer;\nuse WechatPay\\GuzzleMiddleware\\Auth\\SignatureResult;\nuse WechatPay\\GuzzleMiddleware\\Auth\\WechatPay2Credentials;\n\nclass CustomSigner implements Signer\n{\n    public function sign($message)\n    {\n        // 调用签名RPC服务，然后返回包含签名和证书序列号的SignatureResult\n        return new SignatureResult('xxxx', 'yyyyy');\n    }\n}\n\n$credentials = new WechatPay2Credentials($merchantId, new CustomSigner);\n\n$wechatpayMiddleware = WechatPayMiddleware::builder()\n    -\u003ewithCredentials($credentials)\n    -\u003ewithWechatPay([ $wechatpayCertificate ])\n    -\u003ebuild();\n```\n\n\n\n## 常见问题\n\n### 如何下载平台证书？\n\n使用`WechatPayMiddlewareBuilder`需要调用`withWechatpay`设置[微信支付平台证书](https://wechatpay-api.gitbook.io/wechatpay-api-v3/ren-zheng/zheng-shu#ping-tai-zheng-shu)，而平台证书又只能通过调用[获取平台证书接口](https://wechatpay-api.gitbook.io/wechatpay-api-v3/jie-kou-wen-dang/ping-tai-zheng-shu#huo-qu-ping-tai-zheng-shu-lie-biao)下载。为了解开\"死循环\"，你可以在第一次下载平台证书时，按照下述方法临时\"跳过”应答签名的验证。\n\n```php\nuse WechatPay\\GuzzleMiddleware\\Validator;\n\nclass NoopValidator implements Validator\n{\n    public function validate(\\Psr\\Http\\Message\\ResponseInterface $response)\n    {\n        return true;\n    }\n}\n\n$wechatpayMiddleware = WechatPayMiddleware::builder()\n    -\u003ewithMerchant($merchantId, $merchantSerialNumber, $merchantPrivateKey)\n    -\u003ewithValidator(new NoopValidator) // NOTE: 设置一个空的应答签名验证器，**不要**用在业务请求\n    -\u003ebuild();\n```\n\n**注意**：业务请求请使用标准的初始化流程，务必验证应答签名。\n\n### 证书和回调解密需要的AesGcm解密在哪里？\n\n请参考[AesUtil.php](src/Util/AesUtil.php)。\n\n### 配合swoole使用时，上传文件接口报错\n\n建议升级至swoole 4.6+，swoole在 4.6.0 中增加了native-curl([swoole/swoole-src#3863](https://github.com/swoole/swoole-src/pull/3863))支持，我们测试能正常使用了。\n更详细的信息，请参考[#36](https://github.com/wechatpay-apiv3/wechatpay-guzzle-middleware/issues/36)。\n\n## 联系我们\n\n如果你发现了**BUG**或者有任何疑问、建议，请通过issue进行反馈。\n\n也欢迎访问我们的[开发者社区](https://developers.weixin.qq.com/community/pay)。\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwechatpay-apiv3%2Fwechatpay-guzzle-middleware","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwechatpay-apiv3%2Fwechatpay-guzzle-middleware","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwechatpay-apiv3%2Fwechatpay-guzzle-middleware/lists"}