Postback Security
HMAC Hashing & Security
To ensure that all postbacks are genuine and originate strictly from TarGo, each callback request includes a security signature called a hash. This hash is automatically appended to the end of every postback URL we send to prevent data tampering.
How It Works
- Define your URL: You provide your postback URL with placeholders, for example:
https://yourdomain.com/pb?uid={user_id}&reward={payout}
-
Replace Placeholders: TarGo replaces the placeholders (like
{user_id}and{payout}) with actual conversion data. -
Compute the Signature: TarGo calculates the signature using the following logic:
hash=HMAC-SHA1(full_callback_url_with_empty_hash,app_secret_key)
- Final Delivery: TarGo replaces the empty hash parameter with the newly computed hash and sends the final request to your server.
Example Final Request
The final URL sent to your server will look like this:
https://yourdomain.com/pb?uid=abc123&reward=5.00&hash=dbcd6bb892842a52b4fca9bec36cd4b
Verification Logic
To verify the request on your end, you should:
-
Capture the full URL received.
-
Set the
hashvalue to empty. -
Run the same HMAC-SHA1 function using your App Secret Key.
-
Compare your result with the hash provided in the URL. If they match, the request is authentic.
⚠️ Important Notes
| Rule | Description | Why It Matters |
|---|---|---|
| Do NOT modify the URL | Verify the hash using the exact URL string received | Any change results in a different hash and failed validation |
| Do NOT reorder query parameters | Keep parameter order exactly as TarGo sent it | Parameter order affects the hash value |
| Do NOT encode or decode the URL | Do not apply decode, encode, or format conversions | Encoding changes characters and invalidates the hash |
Keephash=** placeholder but empty during verification** | Replace hash=VALUE → hash= before hashing | The hash is computed on the URL with hash value removed |
| Use HMAC-SHA1 Algorithm | Hash is always HMAC-SHA1(URL, secret_key) | Other algorithms will not match the signature |
| Compare using a timing-safe compare | Use hash_equals() / timingSafeEqual() | Prevents timing-based forgery attacks |
| Use full URL including domain | Hash input must include scheme, host, path & query | Hash must match TarGo’s full signed string |
<?php
$secretKey = "YOUR_SECRET_KEY";
// Get the raw POST data
$receivedHash = $_GET['hash'] ?? '';
// Rebuild the full URL excluding the hash
$fullUrl = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? "https" : "http")
. "://{$_SERVER['HTTP_HOST']}{$_SERVER['REQUEST_URI']}";
// Remove the hash value → leave "hash="
$urlWithoutHash = preg_replace('/hash=[A-Za-z0-9]+/i', 'hash=', $fullUrl);
// Calculate expected hash
$expectedHash = hash_hmac('sha1', $urlWithoutHash, $secretKey);
// Compare hashes securely
if (hash_equals($expectedHash, $receivedHash)) {
http_response_code(200);
echo "OK";
} else {
http_response_code(403);
echo "INVALID";
}
import hmac
import hashlib
import re
from urllib.parse import urlparse, parse_qs, urlunparse, urlencode
# === Configuration ===
SECRET_KEY = b"YOUR_SECRET_KEY" # Must be bytes
# Example input URL
full_url = "https://example.com/callback?param1=value1&hash=abc123"
# Parse the URL
parsed = urlparse(full_url)
query_params = parse_qs(parsed.query)
# Get the received hash
received_hash = query_params.get('hash', [''])[0]
# Replace the hash value → leave "hash="
query_params['hash'] = [''] # empty value
new_query = urlencode(query_params, doseq=True)
# Rebuild the URL without the hash
url_without_hash = urlunparse((
parsed.scheme,
parsed.netloc,
parsed.path,
parsed.params,
new_query,
parsed.fragment
))
# Calculate expected HMAC-SHA1 hash
expected_hash = hmac.new(SECRET_KEY, url_without_hash.encode(), hashlib.sha1).hexdigest()
# Compare securely
if hmac.compare_digest(expected_hash, received_hash):
print("OK")
else:
print("INVALID")
IP Whitelisting
You can restrict the callbacks to be accepted only from our sever IP address(es). Please whitelist the following IP(s) and regularly check back to find possible changes
- 15.204.134.17
Last updated 4 weeks ago
Built with Documentation.AI