From 30362f14bd2c1c5707e5a409f777c253daf34f6c Mon Sep 17 00:00:00 2001 From: yml2213 Date: Fri, 31 Jul 2026 15:25:26 +0800 Subject: [PATCH] =?UTF-8?q?=E7=AE=80=E5=8C=96=E5=BC=80=E6=94=BE=E4=B8=8B?= =?UTF-8?q?=E5=8D=95=E5=B9=82=E7=AD=89=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/internal/handler/open_v1.go | 5 ----- backend/internal/router/router.go | 2 +- frontend/src/openapi/endpoints.ts | 4 ++-- frontend/src/pages/ApiDebugger.tsx | 16 ---------------- frontend/src/pages/OpenApiDocs.tsx | 4 ++-- 5 files changed, 5 insertions(+), 26 deletions(-) diff --git a/backend/internal/handler/open_v1.go b/backend/internal/handler/open_v1.go index 83c8052..2c53091 100644 --- a/backend/internal/handler/open_v1.go +++ b/backend/internal/handler/open_v1.go @@ -56,11 +56,6 @@ func (h *OpenV1Handler) CreateOrder(c *gin.Context) { response.BadRequest(c, "参数错误:client_order_no 与 sku 必填") return } - if key := c.GetHeader("Idempotency-Key"); key != "" && key != req.ClientOrderNo { - openlog.Warn(c, "create_order idempotency_key_mismatch header=%s body=%s", key, req.ClientOrderNo) - response.BadRequest(c, "Idempotency-Key 必须与 client_order_no 一致") - return - } var data interface{} if len(req.Data) > 0 { var decoded interface{} diff --git a/backend/internal/router/router.go b/backend/internal/router/router.go index d176caa..240823b 100644 --- a/backend/internal/router/router.go +++ b/backend/internal/router/router.go @@ -36,7 +36,7 @@ func Setup(h *Handlers) *gin.Engine { r.Use(cors.New(cors.Config{ AllowOrigins: []string{"*"}, AllowMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"}, - AllowHeaders: []string{"Origin", "Content-Type", "Authorization", "X-Merchant-ID", "X-App-Key", "X-Api-Key", "X-Timestamp", "X-Nonce", "X-Sign", "Idempotency-Key", "X-Request-ID"}, + AllowHeaders: []string{"Origin", "Content-Type", "Authorization", "X-Merchant-ID", "X-App-Key", "X-Api-Key", "X-Timestamp", "X-Nonce", "X-Sign", "X-Request-ID"}, ExposeHeaders: []string{"Content-Length", "X-Request-ID"}, AllowCredentials: true, })) diff --git a/frontend/src/openapi/endpoints.ts b/frontend/src/openapi/endpoints.ts index abc3679..fc44332 100644 --- a/frontend/src/openapi/endpoints.ts +++ b/frontend/src/openapi/endpoints.ts @@ -201,7 +201,7 @@ export const endpoints: EndpointSpec[] = [ summary: '幂等创建订单并扣款(成功返回 201,重复请求返回 200 且 idempotent=true)', scope: 'orders:write', bodyParams: [ - { name: 'client_order_no', type: 'string', required: true, desc: '调用方幂等单号,需与 Idempotency-Key 一致', example: 'shop-10001' }, + { 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' }, @@ -233,7 +233,7 @@ export const endpoints: EndpointSpec[] = [ { name: 'order', type: 'object', desc: '订单对象,字段见「订单字段说明」' }, ], notes: [ - 'Header 需带 Idempotency-Key,且必须与 client_order_no 一致。', + 'client_order_no 是唯一幂等单号,请保证同一商户下不重复。', '下单即扣款(payment_status=paid),余额不足返回 400。', '同一 client_order_no 重复请求不会重复扣款,返回 idempotent=true。', ], diff --git a/frontend/src/pages/ApiDebugger.tsx b/frontend/src/pages/ApiDebugger.tsx index 0702a84..d30b5cd 100644 --- a/frontend/src/pages/ApiDebugger.tsx +++ b/frontend/src/pages/ApiDebugger.tsx @@ -77,7 +77,6 @@ export default function ApiDebugger() { const [pathValues, setPathValues] = useState>({}) const [queryValues, setQueryValues] = useState>({}) const [bodyText, setBodyText] = useState('') - const [idempotencyKey, setIdempotencyKey] = useState('') const [loading, setLoading] = useState(false) const handleAppKeyChange = (val: string) => { @@ -114,7 +113,6 @@ export default function ApiDebugger() { } | null>(null) const endpoint = endpoints.find((e) => e.key === selectedKey) - const isCreateOrder = selectedKey === 'create-order' useEffect(() => { if (endpoint?.requestExample) { @@ -129,7 +127,6 @@ export default function ApiDebugger() { } setPathValues({}) setQueryValues({}) - setIdempotencyKey('') setResponse(null) setSignInfo(null) }, [selectedKey, endpoint]) @@ -192,9 +189,6 @@ export default function ApiDebugger() { if (method !== 'GET' && bodyText) { fetchHeaders['Content-Type'] = 'application/json' } - if (isCreateOrder && idempotencyKey.trim()) { - fetchHeaders['Idempotency-Key'] = idempotencyKey.trim() - } setLoading(true) const start = performance.now() @@ -363,16 +357,6 @@ export default function ApiDebugger() { )} - {isCreateOrder && ( - Idempotency-Key} help="须与 client_order_no 一致"> - setIdempotencyKey(e.target.value)} - style={{ maxWidth: 480 }} - placeholder="会从 Request Body 自动取 client_order_no" - /> - - )} &met
  • POST 签名用的 body 必须与实际发送的 body 字节级一致,不要签名后再改空格或字段顺序。
  • value 不要 URL encode,原样参与拼接。
  • 签名失败常见原因:secret 错、path 多了 query、body 与签名不一致、时间戳过期、nonce 重复、参数未按字典序拼接。
  • -
  • 下单接口还需携带 Idempotency-Key,且必须与 client_order_no 一致。
  • +
  • 下单幂等直接使用 client_order_no,无需额外 Header。
  • } /> @@ -495,7 +495,7 @@ function StatusSection() { description={
    {`拿到 sku(商品列表)
         ↓
    -POST /orders 下单(幂等,Idempotency-Key = client_order_no)
    +POST /orders 下单(client_order_no 幂等)
         ↓
     拿响应里的 order_no 进入发货平台链接
         ↓