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 = "https://example.com/cancel";
String completeCheckoutURL = "https://example.com/complete";
String country = "US";
String currency = "USD";
String language = "en";
public static String hash256(String data) throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(data.getBytes());
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);
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) {
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));
buffer.append((char)randomLimitedInt);
}
String generatedString = buffer.toString();
return (generatedString);
}
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws IOException {
try {
System.clearProperty("javax.net.ssl.trustStore");
System.out.println("GetPOS Start");
String httpMethod = "post"; // get|put|post|delete - must be lowercase
String urlPath = "/v1/checkout";
String basePath = "https://sandboxapi.rapyd.net"; // 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.setEntity(entity);
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);
System.out.println("----------------------------------------Response");
System.out.println(responseBody);
} catch (Exception e) {
System.out.println("URL:" + basePath + urlPath);
System.out.println(e.getMessage());
for (StackTraceElement exc : e.getStackTrace()) {
System.out.println(exc.toString());
}
}
} catch (Exception e) {
System.out.println(e.getMessage());
for (StackTraceElement exc : e.getStackTrace()) {
System.out.println(exc.toString());
}
}
}
}
What am I doing wrong?
Thanks in advance.