尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
yansongda/pay 微信 Openapi 插件域与 OAuth 用户身份能力(v3.8)设计详解
后端金融科技【免费下载链接】pay可能是我用过的最优雅的 Alipay/WeChat/Unipay/江苏银行 的支付 SDK 扩展包了项目地址https://gitcode.com/yansongda/pay点击查看免费下载本篇基于仓库内设计文档 wechat-oauth-openapi.md 展开讲解 pay v3.8 中新增的Plugin/Wechat/Openapi/插件域如何在不影响纯支付用户的前提下让Pay::wechat()-oauth()一次性覆盖公众号网页授权web_token/refresh/userinfo与小程序 code2session 登录。读完你会掌握OAuth 四个_action的配置方式与调用链、GET 管道“业务插件自拼 URL query 空 body”的关键契约、/sns/前缀路由到api.weixin.qq.com的实现以及合并版Openapi\ResponsePlugin的 HTTP 状态码 errcode 双层校验顺序论证。一、背景与问题为什么微信侧需要一个独立的 Openapi 插件域v3.8当前最新 tag v3.8.0-beta.5之前SDK 支付宝侧已具备用户身份能力Plugin/Alipay/V2/Member/Authorization/*、DetailPlugin而微信侧仅有支付能力。要补齐“获取微信用户身份”公众号网页授权、小程序 code2session设计文档指出了三个具体困境必须另装 easywechat用户此前若想拿到微信用户身份只能在 pay 之外再引入 easywechat——即 issue #788 的诉求两年未闭环Wechat::URL_VIRTUAL命名名不副实微信虚拟支付模块已打通api.weixin.qq.com通路getWechatUrl()对/xpay/、/cgi-bin/前缀返回虚拟支付基地址但 OAuth 的/sns/*接口同样走api.weixin.qq.com而现有路由不认/sns/前缀已验证见 WechatTrait::getWechatUrl()管道对 GET 无 query 支持AddPayloadBodyPlugin会无条件打包 body而 OAuth 要求参数放在 URL query 中。由此确立的设计目标独立的 Openapi 插件域承接api.weixin.qq.com上与具体支付业务解耦的通用能力纯支付用户零影响新增配置字段全部可选、不改既有管道行为命名消除 token 歧义按接口场景统一命名web_token用户级、session会话级BC 变更明示且可控按已批准决策执行在 UPGRADE 文档记录。二、整体方案OAuth 走“自拼 URL query 空 body”的 GET 管道核心思路是新增Plugin/Wechat/Openapi/插件域OAuth 走「业务插件自拼 URL query 空 body」的 GET 管道统一响应校验合并为一个ResponsePlugin。完整调用链如下Pay::wechat()-oauth([_action session, js_code ...]) │ ▼ OauthShortcut_action → 业务插件 ┌─────────────────────────────────────────────────────────────┐ │ StartPlugin → Oauth业务插件 → Wechat AddRadarPlugin → │ │ Openapi\ResponsePlugin → ParserPlugin │ └─────────────────────────────────────────────────────────────┘ │ │ ▼ ▼ _url 自拼 /sns/**?query含secret getWechatUrl(): /sns/* → api.weixin.qq.com _body GET 无请求体这条管道与文档中 OauthShortcut 的实现逐位对应四个_action分别装配StartPlugin → 业务插件 → AddRadarPlugin → ResponsePlugin → ParserPlugin五个插件注意刻意不装AddPayloadBodyPlugin原因见第四节。对应的文件结构新增~修改»迁移引用均已在仓库中落地src/ ├── Provider/Wechat.php ~ URL_VIRTUAL → URL_OPENAPI ├── Traits/WechatTrait.php ~ /sns/ 分支 引用 GetStableTokenPlugin ├── Config/WechatConfig.php mpAppSecret / miniAppSecret可选 ├── Plugin/Wechat/Openapi/ 新插件域 │ ├── GetStableTokenPlugin.php » Virtual/GetAccessTokenPlugin 更名迁入/cgi-bin/stable_token │ ├── ResponsePlugin.php » Virtual/CheckResponsePlugin 迁入 合并 HTTP 状态检查 │ └── Oauth/ │ ├── WebAccessTokenPlugin.php /sns/oauth2/access_token网页授权用户级 │ ├── UserInfoPlugin.php /sns/userinfo │ ├── RefreshTokenPlugin.php /sns/oauth2/refresh_token用户级 │ └── Code2SessionPlugin.php /sns/jscode2session会话级 ├── Shortcut/Wechat/OauthShortcut.php 4 个 _action ├── Shortcut/Wechat/VirtualShortcut.php ~ 响应插件位合并 tests/ ~ 改 4 增 7 / 删 2迁自 Virtual 域 web/docs/v3/upgrade/v3.8.md ~ BREAKING CHANGES 3 条 bullet web/docs/v3/wechat/oauth.md 使用文档独立页面对齐 virtual.md 先例三、配置设计两个可选 secret 字段不入validateRequired()3.1 配置格式{ wechat: { default: { mp_app_id: 公众号appid, mp_app_secret: 公众号appsecret, mini_app_id: 小程序appid, mini_app_secret: 小程序appsecret } } }字段类型必填说明mp_app_secret?string否仅web_token/refresh需要mini_app_secret?string否仅session需要不加入validateRequired()secret 优先级为params 显式传入 → config仿虚拟支付惯例。3.2 源码印证WechatConfig 中新增了两个可空字段与对应 getter/setterprivate ?string $mpAppSecret null; private ?string $miniAppSecret null;其 validateRequired() 只校验mchId、mchSecretKey、mchSecretCert、mchPublicCertPath四项以及mch_secret_key必须 32 字节、服务商模式下sub_mch_id必填两个 OAuth secret 完全不在其中——这正是“纯支付用户零影响”的落地方式不配 secret支付管道照常工作配了 secretOAuth 能力才可用。缺凭据的错误推迟到具体业务插件中抛出见下一节的异常示例。四、GET 管道契约为什么是“自拼 query _body 不装 AddPayloadBodyPlugin”设计文档3.2 节给出两条已验证的关键契约getWechatBody()要求_body必存缺失抛PARAMS_WECHAT_BODY_MISSING故业务插件自设_body 且不装AddPayloadBodyPlugin否则会覆盖_body——GET 请求体为空_url以/sns/开头即可被新路由分支补全域名业务插件拼 query 对齐Virtual/AddPayloadSignaturePlugin::appendQueryParams先例。对照 WechatTrait 的实现public static function getWechatBody(?Collection $payload): mixed { $body get_radar_body($payload); if (is_null($body)) { throw new InvalidParamsException(Exception::PARAMS_WECHAT_BODY_MISSING, 参数异常: 微信 _body 参数缺失...); } return $body; }注意判断的是is_null($body)而非空串——业务插件写入_body 恰好绕过缺失检查同时保证 GET 请求不携带请求体。以 Code2SessionPlugin 为例其装配逻辑与设计文档伪代码一致$jsCode $params[js_code] ?? ; if (empty($jsCode)) { throw new InvalidParamsException(Exception::PARAMS_NECESSARY_PARAMS_MISSING, 参数异常: 缺少微信小程序登录参数 -- [js_code]); } $appid $params[appid] ?? $config-getMiniAppId(); $secret $params[secret] ?? $config-getMiniAppSecret(); if (empty($appid) || empty($secret)) { throw new InvalidConfigException(Exception::CONFIG_WECHAT_INVALID, 配置异常: 缺少微信配置 -- [mini_app_secret] 或 [mini_app_id]); } $rocket-mergePayload([ _method GET, _url /sns/jscode2session?.http_build_query([appid $appid, secret $secret, js_code $jsCode, grant_type authorization_code]), _body , ]);可以看出参数解析顺序就是文档声明的「params 显式传入 → config」js_code缺失抛InvalidParamsExceptionappid/secret 缺失抛InvalidConfigException与文档 3.2 节伪代码中的异常契约一一对应。OauthShortcutTest 则从测试侧验证了四个_action各自的管道插件序列及非法_action抛PARAMS_SHORTCUT_ACTION_INVALID。五、接口规格与关键契约五个 Openapi 插件的接口规格已验证微信官方文档 存档快照错误响应经真实 HTTP 实测插件请求query 参数WebAccessTokenPluginGET/sns/oauth2/access_tokenappid、secret、code、grant_typeauthorization_codeUserInfoPluginGET/sns/userinfoaccess_token、openid、lang可选默认zh_CNRefreshTokenPluginGET/sns/oauth2/refresh_tokenappid、grant_typerefresh_token、refresh_tokenCode2SessionPluginGET/sns/jscode2sessionappid、secret、js_code、grant_typeauthorization_codeGetStableTokenPluginPOST/cgi-bin/stable_tokenbody JSONgrant_typeclient_credential、appid、secret、force_refresh可选以 code2session 为例的等价 curl 与成功响应curl https://api.weixin.qq.com/sns/jscode2session?appidAPPIDsecretSECRETjs_codeJSCODEgrant_typeauthorization_code{ openid: xxx, session_key: xxx, unionid: xxx, errcode: 0, errmsg: xxx }关键契约这些细节直接决定上层代码怎么写参数全小写appid/js_code/grant_typegrant_typeclient_credential为单数官方原文成功/失败不依赖 HTTP 状态码实测错误也返回 HTTP 200错误统一{errcode: int, errmsg: ...}errmsg常带rid:uuid后缀——代码不得按 errmsg 全文匹配code2session成功响应自带errcode:0与null ! errcode 0 ! errcode判断兼容已验证公众号/小程序 appidsecret 是独立凭据对不互通unionid需开放平台绑定不硬编码假设。对照源码UserInfoPlugin 中lang $params[lang] ?? zh_CN正是“可选默认 zh_CN”的落地RefreshTokenPlugin 中grant_type固定为refresh_token且只需mp_app_id不需要 secret与规格表一致GetStableTokenPlugin 只负责声明_method POST与_url /cgi-bin/stable_tokenbody 由管道中的AddPayloadBodyPlugin打包getWechatVirtualAccessToken()内部管道即此用法见 WechatTrait。六、Openapi/ResponsePlugin合并两层校验行为顺序等价性论证微信开放接口响应不在 HTTP Header 中携带签名因此统一校验插件只检查两层HTTP 状态码 业务 errcode。ResponsePlugin 的实现与设计文档伪代码一致if (!should_do_http_request($rocket-getDirection()) || is_null($rocket-getDestinationOrigin())) { return $rocket; // 原 CheckResponse 短路 } if ($response-getStatusCode() 200 || $response-getStatusCode() 300) { throw new InvalidResponseException(Exception::RESPONSE_CODE_WRONG, 微信开放接口返回状态码异常请检查参数是否错误, ...); // 原 Wechat\ResponsePlugin 职责 } if (null ! $errcode 0 ! $errcode) { throw new InvalidResponseException(Exception::RESPONSE_BUSINESS_CODE_WRONG, 微信开放接口返回业务异常: .$destination-get(errmsg), ...); // 原 CheckResponse 职责 }行为顺序论证设计文档 3.4 节已验证读了Artful::artful()/Artful::ignite()、supportsPipeline、artfulParserPlugin与双侧插件源码HTTP 请求由Artful::ignite()在管道最内层发出Artful.php:264的-then(fn ($rocket) self::ignite($rocket))ignite内$http-sendRequest(...)并setDestination(clone $response)-setDestinationOrigin(...)——任何状态码含 500都会设置 destinationParserPlugin只负责$direction-guide()解包响应体不发请求。supportsPipeline为array_reduce(array_reverse($pipes), ...)洋葱模型Pipeline.php:47-49原管道数组[..., AddRadar, CheckResponse, Response, Parser]的弹栈顺序为 ignite发 HTTP→ Parser 后置 →Wechat\ResponsePlugin后置HTTP 检查先抛→CheckResponsePlugin后置errcode 检查后抛。故HTTP 状态检查先于 errcode 检查。合并插件按「HTTP 先、errcode 后」顺序与原管道一致HTTP 检查抛异常时 errcode 检查不会执行而非「HTTP 错误时无 destination」。已知理论边界不可达仅记录direction 为NoHttpRequestDirection且 destinationOrigin 为非 2xxResponse时原Wechat\ResponsePlugin无should_do_http_request守卫会抛RESPONSE_CODE_WRONG合并版守卫短路不抛SDK 内无产生该状态的代码路径。连锁影响方面VirtualShortcut::serverSidePlugins() 原来的CheckResponsePlugin ResponsePlugin两个位置已合为Openapi\ResponsePlugin一个管道插件数 8→7存量测试testHttpStatusCodeErrorStillChecksErrcode的断言由RESPONSE_BUSINESS_CODE_WRONG改为RESPONSE_CODE_WRONG单插件直测视角下 HTTP 检查先执行并新增管道级用例mock HTTP 500 errcode body →RESPONSE_CODE_WRONG验证真实弹栈中 HTTP 检查先抛errcode 业务异常路径由单插件用例HTTP 200 errcode 43001 →RESPONSE_BUSINESS_CODE_WRONG覆盖。getWechatVirtualAccessToken()内部管道仅换类引用GetStableTokenPlugin其Wechat\ResponsePlugin不动。异常文案变化微信虚拟支付返回业务异常/微信返回状态码异常→微信开放接口返回业务异常/微信开放接口返回状态码异常作为 BC 申报进 UPGRADE bullet异常码RESPONSE_CODE_WRONG/RESPONSE_BUSINESS_CODE_WRONG不变。七、兼容性设计BC 清单与getWechatUrl()新路由变更类型影响面已验证Wechat::URL_VIRTUAL删除 →URL_OPENAPIchange定义 1 处 引用 1 处Virtual\GetAccessTokenPlugin→Openapi\GetStableTokenPluginchangeWechatTrait 2 处 1 测试Virtual\CheckResponsePlugin→Openapi\ResponsePlugin合并changeVirtualShortcut 1 处 tests/Provider/WechatTest.php10 处4 用例tests/Stubs/Plugin/VirtualCheckResponsePluginStub.phpextends 1 测试其余 Virtual 业务插件AddPayloadSignaturePlugin、Order/Currency/Goods/Subscribe/Withdraw不动语义与虚拟支付业务绑定docs/fix-1186.md与docs/learning/fix-1186.md旧类名字符串不动历史设计记录路由改名后Wechat::URL_OPENAPI 定义为https://api.weixin.qq.comgetWechatUrl() 的新路由规则为http 开头 → 原样 | requestVirtualPayment → 原样 /xpay/ | /cgi-bin/ | /sns/ → URL_OPENAPI url ← 新增 /sns/ 其余 → URL[mode] url源码中该分支已按设计落地WechatTrait.php#L65-L68// 微信开放接口虚拟支付 /xpay/*、接口凭据 /cgi-bin/*、OAuth /sns/*使用 api.weixin.qq.com非 api.mch.weixin.qq.com if (str_starts_with($url, /xpay/) || str_starts_with($url, /cgi-bin/) || str_starts_with($url, /sns/)) { return Wechat::URL_OPENAPI.$url; }/sns/前缀由此被纳入开放接口路由消除了URL_VIRTUAL名不副实的问题而/xpay/、/cgi-bin/的既有行为保持不变。八、使用示例四个_action的调用与返回_action常量定义在 WechatActionweb_token/refresh/userinfo/session入口为 Wechat 提供者 上声明的oauth(array $order)快捷方法通过__call路由到OauthShortcut。配置与调用示例// 仅小程序登录session需 mini_app_id mini_app_secret Pay::config([wechat [default [ mch_id ..., mch_secret_key ..., mch_secret_cert ..., mch_public_cert_path ..., mini_app_id wx..., mini_app_secret ..., ]]]); // 网页授权web_token/refresh需另配 mp_app_id mp_app_secret按 3.1 字段表缺对应凭据会抛 InvalidConfigException Pay::wechat()-oauth([_action session, js_code ...]); Pay::wechat()-oauth([_action web_token, code ...]); Pay::wechat()-oauth([_action userinfo, access_token ..., openid ...]); Pay::wechat()-oauth([_action refresh, refresh_token ...]);各 action 的返回字段详见 oauth.mdsessionopenid用户唯一标识、session_key会话密钥用于数据签名校验与用户敏感数据解密请勿下发给前端之外的任何端、unionid需绑定微信开放平台未绑定时无该字段web_tokenaccess_token网页授权接口调用凭证用户级与基础access_token即stable_token应用级凭证互相隔离、expires_in有效期秒、refresh_token、openid、unionidscope 为snsapi_userinfo时返回、is_snapshotuser快照页模式虚拟账号时返回值为 1此时 openid 为虚拟账号userinfoopenid、nickname、sex1 男性2 女性0 未知、province、city、country如中国为 CN、headimgurl、privilegeJSON 数组、unionid需绑定开放平台refresh新的access_token、expires_in、refresh_token、openid、scope逗号分隔的授权作用域。九、风险与对策、监控可观测性风险严重度对策secret 进 URL query AddRadarPlugin日志打印 rocket_url含 secret中仓库无脱敏先例已验证沿用 virtual.md 惯例oauth.md 醒目风险提示_body已消除 body 侧暴露BC 变更影响自维护插件的用户中UPGRADE/v3.8.md 三条change:bullet issue #788 回复快照用户is_snapshotuser1时 openid 为虚拟账号低oauth.md 明确提示code一次性且 5 分钟过期官方原文低文档注明业务侧责任errmsg带rid:后缀低代码不匹配全文仅透传userinfo需snsapi_userinfo授权否则 48001低文档标注授权要求session_key敏感性低文档警示此外注意开启 Artful 日志logger.enable true时日志会包含带 secret 的 URL生产环境需注意日志安全。SDK 库无独立监控通道沿用 Artful Logger 现有 debug/info 通道每个 Openapi 插件都有成对的Logger::debug/Logger::info装配日志如 Code2SessionPluginsecret 落盘风险由文档提示覆盖。十、推进策略与仓库内验证路径设计文档规划的单 PR 三阶段逐阶段可验证、整体可 revert迁移与路由URL_OPENAPI/sns/分支 GetStableTokenPlugin/ResponsePlugin迁入更名 UPGRADE bullet → 验证存量测试全绿新增能力WechatConfig 两字段 Oauth 4 插件 OauthShortcut 单测 → 验证composer test绿文档oauth.md sidebar 注册 → 验证docs build。回滚方式为git revert整 PR。仓库中可对照验证的路径插件实现src/Plugin/Wechat/Openapi/含Oauth/四个业务插件入口与路由OauthShortcut、WechatTrait::getWechatUrl()、Wechat::URL_OPENAPI配置WechatConfigmpAppSecret/miniAppSecret字段与可选性测试OauthShortcutTest、tests/Plugin/Wechat/Openapi/GetStableTokenPluginTest、ResponsePluginTest、Oauth/四个业务插件用例用户文档web/docs/v3/wechat/oauth.md。小结这套设计用“新增插件域 可选配置 精确路由分支”的三板斧把一个拖了两年、诉求明确的功能issue #788以最小 BC 代价落进了既有管道其最有复用价值的两处经验是——GET 场景下“业务插件自拼 query 并显式置空_body、绕开AddPayloadBodyPlugin”的契约写法以及“合并响应校验插件时必须以管道洋葱弹栈顺序论证行为等价性”的方法论。赞分享后端金融科技【免费下载链接】pay可能是我用过的最优雅的 Alipay/WeChat/Unipay/江苏银行 的支付 SDK 扩展包了项目地址https://gitcode.com/yansongda/pay点击查看免费下载相关推荐微信 Openapi 插件域与 OAuth 用户身份yansongda/pay 公众号网页授权与小程序登录集成指南微信 Openapi 插件域与 OAuth 用户身份yansongda/pay 公众号网页授权与小程序登录集成指南 本文基于 yansongda/pay 的金融科技后端Pay 微信 OAuth 用户身份能力实战指南公众号网页授权与小程序登录Pay 微信 OAuth 用户身份能力实战指南公众号网页授权与小程序登录 本文以 yansongda/pay 本项目仓库根目录为 gh_mirrors/p金融科技后端yansongda/pay 微信插件全指南从内置插件到自定义组合调用微信支付 APIyansongda/pay 微信插件全指南从内置插件到自定义组合调用微信支付 API 本文以 yansongda/pay 开源支付 SDK 的插件机制为线索金融科技后端上一篇阿里云盘批量重命名终极指南告别手动操作效率提升500%下一篇百度网盘下载神器揭秘5个技巧让你的下载速度飞起来创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED

相关推荐

Lunar 2.0.0 Sync Mode 深度解析:让外接显示器的亮度对比度跟随 MacBook 内置屏自动同步

Lunar 2.0.0 Sync Mode 深度解析:让外接显示器的亮度对比度跟随 MacBook 内置屏自动同步

桌面应用 【免费下载链接】Lunar Intelligent adaptive brightness for your external monitors 项目地址: https://gitcode.com/gh_mirrors/lu/Lunar 点击查看 免费下载 本文围绕 Lunar 2.0.0 的发布说明 ReleaseNotes/2.0.0.md 展开,讲清楚该版本引入…

📅 2026/10/15 3:17:49
MutatingAdmissionPolicy 实践指南:用声明式策略替代 Mutating Webhook

MutatingAdmissionPolicy 实践指南:用声明式策略替代 Mutating Webhook

MutatingAdmissionPolicy 实践指南:用声明式策略替代 Mutating Webhook 【免费下载链接】website Kubernetes website and documentation repo: 项目地址: https://gitcode.com/GitHub_Trending/webs/website Kubernetes 的 MutatingAdmissionPolicy 是一种…

📅 2026/10/15 3:12:48
[Loop Engineering在MAF中的实现-02]循环调用在LoopAgent中的实现

[Loop Engineering在MAF中的实现-02]循环调用在LoopAgent中的实现

在上一篇文章中,我们介绍了Loop Engineering基本原理,并通过两个简单的示例分别演示使用LoopAgent和HarnessAgent来实现Agent循环调用的基本方法。在本篇文章中,我们将介绍LoopAgent在MAF的中定义和背后的设计和实现原理。 1. 利用LoopEvalu…

📅 2026/10/15 3:12:48
MORE NEWS

更多资讯

📰

离散元分析(DEM)和机器学习(Machine Learning, ML)

文章目录1. 机器学习辅助2PFV-DEM参数标定2. GNN DEM:最值得作为创新点3. AI预测非饱和土土水特征曲线4. 深度学习识别SEM/MIP细观结构 → DEM胶结模型5. 论文最推荐的AI路线第一阶段第二阶段第三阶段(创新)第四阶段最终可以形成的论文题目方…

📰

第五章 检索增强与知识库

前面四章已经让 Agent 具备了工具调用、多步规划、错误恢复和记忆能力。但它还有一个重要限制:模型只能依靠训练数据和当前上下文回答问题。如果用户问的是公司内部文档、产品手册、私有知识库,或者最近刚发生的事件,模型就可能答不上来&…

📰

东北农业大学转专业怎么申请?2025级条件、流程和热门学院考核一文讲透

东北农业大学2025级本科生集中转专业安排在2026年春季进行,3上旬起线上填报,每名学生限报1个专业志愿。申请人须未受学校纪律处分、已修课程全部及格,补考及格也可参加;通过学校资格审核后,再按目标学院规则参加考核。…

📰

Docker查看卷的存储路径

概述 docker-compose.yml中使用了volume,好处是首次启动时,会自动拷贝容器内挂载内容到卷中并持久化,坏处是持续化卷的路径不好找。以下记录查找方式 docker-compose.yaml配置 docker-compose.yml中定义的卷和挂载路径 services:wazuh.manage…

📰

中英文AI率直降60%!论文降重一站式学术工具

现在做学术写作、写英文作业,基本都绕不开AI检测,很多同学写完稿子,去 zerogpt、AI Detector、Copyleaks、turnitin、Originality 检测全部超标,想找靠谱的 免费在线查ai率英文、英文aigc检测、英文ai率检测免费 工具自查&#xf…

📰

PoB离线Build规划:天赋树与伤害计算一次算清

PoB离线Build规划:天赋树与伤害计算一次算清 【免费下载链接】PathOfBuilding Offline build planner for Path of Exile. 项目地址: https://gitcode.com/GitHub_Trending/pa/PathOfBuilding 凌晨两点,你又一次在天赋树上手动数格子:…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬