Infini sends HTTP POST requests to the merchant's pre-configured Webhook address when order status changes, notifying the latest order status and exceptions, facilitating shipping, service activation, inventory unlock, and customer service handling.
Subscription types can be configured in the merchant backend, with different subscription types corresponding to the following events:
- Subscribe to order.create → will receive:
- order.created
- Subscribe to order.update → will receive:
- order.processing
- order.completed
- order.expired
- order.late_payment
Event Type Description:
- order.created: Order created successfully, entering awaiting payment status.
- order.processing: Order entering processing (received partial payment or pending on-chain transaction confirmations).
- order.completed: Order amount satisfied, order status is paid.
- order.expired: Order not completed within validity period, expired.
- order.late_payment: Payment received after order expiration (within 24 hours).
If you just want to check if events are triggered and see the real webhook fields, you can use Webhook Cool testing tool, which provides you with a unique WEBHOOK URL to receive these events.
Webhook request body is in JSON format, including fields:
- event: Event type (e.g. order.created)
- order_id: Unique order identifier
- client_reference: Merchant-side order number (i.e. client_reference)
- amount: Order payable amount (fiat amount)
- currency: Order currency (e.g. USD)
- status: Order status:
- pending
- processing
- paid
- partial_paid
- expired
- amount_confirming: Confirming amount (on-chain transaction exists but has not reached confirmation requirement)
- amount_confirmed: Confirmed amount (on-chain confirmation completed)
- created_at: Order creation time (Unix timestamp, seconds)
- updated_at: Order last update time (Unix timestamp, seconds)
- exception_tags (if any): Order exception tag array (e.g. ["underpaid", "late"]), see "Core Business Concepts" chapter for details
Note:
- amount_confirmed + amount_confirming reflects the total payment amount identified on-chain.
- Order status combined with expiration status, confirmed/confirming amounts constitute the current order semantics.
When sending Webhooks, Infini includes the following HTTP Headers for security verification and idempotent processing:
- Content-Type: application/json
- X-Webhook-Timestamp: Unix timestamp (seconds)
- X-Webhook-Event-Id: Unique event identifier, used for idempotent deduplication
- X-Webhook-Signature: HMAC-SHA256 signature, used for merchant-side signature verification
It is recommended that merchants use X-Webhook-Event-Id for idempotent processing to avoid duplicate consumption of the same event.
The following examples show Webhook content in typical scenarios.
After order creation, status is pending, waiting for user payment.
{
"event": "order.created",
"order_id": "10290d05-8f5c-4ecb-84f0-f78d6f30557f",
"client_reference": "",
"amount": "1",
"currency": "USD",
"status": "pending",
"amount_confirmed": "0",
"amount_confirming": "0",
"created_at": 1763512195,
"updated_at": 1763512195
}Payment received, but transaction still confirming on blockchain.
{
"event": "order.processing",
"order_id": "20290d05-8f5c-4ecb-84f0-f78d6f30557f",
"client_reference": "",
"amount": "1",
"currency": "USD",
"status": "processing",
"amount_confirmed": "0",
"amount_confirming": "0.5",
"created_at": 1763512349,
"updated_at": 1763512403
}Partial payment has been confirmed on blockchain.
{
"event": "order.processing",
"order_id": "20290d05-8f5c-4ecb-84f0-f78d6f30557f",
"client_reference": "",
"amount": "1",
"currency": "USD",
"status": "processing",
"amount_confirmed": "0.5",
"amount_confirming": "0",
"created_at": 1763512349,
"updated_at": 1763512453
}Full payment received and confirmed, order completed.
{
"event": "order.completed",
"order_id": "20290d05-8f5c-4ecb-84f0-f78d6f30557f",
"client_reference": "",
"amount": "1",
"currency": "USD",
"status": "paid",
"amount_confirmed": "1",
"amount_confirming": "0",
"created_at": 1763512349,
"updated_at": 1763512573
}Order timeout without receiving any payment.
{
"event": "order.expired",
"order_id": "10290d05-8f5c-4ecb-84f0-f78d6f30557f",
"client_reference": "",
"amount": "1",
"currency": "USD",
"status": "expired",
"amount_confirmed": "0",
"amount_confirming": "0",
"created_at": 1763512195,
"updated_at": 1763512255
}Order timeout but received partial payment, not reaching order amount.
{
"event": "order.expired",
"order_id": "60290d05-8f5c-4ecb-84f0-f78d6f30557f",
"client_reference": "",
"amount": "1",
"currency": "USD",
"status": "partial_paid",
"amount_confirmed": "0.5",
"amount_confirming": "0",
"created_at": 1763514565,
"updated_at": 1763514765
}Payment received within 24 hours after order expiration.
{
"event": "order.late_payment",
"order_id": "30290d05-8f5c-4ecb-84f0-f78d6f30557f",
"client_reference": "",
"amount": "1",
"currency": "USD",
"status": "expired",
"amount_confirmed": "1",
"amount_confirming": "0",
"created_at": 1763512622,
"updated_at": 1763512815
}Tips:
- In late_payment scenarios, order status remains expired, but amount_confirmed has reached the order amount. Merchants can decide whether to ship or refund based on business strategy.
- It is recommended to make business decisions combined with exception tags (such as late, underpaid, overpaid).
Infini will initiate POST requests to your configured Webhook URL. It is recommended that the receiving end follows these principles:
- Verify that all required Headers exist:
- X-Webhook-Signature
- X-Webhook-Timestamp
- X-Webhook-Event-Id
- Verify signature legitimacy (see next section).
- Implement idempotent processing based on X-Webhook-Event-Id (process only once).
- Business logic should be processed asynchronously, quickly return HTTP 200 to avoid timeout.
Verification steps:
- Read from Headers:
- X-Webhook-Signature (signature)
- X-Webhook-Timestamp (timestamp)
- X-Webhook-Event-Id (event ID)
- Get raw request body string payload.
- Assemble signature content string:
{timestamp}.{event_id}.{payload}- Use your WEBHOOK_SECRET for HMAC-SHA256 calculation:
signed_content = f"{timestamp}.{event_id}.{payload}"
expected_sig = hmac.new(
WEBHOOK_SECRET.encode(),
signed_content.encode(),
hashlib.sha256
).hexdigest()- Compare expected_sig with X-Webhook-Signature for consistency.
@app.route('/webhook', methods=['POST'])
def webhook_verification():
signature = request.headers.get('X-Webhook-Signature')
timestamp = request.headers.get('X-Webhook-Timestamp')
event_id = request.headers.get('X-Webhook-Event-Id')
if not all([signature, timestamp, event_id]):
return jsonify({"error": "Missing required headers"}), 400
payload = request.get_data(as_text=True)
signed_content = f"{timestamp}.{event_id}.{payload}"
expected_sig = hmac.new(
WEBHOOK_SECRET.encode(),
signed_content.encode(),
hashlib.sha256
).hexdigest()
if expected_sig != signature:
return jsonify({"error": "Invalid signature"}), 401
# Process valid webhook
return jsonify({"status": "ok"})If the merchant does not return HTTP 200, Infini will retry the event.
- Maximum retries: 8 times
- First 3 retry intervals: 30 seconds
- 4th-8th retries use incremental backoff strategy, example:
| Attempt | Description | Interval (Example) |
|---|---|---|
| 1st | First send | Immediate |
| 2nd | 1st failed | 30 seconds |
| 3rd | 2nd failed | 30 seconds |
| 4th | 3rd failed | 30 seconds |
| 5th | 4th failed | 60 seconds |
| 6th | 5th failed | 120 seconds |
| 7th | 6th failed | 240 seconds |
| 8th | 7th failed | 480 seconds |
If multiple retries still fail, the event will be marked as delivery failed. It is recommended that merchants investigate through logs and reconciliation tools.