简化开放下单幂等参数

This commit is contained in:
yml2213
2026-07-31 15:25:26 +08:00
parent 71cde7e32b
commit 30362f14bd
5 changed files with 5 additions and 26 deletions
-5
View File
@@ -56,11 +56,6 @@ func (h *OpenV1Handler) CreateOrder(c *gin.Context) {
response.BadRequest(c, "参数错误:client_order_no 与 sku 必填") response.BadRequest(c, "参数错误:client_order_no 与 sku 必填")
return 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{} var data interface{}
if len(req.Data) > 0 { if len(req.Data) > 0 {
var decoded interface{} var decoded interface{}
+1 -1
View File
@@ -36,7 +36,7 @@ func Setup(h *Handlers) *gin.Engine {
r.Use(cors.New(cors.Config{ r.Use(cors.New(cors.Config{
AllowOrigins: []string{"*"}, AllowOrigins: []string{"*"},
AllowMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"}, 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"}, ExposeHeaders: []string{"Content-Length", "X-Request-ID"},
AllowCredentials: true, AllowCredentials: true,
})) }))
+2 -2
View File
@@ -201,7 +201,7 @@ export const endpoints: EndpointSpec[] = [
summary: '幂等创建订单并扣款(成功返回 201,重复请求返回 200 且 idempotent=true', summary: '幂等创建订单并扣款(成功返回 201,重复请求返回 200 且 idempotent=true',
scope: 'orders:write', scope: 'orders:write',
bodyParams: [ 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: 'sku', type: 'string', required: true, desc: '商品标识,取自商品列表', example: 'suit_pink_sheep' },
{ name: 'quantity', type: 'int', desc: '数量,默认 1', example: '1' }, { name: 'quantity', type: 'int', desc: '数量,默认 1', example: '1' },
{ name: 'buyer_reference', type: 'string', desc: '买家标识/备注', example: 'buyer-001' }, { name: 'buyer_reference', type: 'string', desc: '买家标识/备注', example: 'buyer-001' },
@@ -233,7 +233,7 @@ export const endpoints: EndpointSpec[] = [
{ name: 'order', type: 'object', desc: '订单对象,字段见「订单字段说明」' }, { name: 'order', type: 'object', desc: '订单对象,字段见「订单字段说明」' },
], ],
notes: [ notes: [
'Header 需带 Idempotency-Key,且必须与 client_order_no 一致。', 'client_order_no 是唯一幂等单号,请保证同一商户下不重复。',
'下单即扣款(payment_status=paid),余额不足返回 400。', '下单即扣款(payment_status=paid),余额不足返回 400。',
'同一 client_order_no 重复请求不会重复扣款,返回 idempotent=true。', '同一 client_order_no 重复请求不会重复扣款,返回 idempotent=true。',
], ],
-16
View File
@@ -77,7 +77,6 @@ export default function ApiDebugger() {
const [pathValues, setPathValues] = useState<Record<string, string>>({}) const [pathValues, setPathValues] = useState<Record<string, string>>({})
const [queryValues, setQueryValues] = useState<Record<string, string>>({}) const [queryValues, setQueryValues] = useState<Record<string, string>>({})
const [bodyText, setBodyText] = useState('') const [bodyText, setBodyText] = useState('')
const [idempotencyKey, setIdempotencyKey] = useState('')
const [loading, setLoading] = useState(false) const [loading, setLoading] = useState(false)
const handleAppKeyChange = (val: string) => { const handleAppKeyChange = (val: string) => {
@@ -114,7 +113,6 @@ export default function ApiDebugger() {
} | null>(null) } | null>(null)
const endpoint = endpoints.find((e) => e.key === selectedKey) const endpoint = endpoints.find((e) => e.key === selectedKey)
const isCreateOrder = selectedKey === 'create-order'
useEffect(() => { useEffect(() => {
if (endpoint?.requestExample) { if (endpoint?.requestExample) {
@@ -129,7 +127,6 @@ export default function ApiDebugger() {
} }
setPathValues({}) setPathValues({})
setQueryValues({}) setQueryValues({})
setIdempotencyKey('')
setResponse(null) setResponse(null)
setSignInfo(null) setSignInfo(null)
}, [selectedKey, endpoint]) }, [selectedKey, endpoint])
@@ -192,9 +189,6 @@ export default function ApiDebugger() {
if (method !== 'GET' && bodyText) { if (method !== 'GET' && bodyText) {
fetchHeaders['Content-Type'] = 'application/json' fetchHeaders['Content-Type'] = 'application/json'
} }
if (isCreateOrder && idempotencyKey.trim()) {
fetchHeaders['Idempotency-Key'] = idempotencyKey.trim()
}
setLoading(true) setLoading(true)
const start = performance.now() const start = performance.now()
@@ -363,16 +357,6 @@ export default function ApiDebugger() {
</Form.Item> </Form.Item>
)} )}
{isCreateOrder && (
<Form.Item label={<Text code style={{ fontSize: 14 }}>Idempotency-Key</Text>} help="须与 client_order_no 一致">
<Input
value={idempotencyKey}
onChange={(e) => setIdempotencyKey(e.target.value)}
style={{ maxWidth: 480 }}
placeholder="会从 Request Body 自动取 client_order_no"
/>
</Form.Item>
)}
</Form> </Form>
<Alert <Alert
+2 -2
View File
@@ -425,7 +425,7 @@ app_key=ak_xxx&body_sha256=<实际请求 body 字节的 SHA256 十六进制>&met
<li>POST <Text code>body</Text> body <b></b></li> <li>POST <Text code>body</Text> body <b></b></li>
<li>value <b></b> URL encode</li> <li>value <b></b> URL encode</li>
<li>secret path querybody nonce </li> <li>secret path querybody nonce </li>
<li> <Text code>Idempotency-Key</Text> <Text code>client_order_no</Text> </li> <li>使 <Text code>client_order_no</Text> Header</li>
</ul> </ul>
} }
/> />
@@ -495,7 +495,7 @@ function StatusSection() {
description={ description={
<pre className="api-docs__pre api-docs__pre--plain">{`拿到 sku(商品列表) <pre className="api-docs__pre api-docs__pre--plain">{`拿到 sku(商品列表)
POST /orders 下单(幂等,Idempotency-Key = client_order_no POST /orders 下单(client_order_no 幂等
拿响应里的 order_no 进入发货平台链接 拿响应里的 order_no 进入发货平台链接