Security Requirement: Always verify the signature against the exact raw HTTP request body before parsing the JSON body. Your API Secret is used locally to compute the signature and is never transmitted in the request.
Required Request Headers
Every incoming webhook request includes six headers essential for authenticity verification:| Header | Description |
Content-Type | Exact request content-type string used during signature generation. |
X-Client-ID | API Key identifying the signing credential. |
X-Timestamp | Request execution time in Unix milliseconds. |
X-Nonce | 32-character lowercase hexadecimal random string. |
X-Content-Hash | Lowercase SHA-256 hex digest computed over the raw payload body. |
X-Signature | Lowercase HMAC-SHA256 hex signature to compare against. |
📌 Note: X-Signature is not included in canonical headers; it is the target value used for final verification.
Signature Construction (StringToSign)
To calculate the expected signature, construct the StringToSign string by concatenating the seven components below with a single line feed (\n). Do not append a trailing line feed at the end.
Plaintext
Canonicalization Rules
- HTTP Method: Convert to uppercase string (e.g.,
POST). - Canonical Path:
- Include path only (exclude scheme, host, query, and fragment).
- Normalize empty paths to
/. - Preserve trailing slashes.
- Best Practice: Use a simple ASCII path (e.g.,
/webhooks/pagsmile) to prevent escaping mismatches across event families.
- Canonical Query: Lexically sort query parameters by name, format as
name=value(using+for spaces), and join with&. If no query exists, use an empty line. - Canonical Headers: Formatted in lexical order for exact header names:
content-type,x-client-id,x-content-hash,x-nonce,x-timestamp. Trim whitespace and format aslowercase-name:trimmed-value. - Content Hash: SHA-256 computed over exact raw network bytes. Do not parse and re-serialize JSON before hashing.
Verification Pipeline (10-Step Execution Flow)
Follow this strict step-by-step pipeline inside your application middleware:
🛡️ Timing-Attack Protection: Always compare your calculated signature withX-Signatureusing a constant-time comparison function (e.g.,crypto.timingSafeEqualin Node.js orhmac.equalin Python) to prevent side-channel timing attacks.