10 KiB
VR票务插件 C端 API 文档
版本: 1.1.0 最后更新: 2026-05-28
本文档描述了 VR 票务插件(vr_ticket)的 C 端 UniApp API,涵盖票夹、核销、核销记录三类接口,适用于移动端用户及授权核销员使用。
一、概述
1.1 核心机制
| 机制 | 说明 |
|---|---|
| 动态 QR Payload | QR 内容含 HMAC-SHA256 签名,默认 30 分钟 有效期,前端应动态刷新 |
| 悲观锁防并发 | 核销接口使用 FOR UPDATE 事务锁,杜绝重复核销 |
| 双重码格式 | 长码(UUID v4)与短码(Base36 + Feistel 混淆),API 自动识别 |
| 核销员白名单 | C 端用户必须存在于 vr_verifiers 表且 status=1 才可执行核销操作 |
1.2 响应标准格式
所有 API 均返回以下 JSON 结构:
{
"code": 0,
"msg": "success",
"data": { ... }
}
二、认证机制
2.1 请求头认证
所有 C 端 API 均需携带用户身份令牌。
Header Key: X-Token
Header Value: {user_token}
兜底方案(按优先级):
X-Token/Authorization: Bearer {token}(App 登录)user_infoCookie 解码(Web 登录)- Session(传统页面)
若均未找到,返回:
{
"code": 401,
"msg": "请先登录",
"data": []
}
2.2 核销员身份鉴权
以下 API 除了要求 C 端登录外,还需验证用户是否为授权核销员:
// 未授权(非核销员)
{
"code": -403,
"msg": "你不是授权核销员,无权核销",
"data": []
}
核销员身份由 vr_verifiers 表决定,管理员可在 ShopXO 后台添加。普通用户(非核销员)可正常使用票夹 API,但不能执行核销。
三、通用错误码
| code | 说明 |
|---|---|
0 |
成功 |
-1 |
通用失败 / 参数错误 |
-2 |
该票已核销 |
-3 |
该票已退款 |
-4 |
QR 数据异常 |
-403 |
无核销权限(不是授权核销员) |
-404 |
票不存在或无权访问 |
-999 |
系统异常(事务失败) |
401 |
未登录 / 未授权 |
四、票夹 API
基础路由:
/api.php?s=plugins/index&pluginsname=vr_ticket&pluginscontrol=ticket&pluginsaction={action}认证: C 端登录态(无需核销员身份)
4.1 获取用户票列表
GET ...&pluginsaction=list
GET ...&pluginsaction=tickets (别名)
成功响应 data:
{
"tickets": [
{
"id": 123,
"goods_id": 456,
"goods_title": "周杰伦演唱会-北京站",
"goods_image": "https://...jpg",
"seat_info": "2026-06-01 20:00|国家体育馆|主厅|A区|A1",
"seat_number": "A1",
"session_time": "2026-06-01 20:00",
"venue_name": "国家体育馆",
"real_name": "张三",
"phone": "138****5678",
"verify_status": 0,
"issued_at": 1716307200,
"short_code": "000ca1b2"
}
],
"count": 1
}
| 字段 | 类型 | 说明 |
|---|---|---|
verify_status |
int | 0=未核销 1=已核销 2=已退款 |
short_code |
string | 9位短码,可供核销员扫码 |
seat_info |
string | 完整 5 维坐席信息,场次|场馆|演播室|分区|座位号 |
4.2 获取票详情(含 QR Payload)
GET ...&pluginsaction=detail&id={ticket_id}
参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
id |
int | ✅ | 票 ID |
成功响应 data:
{
"ticket": {
"id": 123,
"goods_id": 456,
"goods_title": "周杰伦演唱会-北京站",
"goods_image": "https://...jpg",
"seat_info": "2026-06-01 20:00|国家体育馆|主厅|A区|A1",
"session_time": "2026-06-01 20:00",
"venue_name": "国家体育馆",
"seat_number": "A1",
"real_name": "张三",
"phone": "138****5678",
"verify_status": 0,
"verify_time": 0,
"issued_at": 1716307200,
"short_code": "000ca1b2",
"qr_payload": "eyJpZCI6MTIzLCJnIjo0NTYsImNvZGUiOiI4OTM1...",
"qr_expires_at": 1716309000,
"qr_expires_in": 1800
}
}
| 字段 | 类型 | 说明 |
|---|---|---|
qr_payload |
string | 二维码内容,含 HMAC-SHA256 签名,有效期 30 分钟 |
qr_expires_at |
int | 过期时间戳(秒) |
qr_expires_in |
int | 剩余有效期(秒),固定 1800 |
失败响应:
// 票不存在或无权访问
{ "code": -404, "msg": "票不存在或无权访问", "data": [] }
// 票已核销(不返回 QR)
{ "code": -2, "msg": "该票已核销", "data": [] }
// 票已退款
{ "code": -3, "msg": "该票已退款", "data": [] }
4.3 强制刷新二维码
重新生成 QR Payload,重置 30 分钟有效期。
GET ...&pluginsaction=refreshQr&id={ticket_id}
参数: 同 detail
成功响应: 与 detail 完全一致(qr_payload 为新生成)。
4.4 检测核销员身份
轻量接口,用于前端快速判断是否展示核销员入口。
GET ...&pluginsaction=checkVerifier
成功响应 data:
{
"is_verifier": true,
"verifier_id": 3,
"verifier_name": "张三"
}
| 字段 | 类型 | 说明 |
|---|---|---|
is_verifier |
bool | 是否为授权核销员 |
verifier_id |
int | 核销员 ID,未授权时为 0 |
verifier_name |
string | 核销员名称,未授权时为空字符串 |
五、核销 API(UniApp 授权核销员专用)
基础路由:
/api.php?s=plugins/index&pluginsname=vr_ticket&pluginscontrol=ticket&pluginsaction=verify认证: C 端登录态 + 必须是vr_verifiers表中status=1的授权核销员
5.1 扫码核销
POST ...&pluginsaction=verify
Content-Type: application/x-www-form-urlencoded
请求参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
ticket_code |
string | ✅ | 扫码得到的票码(短码或 UUID) |
自动识别规则:
- 长度 < 20 且不含连字符
-→ 短码(Base36 + Feistel 混淆) - 包含连字符
-→ UUID v4 长码
成功响应 (code: 0):
{
"code": 0,
"msg": "核销成功",
"data": {
"seat_info": "2026-06-01 20:00|国家体育馆|主厅|A区|A1",
"real_name": "张三",
"goods_name": "周杰伦演唱会-北京站"
}
}
失败响应:
| code | msg | 场景 |
|---|---|---|
-1 |
票码不存在 |
短码解码失败或票不存在 |
-2 |
该票已核销 |
重复核销 |
-3 |
该票已退款 |
票已退款 |
-403 |
你不是授权核销员,无权核销 |
C 端用户不在核销员白名单 |
401 |
请先登录 |
未登录 |
-999 |
核销失败,请重试 |
数据库事务异常 |
六、核销记录 API(UniApp 授权核销员专用)
基础路由:
/api.php?s=plugins/index&pluginsname=vr_ticket&pluginscontrol=ticket&pluginsaction=myVerifications认证: C 端登录态 + 必须是vr_verifiers表中status=1的授权核销员
6.1 我的核销记录
GET ...&pluginsaction=myVerifications
请求参数:
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
page |
int | ❌ | 1 |
页码 |
page_size |
int | ❌ | 20 |
每页条数(最大 100) |
成功响应 data:
{
"list": [
{
"id": 1,
"ticket_id": 123,
"ticket_code": "8935b3a3-a7b4-4e3d-8c1f-9b7e2a6f5d4c",
"seat_info": "2026-06-01 20:00|国家体育馆|主厅|A区|A1",
"real_name": "张三",
"goods_title": "周杰伦演唱会-北京站",
"created_at": 1716307200
}
],
"total": 50,
"page": 1,
"page_size": 20,
"pages": 3
}
失败响应:
| code | msg | 场景 |
|---|---|---|
-403 |
你不是授权核销员 |
非授权核销员 |
401 |
请先登录 |
未登录 |
七、QR 二维码前端最佳实践
7.1 动态刷新策略
- 倒计时渲染:使用
qr_expires_in启动本地倒计时器 - 过期遮罩:剩余时间 ≤ 0 时显示模糊/遮罩 + "点击刷新"按钮
- 静默刷新:过期前 30 秒自动调用
refreshQr重新获取并渲染
7.2 短码编码规则
短码结构:【4位 goods_id 明文 base36】【变长 ticket_id 混淆 base36】
- 解码 O(1):前 4 位直接取 goods_id,剩余部分用 Feistel 解密得到 ticket_id
- 示例:
000ca1b2→goods_id=0,ticket_id=12345 - 核销员 App 扫描时应获取完整短码字符串进行提交
附录:数据字典
vr_tickets 电子票表
| 字段 | 类型 | 说明 |
|---|---|---|
id |
int | 票 ID(自增) |
goods_id |
int | 关联商品 ID |
order_id |
int | 关联订单 ID |
user_id |
int | 购票用户 ID |
ticket_code |
string | UUID v4 长码 |
qr_data |
string | 格式 短码|payload,内部缓存 |
seat_info |
string | 场次|场馆|演播室|分区|座位号 |
verify_status |
int | 0=未核销 1=已核销 2=已退款 |
verify_time |
int | 核销时间戳 |
verifier_id |
int | 执行核销的核销员 ID |
real_name |
string | 观演人姓名 |
phone |
string | 观演人手机 |
issued_at |
int | 票发放时间戳 |
created_at |
int | 创建时间戳 |
vr_verifiers 核销员表
| 字段 | 类型 | 说明 |
|---|---|---|
id |
int | 核销员 ID(自增) |
user_id |
int | 关联的 C 端用户 ID(ShopXO 会员 ID) |
name |
string | 核销员名称(后台显示用) |
status |
int | 1=启用 0=禁用 |
created_at |
int | 创建时间戳 |
注意:
user_id关联的是 C 端用户 ID,而非后台管理员 ID。C 端用户只需在vr_verifiers表中存在且status=1即可使用 UniApp 核销功能。
vr_verifications 核销记录表
| 字段 | 类型 | 说明 |
|---|---|---|
id |
int | 记录 ID(自增) |
ticket_id |
int | 票 ID |
ticket_code |
string | 票长码快照 |
verifier_id |
int | 执行核销的核销员 ID |
verifier_name |
string | 核销员名称快照 |
goods_id |
int | 商品 ID |
created_at |
int | 核销时间戳 |