支付 SDK 的架构与授权模块定位:SDK 分层、授权服务接口、依赖关系
支付 SDK 采用分层可插拔架构设计,每层职责单一、通过接口契约通信。授权模块作为服务编排层的核心组件,向下依赖 管理与网络传输,向上为商户提供统一的认证入口。本章从整体分层切入,逐步收敛到授权模块的接口定义与依赖关系,为后续的 获取、刷新、验证等实现环节奠定架构基础。
1. SDK 分层架构设计
SDK 内部划分为四层,外加调用方(商户应用系统)构成完整调用链路。每层只依赖其直接下层,禁止跨层调用。
| 层次 | 职责 | 关键组件 / 模块 |
|---|---|---|
| 商户应用系统 | 集成 SDK 的业务系统,发起支付、查询、对账等请求 | 商户后端服务、客户端 App |
| SDK Facade 接口层 | 统一 API 入口,参数校验、请求路由、异常转换 | PaymentClient、AuthorizationClient |
| 服务编排层 | 业务逻辑编排,多模块协同完成原子操作 | 授权模块、支付模块、对账模块 |
| 核心协议层 | 管理、签名验签、协议序列化 | TokenManager、Signer |
| 网络通信层 | 底层 HTTP/gRPC 传输、连接池、重试与超时 | HttpClient、RetryInterceptor |
核心原则:授权模块只通过接口依赖核心协议层的
TokenManager,不直接操作网络连接或加密算法。各层通过依赖注入(DI)解耦,方便单元测试与模块替换。
2. 授权模块在服务层中的定位
授权模块归属于服务编排层,与支付模块、对账模块平级。它封装了与 生命周期相关的全部业务逻辑,对外提供三个核心能力:
- 获取:使用客户端凭据(client_id + client_secret)或授权码向认证服务器申请 access_ + refresh_。
- 刷新:在 access_ 即将过期时,使用 refresh_ 无感续期,避免支付流程中断。
- 验证:对每次支付/查询请求校验 access_ 的有效性、作用域和签名。
常见误区:不要将 的存储与解析逻辑放在网络层或 Facade 层。授权模块必须作为独立编排单元存在,否则会导致 更新时多处耦合、难以维护。
3. 授权服务接口规范
SDK 通过 AuthorizationService 接口暴露授权能力。接口设计遵循无状态 + 幂等原则,每次调用携带完整上下文,不依赖内部缓存。
public interface AuthorizationService {
/**
* 获取访问令牌(客户端凭证模式)
* @param clientId 商户应用ID
* @param clientSecret 商户应用密钥
* @param scopes 请求的权限范围列表
* @return TokenResponse 包含 accessToken、refreshToken、expiresIn
*/
TokenResponse getAccessToken(String clientId,
String clientSecret,
List<String> scopes);
/**
* 刷新访问令牌
* @param refreshToken 上次获取的刷新令牌
* @return TokenResponse 新的访问令牌与刷新令牌
*/
TokenResponse refreshAccessToken(String refreshToken);
/**
* 验证访问令牌有效性
* @param accessToken 待验证的令牌
* @param requiredScopes 要求的权限范围
* @return TokenValidationResult 包含有效标志、过期时间、实际作用域
*/
TokenValidationResult verifyToken(String accessToken,
List<String> requiredScopes);
/**
* 撤销指定令牌(主动失效)
* @param 待撤销的 accessToken 或 refreshToken
*/
void revokeToken(String );
}
| 方法 | 触发时机 | 幂等性 | 典型错误 |
|---|---|---|---|
getAccessToken |
SDK 初始化 / 首次获取 | 是(相同参数返回相同结果) | clientSecret 错误、scope 超出授权范围 |
refreshAccessToken |
access_ 过期前 5 分钟(由调度器触发) | 是(重复刷新返回最新有效 ) | refresh_ 已过期、已被撤销 |
verifyToken |
每次支付 / 查询请求进入时 | 是(纯校验,无副作用) | 篡改、签名不匹配、requiredScopes 缺失 |
revokeToken |
商户主动 logout / 安全策略触发 | 否(重复撤销返回成功,无副作用) | 已过期仍尝试撤销 |
接口设计技巧:
getAccessToken 与 refreshAccessToken 返回相同的 TokenResponse 结构体,上层调用者无需区分 来源。验证接口 verifyToken 通过本地签名校验实现毫秒级响应,无需每次请求都回源认证服务器。
4. 模块依赖关系
授权模块的依赖分为内部 SDK 依赖和外部第三方依赖两类。合理的依赖管理是保证模块可测试、可替换的关键。
4.1 内部依赖(SDK 模块间)
| 依赖方向 | 被依赖模块 | 依赖目的 |
|---|---|---|
| 授权模块 → 核心协议层 | TokenManager |
创建、签名、序列化 对象 |
| 授权模块 → 核心协议层 | Signer |
请求签名与验签 |
| 授权模块 → 网络通信层 | HttpClient |
发送 获取/刷新请求到认证服务器 |
| SDK Facade → 授权模块 | AuthorizationService |
对外暴露授权接口 |
4.2 外部依赖(第三方库)
以下为 Maven 风格的依赖声明,授权模块仅引入 JSON 解析与 HTTP 客户端两个基础库:
<!-- 支付 SDK 核心依赖 (包含授权模块) -->
<dependency>
<groupId>com.payment.sdk</groupId>
<artifactId>payment-sdk-auth</artifactId>
<version>2.1.0</version>
</dependency>
<!-- JSON 序列化 ( 载荷解析) -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.2</version>
</dependency>
<!-- HTTP 客户端 (认证服务器通信) -->
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<version>5.2.1</version>
</dependency>
依赖管理要点:
- 所有外部依赖声明为
compile范围,SDK 打包时自动传递。 - Jackson 与 HttpClient 的版本号统一集中在
dependencyManagement中管理,避免版本冲突。 - 授权模块不直接依赖任何数据库或缓存中间件—— 存储由调用方通过
TokenStore接口注入。
5. 设计要点与常见误区
- 严禁跨层依赖:授权模块只能调用核心协议层和网络通信层的接口,不得直接访问商户应用系统的数据源。
- 接口版本化:
AuthorizationService从 v1 版本开始定义,后续新增方法必须通过接口继承或默认方法实现,避免破坏已有实现。 - 存储由调用方决定:授权模块只负责生成和验证 ,不负责持久化。商户可通过实现
TokenStore接口将 存入 Redis、数据库或内存。
常见错误:
1. 在授权模块内部缓存 导致多实例环境 不一致。
2. 在 HTTP 客户端层直接解析 响应体,绕过授权模块的签名校验。
3. 未将
1. 在授权模块内部缓存 导致多实例环境 不一致。
2. 在 HTTP 客户端层直接解析 响应体,绕过授权模块的签名校验。
3. 未将
clientSecret 配置为独立参数,硬编码在授权模块代码中。
通过以上分层设计与接口规范,授权模块在 SDK 中具备了清晰的边界、可替换的实现以及严格的依赖管控。下一阶段将进入具体的 获取与验证实现环节。