@@ -81,6 +81,75 @@ $menu = $official->post('cgi-bin/menu/create', [
8181]);
8282```
8383
84+
85+ ## 调用约定
86+
87+ SDK 不把官方接口包装成大量固定方法,核心约定是“官方文档 path + 参数数组”:
88+
89+ ``` php
90+ // GET:第二个参数会作为 query string。
91+ $result = $official->get('cgi-bin/user/get', ['next_openid' => '']);
92+
93+ // POST:第二个参数默认作为 JSON body。
94+ $result = $official->post('cgi-bin/message/custom/send', [
95+ 'touser' => 'openid',
96+ 'msgtype' => 'text',
97+ 'text' => ['content' => 'hello'],
98+ ]);
99+
100+ // call:显式指定 HTTP 方法,并可透传 Guzzle options。
101+ $result = $official->call('cgi-bin/menu/get', [], 'GET');
102+ ```
103+
104+ 调用时需要注意:
105+
106+ | 场景 | 写法 |
107+ | ------| ------|
108+ | 微信普通接口需要 ` access_token ` | 默认自动附加。 |
109+ | 微信授权、登录等不需要 ` access_token ` 的接口 | 传 ` ['with_token' => false] ` 。 |
110+ | POST JSON | 默认行为,直接传 ` $params ` 。 |
111+ | GET query | 用 ` get($path, $query) ` 。 |
112+ | 自定义 query + JSON body | ` post($path, $body, ['query' => [...], 'json' => $body]) ` 。 |
113+ | 表单提交或原始 body | 透传 Guzzle 的 ` form_params ` 或 ` body ` 。 |
114+
115+ 示例:小程序登录接口不需要 access token,应该关闭自动 token:
116+
117+ ``` php
118+ $session = $wxapp->get('sns/jscode2session', [
119+ 'appid' => 'wx_appid',
120+ 'secret' => 'app_secret',
121+ 'js_code' => 'login_code',
122+ 'grant_type' => 'authorization_code',
123+ ], ['with_token' => false]);
124+ ```
125+
126+ ## 配置来源示例
127+
128+ 生产项目通常从数据库或配置中心读取账号配置,再使用 ` fromArray() ` 构造配置对象:
129+
130+ ``` php
131+ use We\Client;
132+ use We\Config\WechatPlatformConfig;
133+ use We\Support\FileCacheStore;
134+
135+ $row = [
136+ 'appid' => 'wx_appid',
137+ 'appsecret' => 'app_secret',
138+ 'token' => 'message_token',
139+ 'encoding_aes_key' => 'encoding_aes_key',
140+ 'storage_scope' => 'tenant:10001:account:20002',
141+ ];
142+
143+ $client = new Client(
144+ cache: new FileCacheStore(__DIR__ . '/runtime/wechat-cache'),
145+ cacheKeyPrefix: 'my_project_prod',
146+ );
147+
148+ $official = $client->wechatPlatform(WechatPlatformConfig::fromArray($row));
149+ ```
150+
151+ ` cacheKeyPrefix ` 建议按项目和环境区分,例如 ` mall_prod ` 、` mall_test ` 。` storage_scope ` 建议按租户、账号或业务线区分,避免同一 appid 在不同业务上下文中复用缓存。
152+
84153## 入口 Client
85154
86155``` php
@@ -289,6 +358,8 @@ $plain = $official->post('decrypt_message', [
289358
290359## 微信小程序
291360
361+ 登录换取 ` openid ` 与 ` session_key ` :
362+
292363``` php
293364use We\Config\WechatWxappConfig;
294365
@@ -297,12 +368,24 @@ $wxapp = $client->wechatWxapp(new WechatWxappConfig(
297368 appSecret: 'app_secret',
298369));
299370
300- $result = $wxapp->get('sns/jscode2session', [
371+ $session = $wxapp->get('sns/jscode2session', [
372+ 'appid' => 'wx_appid',
373+ 'secret' => 'app_secret',
301374 'js_code' => 'login_code',
302375 'grant_type' => 'authorization_code',
376+ ], ['with_token' => false]);
377+ ```
378+
379+ 获取手机号:
380+
381+ ``` php
382+ $phone = $wxapp->post('wxa/business/getuserphonenumber', [
383+ 'code' => 'phone_code_from_client',
303384]);
304385```
305386
387+ 如果官方接口返回图片、文件等二进制内容,默认 ` post() ` 会按 JSON 响应解析,不适合直接处理;建议注入自定义 Guzzle 客户端或在业务侧扩展专用下载方法。
388+
306389## 微信开放平台
307390
308391``` php
@@ -405,6 +488,113 @@ $page = $pay->post('page', [
405488]);
406489```
407490
491+
492+ ## 常见业务案例
493+
494+ ### 公众号:创建菜单并发送客服消息
495+
496+ ``` php
497+ $official->post('cgi-bin/menu/create', [
498+ 'button' => [
499+ [
500+ 'name' => '服务',
501+ 'sub_button' => [
502+ ['type' => 'view', 'name' => '官网', 'url' => 'https://example.com'],
503+ ['type' => 'click', 'name' => '帮助', 'key' => 'HELP'],
504+ ],
505+ ],
506+ ],
507+ ]);
508+
509+ $official->post('cgi-bin/message/custom/send', [
510+ 'touser' => 'openid',
511+ 'msgtype' => 'text',
512+ 'text' => ['content' => '您好,客服消息已发送。'],
513+ ]);
514+ ```
515+
516+ ### 公众号:网页授权 URL 与用户资料
517+
518+ ``` php
519+ $redirectUri = 'https://example.com/oauth/callback';
520+ $url = 'https://open.weixin.qq.com/connect/oauth2/authorize?' . http_build_query([
521+ 'appid' => 'wx_appid',
522+ 'redirect_uri' => $redirectUri,
523+ 'response_type' => 'code',
524+ 'scope' => 'snsapi_userinfo',
525+ 'state' => 'state-value',
526+ ]) . '#wechat_redirect';
527+
528+ $oauth = $official->get('sns/oauth2/access_token', [
529+ 'appid' => 'wx_appid',
530+ 'secret' => 'app_secret',
531+ 'code' => $code,
532+ 'grant_type' => 'authorization_code',
533+ ], ['with_token' => false]);
534+
535+ $user = $official->get('sns/userinfo', [
536+ 'access_token' => $oauth['access_token'],
537+ 'openid' => $oauth['openid'],
538+ 'lang' => 'zh_CN',
539+ ], ['with_token' => false]);
540+ ```
541+
542+ ### 开放平台:授权回调后保存授权方 Token
543+
544+ ``` php
545+ $componentToken = $service->componentAccessToken($componentVerifyTicket);
546+ $auth = $service->queryAuth($componentToken, $authorizationCode);
547+
548+ $authorization = $auth['authorization_info'] ?? [];
549+ $authorizerAppid = (string)($authorization['authorizer_appid'] ?? '');
550+
551+ // 业务系统应把 authorizer_refresh_token 保存到数据库,后续 StoreTokenInterface 会读取它。
552+ $repository->saveAuthorizerToken($authorizerAppid, $authorization);
553+ ```
554+
555+ ### 微信支付:创建 JSAPI 订单
556+
557+ ``` php
558+ $order = $payment->post('v3/pay/transactions/jsapi', [
559+ 'appid' => 'wx_appid',
560+ 'mchid' => 'mch_id',
561+ 'description' => '测试订单',
562+ 'out_trade_no' => 'T202605020001',
563+ 'notify_url' => 'https://example.com/wechat-pay/notify',
564+ 'amount' => ['total' => 1, 'currency' => 'CNY'],
565+ 'payer' => ['openid' => 'openid'],
566+ ]);
567+ ```
568+
569+ 前端调起支付需要的 ` paySign ` 可由业务系统使用返回的 ` prepay_id ` 再按微信支付文档签名生成。SDK 只负责 APIv3 请求签名、回调验签与资源解密。
570+
571+ ### 支付宝:电脑网站支付与退款
572+
573+ ``` php
574+ $page = $pay->post('page', [
575+ 'out_trade_no' => 'P202605020001',
576+ 'total_amount' => '0.01',
577+ 'subject' => '测试订单',
578+ 'product_code' => 'FAST_INSTANT_TRADE_PAY',
579+ ], [
580+ 'return_url' => 'https://example.com/alipay/return',
581+ 'notify_url' => 'https://example.com/alipay/notify',
582+ ]);
583+
584+ $refund = $pay->post('refund', [
585+ 'out_trade_no' => 'P202605020001',
586+ 'refund_amount' => '0.01',
587+ 'refund_reason' => '用户退款',
588+ ]);
589+ ```
590+
591+ ## 框架集成建议
592+
593+ - 在 Laravel、Hyperf、Symfony 等框架中,建议把 ` Client ` 注册为容器服务,缓存实现接入框架 Redis 或 Cache 组件。
594+ - 多租户系统应把租户 ID、账号 ID 放入 ` storage_scope ` 或 ` cacheKeyPrefix ` ,保证 token 缓存隔离。
595+ - 密钥、证书、APIv3 Key、支付宝私钥应由业务系统加密保存,运行时解密后传入配置对象。
596+ - 日志中不要记录 app secret、access token、refresh token、私钥、证书、回调密文和支付签名。
597+
408598## 异常处理
409599
410600SDK 抛出的异常基类为:
0 commit comments