Files
affiliate_dash/frontend/src/openapi/endpoints.ts
T
yml2213 a44f58cd0f 优化开放 API 文档:创建订单响应示例补全为真实完整字段
- 响应示例从 4 个字段补全为完整订单对象(product/金额/手续费/时间等 21 字段)
- 备注补充双单号说明:下单后保存 order_no 用于后续链路,client_order_no 仅用于幂等
- 说明 data 透传字段行为
2026-08-03 13:53:18 +08:00

407 lines
17 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { EndpointSpec, ParamSpec } from './types'
// 通用响应体字段说明,所有接口共用。
export const commonResponseFields: ParamSpec[] = [
{ name: 'code', type: 'int', required: true, desc: '0 表示成功,非 0 表示失败' },
{ name: 'message', type: 'string', required: true, desc: '成功为 "ok",失败为错误描述' },
{ name: 'data', type: 'object', desc: '业务数据,失败时省略' },
]
// 通用错误码。
export const errorCodes: ParamSpec[] = [
{ name: '400', type: 'int', desc: '参数错误 / 业务校验失败(余额不足、库存不足、状态非法等)' },
{ name: '401', type: 'int', desc: '鉴权失败(Key 缺失、签名错误、时间戳过期、Nonce 重复)' },
{ name: '403', type: 'int', desc: '权限不足(缺少所需 scope 或商户功能未开通)' },
{ name: '404', type: 'int', desc: '资源不存在(订单号不存在等)' },
{ name: '500', type: 'int', desc: '服务端内部错误' },
]
// 订单状态与可发货说明。
export const orderStatusTable: ParamSpec[] = [
{ name: 'paid', type: 'order_status', desc: '已支付/已扣款,可发货', example: 'can_ship=true' },
{ name: 'delivering', type: 'order_status', desc: '发货中', example: 'can_ship=false' },
{ name: 'delivered', type: 'order_status', desc: '已交付', example: 'can_ship=false' },
{ name: 'ship_failed', type: 'order_status', desc: '发货失败,可重试', example: 'can_ship=true' },
{ name: 'cancelled', type: 'order_status', desc: '已取消', example: 'can_ship=false' },
]
// 订单对象公共字段说明,下单/查询/取消响应共用。
const orderFields: ParamSpec[] = [
{ name: 'order_no', type: 'string', required: true, desc: '平台订单号' },
{ name: 'client_order_no', type: 'string', required: true, desc: '调用方传入的幂等单号' },
{ name: 'order_status', type: 'enum', desc: '订单状态,见「状态说明」' },
{ name: 'can_ship', type: 'bool', desc: '是否允许发货' },
{ name: 'cannot_ship_reason', type: 'string', desc: 'can_ship=false 时的原因' },
{ name: 'product.sku', type: 'string', desc: '商品标识,发货以此为准' },
{ name: 'product.name', type: 'string', desc: '商品展示名' },
{ name: 'quantity', type: 'int', desc: '数量' },
{ name: 'base_amount', type: 'int', desc: '商品基础金额(最小货币单位)' },
{ name: 'fee_type', type: 'enum(rate|fixed)', desc: '手续费类型' },
{ name: 'service_fee_amount', type: 'int', desc: '手续费金额' },
{ name: 'amount', type: 'int', desc: '实付总额 = base_amount + service_fee_amount' },
{ name: 'currency', type: 'string', desc: '货币,默认 POINT' },
{ name: 'buyer_reference', type: 'string', desc: '买家标识/备注' },
{ name: 'data', type: 'object', desc: '下单时透传的请求数据(原样回显)' },
{ name: 'result', type: 'object', desc: '发货平台回调的结果数据(原样回显)' },
{ name: 'provider_order_no', type: 'string', desc: '发货平台侧单号' },
{ name: 'failure_reason', type: 'string', desc: '最近一次失败原因' },
{ name: 'created_at', type: 'datetime', desc: '创建时间' },
{ name: 'delivered_at', type: 'datetime', desc: '交付时间' },
{ name: 'cancelled_at', type: 'datetime', desc: '取消时间' },
]
const orderResponseExample = `{
"code": 0,
"message": "ok",
"data": {
"order_no": "FO20260730000123",
"client_order_no": "shop-10001",
"order_status": "paid",
"can_ship": true,
"cannot_ship_reason": "",
"product": { "sku": "suit_pink_sheep", "name": "套装-糯粉咩咩" },
"quantity": 1,
"base_amount": 100,
"fee_type": "rate",
"service_fee_amount": 5,
"amount": 105,
"currency": "POINT",
"buyer_reference": "buyer-001",
"data": { "server": "ios-wechat", "uid": "player-id" },
"provider_order_no": "",
"failure_reason": "",
"created_at": "2026-07-30T10:00:00+08:00",
"delivered_at": null,
"cancelled_at": null
}
}`
const deliveryOrderResponseExample = `{
"code": 0,
"message": "ok",
"data": {
"order_no": "FO20260730000123",
"status": "paid",
"can_ship": true,
"cannot_ship_reason": "",
"product": { "name": "套装-糯粉咩咩", "sku": "suit_pink_sheep", "game": "和平精英" },
"buyer_name": "buyer-001",
"amount": 105,
"created_at": "2026-07-30T10:00:00+08:00",
"shipped_at": null,
"ship_fail_reason": "",
"game_channel": "安卓-QQ",
"game_uid": "4808146277",
"role_name": "巫师哈丁12",
"pay_score": 360,
"good": { "title": "套装-糯粉咩咩", "image": "https://..." }
}
}`
const deliveryLinkResponseExample = `{
"code": 0,
"message": "ok",
"data": {
"order_no": "FO20260730000123",
"delivery_url": "https://shop.example/delivery/dlc/FO20260730000123?exp=1753934400&sign=...",
"expires_at": "2026-07-31T12:00:00+08:00",
"exp": 1753934400,
"sign": "..."
}
}`
const deliveryBindResponseExample = `{
"code": 0,
"message": "ok",
"data": {
"bind_uuid": "bind-123",
"bind_url": "https://bind.example/qrcode",
"qr_url": "https://api.qrserver.com/v1/create-qr-code/?size=240x240&data=https%3A%2F%2Fbind.example%2Fqrcode"
}
}`
const deliverySubmitResponseExample = `{
"code": 0,
"message": "ok",
"data": {
"order_no": "FO20260730000123",
"status": "delivering",
"message": "已提交上游发货,等待发货结果回传",
"provider_order_no": "provider-1",
"game_account": {
"game_account": "4808146277",
"game_account_role_name": "巫师哈丁12"
},
"upstream_order": {
"order_no": "provider-1"
}
}
}`
export const endpoints: EndpointSpec[] = [
{
key: 'list-products',
method: 'GET',
path: '/api/client/v1/products',
title: '商品列表',
summary: '查询商户已授权且上架的商品列表',
scope: 'products:read',
queryParams: [
{ name: 'page', type: 'int', desc: '页码,默认 1', example: '1' },
{ name: 'size', type: 'int', desc: '每页条数,默认 20', example: '20' },
],
responseExample: `{
"code": 0,
"message": "ok",
"data": {
"list": [
{
"id": 12,
"sku": "suit_pink_sheep",
"display_name": "套装-糯粉咩咩",
"price_amount": 100,
"cost_amount": 80,
"currency": "POINT",
"stock": -1,
"status": "active",
"product": { "code": "suit_pink_sheep", "name": "套装-糯粉咩咩", "category": "和平精英" }
}
],
"total": 1,
"page": 1,
"size": 20
}
}`,
responseFields: [
{ name: 'list[].sku', type: 'string', desc: '商品标识,下单时作为 sku 传入' },
{ name: 'list[].display_name', type: 'string', desc: '商户自定义展示名' },
{ name: 'list[].price_amount', type: 'int', desc: '单价(最小货币单位)' },
{ name: 'list[].stock', type: 'int', desc: '库存,-1 表示不限' },
{ name: 'list[].status', type: 'enum', desc: '商品状态,active 可售' },
{ name: 'list[].product', type: 'object', desc: '平台商品目录快照' },
{ name: 'total', type: 'int', desc: '总条数' },
{ name: 'page', type: 'int', desc: '当前页码' },
{ name: 'size', type: 'int', desc: '每页条数' },
],
},
{
key: 'create-order',
method: 'POST',
path: '/api/client/v1/orders',
title: '创建订单',
summary: '幂等创建订单并扣款(成功返回 201,重复请求返回 200 且 idempotent=true)',
scope: 'orders:write',
bodyParams: [
{ name: 'client_order_no', type: 'string', required: true, desc: '调用方幂等单号,同一商户下唯一', example: 'shop-10001' },
{ name: 'sku', type: 'string', required: true, desc: '商品标识,取自商品列表', example: 'suit_pink_sheep' },
{ name: 'quantity', type: 'int', desc: '数量,默认 1', example: '1' },
{ name: 'buyer_reference', type: 'string', desc: '买家标识/备注', example: 'buyer-001' },
{ name: 'data', type: 'object', desc: '透传给发货流程的业务数据(区服、账号等),原样存储并回显' },
],
requestExample: `{
"client_order_no": "shop-10001",
"sku": "suit_pink_sheep",
"quantity": 1,
"buyer_reference": "buyer-001",
"data": { "server": "ios-wechat", "uid": "player-id" }
}`,
responseExample: `{
"code": 0,
"message": "ok",
"data": {
"idempotent": false,
"order": {
"order_no": "FO20260730000123",
"client_order_no": "shop-10001",
"order_status": "paid",
"can_ship": true,
"cannot_ship_reason": "",
"product": { "sku": "suit_pink_sheep", "name": "套装-糯粉咩咩" },
"quantity": 1,
"base_amount": 100,
"fee_type": "rate",
"service_fee_amount": 5,
"amount": 105,
"currency": "POINT",
"buyer_reference": "buyer-001",
"data": { "server": "ios-wechat", "uid": "player-id" },
"provider_order_no": "",
"failure_reason": "",
"created_at": "2026-07-30T10:00:00+08:00",
"delivered_at": null,
"cancelled_at": null
}
}
}`,
responseFields: [
{ name: 'idempotent', type: 'bool', desc: 'true 表示该 client_order_no 已存在,返回既有订单(未重复扣款)' },
{ name: 'order', type: 'object', desc: '订单完整对象,字段见下方「订单字段说明」' },
],
notes: [
'client_order_no 是唯一幂等单号,请保证同一商户下不重复;重复请求不会重复扣款,返回 idempotent=true 及既有订单。',
'当前无真实支付流程,下单成功即扣款并进入 order_status=paid,余额不足返回 400。',
'下单成功后请保存 order_no,后续查询/发货/回调均以 order_no 为准;client_order_no 仅用于幂等。',
'data 字段会原样存储并回显,可用于透传区服、账号等业务数据。',
],
},
{
key: 'query-order',
method: 'GET',
path: '/api/client/v1/orders/{order_no}',
title: '查询订单',
summary: '查询订单状态与详情',
scope: 'orders:read',
pathParams: [{ name: 'order_no', type: 'string', required: true, desc: '平台订单号', example: 'FO20260730000123' }],
responseExample: orderResponseExample,
responseFields: orderFields,
},
{
key: 'cancel-order',
method: 'POST',
path: '/api/client/v1/orders/{order_no}/cancel',
title: '取消订单',
summary: '取消未发货订单并退款(仅 paid/ship_failed 可取消)',
scope: 'orders:write',
pathParams: [{ name: 'order_no', type: 'string', required: true, desc: '平台订单号', example: 'FO20260730000123' }],
bodyParams: [{ name: 'reason', type: 'string', desc: '取消原因(可选)', example: '买家主动取消' }],
requestExample: `{ "reason": "买家主动取消" }`,
responseExample: orderResponseExample,
responseFields: orderFields,
notes: [
'取消成功后 order_status 变为 cancelled,金额退回商户钱包。',
'已进入 delivering/delivered 的订单不可取消,返回 400。',
],
},
{
key: 'delivery-link',
method: 'GET',
path: '/api/client/v1/orders/{order_no}/delivery-link',
title: '获取发货链接',
summary: '获取签名发货链接,适合直接跳转平台 Web 发货页',
scope: 'orders:read 或 shipping:read',
pathParams: [{ name: 'order_no', type: 'string', required: true, desc: '平台订单号', example: 'FO20260730000123' }],
responseExample: deliveryLinkResponseExample,
responseFields: [
{ name: 'order_no', type: 'string', desc: '平台订单号' },
{ name: 'delivery_url', type: 'string', desc: '可直接打开的发货链接' },
{ name: 'expires_at', type: 'datetime', desc: '链接过期时间' },
{ name: 'exp', type: 'int', desc: '过期 Unix 秒时间戳' },
{ name: 'sign', type: 'string', desc: '签名,供链接校验使用' },
],
notes: [
'适合商户系统只想“返回一个链接”给终端用户的场景。',
'delivery_url 可以直接给用户打开,sign 仅供后端或调试使用,不建议展示。',
],
},
{
key: 'delivery-order',
method: 'GET',
path: '/api/client/v1/orders/{order_no}/delivery',
title: '查询发货数据',
summary: '查询发货页结构化数据,适合商户自建发货界面',
scope: 'orders:read 或 shipping:read',
pathParams: [{ name: 'order_no', type: 'string', required: true, desc: '平台订单号', example: 'FO20260730000123' }],
responseExample: deliveryOrderResponseExample,
responseFields: [
{ name: 'order_no', type: 'string', desc: '平台订单号' },
{ name: 'status', type: 'string', desc: '订单状态' },
{ name: 'can_ship', type: 'bool', desc: '是否可以发货' },
{ name: 'cannot_ship_reason', type: 'string', desc: '不可发货原因' },
{ name: 'product', type: 'object', desc: '商品信息' },
{ name: 'buyer_name', type: 'string', desc: '买家名称 / 备注' },
{ name: 'amount', type: 'int', desc: '订单金额' },
{ name: 'created_at', type: 'datetime', desc: '创建时间' },
{ name: 'shipped_at', type: 'datetime', desc: '发货时间' },
{ name: 'ship_fail_reason', type: 'string', desc: '最近一次失败原因' },
{ name: 'game_channel', type: 'string', desc: '区服渠道' },
{ name: 'game_uid', type: 'string', desc: 'UID / 角色编号' },
{ name: 'role_name', type: 'string', desc: '角色名' },
{ name: 'pay_score', type: 'int', desc: '消耗积分' },
{ name: 'good', type: 'object', desc: '上游商品详情' },
],
notes: [
'该接口返回的是结构化数据,适合商户自有页面渲染。',
'如果只是想直接给用户一个发货页链接,优先用 delivery-link。',
],
},
{
key: 'delivery-bind',
method: 'POST',
path: '/api/client/v1/orders/{order_no}/delivery/bind',
title: '发起绑定',
summary: '发起绑定,返回绑定跳转链接与二维码地址',
scope: 'orders:write',
pathParams: [{ name: 'order_no', type: 'string', required: true, desc: '平台订单号', example: 'FO20260730000123' }],
bodyParams: [
{ name: 'game_account', type: 'string', required: true, desc: '玩家编号 / UID', example: '4808146277' },
],
requestExample: `{
"game_account": "4808146277"
}`,
responseExample: deliveryBindResponseExample,
responseFields: [
{ name: 'bind_uuid', type: 'string', desc: '绑定凭证,提交发货时使用' },
{ name: 'bind_url', type: 'string', desc: '绑定跳转地址' },
{ name: 'qr_url', type: 'string', desc: '绑定二维码地址' },
],
notes: [
'适合商户自建页面先让用户确认 UID,再去拿绑定结果。',
'同一订单重复绑定时,请以最新返回的绑定凭证为准。',
],
},
{
key: 'delivery-submit',
method: 'POST',
path: '/api/client/v1/orders/{order_no}/delivery/submit',
title: '提交发货',
summary: '提交发货到上游并进入发货中状态',
scope: 'orders:write',
pathParams: [{ name: 'order_no', type: 'string', required: true, desc: '平台订单号', example: 'FO20260730000123' }],
bodyParams: [
{ name: 'game_account', type: 'string', required: true, desc: '玩家编号 / UID', example: '4808146277' },
{ name: 'bind_uuid', type: 'string', required: true, desc: '绑定凭证', example: 'bind-123' },
],
requestExample: `{
"game_account": "4808146277",
"bind_uuid": "bind-123"
}`,
responseExample: deliverySubmitResponseExample,
responseFields: [
{ name: 'order_no', type: 'string', desc: '平台订单号' },
{ name: 'status', type: 'string', desc: '提交后的订单状态' },
{ name: 'message', type: 'string', desc: '处理结果说明' },
{ name: 'provider_order_no', type: 'string', desc: '上游订单号' },
{ name: 'game_account', type: 'object', desc: '上游绑定结果' },
{ name: 'upstream_order', type: 'object', desc: '上游创建结果' },
],
notes: [
'同一订单重复提交应直接返回已有结果,不应重复创建上游队列。',
'提交成功后,最终成功/失败仍以 `ship_notify` 回传结果为准。',
],
},
{
key: 'get-wallet',
method: 'GET',
path: '/api/client/v1/wallet',
title: '余额查询',
summary: '查询商户钱包余额',
scope: 'wallet:read',
responseExample: `{
"code": 0,
"message": "ok",
"data": {
"id": 1,
"merchant_id": 2,
"currency": "POINT",
"available_balance": 895,
"frozen_balance": 0
}
}`,
responseFields: [
{ name: 'currency', type: 'string', desc: '货币,默认 POINT' },
{ name: 'available_balance', type: 'int', desc: '可用余额(最小货币单位)' },
{ name: 'frozen_balance', type: 'int', desc: '冻结余额' },
],
},
]