From 667bfbde061d5bb3b253db06ca282e42760b2214 Mon Sep 17 00:00:00 2001 From: yml2213 Date: Fri, 31 Jul 2026 12:29:43 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A1=A5=E5=BC=80=E6=94=BE=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/openapi/endpoints.ts | 165 +++++++++++++++++++++++++++++ frontend/src/pages/OpenApiDocs.tsx | 71 ++++++++++++- 2 files changed, 234 insertions(+), 2 deletions(-) diff --git a/frontend/src/openapi/endpoints.ts b/frontend/src/openapi/endpoints.ts index 5de53a6..ead9589 100644 --- a/frontend/src/openapi/endpoints.ts +++ b/frontend/src/openapi/endpoints.ts @@ -85,6 +85,68 @@ const orderResponseExample = `{ } }` +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:00Z", + "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": "processing", + "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', @@ -200,6 +262,109 @@ export const endpoints: EndpointSpec[] = [ '已进入 processing/succeeded 的订单不可取消,返回 400。', ], }, + { + key: 'delivery-link', + method: 'GET', + path: '/api/client/v1/orders/{order_no}/delivery-link', + summary: '获取签名发货链接,适合直接跳转平台 Web 发货页', + scope: 'orders:read + fulfillment: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', + summary: '查询发货页结构化数据,适合商户自建发货界面', + scope: 'orders:read + fulfillment: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', + 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', + 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', diff --git a/frontend/src/pages/OpenApiDocs.tsx b/frontend/src/pages/OpenApiDocs.tsx index ec3b13d..b631ef8 100644 --- a/frontend/src/pages/OpenApiDocs.tsx +++ b/frontend/src/pages/OpenApiDocs.tsx @@ -28,6 +28,20 @@ const preStyle: CSSProperties = { wordBreak: 'break-all', } +const deliveryModes = [ + { + mode: 'Web 发货页', + scene: '商户想要“只返回一个链接”,让用户直接打开平台页面填写 UID', + interfaces: '/api/client/v1/orders/{order_no}/delivery-link', + }, + { + mode: '自建发货页', + scene: '商户自己有前端页面,想在自己的系统里完成发货流程', + interfaces: + '/api/client/v1/orders/{order_no}/delivery\n/api/client/v1/orders/{order_no}/delivery/bind\n/api/client/v1/orders/{order_no}/delivery/submit', + }, +] + export default function OpenApiDocs() { return (
@@ -37,10 +51,13 @@ export default function OpenApiDocs() { 面向商户系统调用的开放 API。凭证在「商户中心 / API 客户端」创建, 采用 X-App-Key + HMAC-SHA256 签名鉴权。 - /api/open/v1 仅保留给发货平台查询与回传,不在本页展示。 + 发货链路同时支持「返回签名链接」和「返回结构化数据」两种模式。 + /api/open/v1 仍是上游回调链路,和本页的商户 API 分开。 , }, + { + key: 'delivery', + label: '发货模式', + children: , + }, { key: 'status', label: '状态说明', @@ -80,10 +102,29 @@ function OverviewTab() {
  • 在「商户中心 / API 客户端」创建凭证,获得 AppKeyAppSecret
  • 每次请求携带 X-App-Key / X-Timestamp / X-Nonce / X-Sign 四个鉴权头。
  • 调用「商品列表」拿到可售 sku,调用「下单」创建订单并扣款。
  • -
  • 拿到 order_no 后进入发货链接;订单状态由发货平台回调更新,可通过「查询订单」轮询。
  • +
  • 拿到 order_no 后,可选择返回发货链接,或者用结构化发货接口完成自建页面流程。
  • } /> + + ( +
    {v}
    + ), + }, + ]} + /> + @@ -101,6 +142,32 @@ function OverviewTab() { ) } +function DeliveryModeTab() { + return ( + + + + + 用 delivery-link,后端直接返回签名后的 Web 发货页地址,前端只负责跳转或复制。 + + + 用 delivery 获取订单数据,再按需调用 bindsubmit。 + + + 取消作废属于商户后台操作,恢复后会重新签发新有效期和新签名。 + + + + + + ) +} + function AuthTab() { return (