Skip to content

Commit ad8239e

Browse files
committed
docs: 同步协议层能力与测试目录说明
- 更新 README 项目结构,说明 tests 作为 PHPUnit 根测试目录。 - 补充官方 path + params 调用约定,以及 raw/download/upload 的适用场景。 - 说明微信协议层、微信支付签名下载和支付宝网关复用边界,不承诺维护全部接口别名。
1 parent 27c6d03 commit ad8239e

1 file changed

Lines changed: 102 additions & 34 deletions

File tree

README.md

Lines changed: 102 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@ WeChatDeveloper 是一个面向 **微信** 与 **支付宝** 的轻量 PHP SDK
66

77
## 特性
88

9-
- 支持微信公众号、小程序、微信开放平台、微信支付 APIv3。
9+
- 支持微信公众平台、小程序、微信服务平台、微信支付 APIv3。
1010
- 支持支付宝开放平台与支付网关调用。
1111
- 统一入口 `We\Client`,按通道创建客户端。
1212
- 配置对象实现 `ConfigInterface`,构造时完成基础校验。
1313
- 缓存只依赖 `StoreCacheInterface`,可用于单机文件缓存或集群 Redis 适配。
14-
- 开放平台授权方 refresh token 通过 `StoreTokenInterface` 由业务系统存取。
14+
- 微信服务平台授权方 refresh token 通过 `StoreTokenInterface` 由业务系统存取。
15+
- 支持 JSON、原始响应、二进制下载和 multipart 上传等协议层通用能力。
1516
- 返回值默认是数组,失败时抛出 `WechatException` 或其子类。
1617

1718
## 环境要求
@@ -42,9 +43,29 @@ composer require zoujingli/wechat-developer:2.0.x-dev
4243
```bash
4344
cd WeChatDeveloper
4445
composer install
46+
composer validate --strict
4547
composer test
4648
```
4749

50+
## 项目结构
51+
52+
```text
53+
src/
54+
├── Client.php # SDK 根入口与通道客户端工厂
55+
├── Config/ # 微信、支付宝平台配置对象
56+
├── Contract/ # 配置、缓存、授权方 Token 存储契约
57+
├── Exception/ # SDK 异常类型
58+
├── Platform/
59+
│ ├── Wechat/ # 微信公众平台、小程序、微信服务平台、微信支付 APIv3 客户端
60+
│ │ └── Concerns/ # 微信 JSON、raw/download/upload、Token 注入等协议层复用能力
61+
│ └── Alipay/ # 支付宝开放平台与支付客户端
62+
└── Support/ # 缓存、HTTP、签名、XML、消息/支付解密工具
63+
64+
tests/ # PHPUnit 测试用例,命名空间 We\Tests
65+
```
66+
67+
测试入口为根目录 `tests/``phpunit.xml` 使用 `tests/bootstrap.php` 引导 Composer autoload。
68+
4869
## 快速开始
4970

5071
```php
@@ -57,20 +78,20 @@ use We\Config\WechatPlatformConfig;
5778

5879
$client = new Client();
5980

60-
$official = $client->wechatPlatform(new WechatPlatformConfig(
81+
$platform = $client->wechatPlatform(new WechatPlatformConfig(
6182
appid: 'wx_appid',
6283
appSecret: 'app_secret',
6384
));
6485

65-
$users = $official->get('cgi-bin/user/get', [
86+
$users = $platform->get('cgi-bin/user/get', [
6687
'next_openid' => '',
6788
]);
6889
```
6990

7091
`post()``get()``call()` 的 path 与官方文档保持一致,通常不需要前导 `/`
7192

7293
```php
73-
$menu = $official->post('cgi-bin/menu/create', [
94+
$menu = $platform->post('cgi-bin/menu/create', [
7495
'button' => [
7596
[
7697
'type' => 'click',
@@ -88,17 +109,17 @@ SDK 不把官方接口包装成大量固定方法,核心约定是“官方文
88109

89110
```php
90111
// GET:第二个参数会作为 query string。
91-
$result = $official->get('cgi-bin/user/get', ['next_openid' => '']);
112+
$result = $platform->get('cgi-bin/user/get', ['next_openid' => '']);
92113

93114
// POST:第二个参数默认作为 JSON body。
94-
$result = $official->post('cgi-bin/message/custom/send', [
115+
$result = $platform->post('cgi-bin/message/custom/send', [
95116
'touser' => 'openid',
96117
'msgtype' => 'text',
97118
'text' => ['content' => 'hello'],
98119
]);
99120

100121
// call:显式指定 HTTP 方法,并可透传 Guzzle options。
101-
$result = $official->call('cgi-bin/menu/get', [], 'GET');
122+
$result = $platform->call('cgi-bin/menu/get', [], 'GET');
102123
```
103124

104125
调用时需要注意:
@@ -112,6 +133,8 @@ $result = $official->call('cgi-bin/menu/get', [], 'GET');
112133
| GET query |`get($path, $query)`|
113134
| 自定义 query + JSON body | `post($path, $body, ['query' => [...], 'json' => $body])`|
114135
| 表单提交或原始 body | 透传 Guzzle 的 `form_params``body`|
136+
| 二进制/非 JSON 响应 |`raw()``download()` 返回 PSR-7 Response。 |
137+
| multipart 上传 |`upload($path, $multipart, $query)`,SDK 会按通道规则附加 token。 |
115138

116139
示例:小程序登录接口不需要 access token,应该关闭自动 token:
117140

@@ -124,6 +147,43 @@ $session = $wxapp->get('sns/jscode2session', [
124147
], ['with_token' => false]);
125148
```
126149

150+
图片、媒体、文件等非 JSON 响应可直接获取原始响应:
151+
152+
```php
153+
$response = $platform->download('cgi-bin/media/get', [
154+
'media_id' => 'MEDIA_ID',
155+
]);
156+
157+
$binary = (string) $response->getBody();
158+
```
159+
160+
上传媒体或文件时传入 Guzzle multipart 结构:
161+
162+
```php
163+
$media = $platform->upload('cgi-bin/media/upload', [
164+
[
165+
'name' => 'media',
166+
'contents' => fopen(__DIR__ . '/demo.jpg', 'rb'),
167+
'filename' => 'demo.jpg',
168+
],
169+
], [
170+
'type' => 'image',
171+
]);
172+
```
173+
174+
### 协议层能力说明
175+
176+
微信公众平台、小程序、微信服务平台共用 `InteractsProtocol` 协议层能力:
177+
178+
| 方法 | 说明 |
179+
|------|------|
180+
| `request()` | 发送 JSON 风格接口请求并解析为数组。 |
181+
| `raw()` | 返回 PSR-7 Response,不解析 JSON,适合图片、媒体、二维码等原始响应。 |
182+
| `download()` | 以 GET 方式获取二进制资源,并按通道规则附加 token 或签名。 |
183+
| `upload()` | 透传 Guzzle `multipart` 上传结构,并解析平台 JSON 响应。 |
184+
185+
微信支付 APIv3 的 `raw()``download()` 会先按官方规则生成 `Authorization` 签名,再返回原始响应。支付宝网关请求统一复用公共参数构造、签名和验签逻辑,电脑网站支付跳转地址也使用同一套签名参数。
186+
127187
## 配置来源示例
128188

129189
生产项目通常从数据库或配置中心读取账号配置,再使用 `fromArray()` 构造配置对象:
@@ -146,7 +206,7 @@ $client = new Client(
146206
cacheKeyPrefix: 'my_project_prod',
147207
);
148208

149-
$official = $client->wechatPlatform(WechatPlatformConfig::fromArray($row));
209+
$platform = $client->wechatPlatform(WechatPlatformConfig::fromArray($row));
150210
```
151211

152212
`cacheKeyPrefix` 建议按项目和环境区分,例如 `mall_prod``mall_test``storage_scope` 建议按租户、账号或业务线区分,避免同一 appid 在不同业务上下文中复用缓存。
@@ -172,14 +232,14 @@ new Client(
172232
| 参数 | 说明 |
173233
|------|------|
174234
| `$cache` | SDK 运行态缓存,主要保存 access token;不传时默认使用 `FileCacheStore`|
175-
| `$authorizers` | 微信开放平台代调用时,读取和回写授权方 refresh token。 |
235+
| `$authorizers` | 微信服务平台代调用时,读取和回写授权方 refresh token。 |
176236
| `$http` | 可注入自定义 Guzzle HTTP 客户端,便于统一超时、代理或单元测试。 |
177237
| `$cacheKeyPrefix` | 缓存键前缀,默认 `wechat_developer`;生产环境建议按项目设置。 |
178238

179239
也可以用字符串通道创建客户端:
180240

181241
```php
182-
$official = $client->get('wechat.platform', WechatPlatformConfig::fromArray($config));
242+
$platform = $client->get('wechat.platform', WechatPlatformConfig::fromArray($config));
183243
```
184244

185245
支持的通道:
@@ -289,14 +349,14 @@ $client = new Client(
289349
例如:
290350

291351
```text
292-
my_app:wechat.platform:wechat:app:wx123:official:access_token
352+
my_app:wechat.platform:wechat:app:wx123:platform:access_token
293353
```
294354

295355
完整键由 `CacheKey` 生成,微信 token 逻辑键由 `TokenCacheKey` 生成。
296356

297-
## 微信开放平台授权方 Token
357+
## 微信服务平台授权方 Token
298358

299-
开放平台代调用授权方接口时,SDK 需要读取授权方 refresh token,并在刷新后把新 token 回写业务存储。业务系统实现 `StoreTokenInterface` 即可:
359+
微信服务平台代调用授权方接口时,SDK 需要读取授权方 refresh token,并在刷新后把新 token 回写业务存储。业务系统实现 `StoreTokenInterface` 即可:
300360

301361
```php
302362
use We\Contract\StoreTokenInterface;
@@ -325,21 +385,21 @@ $client = new Client(
325385
);
326386
```
327387

328-
## 微信公众号
388+
## 微信公众平台
329389

330390
```php
331391
use We\Config\WechatPlatformConfig;
332392

333-
$official = $client->wechatPlatform(new WechatPlatformConfig(
393+
$platform = $client->wechatPlatform(new WechatPlatformConfig(
334394
appid: 'wx_appid',
335395
appSecret: 'app_secret',
336396
token: 'message_token',
337397
encodingAesKey: 'encoding_aes_key',
338398
));
339399

340-
$accessToken = $official->accessToken();
400+
$accessToken = $platform->accessToken();
341401

342-
$result = $official->post('cgi-bin/message/custom/send', [
402+
$result = $platform->post('cgi-bin/message/custom/send', [
343403
'touser' => 'openid',
344404
'msgtype' => 'text',
345405
'text' => ['content' => 'hello'],
@@ -349,7 +409,7 @@ $result = $official->post('cgi-bin/message/custom/send', [
349409
安全模式消息解密:
350410

351411
```php
352-
$plain = $official->post('decrypt_message', [
412+
$plain = $platform->post('decrypt_message', [
353413
'body' => $rawBody,
354414
'msg_signature' => $signature,
355415
'timestamp' => $timestamp,
@@ -385,15 +445,16 @@ $phone = $wxapp->post('wxa/business/getuserphonenumber', [
385445
]);
386446
```
387447

388-
如果官方接口返回图片、文件等二进制内容,默认 `post()` 会按 JSON 响应解析,不适合直接处理;建议注入自定义 Guzzle 客户端或在业务侧扩展专用下载方法
448+
如果官方接口返回图片、文件等二进制内容,使用 `download()``raw()` 获取原始 PSR-7 Response;默认 `post()` 仍按 JSON 响应解析。
389449

390-
## 微信开放平台
450+
## 微信服务平台
391451

392452
```php
393453
use We\Config\WechatServiceConfig;
394454

455+
$componentAppid = 'component_appid';
395456
$service = $client->wechatService(new WechatServiceConfig(
396-
componentAppid: 'component_appid',
457+
componentAppid: $componentAppid,
397458
componentAppSecret: 'component_secret',
398459
componentToken: 'component_token',
399460
componentEncodingAesKey: 'component_encoding_aes_key',
@@ -439,6 +500,14 @@ $refund = $payment->post('v3/refund/domestic/refunds', [
439500
]);
440501
```
441502

503+
下载账单等非 JSON 响应时使用已签名的 `download()`
504+
505+
```php
506+
$bill = $payment->download('v3/bill/tradebill', [
507+
'bill_date' => '2026-05-08',
508+
]);
509+
```
510+
442511
回调验签与解密:
443512

444513
```php
@@ -497,10 +566,10 @@ $page = $payment->post('page', [
497566

498567
## 常见业务案例
499568

500-
### 公众号:创建菜单并发送客服消息
569+
### 微信公众平台:创建菜单并发送客服消息
501570

502571
```php
503-
$official->post('cgi-bin/menu/create', [
572+
$platform->post('cgi-bin/menu/create', [
504573
'button' => [
505574
[
506575
'name' => '服务',
@@ -512,40 +581,39 @@ $official->post('cgi-bin/menu/create', [
512581
],
513582
]);
514583

515-
$official->post('cgi-bin/message/custom/send', [
584+
$platform->post('cgi-bin/message/custom/send', [
516585
'touser' => 'openid',
517586
'msgtype' => 'text',
518587
'text' => ['content' => '您好,客服消息已发送。'],
519588
]);
520589
```
521590

522-
### 公众号:网页授权 URL 与用户资料
591+
### 微信公众平台:网页授权 URL 与用户资料
523592

524593
```php
525594
$redirectUri = 'https://example.com/oauth/callback';
526-
$url = 'https://open.weixin.qq.com/connect/oauth2/authorize?' . http_build_query([
527-
'appid' => 'wx_appid',
595+
$authUrl = $platform->get('connect/oauth2/authorize', [
528596
'redirect_uri' => $redirectUri,
529-
'response_type' => 'code',
530597
'scope' => 'snsapi_userinfo',
531598
'state' => 'state-value',
532-
]) . '#wechat_redirect';
599+
]);
600+
$url = $authUrl['url'];
533601

534-
$oauth = $official->get('sns/oauth2/access_token', [
602+
$oauth = $platform->get('sns/oauth2/access_token', [
535603
'appid' => 'wx_appid',
536604
'secret' => 'app_secret',
537605
'code' => $code,
538606
'grant_type' => 'authorization_code',
539607
], ['with_token' => false]);
540608

541-
$user = $official->get('sns/userinfo', [
609+
$user = $platform->get('sns/userinfo', [
542610
'access_token' => $oauth['access_token'],
543611
'openid' => $oauth['openid'],
544612
'lang' => 'zh_CN',
545613
], ['with_token' => false]);
546614
```
547615

548-
### 开放平台:授权回调后保存授权方 Token
616+
### 微信服务平台:授权回调后保存授权方 Token
549617

550618
```php
551619
$componentToken = $service->componentAccessToken($componentVerifyTicket);
@@ -647,7 +715,7 @@ WeChatDeveloper 只处理协议层和 HTTP 编排:
647715
- 不提供账号、租户、菜单草稿、订单、授权记录等业务表。
648716
- 不托管密钥加密存储。
649717
- 不绑定 Hyperf、Laravel、Symfony 等框架。
650-
- 不保证覆盖每一个官方接口别名,通用接口通过官方 path 调用
718+
- 不保证覆盖每一个官方接口别名;SDK 通过官方 path、JSON、raw/download、multipart upload 等协议层能力支持接口调用
651719

652720
这种边界使 SDK 更适合作为开源底层包,被后台系统、SaaS 平台或命令行工具组合使用。
653721

0 commit comments

Comments
 (0)