Skip to content

User 前台 API 文档 ​

更新时间:2026-03-31

本文档覆盖 user/src/api/index.ts 当前全部前台 API,字段定义以以下实现为准:

  • api/internal/router/router.go
  • api/internal/http/handlers/public/*.go
  • api/internal/models/*.go

开源仓库地址 ​


0. API 变更日志 ​

0.0.1 Public API Response DTO 精简与安全加固(2026-03-31) ​

破坏性变更 ​

  • 所有订单相关接口不再返回自增 id 字段(Order.id、OrderItem.id、Fulfillment.id),统一使用 order_no 作为订单标识。
  • 订单详情路由从 GET /orders/:id 改为 GET /orders/:order_no;取消路由从 POST /orders/:id/cancel 改为 POST /orders/:order_no/cancel;交付下载从 GET /orders/:id/fulfillment/download 改为 GET /orders/:order_no/fulfillment/download。
  • 游客订单详情路由从 GET /guest/orders/:id 改为 GET /guest/orders/:order_no;交付下载同理。
  • 旧路由 GET /orders/by-order-no/:order_no 和 GET /guest/orders/by-order-no/:order_no 已删除,直接使用 GET /orders/:order_no。
  • 支付接口 POST /payments 和 GET /payments/latest 的请求参数 order_id 改为 order_no(string 类型)。游客支付接口同理。
  • GET /payments/latest 响应中 order_id 改为 order_no。

移除的字段 ​

以下字段从 Public API 响应中永久移除,前端不应再依赖:

  • Order: id、parent_id、user_id、coupon_id、promotion_id、client_ip、updated_at
  • OrderItem: id、order_id、delivered_by、created_at、updated_at
  • Fulfillment: id、order_id、delivered_by、created_at、updated_at
  • PublicProduct: cost_price_amount、manual_stock_locked、manual_stock_sold、is_active、sort_order、created_at、updated_at、is_affiliate_enabled、is_mapped、seo_meta
  • PublicSKU: cost_price_amount、product_id、manual_stock_locked、auto_stock_total、auto_stock_locked、auto_stock_sold、sort_order、created_at、updated_at
  • Banner: name、is_active、start_at、end_at、sort_order、created_at、updated_at
  • Post: is_published、created_at
  • Category: created_at
  • WalletTransaction: order_id
  • AffiliateCommission: order_id

新增的字段 ​

  • Order: member_discount_amount、wallet_paid_amount、online_paid_amount、refunded_amount
  • OrderItem: sku_snapshot、member_discount_amount
  • Fulfillment: payload_line_count
  • UserProfile: member_level_id、total_recharged、total_spent
  • Category: parent_id、icon

0.0 活动价系统改进:阶梯规则 + 前端展示优化(2026-03-09) ​

新增字段 ​

  • PublicProduct 新增 promotion_rules 字段(类型 PromotionRule[]),返回该商品所有生效中的活动规则。
  • 即使当前 SKU 单价未满足活动门槛,该字段也会返回数据,便于前端展示"多买可享折扣"等活动提示。

阶梯活动规则 ​

  • 同一商品支持配置多条活动规则(不同 min_amount 门槛),形成阶梯折扣。
  • 后端按购买小计(单价 × 数量)从高到低匹配门槛,取满足条件的最高档规则计算折扣。
  • 示例:
    • 规则 A:min_amount=50,优惠 1%
    • 规则 B:min_amount=150,优惠 2%
    • 购买金额 49 → 无折扣;100 → 匹配规则 A(1%);200 → 匹配规则 B(2%)
  • 单条规则场景行为不变,无破坏性变更。

活动类型说明 ​

type含义计算方式
percent百分比折扣每件单价 = 原价 × (100 - value) / 100
fixed固定金额减免每件单价 = 原价 - value
special_price直降特价每件单价 = value

注意: 所有折扣均作用于单件商品价格,而非订单总金额。min_amount 是购买小计门槛(单价 × 数量),达到门槛后每件商品享受对应折扣。


1. 通用约定 ​

1.1 Base URL ​

  • API 前缀:/api/v1
  • 本文中的路径均省略 /api/v1,调用时请自行拼接。

1.2 鉴权 ​

用户登录态接口需携带:

http
Authorization: Bearer <user_token>

1.3 统一响应结构 ​

成功响应 ​

json
{
  "status_code": 0,
  "msg": "success",
  "data": {},
  "pagination": {
    "page": 1,
    "page_size": 20,
    "total": 100,
    "total_page": 5
  }
}

失败响应 ​

json
{
  "status_code": 400,
  "msg": "请求参数错误",
  "data": {
    "request_id": "01HR..."
  }
}

顶层字段说明 ​

字段类型说明
status_codenumber业务状态码,0 表示成功,非 0 表示失败
msgstring业务提示信息
dataobject/array/null业务数据
paginationobject分页信息,仅分页接口返回

1.4 分页参数约定 ​

参数类型必填默认说明
pagenumber否1页码,最小 1
page_sizenumber否20每页条数,最大 100

1.5 通用请求结构 ​

CaptchaPayload(验证码载荷) ​

字段类型必填说明
captcha_idstring否图片验证码 ID(provider=image 时使用)
captcha_codestring否图片验证码文本(provider=image 时使用)
turnstile_tokenstring否Turnstile Token(provider=turnstile 时使用)

OrderItemInput(订单项) ​

字段类型必填说明
product_idnumber是商品 ID
quantitynumber是购买数量(>0)
fulfillment_typestring否交付类型,推荐值:manual / auto

ManualFormData(人工交付表单值) ​

manual_form_data 是对象,Key 为 product_id,Value 为该商品的表单提交数据。

json
{
  "1001": {
    "receiver_name": "张三",
    "phone": "13277745648",
    "address": "广东省深圳市..."
  }
}

2. 数据对象字段字典 ​

以下对象用于后续各接口“返回结构”引用。

2.1 PublicProduct ​

字段类型说明
category_idnumber分类 ID
slugstring商品唯一标识
titleobject多语言标题
descriptionobject多语言摘要
contentobject多语言详情内容
price_amountstring商品价格金额
imagesstring[]商品图片列表
tagsstring[]标签列表
purchase_typestring购买身份限制:guest / member
min_purchase_quantitynumber单次最小购买数量(0 表示不限)
max_purchase_quantitynumber单次最大购买数量(0 表示不限)
fulfillment_typestring交付类型:manual / auto
manual_form_schemaobject人工交付表单 Schema
manual_stock_availablenumber人工可用库存
auto_stock_availablenumber自动可用库存
stock_statusstring库存状态:unlimited / in_stock / low_stock / out_of_stock
is_sold_outboolean是否售罄
categoryCategory分类信息
skusPublicSKU[]SKU 列表
promotion_idnumber命中的活动 ID(可选)
promotion_namestring活动名称(可选)
promotion_typestring活动类型(可选)
promotion_price_amountstring活动价金额(可选)
promotion_rulesPromotionRule[]活动规则列表(可选)
member_pricesMemberLevelPrice[]会员等级价格列表(可选)

2.1.1 PublicSKU ​

skus[] 数组中每个元素的结构如下:

字段类型说明
idnumberSKU ID(下单时使用此 ID)
sku_codestringSKU 编码(同商品内唯一)
spec_valuesobject规格值(多语言)
price_amountstringSKU 原价
manual_stock_totalnumber人工库存总量(-1 表示无限库存)
manual_stock_soldnumber人工库存已售量
auto_stock_availablenumber自动发货库存可用量
upstream_stocknumber上游库存(-1 表示无限,0 表示售罄)
is_activeboolean是否启用
promotion_price_amountstringSKU 活动价金额(可选)
member_price_amountstring会员价金额(可选)

2.1.2 PromotionRule ​

promotion_rules[] 数组中每个元素的结构如下:

字段类型说明
idnumber活动规则 ID
namestring活动名称
typestring活动类型:percent / fixed / special_price
valuestring活动数值(字符串金额/百分比,如 "2.00" 或 "5.00")
min_amountstring触发门槛金额(购买小计 = 单价 × 数量,如 "200.00";"0.00" 表示无门槛)

活动类型与折扣计算:

type含义折扣计算(作用于每件单价)
percent百分比折扣单价 = 原价 × (100 - value) / 100
fixed固定金额减免单价 = 原价 - value
special_price直降特价单价 = value
  • 所有折扣均作用于单件商品价格,而非订单总金额。
  • min_amount 是购买小计门槛(单价 × 数量),达到门槛后每件商品享受对应折扣。
  • 同一商品可配置多条不同 min_amount 的规则,形成阶梯折扣。后端从高到低匹配,取满足条件的最高档。

促销价计算说明: 促销活动以商品为维度配置,促销价下沉到每个 SKU 独立计算。例如某商品配置了"优惠 2%"促销,99 元的 SKU 促销价为 97.02,77 元的 SKU 促销价为 75.46。产品级的 promotion_price_amount 取所有 SKU 中的最低促销价,适用于列表页展示。promotion_rules 返回所有生效规则(按 min_amount 升序),即使当前单价未满足门槛也会返回,便于前端展示活动提示。

2.2 Post ​

字段类型说明
idnumber文章 ID
slugstring文章唯一标识
typestring类型:blog / notice
titleobject多语言标题
summaryobject多语言摘要
contentobject多语言内容
thumbnailstring缩略图 URL
published_atstring/null发布时间

2.3 Banner ​

字段类型说明
idnumberBanner ID
positionstring投放位置(如 home_hero)
titleobject多语言标题
subtitleobject多语言副标题
imagestring主图
mobile_imagestring移动端图
link_typestring跳转类型:none / internal / external
link_valuestring跳转值
open_in_new_tabboolean是否新窗口打开

2.4 Category ​

字段类型说明
idnumber分类 ID
parent_idnumber父分类 ID(0 表示一级分类)
slugstring分类唯一标识
nameobject多语言名称
iconstring分类图标
sort_ordernumber排序

2.5 UserProfile ​

字段类型说明
idnumber用户 ID
emailstring邮箱
nicknamestring昵称
email_verified_atstring/null邮箱验证时间
localestring语言(如 zh-CN)
member_level_idnumber会员等级 ID
total_rechargedstring累计充值金额
total_spentstring累计消费金额
email_change_modestring邮箱变更模式:bind_only / change_with_old_and_new
password_change_modestring密码变更模式:set_without_old / change_with_old

2.6 UserLoginLog ​

字段类型说明
idnumber日志 ID
user_idnumber用户 ID(失败时可能为 0)
emailstring登录邮箱
statusstring登录结果:success / failed
fail_reasonstring失败原因枚举
client_ipstring客户端 IP
user_agentstring客户端 UA
login_sourcestring登录来源:web / telegram
request_idstring请求追踪 ID
created_atstring记录创建时间

2.7 OrderPreview ​

字段类型说明
currencystring币种(全站统一,来源 site_config.currency)
original_amountstring原价总额
discount_amountstring总优惠金额
promotion_discount_amountstring活动优惠金额
total_amountstring应付总额
itemsOrderPreviewItem[]预览订单项

2.8 OrderPreviewItem ​

字段类型说明
product_idnumber商品 ID
titleobject商品标题快照(多语言)
tagsstring[]商品标签快照
unit_pricestring单价
quantitynumber数量
total_pricestring小计
coupon_discount_amountstring优惠券分摊金额
promotion_discount_amountstring活动分摊金额
fulfillment_typestring交付类型

2.9 Order ​

字段类型说明
order_nostring订单号
guest_emailstring游客邮箱(游客订单)
guest_localestring游客语言
statusstring订单状态:pending_payment / paid / fulfilling / partially_delivered / delivered / completed / canceled
currencystring订单币种
original_amountstring原价
discount_amountstring优惠金额
member_discount_amountstring会员优惠金额
promotion_discount_amountstring活动优惠金额
total_amountstring实付金额
wallet_paid_amountstring钱包支付金额
online_paid_amountstring在线支付金额
refunded_amountstring已退款金额
expires_atstring/null待支付过期时间
paid_atstring/null支付成功时间
canceled_atstring/null取消时间
created_atstring创建时间
itemsOrderItem[]订单项
fulfillmentFulfillment交付记录(可选)
childrenOrder[]子订单列表(可选)

2.10 OrderItem ​

字段类型说明
titleobject商品标题快照
sku_snapshotobjectSKU 快照(编码/规格)
tagsstring[]商品标签快照
unit_pricestring单价
quantitynumber数量
total_pricestring小计
coupon_discount_amountstring优惠券分摊金额
member_discount_amountstring会员优惠分摊金额
promotion_discount_amountstring活动优惠金额
fulfillment_typestring交付类型
manual_form_schema_snapshotobject人工交付表单 Schema 快照
manual_form_submissionobject用户提交的人工表单值

2.11 Fulfillment ​

字段类型说明
typestring交付类型:auto / manual
statusstring交付状态:pending / delivered
payloadstring文本交付内容
payload_line_countnumber交付内容总行数
delivery_dataobject结构化交付信息
delivered_atstring/null交付时间

2.12 PaymentLaunch ​

字段类型说明
payment_idnumber支付记录 ID
order_nostring订单号(latest 接口返回)
channel_idnumber支付渠道 ID(latest 接口返回)
provider_typestring提供方:official / epay
channel_typestring渠道:alipay / wechat / paypal / stripe 等
interaction_modestring交互方式:qr / redirect / wap / page
pay_urlstring跳转支付链接
qr_codestring二维码内容
expires_atstring/null支付单过期时间

3. 公共接口(无需登录) ​

3.1 获取站点配置 ​

接口:GET /public/config

认证:否

请求参数 ​

无

成功响应示例 ​

json
{
  "status_code": 0,
  "msg": "success",
  "data": {
    "languages": ["zh-CN", "zh-TW", "en-US"],
    "currency": "CNY",
    "contact": {
      "telegram": "https://telegram.me/dujiaostudio",
      "whatsapp": "https://wa.me/1234567890"
    },
    "site_name": "Dujiao-Next",
    "scripts": [
      {
        "name": "Plausible",
        "enabled": true,
        "position": "head",
        "code": "<script defer data-domain=\"localhost\" src=\"https://xxx.com/js/script.js\"></script>"
      }
    ],
    "payment_channels": [
      {
        "id": 1,
        "name": "支付宝电脑站",
        "provider_type": "official",
        "channel_type": "alipay",
        "interaction_mode": "page",
        "fee_rate": "0.00"
      }
    ],
    "captcha": {
      "provider": "turnstile",
      "scenes": {
        "login": true,
        "register_send_code": true,
        "reset_send_code": false,
        "guest_create_order": false
      },
      "turnstile": {
        "site_key": "0x4AAA..."
      }
    },
    "telegram_auth": {
      "enabled": true,
      "bot_username": "dujiao_auth_bot"
    },
    "google_auth": {
      "enabled": true,
      "client_id": "1234567890-xxxx.apps.googleusercontent.com"
    }
  }
}

返回结构(data) ​

字段类型说明
languagesstring[]站点启用语言列表
currencystring全站币种(3 位大写代码,如 CNY)
contactobject联系方式配置
scriptsobject[]前台自定义 JS 脚本配置
payment_channelsobject[]前台可用支付渠道列表
captchaobject验证码公开配置
telegram_authobjectTelegram 登录公开配置(enabled、bot_username)
google_authobjectGoogle 登录公开配置(enabled、client_id)
其他字段any后台站点设置中的公开字段(动态扩展)

3.2 商品列表 ​

接口:GET /public/products

认证:否

Query 参数 ​

参数类型必填说明
pagenumber否页码
page_sizenumber否每页条数(最大 100)
category_idstring否分类 ID
searchstring否搜索关键词(标题等)

成功响应示例 ​

json
{
  "status_code": 0,
  "msg": "success",
  "data": [
    {
      "category_id": 10,
      "slug": "netflix-plus",
      "title": { "zh-CN": "奈飞会员" },
      "description": { "zh-CN": "全区可用" },
      "content": { "zh-CN": "详情说明" },
      "price_amount": "99.00",
      "images": ["/uploads/product/1.png"],
      "tags": ["热门"],
      "purchase_type": "member",
      "fulfillment_type": "manual",
      "manual_form_schema": { "fields": [] },
      "manual_stock_available": 88,
      "auto_stock_available": 0,
      "stock_status": "in_stock",
      "is_sold_out": false
    }
  ],
  "pagination": {
    "page": 1,
    "page_size": 20,
    "total": 1,
    "total_page": 1
  }
}

返回结构(data) ​

  • data:PublicProduct[]
  • pagination:分页对象(见通用约定)

3.3 商品详情 ​

接口:GET /public/products/:slug

认证:否

Path 参数 ​

参数类型必填说明
slugstring是商品 slug

成功响应示例 ​

json
{
  "status_code": 0,
  "msg": "success",
  "data": {
    "slug": "netflix-plus",
    "title": { "zh-CN": "奈飞会员" },
    "price_amount": "99.00",
    "fulfillment_type": "manual",
    "manual_form_schema": {
      "fields": [
        {
          "key": "receiver_name",
          "type": "text",
          "required": true,
          "label": { "zh-CN": "收件人" }
        }
      ]
    },
    "manual_stock_available": 88,
    "auto_stock_available": 0,
    "stock_status": "in_stock",
    "is_sold_out": false
  }
}

返回结构(data) ​

  • data:PublicProduct

3.4 文章列表 ​

接口:GET /public/posts

认证:否

Query 参数 ​

参数类型必填说明
pagenumber否页码
page_sizenumber否每页条数
typestring否文章类型:blog / notice

成功响应示例 ​

json
{
  "status_code": 0,
  "msg": "success",
  "data": [
    {
      "id": 1,
      "slug": "release-2026-02",
      "type": "notice",
      "title": { "zh-CN": "版本更新" },
      "summary": { "zh-CN": "新增支付渠道" },
      "content": { "zh-CN": "详细内容" },
      "thumbnail": "/uploads/post/1.png",
      "published_at": "2026-02-11T10:00:00Z"
    }
  ],
  "pagination": {
    "page": 1,
    "page_size": 20,
    "total": 1,
    "total_page": 1
  }
}

返回结构(data) ​

  • data:Post[]
  • pagination:分页对象

3.5 文章详情 ​

接口:GET /public/posts/:slug

认证:否

Path 参数 ​

参数类型必填说明
slugstring是文章 slug

成功响应示例 ​

json
{
  "status_code": 0,
  "msg": "success",
  "data": {
    "id": 1,
    "slug": "release-2026-02",
    "type": "notice",
    "title": { "zh-CN": "版本更新" },
    "summary": { "zh-CN": "新增支付渠道" },
    "content": { "zh-CN": "详细内容" },
    "thumbnail": "/uploads/post/1.png",
    "published_at": "2026-02-11T10:00:00Z"
  }
}

返回结构(data) ​

  • data:Post

3.6 Banner 列表 ​

接口:GET /public/banners

认证:否

Query 参数 ​

参数类型必填默认说明
positionstring否home_heroBanner 位置
limitnumber否10最大 50

成功响应示例 ​

json
{
  "status_code": 0,
  "msg": "success",
  "data": [
    {
      "id": 1,
      "position": "home_hero",
      "title": { "zh-CN": "欢迎来到 D&N" },
      "subtitle": { "zh-CN": "稳定交付" },
      "image": "/uploads/banner/hero.png",
      "mobile_image": "/uploads/banner/hero-mobile.png",
      "link_type": "internal",
      "link_value": "/products",
      "open_in_new_tab": false
    }
  ]
}

返回结构(data) ​

  • data:Banner[]

3.7 分类列表 ​

接口:GET /public/categories

认证:否

请求参数 ​

无

成功响应示例 ​

json
{
  "status_code": 0,
  "msg": "success",
  "data": [
    {
      "id": 10,
      "parent_id": 0,
      "slug": "memberships",
      "name": { "zh-CN": "会员服务" },
      "icon": "",
      "sort_order": 100
    }
  ]
}

返回结构(data) ​

  • data:Category[]

3.8 获取图片验证码挑战 ​

接口:GET /public/captcha/image

认证:否

请求参数 ​

无

成功响应示例 ​

json
{
  "status_code": 0,
  "msg": "success",
  "data": {
    "captcha_id": "9f2b2be147df4f6eb6f8",
    "image_base64": "data:image/png;base64,iVBORw0KGgoAAA..."
  }
}

返回结构(data) ​

字段类型说明
captcha_idstring本次验证码 ID
image_base64stringBase64 图片(data URL)

4. 认证接口(无需登录) ​

4.1 发送邮箱验证码 ​

接口:POST /auth/send-verify-code

认证:否

Body 参数 ​

字段类型必填说明
emailstring是邮箱
purposestring是验证码用途:register / reset
captcha_payloadobject否验证码参数(见通用结构)

请求示例 ​

json
{
  "email": "[email protected]",
  "purpose": "register",
  "captcha_payload": {
    "captcha_id": "",
    "captcha_code": "",
    "turnstile_token": ""
  }
}

成功响应示例 ​

json
{
  "status_code": 0,
  "msg": "success",
  "data": {
    "sent": true
  }
}

返回结构(data) ​

字段类型说明
sentboolean是否发送成功

4.2 用户注册 ​

接口:POST /auth/register

认证:否

Body 参数 ​

字段类型必填说明
emailstring是邮箱
passwordstring是密码
codestring是邮箱验证码
agreement_acceptedboolean是是否同意协议,必须为 true

请求示例 ​

json
{
  "email": "[email protected]",
  "password": "StrongPass123",
  "code": "123456",
  "agreement_accepted": true
}

成功响应示例 ​

json
{
  "status_code": 0,
  "msg": "success",
  "data": {
    "user": {
      "id": 101,
      "email": "[email protected]",
      "nickname": "user",
      "email_verified_at": "2026-02-11T10:00:00Z"
    },
    "token": "eyJhbGciOi...",
    "expires_at": "2026-02-18T10:00:00Z"
  }
}

返回结构(data) ​

字段类型说明
userobject注册用户信息(id/email/nickname/email_verified_at)
tokenstring用户 JWT
expires_atstringToken 过期时间(RFC3339)

4.3 用户登录 ​

接口:POST /auth/login

认证:否

Body 参数 ​

字段类型必填说明
emailstring是邮箱
passwordstring是密码
remember_meboolean否是否延长登录态
captcha_payloadobject否验证码参数(见通用结构)

请求示例 ​

json
{
  "email": "[email protected]",
  "password": "StrongPass123",
  "remember_me": true,
  "captcha_payload": {
    "captcha_id": "",
    "captcha_code": "",
    "turnstile_token": ""
  }
}

成功响应示例 ​

json
{
  "status_code": 0,
  "msg": "success",
  "data": {
    "user": {
      "id": 101,
      "email": "[email protected]",
      "nickname": "user",
      "email_verified_at": "2026-02-11T10:00:00Z"
    },
    "token": "eyJhbGciOi...",
    "expires_at": "2026-02-25T10:00:00Z"
  }
}

返回结构(data) ​

与注册接口一致:user + token + expires_at


4.4 忘记密码 ​

接口:POST /auth/forgot-password

认证:否

Body 参数 ​

字段类型必填说明
emailstring是邮箱
codestring是邮箱验证码
new_passwordstring是新密码

请求示例 ​

json
{
  "email": "[email protected]",
  "code": "123456",
  "new_password": "NewStrongPass123"
}

成功响应示例 ​

json
{
  "status_code": 0,
  "msg": "success",
  "data": {
    "reset": true
  }
}

返回结构(data) ​

字段类型说明
resetboolean是否重置成功

4.5 Telegram 登录 ​

接口:POST /auth/telegram/login

认证:否

Body 参数 ​

字段类型必填说明
idnumber是Telegram 用户 ID
first_namestring否名
last_namestring否姓
usernamestring否Telegram 用户名
photo_urlstring否Telegram 头像 URL
auth_datenumber是Telegram 授权时间戳(秒)
hashstring是Telegram 登录签名

请求示例 ​

json
{
  "id": 123456789,
  "first_name": "Dujiao",
  "last_name": "User",
  "username": "dujiao_user",
  "photo_url": "https://t.me/i/userpic/320/xxx.jpg",
  "auth_date": 1739250000,
  "hash": "f1b2c3..."
}

成功响应示例 ​

json
{
  "status_code": 0,
  "msg": "success",
  "data": {
    "user": {
      "id": 101,
      "email": "[email protected]",
      "nickname": "telegram_123456789",
      "email_verified_at": null
    },
    "token": "eyJhbGciOi...",
    "expires_at": "2026-02-25T10:00:00Z"
  }
}

返回结构(data) ​

与注册接口一致:user + token + expires_at

首次 Telegram 登录且未绑定站内账号时,系统会自动创建账号并直接登录。


4.6 Google 登录 ​

接口:POST /auth/google/login

认证:否

Body 参数 ​

字段类型必填说明
credentialstring是Google Identity Services 回调返回的不透明 ID Token;前端不得自行解析后信任其中声明

请求示例 ​

json
{
  "credential": "eyJhbGciOiJSUzI1NiIs..."
}

成功响应示例 ​

无需二次验证时:

json
{
  "status_code": 0,
  "msg": "success",
  "data": {
    "requires_totp": false,
    "user": {
      "id": 101,
      "email": "[email protected]",
      "nickname": "Google User",
      "email_verified_at": "2026-07-30T10:00:00Z"
    },
    "token": "eyJhbGciOi...",
    "expires_at": "2026-07-30T12:00:00Z"
  }
}

账号已启用 TOTP 时:

json
{
  "status_code": 0,
  "msg": "success",
  "data": {
    "requires_totp": true,
    "challenge_token": "eyJhbGciOi...",
    "challenge_expires_at": "2026-07-30T10:05:00Z"
  }
}

收到二次验证挑战后,继续调用 POST /auth/login/verify-2fa。

后端会校验 Google 签名、aud、iss、有效期、sub 和邮箱验证状态。Gmail 地址及带 hd 的 Google Workspace 地址可以安全地按邮箱关联已有账号;其他自定义域同邮箱账号必须先登录站内账号,再通过绑定接口显式确认。首次创建账号仍受注册开关和邮箱域名白名单约束。

iOS/iPadOS redirect 登录 ​

桌面浏览器与 Android 使用上述 popup/FedCM credential 接口。iOS/iPadOS 使用以下 redirect form_post 流程:

  1. 前端调用 POST /auth/google/redirect/intent 创建一次性登录意图。
  2. Google 向同源 POST /auth/google/redirect/callback 提交 credential、state 和 CSRF 字段。
  3. 后端校验并消费一次性 state 后,以 303 重定向到前台固定路径 /auth/google/callback?flow=login(失败时仅追加白名单内的 error 代码)。
  4. 前端调用 POST /auth/google/redirect/exchange,消费 HttpOnly handoff 并取得与 popup 登录相同的登录/2FA 响应。
创建登录意图 ​

接口:POST /auth/google/redirect/intent

认证:否

Body:空 JSON 对象 {}。

json
{
  "status_code": 0,
  "msg": "success",
  "data": {
    "state": "c3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3M",
    "expires_in": 600,
    "issued_at": "2026-07-30T10:00:00Z"
  }
}

前端必须把返回的 state 原样传给 Google Identity Services 按钮配置;同时后端会设置同源、Secure、HttpOnly 的 state Cookie。意图只可使用一次。

Google form_post 回调 ​

接口:POST /auth/google/redirect/callback

认证:否;此接口供 Google Identity Services 直接提交,不应由前端手工调用。

Content-Type:application/x-www-form-urlencoded

字段类型必填说明
credentialstring是Google 返回的 ID Token
statestring是创建意图时取得的一次性 state
g_csrf_tokenstring是必须与 Google 设置的同名 Cookie 一致

回调不返回 JSON。后端完成 Google credential、双提交 CSRF、state、租户与流程校验后,使用 303 See Other 跳转到前台固定回调页。credential、state、handoff、站内 Token 均不会写入重定向 URL。

交换登录结果 ​

接口:POST /auth/google/redirect/exchange

认证:否

Body:空 JSON 对象 {};浏览器必须携带同源 Cookie。

成功响应与 POST /auth/google/login 完全相同,包括 TOTP challenge 分支。handoff 仅可交换一次,有效期为 120 秒。

redirect 登录依赖已启用且可用的 Redis 7,通过 GETDEL 原子消费 state/handoff;Redis 不可用时这些 redirect 接口返回服务不可用。popup/FedCM 的 POST /auth/google/login 不依赖该状态存储。


5. 登录用户资料接口(需 Bearer Token) ​

5.1 获取当前用户 ​

接口:GET /me

认证:是

请求参数 ​

无

成功响应示例 ​

json
{
  "status_code": 0,
  "msg": "success",
  "data": {
    "id": 101,
    "email": "[email protected]",
    "nickname": "user",
    "email_verified_at": "2026-02-11T10:00:00Z",
    "locale": "zh-CN",
    "email_change_mode": "change_with_old_and_new",
    "password_change_mode": "change_with_old"
  }
}

返回结构(data) ​

  • data:UserProfile

5.2 登录日志列表 ​

接口:GET /me/login-logs

认证:是

Query 参数 ​

参数类型必填说明
pagenumber否页码
page_sizenumber否每页条数

成功响应示例 ​

json
{
  "status_code": 0,
  "msg": "success",
  "data": [
    {
      "id": 1,
      "user_id": 101,
      "email": "[email protected]",
      "status": "success",
      "fail_reason": "",
      "client_ip": "127.0.0.1",
      "user_agent": "Mozilla/5.0",
      "login_source": "web",
      "request_id": "01HR...",
      "created_at": "2026-02-11T12:00:00Z"
    }
  ],
  "pagination": {
    "page": 1,
    "page_size": 20,
    "total": 1,
    "total_page": 1
  }
}

返回结构(data) ​

  • data:UserLoginLog[]
  • pagination:分页对象

5.3 更新用户资料 ​

接口:PUT /me/profile

认证:是

Body 参数 ​

字段类型必填说明
nicknamestring否昵称
localestring否语言,例如 zh-CN

nickname 与 locale 至少传一个。

请求示例 ​

json
{
  "nickname": "新昵称",
  "locale": "zh-CN"
}

成功响应示例 ​

json
{
  "status_code": 0,
  "msg": "success",
  "data": {
    "id": 101,
    "email": "[email protected]",
    "nickname": "新昵称",
    "email_verified_at": "2026-02-11T10:00:00Z",
    "locale": "zh-CN",
    "email_change_mode": "change_with_old_and_new",
    "password_change_mode": "change_with_old"
  }
}

返回结构(data) ​

  • data:UserProfile

5.4 获取 Telegram 绑定状态 ​

接口:GET /me/telegram

认证:是

请求参数 ​

无

成功响应示例(已绑定) ​

json
{
  "status_code": 0,
  "msg": "success",
  "data": {
    "bound": true,
    "provider": "telegram",
    "provider_user_id": "123456789",
    "username": "dujiao_user",
    "avatar_url": "https://t.me/i/userpic/320/xxx.jpg",
    "auth_at": "2026-02-20T12:00:00Z",
    "updated_at": "2026-02-20T12:00:00Z"
  }
}

成功响应示例(未绑定) ​

json
{
  "status_code": 0,
  "msg": "success",
  "data": {
    "bound": false
  }
}

返回结构(data) ​

字段类型说明
boundboolean是否已绑定 Telegram
providerstringOAuth 提供方(绑定时为 telegram)
provider_user_idstringTelegram 用户 ID(字符串)
usernamestringTelegram 用户名
avatar_urlstringTelegram 头像 URL
auth_atstringTelegram 授权时间
updated_atstring绑定信息更新时间

当 bound=false 时,仅返回 bound 字段。


5.5 绑定 Telegram ​

接口:POST /me/telegram/bind

认证:是

Body 参数 ​

字段类型必填说明
idnumber是Telegram 用户 ID
first_namestring否名
last_namestring否姓
usernamestring否Telegram 用户名
photo_urlstring否Telegram 头像 URL
auth_datenumber是Telegram 授权时间戳(秒)
hashstring是Telegram 登录签名

请求示例 ​

json
{
  "id": 123456789,
  "first_name": "Dujiao",
  "last_name": "User",
  "username": "dujiao_user",
  "photo_url": "https://t.me/i/userpic/320/xxx.jpg",
  "auth_date": 1739250000,
  "hash": "f1b2c3..."
}

成功响应示例 ​

json
{
  "status_code": 0,
  "msg": "success",
  "data": {
    "bound": true,
    "provider": "telegram",
    "provider_user_id": "123456789",
    "username": "dujiao_user",
    "avatar_url": "https://t.me/i/userpic/320/xxx.jpg",
    "auth_at": "2026-02-20T12:00:00Z",
    "updated_at": "2026-02-20T12:00:00Z"
  }
}

返回结构(data) ​

与 GET /me/telegram(已绑定)一致。


5.6 解绑 Telegram ​

接口:DELETE /me/telegram/unbind

认证:是

请求参数 ​

无

成功响应示例 ​

json
{
  "status_code": 0,
  "msg": "success",
  "data": {
    "unbound": true
  }
}

返回结构(data) ​

字段类型说明
unboundboolean是否解绑成功

仅当账号仍有有效本地密码,或另一个当前已启用且配置完整的第三方登录方式时,才允许解绑 Telegram;否则后端会拒绝操作,避免账号失去全部登录入口。


5.7 发送更换邮箱验证码 ​

接口:POST /me/email/send-verify-code

认证:是

Body 参数 ​

字段类型必填说明
kindstring是old(发到旧邮箱)/ new(发到新邮箱)
new_emailstring条件必填当 kind=new 时必填

当 email_change_mode=bind_only 时,kind=old 不可用,请直接使用 kind=new 绑定真实邮箱。

请求示例 ​

json
{
  "kind": "new",
  "new_email": "[email protected]"
}

成功响应示例 ​

json
{
  "status_code": 0,
  "msg": "success",
  "data": {
    "sent": true
  }
}

返回结构(data) ​

字段类型说明
sentboolean是否发送成功

5.8 更换邮箱 ​

接口:POST /me/email/change

认证:是

Body 参数 ​

字段类型必填说明
new_emailstring是新邮箱
old_codestring条件必填当 email_change_mode=change_with_old_and_new 时必填;当 bind_only 时可不传(服务端忽略)
new_codestring是新邮箱验证码

请求示例 ​

json
{
  "new_email": "[email protected]",
  "old_code": "123456",
  "new_code": "654321"
}

成功响应示例 ​

json
{
  "status_code": 0,
  "msg": "success",
  "data": {
    "id": 101,
    "email": "[email protected]",
    "nickname": "user",
    "email_verified_at": "2026-02-11T10:00:00Z",
    "locale": "zh-CN",
    "email_change_mode": "change_with_old_and_new",
    "password_change_mode": "change_with_old"
  }
}

返回结构(data) ​

  • data:UserProfile

5.9 修改密码 ​

接口:PUT /me/password

认证:是

Body 参数 ​

字段类型必填说明
old_passwordstring条件必填当 password_change_mode=change_with_old 时必填;当 set_without_old 时可不传
new_passwordstring是新密码

首次通过 Telegram 自动创建且未设置密码的账号,password_change_mode=set_without_old,仅需提交 new_password。

请求示例 ​

json
{
  "old_password": "OldPass123",
  "new_password": "NewPass123"
}

成功响应示例 ​

json
{
  "status_code": 0,
  "msg": "success",
  "data": {
    "updated": true
  }
}

返回结构(data) ​

字段类型说明
updatedboolean是否更新成功

5.10 获取 Google 绑定状态 ​

接口:GET /me/google

认证:是

成功响应示例 ​

json
{
  "status_code": 0,
  "msg": "success",
  "data": {
    "bound": true,
    "provider": "google",
    "provider_user_id": "109876543210987654321",
    "username": "[email protected]",
    "email": "[email protected]",
    "display_name": "Google User",
    "avatar_url": "https://lh3.googleusercontent.com/a/example",
    "auth_at": "2026-07-30T10:00:00Z",
    "can_unbind": true
  }
}

can_unbind=false 表示解绑会使账号失去全部可用登录方式,前端必须禁止解绑。


5.11 绑定 Google ​

接口:POST /me/google/bind

认证:是

Body 参数 ​

字段类型必填说明
credentialstring是Google Identity Services 回调返回的不透明 ID Token
json
{
  "credential": "eyJhbGciOiJSUzI1NiIs..."
}

成功响应与 GET /me/google 相同。若该 Google 身份已绑定其他用户,或当前账号已经绑定另一 Google 身份,接口会拒绝操作。

iOS/iPadOS redirect 绑定 ​

redirect 绑定与登录共用 POST /auth/google/redirect/callback,但意图和 exchange 均要求当前用户认证:

  1. 调用 POST /me/google/redirect/intent 创建绑定意图(Body 为 {},响应与登录意图相同)。
  2. 将返回的 state 传给 Google Identity Services,由 Google form_post 到同源 callback。
  3. callback 成功后以 303 跳转到 /auth/google/callback?flow=bind。
  4. 调用 POST /me/google/redirect/exchange(Body 为 {})交换绑定结果,成功响应与 GET /me/google 相同。

创建和交换绑定意图都必须携带 Bearer Token。一次性状态同时绑定当前租户与创建意图的用户;换域名、换租户或换用户均不能完成交换。该流程同样要求 Redis 7 可用。


5.12 解绑 Google ​

接口:DELETE /me/google/unbind

认证:是

仅当账号仍有有效本地密码或其他当前可用的第三方登录方式时允许解绑。

成功响应示例 ​

json
{
  "status_code": 0,
  "msg": "success",
  "data": {
    "unbound": true
  }
}

6. 登录用户订单与支付接口(需 Bearer Token) ​

6.1 订单金额预览 ​

接口:POST /orders/preview

认证:是

Body 参数 ​

字段类型必填说明
itemsOrderItemInput[]是订单项
coupon_codestring否优惠码
manual_form_dataobject否人工交付表单提交值(见通用结构)

请求示例 ​

json
{
  "items": [
    {
      "product_id": 1001,
      "quantity": 1,
      "fulfillment_type": "manual"
    }
  ],
  "coupon_code": "SPRING2026",
  "manual_form_data": {
    "1001": {
      "receiver_name": "张三",
      "phone": "13277745648",
      "address": "广东省深圳市南山区"
    }
  }
}

成功响应示例 ​

json
{
  "status_code": 0,
  "msg": "success",
  "data": {
    "currency": "CNY",
    "original_amount": "99.00",
    "discount_amount": "10.00",
    "promotion_discount_amount": "5.00",
    "total_amount": "84.00",
    "items": [
      {
        "product_id": 1001,
        "title": { "zh-CN": "奈飞会员" },
        "tags": ["热门"],
        "unit_price": "99.00",
        "quantity": 1,
        "total_price": "99.00",
        "coupon_discount_amount": "10.00",
        "promotion_discount_amount": "5.00",
        "fulfillment_type": "manual"
      }
    ]
  }
}

返回结构(data) ​

  • data:OrderPreview

6.2 创建订单 ​

接口:POST /orders

认证:是

Body 参数 ​

与 POST /orders/preview 相同。

请求示例 ​

json
{
  "items": [
    {
      "product_id": 1001,
      "quantity": 1,
      "fulfillment_type": "manual"
    }
  ],
  "manual_form_data": {
    "1001": {
      "receiver_name": "张三",
      "phone": "13277745648",
      "address": "广东省深圳市南山区"
    }
  }
}

成功响应示例 ​

json
{
  "status_code": 0,
  "msg": "success",
  "data": {
    "order_no": "DN202602110001",
    "status": "pending_payment",
    "currency": "CNY",
    "original_amount": "99.00",
    "discount_amount": "0.00",
    "promotion_discount_amount": "0.00",
    "total_amount": "99.00",
    "expires_at": "2026-02-11T12:30:00Z",
    "items": [
      {
        "title": { "zh-CN": "奈飞会员" },
        "quantity": 1,
        "unit_price": "99.00",
        "total_price": "99.00",
        "coupon_discount_amount": "0.00",
        "promotion_discount_amount": "0.00",
        "fulfillment_type": "manual",
        "manual_form_schema_snapshot": {
          "fields": [
            { "key": "receiver_name", "type": "text", "required": true }
          ]
        },
        "manual_form_submission": {
          "receiver_name": "张三"
        }
      }
    ]
  }
}

返回结构(data) ​

  • data:Order

6.3 订单列表 ​

接口:GET /orders

认证:是

Query 参数 ​

参数类型必填说明
pagenumber否页码
page_sizenumber否每页条数
statusstring否状态过滤(见 Order.status 枚举)
order_nostring否订单号模糊查询

成功响应示例 ​

json
{
  "status_code": 0,
  "msg": "success",
  "data": [
    {
      "order_no": "DN202602110001",
      "status": "pending_payment",
      "currency": "CNY",
      "total_amount": "99.00",
      "created_at": "2026-02-11T12:00:00Z",
      "items": [
        {
          "title": { "zh-CN": "奈飞会员" },
          "quantity": 1,
          "unit_price": "99.00",
          "total_price": "99.00",
          "coupon_discount_amount": "0.00",
          "promotion_discount_amount": "0.00",
          "fulfillment_type": "manual",
          "manual_form_schema_snapshot": {},
          "manual_form_submission": {}
        }
      ]
    }
  ],
  "pagination": {
    "page": 1,
    "page_size": 20,
    "total": 1,
    "total_page": 1
  }
}

返回结构(data) ​

  • data:Order[]
  • pagination:分页对象

6.4 订单详情 ​

接口:GET /orders/:order_no

认证:是

Path 参数 ​

参数类型必填说明
order_nostring是订单号

成功响应示例 ​

json
{
  "status_code": 0,
  "msg": "success",
  "data": {
    "order_no": "DN202602110001",
    "status": "pending_payment",
    "currency": "CNY",
    "total_amount": "99.00",
    "items": [
      {
        "title": { "zh-CN": "奈飞会员" },
        "quantity": 1,
        "unit_price": "99.00",
        "total_price": "99.00",
        "coupon_discount_amount": "0.00",
        "promotion_discount_amount": "0.00",
        "fulfillment_type": "manual",
        "manual_form_schema_snapshot": {},
        "manual_form_submission": {}
      }
    ],
    "fulfillment": null,
    "children": []
  }
}

返回结构(data) ​

  • data:Order

6.5 取消订单 ​

接口:POST /orders/:order_no/cancel

认证:是

Path 参数 ​

参数类型必填说明
order_nostring是订单号

Body 参数 ​

无

成功响应示例 ​

json
{
  "status_code": 0,
  "msg": "success",
  "data": {
    "order_no": "DN202602110001",
    "status": "canceled",
    "currency": "CNY",
    "total_amount": "99.00",
    "canceled_at": "2026-02-11T12:10:00Z"
  }
}

返回结构(data) ​

  • data:Order

6.6 创建支付单 ​

接口:POST /payments

认证:是

Body 参数 ​

字段类型必填说明
order_nostring是订单号
channel_idnumber是支付渠道 ID

请求示例 ​

json
{
  "order_no": "DN202602110001",
  "channel_id": 10
}

成功响应示例 ​

json
{
  "status_code": 0,
  "msg": "success",
  "data": {
    "payment_id": 3001,
    "provider_type": "official",
    "channel_type": "alipay",
    "interaction_mode": "page",
    "pay_url": "https://openapi.alipay.com/gateway.do?...",
    "qr_code": "",
    "expires_at": "2026-02-11T12:30:00Z"
  }
}

返回结构(data) ​

  • data:PaymentLaunch(创建支付时通常不含 order_no/channel_id 字段)

6.7 捕获支付结果 ​

接口:POST /payments/:id/capture

认证:是

Path 参数 ​

参数类型必填说明
idnumber是支付记录 ID

Body 参数 ​

无

成功响应示例 ​

json
{
  "status_code": 0,
  "msg": "success",
  "data": {
    "payment_id": 3001,
    "status": "success"
  }
}

返回结构(data) ​

字段类型说明
payment_idnumber支付记录 ID
statusstring支付状态:initiated / pending / success / failed / expired

6.8 获取最新待支付记录 ​

接口:GET /payments/latest

认证:是

Query 参数 ​

参数类型必填说明
order_nostring是订单号

成功响应示例 ​

json
{
  "status_code": 0,
  "msg": "success",
  "data": {
    "payment_id": 3001,
    "order_no": "DN202602110001",
    "channel_id": 10,
    "provider_type": "official",
    "channel_type": "alipay",
    "interaction_mode": "page",
    "pay_url": "https://openapi.alipay.com/gateway.do?...",
    "qr_code": "",
    "expires_at": "2026-02-11T12:30:00Z"
  }
}

返回结构(data) ​

  • data:PaymentLaunch

7. 游客订单与支付接口 ​

游客订单访问凭证为:email + order_password。

7.1 游客订单预览 ​

接口:POST /guest/orders/preview

认证:否

Body 参数 ​

字段类型必填说明
emailstring是游客邮箱
order_passwordstring是查询密码
itemsOrderItemInput[]是订单项
coupon_codestring否优惠码
manual_form_dataobject否人工交付表单提交值
captcha_payloadobject否验证码参数(当前预览不会校验,可忽略)

请求示例 ​

json
{
  "email": "[email protected]",
  "order_password": "guest-pass",
  "items": [
    {
      "product_id": 1001,
      "quantity": 1,
      "fulfillment_type": "manual"
    }
  ],
  "manual_form_data": {
    "1001": {
      "receiver_name": "张三",
      "phone": "13277745648",
      "address": "广东省深圳市南山区"
    }
  }
}

成功响应示例 ​

与 POST /orders/preview 一致。

返回结构(data) ​

  • data:OrderPreview

7.2 游客创建订单 ​

接口:POST /guest/orders

认证:否

Body 参数 ​

与 POST /guest/orders/preview 相同。

请求示例 ​

json
{
  "email": "[email protected]",
  "order_password": "guest-pass",
  "items": [
    {
      "product_id": 1001,
      "quantity": 1,
      "fulfillment_type": "manual"
    }
  ],
  "manual_form_data": {
    "1001": {
      "receiver_name": "张三",
      "phone": "13277745648",
      "address": "广东省深圳市南山区"
    }
  },
  "captcha_payload": {
    "captcha_id": "abc",
    "captcha_code": "x7g5",
    "turnstile_token": ""
  }
}

成功响应示例 ​

与 POST /orders 一致(游客单的 user_id=0,guest_email 有值)。

返回结构(data) ​

  • data:Order

7.3 游客订单列表 ​

接口:GET /guest/orders

认证:否

Query 参数 ​

参数类型必填说明
emailstring是游客邮箱
order_passwordstring是查询密码
order_nostring否订单号,传入时按单号查询并返回 0/1 条
pagenumber否页码
page_sizenumber否每页条数

成功响应示例 ​

json
{
  "status_code": 0,
  "msg": "success",
  "data": [
    {
      "order_no": "DN202602110002",
      "guest_email": "[email protected]",
      "status": "pending_payment",
      "currency": "CNY",
      "total_amount": "99.00",
      "items": [
        {
          "title": { "zh-CN": "奈飞会员" },
          "quantity": 1,
          "unit_price": "99.00",
          "total_price": "99.00",
          "coupon_discount_amount": "0.00",
          "promotion_discount_amount": "0.00",
          "fulfillment_type": "manual",
          "manual_form_schema_snapshot": {},
          "manual_form_submission": {}
        }
      ]
    }
  ],
  "pagination": {
    "page": 1,
    "page_size": 20,
    "total": 1,
    "total_page": 1
  }
}

返回结构(data) ​

  • data:Order[]
  • pagination:分页对象

7.4 游客订单详情 ​

接口:GET /guest/orders/:order_no

认证:否

Path 参数 ​

参数类型必填说明
order_nostring是订单号

Query 参数 ​

参数类型必填说明
emailstring是游客邮箱
order_passwordstring是查询密码

成功响应示例 ​

与用户订单详情结构一致。

返回结构(data) ​

  • data:Order

7.5 游客创建支付单 ​

接口:POST /guest/payments

认证:否

Body 参数 ​

字段类型必填说明
emailstring是游客邮箱
order_passwordstring是查询密码
order_nostring是订单号
channel_idnumber是支付渠道 ID

请求示例 ​

json
{
  "email": "[email protected]",
  "order_password": "guest-pass",
  "order_no": "DN202602110002",
  "channel_id": 10
}

成功响应示例 ​

与 POST /payments 返回结构一致。

返回结构(data) ​

  • data:PaymentLaunch

7.6 游客捕获支付结果 ​

接口:POST /guest/payments/:id/capture

认证:否

Path 参数 ​

参数类型必填说明
idnumber是支付记录 ID

Body 参数 ​

字段类型必填说明
emailstring是游客邮箱
order_passwordstring是查询密码

请求示例 ​

json
{
  "email": "[email protected]",
  "order_password": "guest-pass"
}

成功响应示例 ​

与 POST /payments/:id/capture 一致。

返回结构(data) ​

字段类型说明
payment_idnumber支付记录 ID
statusstring支付状态

7.7 游客获取最新待支付记录 ​

接口:GET /guest/payments/latest

认证:否

Query 参数 ​

参数类型必填说明
emailstring是游客邮箱
order_passwordstring是查询密码
order_nostring是订单号

成功响应示例 ​

与 GET /payments/latest 一致。

返回结构(data) ​

  • data:PaymentLaunch

8. 前台接入建议 ​

8.1 订单接口统一使用 order_no ​

所有面向用户的订单接口均使用 order_no 作为标识符,不再暴露自增 ID:

  • GET /orders/:order_no — 订单详情
  • POST /orders/:order_no/cancel — 取消订单
  • GET /orders/:order_no/fulfillment/download — 下载交付内容
  • GET /guest/orders/:order_no — 游客订单详情
  • GET /guest/orders/:order_no/fulfillment/download — 游客下载交付内容
  • POST /payments、GET /payments/latest — 使用 order_no 参数

8.2 统一错误处理 ​

前端必须同时判断:

  • HTTP 状态(网络层)
  • status_code(业务层)

当 status_code != 0 时,请读取 msg 提示并记录 data.request_id 便于排查。

8.3 支付成功页与轮询 ​

建议支付流程组合使用:

  1. 发起支付后跳转 pay_url 或展示 qr_code
  2. 支付完成回跳后,调用 capture
  3. 再调用 latest 兜底轮询确认

可显著降低“已支付但页面未及时更新”的感知问题。


9. 非前台主动调用接口(说明) ​

9.1 支付平台回调接口 ​

以下回调接口一般由支付平台服务器调用,前台模板无需主动请求:

  • POST /payments/callback
  • GET /payments/callback
  • POST /payments/webhook/paypal
  • POST /payments/webhook/stripe

9.2 管理后台 Telegram 登录配置接口(Admin) ​

以下接口由管理后台调用,不属于前台用户侧接口:

9.2.1 获取 Telegram 登录配置 ​

接口:GET /admin/settings/telegram-auth

认证:管理员 Token

成功响应示例 ​

json
{
  "status_code": 0,
  "msg": "success",
  "data": {
    "enabled": true,
    "bot_username": "dujiao_auth_bot",
    "bot_token": "",
    "has_bot_token": true,
    "login_expire_seconds": 300,
    "replay_ttl_seconds": 300
  }
}

返回中的 bot_token 始终为脱敏空串,是否已配置通过 has_bot_token 判断。

9.2.2 更新 Telegram 登录配置 ​

接口:PUT /admin/settings/telegram-auth

认证:管理员 Token

Body 参数(Patch) ​

字段类型必填说明
enabledboolean否是否启用 Telegram 登录
bot_usernamestring否Telegram Bot 用户名(不带 @)
bot_tokenstring否Telegram Bot Token(传空串不会覆盖已有值)
login_expire_secondsnumber否登录有效期(30-86400 秒)
replay_ttl_secondsnumber否重放保护时长(60-86400 秒)

请求示例 ​

json
{
  "enabled": true,
  "bot_username": "dujiao_auth_bot",
  "bot_token": "123456:ABCDEF",
  "login_expire_seconds": 300,
  "replay_ttl_seconds": 300
}

成功响应 ​

返回结构与 GET /admin/settings/telegram-auth 一致(脱敏)。

9.3 管理后台商品多 SKU 接口(Admin) ​

以下接口由管理后台调用,不属于前台用户侧接口。

9.3.1 创建商品(支持多 SKU) ​

接口:POST /admin/products
认证:管理员 Token

Body 关键参数 ​

字段类型必填说明
category_idnumber是分类 ID
slugstring是商品唯一标识
titleobject是多语言标题
price_amountnumber是单规格模式下商品价格;多 SKU 时建议传 0 或任意占位值,最终以后端 SKU 计算为准
fulfillment_typestring否manual / auto
manual_stock_totalnumber否单规格模式下人工库存总量
skusarray否多 SKU 数组,传空或不传表示单规格模式

skus[] 字段 ​

字段类型必填说明
idnumber否更新已有 SKU 时传;创建新 SKU 可不传
sku_codestring是SKU 编码(同商品内唯一)
spec_valuesobject否规格展示文案(如多语言 {"zh-CN":"标准版","en-US":"Standard"})
price_amountnumber是SKU 价格(必须大于 0)
manual_stock_totalnumber否SKU 人工库存(手动交付模式下生效)
is_activeboolean否是否启用,默认 true
sort_ordernumber否排序权重,默认 0;数值越大越靠前

请求示例(单规格兼容) ​

json
{
  "category_id": 1,
  "slug": "vpn-monthly",
  "title": {
    "zh-CN": "VPN 月付",
    "zh-TW": "VPN 月付",
    "en-US": "VPN Monthly"
  },
  "price_amount": 29.9,
  "fulfillment_type": "manual",
  "manual_stock_total": 100
}

请求示例(多 SKU) ​

json
{
  "category_id": 1,
  "slug": "vpn-subscription",
  "title": {
    "zh-CN": "VPN 订阅",
    "zh-TW": "VPN 訂閱",
    "en-US": "VPN Subscription"
  },
  "price_amount": 0,
  "fulfillment_type": "manual",
  "skus": [
    {
      "sku_code": "STANDARD",
      "spec_values": {
        "zh-CN": "标准版",
        "zh-TW": "標準版",
        "en-US": "Standard"
      },
      "price_amount": 29.9,
      "manual_stock_total": 100,
      "is_active": true,
      "sort_order": 10
    },
    {
      "sku_code": "PRO",
      "spec_values": {
        "zh-CN": "专业版",
        "zh-TW": "專業版",
        "en-US": "Pro"
      },
      "price_amount": 49.9,
      "manual_stock_total": 80,
      "is_active": true,
      "sort_order": 20
    }
  ]
}

9.3.2 更新商品(支持多 SKU) ​

接口:PUT /admin/products/:id
认证:管理员 Token

请求结构与 POST /admin/products 一致;若要更新已有 SKU,请在 skus[] 中传对应 id。

9.3.3 后端处理规则 ​

  • 当 skus 非空时:
    • 商品展示价自动取“启用 SKU 中的最低价”;
    • 若为人工交付,商品人工库存总量自动汇总“启用 SKU 的 manual_stock_total”。
  • 当 skus 为空时:
    • 按历史单规格模式处理,价格与库存使用商品本身字段。

9.3.4 管理后台操作指引 ​

  1. 进入后台 商品管理,新建或编辑商品。
  2. 在“SKU 规格配置”区域新增一个或多个 SKU,填写编码、规格文案、价格、库存、状态与排序。
  3. 若已配置 SKU,商品“价格/人工库存总量”字段仅作展示参考,实际以 SKU 数据为准。
  4. 保存后可在前台商品详情页按 SKU 展示与下单。

9.4 推广返利接口(Affiliate) ​

以下接口对应前台返利中心与后台返利审核能力。

9.4.1 下单接口新增 affiliate_code 字段 ​

以下接口的请求体支持附带 affiliate_code(联盟ID):

  • POST /orders/preview
  • POST /orders
  • POST /guest/orders/preview
  • POST /guest/orders

字段定义:

字段类型必填说明
affiliate_codestring否推广联盟ID(如 AB12CD34),用于订单返利归因

9.4.2 公开点击上报 ​

接口:POST /public/affiliate/click
认证:无需登录

字段类型必填说明
affiliate_codestring是联盟ID
visitor_keystring否访客标识(前端可持久化)
landing_pathstring否落地路径(如 /?aff=AB12CD34)
referrerstring否来源页 URL

成功返回:

json
{
  "status_code": 0,
  "msg": "success",
  "data": {
    "ok": true
  }
}

9.4.3 用户返利中心接口(需 Bearer Token) ​

A) 开通返利 ​
  • 接口:POST /affiliate/open
  • 说明:开通成功后返回返利档案(含联盟ID)。
B) 获取返利看板 ​
  • 接口:GET /affiliate/dashboard

返回 data 关键字段:

字段类型说明
openedboolean是否已开通
affiliate_codestring联盟ID
promotion_pathstring推广路径(如 /?aff=AB12CD34)
click_countnumber点击数
valid_order_countnumber有效订单数
conversion_ratenumber转化率(百分比数值)
pending_commissionstring待确认佣金
available_commissionstring可提现佣金
withdrawn_commissionstring已提现佣金
C) 查询我的佣金记录 ​
  • 接口:GET /affiliate/commissions
  • 参数:page、page_size、status
  • status 可选值:pending_confirm / available / rejected / withdrawn
D) 查询我的提现记录 ​
  • 接口:GET /affiliate/withdraws
  • 参数:page、page_size、status
  • status 可选值:pending_review / rejected / paid
E) 申请提现 ​
  • 接口:POST /affiliate/withdraws
字段类型必填说明
amountstring是提现金额(字符串金额,保留 2 位小数)
channelstring是提现渠道
accountstring是提现账号

9.4.4 管理后台返利设置(Admin) ​

A) 获取返利设置 ​
  • 接口:GET /admin/settings/affiliate
  • 认证:管理员 Token
B) 更新返利设置 ​
  • 接口:PUT /admin/settings/affiliate
  • 认证:管理员 Token
字段类型必填说明
enabledboolean是是否开启返利
commission_ratenumber是返利比例(0-100,支持 2 位小数)
confirm_daysnumber是佣金确认天数(0-3650)
min_withdraw_amountnumber是最低提现金额(>=0)
withdraw_channelsstring[]是提现渠道列表

9.4.5 管理后台返利管理(Admin) ​

以下接口均需管理员 Token:

接口说明
GET /admin/affiliates/users返利用户列表
GET /admin/affiliates/commissions佣金记录列表
GET /admin/affiliates/withdraws提现申请列表
POST /admin/affiliates/withdraws/:id/reject拒绝提现申请
POST /admin/affiliates/withdraws/:id/pay标记提现已打款

其中:

  • POST /admin/affiliates/withdraws/:id/reject body 支持 { "reason": "拒绝原因" }
  • POST /admin/affiliates/withdraws/:id/pay 无需额外 body 字段

9.5 管理后台 Google 登录配置接口(Admin) ​

以下接口由管理后台调用,需要 system_admin 权限。

9.5.1 获取 Google 登录配置 ​

接口:GET /admin/settings/google-auth

认证:管理员 Token

json
{
  "status_code": 0,
  "msg": "success",
  "data": {
    "enabled": true,
    "client_id": "1234567890-example.apps.googleusercontent.com"
  }
}

9.5.2 更新 Google 登录配置 ​

接口:PUT /admin/settings/google-auth

认证:管理员 Token

Body 参数(Patch) ​

字段类型必填说明
enabledboolean否是否启用 Google 登录
client_idstring否Google OAuth 2.0 Web Client ID;启用时不能为空
json
{
  "enabled": true,
  "client_id": "1234567890-example.apps.googleusercontent.com"
}

保存后配置立即生效。Client ID 是浏览器端公开标识,不需要 Client Secret;本功能也不请求 Gmail 邮箱权限。

Released under the GPLv3 License.