【问题标题】:error :- {"code":"403", "message":"HMAC validation Failure"}错误:- {"code":"403", "message":"HMAC 验证失败"}
【发布时间】:2015-06-04 16:54:43
【问题描述】:

我在这里附上代码和一个包含完整代码的链接,看看它:- 我的授权标题接缝的长度与 payeezy 官方网站中提到的长度相同。我也使我的 hmacString 的顺序与此链接 (https://developer.payeezy.com/content/hmac-validation-failure) 中提到的相同。完成所有这些后,我仍然遇到同样的问题

public static String excutePost(String urlParameters) throws IOException {
        URL url = new URL("https://api-cert.payeezy.com/v1/transactions");
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
        try {
            // Create connection
            connection.setRequestMethod("POST");
            connection.setRequestProperty("Content-Type", headerContentType);
            connection.setRequestProperty("apikey ", apikey);
            connection.setRequestProperty("token", MerchantToken);
            connection
                    .setRequestProperty("Authorization", authorizationHeader);
            connection.setRequestProperty("timestamp", ""+epoch);
            connection.setRequestProperty("nonce", ""+nonce);
            connection.setDoOutput(true);
            connection.setReadTimeout(30000);

            // Send request
            DataOutputStream wr = new DataOutputStream(connection.getOutputStream());
            wr.writeBytes(urlParameters);
            wr.flush();
            wr.close();

            // Get Response
            InputStream is = connection.getInputStream();
            BufferedReader rd = new BufferedReader(new InputStreamReader(is));
            String line;
            StringBuffer response = new StringBuffer();
            while ((line = rd.readLine()) != null) {
                response.append(line);
                response.append('\r');
            }
            rd.close();
            return response.toString();

        } catch (Exception e) {

            e.printStackTrace();
            return null;

        } finally {

            if (connection != null) {
                connection.disconnect();
            }
        }
    }

这里是完整的 java 类代码:- http://piratepad.net/ep/pad/view/ro.WwZ9v6FX1a6/latest

【问题讨论】:

    标签: java payment-gateway


    【解决方案1】:

    您必须为每个请求生成一个新的timestampnonce,即每个新请求都必须有其唯一的timestampnonce

    在java中,timestamp可以设置为System.currentTimeMillis()nonce可以使用UUID(UUID.randomUUID().toString())设置。

    最后,确保您的 Authorization 计算正确(我看到他们使用 API 密钥使用 HMAC-SHA1)。

    我希望这会有所帮助。


    编辑:怀疑是您的HMAC-SHA1 Authorization 值不正确。运行您的代码时,我得到以下响应(经过我自己的几次编码)。

    Connection = keep-alive
    Content-Length = 51
    Content-Type = application/json
    {"code":"403", "message":"HMAC validation Failure"}
    

    确保您正确计算您的 HMAC-SHA1 值(正如我上面所说的)。

    请参阅以下(更新的)代码,您可以自己编译和运行这些代码。您现在需要 Java 8,因为它带有 Base 64 编码器/解码器。

    import java.io.BufferedReader;
    import java.io.DataOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.net.HttpURLConnection;
    import java.util.List;
    import java.util.Map;
    import java.util.Map.Entry;
    import java.net.URI;
    import java.net.URL;
    import java.security.MessageDigest;
    import java.security.SecureRandom;
    import java.text.SimpleDateFormat;
    import java.util.Base64;
    import java.util.Date;
    import java.util.TimeZone;
    
    import javax.crypto.Mac;
    import javax.crypto.spec.SecretKeySpec;
    
    //import org.apache.commons.codec.binary.Base64;
    
    public class MainJava {
        private static final String    myEncoding            = "UTF-8";
        private static final String    myMessageDigest        = "SHA-1";
        private static final String    myKeySpec            = "HmacSHA1";
        private static String        NEWLINE                = "\n";
        private static String        authorizationHeader;
        private static String        contentSha1;
        // private static String keyId = "230297";
        // private static String hmacKey = "tcwR9r1OR85V9bcV5tc7a9d1XkWigjqY";
        private static String        ApiSecretkey        = "0779eb593286b278aaf8cfcf83c8e33bc757d53a8a642b53d24d63bda844da5b";
        private static String        MerchantToken        = "fdoa-a480ce8951daa73262734cf102641994c1e55e7cdf4c02b6";
        private static String        reportingToken        = "e56a0223d0415067";
        private static String        apikey                = "XSjbv8PLDINJ28qXLEYAhcrz8rxKXQ4Y";
        private static long            nonce;
        public static String        headerContentType    = "application/json";
        private static long            epoch;
    
        public static void main(String[] args) throws Exception {
            String json_string_dataTwo = "{\"type\":\"visa\",\"cardholder_name\":\"John Smith\",\"card_number\":\"4788250000028291\",\"exp_date\":1020,\"cvv\":\"123\"}";
            // String json_string =
            // "{\"gateway_id\":\"AI2010-01\",\"password\":\"w226638qtot48xu503zumwt2iy46g26q\",\"transaction_type\":\"00\",\"amount\":10,\"cardholder_name\":\"test\",\"cc_number\":\"4111111111111111\",\"cc_expiry\":\"1219\"}";
            String json_string_data = "{\"merchant_ref\":\"Astonishing-Sale\",\"transaction_type\":\"authorize\",\"method\":\"credit_card\",\"amount\":1299,\"currency_code\":\"USD\",\"credit_card\":"
                    + json_string_dataTwo + "}";
            // "{\r\n  \"merchant_ref\": \"Astonishing-Sale\",\r\n  \"transaction_type\": \"authorize\",\r\n  \"method\": \"credit_card\",\r\n  \"amount\": \"1299\",\r\n  \"currency_code\": \"USD\",\r\n  \"credit_card\": {\r\n    \"type\": \"visa\",\r\n    \"cardholder_name\": \"John Smith\",\r\n    \"card_number\": \"4788250000028291\",\r\n    \"exp_date\": \"1020\",\r\n    \"cvv\": \"123\"\r\n  }\r\n}";
    
            epoch = System.currentTimeMillis();// / 1000;
            // nonce = UUID.randomUUID().toString();
            nonce = Math.abs(SecureRandom.getInstance("SHA1PRNG").nextLong());
            contentSha1 = contentSha1(json_string_data);
            authorizationHeader = authHeader(epoch, contentSha1);
            System.out.println(excutePost(json_string_data));
        }
    
        private static String authHeader(long hashTime, String contentSha1) {
            String authorizationHeader = null;
            try {
                String hmacString = "POST" + NEWLINE + "application/json" + NEWLINE + contentSha1 + NEWLINE + hashTime + NEWLINE + apikey + NEWLINE
                        + new URI("https://api-cert.payeezy.com/v1/transactions");
    
                return sha1(hmacString, ApiSecretkey);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    
        private static String contentSha1(String content) throws Exception {
            MessageDigest md = MessageDigest.getInstance("SHA-1");
            byte[] sha1hash = new byte[40];
            md.update(content.getBytes("UTF-8"), 0, content.length());
            sha1hash = md.digest();
            return convertToHex(sha1hash);
        }
    
        private static String convertToHex(byte[] data) {
            StringBuffer buf = new StringBuffer();
            for (int i = 0; i < data.length; i++) {
                int halfbyte = data[i] >>> 4 & 0xF;
                int two_halfs = 0;
                do {
                    if ((0 <= halfbyte) && (halfbyte <= 9))
                        buf.append((char) (48 + halfbyte));
                    else
                        buf.append((char) (97 + (halfbyte - 10)));
                    halfbyte = data[i] & 0xF;
                } while (two_halfs++ < 1);
            }
            return buf.toString();
        }
    
    //     private static String sha1(String s, String keyString) {
    //     Base64 base64 = new Base64();
    //     try {
    //     SecretKeySpec key = new SecretKeySpec(keyString.getBytes("UTF-8"),
    //     "HmacSHA1");
    //     Mac mac = Mac.getInstance("HmacSHA1");
    //     mac.init(key);
    //     byte[] bytes = mac.doFinal(s.getBytes("UTF-8"));
    //    
    //     return new String(base64.encode(bytes));
    //     } catch (Exception e) {
    //     throw new RuntimeException(e);
    //     }
    //     }
        private static String sha1(String s, String keyString) {
            byte[] bytes = null;
            try {
                Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
                SecretKeySpec secret_key = new SecretKeySpec(keyString.getBytes(), "HmacSHA256");
                sha256_HMAC.init(secret_key);
                bytes = sha256_HMAC.doFinal(s.getBytes("UTF-8"));
                //return new String(Base64.encodeBase64String(bytes));
            } catch (Exception e) {
                System.out.println("Error");
            }
            return  Base64.getEncoder().encodeToString(bytes);
        }
    
        private static String hashTime() {
            String time = getUTCFormattedDate("yyyy-MM-dd'T'HH:mm:ss'Z'");
    
            return time;
        }
    
        private static String getUTCFormattedDate(String format) {
            SimpleDateFormat dateFormat = new SimpleDateFormat(format);
            dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
            return dateFormat.format(new Date());
        }
    
        public static String excutePost(String urlParameters) throws IOException {
        System.out.println(urlParameters);
        System.out.println(headerContentType);
        System.out.println(MerchantToken);
        System.out.println(authorizationHeader);
        System.out.println(epoch);
        System.out.println(nonce);
            URL url = new URL("https://api-cert.payeezy.com/v1/transactions");
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            try {
                // Create connection
                connection.setRequestMethod("POST");
                connection.setRequestProperty("Content-Type", headerContentType);
                connection.setRequestProperty("apikey ", apikey);
                connection.setRequestProperty("token", MerchantToken);
                connection.setRequestProperty("Authorization", authorizationHeader);
                connection.setRequestProperty("timestamp", "" + epoch);
                connection.setRequestProperty("nonce", "" + nonce);
                connection.setDoOutput(true);
                connection.setReadTimeout(30000);
    
                // Send request
                DataOutputStream wr = new DataOutputStream(connection.getOutputStream());
                wr.writeBytes(urlParameters);
                wr.flush();
                wr.close();
    
                // Get Response
                InputStream is = null;
                int statusCode = connection.getResponseCode();
    
            try {
                is = connection.getInputStream();
            } catch (IOException e) {
                if (statusCode >= 400) {
                    is = connection.getErrorStream();
                }
            }
            BufferedReader rd = new BufferedReader(new InputStreamReader(is));
                String line;
                StringBuffer response = new StringBuffer();
                while ((line = rd.readLine()) != null) {
                    response.append(line);
                    response.append('\r');
                }
                rd.close();
                return response.toString();
    
            } catch (Exception e) {
    
                e.printStackTrace();
                return null;
    
            } finally {
    
                if (connection != null) {
                    connection.disconnect();
                }
            }
        }
    
    }
    

    【讨论】:

    • 嘿,谢谢您的回复,是的,我知道我必须在每次点击 api 时生成我的时间戳和随机数向您发送我的主要 java 类...请看看它并告诉我我做错了什么... (piratepad.net/ep/pad/view/ro.WwZ9v6FX1a6/latest)
    • 嘿,谢谢你的努力,我会试试,让你知道它是怎么回事:)
    • 嘿,Sindi ..我只是在我的机器上运行你的代码,是的,我遇到了你上面提到的同样的问题......我没有更新我的 java 版本的 eclipse,但使用了 java。 util,base64 class from this site(grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/…) 你能告诉我我应该使用哪种方法来生成我的 Hmac 密钥。谢谢
    • 您是否运行了您编辑的代码并对其进行了测试?你得到了什么结果?
    【解决方案2】:

    我唯一的问题是字符编码,我假设是 UTF-8。我怀疑错误出在其他地方。

            // Send request
            byte[] data = urlParameters.getBytes(StandardCharsets.UTF_8);
            BufferedOutputStream wr = new BufferedOutputStream(connection.getOutputStream());
            wr.writeBytes(data);
            wr.close();
    
            // Get Response
            InputStream is = connection.getInputStream();
            BufferedReader rd = new BufferedReader(new InputStreamReader(is,
                    StandardCharsets.UTF_8));
    

    并且\r,CR,不作为行分隔符(除了旧的 MacOS)。

               response.append("\r\n"); // Or '\n'
    

    【讨论】:

    • 嘿,Eggen 感谢您的回复......但我仍然收到同样的错误:- java.lang.IllegalArgumentException:消息头值中的非法字符:AMru2osZZnotc25nRr7/dAOwkV/S3J1T7Yn7zs2XpFc=
    • 另外,当我尝试调试代码时,我发现类似:- java.lang.IllegalStateException: 已经连接
    • 尽量省略结尾的 = 填充字符。
    • 对不起,我没明白你的意思……你能详细说明一下吗,谢谢
    • Base64 键的值末尾有多余的等号。你可以试试没有。还有什么? URLEncoder.encode(value) 可能仍然是另一个更可疑的尝试。
    【解决方案3】:

    我终于通过在 api url hit 中发送直接字符串作为参数来解决这个错误。这里我发布了一些我的代码来解决我的错误:-

    String str = "{\"amount\":\"1299\",\"merchant_ref\":\"Astonishing-Sale\",\"transaction_type\":\"authorize\",\"credit_card\":{\"card_number\":\"4788250000028291\",\"cvv\":\"123\",\"exp_date\": \"1020\",\"cardholder_name\": \"John Smith\",\"type\": \"visa\"},\"method\": \"credit_card\",\"currency_code\": \"USD\"}";

    现在这个字符串将用于生成我的授权密钥。 整个过程定义如下 :-

    getSecurityKeys(apikey, pzsecret,str);
    
    private static Map<String, String> getSecurityKeys(String appId,
    			String secureId, String payLoad) throws Exception {
    		Map<String, String> returnMap = new HashMap<String, String>();
    		try {
    			returnMap.put(NONCE, Long.toString(nonce));
    			returnMap.put(APIKEY, appId);
    			returnMap.put(TIMESTAMP, Long.toString(System.currentTimeMillis()));
    			returnMap.put(TOKEN, MerchantToken);
    			returnMap.put(APISECRET, pzsecret);
    			returnMap.put(PAYLOAD, payLoad);
    			returnMap.put(AUTHORIZE, getMacValue(returnMap));
    			authorizationHeader = returnMap.get(AUTHORIZE);
    			return returnMap;
    
    		} catch (NoSuchAlgorithmException e) {
    			throw new RuntimeException(e.getMessage(), e);
    		}
    	}
    
    	public static String getMacValue(Map<String, String> data) throws Exception {
    		Mac mac = Mac.getInstance("HmacSHA256");
    		String apiSecret = data.get(APISECRET);
    		SecretKeySpec secret_key = new SecretKeySpec(apiSecret.getBytes(),
    				"HmacSHA256");
    		mac.init(secret_key);
    		StringBuilder buff = new StringBuilder();
    		buff.append(data.get(APIKEY)).append(data.get(NONCE))
    				.append(data.get(TIMESTAMP));
    		if (data.get(TOKEN) != null)
    			buff.append(data.get(TOKEN));
    		if (data.get(PAYLOAD) != null)
    			buff.append(data.get(PAYLOAD));
    		byte[] macHash = mac.doFinal(buff.toString().getBytes("UTF-8"));
    		String authorizeString = Base64.encodeBase64String(toHex(macHash));
    		return authorizeString;
    	}
    现在终于可以通过直接字符串(即str)作为参数在java中点击post api。

    希望它可以帮助其他人在不使用任何依赖项的情况下集成 payeezy 支付网关。 快乐编码!!!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-03-29
      • 2020-05-04
      • 2020-09-26
      • 1970-01-01
      • 2016-02-02
      • 2014-11-15
      • 2019-06-29
      相关资源
      最近更新 更多