Appearance
API Signature Verification
When our system sends a callback request to your endpoint, the request will include a signature header so you can verify the request is authentic and has not been tampered with.
3.1 Integration Information
The system provides the following information to the integrated partner:
| Property | Type | Description |
|---|---|---|
signatureKey | string | A secret key (UUID format) used for HMAC-SHA256 signature generation from Back Office. |
3.2 Request Headers
Each callback request includes the following headers. Use both values to verify the signature on your server:
| Header | Type | Description |
|---|---|---|
sapi-timestamp | string | Unix timestamp in milliseconds sent by the system at request time. |
sapi-signature | string | HMAC-SHA256 hex digest of the signing string, using signatureKey as the secret. |
3.3 Signature Generation
The signature is generated using HMAC-SHA256.
Signing String
Build the signing string by concatenating the sapi-timestamp header value and the raw JSON body string:
signingString = rawBodyString + "." + sapi-timestamp| Part | Type | Description |
|---|---|---|
rawBodyString | string | The raw JSON-serialized request body (before any parsing). |
sapi-timestamp | string | Value taken directly from the sapi-timestamp request header. |
HMAC-SHA256
signature = HMAC-SHA256(secret=signatureKey, data=signingString).hex()3.4 Examples
Raw Data
| Property | Value |
|---|---|
signatureKey | xxxxxxxxx-xxxx-xxxx-xxxx-xxxxx |
sapi-timestamp | 1776929280534 |
| Request Body (JSON) | {"id":"1db0f513-a31f-4afa-9def-fdd6d2398c22","currency":"THB","productId":"5G_GAMES","timestampMillis":1776929280534,"username":"testaoo0012"} |
Step 1 — Serialize the request body
rawBodyString = '{"id":"1db0f513-a31f-4afa-9def-fdd6d2398c22","currency":"THB","productId":"5G_GAMES","timestampMillis":1776929280534,"username":"testaoo0012"}'Step 2 — Build the signing string
signingString = rawBodyString + "." + "1776929280534"
= "{"id":"1db0f513-a31f-4afa-9def-fdd6d2398c22","currency":"THB","productId":"5G_GAMES","timestampMillis":1776929280534,"username":"testaoo0012"}.1776929280534"Step 3 — Compute HMAC-SHA256
signature = HMAC-SHA256(
secret = "xxxxxxxxx-xxxx-xxxx-xxxx-xxxxx",
data = signingString
).hex()3.5 Signature Example Code
JavaScript / Node.js
javascript
const crypto = require("crypto");
const buildSigningString = (timestamp, rawBody) => {
return `${rawBody}.${timestamp}`;
};
const generate = (secret, timestamp, rawBody) => {
const data = buildSigningString(timestamp, rawBody);
return crypto.createHmac("sha256", secret).update(data).digest("hex");
};
// --- Example usage ---
const signatureKey = "xxxxxxxxx-xxxx-xxxx-xxxx-xxxxx";
const timestamp = "1776929280534";
const requestBody = {
id: "1db0f513-a31f-4afa-9def-fdd6d2398c22",
currency: "THB",
productId: "5G_GAMES",
timestampMillis: 1776929280534,
username: "testaoo0012",
};
const rawBody = JSON.stringify(requestBody);
const signature = generate(signatureKey, timestamp, rawBody);
return signature;PHP
php
<?php
function buildSigningString(string $timestamp, string $rawBody): string {
return $rawBody . '.' . $timestamp;
}
function generateSignature(string $signatureKey, string $timestamp, string $rawBody): string {
$signingString = buildSigningString($timestamp, $rawBody);
return hash_hmac('sha256', $signingString, $signatureKey);
}
// --- Example usage ---
$signatureKey = 'xxxxxxxxx-xxxx-xxxx-xxxx-xxxxx';
$timestamp = '1776929280534';
$requestBody = [
'id' => '1db0f513-a31f-4afa-9def-fdd6d2398c22',
'currency' => 'THB',
'productId' => '5G_GAMES',
'timestampMillis' => 1776929280534,
'username' => 'testaoo0012',
];
// Verification: read timestamp from header
$timestamp = $_SERVER['HTTP_SAPI_TIMESTAMP'];
$received = $_SERVER['HTTP_SAPI_SIGNATURE'];
$rawBody = file_get_contents('php://input');
$expected = generateSignature($signatureKey, $timestamp, $rawBody);
$valid = hash_equals($expected, $received);
if (!$valid) {
http_response_code(401);
echo json_encode(['message' => 'Invalid signature']);
exit;
}3.6 Verification Flow
When receiving a callback request, follow these steps:
- Read
sapi-timestampandsapi-signaturefrom the request headers. - Get the raw (unparsed) request body string.
- Build the signing string:
rawBody + "." + sapi-timestamp. - Re-compute HMAC-SHA256 using your
signatureKey. - Compare with
sapi-signatureusing constant-time comparison to prevent timing attacks. - If the signatures do not match, respond with
statusCode: 30002(Invalid Signature).
Headers received:
sapi-timestamp: 1776929280534
sapi-signature: <hex string>
Verification:
signingString = "1776929280534." + rawBody
expected = HMAC-SHA256(signatureKey, signingString).hex()
valid = timingSafeEqual(expected, sapi-signature)
If not valid → respond with statusCode 30002See Common Status Code for the full list of status codes.
