Skip to content

VIO 对外 API 参考(External API)

本文档说明如何通过 API Key 调用 VIO 对外接口;文中路径、请求与响应字段名与正式环境一致。

目录


1. 概述(Overview)

什么是 VIO External API?

VIO External API 供租户以编程方式将自有系统与 VIO 平台对接。可通过本 API:

  • 管理礼券(创建、更新、删除、核销)
  • 管理用户及其数据
  • 创建并管理礼券活动
  • 处理代币、余额与交易
  • 获取分析与报表数据

基础 URL(Base URL)

https://{your-domain}/api/external/v1

所有接口路径均相对于上述基础 URL。

API 版本(Versioning)

当前 API 发布版本为 1.1.0,URL 路径版本保持为 v1。当出现破坏性变更时将发布新的 URL 路径版本,并尽可能保持旧版本可用。

架构示意(Architecture)

┌─────────────────────────────────────────────────────────────────┐
│                       你的应用                                   │
│  ┌───────────────────────────────────────────────────────────┐  │
│  │                                                           │  │
│  │   1. 携带 X-API-Key 请求头                                 │  │
│  │   2. 向 /api/external/v1/* 发起 HTTPS 请求                 │  │
│  │   3. 解析 JSON 响应                                        │  │
│  │                                                           │  │
│  └───────────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────────┘


                    ┌─────────────────────┐
                    │    VIO API 服务     │
                    │  /api/external/v1   │
                    │                     │
                    │  • 鉴权             │
                    │  • 限流             │
                    │  • Scope 校验       │
                    │  • 请求处理         │
                    └─────────────────────┘


                    ┌─────────────────────┐
                    │      数据库         │
                    │   (租户数据)      │
                    └─────────────────────┘

2. 鉴权(Authentication)

API Key

所有请求须使用 API Key 鉴权。API Key 绑定租户,可配置不同权限范围(scope)。

如何获取 API Key

  1. 登录 VIO 管理后台(Admin Portal)
  2. 进入 设置 > API Keys
  3. 点击 创建 API Key
  4. 为该 Key 选择 scope(权限)
  5. 可选:配置 IP 白名单
  6. 复制并安全保存生成的 Key

重要:API Key 仅在创建时完整展示一次,请务必妥善保管。

在请求中使用 API Key

每个请求在请求头中携带 X-API-Key

bash
curl -X GET "https://your-domain.com/api/external/v1/vouchers" \
  -H "X-API-Key: vio_live_your_api_key_here"

API Key 格式

  • 生产:vio_live_xxxxxxxxxxxxxxxx
  • 测试:vio_test_xxxxxxxxxxxxxxxx

Scope(权限范围)

每个 API Key 可配置一个或多个 scope,决定可访问的接口:

Scope说明路径前缀
vouchers礼券与领取记录/vouchers/*
users用户/users/*
campaigns活动/campaigns/*
tokens代币与余额/tokens/*
analytics分析数据/analytics/*

IP 白名单

为加强安全,可将 API Key 限制在指定 IP:

  1. 打开 管理后台 > 设置 > API Keys
  2. 编辑对应 API Key
  3. 添加允许的 IP 或 CIDR
  4. 保存

非白名单 IP 请求将返回 403 Forbidden


3. 限流(Rate Limiting)

默认限额

为保证公平与稳定,接口实行限流:

限额类型默认值
每分钟60 次请求
每天10,000 次

限流响应头

响应中会包含限流相关信息:

Header说明
X-RateLimit-Limit当前时间窗口内上限
X-RateLimit-Remaining当前窗口剩余次数
X-RateLimit-Reset限额重置的 Unix 时间戳

响应头示例

X-RateLimit-Limit: 60
X-RateLimit-Remaining: 45
X-RateLimit-Reset: 1699574400

超出限流

超限将返回 429 Too Many Requests

json
{
  "success": false,
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "Too many requests. Please retry after 60 seconds."
  }
}

响应通常包含 Retry-After,提示多久后可重试。

最佳实践

  • 收到 429 时使用指数退避重试
  • 在合适场景下缓存响应
  • 尽量批量操作
  • 根据响应头监控用量

4. 请求与响应格式(Request & Response)

请求格式

  • 请求体须为 JSON
  • POST/PATCH 须带 Content-Type: application/json
  • 过滤与分页使用查询参数

成功响应

成功时结构如下:

json
{
  "success": true,
  "data": { ... },
  "message": "Optional success message"
}

分页列表

列表类接口返回分页结构:

json
{
  "success": true,
  "data": [ ... ],
  "pagination": {
    "page": 1,
    "limit": 20,
    "total": 150,
    "totalPages": 8,
    "hasNextPage": true,
    "hasPrevPage": false
  }
}

分页参数

参数类型默认值说明
pageinteger1页码(从 1 开始)
limitinteger20每页条数(最大 100)

错误响应

错误时结构如下:

json
{
  "success": false,
  "error": {
    "code": "ERROR_CODE",
    "message": "Human-readable error description"
  }
}

5. 错误处理(Error Handling)

HTTP 状态码

Status Code含义
200 OK成功
201 Created创建成功
400 Bad Request参数或 body 无效
401 Unauthorized缺少或无效的 API Key
403 ForbiddenScope 不足或未通过 IP 白名单
404 Not Found资源不存在
429 Too Many Requests超出限流
500 Internal Server Error服务器错误

业务错误码(Error Codes)

Error Code说明
VALIDATION_ERROR校验失败
UNAUTHORIZEDAPI Key 缺失或无效
FORBIDDEN无所需 scope
NOT_FOUND资源不存在
RATE_LIMIT_EXCEEDED请求过于频繁
ALREADY_EXISTS资源已存在(如重复用户)
INSUFFICIENT_BALANCE代币余额不足
ALREADY_REDEEMED礼券已核销
EXPIRED礼券或代币已过期
INTERNAL_ERROR未预期的服务器错误

错误响应示例

json
{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid request parameters",
    "details": [
      {
        "field": "email",
        "message": "Invalid email format"
      }
    ]
  }
}

6. API 参考:礼券(Vouchers)

说明: 本章中 HTTP 路径、方法、JSON 字段名及 cURL/代码块与线上实现一致;说明性文字为中文。

所需 Scope: vouchers

领取与核销流程(重要)

以下为两类不同概念,请勿混淆:

  • 礼券模板Voucher):租户在平台中创建或与外部同步的礼券定义。
  • 用户礼券 / 领取记录UserVoucher):用户经「领取」或「发放(send)」后得到的单用户实例。

按场景选择流程

若仅通过 API 对接,请按下表选用路径:

场景第 1 步(发放/领取)第 2 步(核销)典型用途
用户从活动自助领取POST /api/vouchers/:voucherId/claim 且带 campaignIdPOST /api/vouchers/me/:userVoucherId/redeem会员端应用(JWT)
租户/后端直接向用户发券POST /vouchers/:voucherId/send 且带 userIdPOST /vouchers/redeem-by-codePOST /vouchers/redeem/:code/pin服务端 API Key 对接

租户自管礼券与外部供货礼券的阶段顺序相同:均为先发券(领取或 send)→ 再核销。差异在核销阶段:

  • 租户自管礼券:核销将领取记录标记为已使用,不调用外部供货系统。若 consumptionTypeqr_codecoupon_code 且配置了 CSV 码池(管理端上传),核销时会从池中原子分配下一条码至 externalVoucherCode,再标记已使用;端侧应在此时再展示供应商码 / 由该码生成的 QR 或条码。
  • 外部供货礼券:核销会触发供货方履约(如下单)。核销后须根据响应中的 externalFulfillmentStatus 确认是否成功。

POST /vouchers/:voucherId/send 在 External API 中等价于「为指定用户完成领取」:会创建 UserVoucher 并返回 redemptionCode

类型判断规则

依据礼券模板字段区分来源与用法:

  • externalProviderexternalId 均存在:为外部供货礼券
  • externalProviderexternalId 均缺失:为租户自管礼券
  • consumptionType 表示核销交互方式(vio_codecoupon_codeurlqr_codemanualzhichong),不能单独用作区分「外部 / 租户自管」的依据。租户自管的 qr_code / coupon_code 可与管理端 CSV 码池 配合:每次会员 / 店员 / API 核销成功时从池中取一条唯一码写入 externalVoucherCode;模板 领取上限 不得超过池中码总数(见 Admin 用户指南)。
  • voucherType 为业务分类字段,不能单独用作区分「外部 / 租户自管」的依据。

应在哪些接口读取判断信息

需要判断的内容使用的接口关键字段
外部供货还是租户自管?GET /vouchersGET /vouchers/:voucherIdexternalProviderexternalId
核销时需要哪种输入方式?GET /vouchersGET /vouchers/:voucherIdconsumptionTypeexternalRequiresDirectOrderParams
应对哪一条领取记录核销?领取/send 的响应或用户领取列表userVoucherId_id)、redemptionCodestatus
外部履约结果?核销响应externalVoucherCodeexternalRedemptionUrlexternalOrderIdexternalFulfillmentStatus

GET /vouchers/redeem/:code/info 仅用于展示,不要作为判断礼券类型的数据源。

领取 / 核销示例

A) 使用 External API(API Key)向指定用户发放(send)

适用于由贵方后端直接向用户派券的场景。

bash
curl -X POST "https://your-domain.com/api/external/v1/vouchers/{voucherId}/send" \
  -H "X-API-Key: vio_live_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "userId": "507f1f77bcf86cd799439015"
  }'

成功响应示例(关键字段):

json
{
  "success": true,
  "data": {
    "_id": "507f1f77bcf86cd799439050",
    "voucherId": "507f1f77bcf86cd799439011",
    "userId": "507f1f77bcf86cd799439015",
    "status": "active",
    "redemptionCode": "VCH-M1ABC2-XY3Z"
  },
  "message": "Voucher sent to user"
}

B) 使用 External API(API Key)按核销码核销

适用于非 zhichong 的服务端对接或门店收银等。

bash
curl -X POST "https://your-domain.com/api/external/v1/vouchers/redeem-by-code" \
  -H "X-API-Key: vio_live_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "redemptionCode": "VCH-M1ABC2-XY3Z",
    "location": "Store #42",
    "notes": "POS order #12345"
  }'

成功响应示例(关键字段):

json
{
  "success": true,
  "data": {
    "_id": "507f1f77bcf86cd799439050",
    "status": "redeemed",
    "redemptionCode": "VCH-M1ABC2-XY3Z",
    "redeemedAt": "2024-02-01T10:15:00.000Z",
    "externalVoucherCode": "ABC-123-XYZ",
    "externalRedemptionUrl": null,
    "externalOrderId": "69f75fe31134f32e472b2f98",
    "externalFulfillmentStatus": "fulfilled"
  },
  "message": "Voucher redeemed"
}

C) zhichong 直充核销(会员 API,JWT)

consumptionTypezhichong 时,须调用会员侧核销接口,并在 body 中传入 providerParams(含充值账号等):

bash
curl -X POST "https://your-domain.com/api/vouchers/me/{userVoucherId}/redeem" \
  -H "Authorization: Bearer {member_jwt}" \
  -H "Content-Type: application/json" \
  -d '{
    "providerParams": {
      "account": "12312332123"
    }
  }'

成功响应示例(关键字段):

json
{
  "success": true,
  "data": {
    "_id": "507f1f77bcf86cd799439050",
    "status": "redeemed",
    "externalFulfillmentStatus": "fulfilled",
    "externalIsDirectRecharge": true,
    "externalRechargeAccount": "12312332123",
    "externalOrderId": "69f75fe31134f32e472b2f98"
  },
  "message": "Voucher redeemed"
}

礼券来源与类型对照

来源externalProviderexternalId常见 consumptionType建议领取/发放建议核销
租户自管vio_codecoupon_codeurlqr_codemanualPOST /vouchers/:voucherId/send(API Key)或会员领取流程POST /vouchers/redeem-by-codePOST /vouchers/redeem/:code/pin 或会员侧核销
外部供货(码/链接类)多为 coupon_codeurl须先领取/send领取/send 后再核销
外部供货(zhichongzhichong须先领取/sendPOST /api/vouchers/me/:userVoucherId/redeem 且带 providerParams.account

POST /api/external/v1/vouchers/redeem-by-code 不接受 providerParams
POST /api/external/v1/vouchers/redeem/:code/pin 不能用于 zhichong 类型。

错误处理与履约状态

常见发放(send/claim)错误

HTTP 状态错误码原因
404NOT_FOUNDvoucherIduserId 不存在
400VALIDATION_ERROR礼券未启用或已过期
400VALIDATION_ERROR已抢完(claimedQuantity >= totalQuantity
400VALIDATION_ERROR用户已达 maxClaimsPerUser 上限

常见核销错误

HTTP 状态错误码原因
404NOT_FOUNDredemptionCode 无法匹配有效领取记录
400VALIDATION_ERROR领取记录已核销(ALREADY_REDEEMED
400VALIDATION_ERROR领取记录已过期(EXPIRED
400VALIDATION_ERRORzhichong 礼券不能使用 PIN 核销

外部礼券履约状态

核销外部供货礼券后,请检查响应中的 externalFulfillmentStatus

externalFulfillmentStatus含义建议处理
fulfilled供货成功;非直充场景下通常可得到 externalVoucherCodeexternalRedemptionUrl将码或链接交付用户
failed供货失败可按 externalFulfillmentError 排查后,对同一领取记录重试核销
processing供货处理中(异步等少见场景)稍后重试,或通过 GET /users/:userId/vouchers 轮询领取状态

若供货方购买失败,核销请求会返回错误,领取记录仍可重试。系统会把该领取记录更新为 externalFulfillmentStatus: "failed" 并记录 externalFulfillmentError

外部履约失败后的领取记录状态示例:

json
{
  "_id": "507f1f77bcf86cd799439050",
  "status": "active",
  "externalFulfillmentStatus": "failed",
  "externalFulfillmentError": "Provider returned: insufficient inventory",
  "externalOrderId": null
}

externalFulfillmentStatusfailed 时,领取记录仍为 active,可在修正问题后再次调用核销。


列举礼券

分页获取当前租户下可见的礼券模板列表。可选地排除当前 tenant 自管创建的礼券。

GET /vouchers

查询参数:

参数类型必填约束说明
pageinteger最小 1,默认 1页码(从 1 起)
limitinteger1100,默认 20每页条数
visibilitystring枚举:privatepublicshared按可见性筛选
isActiveboolean字符串 "true" / "false",解析为布尔值是否仅返回启用中的模板
categorystring任意字符串按分类标签筛选
searchstring任意字符串按礼券名称模糊搜索
subCompanyIdstringMongoDB ObjectId(24 位十六进制)按子公司范围筛选
excludeTenantManualCreatedboolean字符串 "true" / "false",默认 falsetrue 时排除当前 tenant 自管创建的礼券,保留 vouchain 等外部供货礼券

请求示例:

bash
curl -X GET "https://your-domain.com/api/external/v1/vouchers?page=1&limit=10&isActive=true&excludeTenantManualCreated=true" \
  -H "X-API-Key: vio_live_your_api_key_here"

响应示例:

json
{
  "success": true,
  "data": [
    {
      "_id": "507f1f77bcf86cd799439011",
      "name": "20% Off Discount",
      "description": "Get 20% off your next purchase",
      "value": 20,
      "valueType": "percentage",
      "terms": "Valid on orders over $50",
      "images": ["https://example.com/image.jpg"],
      "isActive": true,
      "totalQuantity": 100,
      "claimedQuantity": 45,
      "maxClaimsPerUser": 1,
      "voucherType": "discount",
      "consumptionType": "coupon_code",
      "externalProvider": "vouchain",
      "externalId": "VCH-EXT-001",
      "externalRequiresDirectOrderParams": false,
      "category": "Lifestyle & Services",
      "categories": ["food", "lifestyle"],
      "settlementAmount": 80,
      "settlementCurrency": "THB",
      "startDate": "2024-01-01T00:00:00.000Z",
      "endDate": "2024-12-31T23:59:59.000Z",
      "visibility": "public",
      "createdAt": "2024-01-01T00:00:00.000Z",
      "updatedAt": "2024-01-15T10:30:00.000Z"
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 10,
    "total": 45,
    "totalPages": 5,
    "hasNextPage": true,
    "hasPrevPage": false
  }
}

响应字段(data 数组每一项):

字段类型可空约束 / 格式说明
_idstringMongoDB ObjectId(24 位十六进制)礼券模板唯一 ID
namestring至少 1 字符,已 trim展示名称
descriptionstring可为空串 ""礼券说明
valuenumber>= 0,默认 0面额/折扣数值,具体含义见 valueType
valueTypestring枚举:fixedpercentagefixed 为固定金额,percentage 为折扣比例
valueCurrencystringISO 4217(如 THBHKDUSD),默认 THBvalueTypefixedvalue 的币种
minSpendnumber>= 0,默认 0最低消费门槛;0 表示无门槛
maxDiscountnumber> 0;未设置可为 null百分比折扣的封顶金额
termsstring可为空串 ""使用条款
imagesstring[]合法 URL 数组,可为 []配图 URL,首张为主图
isActivebooleantrue / false是否启用、可被领取
isTransferablebooleantrue / false,默认 false用户领取后是否允许转让
totalQuantityinteger-1 表示不限量;否则 >= 0可领取总量上限;当 claimedQuantity >= totalQuantity(且非 -1)时不可再领取
claimedQuantityinteger>= 0已被领取的次数
maxClaimsPerUserinteger0 表示每用户不限次;否则 >= 1单用户最多可领取次数
voucherTypestring枚举:cashdiscountproductcash_discount,默认 discount业务分类;单独用于判断外部/租户自管
consumptionTypestring枚举:vio_codecoupon_codeurlqr_codemanualzhichong核销交互形态,配合 externalProvider / externalId 决定对接方式
externalProviderstring供货方代码或 nullexternalId 同时存在时表示外部供货礼券
externalIdstring供货侧模板 ID 或 nullexternalProvider 同时存在时表示外部供货礼券
externalRequiresDirectOrderParamsbooleantrue / false / null外部核销是否需额外账户参数(如直充账号)
externalDataobject对象或 null外部供货方原始/扩展数据;结构可能因供货方而异
categorystring枚举:WellnessHealthFood & BeverageLeisure & EntertainmentTravel & HospitalityLifestyle & ServicesOthers;可为 null礼券主分类,可用于分类筛选与展示
categoriesstring[]trim 后的字符串数组,可为 []旧版自定义分类标签(已弃用,建议使用 category),为兼容仍会返回
applicableBrandsobject[]每项包含 _idnameslug适用品牌/子公司(旧字段,建议优先使用 applicableBrandTags
applicableBrandTagsobject[]每项包含 _idnamelogo适用品牌标签
settlementAmountnumber>= 0,默认 0该礼券的结算单价:当此礼券在跨租户场景下被用户核销时,VIO 与发券租户之间记账的结算金额(即 VIO 给客户的结算价)。可用于客户对账
settlementCurrencystringISO 4217 代码,大写,默认 THBsettlementAmount 的币种
crossTenantReceivableTimingstring枚举:redemptionconsumption,默认 redemption跨租户应收入账时点
startDatestringISO 8601;未设置可为 null生效起始时间
endDatestringISO 8601;null 表示不设结束失效时间
visibilitystring枚举:privatepublicshared可见范围;亦影响用户间转让规则
tenantIdobject包含 _idnameslug礼券所属租户
subCompanyIdobject包含 _idnameslug;未归属子公司时可为 null礼券所属子公司
isOwnbooleantrue / false是否属于当前请求上下文的组织
ownerOrgobject{ "name": string, "type": "tenant/subCompany" };自有礼券通常不返回非自有礼券的拥有方信息
targetTiersstring[]字符串数组,可为 []限定可见/可领取的会员等级
targetUserGroupsstring[]MongoDB ObjectId 数组,可为 []限定可见/可领取的用户组
applicableScopestring枚举:all_outletspartial_outletssingle_store,默认 all_outlets适用门店范围
applicableCountrystringISO 国家/地区代码;可为 null单一适用国家/地区(旧字段)
applicableCountriesstring[]ISO 国家/地区代码数组,可为 []适用国家/地区列表
storeIdstringMongoDB ObjectId;可为 null单门店适用范围对应的门店 ID
storeLocationobject包含 nameaddresslatitudelongitude;可为 null内嵌门店位置
bookingEnabledbooleantrue / false,默认 false是否启用预约
bookingDaysInAdvanceinteger>= 0,默认 0可提前预约天数
consumptionMessagestring可为空串 ""manual 核销方式的提示文本
consumptionUrlstring可为空串 ""url 核销方式的链接
consumptionQrCodestring可为空串 ""qr_code 核销方式的二维码图片路径
contractAddressstring区块链合约地址;可为 null礼券 NFT 合约地址
createdBystringMongoDB ObjectId;可为 null创建/部署该礼券的管理员用户 ID
metadataobject对象,默认 {}扩展元数据
createdAtstringISO 8601创建时间
updatedAtstringISO 8601最近更新时间

创建礼券

为当前租户新建一条礼券模板。

POST /vouchers

请求体:

字段类型必填约束说明
namestring至少 1 字符,已 trim会员端展示的礼券名称
descriptionstring无最大长度,默认 ""礼券说明,纯文本
voucherTypestring枚举:cashdiscountproductcash_discount,默认 discount礼券业务类型
visibilitystring枚举:privatepublicshared,默认 private可见范围:private 本租户;public 全平台;sharedsharedWithTenants 中租户
sharedWithTenantsstring[]每项为 MongoDB ObjectId(24 位十六进制)共享目标租户 ID 列表;仅当 visibilityshared 时有效
sharedWithUsersstring[]每项为 MongoDB ObjectId限定可见的用户 ID,用于定向投放
termsstring无最大长度,默认 ""领取/核销前向用户展示的条款
valuenumber最小 0,默认 0面额或折扣数值:valueTypefixed 时为扣减金额;为 percentage 时为百分比(如 20 表示 20% off)
valueTypestring枚举:fixedpercentage,默认 fixedvalue 的语义:固定金额或比例
valueCurrencystringISO 4217(如 THBHKDUSD),默认 THBvalueTypefixedvalue 的币种
minSpendnumber最小 0,默认 0最低消费门槛;0 表示无门槛
maxDiscountnumber须 > 0(若填写)折扣上限,常见于百分比券(如八折但最多减 100)
totalQuantityinteger整数,-1 表示不限量,默认 -1可领取总次数上限;抢完后不可再领取
startDatestringISO 8601,默认当前时间开始可领取时间;省略表示立即生效
endDatestringISO 8601,须晚于 startDate过期时间;省略表示不设过期;过期后不可领取/核销
maxClaimsPerUserinteger整数,最小 00 表示每用户不限次,默认 1单用户最多可领取次数
settlementAmountnumber最小 0,默认 0跨租户核销时的法币结算金额
settlementCurrencystringISO 4217,存盘为大写,默认 THBsettlementAmount 币种;默认取租户账单设置主币种;结算记录均以此币种记账
categorystring枚举:WellnessHealthFood & BeverageLeisure & EntertainmentTravel & HospitalityLifestyle & ServicesOthers礼券主分类,建议优先使用;例如 "Others"
categoriesstring[]字符串数组,元素已 trim分类标签,如 ["food", "lifestyle"]
imagesstring[]每项为合法 URL配图,首图为主图
targetTiersstring[]字符串数组,元素已 trim会员等级名称;仅这些等级可见/可领
targetUserGroupsstring[]每项为 MongoDB ObjectId用户组 ID;仅组内用户可见/可领
subCompanyIdstringMongoDB ObjectId将礼券归属到指定子公司

请求示例:

bash
curl -X POST "https://your-domain.com/api/external/v1/vouchers" \
  -H "X-API-Key: vio_live_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Summer Sale 20% Off",
    "description": "Valid for summer collection",
    "value": 20,
    "valueType": "percentage",
    "category": "Others",
    "totalQuantity": 100,
    "maxClaimsPerUser": 1,
    "startDate": "2024-06-01T00:00:00.000Z",
    "endDate": "2024-08-31T23:59:59.000Z",
    "visibility": "public",
    "terms": "Cannot be combined with other offers"
  }'

响应示例:

json
{
  "success": true,
  "data": {
    "_id": "507f1f77bcf86cd799439012",
    "name": "Summer Sale 20% Off",
    "description": "Valid for summer collection",
    "value": 20,
    "valueType": "percentage",
    "totalQuantity": 100,
    "claimedQuantity": 0,
    "maxClaimsPerUser": 1,
    "startDate": "2024-06-01T00:00:00.000Z",
    "endDate": "2024-08-31T23:59:59.000Z",
    "visibility": "public",
    "isActive": true,
    "createdAt": "2024-05-15T10:00:00.000Z"
  },
  "message": "Voucher created"
}

响应字段:

返回新建后的完整礼券对象。大多数字段与「列举礼券」响应字段一致,但 isOwnownerOrg 等列表接口计算字段不会由创建接口额外添加。


获取礼券详情

获取指定礼券模板的完整信息。

GET /vouchers/:voucherId

路径参数:

参数类型必填说明
voucherIdstring礼券模板 ID

请求示例:

bash
curl -X GET "https://your-domain.com/api/external/v1/vouchers/507f1f77bcf86cd799439011" \
  -H "X-API-Key: vio_live_your_api_key_here"

响应示例:

json
{
  "success": true,
  "data": {
    "_id": "507f1f77bcf86cd799439011",
    "name": "20% Off Discount",
    "description": "Get 20% off your next purchase",
    "value": 20,
    "valueType": "percentage",
    "terms": "Valid on orders over $50",
    "images": ["https://example.com/image.jpg"],
    "isActive": true,
    "totalQuantity": 100,
    "claimedQuantity": 45,
    "maxClaimsPerUser": 1,
    "startDate": "2024-01-01T00:00:00.000Z",
    "endDate": "2024-12-31T23:59:59.000Z",
    "visibility": "public",
    "createdAt": "2024-01-01T00:00:00.000Z",
    "updatedAt": "2024-01-15T10:30:00.000Z"
  }
}

响应字段:

返回完整礼券对象。大多数字段与「列举礼券」响应字段一致,但 isOwnownerOrg 等列表接口计算字段不会由详情接口额外添加。若礼券为 sharedsharedWithTenantssharedWithSubCompaniessharedWithUsers 可能返回已填充的摘要对象。


更新礼券

更新已有礼券模板(部分字段)。

PATCH /vouchers/:voucherId

路径参数:

参数类型必填说明
voucherIdstring礼券模板 ID

请求体:

所有字段均为可选,仅传需要修改的字段。

字段类型约束说明
namestring至少 1 字符展示名称
descriptionstring无最大长度说明
voucherTypestring枚举:cashdiscountproductcash_discount礼券业务类型
visibilitystring枚举:privatepublicshared可见范围
sharedWithTenantsstring[]每项为 MongoDB ObjectId共享租户 ID(仅 visibilityshared 时)
sharedWithUsersstring[]每项为 MongoDB ObjectId定向投放的用户 ID
termsstring无最大长度条款
valuenumber最小 0面额/折扣数值
valueTypestring枚举:fixedpercentagevalue 语义
valueCurrencystringISO 4217fixed 时的币种
minSpendnumber最小 0最低消费,0 表示无门槛
maxDiscountnumber须 > 0(若填写)折扣封顶(常见于百分比券)
totalQuantityinteger整数,-1 表示不限量可领取总量
startDatestringISO 8601生效时间
endDatestringISO 8601失效时间
maxClaimsPerUserinteger最小 00 表示不限单用户领取上限
settlementAmountnumber最小 0跨租户结算金额
settlementCurrencystringISO 4217,存盘大写结算币种;默认租户账单主币种
categorystring枚举:WellnessHealthFood & BeverageLeisure & EntertainmentTravel & HospitalityLifestyle & ServicesOthers礼券主分类,例如 "Others"
isActivebooleantrue / false是否启用
categoriesstring[]每项已 trim分类标签
imagesstring[]每项为合法 URL配图
targetTiersstring[]每项已 trim目标会员等级
targetUserGroupsstring[]每项为 MongoDB ObjectId目标用户组

请求示例:

bash
curl -X PATCH "https://your-domain.com/api/external/v1/vouchers/507f1f77bcf86cd799439011" \
  -H "X-API-Key: vio_live_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Updated Voucher Name",
    "isActive": false
  }'

响应示例:

json
{
  "success": true,
  "data": {
    "_id": "507f1f77bcf86cd799439011",
    "name": "Updated Voucher Name",
    "isActive": false,
    "updatedAt": "2024-02-01T12:00:00.000Z"
  },
  "message": "Voucher updated"
}

删除礼券

软删除礼券模板(标记删除,非物理清除)。

DELETE /vouchers/:voucherId

路径参数:

参数类型必填说明
voucherIdstring礼券模板 ID

请求示例:

bash
curl -X DELETE "https://your-domain.com/api/external/v1/vouchers/507f1f77bcf86cd799439011" \
  -H "X-API-Key: vio_live_your_api_key_here"

响应示例:

json
{
  "success": true,
  "data": {
    "_id": "507f1f77bcf86cd799439011",
    "isDeleted": true,
    "isActive": false,
    "deletedAt": "2024-02-01T12:00:00.000Z"
  },
  "message": "Voucher deleted"
}

响应字段:

返回软删除后的礼券对象。关键字段为 _idisDeleted: trueisActive: falsedeletedAt;响应中也可能包含其他礼券字段。


复制礼券

基于现有模板复制一条新礼券。

POST /vouchers/:voucherId/duplicate

路径参数:

参数类型必填说明
voucherIdstring待复制的礼券模板 ID

请求示例:

bash
curl -X POST "https://your-domain.com/api/external/v1/vouchers/507f1f77bcf86cd799439011/duplicate" \
  -H "X-API-Key: vio_live_your_api_key_here"

响应示例:

json
{
  "success": true,
  "data": {
    "_id": "507f1f77bcf86cd799439013",
    "name": "20% Off Discount (Copy)",
    "description": "Get 20% off your next purchase",
    "value": 20,
    "valueType": "percentage",
    "claimedQuantity": 0,
    "createdAt": "2024-02-01T12:00:00.000Z"
  },
  "message": "Voucher duplicated"
}

响应字段:

返回复制后的完整礼券对象。副本会复制原模板字段,将 name 加上 (Copy) 后缀,将 claimedQuantity 重置为 0,并部署新的 contractAddress


向用户发放礼券(send)

将礼券直接发放给指定用户:系统会铸造对应 NFT(如链上流程尚未完成则可能延后),并创建一条状态为 active 的用户礼券记录;不经过活动领取流程。

POST /vouchers/:voucherId/send

路径参数:

参数类型必填说明
voucherIdstring礼券模板 ID

请求体:

字段类型必填约束说明
userIdstring至少 1 字符,MongoDB ObjectId(24 位十六进制)接收用户 ID

请求示例:

bash
curl -X POST "https://your-domain.com/api/external/v1/vouchers/507f1f77bcf86cd799439011/send" \
  -H "X-API-Key: vio_live_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "userId": "507f1f77bcf86cd799439015"
  }'

响应示例:

json
{
  "success": true,
  "data": {
    "_id": "507f1f77bcf86cd799439050",
    "userId": "507f1f77bcf86cd799439015",
    "voucherId": "507f1f77bcf86cd799439011",
    "tenantId": "507f1f77bcf86cd799439001",
    "nftTokenId": "42",
    "status": "active",
    "redemptionCode": "VCH-M1ABC2-XY3Z",
    "expiresAt": "2024-08-31T23:59:59.000Z",
    "settlementAmount": 0,
    "settlementCurrency": "HKD",
    "claimedAt": "2024-02-01T12:00:00.000Z",
    "createdAt": "2024-02-01T12:00:00.000Z"
  },
  "message": "Voucher sent to user"
}

响应字段:

字段类型可空约束 / 格式说明
_idstringMongoDB ObjectId用户礼券记录(UserVoucher)ID
userIdstringMongoDB ObjectId接收用户
voucherIdstringMongoDB ObjectId礼券模板 ID
tenantIdstringMongoDB ObjectId所属租户
nftTokenIdstring链上 token ID 的数字字符串;铸造中可为 nullNFT token ID
statusstring枚举:activeclaimedredeemedexpired;新发放恒为 active领取记录状态
redemptionCodestring格式 VCH-{base36 时间戳}-{4 位随机}用户出示用于核销的码
expiresAtstringISO 8601;null 表示不设过期该条领取的过期时间,通常继承模板 endDate
settlementAmountnumber>= 0,默认 0发放时自模板复制的跨租户结算金额
settlementCurrencystringISO 4217 大写settlementAmount 币种
claimedAtstringISO 8601发放(到账)时间
createdAtstringISO 8601记录创建时间

错误响应:

HTTP 状态错误码说明
400VALIDATION_ERROR礼券未启用、已过期、已抢完,或用户已达领取上限
404NOT_FOUND礼券或用户不存在

列举礼券领取记录

分页查询当前租户下所有用户礼券(领取)记录。

GET /vouchers/claims/list

查询参数:

参数类型必填约束说明
pagestring解析为整数,最小 1,默认 1页码
limitstring解析为整数,最小 1,默认 20每页条数
voucherIdstringMongoDB ObjectId按礼券模板 ID 筛选
statusstring枚举:activeclaimedredeemedexpired按领取记录状态筛选
fromDatestringISO 8601起始时间(含)
toDatestringISO 8601结束时间(含)
searchstring任意字符串按用户姓名或邮箱模糊搜索

请求示例:

bash
curl -X GET "https://your-domain.com/api/external/v1/vouchers/claims/list?status=active&limit=10" \
  -H "X-API-Key: vio_live_your_api_key_here"

响应示例:

json
{
  "success": true,
  "data": [
    {
      "_id": "507f1f77bcf86cd799439014",
      "voucherId": {
        "_id": "507f1f77bcf86cd799439011",
        "name": "20% Off Discount",
        "images": ["https://example.com/image.jpg"],
        "value": 20,
        "valueType": "percentage"
      },
      "userId": {
        "_id": "507f1f77bcf86cd799439015",
        "displayName": "John Doe",
        "email": "john@example.com",
        "avatar": "https://example.com/avatar.jpg"
      },
      "tenantId": "507f1f77bcf86cd799439001",
      "status": "active",
      "redemptionCode": "VCH-M1ABC2-XY3Z",
      "claimedAt": "2024-01-15T14:30:00.000Z",
      "expiresAt": "2024-12-31T23:59:59.000Z",
      "settlementAmount": 80,
      "settlementCurrency": "THB"
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 10,
    "total": 45,
    "totalPages": 5,
    "hasNextPage": true,
    "hasPrevPage": false
  }
}

响应字段(data 数组每一项):

字段类型可空约束 / 格式说明
_idstringMongoDB ObjectId领取记录 ID
voucherIdobject已填充的礼券摘要对象关联礼券模板
voucherId._idstringMongoDB ObjectId礼券模板 ID
voucherId.namestring已 trim礼券名称
voucherId.imagesstring[]URL 数组,可为 []礼券图片
voucherId.valuenumber>= 0礼券数值
voucherId.valueTypestring枚举:fixedpercentage数值语义
userIdobject已填充的用户摘要对象接收/领取该礼券的用户
userId._idstringMongoDB ObjectId用户 ID
userId.displayNamestring可能为 null用户展示名
userId.emailstring邮箱用户邮箱
userId.avatarstringURL 或 null用户头像
tenantIdstringMongoDB ObjectId领取记录所属租户
voucherOwnerTenantIdstringMongoDB ObjectId礼券模板所属租户
nftTokenIdstring链上 token ID,可为 null该领取记录对应的 NFT token
statusstring枚举:activeclaimedredeemedexpired当前状态
redemptionCodestring格式 VCH-{base36 时间戳}-{4 位随机}核销码
claimedAtstringISO 8601领取/发放时间
redeemedAtstringISO 8601,可为 null核销时间
expiresAtstringISO 8601,null 表示不设过期该条记录过期时间
campaignIdstringMongoDB ObjectId,可为 null关联活动 ID
settlementAmountnumber>= 0,默认 0领取时记录的结算金额
settlementCurrencystringISO 4217结算币种
tokenAmountPaidstring数字字符串,默认 "0"领取时支付的代币数量
redemptionDetailsobject对象或 null核销后的元数据
externalVoucherCodestring字符串或 null外部供货返回的券码
externalRedemptionUrlstringURL 或 null外部供货返回的核销链接
externalOrderIdstring供货方订单 ID 或 null外部供货订单 ID
externalBuyStatusstring枚举:pendingrecordedfailednull外部购买记录状态
externalFulfillmentStatusstring枚举:pendingprocessingfulfilledfailednull外部履约状态
externalFulfillmentErrorstring字符串或 null外部履约错误详情
externalIsDirectRechargebooleantrue / false,默认 false是否为直充履约
externalRechargeAccountstring字符串或 null直充账号
metadataobject对象,默认 {}扩展元数据
createdAtstringISO 8601记录创建时间
updatedAtstringISO 8601最近更新时间

按核销码核销

根据用户出示的核销码完成核销;适用于门店收银、自助结算等场景(API Key 鉴权)。

POST /vouchers/redeem-by-code

请求体:

字段类型必填约束说明
redemptionCodestring至少 1 字符,区分大小写领取记录上的核销码(如 VCH-M1ABC2-XY3Z
locationstring无最大长度核销地点(如门店名、分店)
notesstring无最大长度备注(如订单号)

请求示例:

bash
curl -X POST "https://your-domain.com/api/external/v1/vouchers/redeem-by-code" \
  -H "X-API-Key: vio_live_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "redemptionCode": "ABC123XYZ",
    "location": "Store #42",
    "notes": "Customer purchased item XYZ"
  }'

响应示例:

json
{
  "success": true,
  "data": {
    "_id": "507f1f77bcf86cd799439014",
    "voucherId": "507f1f77bcf86cd799439011",
    "userId": "507f1f77bcf86cd799439015",
    "status": "redeemed",
    "redemptionCode": "VCH-M1ABC2-XY3Z",
    "claimedAt": "2024-01-15T14:30:00.000Z",
    "redeemedAt": "2024-02-01T10:15:00.000Z",
    "redemptionDetails": {
      "location": "Store #42",
      "notes": "Customer purchased item XYZ",
      "method": "api_key",
      "redeemedBy": "api:507f1f77bcf86cd799439099",
      "redeemedByType": "api_key",
      "redeemedByApiKey": "507f1f77bcf86cd799439099"
    },
    "externalVoucherCode": "ABC-123-XYZ",
    "externalRedemptionUrl": null,
    "externalOrderId": "69f75fe31134f32e472b2f98",
    "externalFulfillmentStatus": "fulfilled"
  },
  "message": "Voucher redeemed"
}

响应字段:

字段类型可空约束 / 格式说明
_idstringMongoDB ObjectId领取记录 ID
voucherIdstring 或 objectMongoDB ObjectId 或已填充的礼券对象礼券模板
userIdstringMongoDB ObjectId礼券持有人原用户 ID
statusstring成功后为 redeemed状态
redemptionCodestring格式 VCH-{base36 时间戳}-{4 位随机}已核销的码
claimedAtstringISO 8601原领取时间
redeemedAtstringISO 8601核销时间
redemptionDetails.locationstring请求未传则可能不存在核销地点
redemptionDetails.notesstring请求未传则可能不存在核销备注
redemptionDetails.methodstringapi_key核销方式
redemptionDetails.redeemedBystringapi:{apiKeyId}记录为核销主体的 API Key
redemptionDetails.redeemedByTypestringapi_key核销主体类型
redemptionDetails.redeemedByApiKeystringMongoDB ObjectIdAPI Key ID
externalVoucherCodestring字符串或 null外部供货返回的券码
externalRedemptionUrlstringURL 或 null外部供货返回的核销链接
externalOrderIdstring供货方订单 ID 或 null外部供货订单 ID
externalFulfillmentStatusstring枚举:fulfilledfailedprocessingpendingnull外部履约状态

按核销码查询信息(核销前)

在正式核销前根据核销码拉取礼券与领取信息,用于收银台展示与校验。

GET /vouchers/redeem/:code/info

路径参数:

参数类型必填说明
codestring核销码

请求示例:

bash
curl -X GET "https://your-domain.com/api/external/v1/vouchers/redeem/ABC123XYZ/info" \
  -H "X-API-Key: vio_live_your_api_key_here"

响应示例:

json
{
  "success": true,
  "data": {
    "redemptionCode": "VCH-M1ABC2-XY3Z",
    "status": "active",
    "voucher": {
      "name": "20% Off Discount",
      "description": "Get 20% off your next purchase",
      "value": 20,
      "valueType": "percentage",
      "valueCurrency": "THB",
      "image": "https://example.com/image.jpg",
      "terms": "Valid on orders over $50"
    },
    "tenant": {
      "name": "Demo Tenant",
      "logo": "https://example.com/logo.png",
      "slug": "demo-tenant"
    },
    "pinPrefix": "VI",
    "expiresAt": "2024-12-31T23:59:59.000Z",
    "canRedeem": true
  }
}

响应字段:

字段类型可空约束 / 格式说明
redemptionCodestring核销码本次查询的核销码
statusstring枚举:activeclaimedredeemedexpired展示状态;已过期的 active 记录返回 expired
voucherobject对象或 null礼券展示摘要
voucher.namestring已 trim礼券名称
voucher.descriptionstring可为 ""说明
voucher.valuenumber>= 0面额/折扣数值
voucher.valueTypestring枚举:fixedpercentage数值语义
voucher.valueCurrencystringISO 4217fixed 类时的币种
voucher.imagestringURL 或 null第一张礼券图片
voucher.termsstring可为 ""条款
tenantobject对象或 null礼券创建方租户摘要
tenant.namestring文本租户名称
tenant.logostringURL 或 null租户 logo
tenant.slugstringslug 或 null租户 slug
pinPrefixstring文本,如 VIPIN 核销时应输入的前缀
expiresAtstringISO 8601,null 表示不设过期该条领取记录的过期时间
canRedeembooleantrue / false当前状态是否允许核销

店员 PIN 核销(consume)

使用店员个人 PIN 在核销前完成身份校验,再将礼券标记为已消费。适用于门店需落实到具体店员责任的场景。

重要:PIN 校验归属「创建礼券」的组织。 PIN 与创建该礼券模板的租户(或子公司)绑定,而非分发活动的组织。跨租户场景下(例如 A 公司创建礼券、B 公司通过活动分发),会员须在 A 公司门店 由 A 公司店员输入 PIN。若礼券由子公司创建,仅该公司子公司的 PIN 有效。

PIN 权限: 店员 PIN 须具备 voucher_redemption 权限方可核销礼券;仅有 token_claim 的 PIN 不能用于礼券核销。PIN 权限配置见管理后台文档。

按码核销(redeem-by-code)与本接口(PIN)的区别

  • 按码核销POST /vouchers/redeem-by-code):凭 API Key 鉴权,核销主体记为 API Key 对应方;适合服务端对接。
  • PIN 核销POST /vouchers/redeem/:code/pin):凭店员 PIN 鉴权,核销主体记为具体店员;适合门店落实到人。
POST /vouchers/redeem/:code/pin

路径参数:

参数类型必填说明
codestring领取记录上的核销码

请求体:

字段类型必填约束说明
pinstring5–20 字符;常见为 2 位字母前缀 + 4 位数字(如 HA1234);须具备 voucher_redemption 权限店员 PIN

请求示例:

bash
curl -X POST "https://your-domain.com/api/external/v1/vouchers/redeem/ABC123XYZ/pin" \
  -H "X-API-Key: vio_live_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "pin": "HA1234"
  }'

响应示例:

json
{
  "success": true,
  "data": {
    "success": true,
    "voucher": {
      "name": "20% Off Discount",
      "value": 20,
      "valueType": "percentage",
      "valueCurrency": "THB"
    },
    "redeemedAt": "2024-02-01T10:15:00.000Z",
    "redeemedBy": "John Staff"
  },
  "message": "Voucher consumed"
}

响应字段:

字段类型可空约束 / 格式说明
successboolean成功时为 true业务成功标记
voucher.namestring已 trim已核销礼券名称
voucher.valuenumber>= 0面额/折扣数值
voucher.valueTypestring枚举:fixedpercentage数值语义
voucher.valueCurrencystringISO 4217fixed 类时的币种
redeemedAtstringISO 8601核销时间
redeemedBystring文本校验 PIN 的店员展示名

错误响应:

HTTP 状态错误码说明
400VALIDATION_ERRORPIN 格式无效(须 5–20 字符)
400VALIDATION_ERRORPIN 无效(前缀或号码与任一店员不匹配)
400VALIDATION_ERRORPIN 缺少 voucher_redemption 权限
400VALIDATION_ERROR礼券已核销或已过期
404NOT_FOUND无此核销码对应的礼券记录

7. API 参考:用户(Users)

所需 Scope: users

列举用户

分页获取当前租户下的用户列表。

GET /users

查询参数:

参数类型必填说明
pageinteger页码(默认 1
limitinteger每页条数(默认 20
rolestring按角色筛选:super_admintenant_adminsub_company_adminmember
searchstring按邮箱、手机号或姓名搜索
isActiveboolean是否仅返回启用用户
subCompanyIdstring按子公司 ID 筛选

请求示例:

bash
curl -X GET "https://your-domain.com/api/external/v1/users?role=member&isActive=true&limit=10" \
  -H "X-API-Key: vio_live_your_api_key_here"

响应示例:

json
{
  "success": true,
  "data": [
    {
      "_id": "507f1f77bcf86cd799439015",
      "email": "john@example.com",
      "phone": "+66812345678",
      "displayName": "John Doe",
      "role": "member",
      "isActive": true,
      "walletAddress": "0x1234567890abcdef...",
      "registrationSource": "direct",
      "storeId": null,
      "campaignId": null,
      "createdAt": "2024-01-01T00:00:00.000Z",
      "updatedAt": "2024-01-15T10:30:00.000Z"
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 10,
    "total": 150,
    "totalPages": 15,
    "hasNextPage": true,
    "hasPrevPage": false
  }
}

创建用户

在当前租户下创建新用户。

POST /users

请求体:

字段类型必填说明
emailstring否*邮箱地址
phonestring否*手机号
passwordstring密码(至少 6 位)
displayNamestring展示名
rolestringmembersub_company_admin(默认 member
subCompanyIdstring指派到的子公司 ID
metadataobject自定义键值元数据

*emailphone 至少填一项。

请求示例:

bash
curl -X POST "https://your-domain.com/api/external/v1/users" \
  -H "X-API-Key: vio_live_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "newuser@example.com",
    "phone": "+66812345678",
    "password": "securepassword123",
    "displayName": "New User",
    "role": "member",
    "metadata": {
      "referralSource": "website",
      "tier": "gold"
    }
  }'

响应示例:

json
{
  "success": true,
  "data": {
    "_id": "507f1f77bcf86cd799439016",
    "email": "newuser@example.com",
    "phone": "+66812345678",
    "displayName": "New User",
    "role": "member",
    "isActive": true,
    "walletAddress": "0xabcdef1234567890...",
    "metadata": {
      "referralSource": "website",
      "tier": "gold"
    },
    "createdAt": "2024-02-01T12:00:00.000Z"
  },
  "message": "User created"
}

获取用户详情

按用户 ID 获取详细信息。

GET /users/:userId

路径参数:

参数类型必填说明
userIdstring用户 ID

请求示例:

bash
curl -X GET "https://your-domain.com/api/external/v1/users/507f1f77bcf86cd799439015" \
  -H "X-API-Key: vio_live_your_api_key_here"

响应示例:

json
{
  "success": true,
  "data": {
    "_id": "507f1f77bcf86cd799439015",
    "email": "john@example.com",
    "phone": "+66812345678",
    "displayName": "John Doe",
    "role": "member",
    "isActive": true,
    "walletAddress": "0x1234567890abcdef...",
    "avatar": "https://example.com/avatar.jpg",
    "metadata": {
      "tier": "gold"
    },
    "createdAt": "2024-01-01T00:00:00.000Z",
    "updatedAt": "2024-01-15T10:30:00.000Z"
  }
}

更新用户

更新已有用户信息。

PATCH /users/:userId

路径参数:

参数类型必填说明
userIdstring用户 ID

请求体:

字段类型说明
displayNamestring展示名
avatarstring头像 URL
isActiveboolean是否启用
subCompanyIdstring子公司 ID
metadataobject自定义元数据

请求示例:

bash
curl -X PATCH "https://your-domain.com/api/external/v1/users/507f1f77bcf86cd799439015" \
  -H "X-API-Key: vio_live_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "displayName": "John Smith",
    "metadata": {
      "tier": "platinum"
    }
  }'

响应示例:

json
{
  "success": true,
  "data": {
    "_id": "507f1f77bcf86cd799439015",
    "displayName": "John Smith",
    "metadata": {
      "tier": "platinum"
    },
    "updatedAt": "2024-02-01T12:00:00.000Z"
  },
  "message": "User updated"
}

停用用户

软删除用户(将 isActive 置为 false)。

DELETE /users/:userId

路径参数:

参数类型必填说明
userIdstring用户 ID

请求示例:

bash
curl -X DELETE "https://your-domain.com/api/external/v1/users/507f1f77bcf86cd799439015" \
  -H "X-API-Key: vio_live_your_api_key_here"

响应示例:

json
{
  "success": true,
  "data": null,
  "message": "User deactivated"
}

获取用户代币余额

查询指定用户在各代币下的可用余额(含锁定余额)。

GET /users/:userId/balances

路径参数:

参数类型必填说明
userIdstring用户 ID

请求示例:

bash
curl -X GET "https://your-domain.com/api/external/v1/users/507f1f77bcf86cd799439015/balances" \
  -H "X-API-Key: vio_live_your_api_key_here"

响应示例:

json
{
  "success": true,
  "data": [
    {
      "tokenId": "507f1f77bcf86cd799439020",
      "tokenName": "Loyalty Points",
      "symbol": "LP",
      "balance": "1500",
      "lockedBalance": "0"
    },
    {
      "tokenId": "507f1f77bcf86cd799439021",
      "tokenName": "Reward Coins",
      "symbol": "RC",
      "balance": "250",
      "lockedBalance": "50"
    }
  ]
}

获取用户已领取礼券

分页返回该用户已领取的礼券记录。

GET /users/:userId/vouchers

路径参数:

参数类型必填说明
userIdstring用户 ID

查询参数:

参数类型必填说明
pageinteger页码(默认 1
limitinteger每页条数(默认 20
statusstring按状态筛选:activeredeemedexpired

请求示例:

bash
curl -X GET "https://your-domain.com/api/external/v1/users/507f1f77bcf86cd799439015/vouchers?status=active" \
  -H "X-API-Key: vio_live_your_api_key_here"

响应示例:

json
{
  "success": true,
  "data": [
    {
      "_id": "507f1f77bcf86cd799439014",
      "voucherId": "507f1f77bcf86cd799439011",
      "status": "active",
      "redemptionCode": "ABC123XYZ",
      "claimedAt": "2024-01-15T14:30:00.000Z",
      "expiresAt": "2024-12-31T23:59:59.000Z",
      "voucher": {
        "name": "20% Off Discount",
        "value": 20,
        "valueType": "percentage"
      }
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 20,
    "total": 5,
    "totalPages": 1,
    "hasNextPage": false,
    "hasPrevPage": false
  }
}

按标识符搜索用户

通过邮箱、手机号或钱包地址定位用户。

GET /users/search/by-identifier

查询参数:

参数类型必填说明
emailstring否*邮箱
phonestring否*手机号
walletAddressstring否*钱包地址

*至少提供上述之一。

请求示例:

bash
curl -X GET "https://your-domain.com/api/external/v1/users/search/by-identifier?email=john@example.com" \
  -H "X-API-Key: vio_live_your_api_key_here"

响应示例:

json
{
  "success": true,
  "data": {
    "_id": "507f1f77bcf86cd799439015",
    "email": "john@example.com",
    "phone": "+66812345678",
    "displayName": "John Doe",
    "role": "member",
    "isActive": true,
    "walletAddress": "0x1234567890abcdef..."
  }
}

8. API 参考:活动(Campaigns)

所需 Scope: campaigns

列举活动

分页获取当前租户下的礼券活动列表。

GET /campaigns

查询参数:

参数类型必填约束说明
pagestring解析为整数,最小 1,默认 1页码(从 1 起)
limitstring解析为整数,最小 1,默认 20每页条数
searchstring任意字符串按活动名称模糊搜索
isActivestring字符串 "true" / "false",解析为布尔值按是否启用筛选

请求示例:

bash
curl -X GET "https://your-domain.com/api/external/v1/campaigns?isActive=true&limit=10" \
  -H "X-API-Key: vio_live_your_api_key_here"

响应示例:

json
{
  "success": true,
  "data": [
    {
      "_id": "507f1f77bcf86cd799439030",
      "name": "Summer Promotion",
      "description": "Summer 2024 voucher campaign",
      "slug": "summer-promotion",
      "tokenId": "507f1f77bcf86cd799439020",
      "isActive": true,
      "isPublic": true,
      "startDate": "2024-06-01T00:00:00.000Z",
      "endDate": "2024-08-31T23:59:59.000Z",
      "images": ["https://example.com/campaign.jpg"],
      "createdAt": "2024-05-15T10:00:00.000Z"
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 10,
    "total": 5,
    "totalPages": 1,
    "hasNextPage": false,
    "hasPrevPage": false
  }
}

响应字段(data 数组每一项):

字段类型可空约束 / 格式说明
_idstringMongoDB ObjectId(24 位十六进制)活动唯一 ID
namestring至少 1 字符,已 trim活动展示名称
descriptionstring可为空串 ""活动说明
slugstring小写、URL 安全字符;租户+子公司内唯一活动落地页友好路径(如 summer-promotion
tokenIdstringMongoDB ObjectId(24 位十六进制)用户领取礼券时需消耗的代币 ID
isActivebooleantrue / false活动是否启用
isPublicbooleantrue / false是否公开(免登录可访问)
startDatestringISO 8601;未传时默认为创建时间活动开始时间
endDatestringISO 8601;null 表示不设结束时间活动结束时间
imagesstring[]合法 URL 数组,可为 []横幅/封面图
createdAtstringISO 8601创建时间

创建活动

新建一条礼券活动。

POST /campaigns

请求体:

字段类型必填约束说明
namestring至少 1 字符,已 trim活动名称;未传 slug 时据此自动生成
descriptionstring无最大长度,默认 ""活动说明
slugstring小写、URL 安全;租户+子公司内唯一;省略时对 name 做 slugify活动页路径标识(如 summer-promotion-2024
tokenIdstringMongoDB ObjectId,须为已存在的 Token用户领取消耗用的代币
startDatestringISO 8601,默认当前时间开始接受领取的时间
endDatestringISO 8601,宜晚于 startDate;省略表示不设结束活动结束时间
isActiveboolean默认 true停用后对用户隐藏
isPublicboolean默认 true是否在公开页展示(无需登录)
imagesstring[]每项为合法 URL,可为 [];首张为主图横幅/配图 URL

请求示例:

bash
curl -X POST "https://your-domain.com/api/external/v1/campaigns" \
  -H "X-API-Key: vio_live_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Summer Promotion 2024",
    "description": "Exclusive summer deals for loyal customers",
    "tokenId": "507f1f77bcf86cd799439020",
    "startDate": "2024-06-01T00:00:00.000Z",
    "endDate": "2024-08-31T23:59:59.000Z",
    "isPublic": true
  }'

响应示例:

json
{
  "success": true,
  "data": {
    "_id": "507f1f77bcf86cd799439031",
    "name": "Summer Promotion 2024",
    "description": "Exclusive summer deals for loyal customers",
    "slug": "summer-promotion-2024",
    "tokenId": "507f1f77bcf86cd799439020",
    "isActive": true,
    "isPublic": true,
    "startDate": "2024-06-01T00:00:00.000Z",
    "endDate": "2024-08-31T23:59:59.000Z",
    "createdAt": "2024-05-15T10:00:00.000Z"
  },
  "message": "Campaign created"
}

响应字段:

返回新建的活动对象;各字段含义参见 列举活动 中的响应字段。


获取活动详情

按活动 ID 获取单个活动的完整信息。

GET /campaigns/:campaignId

路径参数:

参数类型必填说明
campaignIdstring活动 ID

请求示例:

bash
curl -X GET "https://your-domain.com/api/external/v1/campaigns/507f1f77bcf86cd799439030" \
  -H "X-API-Key: vio_live_your_api_key_here"

响应示例:

json
{
  "success": true,
  "data": {
    "_id": "507f1f77bcf86cd799439030",
    "name": "Summer Promotion",
    "description": "Summer 2024 voucher campaign",
    "slug": "summer-promotion",
    "tokenId": "507f1f77bcf86cd799439020",
    "isActive": true,
    "isPublic": true,
    "startDate": "2024-06-01T00:00:00.000Z",
    "endDate": "2024-08-31T23:59:59.000Z",
    "images": ["https://example.com/campaign.jpg"],
    "createdAt": "2024-05-15T10:00:00.000Z"
  }
}

响应字段:

返回完整活动对象;各字段含义参见 列举活动 中的响应字段。


更新活动

更新已有活动信息。

PATCH /campaigns/:campaignId

路径参数:

参数类型必填说明
campaignIdstring活动 ID

请求体:

字段类型约束说明
namestring至少 1 字符,已 trim活动展示名称
descriptionstring无最大长度活动说明
tokenIdstringMongoDB ObjectId,须引用已存在代币活动消耗的代币
startDatestringISO 8601开始时间
endDatestringISO 8601结束时间
isActivebooleantrue / false是否启用
isPublicbooleantrue / false是否公开页可见
imagesstring[]每项为合法 URL配图 URL

请求示例:

bash
curl -X PATCH "https://your-domain.com/api/external/v1/campaigns/507f1f77bcf86cd799439030" \
  -H "X-API-Key: vio_live_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Extended Summer Promotion",
    "endDate": "2024-09-30T23:59:59.000Z"
  }'

响应示例:

json
{
  "success": true,
  "data": {
    "_id": "507f1f77bcf86cd799439030",
    "name": "Extended Summer Promotion",
    "endDate": "2024-09-30T23:59:59.000Z",
    "updatedAt": "2024-08-15T10:00:00.000Z"
  },
  "message": "Campaign updated"
}

删除活动

删除指定活动。

DELETE /campaigns/:campaignId

路径参数:

参数类型必填说明
campaignIdstring活动 ID

请求示例:

bash
curl -X DELETE "https://your-domain.com/api/external/v1/campaigns/507f1f77bcf86cd799439030" \
  -H "X-API-Key: vio_live_your_api_key_here"

响应示例:

json
{
  "success": true,
  "data": {
    "_id": "507f1f77bcf86cd799439030"
  },
  "message": "Campaign deleted"
}

响应字段:

字段类型约束 / 格式说明
_idstringMongoDB ObjectId(24 位十六进制)已删活动 ID

获取活动关联礼券

分页返回已加入该活动的礼券模板及活动内配置。

GET /campaigns/:campaignId/vouchers

路径参数:

参数类型必填说明
campaignIdstring活动 ID

查询参数:

参数类型必填说明
pageinteger页码(默认 1
limitinteger每页条数(默认 20

请求示例:

bash
curl -X GET "https://your-domain.com/api/external/v1/campaigns/507f1f77bcf86cd799439030/vouchers" \
  -H "X-API-Key: vio_live_your_api_key_here"

响应示例:

json
{
  "success": true,
  "data": [
    {
      "_id": "507f1f77bcf86cd799439011",
      "name": "20% Off Discount",
      "value": 20,
      "valueType": "percentage",
      "tokenAmount": "100",
      "sortOrder": 1,
      "isActive": true
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 20,
    "total": 3,
    "totalPages": 1,
    "hasNextPage": false,
    "hasPrevPage": false
  }
}

响应字段(data 数组每一项):

字段类型可空约束 / 格式说明
_idstringMongoDB ObjectId礼券模板 ID
namestring已 trim礼券名称
valuenumber>= 0面额/折扣数值,含义见 valueType
valueTypestring枚举:fixedpercentage数值语义
tokenAmountstring数字字符串;"0" 表示活动级不额外限制库存活动内该礼券的代币定价/活动库存上限
sortOrderinteger默认 0,越小越靠前在活动内的展示顺序
isActivebooleantrue / false该礼券在本活动中是否可领

向活动添加礼券

将礼券模板加入活动,并设置活动维度代币定价/库存上限。

POST /campaigns/:campaignId/vouchers

路径参数:

参数类型必填说明
campaignIdstring活动 ID

请求体:

字段类型必填约束说明
voucherIdsstring[]至少 1 个 ObjectId;同一活动内重复提交会被拒绝要加入活动的礼券模板 ID 列表
tokenAmountstring非空字符串,高精度存储;"0" 表示不在活动层做额外库存上限这些礼券在活动内的代币/库存上限配置

请求示例:

bash
curl -X POST "https://your-domain.com/api/external/v1/campaigns/507f1f77bcf86cd799439030/vouchers" \
  -H "X-API-Key: vio_live_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "voucherIds": ["507f1f77bcf86cd799439011", "507f1f77bcf86cd799439012"],
    "tokenAmount": "100"
  }'

响应示例:

json
{
  "success": true,
  "data": {
    "added": 2
  },
  "message": "Vouchers added to campaign"
}

响应字段:

字段类型约束 / 格式说明
addedinteger>= 0成功加入活动的礼券条数

更新活动内礼券配置

调整某礼券在该活动内的排序、库存上限及是否可领取等。

PATCH /campaigns/:campaignId/vouchers/:voucherId

路径参数:

参数类型必填说明
campaignIdstring活动 ID
voucherIdstring礼券 ID

请求体:

字段类型约束说明
tokenAmountstring数字字符串;"0" 表示活动层不额外限制本礼券在该活动内的库存/代币上限
sortOrderinteger可为负数,默认 0,越小越靠前活动内展示顺序
isActivebooleantrue / false是否允许在本活动中领取(可临时关闭)

请求示例:

bash
curl -X PATCH "https://your-domain.com/api/external/v1/campaigns/507f1f77bcf86cd799439030/vouchers/507f1f77bcf86cd799439011" \
  -H "X-API-Key: vio_live_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "tokenAmount": "150",
    "sortOrder": 1
  }'

响应示例:

json
{
  "success": true,
  "data": {
    "campaignId": "507f1f77bcf86cd799439030",
    "voucherId": "507f1f77bcf86cd799439011",
    "tokenAmount": "150",
    "sortOrder": 1
  },
  "message": "Campaign voucher updated"
}

响应字段:

字段类型约束 / 格式说明
campaignIdstringMongoDB ObjectId活动 ID
voucherIdstringMongoDB ObjectId礼券 ID
tokenAmountstring数字字符串更新后的活动库存上限
sortOrderinteger越小越靠前更新后的排序

从活动移除礼券

将礼券从活动中移除(不删除礼券模板本身)。

DELETE /campaigns/:campaignId/vouchers/:voucherId

路径参数:

参数类型必填说明
campaignIdstring活动 ID
voucherIdstring礼券 ID

请求示例:

bash
curl -X DELETE "https://your-domain.com/api/external/v1/campaigns/507f1f77bcf86cd799439030/vouchers/507f1f77bcf86cd799439011" \
  -H "X-API-Key: vio_live_your_api_key_here"

响应示例:

json
{
  "success": true,
  "data": null,
  "message": "Voucher removed from campaign"
}

可取加入活动的礼券列表

返回尚未加入该活动、可被添加的礼券模板(分页)。

GET /campaigns/:campaignId/available-vouchers

路径参数:

参数类型必填说明
campaignIdstring活动 ID

查询参数:

参数类型必填约束说明
pagestring解析为整数,最小 1,默认 1页码
limitstring解析为整数,最小 1,默认 20每页条数
searchstring任意字符串按礼券名称模糊搜索

请求示例:

bash
curl -X GET "https://your-domain.com/api/external/v1/campaigns/507f1f77bcf86cd799439030/available-vouchers?search=discount" \
  -H "X-API-Key: vio_live_your_api_key_here"

响应示例:

json
{
  "success": true,
  "data": [
    {
      "_id": "507f1f77bcf86cd799439013",
      "name": "10% New User Discount",
      "value": 10,
      "valueType": "percentage",
      "isActive": true
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 20,
    "total": 10,
    "totalPages": 1,
    "hasNextPage": false,
    "hasPrevPage": false
  }
}

响应字段(data 数组每一项):

字段类型可空约束 / 格式说明
_idstringMongoDB ObjectId礼券模板 ID
namestring已 trim名称
valuenumber>= 0面额/折扣
valueTypestringfixed / percentage数值语义
isActivebooleantrue / false模板是否启用

9. API 参考:代币(Tokens)

所需 Scope: tokens

列举代币

分页列出当前租户下的代币定义。

GET /tokens

查询参数:

参数类型必填说明
pageinteger页码(默认 1
limitinteger每页条数(默认 20
isActiveboolean按是否启用筛选
subCompanyIdstring按子公司筛选

请求示例:

bash
curl -X GET "https://your-domain.com/api/external/v1/tokens?isActive=true" \
  -H "X-API-Key: vio_live_your_api_key_here"

响应示例:

json
{
  "success": true,
  "data": [
    {
      "_id": "507f1f77bcf86cd799439020",
      "name": "Loyalty Points",
      "symbol": "LP",
      "description": "Earn points on every purchase",
      "totalSupply": "1000000",
      "isActive": true,
      "createdAt": "2024-01-01T00:00:00.000Z"
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 20,
    "total": 2,
    "totalPages": 1,
    "hasNextPage": false,
    "hasPrevPage": false
  }
}

获取代币详情

按代币 ID 获取代币元数据。

GET /tokens/:tokenId

路径参数:

参数类型必填说明
tokenIdstring代币 ID

请求示例:

bash
curl -X GET "https://your-domain.com/api/external/v1/tokens/507f1f77bcf86cd799439020" \
  -H "X-API-Key: vio_live_your_api_key_here"

响应示例:

json
{
  "success": true,
  "data": {
    "_id": "507f1f77bcf86cd799439020",
    "name": "Loyalty Points",
    "symbol": "LP",
    "description": "Earn points on every purchase",
    "totalSupply": "1000000",
    "isActive": true,
    "decimals": 0,
    "createdAt": "2024-01-01T00:00:00.000Z",
    "updatedAt": "2024-01-15T10:30:00.000Z"
  }
}

获取代币统计

返回某代币的供应量、持有人数等汇总指标。

GET /tokens/:tokenId/stats

路径参数:

参数类型必填说明
tokenIdstring代币 ID

请求示例:

bash
curl -X GET "https://your-domain.com/api/external/v1/tokens/507f1f77bcf86cd799439020/stats" \
  -H "X-API-Key: vio_live_your_api_key_here"

响应示例:

json
{
  "success": true,
  "data": {
    "totalSupply": "1000000",
    "circulatingSupply": "750000",
    "holdersCount": 1250,
    "totalTransactions": 15000,
    "averageBalance": "600"
  }
}

获取代币持有者

分页列出持有该代币的用户及其余额。

GET /tokens/:tokenId/holders

路径参数:

参数类型必填说明
tokenIdstring代币 ID

查询参数:

参数类型必填说明
pageinteger页码(默认 1
limitinteger每页条数(默认 20
sortstring排序:balancenamerecent(默认 balance
searchstring按用户姓名或邮箱搜索

请求示例:

bash
curl -X GET "https://your-domain.com/api/external/v1/tokens/507f1f77bcf86cd799439020/holders?sort=balance&limit=10" \
  -H "X-API-Key: vio_live_your_api_key_here"

响应示例:

json
{
  "success": true,
  "data": [
    {
      "user": {
        "_id": "507f1f77bcf86cd799439015",
        "displayName": "John Doe",
        "email": "john@example.com"
      },
      "balance": "5000"
    },
    {
      "user": {
        "_id": "507f1f77bcf86cd799439016",
        "displayName": "Jane Smith",
        "email": "jane@example.com"
      },
      "balance": "3500"
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 10,
    "total": 1250,
    "totalPages": 125,
    "hasNextPage": true,
    "hasPrevPage": false
  },
  "token": {
    "_id": "507f1f77bcf86cd799439020",
    "name": "Loyalty Points",
    "symbol": "LP"
  }
}

增发代币(mint)

向指定用户账户增发代币并记入流水。

POST /tokens/:tokenId/mint

路径参数:

参数类型必填说明
tokenIdstring代币 ID

请求体:

字段类型必填说明
toUserIdstring接收用户 ID
amountstring增发数量
memostring备注

请求示例:

bash
curl -X POST "https://your-domain.com/api/external/v1/tokens/507f1f77bcf86cd799439020/mint" \
  -H "X-API-Key: vio_live_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "toUserId": "507f1f77bcf86cd799439015",
    "amount": "1000",
    "memo": "Welcome bonus"
  }'

响应示例:

json
{
  "success": true,
  "data": {
    "_id": "507f1f77bcf86cd799439040",
    "tokenId": "507f1f77bcf86cd799439020",
    "type": "mint",
    "amount": "1000",
    "toUserId": "507f1f77bcf86cd799439015",
    "memo": "Welcome bonus",
    "createdAt": "2024-02-01T12:00:00.000Z"
  },
  "message": "Tokens minted successfully"
}

销毁代币(burn)

从指定用户余额中销毁代币。

POST /tokens/:tokenId/burn

路径参数:

参数类型必填说明
tokenIdstring代币 ID

请求体:

字段类型必填说明
fromUserIdstring销毁来源用户 ID
amountstring销毁数量
memostring备注

请求示例:

bash
curl -X POST "https://your-domain.com/api/external/v1/tokens/507f1f77bcf86cd799439020/burn" \
  -H "X-API-Key: vio_live_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "fromUserId": "507f1f77bcf86cd799439015",
    "amount": "500",
    "memo": "Redemption for reward"
  }'

响应示例:

json
{
  "success": true,
  "data": {
    "_id": "507f1f77bcf86cd799439041",
    "tokenId": "507f1f77bcf86cd799439020",
    "type": "burn",
    "amount": "500",
    "fromUserId": "507f1f77bcf86cd799439015",
    "memo": "Redemption for reward",
    "createdAt": "2024-02-01T12:00:00.000Z"
  },
  "message": "Tokens burned successfully"
}

调整用户余额

在公司管理池与指定用户之间划拨或收回代币。

POST /tokens/:tokenId/adjust

路径参数:

参数类型必填说明
tokenIdstring代币 ID

请求体:

字段类型必填说明
userIdstring用户 ID
amountstring调整数量
operationstringsend(拨给用户)或 recall(收回)
reasonstring调整原因(审计用)

请求示例:

bash
curl -X POST "https://your-domain.com/api/external/v1/tokens/507f1f77bcf86cd799439020/adjust" \
  -H "X-API-Key: vio_live_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "userId": "507f1f77bcf86cd799439015",
    "amount": "100",
    "operation": "send",
    "reason": "Manual adjustment for customer service"
  }'

响应示例:

json
{
  "success": true,
  "data": {
    "userBalance": "1100",
    "adminBalance": "8900",
    "transaction": {
      "_id": "507f1f77bcf86cd799439042",
      "tokenId": "507f1f77bcf86cd799439020",
      "type": "transfer",
      "amount": "100"
    }
  },
  "message": "Balance sent"
}

到期行为: 发放代币时,接收方分组的到期日继承来源分组在增发(mint)时定义的到期规则;发放与收回流程中不可单独覆盖到期日。从用户收回时,回到公司池的分组继承用户侧分组到期信息,以保持账本与可发放余额一致。

向用户发放代币

从公司代币池向用户发放;等价于 adjustoperation 固定为 send 的简化接口。

POST /tokens/:tokenId/send

路径参数:

参数类型必填说明
tokenIdstring代币 ID

请求体:

字段类型必填说明
userIdstring目标用户 ID
amountstring发放数量
reasonstring发放原因

请求示例:

bash
curl -X POST "https://your-domain.com/api/external/v1/tokens/507f1f77bcf86cd799439020/send" \
  -H "X-API-Key: vio_live_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "userId": "507f1f77bcf86cd799439015",
    "amount": "1000",
    "reason": "Loyalty reward"
  }'

响应示例:

json
{
  "success": true,
  "data": {
    "userBalance": "2000",
    "adminBalance": "8000",
    "transaction": {
      "_id": "507f1f77bcf86cd799439043",
      "tokenId": "507f1f77bcf86cd799439020",
      "type": "transfer",
      "amount": "1000"
    }
  },
  "message": "Tokens sent to user"
}

错误响应:

HTTP 状态错误码说明
400VALIDATION_ERROR公司池余额不足,无法完成发放
404NOT_FOUND代币或用户不存在

从用户收回代币

将用户余额中的代币收回至公司池;等价于 adjustoperation 固定为 recall

POST /tokens/:tokenId/recall

路径参数:

参数类型必填说明
tokenIdstring代币 ID

请求体:

字段类型必填说明
userIdstring收回来源用户 ID
amountstring收回数量
reasonstring收回原因

请求示例:

bash
curl -X POST "https://your-domain.com/api/external/v1/tokens/507f1f77bcf86cd799439020/recall" \
  -H "X-API-Key: vio_live_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "userId": "507f1f77bcf86cd799439015",
    "amount": "500",
    "reason": "Balance correction"
  }'

响应示例:

json
{
  "success": true,
  "data": {
    "userBalance": "1500",
    "adminBalance": "8500",
    "transaction": {
      "_id": "507f1f77bcf86cd799439044",
      "tokenId": "507f1f77bcf86cd799439020",
      "type": "transfer",
      "amount": "500"
    }
  },
  "message": "Tokens recalled from user"
}

错误响应:

HTTP 状态错误码说明
400VALIDATION_ERROR用户余额不足,无法收回
404NOT_FOUND代币或用户不存在

列举代币流水

分页查询代币转账、增发、销毁等流水记录。

GET /tokens/transactions/list

查询参数:

参数类型必填说明
pageinteger页码(默认 1
limitinteger每页条数(默认 20
tokenIdstring按代币 ID 筛选
typestring按类型:minttransferburnrewardredeem
fromDatedatetime起始时间
toDatedatetime结束时间
searchstring按用户姓名或流水备注搜索

请求示例:

bash
curl -X GET "https://your-domain.com/api/external/v1/tokens/transactions/list?type=mint&limit=10" \
  -H "X-API-Key: vio_live_your_api_key_here"

响应示例:

json
{
  "success": true,
  "data": [
    {
      "_id": "507f1f77bcf86cd799439040",
      "tokenId": "507f1f77bcf86cd799439020",
      "type": "mint",
      "amount": "1000",
      "toUserId": "507f1f77bcf86cd799439015",
      "memo": "Welcome bonus",
      "createdAt": "2024-02-01T12:00:00.000Z",
      "token": {
        "name": "Loyalty Points",
        "symbol": "LP"
      },
      "toUser": {
        "displayName": "John Doe",
        "email": "john@example.com"
      }
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 10,
    "total": 500,
    "totalPages": 50,
    "hasNextPage": true,
    "hasPrevPage": false
  }
}

用户代币余额(/tokens/balance/user/:userId

与用户章节中 /users/:userId/balances 数据一致,仅为不同入口。

GET /tokens/balance/user/:userId

路径参数:

参数类型必填说明
userIdstring用户 ID

请求示例:

bash
curl -X GET "https://your-domain.com/api/external/v1/tokens/balance/user/507f1f77bcf86cd799439015" \
  -H "X-API-Key: vio_live_your_api_key_here"

响应示例:

json
{
  "success": true,
  "data": [
    {
      "tokenId": "507f1f77bcf86cd799439020",
      "tokenName": "Loyalty Points",
      "symbol": "LP",
      "balance": "1500",
      "lockedBalance": "0"
    },
    {
      "tokenId": "507f1f77bcf86cd799439021",
      "tokenName": "Reward Coins",
      "symbol": "RC",
      "balance": "250",
      "lockedBalance": "50"
    }
  ]
}

10. API 参考:分析(Analytics)

所需 Scope: analytics

分析总览

返回租户用户、礼券、交易等汇总指标。

GET /analytics/overview

请求示例:

bash
curl -X GET "https://your-domain.com/api/external/v1/analytics/overview" \
  -H "X-API-Key: vio_live_your_api_key_here"

响应示例:

json
{
  "success": true,
  "data": {
    "users": {
      "total": 5000,
      "active": 4500,
      "newThisMonth": 250
    },
    "vouchers": {
      "totalClaims": 12000,
      "redeemed": 8500,
      "active": 3500,
      "redemptionRate": "70.83%"
    },
    "transactions": {
      "total": 25000,
      "thisMonth": 3500
    },
    "generatedAt": "2024-02-01T12:00:00.000Z"
  }
}

礼券分析

按时间范围返回礼券领取/核销趋势及 Top 礼券等。

GET /analytics/vouchers

查询参数:

参数类型必填说明
fromDatedatetime统计开始时间
toDatedatetime统计结束时间

请求示例:

bash
curl -X GET "https://your-domain.com/api/external/v1/analytics/vouchers?fromDate=2024-01-01T00:00:00.000Z&toDate=2024-01-31T23:59:59.000Z" \
  -H "X-API-Key: vio_live_your_api_key_here"

响应示例:

json
{
  "success": true,
  "data": {
    "totalVouchers": 50,
    "totalClaims": 1200,
    "totalRedemptions": 850,
    "redemptionRate": "70.83%",
    "claimsByDay": [
      { "date": "2024-01-01", "count": 45 },
      { "date": "2024-01-02", "count": 52 }
    ],
    "redemptionsByDay": [
      { "date": "2024-01-01", "count": 30 },
      { "date": "2024-01-02", "count": 38 }
    ],
    "topVouchers": [
      {
        "voucherId": "507f1f77bcf86cd799439011",
        "name": "20% Off Discount",
        "claims": 250,
        "redemptions": 180
      }
    ]
  }
}

用户分析

按时间范围返回新增用户、活跃用户数及按角色分布等。

GET /analytics/users

查询参数:

参数类型必填说明
fromDatedatetime统计开始时间
toDatedatetime统计结束时间

请求示例:

bash
curl -X GET "https://your-domain.com/api/external/v1/analytics/users?fromDate=2024-01-01T00:00:00.000Z&toDate=2024-01-31T23:59:59.000Z" \
  -H "X-API-Key: vio_live_your_api_key_here"

响应示例:

json
{
  "success": true,
  "data": {
    "summary": {
      "totalUsers": 5000,
      "newUsersInPeriod": 500,
      "activeUsers": 4500
    },
    "byRole": {
      "member": 4800,
      "sub_company_admin": 150,
      "tenant_admin": 50
    },
    "dailySignups": [
      { "date": "2024-01-01", "count": 15 },
      { "date": "2024-01-02", "count": 22 }
    ]
  }
}

代币分析

按时间范围返回代币流水汇总、按类型分布等。

GET /analytics/tokens

查询参数:

参数类型必填说明
fromDatedatetime统计开始时间
toDatedatetime统计结束时间

请求示例:

bash
curl -X GET "https://your-domain.com/api/external/v1/analytics/tokens?fromDate=2024-01-01T00:00:00.000Z&toDate=2024-01-31T23:59:59.000Z" \
  -H "X-API-Key: vio_live_your_api_key_here"

响应示例:

json
{
  "success": true,
  "data": {
    "summary": {
      "totalTransactions": 3500,
      "totalMinted": "500000",
      "totalBurned": "150000",
      "netChange": "350000"
    },
    "dailyTransactions": [
      { "date": "2024-01-01", "count": 120, "volume": "15000" },
      { "date": "2024-01-02", "count": 145, "volume": "18500" }
    ],
    "byType": {
      "mint": 800,
      "transfer": 2000,
      "burn": 400,
      "reward": 200,
      "redeem": 100
    }
  }
}

11. 数据模型(Data Models)

以下模型与 API JSON 字段名一致;约束/格式列保留与校验规则相关的原文术语(如 ISO 8601、ObjectId),说明列为中文。

Voucher(礼券模板)

字段类型约束 / 格式说明
_idstringMongoDB ObjectId(24 位十六进制)唯一 ID
namestring至少 1 字符,已 trim展示名称
descriptionstring可为空串 "",无最大长度说明(纯文本)
valuenumber>= 0,默认 0面额或折扣数值,含义由 valueType 决定
valueTypestring枚举:fixedpercentage,默认 fixedfixed 为固定金额,percentage 为折扣比例
valueCurrencystringISO 4217(如 THBHKD),默认 THB固定金额类礼券的币种
voucherTypestring枚举:cashdiscountproductcash_discount礼券业务类型
consumptionTypestring枚举:vio_codecoupon_codeurlqr_codemanualzhichong礼券核销交互方式
termsstring可为空串条款
imagesstring[]每项为合法 URL,可为 []配图,首张为主图
isActiveboolean默认 true是否启用、可领取
isTransferableboolean默认 false领取后是否允许转让
totalQuantityinteger-1 表示不限量;否则 >= 0,默认 -1可领取总量
claimedQuantityinteger>= 0,默认 0已领取次数
maxClaimsPerUserinteger0 表示每用户不限;否则 >= 1,默认 1单用户领取上限
startDatestringISO 8601,默认创建时间生效时间
endDatestringISO 8601;null 表示不设到期失效时间
visibilitystring枚举:privatepublicshared,默认 private可见范围,亦影响用户间转让规则
categorystring枚举:WellnessHealthFood & BeverageLeisure & EntertainmentTravel & HospitalityLifestyle & ServicesOthers;可空礼券主分类
categoriesstring[]字符串数组,每项已 trim分类标签
minSpendnumber>= 0,默认 0最低消费门槛;0 表示无门槛
maxDiscountnumber若设置须 > 0;可空折扣封顶(比例类礼券常用)
settlementAmountnumber>= 0,默认 0跨租户结算金额
settlementCurrencystringISO 4217,默认 THB结算币种
externalProviderstring供货方代码;可空外部供货方,如 vouchain
externalIdstring供货方模板 ID;可空外部供货模板 ID
externalRequiresDirectOrderParamsboolean默认 false外部核销是否需要额外账号参数
applicableScopestring枚举:all_outletspartial_outletssingle_store适用门店范围
bookingEnabledboolean默认 false是否启用预约
bookingDaysInAdvanceinteger>= 0,默认 0可提前预约天数
createdAtstringISO 8601,自动生成创建时间
updatedAtstringISO 8601,自动更新最近更新时间

VoucherClaim(UserVoucher,用户礼券领取记录)

字段类型约束 / 格式说明
_idstringMongoDB ObjectId唯一 ID
voucherIdstringMongoDB ObjectId关联的礼券模板 ID
userIdstringMongoDB ObjectId所属用户 ID
statusstring枚举:activeclaimedredeemedexpired,默认 active领取记录状态
redemptionCodestring唯一;格式如 VCH-{base36时间戳}-{4位随机}核销码
claimedAtstringISO 8601,默认当前时间领取时间
redeemedAtstringISO 8601;未核销前为 null核销时间
expiresAtstringISO 8601;null 为不设到期;一般继承模板 endDate本条记录到期时间
redemptionDetails.locationstring可选核销地点
redemptionDetails.notesstring可选核销备注
redemptionDetails.methodstring可选核销方式,如 api_keypin
externalVoucherCodestring可空外部供货返回的券码
externalRedemptionUrlstring可空外部供货返回的核销链接
externalOrderIdstring可空外部供货订单 ID
externalFulfillmentStatusstring枚举:pendingprocessingfulfilledfailednull外部履约状态

User(用户)

字段类型约束 / 格式说明
_idstringMongoDB ObjectId唯一 ID
emailstring邮箱格式;未设置为 nullemailphone 至少其一有值邮箱
phonestring手机号格式;未设置为 null手机
displayNamestring未设置为 null,已 trim展示名
avatarstring合法 URL;未设置为 null头像
rolestring枚举:membersub_company_admintenant_adminsuper_admin角色
isActiveboolean默认 true是否启用
walletAddressstring以太坊地址 0x…;创建时自动分配托管钱包地址
registrationSourcestring枚举:createddirectstorecampaign注册来源
storeIdobject可能被 populate;含 _idname 等;否则 null门店注册场景
campaignIdobject可能被 populate;含 _idnameslug 等;否则 null活动注册场景
metadataobject任意键值自定义元数据
createdAtstringISO 8601注册时间
updatedAtstringISO 8601最近更新时间

Campaign(活动)

字段类型约束 / 格式说明
_idstringMongoDB ObjectId唯一 ID
namestring至少 1 字符活动名称
descriptionstring可为空串活动说明
slugstring小写 URL 安全;租户+子公司内唯一;可由 name 生成活动页路径标识
tokenIdstringMongoDB ObjectId,必填用户领取礼券时消耗的代币
isActiveboolean默认 true是否启用;停用则对用户隐藏
isPublicboolean默认 true是否公开(免登录可访问)
startDatestringISO 8601,默认创建时间开始时间
endDatestringISO 8601;null 为不设结束结束时间
imagesstring[]每项为合法 URL,可为 []横幅/配图
createdAtstringISO 8601创建时间

Token(代币)

字段类型约束 / 格式说明
_idstringMongoDB ObjectId唯一 ID
namestring已 trim名称
symbolstring通常 2–5 位大写符号(如 LP
descriptionstring可为空串说明
totalSupplystring数字字符串,精度由业务定义总供应量
decimalsinteger>= 0,默认 0小数位
isActiveboolean默认 true是否启用
createdAtstringISO 8601创建时间
updatedAtstringISO 8601更新时间

TokenBalance(代币余额)

字段类型约束 / 格式说明
tokenIdstringMongoDB ObjectId代币 ID
tokenNamestring已 trim代币名称
symbolstring通常 2–5 位大写符号
balancestring数字字符串,>= "0"可用余额
lockedBalancestring数字字符串,>= "0"锁定余额

Transaction(交易流水)

字段类型约束 / 格式说明
_idstringMongoDB ObjectId流水 ID
tokenIdstringMongoDB ObjectId代币 ID
typestring枚举:minttransferburnrewardredeemexpire类型
amountstring数字字符串,> "0"金额
fromUserIdstringMongoDB ObjectId;增发场景为 null转出用户
toUserIdstringMongoDB ObjectId;销毁场景为 null转入用户
memostring未提供为 null备注/原因
createdAtstringISO 8601发生时间

Pagination(分页元数据)

字段类型约束 / 格式说明
pageinteger>= 1当前页(从 1 起)
limitinteger1100每页条数
totalinteger>= 0符合条件的总数
totalPagesinteger>= 0总页数
hasNextPagebooleantrue/false是否有下一页
hasPrevPagebooleantrue/false是否有上一页

12. 代码示例(Code Examples)

以下为可运行示例:路径与 JSON 字段名与线上一致,便于直接复制;示例代码内注释仍为英文不影响运行。

JavaScript / Node.js 示例

javascript
// VIO API Client Example
const VIO_API_BASE = "https://your-domain.com/api/external/v1";
const API_KEY = "vio_live_your_api_key_here";

// Helper function for API calls
async function vioApi(method, endpoint, body = null) {
  const options = {
    method,
    headers: {
      "X-API-Key": API_KEY,
      "Content-Type": "application/json",
    },
  };

  if (body) {
    options.body = JSON.stringify(body);
  }

  const response = await fetch(`${VIO_API_BASE}${endpoint}`, options);
  const data = await response.json();

  if (!data.success) {
    throw new Error(data.error?.message || "API request failed");
  }

  return data;
}

// Example: Create a voucher
async function createVoucher() {
  const result = await vioApi("POST", "/vouchers", {
    name: "Welcome Discount",
    description: "10% off your first purchase",
    value: 10,
    valueType: "percentage",
    totalQuantity: 1000,
    maxClaimsPerUser: 1,
    visibility: "public",
    endDate: "2024-12-31T23:59:59.000Z",
  });

  console.log("Created voucher:", result.data._id);
  return result.data;
}

// Example: Mint tokens to a user
async function mintTokensToUser(tokenId, userId, amount) {
  const result = await vioApi("POST", `/tokens/${tokenId}/mint`, {
    toUserId: userId,
    amount: amount.toString(),
    memo: "Reward for purchase",
  });

  console.log("Minted tokens:", result.data);
  return result.data;
}

// Example: Redeem a voucher by code
async function redeemVoucher(redemptionCode, location) {
  const result = await vioApi("POST", "/vouchers/redeem-by-code", {
    redemptionCode,
    location,
    notes: "Redeemed at checkout",
  });

  console.log("Voucher redeemed:", result.data);
  return result.data;
}

// Example: Consume a voucher with staff PIN
async function consumeVoucherByPin(redemptionCode, staffPin) {
  const result = await vioApi(
    "POST",
    `/vouchers/redeem/${redemptionCode}/pin`,
    {
      pin: staffPin, // e.g., 'HA1234'
    },
  );

  console.log("Voucher consumed by:", result.data.redeemedBy);
  return result.data;
}

// Example: Get user with their balances
async function getUserWithBalances(userId) {
  const [userResult, balancesResult] = await Promise.all([
    vioApi("GET", `/users/${userId}`),
    vioApi("GET", `/users/${userId}/balances`),
  ]);

  return {
    user: userResult.data,
    balances: balancesResult.data,
  };
}

// Example: Search for a user by email
async function findUserByEmail(email) {
  const result = await vioApi(
    "GET",
    `/users/search/by-identifier?email=${encodeURIComponent(email)}`,
  );
  return result.data;
}

Python 示例

python
import requests
from typing import Optional, Dict, Any

VIO_API_BASE = 'https://your-domain.com/api/external/v1'
API_KEY = 'vio_live_your_api_key_here'

def vio_api(method: str, endpoint: str, body: Optional[Dict] = None) -> Dict[str, Any]:
    """Make a request to the VIO API."""
    headers = {
        'X-API-Key': API_KEY,
        'Content-Type': 'application/json',
    }

    url = f'{VIO_API_BASE}{endpoint}'

    if method == 'GET':
        response = requests.get(url, headers=headers)
    elif method == 'POST':
        response = requests.post(url, headers=headers, json=body)
    elif method == 'PATCH':
        response = requests.patch(url, headers=headers, json=body)
    elif method == 'DELETE':
        response = requests.delete(url, headers=headers)
    else:
        raise ValueError(f'Unsupported method: {method}')

    data = response.json()

    if not data.get('success'):
        error = data.get('error', {})
        raise Exception(error.get('message', 'API request failed'))

    return data

# Example: List active vouchers
def list_vouchers(page: int = 1, limit: int = 20):
    result = vio_api('GET', f'/vouchers?page={page}&limit={limit}&isActive=true')
    return result['data'], result['pagination']

# Example: Create a user
def create_user(email: str, password: str, display_name: str):
    result = vio_api('POST', '/users', {
        'email': email,
        'password': password,
        'displayName': display_name,
        'role': 'member',
    })
    return result['data']

# Example: Get analytics overview
def get_analytics_overview():
    result = vio_api('GET', '/analytics/overview')
    return result['data']

# Usage
if __name__ == '__main__':
    # List vouchers
    vouchers, pagination = list_vouchers()
    print(f'Found {pagination["total"]} vouchers')

    # Get analytics
    analytics = get_analytics_overview()
    print(f'Total users: {analytics["users"]["total"]}')

完整流程:创建礼券并跟踪领取

bash
#!/bin/bash
# 使用 cURL 的完整流程示例

API_BASE="https://your-domain.com/api/external/v1"
API_KEY="vio_live_your_api_key_here"

# 1. 新建礼券
echo "Creating voucher..."
VOUCHER_RESPONSE=$(curl -s -X POST "$API_BASE/vouchers" \
  -H "X-API-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Flash Sale 25% Off",
    "description": "Limited time offer - 25% discount",
    "value": 25,
    "valueType": "percentage",
    "totalQuantity": 100,
    "maxClaimsPerUser": 1,
    "visibility": "public",
    "startDate": "2024-02-01T00:00:00.000Z",
    "endDate": "2024-02-28T23:59:59.000Z"
  }')

VOUCHER_ID=$(echo $VOUCHER_RESPONSE | jq -r '.data._id')
echo "Created voucher: $VOUCHER_ID"

# 2. 查询礼券详情
echo "Fetching voucher details..."
curl -s -X GET "$API_BASE/vouchers/$VOUCHER_ID" \
  -H "X-API-Key: $API_KEY" | jq

# 3. 列出该礼券的领取记录
echo "Listing claims..."
curl -s -X GET "$API_BASE/vouchers/claims/list?voucherId=$VOUCHER_ID" \
  -H "X-API-Key: $API_KEY" | jq

# 4. 顾客出示核销码时先查询
REDEMPTION_CODE="ABC123XYZ"
echo "Looking up redemption code..."
curl -s -X GET "$API_BASE/vouchers/redeem/$REDEMPTION_CODE/info" \
  -H "X-API-Key: $API_KEY" | jq

# 5. 核销礼券
echo "Redeeming voucher..."
curl -s -X POST "$API_BASE/vouchers/redeem-by-code" \
  -H "X-API-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d "{
    \"redemptionCode\": \"$REDEMPTION_CODE\",
    \"location\": \"Main Store\",
    \"notes\": \"Customer order #12345\"
  }" | jq

# 6. 礼券维度分析数据
echo "Fetching voucher analytics..."
curl -s -X GET "$API_BASE/analytics/vouchers" \
  -H "X-API-Key: $API_KEY" | jq

13. 更新日志(Changelog)

版本 1.1.0(2026 年 4 月)

注册来源、活动分析与 PIN 权限

  • User 模型增加 registrationSourcecreateddirectstorecampaign
  • User 响应中 storeIdcampaignId 可能为 populate 后的对象引用
  • 增加活动访问与报名等分析统计
  • 新增公开接口 POST /api/campaigns/:id/track-visit 用于活动页访问统计
  • 活动列表项包含 analytics 对象,含 visitsregistrations 等计数
  • 核销 PIN 增加 permissions 字段,可取 voucher_redemptiontoken_claim
  • 每个 PIN 可配置一项或多项权限,决定可授权的操作类型
  • POST /api/public/verify-pin 响应中增加 permissions 数组
  • 店员 PIN 核销礼券(POST /vouchers/redeem/:code/pin)要求 PIN 具备 voucher_redemption 权限
  • 历史上未显式配置权限的 PIN 默认视为拥有 voucher_redemption,以保持兼容

版本 1.0.0(2024 年 2 月)

首次发布

  • 礼券完整 CRUD
  • 用户完整 CRUD
  • 活动完整 CRUD
  • 代币管理(列表、增发、销毁、调整)
  • 礼券、用户、代币分析接口
  • API Key 鉴权与 scope
  • 限流(每分钟 60 次、每日 10,000 次)
  • 支持 API Key IP 白名单

需要帮助?

  • 管理后台:在 设置 > API Keys 创建与管理 API Key
  • Swagger UI:交互式文档位于 /api-docs
  • 支持:请联系 VIO 技术支持

本文档对应 VIO External API v1。最后更新:2026 年 3 月。

VIO v4 平台文档