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 transaction.created

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: IN or OUT

eventType

String

Event classification

status

String

Transaction status

createTimeUtc

String

UTC time (yyyy-MM-dd HH:mm:ss)

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 X-Webhook-Timestamp header

requestBody

Raw JSON request body

signature

Lowercase hex-encoded HMAC digest

Verification Steps

  1. Read X-Webhook-Signature and X-Webhook-Timestamp

  2. Read the raw request body as a string

  3. Concatenate: timestamp + "." + requestBody

  4. Compute HMAC-SHA256 using appSecret

  5. Compare with X-Webhook-Signature

  6. (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

1 = enabled, 0 = disabled

app_secret

Used to generate webhook signatures

Best Practices

  • Always verify webhook signatures

  • Process requests quickly and asynchronously

  • Use fundEventCode as an idempotency key

  • Use HTTPS only

  • Validate timestamp freshness

  • Log all webhook requests for audit and debugging