Appendix 1
Security Control
Merchant and PayMaster share a SECRET KEY, used by both parties to create a Secure Hash Value for the transaction data.
- Obtain the Secret Key from the PayMaster Gateway.
The Secure Hash is a hexadecimal-encoded SHA-256 HMAC (or MD5 HMAC) of the concatenation of all fields in the Message Header and Data Content (excluding trailer fields), followed by the secret key.
Request Page t002_SHV
Before calling PayMaster, the Merchant must generate t002_SHV for the request message and append t001_SHT and t002_SHV as trailer fields.
Step 1 : Build the request string (without trailer)
Concatenate header and data fields in ascending field-ID order. Separate each name=value pair with &. Do not include t001_SHT or t002_SHV.
Example DuitNow Pay payment request (0880):
h001_MTI=0880&h002_VNO=06&h003_TDT=20260824&h004_TTM=12432905&f001_MID=120567801561128&f003_ProcCode=000000&f006_TxnDtTm=20010831124329&f007_TxnAmt=000000057080&f010_CurrCode=458&f019_ExpTxnAmt=2&f249_TxnCh=WEB&f253_CntyCode=MY&f256_FICode=RPP&f260_ServID=FNX&f263_MRN=2012121912345&f264_Locale=en&f268_CHName=Thomson&f275_RURL_DD=https://www.megastore.com/dd/done.jsp&f278_EMailAddr=thomson8899@yahoo.com&f279_HP=60128888888&f285_IPAddr=11.11.88.88&f352_AcqBank=FNXSMYNB
Step 2 : Append the Secret Key
Append the Secret Key to the string from Step 1:
Request Message without Trailer Fields + Secret Key
Step 3 : Generate the hash value
Encode the string using the algorithm specified in t001_SHT. In this example, SHA-256 HMAC (SH2) is used. Set the result as t002_SHV, then append the trailer fields to the request.
In this example:
- Assuming secret key is
00112233445566778899AABBCCDDEEFF, the hash value is6C825DC0E39F2917A8BC34A45F5BAF6EDDF7353446C19D3CC6296E6F3A962892.
Final request trailer:
t001_SHT=SH2&t002_SHV=6C825DC0E39F2917A8BC34A45F5BAF6EDDF7353446C19D3CC6296E6F3A962892
Sample Code (Generate)
public void reqPayment() throws Exception {
String secretKey = "00112233445566778899AABBCCDDEEFF";
String request =
"h001_MTI=0880&h002_VNO=06&h003_TDT=20260824&h004_TTM=12432905"
+ "&f001_MID=120567801561128&f003_ProcCode=000000"
+ "&f006_TxnDtTm=20010831124329&f007_TxnAmt=000000057080"
+ "&f010_CurrCode=458&f019_ExpTxnAmt=2&f249_TxnCh=WEB"
+ "&f253_CntyCode=MY&f256_FICode=RPP&f260_ServID=FNX"
+ "&f263_MRN=2012121912345&f264_Locale=en&f268_CHName=Thomson"
+ "&f275_RURL_DD=https://www.megastore.com/dd/done.jsp"
+ "&f278_EMailAddr=thomson8899@yahoo.com&f279_HP=60128888888"
+ "&f285_IPAddr=11.11.88.88&f352_AcqBank=FNXSMYNB"
+ secretKey;
SecretKeySpec byteSecretKey = new SecretKeySpec(secretKey.getBytes(), "HmacSHA256");
Mac sha256_MAC = Mac.getInstance("HmacSHA256");
sha256_MAC.init(byteSecretKey);
String hashed = this.bytesToHex(sha256_MAC.doFinal(request.getBytes())).toUpperCase();
System.out.println(hashed);
// 6C825DC0E39F2917A8BC34A45F5BAF6EDDF7353446C19D3CC6296E6F3A962892
}
public String bytesToHex(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02x", b));
}
return sb.toString();
}
Response Page t002_SHV
The payment response message includes t001_SHT and t002_SHV. The Merchant shall use these two values to verify the response message sent by PayMaster to ensure that the data has not been tampered with.
To verify this, the Merchant should remove the Trailer Fields (t001_SHT and t002_SHV) from the response message, append the remaining message with the Merchant's Secret Key, and then encode the string using the SHA-256 HMAC hashing algorithm.
Once the encoded result is obtained, compare it with the value in t002_SHV.
- The values must be equal.
Example
Step 1 : Remove trailer fields
Remove the trailer fields from the response message returned by the PayMaster Gateway.
Example DuitNow Pay payment response (0890):
h001_MTI=0890&h002_VNO=06&h003_TDT=20260824&h004_TTM=12432905&f001_MID=120567801561128&f003_ProcCode=000000&f006_TxnDtTm=20120831124329&f007_TxnAmt=000000057080&f010_CurrCode=458&f019_ExpTxnAmt=2&f254_DDRespCode=00&f256_FICode=RPP&f257_PGRN=AJ789EG07S0012012022&f258_TxnStatDetCde=0000&f259_TxnStatMsg=OK&f260_ServID=FNX&f263_MRN=2012121912345&f277_DDRN=DD200108301234&f286_OrgDDRespCode=00&f407_PayerAccType=TBH&t001_SHT=SH2&t002_SHV=0978A771F4F3395B00455BFBD7ADF732A09DBCFF65DE773009940DAA113248FC
h001_MTI=0890&h002_VNO=06&h003_TDT=20260824&h004_TTM=12432905&f001_MID=120567801561128&f003_ProcCode=000000&f006_TxnDtTm=20120831124329&f007_TxnAmt=000000057080&f010_CurrCode=458&f019_ExpTxnAmt=2&f254_DDRespCode=00&f256_FICode=RPP&f257_PGRN=AJ789EG07S0012012022&f258_TxnStatDetCde=0000&f259_TxnStatMsg=OK&f260_ServID=FNX&f263_MRN=2012121912345&f277_DDRN=DD200108301234&f286_OrgDDRespCode=00&f407_PayerAccType=TBH
Step 2 : Append the Secret Key to the response message
Append the Secret Key to the response message after removing the trailer fields. This will result in a string formatted as Response Message without Trailer Fields + Secret Key. Encode this string using the algorithm specified in t001_SHT. In this example, SHA-256 is used.
Step 3 : Compare the encoding result with the hash value
Compare the encoded result with the hash value in t002_SHV. The two values must match.
In this example:
- Assuming secret key is
00112233445566778899AABBCCDDEEFF, the hash value is0978A771F4F3395B00455BFBD7ADF732A09DBCFF65DE773009940DAA113248FC.
The Merchant MUST reject any non-matching results and report them to PayMaster.
Sample Code (Verify)
public void resPayment() throws Exception {
String secretKey = "00112233445566778899AABBCCDDEEFF";
String response =
"h001_MTI=0890&h002_VNO=06&h003_TDT=20260824&h004_TTM=12432905"
+ "&f001_MID=120567801561128&f003_ProcCode=000000"
+ "&f006_TxnDtTm=20120831124329&f007_TxnAmt=000000057080"
+ "&f010_CurrCode=458&f019_ExpTxnAmt=2&f254_DDRespCode=00"
+ "&f256_FICode=RPP&f257_PGRN=AJ789EG07S0012012022"
+ "&f258_TxnStatDetCde=0000&f259_TxnStatMsg=OK&f260_ServID=FNX"
+ "&f263_MRN=2012121912345&f277_DDRN=DD200108301234"
+ "&f286_OrgDDRespCode=00&f407_PayerAccType=TBH"
+ secretKey;
SecretKeySpec byteSecretKey = new SecretKeySpec(secretKey.getBytes(), "HmacSHA256");
Mac sha256_MAC = Mac.getInstance("HmacSHA256");
sha256_MAC.init(byteSecretKey);
String hashed = this.bytesToHex(sha256_MAC.doFinal(response.getBytes())).toUpperCase();
System.out.println(hashed.equalsIgnoreCase(
"0978A771F4F3395B00455BFBD7ADF732A09DBCFF65DE773009940DAA113248FC"));
}
public String bytesToHex(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02x", b));
}
return sb.toString();
}