Integração Avançada
Webhooks e Sincronização de Dados
Para manter as suas aplicações satélite perfeitamente sincronizadas com o ACAPADEV ID, disponibilizamos um sistema de Webhooks em tempo real. Sempre que um evento importante ocorre no perfil de um utilizador (ou na sua aplicação), enviamos um pedido POST para o seu servidor.
Eventos Disponíveis
Atualmente, o nosso sistema suporta os seguintes eventos:
user.updated- Quando um utilizador atualiza o seu perfil (nome, email, avatar).user.logout- Quando um utilizador termina a sessão globalmente (Single Sign-Out).user.role_updated- Quando o cargo (role) de um utilizador é alterado no portal do desenvolvedor.invoice.paid- Quando uma fatura AcapaPay associada à sua App é paga.partner.created- Quando um novo utilizador é criado através de um convite de parceiro.partner.approved- Quando um convite de parceiro é aprovado.partner.rejected- Quando um convite de parceiro é rejeitado.partner.role_updated- Quando o cargo de um utilizador associado a uma empresa parceira é alterado.ping- Evento sintético enviado pelo botão "Testar Webhook" no Portal do Desenvolvedor, para validar a sua configuração.
Aviso de Segurança: O Segredo do Webhook (Webhook Secret)
Muitos desenvolvedores cometem o erro de tentar validar as assinaturas dos Webhooks utilizando o Client Secret do OAuth. No ACAPADEV ID, por motivos de segurança, os Webhooks utilizam uma chave independente chamada Webhook Secret (ou Signing Secret).
Como Validar a Assinatura (HMAC)
Para garantir que o pedido POST veio efetivamente do ACAPADEV ID e não de um atacante, incluímos os seguintes cabeçalhos em cada webhook:
X-Acapadev-Signature- O HMAC-SHA256 a validar.X-Acapadev-Timestamp- O timestamp Unix usado no cálculo da assinatura.X-Acapadev-Delivery- ID único desta entrega, útil para idempotência e para consultar o histórico no Portal do Desenvolvedor.X-Acapadev-Event- O nome do evento (ex:user.updated), igual ao campoeventdo payload.
A assinatura não é calculada apenas sobre o corpo do pedido — é um HMAC-SHA256 do payload JSON exato concatenado com o timestamp, no formato "{timestamp}.{payload_json}". Isto protege contra ataques de replay.
1. Obter o Webhook Secret
Vá ao Portal do Desenvolvedor, abra a sua App e aceda ao separador Webhooks. Copie o valor do Webhook Secret (geralmente tem 40 caracteres). Coloque este valor no seu ficheiro .env local:
ACAPADEV_WEBHOOK_SECRET=o_seu_webhook_secret_aqui
2. Código de Validação no seu Servidor (Exemplo Laravel)
No seu controlador que recebe o webhook, utilize o seguinte código para validar a assinatura antes de processar o evento:
use Illuminate\Http\Request;
public function handleWebhook(Request $request)
{
// 1. Obter a assinatura e o timestamp enviados pelo ACAPADEV
$signature = $request->header('X-Acapadev-Signature');
$timestamp = $request->header('X-Acapadev-Timestamp');
// 2. Obter o corpo exato (cru) do pedido JSON
$payloadRaw = $request->getContent();
// 3. Obter o Webhook Secret do seu .env
$secret = env('ACAPADEV_WEBHOOK_SECRET');
// 4. Calcular o HMAC-SHA256 sobre "{timestamp}.{payload}"
$expectedSignature = hash_hmac('sha256', $timestamp . '.' . $payloadRaw, $secret);
// 5. Comparar de forma segura
if (!hash_equals($expectedSignature, $signature)) {
abort(401, 'Assinatura Inválida!');
}
// Assinatura válida! Processar o evento...
$payload = json_decode($payloadRaw, true);
$event = $payload['event'];
if ($event === 'user.updated') {
// Atualizar utilizador na sua base de dados local
}
if ($event === 'user.role_updated') {
// Obter o novo cargo (ex: 'admin', 'editor' ou null se revogado)
$newRole = $payload['new_role'];
$userId = $payload['user_id'];
// Exemplo: Atualizar flag de administrador
$localUser = User::where('sso_id', $userId)->first();
if ($localUser) {
$localUser->is_admin = ($newRole === 'admin');
$localUser->save();
}
}
return response()->json(['status' => 'success']);
}
Estrutura do Payload: user.role_updated
Para o evento de atribuição de cargos RBAC, o payload JSON que a sua aplicação receberá será semelhante a este:
{
"event": "user.role_updated",
"app_id": "99a8b7c6-...",
"timestamp": "2026-06-20T12:34:56+00:00",
"user_id": 123,
"email": "[email protected]",
"new_role": "admin"
}
Nota: Se o acesso for revogado pelo desenvolvedor através do portal, o campo new_role será enviado com o valor null.
Dicas de Resolução de Problemas
- As assinaturas não coincidem de forma alguma? Certifique-se de que não está a usar o `Client Secret` em vez do `Webhook Secret`.
- Ainda não coincidem? Verifique se não está a modificar ou a recodificar o JSON com `json_encode()` antes de calcular o Hash — use a string crua (`$request->getContent()`). Confirme também que está a concatenar o timestamp (`X-Acapadev-Timestamp`) antes do payload, no formato exato `"{timestamp}.{payload}"`.