
战略品牌合作方
VMRACK.NET | 让Dujiao-Next部署更简单
⚡️官方合作闪购款,仅需76刀/年,三网优化线路,助力您业务起飞,支持:支付宝/paypal/USDT。点我享低价独家闪购!🎉







更新时间:2026-03-31
本文档覆盖 user/src/api/index.ts 当前全部前台 API,字段定义以以下实现为准:
api/internal/router/router.goapi/internal/http/handlers/public/*.goapi/internal/models/*.goid 字段(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 响应中永久移除,前端不应再依赖:
id、parent_id、user_id、coupon_id、promotion_id、client_ip、updated_atid、order_id、delivered_by、created_at、updated_atid、order_id、delivered_by、created_at、updated_atcost_price_amount、manual_stock_locked、manual_stock_sold、is_active、sort_order、created_at、updated_at、is_affiliate_enabled、is_mapped、seo_metacost_price_amount、product_id、manual_stock_locked、auto_stock_total、auto_stock_locked、auto_stock_sold、sort_order、created_at、updated_atname、is_active、start_at、end_at、sort_order、created_at、updated_atis_published、created_atcreated_atorder_idorder_idmember_discount_amount、wallet_paid_amount、online_paid_amount、refunded_amountsku_snapshot、member_discount_amountpayload_line_countmember_level_id、total_recharged、total_spentparent_id、iconPublicProduct 新增 promotion_rules 字段(类型 PromotionRule[]),返回该商品所有生效中的活动规则。min_amount 门槛),形成阶梯折扣。单价 × 数量)从高到低匹配门槛,取满足条件的最高档规则计算折扣。min_amount=50,优惠 1%min_amount=150,优惠 2%| type | 含义 | 计算方式 |
|---|---|---|
percent | 百分比折扣 | 每件单价 = 原价 × (100 - value) / 100 |
fixed | 固定金额减免 | 每件单价 = 原价 - value |
special_price | 直降特价 | 每件单价 = value |
注意: 所有折扣均作用于单件商品价格,而非订单总金额。
min_amount是购买小计门槛(单价 × 数量),达到门槛后每件商品享受对应折扣。
/api/v1/api/v1,调用时请自行拼接。用户登录态接口需携带:
Authorization: Bearer <user_token>{
"status_code": 0,
"msg": "success",
"data": {},
"pagination": {
"page": 1,
"page_size": 20,
"total": 100,
"total_page": 5
}
}{
"status_code": 400,
"msg": "请求参数错误",
"data": {
"request_id": "01HR..."
}
}| 字段 | 类型 | 说明 |
|---|---|---|
| status_code | number | 业务状态码,0 表示成功,非 0 表示失败 |
| msg | string | 业务提示信息 |
| data | object/array/null | 业务数据 |
| pagination | object | 分页信息,仅分页接口返回 |
| 参数 | 类型 | 必填 | 默认 | 说明 |
|---|---|---|---|---|
| page | number | 否 | 1 | 页码,最小 1 |
| page_size | number | 否 | 20 | 每页条数,最大 100 |
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| captcha_id | string | 否 | 图片验证码 ID(provider=image 时使用) |
| captcha_code | string | 否 | 图片验证码文本(provider=image 时使用) |
| turnstile_token | string | 否 | Turnstile Token(provider=turnstile 时使用) |
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| product_id | number | 是 | 商品 ID |
| quantity | number | 是 | 购买数量(>0) |
| fulfillment_type | string | 否 | 交付类型,推荐值:manual / auto |
manual_form_data 是对象,Key 为 product_id,Value 为该商品的表单提交数据。
{
"1001": {
"receiver_name": "张三",
"phone": "13277745648",
"address": "广东省深圳市..."
}
}以下对象用于后续各接口“返回结构”引用。
| 字段 | 类型 | 说明 |
|---|---|---|
| category_id | number | 分类 ID |
| slug | string | 商品唯一标识 |
| title | object | 多语言标题 |
| description | object | 多语言摘要 |
| content | object | 多语言详情内容 |
| price_amount | string | 商品价格金额 |
| images | string[] | 商品图片列表 |
| tags | string[] | 标签列表 |
| purchase_type | string | 购买身份限制:guest / member |
| min_purchase_quantity | number | 单次最小购买数量(0 表示不限) |
| max_purchase_quantity | number | 单次最大购买数量(0 表示不限) |
| fulfillment_type | string | 交付类型:manual / auto |
| manual_form_schema | object | 人工交付表单 Schema |
| manual_stock_available | number | 人工可用库存 |
| auto_stock_available | number | 自动可用库存 |
| stock_status | string | 库存状态:unlimited / in_stock / low_stock / out_of_stock |
| is_sold_out | boolean | 是否售罄 |
| category | Category | 分类信息 |
| skus | PublicSKU[] | SKU 列表 |
| promotion_id | number | 命中的活动 ID(可选) |
| promotion_name | string | 活动名称(可选) |
| promotion_type | string | 活动类型(可选) |
| promotion_price_amount | string | 活动价金额(可选) |
| promotion_rules | PromotionRule[] | 活动规则列表(可选) |
| member_prices | MemberLevelPrice[] | 会员等级价格列表(可选) |
skus[] 数组中每个元素的结构如下:
| 字段 | 类型 | 说明 |
|---|---|---|
| id | number | SKU ID(下单时使用此 ID) |
| sku_code | string | SKU 编码(同商品内唯一) |
| spec_values | object | 规格值(多语言) |
| price_amount | string | SKU 原价 |
| manual_stock_total | number | 人工库存总量(-1 表示无限库存) |
| manual_stock_sold | number | 人工库存已售量 |
| auto_stock_available | number | 自动发货库存可用量 |
| upstream_stock | number | 上游库存(-1 表示无限,0 表示售罄) |
| is_active | boolean | 是否启用 |
| promotion_price_amount | string | SKU 活动价金额(可选) |
| member_price_amount | string | 会员价金额(可选) |
promotion_rules[] 数组中每个元素的结构如下:
| 字段 | 类型 | 说明 |
|---|---|---|
| id | number | 活动规则 ID |
| name | string | 活动名称 |
| type | string | 活动类型:percent / fixed / special_price |
| value | string | 活动数值(字符串金额/百分比,如 "2.00" 或 "5.00") |
| min_amount | string | 触发门槛金额(购买小计 = 单价 × 数量,如 "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升序),即使当前单价未满足门槛也会返回,便于前端展示活动提示。
| 字段 | 类型 | 说明 |
|---|---|---|
| id | number | 文章 ID |
| slug | string | 文章唯一标识 |
| type | string | 类型:blog / notice |
| title | object | 多语言标题 |
| summary | object | 多语言摘要 |
| content | object | 多语言内容 |
| thumbnail | string | 缩略图 URL |
| published_at | string/null | 发布时间 |
| 字段 | 类型 | 说明 |
|---|---|---|
| id | number | Banner ID |
| position | string | 投放位置(如 home_hero) |
| title | object | 多语言标题 |
| subtitle | object | 多语言副标题 |
| image | string | 主图 |
| mobile_image | string | 移动端图 |
| link_type | string | 跳转类型:none / internal / external |
| link_value | string | 跳转值 |
| open_in_new_tab | boolean | 是否新窗口打开 |
| 字段 | 类型 | 说明 |
|---|---|---|
| id | number | 分类 ID |
| parent_id | number | 父分类 ID(0 表示一级分类) |
| slug | string | 分类唯一标识 |
| name | object | 多语言名称 |
| icon | string | 分类图标 |
| sort_order | number | 排序 |
| 字段 | 类型 | 说明 |
|---|---|---|
| id | number | 用户 ID |
| string | 邮箱 | |
| nickname | string | 昵称 |
| email_verified_at | string/null | 邮箱验证时间 |
| locale | string | 语言(如 zh-CN) |
| member_level_id | number | 会员等级 ID |
| total_recharged | string | 累计充值金额 |
| total_spent | string | 累计消费金额 |
| email_change_mode | string | 邮箱变更模式:bind_only / change_with_old_and_new |
| password_change_mode | string | 密码变更模式:set_without_old / change_with_old |
| 字段 | 类型 | 说明 |
|---|---|---|
| id | number | 日志 ID |
| user_id | number | 用户 ID(失败时可能为 0) |
| string | 登录邮箱 | |
| status | string | 登录结果:success / failed |
| fail_reason | string | 失败原因枚举 |
| client_ip | string | 客户端 IP |
| user_agent | string | 客户端 UA |
| login_source | string | 登录来源:web / telegram |
| request_id | string | 请求追踪 ID |
| created_at | string | 记录创建时间 |
| 字段 | 类型 | 说明 |
|---|---|---|
| currency | string | 币种(全站统一,来源 site_config.currency) |
| original_amount | string | 原价总额 |
| discount_amount | string | 总优惠金额 |
| promotion_discount_amount | string | 活动优惠金额 |
| total_amount | string | 应付总额 |
| items | OrderPreviewItem[] | 预览订单项 |
| 字段 | 类型 | 说明 |
|---|---|---|
| product_id | number | 商品 ID |
| title | object | 商品标题快照(多语言) |
| tags | string[] | 商品标签快照 |
| unit_price | string | 单价 |
| quantity | number | 数量 |
| total_price | string | 小计 |
| coupon_discount_amount | string | 优惠券分摊金额 |
| promotion_discount_amount | string | 活动分摊金额 |
| fulfillment_type | string | 交付类型 |
| 字段 | 类型 | 说明 |
|---|---|---|
| order_no | string | 订单号 |
| guest_email | string | 游客邮箱(游客订单) |
| guest_locale | string | 游客语言 |
| status | string | 订单状态:pending_payment / paid / fulfilling / partially_delivered / delivered / completed / canceled |
| currency | string | 订单币种 |
| original_amount | string | 原价 |
| discount_amount | string | 优惠金额 |
| member_discount_amount | string | 会员优惠金额 |
| promotion_discount_amount | string | 活动优惠金额 |
| total_amount | string | 实付金额 |
| wallet_paid_amount | string | 钱包支付金额 |
| online_paid_amount | string | 在线支付金额 |
| refunded_amount | string | 已退款金额 |
| expires_at | string/null | 待支付过期时间 |
| paid_at | string/null | 支付成功时间 |
| canceled_at | string/null | 取消时间 |
| created_at | string | 创建时间 |
| items | OrderItem[] | 订单项 |
| fulfillment | Fulfillment | 交付记录(可选) |
| children | Order[] | 子订单列表(可选) |
| 字段 | 类型 | 说明 |
|---|---|---|
| title | object | 商品标题快照 |
| sku_snapshot | object | SKU 快照(编码/规格) |
| tags | string[] | 商品标签快照 |
| unit_price | string | 单价 |
| quantity | number | 数量 |
| total_price | string | 小计 |
| coupon_discount_amount | string | 优惠券分摊金额 |
| member_discount_amount | string | 会员优惠分摊金额 |
| promotion_discount_amount | string | 活动优惠金额 |
| fulfillment_type | string | 交付类型 |
| manual_form_schema_snapshot | object | 人工交付表单 Schema 快照 |
| manual_form_submission | object | 用户提交的人工表单值 |
| 字段 | 类型 | 说明 |
|---|---|---|
| type | string | 交付类型:auto / manual |
| status | string | 交付状态:pending / delivered |
| payload | string | 文本交付内容 |
| payload_line_count | number | 交付内容总行数 |
| delivery_data | object | 结构化交付信息 |
| delivered_at | string/null | 交付时间 |
| 字段 | 类型 | 说明 |
|---|---|---|
| payment_id | number | 支付记录 ID |
| order_no | string | 订单号(latest 接口返回) |
| channel_id | number | 支付渠道 ID(latest 接口返回) |
| provider_type | string | 提供方:official / epay |
| channel_type | string | 渠道:alipay / wechat / paypal / stripe 等 |
| interaction_mode | string | 交互方式:qr / redirect / wap / page |
| pay_url | string | 跳转支付链接 |
| qr_code | string | 二维码内容 |
| expires_at | string/null | 支付单过期时间 |
接口:GET /public/config
认证:否
无
{
"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"
}
}
}| 字段 | 类型 | 说明 |
|---|---|---|
| languages | string[] | 站点启用语言列表 |
| currency | string | 全站币种(3 位大写代码,如 CNY) |
| contact | object | 联系方式配置 |
| scripts | object[] | 前台自定义 JS 脚本配置 |
| payment_channels | object[] | 前台可用支付渠道列表 |
| captcha | object | 验证码公开配置 |
| telegram_auth | object | Telegram 登录公开配置(enabled、bot_username) |
| google_auth | object | Google 登录公开配置(enabled、client_id) |
| 其他字段 | any | 后台站点设置中的公开字段(动态扩展) |
接口:GET /public/products
认证:否
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| page | number | 否 | 页码 |
| page_size | number | 否 | 每页条数(最大 100) |
| category_id | string | 否 | 分类 ID |
| search | string | 否 | 搜索关键词(标题等) |
{
"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:PublicProduct[]pagination:分页对象(见通用约定)接口:GET /public/products/:slug
认证:否
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| slug | string | 是 | 商品 slug |
{
"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:PublicProduct接口:GET /public/posts
认证:否
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| page | number | 否 | 页码 |
| page_size | number | 否 | 每页条数 |
| type | string | 否 | 文章类型:blog / notice |
{
"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:Post[]pagination:分页对象接口:GET /public/posts/:slug
认证:否
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| slug | string | 是 | 文章 slug |
{
"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:Post接口:GET /public/banners
认证:否
| 参数 | 类型 | 必填 | 默认 | 说明 |
|---|---|---|---|---|
| position | string | 否 | home_hero | Banner 位置 |
| limit | number | 否 | 10 | 最大 50 |
{
"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:Banner[]接口:GET /public/categories
认证:否
无
{
"status_code": 0,
"msg": "success",
"data": [
{
"id": 10,
"parent_id": 0,
"slug": "memberships",
"name": { "zh-CN": "会员服务" },
"icon": "",
"sort_order": 100
}
]
}data:Category[]接口:GET /public/captcha/image
认证:否
无
{
"status_code": 0,
"msg": "success",
"data": {
"captcha_id": "9f2b2be147df4f6eb6f8",
"image_base64": "data:image/png;base64,iVBORw0KGgoAAA..."
}
}| 字段 | 类型 | 说明 |
|---|---|---|
| captcha_id | string | 本次验证码 ID |
| image_base64 | string | Base64 图片(data URL) |
接口:POST /auth/send-verify-code
认证:否
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| string | 是 | 邮箱 | |
| purpose | string | 是 | 验证码用途:register / reset |
| captcha_payload | object | 否 | 验证码参数(见通用结构) |
{
"email": "[email protected]",
"purpose": "register",
"captcha_payload": {
"captcha_id": "",
"captcha_code": "",
"turnstile_token": ""
}
}{
"status_code": 0,
"msg": "success",
"data": {
"sent": true
}
}| 字段 | 类型 | 说明 |
|---|---|---|
| sent | boolean | 是否发送成功 |
接口:POST /auth/register
认证:否
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| string | 是 | 邮箱 | |
| password | string | 是 | 密码 |
| code | string | 是 | 邮箱验证码 |
| agreement_accepted | boolean | 是 | 是否同意协议,必须为 true |
{
"email": "[email protected]",
"password": "StrongPass123",
"code": "123456",
"agreement_accepted": true
}{
"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"
}
}| 字段 | 类型 | 说明 |
|---|---|---|
| user | object | 注册用户信息(id/email/nickname/email_verified_at) |
| token | string | 用户 JWT |
| expires_at | string | Token 过期时间(RFC3339) |
接口:POST /auth/login
认证:否
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| string | 是 | 邮箱 | |
| password | string | 是 | 密码 |
| remember_me | boolean | 否 | 是否延长登录态 |
| captcha_payload | object | 否 | 验证码参数(见通用结构) |
{
"email": "[email protected]",
"password": "StrongPass123",
"remember_me": true,
"captcha_payload": {
"captcha_id": "",
"captcha_code": "",
"turnstile_token": ""
}
}{
"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"
}
}与注册接口一致:user + token + expires_at
接口:POST /auth/forgot-password
认证:否
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| string | 是 | 邮箱 | |
| code | string | 是 | 邮箱验证码 |
| new_password | string | 是 | 新密码 |
{
"email": "[email protected]",
"code": "123456",
"new_password": "NewStrongPass123"
}{
"status_code": 0,
"msg": "success",
"data": {
"reset": true
}
}| 字段 | 类型 | 说明 |
|---|---|---|
| reset | boolean | 是否重置成功 |
接口:POST /auth/telegram/login
认证:否
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| id | number | 是 | Telegram 用户 ID |
| first_name | string | 否 | 名 |
| last_name | string | 否 | 姓 |
| username | string | 否 | Telegram 用户名 |
| photo_url | string | 否 | Telegram 头像 URL |
| auth_date | number | 是 | Telegram 授权时间戳(秒) |
| hash | string | 是 | Telegram 登录签名 |
{
"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..."
}{
"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"
}
}与注册接口一致:user + token + expires_at
首次 Telegram 登录且未绑定站内账号时,系统会自动创建账号并直接登录。
接口:POST /auth/google/login
认证:否
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| credential | string | 是 | Google Identity Services 回调返回的不透明 ID Token;前端不得自行解析后信任其中声明 |
{
"credential": "eyJhbGciOiJSUzI1NiIs..."
}无需二次验证时:
{
"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 时:
{
"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 地址可以安全地按邮箱关联已有账号;其他自定义域同邮箱账号必须先登录站内账号,再通过绑定接口显式确认。首次创建账号仍受注册开关和邮箱域名白名单约束。
桌面浏览器与 Android 使用上述 popup/FedCM credential 接口。iOS/iPadOS 使用以下 redirect form_post 流程:
POST /auth/google/redirect/intent 创建一次性登录意图。POST /auth/google/redirect/callback 提交 credential、state 和 CSRF 字段。303 重定向到前台固定路径 /auth/google/callback?flow=login(失败时仅追加白名单内的 error 代码)。POST /auth/google/redirect/exchange,消费 HttpOnly handoff 并取得与 popup 登录相同的登录/2FA 响应。接口:POST /auth/google/redirect/intent
认证:否
Body:空 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。意图只可使用一次。
接口:POST /auth/google/redirect/callback
认证:否;此接口供 Google Identity Services 直接提交,不应由前端手工调用。
Content-Type:application/x-www-form-urlencoded
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| credential | string | 是 | Google 返回的 ID Token |
| state | string | 是 | 创建意图时取得的一次性 state |
| g_csrf_token | string | 是 | 必须与 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不依赖该状态存储。
接口:GET /me
认证:是
无
{
"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:UserProfile接口:GET /me/login-logs
认证:是
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| page | number | 否 | 页码 |
| page_size | number | 否 | 每页条数 |
{
"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:UserLoginLog[]pagination:分页对象接口:PUT /me/profile
认证:是
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| nickname | string | 否 | 昵称 |
| locale | string | 否 | 语言,例如 zh-CN |
nickname与locale至少传一个。
{
"nickname": "新昵称",
"locale": "zh-CN"
}{
"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:UserProfile接口:GET /me/telegram
认证:是
无
{
"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"
}
}{
"status_code": 0,
"msg": "success",
"data": {
"bound": false
}
}| 字段 | 类型 | 说明 |
|---|---|---|
| bound | boolean | 是否已绑定 Telegram |
| provider | string | OAuth 提供方(绑定时为 telegram) |
| provider_user_id | string | Telegram 用户 ID(字符串) |
| username | string | Telegram 用户名 |
| avatar_url | string | Telegram 头像 URL |
| auth_at | string | Telegram 授权时间 |
| updated_at | string | 绑定信息更新时间 |
当
bound=false时,仅返回bound字段。
接口:POST /me/telegram/bind
认证:是
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| id | number | 是 | Telegram 用户 ID |
| first_name | string | 否 | 名 |
| last_name | string | 否 | 姓 |
| username | string | 否 | Telegram 用户名 |
| photo_url | string | 否 | Telegram 头像 URL |
| auth_date | number | 是 | Telegram 授权时间戳(秒) |
| hash | string | 是 | Telegram 登录签名 |
{
"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..."
}{
"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"
}
}与 GET /me/telegram(已绑定)一致。
接口:DELETE /me/telegram/unbind
认证:是
无
{
"status_code": 0,
"msg": "success",
"data": {
"unbound": true
}
}| 字段 | 类型 | 说明 |
|---|---|---|
| unbound | boolean | 是否解绑成功 |
仅当账号仍有有效本地密码,或另一个当前已启用且配置完整的第三方登录方式时,才允许解绑 Telegram;否则后端会拒绝操作,避免账号失去全部登录入口。
接口:POST /me/email/send-verify-code
认证:是
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| kind | string | 是 | old(发到旧邮箱)/ new(发到新邮箱) |
| new_email | string | 条件必填 | 当 kind=new 时必填 |
当
email_change_mode=bind_only时,kind=old不可用,请直接使用kind=new绑定真实邮箱。
{
"kind": "new",
"new_email": "[email protected]"
}{
"status_code": 0,
"msg": "success",
"data": {
"sent": true
}
}| 字段 | 类型 | 说明 |
|---|---|---|
| sent | boolean | 是否发送成功 |
接口:POST /me/email/change
认证:是
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| new_email | string | 是 | 新邮箱 |
| old_code | string | 条件必填 | 当 email_change_mode=change_with_old_and_new 时必填;当 bind_only 时可不传(服务端忽略) |
| new_code | string | 是 | 新邮箱验证码 |
{
"new_email": "[email protected]",
"old_code": "123456",
"new_code": "654321"
}{
"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:UserProfile接口:PUT /me/password
认证:是
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| old_password | string | 条件必填 | 当 password_change_mode=change_with_old 时必填;当 set_without_old 时可不传 |
| new_password | string | 是 | 新密码 |
首次通过 Telegram 自动创建且未设置密码的账号,
password_change_mode=set_without_old,仅需提交new_password。
{
"old_password": "OldPass123",
"new_password": "NewPass123"
}{
"status_code": 0,
"msg": "success",
"data": {
"updated": true
}
}| 字段 | 类型 | 说明 |
|---|---|---|
| updated | boolean | 是否更新成功 |
接口:GET /me/google
认证:是
{
"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 表示解绑会使账号失去全部可用登录方式,前端必须禁止解绑。
接口:POST /me/google/bind
认证:是
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| credential | string | 是 | Google Identity Services 回调返回的不透明 ID Token |
{
"credential": "eyJhbGciOiJSUzI1NiIs..."
}成功响应与 GET /me/google 相同。若该 Google 身份已绑定其他用户,或当前账号已经绑定另一 Google 身份,接口会拒绝操作。
redirect 绑定与登录共用 POST /auth/google/redirect/callback,但意图和 exchange 均要求当前用户认证:
POST /me/google/redirect/intent 创建绑定意图(Body 为 {},响应与登录意图相同)。state 传给 Google Identity Services,由 Google form_post 到同源 callback。303 跳转到 /auth/google/callback?flow=bind。POST /me/google/redirect/exchange(Body 为 {})交换绑定结果,成功响应与 GET /me/google 相同。创建和交换绑定意图都必须携带 Bearer Token。一次性状态同时绑定当前租户与创建意图的用户;换域名、换租户或换用户均不能完成交换。该流程同样要求 Redis 7 可用。
接口:DELETE /me/google/unbind
认证:是
仅当账号仍有有效本地密码或其他当前可用的第三方登录方式时允许解绑。
{
"status_code": 0,
"msg": "success",
"data": {
"unbound": true
}
}接口:POST /orders/preview
认证:是
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| items | OrderItemInput[] | 是 | 订单项 |
| coupon_code | string | 否 | 优惠码 |
| manual_form_data | object | 否 | 人工交付表单提交值(见通用结构) |
{
"items": [
{
"product_id": 1001,
"quantity": 1,
"fulfillment_type": "manual"
}
],
"coupon_code": "SPRING2026",
"manual_form_data": {
"1001": {
"receiver_name": "张三",
"phone": "13277745648",
"address": "广东省深圳市南山区"
}
}
}{
"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:OrderPreview接口:POST /orders
认证:是
与 POST /orders/preview 相同。
{
"items": [
{
"product_id": 1001,
"quantity": 1,
"fulfillment_type": "manual"
}
],
"manual_form_data": {
"1001": {
"receiver_name": "张三",
"phone": "13277745648",
"address": "广东省深圳市南山区"
}
}
}{
"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:Order接口:GET /orders
认证:是
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| page | number | 否 | 页码 |
| page_size | number | 否 | 每页条数 |
| status | string | 否 | 状态过滤(见 Order.status 枚举) |
| order_no | string | 否 | 订单号模糊查询 |
{
"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:Order[]pagination:分页对象接口:GET /orders/:order_no
认证:是
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| order_no | string | 是 | 订单号 |
{
"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:Order接口:POST /orders/:order_no/cancel
认证:是
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| order_no | string | 是 | 订单号 |
无
{
"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:Order接口:POST /payments
认证:是
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| order_no | string | 是 | 订单号 |
| channel_id | number | 是 | 支付渠道 ID |
{
"order_no": "DN202602110001",
"channel_id": 10
}{
"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:PaymentLaunch(创建支付时通常不含 order_no/channel_id 字段)接口:POST /payments/:id/capture
认证:是
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| id | number | 是 | 支付记录 ID |
无
{
"status_code": 0,
"msg": "success",
"data": {
"payment_id": 3001,
"status": "success"
}
}| 字段 | 类型 | 说明 |
|---|---|---|
| payment_id | number | 支付记录 ID |
| status | string | 支付状态:initiated / pending / success / failed / expired |
接口:GET /payments/latest
认证:是
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| order_no | string | 是 | 订单号 |
{
"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:PaymentLaunch游客订单访问凭证为:
email + order_password。
接口:POST /guest/orders/preview
认证:否
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| string | 是 | 游客邮箱 | |
| order_password | string | 是 | 查询密码 |
| items | OrderItemInput[] | 是 | 订单项 |
| coupon_code | string | 否 | 优惠码 |
| manual_form_data | object | 否 | 人工交付表单提交值 |
| captcha_payload | object | 否 | 验证码参数(当前预览不会校验,可忽略) |
{
"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:OrderPreview接口:POST /guest/orders
认证:否
与 POST /guest/orders/preview 相同。
{
"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:Order接口:GET /guest/orders
认证:否
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| string | 是 | 游客邮箱 | |
| order_password | string | 是 | 查询密码 |
| order_no | string | 否 | 订单号,传入时按单号查询并返回 0/1 条 |
| page | number | 否 | 页码 |
| page_size | number | 否 | 每页条数 |
{
"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:Order[]pagination:分页对象接口:GET /guest/orders/:order_no
认证:否
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| order_no | string | 是 | 订单号 |
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| string | 是 | 游客邮箱 | |
| order_password | string | 是 | 查询密码 |
与用户订单详情结构一致。
data:Order接口:POST /guest/payments
认证:否
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| string | 是 | 游客邮箱 | |
| order_password | string | 是 | 查询密码 |
| order_no | string | 是 | 订单号 |
| channel_id | number | 是 | 支付渠道 ID |
{
"email": "[email protected]",
"order_password": "guest-pass",
"order_no": "DN202602110002",
"channel_id": 10
}与 POST /payments 返回结构一致。
data:PaymentLaunch接口:POST /guest/payments/:id/capture
认证:否
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| id | number | 是 | 支付记录 ID |
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| string | 是 | 游客邮箱 | |
| order_password | string | 是 | 查询密码 |
{
"email": "[email protected]",
"order_password": "guest-pass"
}与 POST /payments/:id/capture 一致。
| 字段 | 类型 | 说明 |
|---|---|---|
| payment_id | number | 支付记录 ID |
| status | string | 支付状态 |
接口:GET /guest/payments/latest
认证:否
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| string | 是 | 游客邮箱 | |
| order_password | string | 是 | 查询密码 |
| order_no | string | 是 | 订单号 |
与 GET /payments/latest 一致。
data:PaymentLaunchorder_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 参数前端必须同时判断:
status_code(业务层)当 status_code != 0 时,请读取 msg 提示并记录 data.request_id 便于排查。
建议支付流程组合使用:
pay_url 或展示 qr_codecapturelatest 兜底轮询确认可显著降低“已支付但页面未及时更新”的感知问题。
以下回调接口一般由支付平台服务器调用,前台模板无需主动请求:
POST /payments/callbackGET /payments/callbackPOST /payments/webhook/paypalPOST /payments/webhook/stripe以下接口由管理后台调用,不属于前台用户侧接口:
接口:GET /admin/settings/telegram-auth
认证:管理员 Token
{
"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判断。
接口:PUT /admin/settings/telegram-auth
认证:管理员 Token
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| enabled | boolean | 否 | 是否启用 Telegram 登录 |
| bot_username | string | 否 | Telegram Bot 用户名(不带 @) |
| bot_token | string | 否 | Telegram Bot Token(传空串不会覆盖已有值) |
| login_expire_seconds | number | 否 | 登录有效期(30-86400 秒) |
| replay_ttl_seconds | number | 否 | 重放保护时长(60-86400 秒) |
{
"enabled": true,
"bot_username": "dujiao_auth_bot",
"bot_token": "123456:ABCDEF",
"login_expire_seconds": 300,
"replay_ttl_seconds": 300
}返回结构与 GET /admin/settings/telegram-auth 一致(脱敏)。
以下接口由管理后台调用,不属于前台用户侧接口。
接口:POST /admin/products
认证:管理员 Token
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| category_id | number | 是 | 分类 ID |
| slug | string | 是 | 商品唯一标识 |
| title | object | 是 | 多语言标题 |
| price_amount | number | 是 | 单规格模式下商品价格;多 SKU 时建议传 0 或任意占位值,最终以后端 SKU 计算为准 |
| fulfillment_type | string | 否 | manual / auto |
| manual_stock_total | number | 否 | 单规格模式下人工库存总量 |
| skus | array | 否 | 多 SKU 数组,传空或不传表示单规格模式 |
skus[] 字段 | 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| id | number | 否 | 更新已有 SKU 时传;创建新 SKU 可不传 |
| sku_code | string | 是 | SKU 编码(同商品内唯一) |
| spec_values | object | 否 | 规格展示文案(如多语言 {"zh-CN":"标准版","en-US":"Standard"}) |
| price_amount | number | 是 | SKU 价格(必须大于 0) |
| manual_stock_total | number | 否 | SKU 人工库存(手动交付模式下生效) |
| is_active | boolean | 否 | 是否启用,默认 true |
| sort_order | number | 否 | 排序权重,默认 0;数值越大越靠前 |
{
"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
}{
"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
}
]
}接口:PUT /admin/products/:id
认证:管理员 Token
请求结构与 POST /admin/products 一致;若要更新已有 SKU,请在 skus[] 中传对应 id。
skus 非空时: manual_stock_total”。skus 为空时: 商品管理,新建或编辑商品。以下接口对应前台返利中心与后台返利审核能力。
affiliate_code 字段 以下接口的请求体支持附带 affiliate_code(联盟ID):
POST /orders/previewPOST /ordersPOST /guest/orders/previewPOST /guest/orders字段定义:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| affiliate_code | string | 否 | 推广联盟ID(如 AB12CD34),用于订单返利归因 |
接口:POST /public/affiliate/click
认证:无需登录
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| affiliate_code | string | 是 | 联盟ID |
| visitor_key | string | 否 | 访客标识(前端可持久化) |
| landing_path | string | 否 | 落地路径(如 /?aff=AB12CD34) |
| referrer | string | 否 | 来源页 URL |
成功返回:
{
"status_code": 0,
"msg": "success",
"data": {
"ok": true
}
}POST /affiliate/openGET /affiliate/dashboard返回 data 关键字段:
| 字段 | 类型 | 说明 |
|---|---|---|
| opened | boolean | 是否已开通 |
| affiliate_code | string | 联盟ID |
| promotion_path | string | 推广路径(如 /?aff=AB12CD34) |
| click_count | number | 点击数 |
| valid_order_count | number | 有效订单数 |
| conversion_rate | number | 转化率(百分比数值) |
| pending_commission | string | 待确认佣金 |
| available_commission | string | 可提现佣金 |
| withdrawn_commission | string | 已提现佣金 |
GET /affiliate/commissionspage、page_size、statuspending_confirm / available / rejected / withdrawnGET /affiliate/withdrawspage、page_size、statuspending_review / rejected / paidPOST /affiliate/withdraws| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| amount | string | 是 | 提现金额(字符串金额,保留 2 位小数) |
| channel | string | 是 | 提现渠道 |
| account | string | 是 | 提现账号 |
GET /admin/settings/affiliatePUT /admin/settings/affiliate| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| enabled | boolean | 是 | 是否开启返利 |
| commission_rate | number | 是 | 返利比例(0-100,支持 2 位小数) |
| confirm_days | number | 是 | 佣金确认天数(0-3650) |
| min_withdraw_amount | number | 是 | 最低提现金额(>=0) |
| withdraw_channels | string[] | 是 | 提现渠道列表 |
以下接口均需管理员 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 字段以下接口由管理后台调用,需要 system_admin 权限。
接口:GET /admin/settings/google-auth
认证:管理员 Token
{
"status_code": 0,
"msg": "success",
"data": {
"enabled": true,
"client_id": "1234567890-example.apps.googleusercontent.com"
}
}接口:PUT /admin/settings/google-auth
认证:管理员 Token
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| enabled | boolean | 否 | 是否启用 Google 登录 |
| client_id | string | 否 | Google OAuth 2.0 Web Client ID;启用时不能为空 |
{
"enabled": true,
"client_id": "1234567890-example.apps.googleusercontent.com"
}保存后配置立即生效。Client ID 是浏览器端公开标识,不需要 Client Secret;本功能也不请求 Gmail 邮箱权限。