Webhook Overview
PIK Payment uses webhooks to send real-time transaction event notifications to merchant systems.
When a blockchain transaction is detected and recorded, PIK will automatically send an HTTP POST request to the webhook URL configured by the merchant.
Webhooks allow you to receive payment status updates without polling the API.
Webhook HTTP Request
Request Method & Headers
POST {merchant_webhook_url}
Content-Type: application/json
X-Webhook-Signature: <HMAC-SHA256 hex digest>
X-Webhook-Timestamp: <Unix timestamp in milliseconds>
|
Header |
Description |
|---|---|
|
X-Webhook-Signature |
HMAC-SHA256 signature for request verification |
|
X-Webhook-Timestamp |
Unix timestamp (milliseconds) when the webhook was generated |
Request Body (JSON)
{
"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"
}
}
Field Descriptions
Top-Level Fields
|
Field |
Type |
Description |
|---|---|---|
|
event |
String |
Webhook event type. Currently fixed as |
|
timestamp |
Long |
Webhook generation time in Unix milliseconds |
|
data |
Object |
Transaction event payload |
Transaction Data Fields (data)
|
Field |
Type |
Description |
|---|---|---|
|
fundEventCode |
String |
Unique identifier of the fund event |
|
paymentLinkName |
String |
Payment link name (nullable) |
|
businessRefType |
String |
Business category |
|
chain |
String |
Blockchain network name |
|
tokenSymbol |
String |
Token symbol |
|
tokenAddress |
String |
Token contract address (empty for native tokens) |
|
txHash |
String |
Blockchain transaction hash |
|
fromAddress |
String |
Sender wallet address |
|
toAddress |
String |
Receiver wallet address |
|
amount |
BigDecimal |
Transaction amount |
|
direction |
String |
Fund direction: |
|
eventType |
String |
Event classification |
|
status |
String |
Transaction status |
|
createTimeUtc |
String |
UTC time ( |
Event Types (eventType)
|
Value |
Description |
|---|---|
|
CUSTOMER_PAYMENT |
Customer payment via payment link |
|
WEB3_DIRECT_PAYMENT |
Direct Web3 wallet payment |
|
MASTER_RECHARGE |
Master account recharge |
|
ORDER_COLLECT_OUT |
Settlement / collection outbound transfer |
|
WITHDRAW_OUT |
Withdrawal outbound transfer |
|
CUSTOMER_REFUND |
Customer refund |
Business Reference Types (businessRefType)
|
Value |
Description |
|---|---|
|
PAYMENT |
Payment transaction |
|
COLLECT |
Collection / settlement |
|
WITHDRAW |
Withdrawal |
|
REFUND |
Refund |
Signature Verification
Each webhook request includes an HMAC-SHA256 signature to ensure authenticity and integrity.
Signature Formula
signature = HMAC-SHA256(appSecret, timestamp + "." + requestBody)
|
Parameter |
Description |
|---|---|
|
appSecret |
Merchant APP Secret |
|
timestamp |
Value from |
|
requestBody |
Raw JSON request body |
|
signature |
Lowercase hex-encoded HMAC digest |
Verification Steps
-
Read
X-Webhook-SignatureandX-Webhook-Timestamp -
Read the raw request body as a string
-
Concatenate:
timestamp + "." + requestBody -
Compute HMAC-SHA256 using
appSecret -
Compare with
X-Webhook-Signature -
(Recommended) Reject requests older than 5 minutes
Code Examples
Java
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
public static String computeSignature(String appSecret, String timestamp, String body) throws Exception {
String message = timestamp + "." + body;
Mac mac = Mac.getInstance("HmacSHA256");
SecretKeySpec keySpec = new SecretKeySpec(appSecret.getBytes("UTF-8"), "HmacSHA256");
mac.init(keySpec);
byte[] hash = mac.doFinal(message.getBytes("UTF-8"));
StringBuilder hex = new StringBuilder();
for (byte b : hash) {
hex.append(String.format("%02x", b & 0xff));
}
return hex.toString();
}
Python
import hmac
import hashlib
def verify_signature(app_secret, timestamp, body, received_signature):
message = f"{timestamp}.{body}"
expected = hmac.new(
app_secret.encode('utf-8'),
message.encode('utf-8'),
hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, received_signature)
Node.js
const crypto = require('crypto');
function verifySignature(appSecret, timestamp, body, receivedSignature) {
const message = `${timestamp}.${body}`;
const expected = crypto.createHmac('sha256', appSecret)
.update(message, 'utf8')
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(receivedSignature)
);
}
Webhook Response Requirements
-
Your endpoint must return HTTP 200
-
Any non-2xx response will be treated as a failure
Retry Policy
|
Attempt |
Delay |
|---|---|
|
1st |
Immediate |
|
2nd |
1 second |
|
3rd |
5 seconds |
After 3 failed attempts, no further retries will be made.
Configuration
Webhook settings are configured in the merchant API configuration page.
|
Field |
Description |
|---|---|
|
webhook_url |
HTTPS endpoint to receive webhooks |
|
webhook_status |
|
|
app_secret |
Used to generate webhook signatures |
Best Practices
-
Always verify webhook signatures
-
Process requests quickly and asynchronously
-
Use
fundEventCodeas an idempotency key -
Use HTTPS only
-
Validate timestamp freshness
-
Log all webhook requests for audit and debugging