{"openapi":"3.1.0","info":{"title":"Pushlib 商户 API","version":"1.0.0","description":"商户下单、查单、按时间区间列单、关单，以及支付成功回调。所有请求都要 HMAC 签名，签名算法、接入步骤与注意事项见 https://platform.pushlib.cn/llms/merchant.md","contact":{"name":"Pushlib 支持","email":"help@pushlib.cn"}},"servers":[{"url":"https://platform.pushlib.cn"}],"security":[{"keyId":[],"timestamp":[],"nonce":[],"signature":[]}],"paths":{"/api/merchant/ping":{"get":{"operationId":"ping","summary":"验签自检","description":"不产生任何业务数据。接入第一步：用它确认密钥与签名实现正确","responses":{"200":{"description":"签名通过","content":{"application/json":{"schema":{"type":"object","required":["ok","role"],"properties":{"ok":{"const":true},"role":{"const":"merchant"}}}}}},"401":{"description":"签名头缺失或不合法、密钥编号不存在或已吊销、签名错误、时间戳超出窗口","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","enum":["missing_headers","bad_timestamp","bad_nonce","bad_key","bad_signature"]}},"additionalProperties":false}}}},"403":{"description":"租户未激活或已停用，指定的收款终端已停用，或请求地址不在租户的 IP 白名单内","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","enum":["tenant_inactive","tenant_suspended","payee_disabled","ip_not_allowed"]}},"additionalProperties":false}}}},"409":{"description":"nonce 已用过","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","enum":["replay"]}},"additionalProperties":false}}}},"429":{"description":"同一密钥请求过于频繁（每分钟至多 600 次），按 Retry-After 秒数后重试","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","enum":["rate_limited"]}},"additionalProperties":false}}}}}}},"/api/merchant/orders":{"get":{"operationId":"listOrders","summary":"按时间区间列单","description":"用于对账：创建时间落在 [from, to) 的订单，按创建时间从旧到新（同一时刻按订单号）。每页至多 100 条，条数不足 limit 即读完；从旧到新排，翻页时新单落在末尾，不会错页。query 参与签名，签名的 path 必须与实际发送的 path+query 完全一致","parameters":[{"name":"from","in":"query","required":true,"schema":{"type":"string","pattern":"^\\d{1,15}$"},"description":"创建时间下界，Unix 毫秒整数，含"},{"name":"to","in":"query","required":true,"schema":{"type":"string","pattern":"^\\d{1,15}$"},"description":"创建时间上界，Unix 毫秒整数，不含；不得早于 from"},{"name":"channel","in":"query","required":false,"schema":{"type":"string","enum":["wechat","alipay"]},"description":"只看这个渠道"},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["pending","paid","expired","cancelled"]},"description":"只看这个支付状态"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":50},"description":"每页条数，默认 50，超过 100 按 100"},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0},"description":"跳过的条数"}],"responses":{"200":{"description":"订单列表","content":{"application/json":{"schema":{"type":"object","required":["items"],"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Order"}}}}}}},"400":{"description":"from / to 缺失、不是整数或 to 早于 from，渠道、状态、limit、offset 不合法","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","enum":["bad_request"]}},"additionalProperties":false}}}},"401":{"description":"签名头缺失或不合法、密钥编号不存在或已吊销、签名错误、时间戳超出窗口","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","enum":["missing_headers","bad_timestamp","bad_nonce","bad_key","bad_signature"]}},"additionalProperties":false}}}},"403":{"description":"租户未激活或已停用，指定的收款终端已停用，或请求地址不在租户的 IP 白名单内","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","enum":["tenant_inactive","tenant_suspended","payee_disabled","ip_not_allowed"]}},"additionalProperties":false}}}},"409":{"description":"nonce 已用过","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","enum":["replay"]}},"additionalProperties":false}}}},"429":{"description":"同一密钥请求过于频繁（每分钟至多 600 次），按 Retry-After 秒数后重试","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","enum":["rate_limited"]}},"additionalProperties":false}}}}}},"post":{"operationId":"createOrder","summary":"创建订单","description":"同一 merchantOrderId 且 amount、channel、notifyUrl、returnUrl 都相同的重放返回原单（200）。向付款人展示并要求支付的是 matchAmount，不是 amount","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateOrderRequest"}}}},"responses":{"200":{"description":"幂等重放：返回原订单","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Order"}}}},"201":{"description":"新订单","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Order"}}}},"400":{"description":"请求体不是 JSON、字段不合法或金额不合法","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","enum":["bad_json","bad_request","bad_amount","payee_required","payee_not_found"]}},"additionalProperties":false}}}},"401":{"description":"签名头缺失或不合法、密钥编号不存在或已吊销、签名错误、时间戳超出窗口","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","enum":["missing_headers","bad_timestamp","bad_nonce","bad_key","bad_signature"]}},"additionalProperties":false}}}},"403":{"description":"租户未激活或已停用，指定的收款终端已停用，或请求地址不在租户的 IP 白名单内","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","enum":["tenant_inactive","tenant_suspended","payee_disabled","ip_not_allowed"]}},"additionalProperties":false}}}},"409":{"description":"nonce 已用过、匹配金额用尽，或同一 merchantOrderId 的内容冲突","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","enum":["replay","amount_busy","merchant_order_conflict"]}},"additionalProperties":false}}}},"429":{"description":"同一密钥请求过于频繁（每分钟至多 600 次），按 Retry-After 秒数后重试","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","enum":["rate_limited"]}},"additionalProperties":false}}}},"503":{"description":"监控端离线，或该渠道没有收款链接","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","enum":["monitor_offline","qrcode_missing"]}},"additionalProperties":false}}}}}}},"/api/merchant/orders/{orderId}":{"get":{"operationId":"getOrder","summary":"按订单号查询","parameters":[{"name":"orderId","in":"path","required":true,"schema":{"type":"string"},"description":"服务端生成的订单号（UUID）"}],"responses":{"200":{"description":"订单","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Order"}}}},"401":{"description":"签名头缺失或不合法、密钥编号不存在或已吊销、签名错误、时间戳超出窗口","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","enum":["missing_headers","bad_timestamp","bad_nonce","bad_key","bad_signature"]}},"additionalProperties":false}}}},"403":{"description":"租户未激活或已停用，指定的收款终端已停用，或请求地址不在租户的 IP 白名单内","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","enum":["tenant_inactive","tenant_suspended","payee_disabled","ip_not_allowed"]}},"additionalProperties":false}}}},"404":{"description":"订单不存在","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","enum":["not_found"]}},"additionalProperties":false}}}},"409":{"description":"nonce 已用过","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","enum":["replay"]}},"additionalProperties":false}}}},"429":{"description":"同一密钥请求过于频繁（每分钟至多 600 次），按 Retry-After 秒数后重试","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","enum":["rate_limited"]}},"additionalProperties":false}}}}}}},"/api/merchant/orders/by-merchant/{merchantOrderId}":{"get":{"operationId":"getOrderByMerchantOrderId","summary":"按商户单号查询","parameters":[{"name":"merchantOrderId","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":64},"description":"商户单号"}],"responses":{"200":{"description":"订单","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Order"}}}},"401":{"description":"签名头缺失或不合法、密钥编号不存在或已吊销、签名错误、时间戳超出窗口","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","enum":["missing_headers","bad_timestamp","bad_nonce","bad_key","bad_signature"]}},"additionalProperties":false}}}},"403":{"description":"租户未激活或已停用，指定的收款终端已停用，或请求地址不在租户的 IP 白名单内","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","enum":["tenant_inactive","tenant_suspended","payee_disabled","ip_not_allowed"]}},"additionalProperties":false}}}},"404":{"description":"订单不存在","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","enum":["not_found"]}},"additionalProperties":false}}}},"409":{"description":"nonce 已用过","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","enum":["replay"]}},"additionalProperties":false}}}},"429":{"description":"同一密钥请求过于频繁（每分钟至多 600 次），按 Retry-After 秒数后重试","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","enum":["rate_limited"]}},"additionalProperties":false}}}}}}},"/api/merchant/orders/{orderId}/close":{"post":{"operationId":"closeOrder","summary":"按订单号关闭","description":"仅待支付订单可关闭；已取消的返回当前状态。关闭不触发回调","parameters":[{"name":"orderId","in":"path","required":true,"schema":{"type":"string"},"description":"服务端生成的订单号（UUID）"}],"responses":{"200":{"description":"订单（paymentStatus 为 cancelled）","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Order"}}}},"401":{"description":"签名头缺失或不合法、密钥编号不存在或已吊销、签名错误、时间戳超出窗口","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","enum":["missing_headers","bad_timestamp","bad_nonce","bad_key","bad_signature"]}},"additionalProperties":false}}}},"403":{"description":"租户未激活或已停用，指定的收款终端已停用，或请求地址不在租户的 IP 白名单内","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","enum":["tenant_inactive","tenant_suspended","payee_disabled","ip_not_allowed"]}},"additionalProperties":false}}}},"404":{"description":"订单不存在","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","enum":["not_found"]}},"additionalProperties":false}}}},"409":{"description":"nonce 已用过（replay），或订单已支付/已过期不可关闭（not_cancellable）","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","enum":["replay","not_cancellable"]}},"additionalProperties":false}}}},"429":{"description":"同一密钥请求过于频繁（每分钟至多 600 次），按 Retry-After 秒数后重试","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","enum":["rate_limited"]}},"additionalProperties":false}}}}}}},"/api/merchant/orders/by-merchant/{merchantOrderId}/close":{"post":{"operationId":"closeOrderByMerchantOrderId","summary":"按商户单号关闭","description":"语义同按订单号关闭","parameters":[{"name":"merchantOrderId","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":64},"description":"商户单号"}],"responses":{"200":{"description":"订单（paymentStatus 为 cancelled）","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Order"}}}},"401":{"description":"签名头缺失或不合法、密钥编号不存在或已吊销、签名错误、时间戳超出窗口","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","enum":["missing_headers","bad_timestamp","bad_nonce","bad_key","bad_signature"]}},"additionalProperties":false}}}},"403":{"description":"租户未激活或已停用，指定的收款终端已停用，或请求地址不在租户的 IP 白名单内","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","enum":["tenant_inactive","tenant_suspended","payee_disabled","ip_not_allowed"]}},"additionalProperties":false}}}},"404":{"description":"订单不存在","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","enum":["not_found"]}},"additionalProperties":false}}}},"409":{"description":"nonce 已用过（replay），或订单已支付/已过期不可关闭（not_cancellable）","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","enum":["replay","not_cancellable"]}},"additionalProperties":false}}}},"429":{"description":"同一密钥请求过于频繁（每分钟至多 600 次），按 Retry-After 秒数后重试","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","enum":["rate_limited"]}},"additionalProperties":false}}}}}}}},"webhooks":{"paymentSucceeded":{"post":{"operationId":"paymentSucceeded","summary":"支付成功回调","description":"订单进入 paid 后，服务端向订单的 notifyUrl 发出。签名头与商户请求相同，用租户最新有效的商户密钥签名，X-Pushlib-Key-Id 是该密钥的编号，商户据此选密钥；商户须自行验签并拒绝窗口外和重复的 nonce。只有 HTTP 200 算成功，正文不参与确认；非 200、网络错误、10 秒超时都算失败，不跟随重定向；地址解析到内网（回环、私有、链路本地、CGNAT、云元数据等）时不发出请求，同样算失败。失败后按 60、300、900、3600、21600 秒重试，共 6 次投递。同一订单可能重复收到，须按 orderId 幂等处理。须按 paymentStatus 判断结果，不认识的取值不做业务处理并回 200。详见 /llms/merchant.md","parameters":[{"name":"X-Pushlib-Key-Id","in":"header","required":true,"schema":{"type":"string","pattern":"^mk_[0-9A-Z]{26}$"},"description":"签名所用商户密钥的编号（mk_ 开头），公开、可写进日志"},{"name":"X-Pushlib-Timestamp","in":"header","required":true,"schema":{"type":"string","pattern":"^\\d+$"},"description":"Unix 秒，十进制整数"},{"name":"X-Pushlib-Nonce","in":"header","required":true,"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[\\x21-\\x7E]+$"},"description":"每个请求一个新值，不得重复"},{"name":"X-Pushlib-Signature","in":"header","required":true,"schema":{"type":"string","pattern":"^[0-9a-f]{64}$"},"description":"小写 hex 的 HMAC-SHA256，算法见 /llms/merchant.md"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentCallback"}}}},"responses":{"200":{"description":"已处理，回调成功"}}}}},"components":{"securitySchemes":{"keyId":{"type":"apiKey","in":"header","name":"X-Pushlib-Key-Id","description":"商户密钥编号"},"timestamp":{"type":"apiKey","in":"header","name":"X-Pushlib-Timestamp","description":"Unix 秒"},"nonce":{"type":"apiKey","in":"header","name":"X-Pushlib-Nonce","description":"一次请求一个"},"signature":{"type":"apiKey","in":"header","name":"X-Pushlib-Signature","description":"四个头必须同时提供；签名算法见 /llms/merchant.md"}},"schemas":{"CreateOrderRequest":{"type":"object","properties":{"payeeId":{"type":"string","minLength":1,"maxLength":64,"description":"可选，收款终端编号。租户恰有 1 个启用的终端时可省略；否则必填，缺失回 400 payee_required"},"merchantOrderId":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[\\x21-\\x7E]+$","description":"商户单号，1–64 个可见 ASCII 字符，不含空白。全库唯一，终态后也不回收"},"channel":{"type":"string","enum":["wechat","alipay"],"description":"支付渠道"},"amount":{"type":"string","description":"标价，元，字符串，最多两位小数，大于 0 且不超过 999999.99；多于两位小数会被拒绝，不四舍五入"},"notifyUrl":{"type":"string","minLength":1,"maxLength":2048,"description":"支付成功回调地址，绝对 http(s) URL，最长 2048。没有默认地址"},"returnUrl":{"type":"string","minLength":1,"maxLength":2048,"description":"可选。付款人在托管收银台看到已支付后跳转的地址，https（本机 localhost、127.0.0.1、[::1] 可用 http），不含账号口令，最长 2048。跳转不代表商户已收到钱，以回调和查单为准"}},"required":["merchantOrderId","channel","amount","notifyUrl"],"additionalProperties":false},"Order":{"type":"object","properties":{"orderId":{"type":"string","description":"服务端生成的订单号（UUID）"},"payeeId":{"type":"string","description":"收款终端编号：这笔订单由哪台监控设备的收款码收款"},"merchantOrderId":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[\\x21-\\x7E]+$"},"channel":{"type":"string","enum":["wechat","alipay"]},"amount":{"type":"string","description":"商户标价，元"},"matchAmount":{"type":"string","description":"付款人应实际支付的金额，元。等于标价，或比标价高 0.01–0.09"},"payUrl":{"type":"string","description":"收款链接，内容是要编码成二维码的字符串，不是图片"},"notifyUrl":{"type":"string"},"returnUrl":{"type":["string","null"],"description":"创建时给的跳转地址，没给为 null"},"cashierUrl":{"type":"string","description":"托管收银台页的地址：把付款人带到这里（新页面或 302，不可嵌入 iframe），页面展示二维码、应付金额与倒计时"},"paymentStatus":{"type":"string","enum":["pending","paid","expired","cancelled"],"description":"支付状态：pending → paid / expired / cancelled。expired 之后仍可能变为 paid（到期后才到的款被运营者关联到原单）"},"notifyStatus":{"type":"string","enum":["idle","pending","succeeded","failed"],"description":"回调状态：idle（未支付）→ pending → succeeded / failed"},"expiresAt":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"到期时间，Unix 毫秒。创建时写定，之后不变"},"createdAt":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"创建时间，Unix 毫秒"}},"required":["orderId","payeeId","merchantOrderId","channel","amount","matchAmount","payUrl","notifyUrl","returnUrl","cashierUrl","paymentStatus","notifyStatus","expiresAt","createdAt"],"additionalProperties":false},"PaymentCallback":{"type":"object","properties":{"orderId":{"type":"string"},"merchantOrderId":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[\\x21-\\x7E]+$"},"channel":{"type":"string","enum":["wechat","alipay"]},"amount":{"type":"string"},"matchAmount":{"type":"string"},"paymentStatus":{"type":"string","const":"paid"}},"required":["orderId","merchantOrderId","channel","amount","matchAmount","paymentStatus"],"additionalProperties":false}}}}