Signature Verification

Learn how to ensure the webhook payload received is sent by Plural.

Webhook signature verification is a critical security measure to protect your system from unauthorized access, data tampering, and other potential threats.

We include a signature that you can use to verify that the webhook payload you receive is legitimate and sent by us. The signature is sent against the X-verify parameter in the header in all the webhook events.

Verify Signature

Follow the below steps to verify the webhook signature.

  1. Generate a signature on your backend server using the webhook event's payload and the SHA256 algorithm. A sample webhook event is shown below.
{
  "event_name": "payment.captured",
  "merchant_response": {
    "merchant_id": "113484",
    "merchant_access_code": "7f532770-f8a7-46f8-a463-182727a29350",
    "unique_merchant_txn_id": "104943038807791693",
    "pine_pg_txn_status": "4",
    "txn_completion_date_time": "29/11/2023 12:18:49 PM",
    "amount_in_paisa": "20000",
    "txn_response_code": "1",
    "txn_response_msg": "SUCCESS",
    "acquirer_name": "HDFC",
    "pine_pg_transaction_id": "7831007",
    "captured_amount_in_paisa": "20188",
    "refund_amount_in_paisa": "0",
    "payment_mode": "CREDIT_DEBIT_CARD",
    "parent_txn_status": "",
    "parent_txn_response_code": "",
    "parent_txn_response_message": "",
    "masked_card_number": "************1112",
    "card_holder_name": "mojiz",
    "salted_card_hash": "B6B6A7CE1E6E2AA0DD7C028385446A3BBADCEE026A283859C69F5D2B8CC645AD",
    "rrn": "425847096720",
    "auth_code": "999999"
  }
}
  1. Remove spaces from the above sample payload.
{"event_name":"payment.captured","merchant_response":{"merchant_id": "113484","merchant_access_code":"7f532770-f8a7-46f8-a463-182727a29350","unique_merchant_txn_id":"104943038807791693","pine_pg_txn_status":"4","txn_completion_date_time":"29/11/2023 12:18:49 PM","amount_in_paisa":"20000","txn_response_code":"1","txn_response_msg":"SUCCESS","acquirer_name":"HDFC","pine_pg_transaction_id":"7831007","captured_amount_in_paisa":"20188","refund_amount_in_paisa":"0","payment_mode":"CREDIT_DEBIT_CARD","parent_txn_status":"","parent_txn_response_code":"","parent_txn_response_message":"","masked_card_number":"************1112","card_holder_name":"mojiz","salted_card_hash":"B6B6A7CE1E6E2AA0DD7C028385446A3BBADCEE026A283859C69F5D2B8CC645AD","rrn":"425847096720","auth_code": "999999"}}
  1. Convert the payload to a Base64 encoding.
eyJldmVudF9uYW1lIjoicGF5bWVudC5jYXB0dXJlZCIsIm1lcmNoYW50X3Jlc3BvbnNlIjp7Im1lcmNoYW50X2lkIjogIjExMzQ4NCIsIm1lcmNoYW50X2FjY2Vzc19jb2RlIjoiN2Y1MzI3NzAtZjhhNy00NmY4LWE0NjMtMTgyNzI3YTI5MzUwIiwidW5pcXVlX21lcmNoYW50X3R4bl9pZCI6IjEwNDk0MzAzODgwNzc5MTY5MyIsInBpbmVfcGdfdHhuX3N0YXR1cyI6IjQiLCJ0eG5fY29tcGxldGlvbl9kYXRlX3RpbWUiOiIyOS8xMS8yMDIzIDEyOjE4OjQ5IFBNIiwiYW1vdW50X2luX3BhaXNhIjoiMjAwMDAiLCJ0eG5fcmVzcG9uc2VfY29kZSI6IjEiLCJ0eG5fcmVzcG9uc2VfbXNnIjoiU1VDQ0VTUyIsImFjcXVpcmVyX25hbWUiOiJIREZDIiwicGluZV9wZ190cmFuc2FjdGlvbl9pZCI6Ijc4MzEwMDciLCJjYXB0dXJlZF9hbW91bnRfaW5fcGFpc2EiOiIyMDE4OCIsInJlZnVuZF9hbW91bnRfaW5fcGFpc2EiOiIwIiwicGF5bWVudF9tb2RlIjoiQ1JFRElUX0RFQklUX0NBUkQiLCJwYXJlbnRfdHhuX3N0YXR1cyI6IiIsInBhcmVudF90eG5fcmVzcG9uc2VfY29kZSI6IiIsInBhcmVudF90eG5fcmVzcG9uc2VfbWVzc2FnZSI6IiIsIm1hc2tlZF9jYXJkX251bWJlciI6IioqKioqKioqKioqKjExMTIiLCJjYXJkX2hvbGRlcl9uYW1lIjoibW9qaXoiLCJzYWx0ZWRfY2FyZF9oYXNoIjoiQjZCNkE3Q0UxRTZFMkFBMEREN0MwMjgzODU0NDZBM0JCQURDRUUwMjZBMjgzODU5QzY5RjVEMkI4Q0M2NDVBRCIsInJybiI6IjQyNTg0NzA5NjcyMCIsImF1dGhfY29kZSI6ICI5OTk5OTkifX0=
  1. Use the below sample code to generate a HashMap signature using the SHA-256 algorithm.
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
 
public class hash {
    public static void main(String[] args) {
        // Test the GenerateHash method
        String input = "<string>";
        String secretKey = "<secret_key>";  // Example key in hex
 
        String hash = GenerateHash(input, secretKey);
        System.out.println("Generated Hash: " + hash);
    }
    public static String GenerateHash(String input, String strSecretKey) {
        String strHash = "";
        try {
            if (!isValidString(input) || !isValidString(strSecretKey)) {
                return strHash;
            }
            byte[] convertedHashKey = new byte[strSecretKey.length() / 2];
 
            for (int i = 0; i < strSecretKey.length() / 2; i++) {
                convertedHashKey[i] =
                        (byte)Integer.parseInt(strSecretKey.substring(i * 2, (i*2)+2),16); //hexNumber radix
            }
            strHash = hmacDigest(input.toString(), convertedHashKey,
                    "HmacSHA256");
        } catch (Exception ex) {
            strHash = "";
        }
        return strHash.toUpperCase();
    }
    private static String hmacDigest(String msg, byte[] keyString, String algo) {
        String digest = null;
        try {
            SecretKeySpec key = new SecretKeySpec(keyString, algo);
            Mac mac = Mac.getInstance(algo);
            mac.init(key);
            byte[] bytes = mac.doFinal(msg.getBytes("UTF-8"));
            StringBuffer hash = new StringBuffer();
            for (int i = 0; i < bytes.length; i++) {
                String hex = Integer.toHexString(0xFF & bytes[i]);
                if (hex.length() == 1) {
                    hash.append('0');
                }
                hash.append(hex);
            }
            digest = hash.toString();
        } catch (UnsupportedEncodingException e) {
// logger.error("Exception occured in hashing the pine payment gateway request"+e);
        } catch (InvalidKeyException e) {
// logger.error("Exception occured in hashing the pine payment gateway request"+e);
        } catch (NoSuchAlgorithmException e) {
// logger.error("Exception occured in hashing the pine payment gateway request"+e);
        }
        return digest;
    }
    public static boolean isValidString(String str){
        if(str != null && !"".equals(str.trim())){
            return true;
        }
        return false;
    }
}
  1. Finally, compare the generated signature with the X-verify header.

πŸ“˜

Note:

  • By comparing the generated signature with the one provided, you can verify that the webhook was sent by Plural and that the data remained intact during transmission.

Ask AI
Assistant
Hourly Limit
0 / 20000 tokens used Resets in: 60m 0s
Order Lifecycle
Refunds
Settlements
Checkout
Dashboard
International Payments
How do I implement webhook notifications for payment status updates, and what's the recommended way to verify webhook authenticity?
How do I integrate Pine Labs payment gateway with my React Native mobile app and what are the required API credentials?
Can I customize the payment UI for card transactions, and what parameters can I pass to modify the checkout experience for my customers?
Assistant