razorpay / razorpay/razorpay-java
Fix webhook signature verification for payloads containing special characters
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 74
- Forks
- 83
- PR merge metrics
- No merged PRs in 30d
Description
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
- Receive a Razorpay webhook containing special characters or multi-byte characters in fields such as customer name or notes.
- Parse the HTTP body as a
Stringin Scala Play. - Pass the resulting string to
Utils.verifyWebhookSignature(...). - Observe that the method returns
falseeven 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.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start at Utils.verifyWebhookSignature and inspect how the current String payload and webhook secret are converted before HMAC verification. Reproduce the Scala Play scenario with special-character or multi-byte payloads, then verify that raw byte input and explicit UTF-8 handling produce the expected signature without changing valid ASCII behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java
- Domain
- security
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 65/100