Signature
During the payment process, signatures are used to ensure the security and integrity of transaction requests and responses. Merchants use secret keys to sign transaction data, preventing data tampering during transmission.
Signature Algorithm: SHA-256
Signature Process
Obtain the Merchant Secret Key: Log in to the Onerway dashboard → Click "Account" → "Account Info" → Retrieve the "Secret key" value from the page.
Prepare Data for Signing:
- Filter all request parameters where the
Sign
column is marked asYES
- Remove all empty parameters (null, empty string)
- Sort parameters by their names in
ASCII
order - Concatenate their values in the format
value1value2value3...
- Append the merchant
Secret key
to the end of the string
- Filter all request parameters where the
Generate Signature: Apply the
SHA-256
algorithm to the concatenated string
Asynchronous Notification Signing
Asynchronous Notifications use a separate signing method. Please refer to the related documentation.
Signature Example
Parameter | Sign |
---|---|
test1 | YES |
test2 | NO |
test3 | YES |
test4 | YES |
test5 | YES |
test6 | YES |
// Unsorted Original Parameters
{
"test3": "test3value",
"test2": "test2value",
"test1": "0",
"test5": "",
"test4": "test4value",
"test6": null
}
2
3
4
5
6
7
8
9
1. Filter Valid Parameters
Select parameters where the Sign
column is YES
and values are not empty, removing test2
, test5
, and test6
:
{
"test2": "test2value", // Signature column is NO, not included in the signature.
"test1": "0",
"test3": "test3value",
"test5": "",
"test4": "test4value",
"test6": null
}
2
3
4
5
6
7
8
2. Sort Parameters by ASCII Code
{
"test1": "0",
"test2": "test2value", // Signature column is NO, not included in the signature.
"test3": "test3value",
"test4": "test4value",
"test5": "",
"test6": null
}
2
3
4
5
6
7
8
3. Concatenate Parameter Values
0test3valuetest4value
4. Add Merchant Secret Key
Secret Key: 3b5e10b65bff4172a5b9ca2d2ec00a6e
Concatenated String: 0test3valuetest4value3b5e10b65bff4172a5b9ca2d2ec00a6e
5. Generate SHA-256 Signature
836831ae68fce5e61d4a64363bb72c636efe6e94b62ecaa8d1c95fc58cc9cbed
Signature Implementation Examples
Signature Implementation Notes
- Data Type Conversion: When signing, all values must be strings
- Object Handling: If a value is an
object
, convert it to a string first. See thebillingInformation
andshippingInformation
fields below - Nested Object Handling: If a value contains nested objects, convert the inner object to a string first, then convert the outer object. See the
txnOrderMsg
field below, where theproducts
field is a string converted from an array
Using the Checkout Payment request parameters as an example:
{
"billingInformation": "{\"country\":\"DE\",\"email\":\"abel.wang@onerway.com\",\"firstName\":\"şş\",\"lastName\":\"café\",\"phone\":\"17700492982\",\"address\":\"Apt. 870\",\"city\":\"Akşehir\",\"postalCode\":\"66977\",\"identityNumber\":\"12345678\",\"province\":\"Akşehir\"}",
"merchantCustId": "1723097638000",
"merchantNo": "800209",
"merchantTxnId": "1723097638000",
"merchantTxnTime": "2024-08-08 14:13:58",
"merchantTxnTimeZone": "+08:00",
"orderAmount": "100",
"orderCurrency": "USD",
"productType": "CARD",
"shippingInformation": "{\"country\":\"DE\",\"email\":\"abel.wang@onerway.com\",\"firstName\":\"şş\",\"lastName\":\"café\",\"phone\":\"17700492982\",\"address\":\"Apt. 870\",\"city\":\"Akşehir\",\"postalCode\":\"66977\",\"identityNumber\":\"12345678\",\"province\":\"Akşehir\"}",
"sign": "d23532edf2d8d4c6cb21b622bfbef4f067ef9ac2796e89117578037d11b04e98",
"subProductType": "DIRECT",
"txnOrderMsg": "{\"products\":\"[{\\\"price\\\":\\\"110.00\\\",\\\"num\\\":\\\"1\\\",\\\"name\\\":\\\"iphone11\\\",\\\"currency\\\":\\\"USD\\\"}]\",\"returnUrl\":\"https://docs.onerway.com/\",\"transactionIp\":\"127.0.0.1\",\"appId\":\"1739545982264549376\",\"javaEnabled\":false,\"colorDepth\":\"24\",\"screenHeight\":\"1080\",\"screenWidth\":\"1920\",\"timeZoneOffset\":\"-480\",\"accept\":\"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9\",\"userAgent\":\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36\",\"contentLength\":\"340\",\"language\":\"zh-CN\"}",
"txnType": "SALE"
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/*
$params: Data to be signed
Parameter type: One-dimensional array
$PrivateKey: Merchant secret key
Parameter type: String
*/
// ASCII code sorting and encryption
function ASCII_HASH($params, $payment_pacypayment_secret) {
$PrivateKey = $payment_pacypayment_secret;
if(!empty($params)) {
$p = ksort($params);
// Fields excluded from signature
$badkey = array('sign', 'route', 'originTransactionId', 'originMerchantTxnId',
'customsDeclarationAmount', 'customsDeclarationCurrency',
'paymentMethod', 'walletTypeName', 'periodValue', 'tokenExpireTime');
if($p) {
$strs = '';
foreach ($params as $k => $val) {
if((!empty($val) || $val == 0) && !in_array($k, $badkey)) {
$strs .= $val;
}
}
$strs = $strs.$PrivateKey;
return hash('sha256', $strs);
}
}
return 'error';
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import lombok.extern.slf4j.Slf4j;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.List;
import java.util.TreeMap;
@Slf4j
public class HashUtil {
public static void main(String[] args) {
TreeMap<String, Object> params = new TreeMap<>();
params.put("merchantNo", "800135");
params.put("test", "yesdas1dsa");
params.put("bizContent", "dsaaass1dsag");
params.put("as", "12334567");
params.put("bc", "098754");
// TODO: Replace with your own merchant secret key
String secret = "Your Secret Key";
// Concatenate request parameters and merchant secret key
String data = concatValue(params) + secret;
// Generate signature
String sign = hash(data);
System.out.println("Generated signature = " + sign);
}
/**
* Remove empty values and excluded parameters, then concatenate values into a string
*
* @param data Request parameters
* @return Concatenated string
*/
public static String concatValue(TreeMap<String, Object> data) {
// Fields excluded from signature
List<String> filteredOut = Arrays.asList(
"sign", "originTransactionId", "originMerchantTxnId",
"customsDeclarationAmount", "customsDeclarationCurrency",
"paymentMethod", "walletTypeName", "periodValue", "tokenExpireTime"
);
StringBuilder sb = new StringBuilder();
for (String key : data.keySet()) {
if (data.get(key) != null && !data.get(key).equals("") && !filteredOut.contains(key)) {
sb.append(data.get(key));
}
}
return sb.toString();
}
/**
* Apply SHA-256 encryption to the concatenated string
*
* @param concatStr Concatenated string
* @return Encrypted string
*/
public static String hash(String concatStr) {
String sign = null;
final String algorithm = "SHA-256";
try {
MessageDigest md = MessageDigest.getInstance(algorithm);
System.out.println("Concatenated string = " + concatStr);
md.update(concatStr.getBytes(StandardCharsets.UTF_8));
sign = byte2Hex(md.digest());
} catch (NoSuchAlgorithmException e) {
log.error("Error: NoSuchAlgorithmException, Algorithm {} is not available", algorithm, e);
} catch (Exception e) {
log.error("Error while encrypting message: {}", e.getMessage());
}
return sign;
}
/**
* Convert byte array to hexadecimal string
*
* @param bytes Byte array to convert
* @return Hexadecimal string representation
*/
public static String byte2Hex(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte value : bytes) {
String hexValue = Integer.toHexString(value & 0xFF);
if (hexValue.length() == 1) {
sb.append("0");
}
sb.append(hexValue);
}
return sb.toString();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
import hashlib
from collections import OrderedDict
class HashUtil:
@staticmethod
def concat_value(data):
"""
Remove empty values and excluded fields, then concatenate values into a string
"""
# Fields excluded from signature
filtered_out = [
"sign", "originTransactionId", "originMerchantTxnId",
"customsDeclarationAmount", "customsDeclarationCurrency",
"paymentMethod", "walletTypeName", "periodValue", "tokenExpireTime"
]
return ''.join(
str(value) for key, value in data.items()
if value is not None and value != "" and key not in filtered_out
)
@staticmethod
def hash(concat_str):
"""
Apply SHA-256 encryption to the concatenated string
"""
try:
# Encode concatenated string to bytes
concat_bytes = concat_str.encode('utf-8')
# Create SHA-256 hash
sha_signature = hashlib.sha256(concat_bytes).hexdigest()
print("Concatenated string:", concat_str)
return sha_signature
except Exception as e:
print("Error while encrypting message:", str(e))
return None
@staticmethod
def byte2hex(bytes_val):
"""
Convert byte array to hexadecimal string
"""
return ''.join(f'{byte:02x}' for byte in bytes_val)
if __name__ == "__main__":
# Sort fields alphabetically
params = OrderedDict([
("merchantNo", "800135"),
("test", "yesdas1dsa"),
("bizContent", "dsaaass1dsag"),
("as", "12334567"),
("bc", "098754"),
])
# TODO: Replace with your own merchant secret key
secret = "Your Secret Key"
# Concatenate request parameters and merchant secret key
data = HashUtil.concat_value(params) + secret
# Generate signature
sign = HashUtil.hash(data)
print("Generated signature =", sign)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63