The encoding format is fixed as: UTF-8.
1.
The signature string consists of key-value pairs from all non-empty fields except sign, x-nexus-api-key, and versions.
2.
All fields participating in the signature are sorted in ascending order according to the ASCII code of the field names, then concatenated into a string in URL key-value pair format (i.e., key1=value1&key2=value2).
Example:
appId=companyAppId001
nonce=V6BC6WHMU1D2NGT17D959C4W6RQP3I0D
timestamp=20250421111104
userCode=54
The sorted string:
string="appId=companyAppId001&nonce=V6BC6WHMU1D2NGT17D959C4W6RQP3I0D×tamp=20250421111104userCode=54"
3.
Sign with the merchant's RSA private key;
RSAUtils.sign(string, cusRsaPrivateKey)
Such as testing the private key:
cusRsaPrivateKey = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCKu0DkMEGg4oRuVxL/vA8rhltFd+iN+T48VtyG/fM4qI0UaDixYeSBr78z/6hoBfqomUzFxlKhtQvR0gXN1NWH47wLH9yMJ7nqBtsgGfnfjFCKusmbnw+DpK2EcQfftEgbtRpp/WlUW2dlCt3uSa0Rkf8anbMYYYics7xu3VkBjo/CFMlP/yAf6KrmgBQ/tfmydOFPsQ3FgP9Wi/v4bEZkSjzBiLMzNaiXVMw1Uh4VjWXoz6ZtiDjE4xmL6Z7oukkYkZZwP00v0LQS4uyWHdr40rbfqYed3oMoZzIqXH+xaATFQrAfkGgBb8VSPdl0UzrH6iPHN5HBs6IAX95dS9o1AgMBAAECggEAUXkW6uvMHwfobkSEy9tNrWoCQBHjaT1u51C7witxo1yZPdrXMJUk9WSZDZie9jhMtKcTBzMpY/5HKroE3pSXsm50CLR1eXn7HQ1ZssiOjlKBGngH3nTTT6RuotEepqnhjyQUlxpTuU7Usepn24E5OpBbGO50N2VQLXcAursTgm2xaKCm8/dC17kpmeNT53bAECuFWhA2VvfeEtjgkafLdEZDnazOxAaRPha9IT64e2D0JiW9ituFBwJELcE6vZ2Av61onGtq8QZ7eAOqhzJ2Pfgxxhxk6HGJu1CCkWmXEscE8+kEMqLt0ziCCXRAniFtImmdCVzTWG91asktcAB/AQKBgQDAgAMYJizYsSDv7TK+67bul4au6RsBcD9vxICcX9OyFk5o3ucmFrYgPe1+BH1/3b42vTLjnAy0mhVZgiFNPqVuaX6OsTcX+6D8ndkS95csBN1oDJ1ElQ1TZGKnJ0FZGualLZTx4szZebR46RFVOD1fS30191SKYPlywvYV2QkQfQKBgQC4fqnF4pVOhEA+z7YRaWw8fhe33vdsHnXVhoEpJNvblLCxpOcs5ksjN3RtX2sI/9JGDqcc2RWrohu8oe5cojRdwyW0niyAkyjE7f5kiP7aDSM7JcAmRKMXTzQuaVnG/b+GrvIQ2bCp5KZZ93j3sAT3tzt/zefW+UQELUys1sCWGQKBgQCpOZ/+wciY8hX/dakvt64IT0LCTwBDonbR/ZAGaCOFIiDqBlJz5HFVJLu8FZxyJPBL1MWA3DU7K11rY399FX5+8tSkAlxpg/bBPM6Q4wGNW/V7u+MhYFrnnY8wXM0Q1Ro/yTNv9S6aXGhwBowLO7aQKJ+5KmWyhI+l+Ig3KqXSTQKBgQCDy7A6NcgVQ/KlLDrTK21vtV/6MSStLwFcXO6t1q/cp9alwLiIYadxa+8XJt/OmPm8pEELIaUSbAbGdSNTN2iTRpIL+iauXvHz5FQju53bZSEy4p/mdofPYfxT31yMA78rVpSEJw7F/xo7EMdwoWSbFWFCxnQFJSJKnZW1rt/7GQKBgFq1PPIWoPvYbMpMMWyzQbQtXHt6CxA9PDi8ZGslxQHBfsl1WXieEGP/JUXcZchLyHFzFvvQf3uK2EkWwjqeIrJF4VrMEMLpdIjR9fhnhLd6j9M52iLhu/q05W3xkU54oF9iD83KHtzqt5qOkSW6PBoBR3BwFyT+bAfg4JJYB8BD";
The encrypted string above is:
sign="DYtffEz/KTeHDOtG47pBbNntgJJEM2akmPRS2w9V43HDTSW5S52d8/k7596cO6/MF/IeEus3o9NLtqKMFwrZc0ju/qYHaSJbYL/YnPgs3peoI8xMTobge2bvIeEdbQXNxb0yxCQe5Im8Ndi5fITTfdFcz1ZM8+4T9M6MSDBnjfdpHpGV/KSgRFAeh71kP2PFYzt/g0NNqWVbeJCJVvStdYYumMis9fnY+quyD1XF8V+gymaQQWIxDKBXbMSpJuOgxjVuHE0jIe+x2dP8PNC/BlxspAFbdv1SRK7qOz7Ph83NPETm2RRigIA2SxLEw+ITC7PfI5kI6+PpZj29wk0KsQ==";
4.
After the request endpoint is successfully sent, the JSON string data is returned;
All parameters (except sign, x-nexus-api-key, and versions fields) must be signed as long as they have values (excluding empty strings), otherwise the signature verification will fail!Code Example (JAVA Version)
Contains MAVEN-JAR package version
https://oss.tevaupay.com/tevau-nexus-test/Public_catalogue/demo.zip
5.
Verify the incoming webhook;
{
"orderId": "12345",
"eventType": "UsdtDeposit",
"tradeStatus": "Success"
}
To ensure the authenticity of the incoming Webhook request, you should verify the digital signature contained in the "x-timestamp" and "x-signature" headers. Here is an example of how to perform this in JAVA: private static final String RSA = "RSA";
private static final String SIGN_ALGORITHMS = "SHA1WithRSA";
public static void main(String[] args) throws Exception {
//Post all request parameters
String body = "{\"orderId\":\"12345\",\"eventType\":\"UsdtDeposit\",\"tradeStatus\":\"Success\"}";
//Headers: x-timestamp
String timestamp = "20250903140909";
//Headers:x-signature
String signature = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzURhq9eaOYsGCnYK0JPgXduRKCOJgbJmiASW+1L+xx3pvsxLH/OScxMwXsEacygmYFHV7MijNEOGSuzbd2u0O7/SzHgqCnn1ID6ehZQktnnF9gZQb1muBmvM4In9+2lSXmuF/t547rpcBuXZaEf5KXR+gQK82u9i8KRl8IszoGoPa4mxf8Krr+C1uXgFhgHyyLxi0GuCOoAJ/lvD5ArIhyOwYS4sGJJXDHt6ZLfNcnyaNHQmx9b7YnytIdswqd6mQ1tN7eHjzcvcE8baArCdZ9E0yVyTQ9tHZ12fhg+qq8HLRd/qJDwTxYYILZ3fZDNm+OPo88iXRjr2qI44CF6y4wIDAQAB";
//WebHook signature verification public key
String publicKey = "";
String data = "timestamp=" + timestamp + getSortedString(body);
boolean verifyResult = verify(data, signature, publicKey);
System.out.printf("Print the verification result:" + verifyResult);
}
/**
* Public key signature verification
*
* @param data
* @param sign
* @param publicKey
* @return
* @throws Exception
*/
public static boolean verify(String data, String sign, String publicKey) throws Exception {
byte[] keyBytes = java.util.Base64.getDecoder().decode(publicKey);
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(RSA);
PublicKey publicK = keyFactory.generatePublic(x509EncodedKeySpec);
Signature signature = Signature.getInstance(SIGN_ALGORITHMS);
signature.initVerify(publicK);
signature.update(data.getBytes());
return signature.verify(Base64.getDecoder().decode(sign));
}
/**
* getSortedString Sort the parameters by Key in ASCII order
*
* @param requestBodyStr required parameter
* @return The sorted and assembled string
*/
public static String getSortedString(String requestBodyStr) {
if (jodd.util.StringUtil.isEmpty(requestBodyStr)) {
return "";
}
com.aliyun.openservices.shade.com.alibaba.fastjson.JSONObject jsonObject = com.aliyun.openservices.shade.com.alibaba.fastjson.JSON.parseObject(requestBodyStr);
SortedMap<String, Object> sortMap = new TreeMap<>();
StringBuffer sbf = new StringBuffer();
for (Map.Entry<String, Object> objectEntry : jsonObject.entrySet()) {
String key = objectEntry.getKey();
Object value = objectEntry.getValue();
if ("sign".equals(key)) {
continue;
}
sortMap.put(key, value);
}
Set mapEntrySet = sortMap.entrySet();
Iterator it = mapEntrySet.iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
String k = (String) entry.getKey();
Object v = entry.getValue();
sbf.append(k + "=" + v + "&");
}
String sbfString = sbf.toString().replaceAll("\"", "");
return sbfString.substring(0, sbfString.length() - 1);
}