Payment Links Webhook — Overview#
PIK Payment pushes real-time transaction notifications to your server via HTTPS POST. Whenever a blockchain transaction relevant to your account is detected, confirmed, or fails, our system delivers a signed JSON payload to the webhook URL configured in your API client.This document covers the transport contract: request format, signature verification, retry policy, and the lifecycle (state machine) common to all event types.For the JSON payload and business semantics of each individual event, see the per-event documents:| Document | Event Type | When it fires |
|---|
webhook-customer-payment.md | CUSTOMER_PAYMENT | A customer pays via a payment link to an order address |
webhook-web3-payment.md | WEB3_DIRECT_PAYMENT | A customer pays a payment link directly from a Web3 wallet to the master address |
webhook-master-recharge.md | MASTER_RECHARGE | Funds are deposited directly to the master address (no payment link) |
webhook-collect-out.md | ORDER_COLLECT_OUT | Order address funds are swept into the master address (internal sweep) |
webhook-withdraw-out.md | WITHDRAW_OUT | Withdrawal from the master address to an external destination |
webhook-customer-refund.md | CUSTOMER_REFUND | A refund is issued from an order address back to the payer |
POST {your_webhook_url}
Content-Type: application/json
X-Webhook-Signature: <HMAC-SHA256 hex digest>
X-Webhook-Timestamp: <Unix timestamp in milliseconds>
Common payload envelope#
Every webhook shares the same outer envelope; only data varies by event type.{
"event": "transaction.created",
"timestamp": 1738800000000,
"data": {
"fundEventCode": "FE20260206120000001",
"paymentLinkName": "My Store Payment",
"businessRefType": "PAYMENT",
"chain": "Ethereum",
"tokenSymbol": "USDC",
"tokenAddress": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"txHash": "0xabc123def456...",
"fromAddress": "0x1234567890abcdef...",
"toAddress": "0xfedcba0987654321...",
"amount": 100.00,
"direction": "IN",
"eventType": "CUSTOMER_PAYMENT",
"status": "PENDING",
"createTimeUtc": "2026-02-06 12:00:00"
}
}
Envelope fields#
| Field | Type | Description |
|---|
event | String | Always transaction.created for the current API version |
timestamp | Long | Server time (Unix ms) when the webhook was generated. Used for signature & replay protection |
data | Object | Event-specific payload (see per-event docs) |
Common data fields#
| Field | Type | Description |
|---|
fundEventCode | String | Unique identifier for the fund event. Use this as the idempotency key |
paymentLinkName | String|null | Payment link name (only present for payment events) |
businessRefType | String | High-level business class — see table below |
chain | String | Blockchain network: Ethereum, Tron, BSC, Solana, Polygon, etc. |
tokenSymbol | String | Token symbol: USDC, USDT, ETH, TRX, etc. |
tokenAddress | String | Contract address. Empty string for native tokens (ETH, TRX, etc.) |
txHash | String | On-chain transaction hash |
fromAddress | String | Sender wallet address |
toAddress | String | Receiver wallet address |
amount | BigDecimal | Transaction amount (decimal, in token units, not wei/sat) |
direction | String | IN = funds entering the merchant's PIK wallet; OUT = leaving |
eventType | String | Specific event classification — see per-event docs |
status | String | Lifecycle state — see §3 State Machine |
createTimeUtc | String | UTC creation time, format yyyy-MM-dd HH:mm:ss |
businessRefType reference#
| Value | Description | Direction |
|---|
PAYMENT | Customer-initiated payment to merchant | IN |
COLLECT | Internal sweep from order address to master address | IN (from master's perspective) |
WITHDRAW | Merchant withdrawal to external address | OUT |
REFUND | Refund back to customer | OUT |
2. Signature Verification#
Every request includes an X-Webhook-Signature header. You must verify it before trusting the payload.Algorithm#
signature = HMAC-SHA256(appSecret, timestamp + "." + rawRequestBody)
appSecret — the App Secret assigned in your API client configuration. Never expose it client-side.
timestamp — the value of X-Webhook-Timestamp (treat as a string, do not parse and re-stringify).
rawRequestBody — the raw JSON body as received, byte-for-byte. Do not re-serialize; whitespace and key ordering matter.
Verification steps#
1.
Read X-Webhook-Signature and X-Webhook-Timestamp from headers.
2.
Read the raw request body as a string (before any JSON parsing).
3.
Concatenate timestamp + "." + body.
4.
Compute HMAC-SHA256 with your appSecret as the key.
5.
Compare the result to X-Webhook-Signature using a constant-time comparison.
6.
Reject the request if |now - timestamp| > 5 minutes (replay protection).
Code examples#
3. State Machine#
Every fund event progresses through a status lifecycle. PIK pushes a webhook on every state transition, so a single business transaction may produce multiple webhooks sharing the same fundEventCode. ┌──────────┐ on-chain confirmed ┌────────────┐
│ PENDING │ ───────────────────────► │ CONFIRMED │ (terminal)
└──────────┘ └────────────┘
│
│ on-chain reverted / internal error
▼
┌──────────┐
│ FAILED │ (terminal)
└──────────┘
| Status | Meaning | Next states |
|---|
PENDING | Transaction detected on-chain but not yet finalized. Balance has not been credited/debited yet. | CONFIRMED, FAILED |
CONFIRMED | Transaction reached sufficient confirmations; balance has been updated. Terminal. | — |
FAILED | Transaction reverted on-chain or rejected by an internal validation. Terminal. | — |
Multi-event lifecycle example#
A successful customer payment generates two webhooks:1.
status = PENDING — transaction first detected
2.
status = CONFIRMED — sufficient confirmations reached, balance credited
Both webhooks carry the same fundEventCode. Your handler must be idempotent.Direction & status combinations per event type#
eventType | direction | States that fire |
|---|
CUSTOMER_PAYMENT | IN | PENDING, CONFIRMED, FAILED |
WEB3_DIRECT_PAYMENT | IN | PENDING, CONFIRMED, FAILED |
MASTER_RECHARGE | IN | PENDING, CONFIRMED, FAILED |
ORDER_COLLECT_OUT | IN (funds entering master) | PENDING, CONFIRMED, FAILED |
WITHDRAW_OUT | OUT | PENDING, CONFIRMED, FAILED |
CUSTOMER_REFUND | OUT | PENDING, CONFIRMED, FAILED |
Note on ORDER_COLLECT_OUT: the name reflects the order address perspective (funds leaving the order address), but direction is reported from the merchant master account perspective (IN, because the master's balance grows).
4. Response Requirements#
Return HTTP 2xx (typically 200 OK) to acknowledge receipt.
Respond within 5 seconds. Heavy processing must happen asynchronously after acknowledgement.
Return any non-2xx (or time out) to signal failure; PIK will retry.
Response body content is ignored — only the status code matters.
5. Retry Policy#
| Attempt | Delay after previous failure |
|---|
| 1 | — (immediate) |
| 2 | 1 second |
| 3 | 5 seconds |
After 3 failed deliveries the webhook is permanently marked failed and logged for manual investigation. There is no exponential backoff beyond attempt 3 and no automatic dead-letter retry — make sure your endpoint is highly available, or contact PIK support to manually resend.
6. Configuration#
Configure your webhook on the API Client settings page:| Field | Description |
|---|
webhookUrl | HTTPS URL to receive callbacks. Must accept POST and return 2xx |
webhookStatus | 1 = enabled, 0 = disabled. Webhooks are skipped silently when disabled |
appSecret | Used for HMAC-SHA256 signing. Rotate via the API Client page |
⚠️ HTTPS only. Plain HTTP endpoints are rejected.
7. Best Practices#
1.
Verify signatures before processing — never trust an unsigned or wrongly-signed request.
2.
Use fundEventCode as your idempotency key — the same event may arrive more than once due to retries or state transitions.
3.
Respond fast (< 5s), process asynchronously — push to a queue, return 200, then handle.
4.
Validate timestamp freshness (±5 minutes) to mitigate replay attacks.
5.
Persist every webhook for audit and debugging, including failed-signature attempts.
6.
Handle terminal-status arrival without a prior PENDING — if your endpoint was down during the PENDING delivery, CONFIRMED may be the first webhook you actually see for a given fundEventCode.
7.
Don't rely on delivery order — although PIK sends in transition order, network reordering is possible. Trust status and updateTimeUtc, not arrival order.
Modified at 2026-05-25 08:32:55