跳转到内容

商户 Webhook

查看 .md
服务地址
https://{merchantWebhookHost}
Merchant-owned delivery endpoint configured for the account.
认证
Ed25519 签名
版本
1.1.0

Canonical outbound contract for PSP merchant events delivered to merchant-owned HTTPS endpoints. The request body is one of five envelopes; route on the X-Webhook-Event header first.

  • ClassifiedMerchantWebhookEnvelope: payment intent, transfer and credit card lifecycle events. data always carries classification schema version 1 and the resolved public payment classification.
  • UnclassifiedMerchantWebhookEnvelope: refund and account freeze lifecycle events. data cannot carry classification fields.
  • MerchantSettlementWebhookEnvelope: merchant_settlement.*.v1 events for settlement items. Different top-level fields; amounts are decimal strings.
  • WithdrawalStatusWebhookEnvelope and SettlementStatusWebhookEnvelope: flat status bodies sent by the withdrawal and settlement workflows.

Amounts are integers in the currency’s minor unit unless a schema states otherwise. Fields documented as aliases always carry the same value. Only the data objects of ClassifiedMerchantWebhookEnvelope and UnclassifiedMerchantWebhookEnvelope stay open (additionalProperties: true) so PSP can add fields without a new contract version. MerchantSettlementWebhookEnvelope, its MerchantSettlementEventData, both flat status envelopes, the classified and unclassified envelopes themselves, InvoicePaymentPolicy and ResolvedPaymentClassification reject undeclared fields. Retries and operator redeliveries resend the stored body unchanged, so fields introduced later may be absent from older events. A callback regenerated by an operator refresh is stored and resent the same way; bodies stored before a refresh rebuilt the complete event can carry only a subset of the fields documented as required for first deliveries (for example classification on payment events), and may omit fields that are optional for first deliveries as well (for example the refund fee_amount or original_intent_type). Stable taxonomy IDs, direction, provenance, aliases, defaults, and legacy flat classification fields are intentionally not exposed.

EVENTmerchantEvent

PSP signs the exact request body as <X-Timestamp>.<raw-body> with the account-level Ed25519 signing key identified by X-Webhook-Key-ID. Verify the signature and timestamp window before parsing the body, and deduplicate by X-Webhook-Event-ID.

请求头

名称 类型 必填 说明
X-Timestamp string 必填 Unix time in seconds (UTC) at which PSP signed this HTTP attempt. It is regenerated on every attempt (automatic retry, merchant manual retry and operator redelivery), so it is neither the event time nor a deduplication key. Reject requests whose value differs from the receiver’s clock by more than 300 seconds. · 格式 ^[0-9]+$
X-Signature string 必填 Standard Base64 encoding of the 64-byte Ed25519 signature over the ASCII bytes <X-Timestamp>.<raw request body>. It is recomputed on every attempt because X-Timestamp changes. Verify against the raw body bytes, never a re-serialized JSON document. · 最短 1
X-Signature-Algorithm string 必填 Signature algorithm; always ed25519. · 固定值 ed25519
X-Webhook-Key-ID string 必填 Identifier of the account-level signing key bound to this delivery. All attempts of a delivery, including operator redeliveries of it, are signed with the originally bound key. Select the public key by this ID and keep retired keys available during rotation. · 最短 1
X-Webhook-Version string 必填 Version of the delivery header and signing contract, fixed to 2026-06-11. It does not version the body; merchant settlement bodies carry their own schema_version. · 固定值 2026-06-11
X-Webhook-ID string<uuid> 必填 Delivery ID. It stays the same across automatic retries and merchant manual retries of one delivery. An operator redelivery creates a new delivery with a new X-Webhook-ID while keeping the same X-Webhook-Event-ID and body. Use it to correlate attempts with PSP delivery records, not for business deduplication.
X-Webhook-Event string 必填 Event type of the body: the body event for event/data envelopes, or the body event_type for merchant settlement, withdrawal and settlement status bodies. Route on this header before parsing the body; record and acknowledge event types you do not handle. · 最短 1
X-Webhook-Event-ID string<uuid> 必填 Event ID. It never changes across attempts, manual retries or operator redeliveries of the same event and is the recommended deduplication key. For merchant settlement events it equals the body event_id; for other events it is a stable PSP-derived ID that is not repeated in the body.
X-Merchant-Account-ID string<uuid> 必填 Merchant account the delivery belongs to; identical on every attempt. Use it to scope events whose data has no account ID, such as refund completion events.

请求体 application/json · 必填

变体 ClassifiedMerchantWebhookEnvelope

字段 类型 必填 说明
event enum 必填 可选值:payment.intent.completed、payment.intent.partially_paid、payment.intent.expired、payment.intent.canceled、payment.transfer.completed、payment.transfer.failed、payment.transfer.returned、creditcard_invoice.captured、creditcard_invoice.3ds_timeout
timestamp string<date-time> 必填 Time the event occurred (RFC 3339, may include fractional seconds and an offset).
data object 必填 —
data.classification_schema_version integer 必填 固定值 1
data.classification object 必填 —
data.classification.classification_status string 必填 固定值 resolved
data.classification.payment_category string 必填 格式 ^[a-z0-9]+(?:_[a-z0-9]+)*$
data.classification.payment_method string 必填 格式 ^[a-z0-9]+(?:_[a-z0-9]+)*$
data.classification.rail_variant string 必填 格式 ^[a-z0-9]+(?:_[a-z0-9]+)*$

变体 UnclassifiedMerchantWebhookEnvelope

字段 类型 必填 说明
event enum 必填 可选值:payment.refund.completed、payment.transfer_refund.completed、payment.refund.failed、account.frozen、account.unfrozen
timestamp string<date-time> 必填 Time the event occurred (RFC 3339, may include fractional seconds and an offset).
data object 必填 Merchant event data for event types that do not carry payment classification.
data.classification_schema_version — 可选 该事件不会包含此字段
data.classification — 可选 该事件不会包含此字段

变体 MerchantSettlementWebhookEnvelope

字段 类型 必填 说明
schema_version string 必填 Body schema version. · 固定值 1.0.0
event_id string<uuid> 必填 Original settlement event ID (UUIDv7); the deduplication key.
event_type enum 必填 Settlement event type. · 可选值:merchant_settlement.completed.v1、merchant_settlement.adjusted.v1、merchant_settlement.reversed.v1
occurred_at string<date-time> 必填 Event time in UTC. · 格式 ^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{1,9})?Z$
aggregate_type string 必填 Aggregate type; always merchant_settlement_item. · 固定值 merchant_settlement_item
aggregate_id string<uuid> 必填 Settlement item ID; equals data.merchant_item_id.
aggregate_version integer 必填 Settlement item version that produced the event. · ≥ 1
data object 必填 Public settlement item projection. Amounts are non-negative decimal strings in minor units, never JSON numbers, and satisfy gross_amount = merchant_fee_amount + net_amount.
data.merchant_item_id string<uuid> 必填 Settlement item ID.
data.merchant_account_id string<uuid> 必填 Merchant account ID; equals X-Merchant-Account-ID.
data.source_type enum 必填 Source of the settlement item. merchant_settlement.completed.v1 is only delivered for invoice items; adjusted and reversed events use adjustment and reversal respectively. · 可选值:invoice、transfer、refund、chargeback、adjustment、reversal
data.status enum 必填 Item status; settled, adjusted or reversed for the respective event type. · 可选值:settled、adjusted、reversed
data.gross_amount string 必填 Gross amount as a decimal string in minor units. · 格式 ^(0|[1-9][0-9]*)$
data.merchant_fee_amount string 必填 Merchant fee as a decimal string in minor units. · 格式 ^(0|[1-9][0-9]*)$
data.net_amount string 必填 Net amount as a decimal string in minor units. · 格式 ^(0|[1-9][0-9]*)$
data.currency string 必填 ISO 4217 currency code. · 格式 ^[A-Z]{3}$
data.due_at string<date-time> 必填 Time the item became due for settlement, in UTC. · 格式 ^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{1,9})?Z$
data.settled_at string<date-time> 必填 Time the item was settled, in UTC. · 格式 ^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{1,9})?Z$
data.parent_item_id string,null<uuid> 必填 Parent settlement item for adjustments and reversals; null otherwise.
data.relation_type enum 必填 Relation to parent_item_id; null when there is no parent. · 可选值:adjustment、reversal、null

变体 WithdrawalStatusWebhookEnvelope

字段 类型 必填 说明
event_type enum 必填 Withdrawal event type; equals X-Webhook-Event. · 可选值:withdrawal.completed、withdrawal.failed
withdrawal_id string<uuid> 必填 PSP withdrawal ID.
status enum 必填 Terminal withdrawal status. · 可选值:completed、failed
timestamp string<date-time> 必填 Workflow time of the status change, UTC with second precision.
amount string 可选 Withdrawal amount as a decimal string; present only when populated.
currency string 可选 ISO 4217 currency code; present only when populated.
channel_ref string 可选 Channel reference; present only when populated.
e2e_id string 可选 End-to-end payment identifier; present only when populated.
error_code string 可选 Failure code; present only when populated.
error_message string 可选 Failure message; present only when populated.

变体 SettlementStatusWebhookEnvelope

字段 类型 必填 说明
event_type enum 必填 Settlement event type; equals X-Webhook-Event. · 可选值:settlement.completed、settlement.failed、settlement.rejected、settlement.canceled
settlement_id string<uuid> 必填 PSP settlement ID.
status enum 必填 Settlement status. · 可选值:completed、failed、rejected、canceled
timestamp string<date-time> 必填 Workflow time of the status change, UTC with second precision.
net_amount string 必填 Settlement amount as a decimal string of integer minor units. · 格式 ^-?(0|[1-9][0-9]*)$
currency string 可选 ISO 4217 currency code; omitted when the settlement has no currency.

请求示例

商户收到的请求
curl -X POST 'https://<merchantWebhookHost>/<your-webhook-path>' \
-H 'X-Timestamp: <timestamp>' \
-H 'X-Signature: <signature>' \
-H 'X-Signature-Algorithm: <signature-algorithm>' \
-H 'X-Webhook-Key-ID: <webhook-key-id>' \
-H 'X-Webhook-Version: <webhook-version>' \
-H 'X-Webhook-ID: <webhook-id>' \
-H 'X-Webhook-Event: <webhook-event>' \
-H 'X-Webhook-Event-ID: <webhook-event-id>' \
-H 'X-Merchant-Account-ID: <merchant-account-id>' \
-H 'Content-Type: application/json' \
-d '{
"event": "payment.intent.completed",
"timestamp": "2026-01-01T00:00:00Z",
"data": {
"classification_schema_version": 0,
"classification": {
"classification_status": "string",
"payment_category": "string",
"payment_method": "string",
"rail_variant": "string"
}
}
}'

响应

Event accepted.