Proxy-seller

Webhooks

Updated:

除了轮询外,您还可以在 HTTPS 端点上接收订单事件。Webhooks 传递的事件与事件顺序. 无论是否启用 webhook,事件日志始终可用,因此您可以随时改用轮询方式。

设置

  • 通过callback_url in下单,或稍后通过更新顺序.

  • 响应结果如下webhook_secret (whsec_…) 仅需设置一次。将其保存:它会为每个 webhook 签名。

  • 修改callback_url 生成一个新的密钥。之前的密钥将在24小时内继续为 webhooks 签名:在此期间X-PS-Signature 带有两个签名。

请求

属性

价值

方法

POST tocallback_url

标题

Content-Type: application/json; charset=utf-8,User-Agent: ProxySeller-Webhooks/1,X-PS-Timestamp,X-PS-Signature

正文

一个事件,例如事件顺序;attempt 表示投递尝试次数(1、2、…)

成功

任何2xx 5秒内完成。不跟随重定向,不读取响应正文

交付

至少出现一次,顺序不固定。通过以下方式去除重复项:event_id

POST /hooks/proxy HTTP/1.1
Host: agent.example.com
Content-Type: application/json; charset=utf-8
User-Agent: ProxySeller-Webhooks/1
X-PS-Timestamp: 1762077838
X-PS-Signature: v1=5f0c2a…9e

{"event_id":"evt_01JZ8QA2C4E6G8J0K2M4P6R8T0","type":"order.active","created_at":"2026-11-02T10:03:58Z","api_version":"v1","order_id":"ord_01JZ8Q7K5N2R4T6V8X0Z2B4D6F","wallet":"0x857b06519e91e3a54538791bdbb0e22373e36b66","attempt":1,"data":{"lines":[{"line_id":"1","status":"active","delivered":2,"qty":2}],"expires_at":"2026-12-02T23:59:59Z"}}

验证签名

X-PS-Signature: v1=hex(HMAC-SHA256(key, timestamp + "." + raw_body))

  • key — 全文的字节(UTF-8)webhook_secret 字符串,包括whsec_ 前缀。

  • timestamp — 的价值X-PS-Timestamp,Unix 秒。

  • raw_body — 正文内容完全按收到的原样保留,未进行任何 JSON 解析。

  • 在秘密变更后的24小时内,页眉显示为v1=<current>,v1=<previous>:若其中任何一项匹配,则接受该请求。

  • 若请求的时间戳与您的系统时间相差超过300秒,请拒绝该请求。

Python

import hashlib
import hmac
import time

def verify(secret: str, timestamp: str, signature_header: str, raw_body: bytes, tolerance: int = 300) -> bool:
    if abs(time.time() - int(timestamp)) > tolerance:
        return False
    expected = hmac.new(secret.encode(), timestamp.encode() + b"." + raw_body, hashlib.sha256).hexdigest()
    candidates = [part.strip()[3:] for part in signature_header.split(",") if part.strip().startswith("v1=")]
    return any(hmac.compare_digest(expected, candidate) for candidate in candidates)

Node.js

import crypto from 'node:crypto';

function verify(secret, timestamp, signatureHeader, rawBody, toleranceSeconds = 300) {
  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > toleranceSeconds) return false;
  const expected = crypto.createHmac('sha256', secret).update(`${timestamp}.`).update(rawBody).digest('hex');
  return signatureHeader.split(',').map((s) => s.trim()).filter((s) => s.startsWith('v1='))
    .some((s) => s.length === expected.length + 3
      && crypto.timingSafeEqual(Buffer.from(s.slice(3)), Buffer.from(expected)));
}

重试

规则

价值

初稿

事件发生后至少30秒

重试

1 分钟、5 分钟、30 分钟、2 小时、6 小时、12 小时和 24 小时后:共 8 次尝试,耗时约 44.6 小时

过时

超过约45.6小时的事件将不再发送

断路器

连续失败3次后,该任务链中的其他事件将等待下一次尝试

禁用

如果某次事件的最后一次尝试失败,且自该事件创建以来该 URL 尚未接收任何 webhook,则将禁用向该 URL 的推送。订单本身不受影响。

若要启用已失效的链接,请重新发送该链接并附上更新顺序:同一URL每小时最多更新一次,新URL可随时更新。遗漏的事件始终位于事件顺序.

callback_url 规则

在报价发出时及PATCH。如有违反,将予以退稿400 invalid_request:

规则

详情

可打印的ASCII格式,最多2048个字符

/callback_url: must be a printable URL up to 2048 characters

https 仅

/callback_url: only https is allowed

URL中不得包含登录名或密码

/callback_url: credentials in URL are not allowed

无片段

/callback_url: fragment is not allowed

仅限443端口

/callback_url: only port 443 is allowed

一个公共DNS名称,或一个规范的IPv4地址,或一个用方括号括起的IPv6地址

/callback_url: host must be a public DNS name,/callback_url: IPv4 address must be a canonical dotted quad

主机的所有地址均为公共地址

/callback_url: host resolves to a private or reserved address,/callback_url: host does not resolve

并非该服务本身的主机

/callback_url: host is not allowed

每次交付前都会再次检查 DNS;请求仅发送至已通过检查的地址。

On this page

Webhooks | Proxy-seller