Skip to content

OpenAPI 开放接口

OpenAPI 模块为外部系统提供基于 AppKey + AppSecret 的开放接口能力。模块代码位于 modules/Openapi,后台页面位于 web/src/views/openapi,当前覆盖客户应用管理、签名认证、限流、余额、余额流水和请求日志。

模块定位

OpenAPI 模块可以分成两条链路:

链路作用入口
管理链路管理客户应用、密钥、余额和日志modules/Openapi/routes/route.php
调用链路校验外部请求、执行业务接口、返回统一响应routes/api.php

当前项目已经在 routes/api.php 中提供 /api/v1/user 示例路由,并挂载签名校验和限流中间件。二次开发时,新的开放接口通常继续放在同一组路由下。

模块组成

组件主要类责任
签名中间件CheckSignatureMiddleware生成请求 ID,校验 app-keysignature 和签名结果
限流中间件RateLimiterMiddleware按客户应用配置限制请求频率
签名服务OpenapiAuth校验应用、时间戳、签名,记录请求日志,更新最近调用时间
响应服务OpenapiResponse输出统一 JSON,并回填日志响应码、错误信息和耗时
客户应用Users保存应用信息、密钥、状态和最近使用时间
余额UserBalance保存客户当前余额
余额流水UserBalanceLog保存充值、扣费、调账、退款流水
请求日志OpenapiRequestLog保存请求参数、调用方、响应结果和耗时

后台菜单当前包含两页:

菜单页面说明
用户管理web/src/views/openapi/users管理客户应用、查看详情、充值、查看流水、重置密钥
请求日志web/src/views/openapi/openapiRequestLog查看调用参数、业务码、错误信息和耗时

数据模型

作用关键字段
openapi_users客户应用app_keyapp_secretqpsstatuslast_used_atsecret_reset_at
openapi_user_balance当前余额user_idbalance
openapi_user_balance_log余额流水typeamountbefore_balanceafter_balancerequest_id
openapi_request_log请求日志request_iduser_idapp_keypathresponse_codeduration_ms

余额流水类型

UserBalanceLogType 当前定义了 5 类流水:

类型
1充值
2扣费
3调账增加
4调账减少
5退款

当前已经落地的后台动作是“充值”,扣费、调账和退款仍保留在枚举层。

请求认证链路

链路里有两个容易忽略的事实:

  • 请求日志在 OpenapiAuth 完成应用、状态和时间戳校验后才写入;缺少请求头、无效应用、应用禁用、时间戳失效这几类请求当前不会生成日志记录。
  • 无效签名会先写入日志,再由 OpenapiResponse 回填失败业务码,便于按 request_id 排查。

签名规则

外部请求需要同时满足以下约定:

约定
请求头app-keysignature
请求参数必须包含 timestamp
时间窗口config('api.timestamp_period', 60),当前默认回退值为 60 秒
算法HMAC-SHA256
密钥客户应用的 app_secret

签名过程:

  1. 读取所有请求参数。
  2. 递归扁平化嵌套对象和数组。
  3. 按参数名排序。
  4. 拼成 key=value&key=value
  5. 使用 app_secret 生成 HMAC-SHA256 摘要。

扁平化示例:

json
{
  "user": {
    "name": "张三"
  },
  "items": [
    { "id": 1 }
  ],
  "timestamp": 1760000000
}

会生成以下键值:

text
items[0].id=1
timestamp=1760000000
user.name=张三

最终签名串:

text
items[0].id=1&timestamp=1760000000&user.name=张三

统一响应

开放接口应统一使用 OpenapiResponse

php
use Modules\Openapi\Facade\OpenapiResponse;

return OpenapiResponse::success(['ok' => true]);

return OpenapiResponse::error('failed');

成功响应示例:

json
{
  "code": 10000,
  "message": "success",
  "data": {
    "ok": true
  },
  "trace": {
    "request_id": "018f0000-0000-7000-9000-000000000000",
    "timestamp": 1760000000,
    "take_time": 12
  }
}

分页响应还会额外返回 totallimitpage

状态码

业务码常量说明
10000SUCCESS成功
10001FAILED通用失败
10002APP_KEY_LOST缺少 app-key
10003SIGNATURE_LOST缺少 signature
10004INVALID_APP_KEY应用不存在
10005INVALID_SIGNATURE签名失败
10006INVALID_TIMESTAMP时间戳失效
10007BALANCE_NOT_ENOUGH余额不足
10008RATE_LIMIT触发限流
10009APP_DISABLED应用已禁用

后台管理接口

后台接口定义在 modules/Openapi/routes/route.php

下表沿用后台前端使用的相对路径;实际 HTTP 路径会带上 /api/ 前缀。

Method接口用途
GETopenapi/users客户应用列表
POSTopenapi/users创建客户应用
GETopenapi/users/{id}客户应用详情
PUTopenapi/users/{id}更新客户应用
DELETEopenapi/users/{id}删除客户应用
PUTopenapi/users/enable/{id}启用或禁用客户应用
PUTopenapi/user/{id}/regenerate重置密钥
POSTopenapi/user/charge充值
GETopenapi/user/balance/log余额流水
GETopenapi/request/log请求日志
DELETEopenapi/request/log/{id}删除请求日志

客户应用

创建客户应用时,Users::booted() 会自动生成 app_keyapp_secret,并把缺省状态写成启用。更新时,空密码会被控制器剔除,原密码保持不变。

常用字段:

字段说明
username客户应用名称
mobile联系手机号,当前按手机号唯一校验
qps调用频率配置
status1 启用,2 禁用
balance_warning余额预警阈值
last_used_at最近一次签名成功时间
secret_reset_at最近一次密钥重置时间

充值

UsersController::charge() 使用事务和 lockForUpdate() 更新余额,再写入 openapi_user_balance_log。当前请求字段:

字段规则
user_id必填,必须存在于 openapi_users
balance必填,整数,最小值 1
remark可选,最多 500 字符

成功响应:

json
{
  "user_id": 1,
  "balance": 500
}

请求日志

请求日志页支持按 user_idapp_keyrequest_idpathresponse_code 查询。详情页会展示请求参数、业务码、HTTP 状态、错误信息和耗时。路由层当前使用 apiResource 注册请求日志资源,控制器已经实现的是列表和删除能力,后台页面也只消费这两条链路。

服务端接入

路由配置

当前项目已经在 routes/api.php 中提供示例:

php
use Illuminate\Support\Facades\Route;
use Modules\Openapi\Facade\OpenapiResponse;
use Modules\Openapi\Middlewares\CheckSignatureMiddleware;
use Modules\Openapi\Middlewares\RateLimiterMiddleware;

Route::prefix('v1')->middleware([
    CheckSignatureMiddleware::class,
    RateLimiterMiddleware::class,
])->group(function () {
    Route::any('user', function () {
        return OpenapiResponse::success([]);
    });
});

Laravel 会给 routes/api.php 自动添加 /api 前缀,所以示例地址是 /api/v1/user

最小调用示例

bash
curl -X POST 'https://your-domain.com/api/v1/user' \
  -H 'app-key: your_app_key' \
  -H 'signature: signature_by_hmac_sha256' \
  -H 'Content-Type: application/json' \
  -d '{"timestamp":1760000000}'

异常渲染

bootstrap/app.php 当前会把 OpenapiException 统一渲染为 OpenapiResponse::error()。对 URI 以 api/v1 开头的普通异常,也会走开放接口响应分支;其中 QueryException 当前仍返回 ApiResponse::error(),这是现有实现边界。

排障

每个通过 OpenapiResponse 返回的响应都会携带 trace.request_id。排障时优先按该字段定位日志:

sql
select *
from openapi_request_log
where request_id = 'response_trace_request_id'
limit 1;
字段用途
response_code判断失败类型
error_message查看服务端错误消息
app_key / user_id确认调用方
path / method确认目标接口
data复算签名
duration_ms判断耗时

常见定位方向:

业务码检查点
10004客户是否仍在使用旧密钥
10005扁平化、排序、app_secret 是否一致
10006客户端时间和服务端时间差
10008当前应用 qps 配置和调用频率
10009客户应用状态

模块已有自动化测试覆盖:

  • 新建应用默认启用
  • 禁用应用拒绝调用
  • 充值写入余额和流水
  • 重置密钥
  • 签名成功后更新最近调用时间并完成请求日志
bash
php artisan test --compact tests/Feature/Openapi/OpenapiAdminMvpTest.php

可扩展方向

当前实现已经留出了几条清晰的延展线:

  • routes/api.phpv1 分组中继续挂载新的开放接口。
  • 基于 UserBalanceLogType 增加扣费、调账和退款业务流。
  • OpenapiAuth 的应用查询增加缓存层。
  • 把请求日志扩展为异步写入或接入外部监控系统。
  • 为新的业务错误补充 Code 枚举和对应异常类。