ApexPy Docs · v1
Dashboard API Keys

Segurança de Webhooks

Cada entrega inclui HMAC-SHA256 do body que de fato vai na rede. São dois headers — a assinatura não vem no formato Stripe t=,v1=.

Headers enviados

X-Apexpy-Signature: 3f1c0a9b…
X-Apexpy-Timestamp: 1735689600
User-Agent: Apexpy-Webhook/1.0
  • X-Apexpy-Timestamp — Unix timestamp (segundos) do envio
  • X-Apexpy-Signature — hex HMAC-SHA256 de {timestamp}.{json}

O JSON usado na assinatura é o mesmo body do POST, com JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE.

Qual secret usar

Webhook global do painel: o secret gerado na criação do webhook. callback_url pontual: a Secret Key da conta (sk_live_… / sk_test_…).

Validação (PHP)

$secret = 'seu_secret';
$payload = file_get_contents('php://input');
$timestamp = $_SERVER['HTTP_X_APEXPY_TIMESTAMP'] ?? '';
$received = $_SERVER['HTTP_X_APEXPY_SIGNATURE'] ?? '';

$expected = hash_hmac('sha256', $timestamp . '.' . $payload, $secret);

if (!hash_equals($expected, $received)) {
    http_response_code(401);
    exit;
}

$event = json_decode($payload, true);

Validação (Node.js)

const crypto = require('crypto');

function validateWebhook(rawBody, signature, timestamp, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(timestamp + '.' + rawBody)
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

Validação (Python)

import hmac, hashlib

def validate_webhook(raw_body: bytes, signature: str, timestamp: str, secret: str) -> bool:
    expected = hmac.new(
        secret.encode(),
        f"{timestamp}.".encode() + raw_body,
        hashlib.sha256,
    ).hexdigest()
    return hmac.compare_digest(expected, signature)

Boas práticas

  • Valide a assinatura antes de processar
  • Recuse timestamps muito antigos (replay)
  • Use HTTPS
  • Responda 200 rápido e processe em background