Finix English
    • Finix API documentation
    • Introduction
    • Request Signing
    • Signature Verification
    • India
      • PayIn
        • Create payment
        • Fetch payment status
        • Payment webhook (async notification)
      • PayOut
        • Initiate payout
        • Fetch payout status
        • Payout webhook (async notification)
    • Egypt
      • PayIn
        • Create payment
        • Fetch payment status
        • Payment webhook (async notification)
      • PayOut
        • Initiate payout
        • Fetch payout status
        • Payout webhook (async notification)
    • Nigeria
      • PayIn
        • Create payment
        • Fetch payment status
        • Payment webhook (async notification)
      • PayOut
        • Initiate payout
        • Fetch payout status
        • Payout webhook (async notification)
        • Bank code
    • Indonesia
      • PayIn
        • Create payment
        • Fetch payment status
        • Payment webhook (async notification)
      • PayOut
        • Initiate payout
        • Fetch payout status
        • Payout webhook (async notification)
        • Bank code
    • Account Balance
      POST
    • USDT Exchange Rate
      POST

    Signature Verification

    RSA-SHA512 Webhook signature verification#

    I. Purpose#

    To ensure webhooks are authentic and untampered, verify the Signature header with Finix’s RSA public key and SHA512.

    II. When verification is required#

    After receiving a Finix callback, verify Signature to confirm the payload is genuine and unchanged.

    III. Structure and rules#

    Callbacks include Signature. Build the verification string from BODY + Timestamp and verify with Finix’s public key.
    1
    Signing algorithms
    Digest: SHA512 (512-bit Secure Hash Algorithm)
    Encryption: RSA
    Full algorithm name: SHA512withRSA
    Encoding: Base64
    2
    Build string to verify
    String to verify:
    stringToVerify = sha512Hex(rawBodyJson) + timestamp
    
    Notes:
    Body: raw callback JSON string (field order must not change).
    Timestamp: value from the Timestamp header, in seconds.
    URL: not included in the verification input.
    Example:
    {"status":"SUCCESS","orderId":"ABC123","amount":100}
    
    After computing SHA512:
    09ec4d91b7dd1fce70320b09f3fd7e98cd66efc8f918bb56e98a37c6b179f7d24fc4e43c0a614f8b9e5e95e410af0c0fd9d5f40ad8e38b30a9ad512b48c9c0e7
    
    If Timestamp is:
    1699447297
    
    Then the string to verify is:
    09ec4d91b7dd1fce70320b09f3fd7e98cd66efc8f918bb56e98a37c6b179f7d24fc4e43c0a614f8b9e5e95e410af0c0fd9d5f40ad8e38b30a9ad512b48c9c0e71699447297
    
    3
    Verify with public key
    Verify using Finix’s RSA public key and SHA512withRSA.
    Base64-decode Signature before verification.
    A successful verification means the payload is authentic.

    IV. Security notes#

    Finix signs certain responses with its private key; verify with Finix’s public key.
    Successful verification proves the payload came from Finix and was not modified.
    This design protects against tampering, man-in-the-middle attacks, and forged callbacks.

    V. Important notes#

    The sequence of fields in the string to verify must be exact.
    Body must be the exact raw JSON (no pretty-printing, reordered keys, or extra whitespace).
    Base64-decode Signature before calling the verifier.
    Read Timestamp from headers; it must match the value Finix used when signing.

    VI. Code samples#

    Java
    Python
    PHP
    C#(.NET Core)
    Go
    import java.nio.charset.StandardCharsets;
    import java.security.*;
    import java.security.spec.X509EncodedKeySpec;
    import java.util.Base64;
    
    public class CallbackVerifier {
        public static void main(String[] args) throws Exception {
            String body = "{\"status\":\"SUCCESS\",\"orderId\":\"ABC123\",\"amount\":100}";
            String timestamp = "1699447297";
            String signatureBase64 = "REQUEST_HEADER_SIGNATURE_VALUE";
    
            String bodyHash = sha512Hex(body);
            String stringToVerify = bodyHash + timestamp;
    
            String publicKeyPem = "-----BEGIN PUBLIC KEY-----\nYOUR_FINIX_PUBLIC_KEY_HERE\n-----END PUBLIC KEY-----";
            PublicKey publicKey = loadPublicKey(publicKeyPem);
    
            boolean isValid = verifySignature(publicKey, stringToVerify, signatureBase64);
            System.out.println("Signature valid: " + isValid);
        }
    
        public static String sha512Hex(String data) throws Exception {
            MessageDigest md = MessageDigest.getInstance("SHA-512");
            byte[] digest = md.digest(data.getBytes(StandardCharsets.UTF_8));
            StringBuilder sb = new StringBuilder();
            for (byte b : digest) sb.append(String.format("%02x", b));
            return sb.toString();
        }
    
        public static PublicKey loadPublicKey(String pem) throws Exception {
            pem = pem.replace("-----BEGIN PUBLIC KEY-----", "")
                     .replace("-----END PUBLIC KEY-----", "")
                     .replaceAll("\\s+", "");
            byte[] decoded = Base64.getDecoder().decode(pem);
            X509EncodedKeySpec spec = new X509EncodedKeySpec(decoded);
            return KeyFactory.getInstance("RSA").generatePublic(spec);
        }
    
        public static boolean verifySignature(PublicKey publicKey, String data, String signatureBase64) throws Exception {
            byte[] signatureBytes = Base64.getDecoder().decode(signatureBase64);
            Signature verifier = Signature.getInstance("SHA512withRSA");
            verifier.initVerify(publicKey);
            verifier.update(data.getBytes(StandardCharsets.UTF_8));
            return verifier.verify(signatureBytes);
        }
    }
    
    Modified at 2026-04-07 17:05:11
    Previous
    Request Signing
    Next
    Create payment
    Built with