Developer Documentation
Dashboard
Offerwall & SDK
Web SDK & Iframe Offerwall REST API Themes & Styling 5 Themes Mobile WebView & SDK Android / iOS
API Reference
API Authentication Request Parameters JSON Responses
Webhooks & Security
Reward Apps Postback Global Account Postback HMAC Security & Anti-Fraud Security
Advertisers & Tracking
Direct CPA/CPI Campaigns S2S Postback & MMPs
Monetization Formats
Content Locker Link Locker Banner Display Ads Display CPV Video & CPC Links
Help & Debugging
Error Codes & Debugging
Critical Security Fraud Prevention

HMAC Postback Security & Signature Verification

Protect your rewards economy against spoofing attacks, forged payouts, and replay tampering using HMAC-SHA256 signature verification.

Never Trust Unauthenticated Postbacks!
If your server awards user currency without verifying the secret signature, malicious actors can send fake HTTP requests directly to your postback URL and generate unlimited coins.

1. How AdVanta Signs Postback Requests

Every postback dispatched by AdVanta includes a cryptographic signature generated using your App's Secret Key (found in your Publisher App Settings).

2. Verification Implementation (PHP / Laravel)

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;

class AdVantaPostbackController extends Controller
{
    public function handle(Request $request)
    {
        $appSecret = config('services.advanta.secret_key'); // Your private App Secret
        $incomingSig = $request->header('X-AdVanta-Signature') ?? $request->query('sig');

        if (!$incomingSig) {
            return response('Missing signature', 400);
        }

        // Build verification payload from query params (excluding 'sig')
        $params = $request->except('sig');
        ksort($params);
        $payload = http_build_query($params);

        $expectedSig = hash_hmac('sha256', $payload, $appSecret);

        // Timing-attack safe hash comparison
        if (!hash_equals($expectedSig, $incomingSig)) {
            Log::warning('AdVanta Postback signature mismatch!', ['expected' => $expectedSig, 'got' => $incomingSig]);
            return response('Invalid signature', 403);
        }

        // Signature verified! Extract transaction parameters
        $userId = $request->input('userId');
        $payout = (float) $request->input('payout');
        $reward = (float) $request->input('reward');
        $txId = $request->input('txId');
        $status = $request->input('status'); // 1 = approved, 2 = reversed/charged back

        // Deduplicate against your transactions database
        if (Transaction::where('tx_id', $txId)->exists()) {
            return response('OK', 200); // Already credited, acknowledge with 200
        }

        if ($status == 1) {
            User::where('id', $userId)->increment('balance', $reward);
            Transaction::create([
                'user_id' => $userId,
                'tx_id' => $txId,
                'amount' => $reward,
                'payout' => $payout
            ]);
        }

        return response('OK', 200);
    }
}

3. Verification Implementation (Node.js / Express)

const express = require('express');
const crypto = require('crypto');
const app = express();

const APP_SECRET = process.env.ADVANTA_APP_SECRET;

app.get('/api/postback', (req, res) => {
    const incomingSig = req.headers['x-advanta-signature'] || req.query.sig;
    if (!incomingSig) {
        return res.status(400).send('Missing signature');
    }

    // Sort query keys alphabetically excluding 'sig'
    const query = { ...req.query };
    delete query.sig;
    const sortedKeys = Object.keys(query).sort();
    const payload = sortedKeys.map(k => `${k}=${encodeURIComponent(query[k])}`).join('&');

    const expectedSig = crypto.createHmac('sha256', APP_SECRET).update(payload).digest('hex');

    if (incomingSig !== expectedSig) {
        console.error('Invalid signature detected');
        return res.status(403).send('Forbidden');
    }

    const { userId, reward, txId } = req.query;
    console.log(`Credited user ${userId} with ${reward} points for transaction ${txId}`);

    return res.status(200).send('OK');
});

4. Replay Attack & Timestamp Protection

To prevent replay attacks where a malicious actor re-sends an intercepted valid request: