Signature and Verification
Generate request 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 request signature:
- Sort request parameters by key in
ASCII
order - Concat each parameters' key value by
=
and join all parameters by&
, then append&
and your safecode at suffix of sign string - Encrypt sign string in
SHA256
by yourRSA private key
- Encode encrypted string in
base64
to get your final signature
// Concatenated string before sign
amount=1&channel=alipay¤cy=CNY&merchantid=123456&mid=1¬ifyurl=www.abc.com/callback&returnurl=www.abc.com/returnurl&service=Payment&PUT_YOUR_SAFECODE_HERE
- PHP
- Java
- Python
function generate_signature($params, $safecode, $sign_type): string
{
$serialize_sign = serialize_sign($params, $safecode, $sign_type);
$private_key = openssl_get_privatekey(file_get_contents('private_key.pem'));
openssl_sign($serialize_sign, $signature, $private_key, 'SHA256');
openssl_free_key($private_key);
return base64_encode($signature);
}
function serialize_sign(array $params, string $safecode, string $sign_type): string
{
switch ($sign_type) {
case 'payment':
$sign_fields = [
'user_id', 'order_id', 'amount', 'currency', 'channel',
'bank_code', 'callback_url', 'redirect_url', 'timestamp'
];
break;
case 'withdraw':
$sign_fields = [
'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':
$sign_fields = ['user_id', 'order_id'];
break;
case 'payment_order_response':
case 'withdraw_order_response':
$sign_fields = [
'user_id', 'order_id', 'transaction_id', 'channel',
'submit_currency', 'submit_amount', 'accept_currency', 'accept_amount',
'exchange_rate', 'status', 'timestamp'
];
break;
case 'payment_response':
$sign_fields = [
'user_id', 'order_id', 'transaction_id', 'channel',
'submit_currency', 'submit_amount', 'accept_currency', 'accept_amount',
'exchange_rate', 'pay_url'
];
break;
case 'withdraw_response':
$sign_fields = [
'user_id', 'order_id', 'transaction_id', 'channel',
'submit_currency', 'submit_amount', 'accept_currency', 'accept_amount',
'exchange_rate'
];
break;
case 'rate':
case 'rate_response':
$sign_fields = ['user_id', 'trade_currency'];
break;
case 'balance':
case 'balance_response':
default:
$sign_fields = ['user_id'];
break;
}
if ($sign_fields) {
$params = array_intersect_key($params, array_flip($sign_fields));
}
ksort($params);
$sign = '';
foreach ($params as $key => $value) {
$sign .= $key.'='.$value.'&';
}
$sign .= $safecode;
return $sign;
}
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;
}
def serialize_sign(params, safecode, sign_type):
sign_fields = []
if sign_type == 'payment':
sign_fields = [
'user_id', 'order_id', 'amount', 'currency', 'channel',
'bank_code', 'callback_url', 'redirect_url', 'timestamp'
]
elif sign_type == 'withdraw':
sign_fields = [
'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'
]
elif sign_type in ['order', 'payment_order', 'withdraw_order']:
sign_fields = ['user_id', 'order_id']
elif sign_type in ['payment_order_response', 'withdraw_order_response']:
sign_fields = [
'user_id', 'order_id', 'transaction_id', 'channel',
'submit_currency', 'submit_amount', 'accept_currency', 'accept_amount',
'exchange_rate', 'status', 'timestamp'
]
elif sign_type == 'payment_response':
sign_fields = [
'user_id', 'order_id', 'transaction_id', 'channel',
'submit_currency', 'submit_amount', 'accept_currency', 'accept_amount',
'exchange_rate', 'pay_url'
]
elif sign_type == 'withdraw_response':
sign_fields = [
'user_id', 'order_id', 'transaction_id', 'channel',
'submit_currency', 'submit_amount', 'accept_currency', 'accept_amount',
'exchange_rate'
]
elif sign_type in ['rate', 'rate_response']:
sign_fields = ['user_id', 'trade_currency']
elif sign_type in ['balance', 'balance_response']:
sign_fields = ['user_id']
if sign_fields:
# Get only the specified keys from the params dictionary
params = {key: value for key, value in params.items() if key in sign_fields}
# Sort the dictionary based on keys
sorted_params = dict(sorted(params.items()))
# Concatenate key-value pairs
sign = '&'.join([f'{key}={value}' for key, value in sorted_params.items()]) + '&' + safecode
return sign
def generate_signature(data):
print('data:::' + data)
with open('key/private_key.pem', 'rb') as key_file:
private_key = serialization.load_pem_private_key(
key_file.read(),
password=None,
)
signature = private_key.sign(
data.encode('utf-8'),
padding.PKCS1v15(),
hashes.SHA256()
)
encoded_signature = base64.b64encode(signature).decode('utf-8')
print('base64 encode signature:::' + encoded_signature)
return encoded_signature
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:
- Sort response parameters except signature by key in ASCII order. All parameters except signature should participate in the verification string
- Concat key/value of parameters by
=
symbol, then join all parameters by&
symbol - Append
&
and your merchant safecode at suffix of the verification string - Decode response signature by base64 decoder to binary signature
- Verify the binary signature via SHA256 with verification string and PTS public key
- PHP
- Java
- Python
function verify_signature($response, $safecode, $signType)
{
$params = $response['data'];
$signature = base64_decode($params['sign']);
$serialize_sign = serialize_sign($params, $safecode, $signType);
$pubKey = openssl_get_publickey(file_get_contents('plateform_public_key.pem'));
$verified = openssl_verify($serialize_sign, $signature, $pubKey, OPENSSL_ALGO_SHA256);
openssl_free_key($pubKey);
return $verified !== 0;
}
public static boolean verifySignature(Map<String, String> params, PublicKey publicKey, String sign,
String signType) {
try {
String serializeSign = serializeSign(params, signType);
Signature publicSignature = Signature.getInstance("SHA256withRSA");
publicSignature.initVerify(publicKey);
publicSignature.update(serializeSign.getBytes("UTF-8"));
byte[] signDecoded = Base64.getDecoder().decode(sign.replace("\n", ""));
boolean verifies = publicSignature.verify(signDecoded);
return verifies;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
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