Back to blog
Adal journal

A payment webhook passed HMAC verification after going through Adal

With payment webhooks, “almost unchanged” is not unchanged enough.

Published on
A payment webhook passed HMAC verification after going through Adal

In one of our production workflows, payment webhooks travel through an Adal Server and the Adal CLI before reaching the application that handles them. The application verifies the provider’s HMAC signature at the end of that journey—and the verification succeeds.

That makes this more than a routine delivery. It is a practical end-to-end check that the signed data reached its destination unchanged.

A payment webhook may report a successful charge, a refund, a cancellation, or a change in transaction status. Receiving the request is only part of the job. The application also needs to establish that the webhook came from the expected provider and that the relevant data was not modified in transit.

This is why payment providers commonly sign webhook requests using HMAC.

Why put Adal in the middle?

The request path in this workflow looks like this:

Payment provider → Adal Server → Adal CLI → webhook handler

The Adal Server exposes a stable public HTTPS URL and receives the incoming webhook. Adal stores the Request, and the Adal CLI delivers it to the handler running inside a private environment.

This setup provides a few practical benefits:

  • Adal can receive the webhook before the downstream handler processes it.

  • The incoming Request and its delivery status are visible in the dashboard.

  • A failed delivery can be retried.

  • The private environment does not need a public IP address or an open inbound port.

  • Headers, the request body, and the delivery history remain available for troubleshooting when storage is enabled.

That visibility matters for payment events. If something fails, there is a received Request and a delivery history to inspect instead of a gap between unrelated logs.

But an intermediary in this path comes with an important requirement: it must not silently transform the data used to verify the webhook.

HMAC makes silent changes visible

The exact signing scheme varies by payment provider. A common approach is to calculate an HMAC over the raw request body using a shared secret. Some providers also include a timestamp or other values in the signed message.

The receiving application builds the same message, calculates its own HMAC, and compares the result with the signature sent by the provider.

This process is sensitive to changes in the signed data. If an intermediary parses a JSON body and serializes it again with different whitespace, escaping, or encoding, the bytes will differ and verification will normally fail. The same applies to any other field included in the provider’s signing scheme.

In our production workflow, verification happens only after the request has completed the full path:

receive → store → send through the CLI → deliver → verify HMAC

The HMAC still verifies.

This tells us that the data covered by the payment provider’s signature arrived in the form the handler expected. Adal did not parse and rebuild the JSON, rewrite the payload, or introduce another hidden transformation that would invalidate the signature.

A real end-to-end check

Automated tests can verify payload preservation by sending a known request and comparing what arrives at the destination. Those tests are essential. A real payment webhook adds another useful layer of evidence because it exercises the complete production path:

  • An external system creates the request.

  • That system calculates the signature according to its own rules.

  • A public Adal Server receives the request.

  • Adal stores the Request and sends it through the actual delivery path.

  • The destination application independently verifies the signature.

This is not an isolated test of a serialization function. It checks the behavior of the delivery chain as a whole, using a request produced and signed by a system outside our control.

What this result does—and does not—prove

It is worth being precise about the conclusion.

A successful HMAC check confirms that the values covered by the provider’s signing scheme were preserved. Depending on the provider, that may include the raw body, a timestamp, and selected request metadata.

It does not prove byte-for-byte equality of every part of the HTTP exchange. Transport-level headers may be added or regenerated as a request moves between systems. For example, connection-specific headers are not expected to remain identical across separate HTTP connections.

For the payload and any other signed values, however, successful verification is strong practical evidence that the data survived the delivery path without a signature-breaking change.

Delivery infrastructure should stay out of the way

One of the principles behind Adal is predictable webhook delivery without hidden payload rewriting.

This production workflow demonstrates that principle in a way that is easy to verify. Adal receives the payment webhook, stores the Request, exposes its delivery history, and sends it through the CLI. At the end of the path, the destination still validates the payment provider’s HMAC.

That is what webhook delivery infrastructure should do: add reliability and visibility without changing the data your application relies on.

More from the Adal blog

Explore product updates, webhook guides, and engineering notes.

Back to blog