Trying to implement the foreign exchange rates API using x-pay-token. The HelloWorld examples works without issue but doing a POST to /forexrates/v2/foreignexchangerates fails with code 9159 - Token Validation Failed.
The request body {"initiatingPartyId": 1002,"rateProductCode": "B","destinationCurrencyCode": "826","sourceCurrencyCode": "840", "quoteIdRequired": true, "sourceAmount": "1.00"} works in Developer Center Playground
// Using HelloWorld as the starting point. Changed the resourcePath, requestBody and url
public void getRate(String fromCurrency, String toCurrencies) {
try {
String apiKey = "<api key>";
String sharedSecret = "<shared secret>";
String resourcePath = "v2/foreignexchangerates"; // excluding /forexrates/
String queryString = "apiKey=" + apiKey;
String requestBody = "{\"initiatingPartyId\": 1002,\"rateProductCode\": \"B\",\"destinationCurrencyCode\": \"826\",\"sourceCurrencyCode\": \"840\", \"quoteIdRequired\": true, \"sourceAmount\": \"1.00\"}";
URL url = new URL("https://sandbox.api.visa.com/forexrates/v2/foreignexchangerates?" + queryString);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestMethod("POST");
con.setRequestProperty("Content-Type", "application/json");
con.setRequestProperty("Accept", "application/json");
// String xPayToken = generateXpaytoken(resourcePath + "?" + queryString, queryString, requestBody, sharedSecret);
String xPayToken = generateXpaytoken(resourcePath, queryString, requestBody, sharedSecret);
con.setRequestProperty("x-pay-token", xPayToken);
int status = con.getResponseCode();
System.out.println("Http Status: " + status);
BufferedReader in ;
if (status == 200) { in = new BufferedReader(new InputStreamReader(con.getInputStream()));
} else { in = new BufferedReader(new InputStreamReader(con.getErrorStream()));
System.out.println("Api Key-Shared Secret (X-Pay-Token) test failed");
}
String response;
StringBuffer content = new StringBuffer();
while ((response = in .readLine()) != null) {
content.append(response);
} in .close();
con.disconnect();
System.out.println(content.toString());
} catch (Exception e) {
throw new RuntimeException(e);
}
});
}
// This line down is a direct copy from https://developer.visa.com/pages/working-with-visa-apis/x-pay-token
public static String generateXpaytoken(String resourcePath, String queryString, String requestBody, String sharedSecret) throws SignatureException {
String timestamp = timeStamp();
String beforeHash = timestamp + resourcePath + queryString + requestBody;
String hash = hmacSha256Digest(beforeHash, sharedSecret);
String token = "xv2:" + timestamp + ":" + hash;
return token;
}
private static String timeStamp() {
return String.valueOf(System.currentTimeMillis()/ 1000L);
}
private static String hmacSha256Digest(String data, String sharedSecret) throws SignatureException {
return getDigest("HmacSHA256", sharedSecret, data, true);
}
private static String getDigest(String algorithm, String sharedSecret, String data, boolean toLower) throws SignatureException {
try {
Mac sha256HMAC = Mac.getInstance(algorithm);
SecretKeySpec secretKey = new SecretKeySpec(sharedSecret.getBytes(StandardCharsets.UTF_8), algorithm);
sha256HMAC.init(secretKey);
byte[] hashByte = sha256HMAC.doFinal(data.getBytes(StandardCharsets.UTF_8));
String hashString = toHex(hashByte);
return toLower ? hashString.toLowerCase() : hashString;
} catch (Exception e) {
throw new SignatureException(e);
}
}
private static String toHex(byte[] bytes) {
BigInteger bi = new BigInteger(1, bytes);
return String.format("%0" + (bytes.length << 1) + "X", bi);
}
Solved! Go to Solution
Hi @alkesh, Thank you for reaching out! An agent is looking into this and will get back to you soon. In the meantime, if any community members know a solution, please feel free to respond to this thread.
Hi @alkesh,
Please refer to the Visa Developer Error Codes page to fix the error. Try the test again and if the error is fixed please click on the Accept as Solution button in this forum post.