应用管理平台

API 对接文档 v1

本平台对外提供统一的 REST API。当前开放:用户端认证(含 Refresh Token 轮换)、应用服务资源读写、完整支付对接(创建订单 → 发起支付 → 回调通知 / 主动查单 → 退款)、营销能力(优惠券领取 / 核销 / 释放、广告位投放)、会员中心与积分(代用户开通 / 撤销会员、调整积分;登录用户可自查自己的会员与积分)、幂等写入与按应用限流。支付渠道由平台集中对接,应用只面对统一的订单与支付模型。

概述

Base URL

http(s)://your-domain/api/v1

统一响应格式

所有接口返回相同结构。HTTP 状态码表达传输层语义,code 表达业务语义(0 为成功)。

{
  "code": 0,                // 业务码,0 = 成功
  "message": "success",     // 提示信息
  "data": { },              // 业务数据,失败时为 null
  "request_id": "3f2a..."   // 全链路追踪 ID,同时通过响应头 X-Request-Id 回传
}

分页类接口的 data 为:{"items": [], "total": 100, "page": 1, "page_size": 20}

分层与身份

平台的设计初衷是一套后端服务所有应用:应用只需有自己的前端,即可直调平台 API。按调用方身份,接口分三层开放:

身份凭证可访问范围
公开(匿名) X-App-Id 请求头(应用标识,公开信息) 公开内容型资源的查询:contents / categories / tags / products / plans 的列表与详情
登录用户 用户 Access Token(登录获得) 公开内容型资源全量可读 + 查看与更新自己的资料(昵称 / 头像 / 性别 / 生日 / 密码)+ 只读自查自己的会员(/memberships/me 等)与积分(/point-accounts/me 等)
应用服务
(进阶,可选)
API Key(仅存放于应用自己的服务器) 按 Key 的权限白名单读写资源,支持创建订单、发起支付、接收支付回调、退款优惠券代领 / 核销 / 释放、广告位投放,以及会员开通 / 撤销、积分调整,适用于有自建后端的应用做服务器间调用
前端不要存放 API Key。前端是公开环境,Key 一旦进入前端代码即等于公开。绝大多数纯前端应用(小程序 / H5 / 网页)只需要 X-App-Id + 用户 Token;只有应用确实拥有自己的后端服务器时才需要 API Key。

快速开始

前端直调(绝大多数应用)

  1. 在后台「应用中心」找到应用的 app_id,前端请求携带 X-App-Id 头即可拉取公开内容;
  2. 需要用户身份时调用 POST /auth/login 获取 Token,之后携带 Authorization 头访问用户接口;
  3. 日志中心按 Request-Id / 应用检索调用记录与安全事件。
# 未登录拉取内容列表
curl https://your-domain/api/v1/contents?page=1 -H "X-App-Id: 1"

服务器间调用(进阶)

  1. 应用有自建后端时,进入开发者中心 → API Keys 生成 API Key(明文仅展示一次),创建时按需勾选接口权限,并可配置限流配额与 IP 白名单;
  2. Key 仅保存在服务器环境,携带 Authorization: Bearer {API Key} 调用。

认证:应用服务 API Key(进阶,可选)

仅当应用拥有自己的后端服务器(需要服务器间调用、批量任务等)时才需要 API Key。Key 用于向平台证明「请求来自该应用的服务器」,务必只保存在服务器环境,不要下发到前端。

要求说明
认证头Authorization: Bearer {API Key}
存储方式平台仅保存 SHA-256 哈希,泄露无法回溯明文;请妥善保管创建时展示的明文
接口权限Key 的 permissions 决定可调用的资源与动作,格式 资源:动作(如 users:write)或 * 全量
IP 白名单配置后仅白名单内 IP(支持 CIDR)可调用;未配置则不限制
X-App-Id(可选)一旦携带,必须与 Key 归属应用一致,否则返回 403,用于防止多应用凭据混用
限流按应用每分钟计数(默认 60 次/分钟,可按 Key 配置),超限返回 429

认证:用户端(Access + Refresh Token)

应用内终端用户的登录采用 Access Token + Refresh Token 双凭证。Access Token 有效期 2 小时,Refresh Token 有效期 14 天,两者签发即绑定应用。

POST /auth/login

POST /api/v1/auth/login
Content-Type: application/json
X-App-Id: 1                       // 必填(或在 body 传 app_id)

{
  "login": "tom",                 // 用户名 / 手机号 / 邮箱
  "password": "secret123",
  "device_name": "miniapp",       // 可选,设备名
  "device_id": "uuid",            // 可选
  "platform": "wechat"            // 可选
}
{
  "code": 0,
  "data": {
    "token": "1|xxx...",                       // Access Token,2 小时
    "expires_at": "2026-09-03T12:00:00.000000Z",
    "refresh_token": "(64 位随机串)",         // 仅本次响应可见,14 天
    "refresh_expires_at": "2026-09-17T10:00:00.000000Z",
    "user": { "id": "01J...", "username": "tom", ... }
  }
}

POST /auth/refresh(轮换)

Access Token 过期前,用 Refresh Token 换取新的一对凭证。一次一换:旧 Refresh Token 立即失效

POST /api/v1/auth/refresh
{ "refresh_token": "(上一次返回的 64 位串)" }
重用检测:已轮换过的 Refresh Token 再次出现将被视为凭据泄露——该用户全部会话立即撤销(包括已签发的新 Token),并写入安全日志(category=token_reuse)。客户端收到 401 后应引导用户重新登录。

GET /auth/me · POST /auth/logout

GET  /api/v1/auth/me      # Authorization: Bearer {access_token}
POST /api/v1/auth/logout  # 撤销当前 Access Token 与其会话

资源接口(读)

三种身份共用同一组资源路径,可访问范围由身份决定(见「分层与身份」):

GET /api/v1/{resource}                     # 列表(分页)
GET /api/v1/{resource}/{public_id}         # 详情(按 ULID 定位)

# 公开身份:X-App-Id 头 + 白名单资源(contents/categories/tags/products/plans)
curl https://your-domain/api/v1/contents?page=1 -H "X-App-Id: 1"

# 登录用户 / 应用服务:Authorization 头
curl https://your-domain/api/v1/contents?page=1 -H "Authorization: Bearer {凭证}"

# 列表查询参数
page=1&page_size=20                        # page_size 上限 100
status=normal                              # 按状态过滤
keyword=tom                                # users/contents/products 支持关键字

所有资源标识与引用字段均为 ULID public_id(26 位),金额字段以字符串输出,避免精度丢失。users 列表与订单接口不对公开 / 普通用户开放。

订单接口

orders应用服务专属资源:匿名与登录用户身份不可访问。支持 API 创建(见「支付对接」)与查询(需 Key 具备 orders:read 权限或 *):

GET /api/v1/orders?status=pending&page=1&page_size=20
GET /api/v1/orders/{public_id}

字段说明

字段说明
id订单 ULID public_id
order_no业务订单号(日期 + 应用位 + 随机,应用内唯一)
user_id下单用户 ULID
statuspending / paid / processing / shipped / completed / cancelled / closed / refunding / refunded
payment_statusunpaid / paid / refunded
shipping_statusunshipped / shipped / received
total_amount 等total_amount · discount_amount · payable_amount · paid_amount · refund_amount,均为字符串金额;currency 默认 CNY
paid_at / created_atISO-8601(UTC)
buyer_remark / metadata买家备注;创建订单时传入的第三方业务数据(如自有单号)原样回传
  • 列表支持 status 筛选与通用分页参数;keyword 不适用于 orders。
  • 订单商品明细与支付单流水暂不通过 API 开放,可在后台「订单中心」查看完整快照与流水。

写操作

开放资源:users / contents / categories / tags / products。写入的数据自动归属当前应用;category_id 等引用字段仅能指向同应用资源,跨应用引用返回 422。

  • 应用服务身份:五类资源全量创建 / 更新 / 删除(软删规则同后台),需对应 {资源}:write 权限。
  • 登录用户身份:仅可更新自己的资料(PATCH /users/{自己的 public_id}),字段限昵称、头像、性别、生日、密码;username、手机号等关键字段由服务端裁剪,不可通过接口修改。
  • 公开身份:不可调用任何写接口。
POST   /api/v1/{resource}                  # 创建
PATCH  /api/v1/{resource}/{public_id}      # 更新(部分字段)
DELETE /api/v1/{resource}/{public_id}      # 删除(users/contents/products 为软删)

字段说明(* 为创建必填)

资源字段
users username(应用内唯一) · nickname · password(6~64 位) · phone(唯一) · email(唯一) · gender(0/1/2) · birthday · avatar · status(normal/disabled)。API 创建的用户 register_source 固定为 api
contents type* · title* · subtitle · summary · body(JSON 正文形态) · cover · category_id · status(draft/published/offline) · sort · publish_at · expire_at · metadata
categories name* · slug* · type · parent_id · icon · sort · status(active/disabled) · metadata
tags name* · slug* · type · sort
products type* · name* · subtitle · description · cover · category_id · status(draft/listed/off_shelf) · sort · price · market_price · cost_price · stock · metadata

支付对接

平台采用集中收款模式:支付宝(当面付 / 网站支付)、微信(Native / H5)、易支付、余额等渠道由平台统一配置与对接,应用只需面对统一的订单与支付模型。第三方应用(持有 API Key 的服务端)的完整支付流程:

  1. 创建订单POST /orders(权限 orders:write),返回订单 public_id;
  2. 发起支付POST /orders/{public_id}/pay(权限 orders:pay),返回收款凭证(二维码内容 / 跳转链接),前端渲染给用户;
  3. 用户完成支付:渠道回调由平台验签并自动结算,订单推进为 paid
  4. 同步结果:双通道保障——平台向你订阅的地址投递 order.paid Webhook(见「Webhook 通知」),或轮询 GET /orders/{public_id}(建议间隔 ≥ 5 秒,进入终态后停止);
  5. 退款(可选):POST /orders/{public_id}/refund(权限 orders:refund),不传金额默认按可退余额全额退款。

POST /orders(创建订单)

POST /api/v1/orders
Authorization: Bearer {API Key}      // 权限 orders:write
X-App-Id: 1
Idempotency-Key: {UUID}               // 强烈建议,防止重复下单

{
  "user": "01J...",                    // 必填:下单用户的 public_id(经 POST /users 创建或已有用户)
  "subject": "会员月卡",               // 必填:订单标题(支付凭证展示),同时写入 metadata.subject
  "total_amount": "199.00",            // 必填:字符串金额
  "discount_amount": "10.00",          // 可选:优惠金额,payable = total - discount
  "currency": "CNY",
  "buyer_remark": "买家备注",
  "metadata": {                       // 可选:第三方业务数据(如自有单号),查询时原样回传
    "client_order_no": "EXT-1001"
  }
}

返回 data 为完整订单对象(字段见「订单接口」)。同时投递 order.created Webhook。

POST /orders/{public_id}/pay(发起支付)

POST /api/v1/orders/{public_id}/pay
Authorization: Bearer {API Key}      // 权限 orders:pay

{ "channel": "alipay", "scene": "f2f" }

channel 取值以支付设置中已启用的渠道为准(alipay / wechat / epay / balance);scene 可选(如 alipay 的 f2f / page / wap,缺省取渠道默认场景)。响应 data

{
  "payment_no": "P2026...",            // 平台支付单号
  "type": "qrcode",                    // qrcode = 二维码内容;redirect = 跳转链接;none = 已同步完成(余额)
  "qrcode": "https://qr.alipay.com/...",  // type=qrcode 时:将该串渲染为二维码展示给用户
  "redirect_url": null,                // type=redirect 时:引导用户跳转完成支付
  "amount": "189.00", "payment_status": "pending", "order_status": "pending"
}
  • 一笔订单允许多次支付尝试(换渠道 / 重新出码),平台仅以验签成功的支付单驱动订单状态,历史尝试保留用于对账;
  • 渠道未启用 / 场景不支持 / 状态不允许等业务原因返回 5001(HTTP 422),message 含具体原因。

POST /orders/{public_id}/refund(退款)

POST /api/v1/orders/{public_id}/refund
Authorization: Bearer {API Key}      // 权限 orders:refund

{ "amount": "50.00", "reason": "部分退款" }   // 不传 amount = 按可退余额全额

同步返回退款结果:status=success 表示资金已原路退回(含 transaction_id);渠道暂不支持在线退款时返回 422 并说明原因,可在后台人工处理。退款成功自动累加订单 refund_amount,并投递 order.refunded Webhook。

本地开发注意:渠道回调发往平台部署域名,本地环境(localhost)收不到公网回调。无需担心结果不一致——平台具备查单补偿能力(支付单未结算时主动向渠道查询真实支付结果并结算);如需完整验证回调推送,请使用内网穿透将部署地址暴露公网。
权益发放以平台订单终态为准。开通会员、发货等操作应依据 payment_status=paid(服务端数据)触发,不要信任任何来自前端或第三方的「支付成功」通知;渠道错误(5001)与回调验签失败(5002)由平台记录流水并处理,重试由支付网关按协议自动执行。

优惠券

营销接口为应用服务专属(匿名与登录用户身份不可访问),数据按应用隔离:只能查询与操作本应用创建的券模板、券实例与广告。模型分两层——券模板(coupon,运营在后台「营销中心」配置)与券实例(coupon-redemption,用户领取后生成的券码)。典型流程:

  1. 查券GET /coupons?claimable=1 拉取可领取的券模板,在前端展示券列表;
  2. 领取POST /coupons/{public_id}/claim(权限 coupons:write)代用户领取,生成券实例;
  3. 核销POST /coupon-redemptions/{public_id}/redeem(权限 coupons:write),传入订单金额,平台校验门槛并计算优惠金额;可传入订单 public_id 完成绑定;
  4. 释放(可选):订单取消时调用 POST /coupon-redemptions/{public_id}/release,券回退为未使用并回退券模板的核销计数;
  5. 查询GET /coupon-redemptions 按 user / coupon / status 查询券实例。

GET /coupons(券模板列表)

GET /api/v1/coupons?claimable=1&page=1&page_size=20
Authorization: Bearer {API Key}      // 权限 coupons:read

# 查询参数
claimable=1        # 仅返回可领取的券(启用中 + 未过发放截止 + 有余量)
status=active      # 按状态过滤(active / disabled)
type=reduction     # 按类型过滤(reduction 满减 / discount 折扣)
keyword=新人       # 按名称或编码模糊搜索
GET /api/v1/coupons/{public_id}   # 券模板详情

POST /coupons/{public_id}/claim(代用户领取)

POST /api/v1/coupons/{public_id}/claim
Authorization: Bearer {API Key}      // 权限 coupons:write
Idempotency-Key: {UUID}

{ "user": "01J..." }                // 必填:领取用户的 public_id

返回 201 与券实例对象。服务端按启用状态、发放余量、每人限领数依次校验,不满足时返回 422 与对应业务码(6001 / 6002 / 6003)。券实例领取后进入有效期(valid_type=days 时从领取时刻起算)。

GET /coupon-redemptions(券实例列表)

GET /api/v1/coupon-redemptions?user=01J...&status=unused
Authorization: Bearer {API Key}      // 权限 coupons:read

# 查询参数
user=01J...        # 按用户 public_id 过滤
coupon=01J...      # 按券模板 public_id 过滤
status=unused      # unused / used / expired / voided

POST /coupon-redemptions/{public_id}/redeem(核销)

POST /api/v1/coupon-redemptions/{public_id}/redeem
Authorization: Bearer {API Key}      // 权限 coupons:write
Idempotency-Key: {UUID}

{
  "amount": "189.00",               // 必填:订单金额(字符串)
  "order": "01J..."                 // 可选:绑定订单 public_id(须属于持券用户)
}

响应 data

{
  "discount_amount": "10",          // 本次优惠金额(字符串)
  "redemption": { ... },            // 核销后的券实例(status=used)
  "coupon": { ... }                 // 券模板(used_count 已累加)
}
  • 优惠计算:满减券取面额(不超过订单金额);折扣券按 订单金额 × 折扣率 ÷ 10 计算(8.5 表示 85 折),不超过 max_discount 封顶;
  • unused 状态的券可核销(6004);订单金额未达 min_spend 门槛返回 6005;
  • 传入 order 后券与订单绑定,订单取消时可用 release 回退;
  • 核销前平台自动将已过失效时间的券置为 expired(过期券不占限领名额,可重新领取)。

POST /coupon-redemptions/{public_id}/release(释放)

POST /api/v1/coupon-redemptions/{public_id}/release
Authorization: Bearer {API Key}      // 权限 coupons:write
Idempotency-Key: {UUID}

{ "order": "01J..." }               // 必填:核销时绑定的订单 public_id

仅限已使用且绑定到该订单的券:回退为 unused、券模板 used_count 减一。用于订单取消 / 退款关闭后的券回退,其他情形返回 422(6004)。

字段说明

券模板字段说明
id / name / code券模板 ULID、名称、唯一编码
typereduction 满减券(amount 为面额)/ discount 折扣券(discount 为折扣率,8.5 = 85 折,max_discount 为单笔封顶)
min_spend使用门槛(字符串金额,0 = 无门槛)
total_quantity / issued_count / used_count / remaining发放总量(0 = 不限量)、已发放、已核销、剩余可发放(不限量时为 null)
per_user_limit每人限领数(仅统计 unused / used 的有效券)
valid_typefixed 固定时间段(valid_start_at ~ valid_end_at)/ days 领取后 N 天内有效(valid_days
status / descriptionactive 启用 / disabled 停用;券描述
券实例字段说明
id / code券实例 ULID、唯一券码(CR- 前缀)
statusunused 未使用 / used 已使用 / expired 已过期 / voided 已作废
sourceadmin 后台发放 / system API 领取 / code 兑换码
coupon_id / coupon_name所属券模板 ULID 与名称
user_id / order_id持券用户 ULID;核销绑定的订单 ULID(未绑定为 null)
used_at / expired_at核销时刻、失效时刻(ISO-8601 UTC)
优惠金额以核销响应为准。discount_amount 由平台按券规则计算(含封顶与「不超过订单金额」约束),应用应直接使用该值计算应付金额,不要在前端复算。领取 / 核销 / 释放均建议携带 Idempotency-Key 防止重复操作。

广告

应用服务身份拉取本应用配置的广告投放(后台「营销中心 → 广告管理」维护)。仅返回投放中且在投放期内(start_at / end_at 任一为空表示不限)的广告,按 sort 升序排列:

GET /api/v1/ads?position=banner&page=1&page_size=50
Authorization: Bearer {API Key}      // 权限 ads:read

# 查询参数
position=banner    # 广告位:banner 首页轮播 / popup 弹窗 / splash 启动图 / feed 信息流
type=image         # 素材类型:image 图片 / text 文字 / code 自定义代码

曝光与点击上报

POST /api/v1/ads/{public_id}/impression   # 广告被展示时上报(权限 ads:write)
POST /api/v1/ads/{public_id}/click        # 用户点击广告时上报(权限 ads:write)

仅投放中的广告可上报(已下线 / 过期返回 404),计数原子累加,返回当前累计值:

{ "id": "01J...", "impression_count": 128, "click_count": 9 }

字段说明

字段说明
id / title / position广告 ULID、标题、广告位(banner / popup / splash / feed)
typeimage 图片 / text 文字 / code 自定义代码
素材字段按 type 取对应字段:image_url(图片地址,配合 link_url 跳转)、text_content(文字内容)、code_content(自定义 HTML/代码,前端按白名单规则渲染)
sort / start_at / end_at排序值(升序)、投放起止时间(ISO-8601 UTC,null 表示不限)
statusonline 投放中 / offline 已下线(接口仅返回 online)
impression_count / click_count累计曝光 / 点击数(由上报接口累加)
code 类型广告安全渲染。code_content 为运营在后台录入的自定义代码,渲染于应用内时请使用 WebView 沙箱或白名单标签过滤,避免直接注入页面 DOM。

会员中心

会员能力分两层——会员计划(plan,运营在后台「会员中心」配置,已通过「资源接口」的 plans 公开开放)与用户会员关系(membership,用户开通后的状态)。应用服务身份可查询本应用的会员关系、变动记录与用户权益,并代用户开通 / 续费 / 撤销;登录用户可只读自查(/me 端点,仅需用户 Access Token)。数据按应用隔离。

GET /memberships(会员关系列表)

GET /api/v1/memberships?user={用户 ULID}&status=active
Authorization: Bearer {API Key}      // 权限 memberships:read

# 查询参数
user={ULID}    # 按用户筛选
plan={ULID}    # 按计划筛选
status=        # active 生效中 / expired 已过期 / cancelled 已取消

登录用户自查:GET /memberships/me(返回自己的会员,含生效中与历史)。

POST /memberships/grant(代用户开通 / 续费)

POST /api/v1/memberships/grant
Authorization: Bearer {API Key}      // 权限 memberships:write
Idempotency-Key: {UUID}

{
  "user": "01J...",                  // 必填:用户 ULID
  "plan": "01J...",                  // 必填:会员计划 ULID
  "source": "order",                 // 可选:order 订单 / purchase 直接购买 / admin 管理员 / gift 赠送(默认 order)
  "order": "01J...",                 // 可选:关联订单 ULID
  "remark": "年卡活动发放"            // 可选:备注
}

同一用户同一计划已生效的会员自动顺延(从当前到期时间续加天数,已过期则从现在起算),并按计划配置同步发放权益;计划已停用返回 422(7002)。成功返回 201 与完整会员对象。撤销:POST /memberships/{public_id}/revoke(权限 memberships:write),终止会员并同步撤销其发放的权益;状态非 active 返回 422(7003)。

GET /membership-records(变动记录)· GET /user-entitlements(用户权益)

均为分页接口。变动记录支持 user / plan / action(purchase 购买 / renew 续费 / gift 赠送 / expire 过期 / cancel 取消)筛选;用户权益支持 user / status(active 生效中 / expired 已过期 / revoked 已撤销)筛选。登录用户自查对应 GET /membership-records/meGET /user-entitlements/me

字段说明

会员关系字段说明
id / status会员关系 ULID、状态(active / expired / cancelled)
user_id / plan_id / plan_name用户 ULID、计划 ULID 与名称
source / order_id开通来源(order / purchase / admin / gift)、关联订单 ULID
started_at / expired_at生效、到期时间(ISO-8601 UTC,null 表示永久)
变动记录字段action 动作、days 本次天数、remark 备注、created_at 时刻
用户权益字段code / name 权益编码与名称、value 权益值(配额型为额度)、source_type 来源、起止时间与状态

积分

积分账户按用户建立,余额一律由流水驱动(平台铁律,不存在直接改余额的通道)。应用服务身份可查询本应用用户的积分账户 / 流水 / 规则,并代用户调整;登录用户可只读自查。每次调整在事务中写入流水并同步余额,余额不足时整体回滚(422 / 7001)。数据按应用隔离。

GET /point-accounts(账户)· GET /point-transactions(流水)· GET /point-rules(规则)

GET /api/v1/point-accounts?user={用户 ULID}
Authorization: Bearer {API Key}      // 权限 points:read

# point-transactions 查询参数
user={ULID}    # 按用户筛选
type=          # earn 获得 / spend 消费 / expire 过期 / adjust 调整
# point-rules 查询参数:status=active 仅返回启用规则

登录用户自查:GET /point-accounts/me(从未获得过积分时返回零账户)、GET /point-transactions/me

POST /points/adjust(代用户调整)

POST /api/v1/points/adjust
Authorization: Bearer {API Key}      // 权限 points:write
Idempotency-Key: {UUID}

{
  "user": "01J...",                  // 必填:用户 ULID
  "amount": 100,                     // 必填:带符号整数(正数增加 / 负数扣减,0 无效,|amount| ≤ 1000000)
  "type": "adjust",                  // 可选:earn / spend / adjust / expire(默认 adjust,语义标注,方向以符号为准)
  "remark": "签到奖励补发"            // 可选:备注
}
// 201 响应:返回新余额与本次流水
{ "balance": 320, "transaction": { "id": "01J...", "type": "adjust", "amount": 100,
  "balance_before": 220, "balance_after": 320, ... } }

扣减超过余额时整体失败(422 / 7001),不产生部分变动。账户与流水字段:

账户字段说明
user_id用户 ULID
balance / frozen当前余额 / 冻结积分
total_earned / total_spent累计获得 / 累计消费
流水字段type 类型、amount 变动额(正收益负支出)、balance_before / balance_after 变动前后余额、source_type / remark 来源与备注、created_at 时刻

Webhook 通知

进入开发者中心 → Webhooks 订阅事件并填写接收地址,事件发生时平台 POST JSON 到该地址。可订阅事件:

事件触发时机data 主要字段
user.created / user.updated通过 API 创建 / 更新用户user_id · username · nickname · status
order.createdAPI 创建订单order_id · order_no · subject · payable_amount
payment.success渠道回调结算 / 查单补偿结算payment_no · order_id · provider · amount · transaction_id
order.paid订单推进为已支付order_id · order_no · payable_amount · paid_amount · paid_at
order.completed订单流转为已完成order_id · order_no · completed_at
order.refunded退款成功refund_no · order_id · amount · transaction_id · completed_at
membership.created用户开通会员(续费不通知)user_id · plan_id · plan_name · expired_at

报文与验签

POST {订阅的接收地址}
Content-Type: application/json
X-Event: order.paid
X-Event-Id: 01M...                    // 事件唯一 ID(用于幂等去重)
X-Signature: 9f8a...                  // HMAC-SHA256(原始请求体, 订阅密钥)

{ "event": "order.paid", "event_id": "01M...", "occurred_at": "...", "data": { ... } }
// PHP 验签示例
$expected = hash_hmac('sha256', file_get_contents('php://input'), $secret);
if (! hash_equals($expected, $_SERVER['HTTP_X_SIGNATURE'] ?? '')) {
    http_response_code(400); exit;    // 验签失败直接拒绝
}
  • 订阅密钥在创建订阅时仅展示一次;收到 2xx 即视为投递成功,请先落库再做业务处理;
  • event_id 幂等去重;投递失败按 2 / 4 / 8 / 16 / 32 分钟指数退避重试,最多 5 次,可在「开发者中心 → 投递记录」查看;
  • Webhook 可能因网络问题延迟或重复,订单最终状态以查询接口为准,回调与轮询互为补充。

幂等

POST 创建类请求携带 Idempotency-Key 请求头(应用内唯一,建议 UUID),可安全应对重复点击与网络重试:

  • 首次请求正常执行,响应被缓存 24 小时
  • 相同 Key 重放直接返回首次响应(不产生新数据);
  • 相同 Key 并发请求返回 1006 幂等冲突(HTTP 409);
  • 服务端 5xx 失败不缓存,可使用相同 Key 重试。

限流

Redis 固定窗口计数,按调用方身份选择维度,每次响应携带:

X-RateLimit-Limit: 60        # 配额
X-RateLimit-Remaining: 42    # 剩余
  • 应用服务:按 API Key 计数,配额取 Key 的 rate_limit 配置(默认 60 次/分钟,0 = 不限);
  • 登录用户:按用户 120 次/分钟;
  • 公开匿名:按 应用 + IP 30 次/分钟(防刷从严)。

超限返回 HTTP 429(code=1005)与 Retry-After 头,同时写入安全日志(category=rate_limit)。用户登录 / 刷新接口另有独立的基础限流保护。

资源标识(ULID public_id)

内部主键为自增数字,对外一律使用 26 位 ULID(Crockford Base32,时间有序、全局唯一)。API 请求路径、响应字段、资源间引用均为 public_id,连续数字 ID 不对外暴露,防止资源枚举。

错误码

code说明HTTP
0成功200 / 201
1001参数错误(message 含具体原因)422
1002未认证(Key / Token 缺失或无效)401
1003无权限(permissions 不足 / IP 不在白名单 / X-App-Id 不一致)403
1004资源不存在404
1005请求过于频繁(限流)429
1006幂等冲突(相同 Key 处理中)409
1999系统错误500
2001账号或密码错误401
2002Token 已过期401
2003账号已禁用403
4001 / 4002库存不足 / 订单状态不允许该操作422
5001 / 5002支付渠道错误 / 回调验签失败422 / 400
6001优惠券不可用(已停用 / 已过发放截止)422
6002优惠券已发完422
6003已达每人限领上限422
6004券不可核销 / 不可释放(状态或订单绑定不匹配)422
6005未达到使用门槛422
7001积分余额不足422
7002 / 7003会员计划不可用 / 会员状态不允许该操作422

安全规范

  • 所有请求必须经 HTTPS 传输;凭据不得出现在 URL 或日志明文中。
  • 按应用分配最小权限的 Key:只读业务不发放 write 权限;调用方网络固定时务必配置 IP 白名单。
  • 写操作建议一律携带 Idempotency-Key;客户端需实现 Refresh Token 轮换与 401 重新登录逻辑。
  • 调用日志(含脱敏请求体)保留于平台,供安全审计与用量分析。