Running into error while demoing Rapyd on Java

Hi guys.

I’m demoing Rapyd’s payment Gateway API for Java. When I run my demo webapp, I receive an error that reads:

{“status”:{“error_code”:“UNAUTHENTICATED_API_CALL”,“status”:“ERROR”,“message”:“The API received a request, but the signature did not match. The request was rejected. Corrective action: (1) Remove all whitespace that is not inside a string. (2) Remove trailing zeroes and decimal points, or wrap numbers in a string.”,“response_code”:“UNAUTHENTICATED_API_CALL”,“operation_id”:“cb511db8-4a6f-4627-9dac-f9c4790a390f”}}|#]

My code is as follows:

public class CheckoutServlet extends HttpServlet{
    String cancelCheckoutURL = "";
    String completeCheckoutURL = "";
    String country = "US";
    String currency = "USD";
    String language = "en";
    public static String hash256(String data) throws NoSuchAlgorithmException {
        MessageDigest md = MessageDigest.getInstance("SHA-256");
        return bytesToHex(md.digest());
    public static String bytesToHex(byte[]bytes) {
        StringBuffer result = new StringBuffer();
        for (byte byt: bytes)
            result.append(Integer.toString((byt & 0xff) + 0x100, 16).substring(1));
        return result.toString();
    public static String hmacDigest(String msg, String keyString, String algo) {
        String digest = null;
        try {
            SecretKeySpec key = new SecretKeySpec((keyString).getBytes("ASCII"), algo);
            Mac mac = Mac.getInstance(algo);
            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) {
            digest = hash.toString();
        } catch (UnsupportedEncodingException e) {
            System.out.println("hmacDigest UnsupportedEncodingException");
        catch (InvalidKeyException e) {
            System.out.println("hmacDigest InvalidKeyException");
        catch (NoSuchAlgorithmException e) {
            System.out.println("hmacDigest NoSuchAlgorithmException");
        return digest;
    public static String generateString() {
        int leftLimit = 97;   // letter 'a'
        int rightLimit = 122; // letter 'z'
        int targetStringLength = 10;
        Random random = new Random();
        StringBuilder buffer = new StringBuilder(targetStringLength);
        for (int i = 0; i < targetStringLength; i++) {
            int randomLimitedInt = leftLimit + (int)
                (random.nextFloat() * (rightLimit - leftLimit + 1));
        String generatedString = buffer.toString();
        return (generatedString);
  public void doPost(HttpServletRequest request, HttpServletResponse response)
      throws IOException {
        try {
            System.out.println("GetPOS Start");
            String httpMethod = "post";                           // get|put|post|delete - must be lowercase
            String urlPath = "/v1/checkout"; 
            String basePath = ""; // hardkeyed for this example
            String salt = generateString(); // Randomly generated for each request.
            long timestamp = System.currentTimeMillis() / 1000L; // Unix time (seconds).
            String accessKey = "0F32811C67FADC15E0ED";                    // The access key received from Rapyd.
            String secretKey = "d8abc747ebfec6cb10a049a9904d7c7652cefeb4cafa6304b15a0176ccf4ef19eac4c0ba61ed8f65";                    // Never transmit the secret key by itself.
            String bodyString = new String("{"
                + "\"amount\": 10,\n" +
                "\"complete_checkout_url\":\"" + completeCheckoutURL +"\",\n" +
                "\"country\":\"" + country + "\",\n" +
                "\"currency\":\"" + currency + "\",\n" +
                "\"cancel_checkout_url\":\"" + cancelCheckoutURL + "\",\n" +
                "\"language\":\"" + language +  "\"}").replaceAll("\\s", "");                                     // Always empty for GET; strip nonfunctional whitespace.
            String toEnc = httpMethod + urlPath + salt + Long.toString(timestamp) + accessKey + secretKey + bodyString;
            System.out.println("String to be encrypted::" + toEnc);
            String StrhashCode = hmacDigest(toEnc, secretKey, "HmacSHA256");
            String signature = Base64.getEncoder().encodeToString(StrhashCode.getBytes());
            HttpClient httpclient = HttpClients.createDefault();
            try {
                HttpPost httppost = new HttpPost (basePath + urlPath);
                String json = new String("{access_key:" + accessKey + ",salt:" + salt + ",timestamp:" + Long.toString(timestamp) + ",signature:" + signature + "}").replaceAll("\\s", "");
                StringEntity entity = new StringEntity(json);
                httppost.setHeader("Accept", "application/json");
                httppost.setHeader("Content-type", "application/json");
                /*httppost.addHeader("Content-Type", "application/json");
                httppost.addHeader("access_key", accessKey);
                httppost.addHeader("salt", salt);
                httppost.addHeader("timestamp", Long.toString(timestamp));
                httppost.addHeader("signature", signature);*/
               // httppost.addHeader("idempotency", idempotency);
                // Create a custom response handler
                ResponseHandler < String > responseHandler = new ResponseHandler < String > () {
                     @ Override
                    public String handleResponse(
                        final HttpResponse response)throws ClientProtocolException,
                    IOException {
                        int status = response.getStatusLine().getStatusCode();
                        HttpEntity entity = response.getEntity();
                        return entity != null ? EntityUtils.toString(entity) : null;
                String responseBody = httpclient.execute(httppost, responseHandler);
            } catch (Exception e) {
                System.out.println("URL:" + basePath + urlPath);
                for (StackTraceElement exc : e.getStackTrace()) {
        } catch (Exception e) {
                for (StackTraceElement exc : e.getStackTrace()) {

What am I doing wrong?

Thanks in advance.

Thanks @Main_Method, here are some tips regarding the error:

The error response mentions two things:

  1. All spaces and other whitespace outside of strings must be removed.
  2. Numbers should be sent in strings, not as integers/numbers with decimal points.

Here are some resources that may help:

I would also take a look around your body string, and and how it may not be parsed correctly.


Thanks for the response. However, I’m at my wits end. I tried eliminating all white spaces using replace and I ensured that all numbers were being sent as string. I wrapped the amount in quotation marks in my body. That didn’t work.

I managed to get it to work on PHP but not Java. I really don’t know what I’m missing here. Is there anyway to see the signature being compared at the Webhook/API? It would help me debug and figure out where the mistake is occurring.

It could be how I’m generating the signature?

My last resort will be to embed a PHP servlet into my code. And I don’t want to do that.

Are there any other Java code samples or tutorials I can use?

I think you guys also need to fix the Java example on this page:

The URL path is all wrong.

I managed to use the example to complete a successful Get request from Rapyd. It’s the post I’m having trouble with now.

Thanks @Main_Method,

The urlPath is defined above the page you linked, so it could include any extension based on the request.

  • url_path is the portion of the URL after the base URI, such as The URL path starts with /v1.

Sandbox would be

Have you been able to take a look at the resources I posted? About three of them relate to the issue I believe you are having, matching with the Webhook signature.

If your problem persist please reach out to Support as shown in Submitting a Support Ticket

We should have a Java integration tutorial coming soon!

Hi, I just saw your reply. I think I meant that this part of the code may be wrong:

HttpGet httpget = new HttpGet(“/v1/data/countries”);

This is from the Java Example of The Authentication page.

But now I see what you’re saying - that you’re meant to edit and fill in the blanks. Okay. I scanned through those resources. Maybe I missed something. I’ll look read through them again.