---
page_title: JWT Webhook
product: API Reference
page_source: https://juspay.io/in/docs/api-reference/docs/express-checkout/jwt-webhook
llms_txt: https://juspay.io/in/docs/llms.txt
product_llms_txt: https://juspay.io/in/docs/api-reference/llms.txt
---


# JWT Webhooks



For enhanced security, Juspay provides JWT encryption support for webhooks.  Merchants already familiar with JWT for API calls can now opt for the feature to encrypt call backs also. 


## Webhook Payload



Webhook payload can be signed then encrypted to provide confidentiality and integrity.  Signed and encrypted payload will be of below format 


#### Shell Code Snippet:

```shell
{
    "JWT": "eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMjU2R0NNIiwia2lkIjoia2V5X2NjY2NjZGE5N2U4YjRkMWJhZDZjYTcyYzF ------------------- ei3wyuhxsVpN_XP-mUlODA"
}

```


For a detailed list of webhook events and sample decrypted payload please refer [here.](https://juspay.io/in/docs/resources/docs/common-resources/webhook-events-and-sample-payloads#Standard-Structure-of-Webhook-Payload)

You can also refer to the sample code to see how to decrypt JWT payload


## Enable JWT webhooks from the Dashboard



You can enable JWT encryption for webhooks through the dashboard in the settings module under the webhooks tab.

> **Warning**
> If you are planning to migrate from the existing auth method to JWT and JWT webhooks, please ensure backwards compatibility of webhooks. Once JWT webhooks is enabled and is activated we will start encrypting the webhooks.



Steps to enable JWT encryption from the dashboard - 

1. Find **Webhook Encryption Key** selector under the webhook settings tab (Payments → Settings → Webhooks)

![Image](https://dth95m2xtyv8v.cloudfront.net/tesseract/assets/api-reference/Screenshot%202024-10-21%20at%2011.55.22%E2%80%AFPM.png)



2. Choose the UUID of the JWT key you plan to use for encrypting webhooks

![Image](https://dth95m2xtyv8v.cloudfront.net/tesseract/assets/api-reference/Screenshot%202024-10-22%20at%2012.13.05%E2%80%AFAM.png)



3. Click the Update Webhook Settings button to confirm your selection

![Image](https://dth95m2xtyv8v.cloudfront.net/tesseract/assets/api-reference/Screenshot%202024-10-22%20at%2012.19.25%E2%80%AFAM-5ee6c.png)




## Key Rotation



For security purposes, it is advisable to rotate your API keys at least every 90 days. To update the JWT key used for webhook encryption from the dashboard, please follow these steps:

1. In the Webhook Encryption Key selector (Payments → Settings → Webhooks), select the UUID of the new key intended for webhook encryption

![Image](https://dth95m2xtyv8v.cloudfront.net/tesseract/assets/api-reference/Screenshot%202024-10-22%20at%2012.28.25%E2%80%AFAM.png)



2. Click the Update Webhook Settings button to confirm your selection

![Image](https://dth95m2xtyv8v.cloudfront.net/tesseract/assets/api-reference/Screenshot%202024-10-22%20at%2012.29.32%E2%80%AFAM.png)



## Sample Code Snippets:
### Sample Code:

#### Java Code Snippet:

```java
package org.example;

import com.nimbusds.jose.*;
import com.nimbusds.jose.crypto.RSADecrypter;
import com.nimbusds.jose.crypto.RSASSAVerifier;
import com.nimbusds.jose.crypto.bc.BouncyCastleProviderSingleton;
import org.apache.commons.codec.binary.Base64;

import javax.xml.bind.DatatypeConverter;
import java.security.*;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.text.ParseException;

public class DecryptAndVerifyApiResponse {
    final private static String keyId = "your api key";
    // private key secretly stored at merchant's side
    final private static String fileContentPrivateKey = "-----BEGIN RSA PRIVATE KEY----- .......... -----END RSA PRIVATE KEY-----";
    // public key secretly stored at merchant's side
    final static private String fileContentPublicKey = "-----BEGIN PUBLIC KEY----- .......... -----END PUBLIC KEY-----";

    final private static PrivateKey privateKey;
    final private static PublicKey publicKey;

    static {
        // This is required if you are reading pkcs1 format i.e. keys have BEGIN/END RSA PUBLIC/PRIVATE KEY
        Provider bc = BouncyCastleProviderSingleton.getInstance();
        Security.addProvider(bc);
        try {
            privateKey = readPrivateKeyFromString(fileContentPrivateKey);
            publicKey = readPublicKeyFromString(fileContentPublicKey);
        } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
            System.out.println("Invalid key format");
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) throws ParseException, JOSEException {
        // api request will of {"JWT": "eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMjU2R0NNIiwia2lkIjoia2V5X2NjY2NjZGE5N2U4YjRkMWJhZDZjYTcyYzF ------------------- ei3wyuhxsVpN_XP-mUlODA"} extract the value of JWT field for decryption
        String encryptedApiResponseToken = "eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMjU2R0NNIiwia2lkIjoia2V5X2NjY2NjZGE5N2U4YjRkMWJhZDZjYTcyYzF ------------------- ei3wyuhxsVpN_XP-mUlODA";
        String decryptedAndVerifiedApiResponse = decryptAndVerifyPayload(encryptedApiResponseToken);
        System.out.println(decryptedAndVerifiedApiResponse);
    }

    public static String decryptAndVerifyPayload(String signedAndEncryptedPayload) throws ParseException, JOSEException {
        // jwe decrypt the request
        JWEObject jweObject = JWEObject.parse(signedAndEncryptedPayload);
        jweObject.decrypt(new RSADecrypter(privateKey));
        String jweData = jweObject.getPayload().toString();
        // jws verify the signature
        JWSObject jwsObject = JWSObject.parse(jweData);
        jwsObject.verify(new RSASSAVerifier((RSAPublicKey) publicKey));
        return jwsObject.getPayload().toString();
    }

    static PrivateKey readPrivateKeyFromString(String key) throws NoSuchAlgorithmException, InvalidKeySpecException {
        String privateKeyPEM = key
                .replace("-----BEGIN PRIVATE KEY-----", "")
                .replace("-----BEGIN RSA PRIVATE KEY-----", "")
                .replace("-----END RSA PRIVATE KEY-----", "")
                .replace(System.lineSeparator(), "")
                .replace("-----END PRIVATE KEY-----", "");
        byte[] encoded = Base64.decodeBase64(privateKeyPEM);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded);
        return keyFactory.generatePrivate(keySpec);
    }

    static RSAPublicKey readPublicKeyFromString(String key) throws NoSuchAlgorithmException, InvalidKeySpecException {
        KeyFactory kf = KeyFactory.getInstance("RSA");
        String keyContent = key.replaceAll("\\n", "")
                .replace("-----BEGIN PUBLIC KEY-----", "")
                .replace("-----END PUBLIC KEY-----", "")
                .replace("-----BEGIN RSA PUBLIC KEY-----", "")
                .replace("-----END RSA PUBLIC KEY-----", "")
                .replace(System.lineSeparator(), "");
        byte[] pkey = DatatypeConverter.parseBase64Binary(keyContent);
        return (RSAPublicKey)kf.generatePublic(new X509EncodedKeySpec(pkey));
    }
}package org.example;

import com.nimbusds.jose.*;
import com.nimbusds.jose.crypto.RSADecrypter;
import com.nimbusds.jose.crypto.RSASSAVerifier;
import com.nimbusds.jose.crypto.bc.BouncyCastleProviderSingleton;
import org.apache.commons.codec.binary.Base64;

import javax.xml.bind.DatatypeConverter;
import java.security.*;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.text.ParseException;

public class DecryptAndVerifyApiResponse {
    final private static String keyId = "your api key";
    // private key secretly stored at merchant's side
    final private static String fileContentPrivateKey = "-----BEGIN RSA PRIVATE KEY----- .......... -----END RSA PRIVATE KEY-----";
    // public key secretly stored at merchant's side
    final static private String fileContentPublicKey = "-----BEGIN PUBLIC KEY----- .......... -----END PUBLIC KEY-----";

    final private static PrivateKey privateKey;
    final private static PublicKey publicKey;

    static {
        // This is required if you are reading pkcs1 format i.e. keys have BEGIN/END RSA PUBLIC/PRIVATE KEY
        Provider bc = BouncyCastleProviderSingleton.getInstance();
        Security.addProvider(bc);
        try {
            privateKey = readPrivateKeyFromString(fileContentPrivateKey);
            publicKey = readPublicKeyFromString(fileContentPublicKey);
        } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
            System.out.println("Invalid key format");
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) throws ParseException, JOSEException {
        // api request will of {"JWT": "eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMjU2R0NNIiwia2lkIjoia2V5X2NjY2NjZGE5N2U4YjRkMWJhZDZjYTcyYzF ------------------- ei3wyuhxsVpN_XP-mUlODA"} extract the value of JWT field for decryption
        String encryptedApiResponseToken = "eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMjU2R0NNIiwia2lkIjoia2V5X2NjY2NjZGE5N2U4YjRkMWJhZDZjYTcyYzF ------------------- ei3wyuhxsVpN_XP-mUlODA";
        String decryptedAndVerifiedApiResponse = decryptAndVerifyPayload(encryptedApiResponseToken);
        System.out.println(decryptedAndVerifiedApiResponse);
    }

    public static String decryptAndVerifyPayload(String signedAndEncryptedPayload) throws ParseException, JOSEException {
        // jwe decrypt the request
        JWEObject jweObject = JWEObject.parse(signedAndEncryptedPayload);
        jweObject.decrypt(new RSADecrypter(privateKey));
        String jweData = jweObject.getPayload().toString();
        // jws verify the signature
        JWSObject jwsObject = JWSObject.parse(jweData);
        jwsObject.verify(new RSASSAVerifier((RSAPublicKey) publicKey));
        return jwsObject.getPayload().toString();
    }

    static PrivateKey readPrivateKeyFromString(String key) throws NoSuchAlgorithmException, InvalidKeySpecException {
        String privateKeyPEM = key
                .replace("-----BEGIN PRIVATE KEY-----", "")
                .replace("-----BEGIN RSA PRIVATE KEY-----", "")
                .replace("-----END RSA PRIVATE KEY-----", "")
                .replace(System.lineSeparator(), "")
                .replace("-----END PRIVATE KEY-----", "");
        byte[] encoded = Base64.decodeBase64(privateKeyPEM);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded);
        return keyFactory.generatePrivate(keySpec);
    }

    static RSAPublicKey readPublicKeyFromString(String key) throws NoSuchAlgorithmException, InvalidKeySpecException {
        KeyFactory kf = KeyFactory.getInstance("RSA");
        String keyContent = key.replaceAll("\\n", "")
                .replace("-----BEGIN PUBLIC KEY-----", "")
                .replace("-----END PUBLIC KEY-----", "")
                .replace("-----BEGIN RSA PUBLIC KEY-----", "")
                .replace("-----END RSA PUBLIC KEY-----", "")
                .replace(System.lineSeparator(), "");
        byte[] pkey = DatatypeConverter.parseBase64Binary(keyContent);
        return (RSAPublicKey)kf.generatePublic(new X509EncodedKeySpec(pkey));
    }
}

```



---

## See Also

- [Notification/Execution Silent retry](https://juspay.io/in/docs/api-reference/docs/express-checkout/notificationexecution-silent-retry)
- [Create Customer Account](https://juspay.io/in/docs/api-reference/docs/express-checkout/create-customer-account)
