Skip to content

Fix webhook signature verification for payloads containing special characters #351

@sagar-rai

Description

@sagar-rai

In certain environments, such as Scala Play 2.8/2.9, Utils.verifyWebhookSignature may return false for valid webhook requests when the payload contains special characters or multi-byte text. The issue appears to come from the SDK accepting only a String, while framework-level parsing can alter the raw request body before verification.

Problem

Utils.verifyWebhookSignature currently accepts only a String.

This issue is especially likely when a user uses a framework such as Play to read the request body into a String before passing it to the SDK, since webhook validation is safest when performed on the raw body bytes rather than a parsed or re-encoded string.

Reproduction

  1. Receive a Razorpay webhook containing special characters or multi-byte characters in fields such as customer name or notes.
  2. Parse the HTTP body as a String in Scala Play.
  3. Pass the resulting string to Utils.verifyWebhookSignature(...).
  4. Observe that the method returns false even though the webhook is valid.

Proposed Fix

1. Add an overload that accepts raw bytes

Introduce a new method in Utils that accepts byte[] so developers can pass the raw request body directly:

public static boolean verifyWebhookSignature(byte[] payload, String expectedSignature, String webhookSecret) throws RazorpayException {
    // Use raw bytes directly to avoid encoding issues
}

2. Use UTF-8 explicitly in string-based hashing

Update the existing implementation so string payloads are always converted using UTF-8:

// Current
byte[] hash = sha256_HMAC.doFinal(payload.getBytes());

// Proposed
byte[] hash = sha256_HMAC.doFinal(payload.getBytes(UTF-8));
//Similar charset as used for encoding the webhook secret to bytes

This makes the behavior deterministic across environments and avoids platform-dependent failures.

Why This Matters

Razorpay’s webhook validation flow depends on the exact payload used to generate the HMAC signature. Any transformation of the request body — including charset conversion, normalization, or parsing into a framework-specific string type — can break verification. A byte-array overload would let developers verify the webhook against the exact raw request body, which is the safest approach for cryptographic checks.

Related Reference

A similar webhook signature verification issue has already been documented here Python SDK, Issue 65, where valid webhook signatures failed to verify in real-world setups.

I am happy to submit a PR for this if the approach is approved.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions