V2 Signature Specification
This document describes the signature mechanism used by all V2 APIs. V2 uses RSA-SHA256 exclusively for both request signing and response verification. MD5 signing is not supported in V2.
1. Key Preparation#
Generate Your RSA Key Pair#
1.
Generate a 2048-bit RSA key pair (use the KeyPairExample utility provided by OnlinePay).
2.
Your Private Key — Keep this secret in your backend. You will use it to sign requests.
3.
Your Public Key — Upload to OnlinePay Developer Center > User PublicKey.
Obtain the OnlinePay Public Key#
1.
In Developer Center, locate the field OnlinePay PublicKey.
2.
Click View to obtain the OnlinePay public key.
3.
You will use this key to:Verify response signatures
Verify and decrypt webhook notifications
2. Request Signing#
Every V2 API request must include a sign field in the request body. The sign value is generated as follows:Step 1 — Build the Sign String#
1.
Take all request body parameters.
2.
Exclude the following fields (they do not participate in signing):| Excluded Field | Reason |
|---|
sign | The signature itself |
authorization | HTTP header value |
referer | HTTP header value |
paymentType | HTTP header value |
serverName | Server-side value |
userAgent | HTTP header value |
protocolId | Internal field |
isfunction | Internal field |
3.
Skip any field with a null or empty value.
4.
Sort the remaining fields alphabetically by key (ascending, case-sensitive).
5.
For nested objects and arrays: serialize to JSON with all keys sorted alphabetically at every level.
6.
Concatenate as key1=value1&key2=value2.
Request body:
{
"merNo": 104001001,
"merOrderNo": "ORD20260527001",
"currencyCode": "USD",
"sourceAmount": "100.00",
"notifyUrl": "https://merchant.com/notify",
"returnUrl": "https://merchant.com/return",
"sign": "..."
}
Fields after exclusion & sorting:
currencyCode=USD&merNo=104001001&merOrderNo=ORD20260527001¬ifyUrl=https://merchant.com/notify&returnUrl=https://merchant.com/return&sourceAmount=100.00
Request body contains:
{
"productInfoList": [
{"sku": "SKU001", "price": "50.00", "productName": "Product A"}
],
"merNo": 104001001
}
Sign string (productInfoList serialized with sorted keys):
merNo=104001001&productInfoList=[{"price":"50.00","productName":"Product A","sku":"SKU001"}]
Step 2 — Generate the RSA-SHA256 Signature#
sign = Base64(SHA256WithRSA(signString, yourPrivateKey))
Algorithm: SHA256WithRSA (RSASSA-PKCS1-v1_5 with SHA-256)
Input: The sign string from Step 1
Key: Your RSA private key (PKCS#8 format, Base64 encoded)
Output: Base64-encoded signature string
Step 3 — Include in Request#
Add the generated sign value to the request body:{
"merNo": 104001001,
"merOrderNo": "ORD20260527001",
"currencyCode": "USD",
"sourceAmount": "100.00",
"sign": "Base64EncodedSignatureString..."
}
3. Response Verification#
V2 API responses include a sign field at the top level:{
"code": "00000",
"message": "SUCCESS",
"data": { ... },
"sign": "Base64EncodedSignatureString..."
}
Verification Steps#
1.
Build the sign string from the response fields using the same rules as request signing (exclude sign, skip null/empty, sort alphabetically, concatenate as key=value pairs).
2.
Verify the signature using the OnlinePay RSA Public Key:
boolean valid = SHA256WithRSA.verify(responseSignString, response.sign, onlinePayPublicKey)
3.
If verification fails, do not trust the response — contact OnlinePay support.
4. Webhook Notification Verification#
V2 webhook notifications (payment result, refund result, chargeback, etc.) use RSA+AES hybrid encryption. You will receive an encrypted payload instead of plaintext JSON.Notification Payload Structure#
{
"encryptedData": "AES-encrypted payment result (Base64)",
"encryptedKey": "RSA-encrypted AES key (Base64)",
"signType": "RSA256"
}
Step 1 — Decrypt the AES Key#
Use the OnlinePay RSA Public Key to decrypt the encryptedKey:aesKey = RSA_Decrypt(Base64Decode(encryptedKey), onlinePayPublicKey)
Note: OnlinePay encrypts the AES key with its private key. You decrypt with the OnlinePay public key.
Step 2 — Decrypt the Data#
Use the AES key from Step 1 to decrypt encryptedData:plaintext = AES_Decrypt(Base64Decode(encryptedData), aesKey)
The decrypted result is a JSON string containing the notification data.Step 3 — Verify the Signature#
The decrypted JSON contains a sign field:{
"tradeNo": "T20260527001",
"merOrderNo": "ORD20260527001",
"code": "00000",
"message": "SUCCESS",
"sign": "Base64EncodedSignature..."
}
Verify the signature using the same method as response verification (Section 3):1.
Build the sign string from all fields in the decrypted JSON, excluding sign and other excluded fields listed in Section 2.
2.
Verify with the OnlinePay RSA Public Key:
boolean valid = SHA256WithRSA.verify(signString, notification.sign, onlinePayPublicKey)
Step 4 — Acknowledge the Notification#
After successful verification, respond with HTTP 200. Any other status code will trigger OnlinePay to retry the notification.Retry Policy: If OnlinePay does not receive HTTP 200, it will retry at increasing intervals. Ensure your endpoint is idempotent.
5. Quick Reference#
| Operation | Key Used | Algorithm |
|---|
| Sign request | Your RSA private key | SHA256WithRSA |
| Verify response | OnlinePay RSA public key | SHA256WithRSA |
| Decrypt notification AES key | OnlinePay RSA public key | RSA decrypt |
| Decrypt notification data | Random AES key (from above) | AES decrypt |
| Verify notification signature | OnlinePay RSA public key | SHA256WithRSA |
6. Error Codes#
| Code | Message | Description |
|---|
| 40002 | SIGN_ERROR | Signature verification failed. Check your private key and sign string construction. |
Modified at 2026-05-27 08:59:15