Skip to main content
Version: 2.0.x

Data Signature and Verification

Generate data signature

Before generate signature, please make sure the following materials are ready:

  • Get your Merchant Safecode
  • Generate a RSA key pair. Your can generate it via our service or by yourself
  • Upload your RSA public key to our service

Please follow this guideline to generate your data signature:

  1. Sort request parameters by key in ASCII order
  2. Concat each parameters' key value by = and join all parameters by &, then append & and your safecode at suffix of sign string
  3. Encrypt sign string in SHA256 by your RSA private key
  4. Encode encrypted string in base64 to get your final signature
// Concatenated string before sign
amount=1&channel=alipay&currency=CNY&merchantid=123456&mid=1&notifyurl=www.abc.com/callback&returnurl=www.abc.com/returnurl&service=Payment&PUT_YOUR_SAFECODE_HERE
public static String generateSignature(Map<String, String> params, String signType) {
String serializeSign = serializeSign(params, signType);

System.out.println("generateSignature serializeSign::" + serializeSign);

try {
Signature privateSignature = Signature.getInstance("SHA256withRSA");
PrivateKey privateKey = getPrivateKey("src/main/resources/private_key.pem");
privateSignature.initSign(privateKey);
privateSignature.update(serializeSign.getBytes("UTF-8"));
byte[] sign = privateSignature.sign();
return Base64.getEncoder().encodeToString(sign);
} catch (Exception e) {
e.printStackTrace();
}

return null;
}

public static String serializeSign(Map<String, String> params, String signType) {
String[] signFields;
Map<String, String> signParams = new HashMap<>();

switch (signType) {
case "payment":
signFields = new String[] { "user_id", "order_id", "amount", "currency", "channel",
"bank_code", "callback_url", "redirect_url", "timestamp" };
break;
case "withdraw":
signFields = new String[] { "user_id", "order_id", "amount", "currency", "channel",
"card_no", "card_name", "card_type", "bank_code",
"bank_name", "bank_branch", "bank_province", "bank_city",
"cnaps_code", "callback_url", "timestamp" };
break;
case "order":
case "payment_order":
case "withdraw_order":
signFields = new String[] { "user_id", "order_id" };
break;
case "payment_order_response":
case "withdraw_order_response":
signFields = new String[] { "user_id", "order_id", "transaction_id", "channel",
"submit_currency", "submit_amount", "accept_currency", "accept_amount",
"exchange_rate", "status", "timestamp" };
break;
case "payment_response":
signFields = new String[] { "user_id", "order_id", "transaction_id", "channel",
"submit_currency", "submit_amount", "accept_currency", "accept_amount",
"exchange_rate", "pay_url" };
break;
case "withdraw_response":
signFields = new String[] { "user_id", "order_id", "transaction_id", "channel",
"submit_currency", "submit_amount", "accept_currency", "accept_amount",
"exchange_rate" };
break;
case "rate":
case "rate_response":
signFields = new String[] { "user_id", "trade_currency" };
break;
case "balance":
case "balance_response":
default:
signFields = new String[] { "user_id" };
break;
}

if (signFields.length > 0) {
for (String key : signFields) {
if (params.containsKey(key)) {
signParams.put(key, params.get(key));
}
}
}

Map<String, String> sortedParams = new TreeMap<>(signParams);

StringBuilder sign = new StringBuilder();
for (Map.Entry<String, String> entry : sortedParams.entrySet()) {
sign.append(entry.getKey()).append('=').append(entry.getValue()).append('&');
}
sign.append(safecode);

return sign.toString();
}

public static PrivateKey getPrivateKey(String filePath) {
try {
File file = new File(filePath);
FileInputStream fis = new FileInputStream(file);
DataInputStream dis = new DataInputStream(fis);
byte[] keyBytes = new byte[(int) file.length()];
dis.readFully(keyBytes);
dis.close();

String temp = new String(keyBytes);
String privateKeyPEM = temp.replace("-----BEGIN PRIVATE KEY-----\n", "");
privateKeyPEM = privateKeyPEM.replace("-----END PRIVATE KEY-----", "");
byte[] decoded = Base64.getDecoder().decode(privateKeyPEM.replace("\n", ""));

PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(decoded);
KeyFactory kf = KeyFactory.getInstance("RSA");

return kf.generatePrivate(privateKeySpec);
} catch (Exception e) {
e.printStackTrace();
}

return null;
}

Verify response signature of PTS

Before verify signature, please make sure the following materials are ready:

  • Get your Merchant Safecode
  • Get RSA public key of PTS

Please follow this guideline to verification signature sent from PTS:

  1. Sort response parameters except signature by key in ASCII order. All parameters except signature should participate in the verification string
  2. Concat key/value of parameters by = symbol, then join all parameters by & symbol
  3. Append & and your merchant safecode at suffix of the verification string
  4. Decode response signature by base64 decoder to binary signature
  5. Verify the binary signature via SHA256 with verification string and PTS public key
def verify_signature(pub_key_pem, before_sign_str, sign):
h = SHA256.new(before_sign_str.encode('utf-8'))
pubkey = RSA.import_key(pub_key_pem)
try:
pkcs1_15.new(pubkey).verify(h, base64.b64decode(sign))
return True
except (ValueError, TypeError):
return False