---
title: "Weryfikacja podpisu webhooka"
description: "Jak sprawdzać nagłówek X-Infakt-Signature, żeby przyjmować tylko prawdziwe zdarzenia od inFakt."
---

> Documentation Index
> Fetch the complete documentation index at: https://docs.niebieskiorzel.pl/llms.txt
> Use this file to discover all available pages before exploring further.

# Weryfikacja podpisu webhooka

Podpis to HMAC-SHA256 surowej treści żądania, wyliczony sekretem webhooka i zapisany w postaci szesnastkowej. Weryfikacja polega na samodzielnym wyliczeniu podpisu i porównaniu go z nagłówkiem `X-Infakt-Signature`.

## Wymagania

- Aktywny webhook i jego sekret w zmiennej `INFAKT_WEBHOOK_SECRET` ([Dodawanie i aktywacja webhooka](/poradniki/webhooki/dodaj-webhook/)).

## Kroki

1. **Odczyt surowej treści żądania**

   Podpis wylicza się z treści w postaci, w jakiej przyszła, zanim framework sparsuje JSON. JSON zapisany ponownie ma inną postać i inny podpis.
2. **Porównanie podpisu**

   ```js
   import crypto from "node:crypto";
   import express from "express";

   const app = express();
   app.post("/webhooks/infakt", express.raw({ type: "application/json" }), (req, res) => {
     const expected = crypto
       .createHmac("sha256", process.env.INFAKT_WEBHOOK_SECRET)
       .update(req.body)
       .digest("hex");
     const received = req.get("X-Infakt-Signature") ?? "";
     const valid =
       received.length === expected.length &&
       crypto.timingSafeEqual(Buffer.from(received), Buffer.from(expected));
     if (!valid) return res.sendStatus(401);

     const payload = JSON.parse(req.body.toString("utf8"));
     // Obsłuż payload.event i payload.resource.
     res.sendStatus(200);
   });
   ```

   ```python
   import hashlib
   import hmac
   import os

   from flask import Flask, abort, request

   app = Flask(__name__)

   @app.post("/webhooks/infakt")
   def infakt_webhook():
       raw = request.get_data()
       expected = hmac.new(os.environ["INFAKT_WEBHOOK_SECRET"].encode(), raw, hashlib.sha256).hexdigest()
       received = request.headers.get("X-Infakt-Signature", "")
       if not hmac.compare_digest(expected, received):
           abort(401)
       payload = request.get_json()
       # Obsłuż payload["event"] i payload["resource"].
       return "", 200
   ```

   ```php
   <?php
   $raw = file_get_contents('php://input');
   $expected = hash_hmac('sha256', $raw, getenv('INFAKT_WEBHOOK_SECRET'));
   $received = $_SERVER['HTTP_X_INFAKT_SIGNATURE'] ?? '';

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

   $payload = json_decode($raw, true);
   // Obsłuż $payload['event'] i $payload['resource'].
   http_response_code(200);
   ```

   ```ruby
   require "openssl"

   def verify_infakt_signature(request)
     raw = request.body.read
     expected = OpenSSL::HMAC.hexdigest("SHA256", ENV.fetch("INFAKT_WEBHOOK_SECRET"), raw)
     received = request.headers["X-Infakt-Signature"].to_s
     ActiveSupport::SecurityUtils.secure_compare(expected, received)
   end
   ```

   ```csharp
   using System.Security.Cryptography;
   using System.Text;

   app.MapPost("/webhooks/infakt", async (HttpRequest request) =>
   {
       using var reader = new StreamReader(request.Body, Encoding.UTF8);
       var raw = await reader.ReadToEndAsync();
       var secret = Environment.GetEnvironmentVariable("INFAKT_WEBHOOK_SECRET")!;

       var hash = HMACSHA256.HashData(Encoding.UTF8.GetBytes(secret), Encoding.UTF8.GetBytes(raw));
       var expected = Convert.ToHexString(hash).ToLowerInvariant();
       var received = request.Headers["X-Infakt-Signature"].ToString().ToLowerInvariant();

       var valid = CryptographicOperations.FixedTimeEquals(
           Encoding.UTF8.GetBytes(expected), Encoding.UTF8.GetBytes(received));
       return valid ? Results.Ok() : Results.Unauthorized();
   });
   ```
3. **Odrzucenie niepoprawnych żądań**

   Przy niezgodnym podpisie aplikacja odpowiada kodem `401` i nie przetwarza zdarzenia.

## Weryfikacja

Po wystawieniu testowej faktury endpoint powinien przyjąć zdarzenie i odpowiedzieć kodem `200`. Następnie można wysłać ręcznie żądanie z nieprawidłowym podpisem:

```sh
curl -i -X POST "https://<twój-adres>/webhooks/infakt" \
  -H "Content-Type: application/json" \
  -H "X-Infakt-Signature: 0000" \
  -d '{"event":{"name":"invoice_paid"}}'
```

Odpowiedź `401` oznacza, że weryfikacja działa poprawnie.

## Następne kroki

- [Podpis webhooka](/koncepcje/podpis-webhooka/)
- [Dostarczanie webhooków](/koncepcje/dostarczanie-webhookow/)

Source: https://docs.niebieskiorzel.pl/poradniki/webhooki/zweryfikuj-podpis/index.mdx
