Appendix A (Data Encryption / Decryption)
To secure your Biz data with data encryption
✅ Data Encryption Process with API Transaction Data
As our API protocol, The Transaction Data within API need to be encrypted in AES methodology which encryption mode is ECB and fill mode is PKCS5 Padding.
☑️ Encryption STEPS:
Sort the data (JSON data format) in dictionary order by their key name, then concatenate their corresponding value into one data string.
Calculate the MD5 value of the data string from step one, and then convert to uppercase. This value will be also passed in as one of the HTTP request parameters.
Concatenate the MD5 value from the STEP 2 and merchant encryption key (signkey) provided by OTT
Perform the MD5 16-bit calculation, and then uppercase it again as AES key
Perform AES and Base64 encryption to the data string
☑️ Encryption Example:
Here is the original transaction data (JSON data format) as below,
{'orderId' : '1234567890', 'idCardName' : ‘张三‘, 'idCardNum' : '123456789012345678', 'bankCardNum' : '62000000000010', 'mobile' : '13711111111', 'idCardType' : '1'}
1️⃣ After Step-1 sorting and concatenate:
62000000000010张三1234567890123456781137111111111234567890
2️ After Step-2 MD5 calculation:
C12F9560769C2CB55E6954935B325916
3️⃣ After Step-3 (Merchant SignKey:6698851A525C9433):
C12F9560769C2CB55E6954935B3259166698851A525C9433
4️⃣ After Step 4: Get AES Key
EF1712FC070C2C21
5️⃣ After Step 5: get Encrypted String
Y2+GoqBBPc6EJ9JzXRFoOziKd+DqWg5FiUZY1IYoVBVv0xfFznT9/qanMpiNEamEN2NM7J+hxoRn8VGSJImA2soeip9nYr+VJvTXe7D8j4aXiKyFipuPVCQLiCDg3jkJP+S2EezYMf7crqKY/YAni1CCeIwr2aJfFVT1vYhsWIm8t3eyPL//cY4kwombAKhE2gAdEFXz6gVvencz80aRWQ==
✅ Data Decryption Process with API Transaction Data
Response/Call-Back API Transaction data is encrypted in AES methodology which encryption mode is ECB and fill mode is PKCS5 Padding.
☑️ Decryption STEPS:
get the data field string value from response/call-back payload parameters level data.
get the md5 field string value from response/call-back payload parameters level data
Get AES key by above above md5 string and merchant signkey provided by OTT Pay
Apply Base64 decoding and Decrypt the step-2 result by using the AES key get from Step 3.
☑️Decryption Example:
Here is the response message with transaction data (JSON data format) as below,
{
"rsp_code":"SUCCESS",
"rsp_msg":"success",
"data":"Y2+GoqBBPc6EJ9JzXRFoOziKd+DqWg5FiUZY1IYoVBVv0xfFznT9/qanMpiNEamEN2NM7J+hxoRn8VGSJImA2soeip9nYr+VJvTXe7D8j4aXiKyFipuPVCQLiCDg3jkJP+S2EezYMf7crqKY/YAni1CCeIwr2aJfFVT1vYhsWIm8t3eyPL//cY4kwombAKhE2gAdEFXz6gVvencz80aRWQ==",
"md5":"C12F9560769C2CB55E6954935B325916"
}
1️⃣ After Step-1 data string value:
Y2+GoqBBPc6EJ9JzXRFoOziKd+DqWg5FiUZY1IYoVBVv0xfFznT9/qanMpiNEamEN2NM7J+hxoRn8VGSJImA2soeip9nYr+VJvTXe7D8j4aXiKyFipuPVCQLiCDg3jkJP+S2EezYMf7crqKY/YAni1CCeIwr2aJfFVT1vYhsWIm8t3eyPL//cY4kwombAKhE2gAdEFXz6gVvencz80aRWQ==
2️ After Step-2 MD5 string value:
C12F9560769C2CB55E6954935B325916
3️⃣ After Step 3: Get AES Key(Merchant SignKey: 6698851A525C9433):
EF1712FC070C2C21
4️⃣ After Step 4: Base64 decoding and decrypted String:
{'orderId' : '1234567890', 'idCardName' : ‘张三‘, 'idCardNum' : '123456789012345678', 'bankCardNum' : '62000000000010', 'mobile' : '13711111111', 'idCardType' : '1'}
✅ Code Samples (Payment Request API)
Here are code samples in different development languages.
package com.ottpay.demo.test;
import com.google.gson.Gson;
import com.ottpay.demo.utils.AppCommonUtils;
import com.ottpay.demo.utils.HTTPSCommClient;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.builder.ToStringBuilder;
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
public class ActivePayTest {
static String key = "XXXyourSignKeyXXX"; //using your SignKey provided by OTTPAY;
static Gson gson = new Gson();
static String url = "https://frontapi.ottpay.com/process";
static String merchantId="ON0000XXXX"; //using your Merchant ID provided by OTTPAY;
public static void main(String[] args) throws NoSuchAlgorithmException, KeyManagementException, IOException {
Map<String, String> bo = new HashMap<String, String>();
bo.put("order_id", "ACT" + System.currentTimeMillis());
bo.put("call_back_url", "http://www.yourcallbackurl.com"); //using your call back url
bo.put("biz_type", "WECHATPAY");
bo.put("operator_id", "XXXXXXX"); //using your 10-digital operator number provided by OTTPAY;
bo.put("amount", "1");
Map map = doSend(bo, "ACTIVEPAY");
}
/**
* 将Map存储的对象,转换为key=value&key=value&的字符,并将key按顺序排序
*/
public static String sortStringByMap(Map<String, String> map) {
String[] arrayToSort = map.keySet().toArray(new String[map.keySet().size()]);
Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < arrayToSort.length; i++) {
sb.append(map.get(arrayToSort[i]));
}
return sb.toString();
}
private static String signByMD5(Map<String, String> objectAsMap) {
String result = sortStringByMap(objectAsMap);
String sign = DigestUtils.md5Hex(result).toUpperCase();
return sign;
}
private static Map doSend(Map<String,String> bo, String type) throws NoSuchAlgorithmException, KeyManagementException, IOException {
Map<String, String> vo = new HashMap<String, String>();
vo.put("action", type);
vo.put("version", "1.0");
vo.put("merchant_id", merchantId);
String md5 = signByMD5(bo);
String encrypted = AppCommonUtils.encrypted(gson.toJson(bo), key, md5);
System.out.println(encrypted);
vo.put("data", encrypted);
vo.put("md5", md5);
Map<String, String> appRespVO = gson.fromJson(HTTPSCommClient.post(url, gson.toJson(vo)), Map.class);
System.out.println(ToStringBuilder.reflectionToString(appRespVO));
Map decipher = gson.fromJson(AppCommonUtils.decipher(appRespVO.get("data"), key, appRespVO.get("md5")), Map.class);
System.out.println("data:" + decipher.toString());
return decipher;
}
}
//---------------------------AppCommonUtils
package com.ottpay.demo.utils;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import org.apache.commons.codec.digest.DigestUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Base64Utils;
import java.util.TreeMap;
public class AppCommonUtils {
static Logger logger = LoggerFactory.getLogger(AppCommonUtils.class);
static Gson gson = new Gson();
/**
* 解密
*
* @param data
* @param key
* @param md5
* @return
*/
public static String decipher(String data, String key, String md5) {
byte[] orgData = Base64Utils.decodeFromString(data);
String aesKeyStr = DigestUtils.md5Hex(md5 + key).substring(8, 24).toUpperCase();
byte[] aesKey = aesKeyStr.getBytes();
String decData = new String(AES.decrypt(orgData, aesKey));
TreeMap<String, Object> treeMap = null;
try {
treeMap = gson.fromJson(decData, TreeMap.class);
} catch (JsonSyntaxException e) {
logger.error("decrypt error!exception:{}", e);
return null;
}
StringBuilder stb = new StringBuilder();
for (String tk : treeMap.keySet()) {
stb.append(treeMap.get(tk));
}
logger.debug("Check the original string data:{}", stb.toString());
String calmd5 = DigestUtils.md5Hex(stb.toString());
if (calmd5.toUpperCase().equals(md5.toUpperCase())) {
return decData;
} else {
throw new RuntimeException("check sign fail");
}
}
/**
* 加密
*
* @param data json
* @param key
* @param md5
* @return
*/
public static String encrypted(String data, String key, String md5) {
String aesKeyStr = DigestUtils.md5Hex(md5 + key).substring(8, 24).toUpperCase();
byte[] encrypt = AES.encrypt(data.getBytes(), aesKeyStr.getBytes());
return Base64Utils.encodeToString(encrypt);
}
public static String getMd5(Object obj) {
String str = gson.toJson(obj);
TreeMap<String, Object> treeMap = gson.fromJson(str, TreeMap.class);
StringBuffer stb = new StringBuffer();
for (String tk : treeMap.keySet()) {
stb.append(treeMap.get(tk));
}
return DigestUtils.md5Hex(stb.toString()).toUpperCase();
}
}
//---------------------------AES
/**
* Copyright: Copyright (c)2017
*/
package com.ottpay.demo.utils;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
public class AES {
/**
* 加密
*
* @param data
* 需要加密的内容
* @param key
* 加密密码
* @return
*/
public static byte[] encrypt(byte[] data, byte[] key) {
CheckUtils.notEmpty(data, "data");
CheckUtils.notEmpty(key, "key");
if(key.length!=16){
throw new RuntimeException("Invalid AES key length (must be 16 bytes)");
}
try {
SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
byte[] enCodeFormat = secretKey.getEncoded();
SecretKeySpec seckey = new SecretKeySpec(enCodeFormat, "AES");
Cipher cipher = Cipher.getInstance("AES");// 创建密码器
cipher.init(Cipher.ENCRYPT_MODE, seckey);// 初始化
byte[] result = cipher.doFinal(data);
return result; // 加密
} catch (Exception e){
throw new RuntimeException("encrypt fail!", e);
}
}
/**
* 解密
*
* @param data
* 待解密内容
* @param key
* 解密密钥
* @return
*/
public static byte[] decrypt(byte[] data, byte[] key) {
CheckUtils.notEmpty(data, "data");
CheckUtils.notEmpty(key, "key");
if(key.length!=16){
throw new RuntimeException("Invalid AES key length (must be 16 bytes)");
}
try {
SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
byte[] enCodeFormat = secretKey.getEncoded();
SecretKeySpec seckey = new SecretKeySpec(enCodeFormat, "AES");
Cipher cipher = Cipher.getInstance("AES");// 创建密码器
cipher.init(Cipher.DECRYPT_MODE, seckey);// 初始化
byte[] result = cipher.doFinal(data);
return result; // 加密
} catch (Exception e){
throw new RuntimeException("decrypt fail!", e);
}
}
}
//----------------------HTTPSCommClient
package com.ottpay.demo.utils;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Map;
public class HTTPSCommClient {
/**
* POST方式请求
*
* @param uri
* 服务器的uri要用物理IP或域名,不识别localhost或127.0.0.1形式!
* @param paramMap
* @return
* @throws IOException
* @throws NoSuchAlgorithmException
* @throws KeyManagementException
*/
public static String post(String uri, Map<String, String> paramMap)
throws NoSuchAlgorithmException, KeyManagementException,
IOException {
String data = null;
StringBuffer stb = new StringBuffer();
if (paramMap != null) {
for (String key : paramMap.keySet()) {
stb.append(key).append("=").append(paramMap.get(key))
.append("&");
}
System.out.println(stb.toString());
data = stb.toString().substring(0, stb.toString().length() - 1);
System.out.println(data.toString());
}
URL url = new URL(uri);
TrustManager[] tms = { new MyX509TrustManager() };
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, tms, new SecureRandom());
SSLSocketFactory ssf = sc.getSocketFactory();
HttpsURLConnection https = (HttpsURLConnection) url.openConnection();
https.setSSLSocketFactory(ssf);
https.setHostnameVerifier(new TrustAnyHostnameVerifier());
https.setDoInput(true);
https.setDoOutput(true);
https.setRequestMethod("POST");
https.setRequestProperty("Content-Length",
String.valueOf(data.getBytes().length));
https.connect();
DataOutputStream os = new DataOutputStream(https.getOutputStream());
if (data != null) {
os.write(data.getBytes("utf-8"));
}
os.flush();
os.close();
int responseCode = https.getResponseCode();
if(responseCode!=200){
throw new IOException("https's responseCode is :"+responseCode);
}
InputStream in = https.getInputStream();
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] bs = new byte[1024];
int len = 0;
while ((len = in.read(bs)) != -1) {
out.write(bs, 0, len);
}
in.close();
byte[] byteArray = out.toByteArray();
out.close();
System.out.println(new String(byteArray, "utf-8"));
https.disconnect();
return new String(byteArray, "utf-8");
}
public static String post(String uri,String data) throws NoSuchAlgorithmException, KeyManagementException, IOException{
URL url = new URL(uri);
TrustManager[] tms = { new MyX509TrustManager() };
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, tms, new SecureRandom());
SSLSocketFactory ssf = sc.getSocketFactory();
HttpsURLConnection https = (HttpsURLConnection) url.openConnection();
https.setSSLSocketFactory(ssf);
https.setHostnameVerifier(new TrustAnyHostnameVerifier());
https.setDoInput(true);
https.setDoOutput(true);
https.setRequestMethod("POST");
https.setRequestProperty("Accept-Charset", "application/json");
https.setRequestProperty("Content-Type", "application/json");
https.setRequestProperty("Content-Length",
String.valueOf(data.getBytes("utf-8").length));
https.setConnectTimeout(30000);
https.setReadTimeout(30000);
https.connect();
DataOutputStream os = new DataOutputStream(https.getOutputStream());
if (data != null) {
os.write(data.getBytes("utf-8"));
}
os.flush();
os.close();
int responseCode = https.getResponseCode();
if(responseCode!=200){
throw new IOException("https's responseCode is :"+responseCode);
}
InputStream in = https.getInputStream();
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] bs = new byte[1024];
int len = 0;
while ((len = in.read(bs)) != -1) {
out.write(bs, 0, len);
}
in.close();
byte[] byteArray = out.toByteArray();
out.close();
System.out.println(new String(byteArray, "utf-8"));
https.disconnect();
return new String(byteArray, "utf-8");
}
}
using System;
using System.Collections.Generic;
using System.Text;
using RestSharp;
using System.Security.Cryptography;
using System.Reflection;
using System.Linq;
using System.IO;
using Newtonsoft.Json;
namespace activePaydemo
{
class apiclient
{
public String resptext;
private RestClient client;
private RestRequest request;
private IRestResponse response;
private static String key = "5B5502E9BE8248CA"; //using your SignKey provided by OTTPAY;
private static String url = "https://frontapi.ottpay.com:443/processV2";
private static String merchantId = "QC00005496"; //using your Merchant ID provided by OTTPAY;
private static String type = "ACTIVEPAY";
private static String version = "1.0";
private static readonly DateTime Jan1st1970 = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
public apiclient()
{
var client = new RestClient(url);
RestRequest request = new RestRequest(Method.POST);
request.AddHeader("Cache-Control", "no-cache");
request.AddHeader("Content-Type", "application/json");
Paydata bo = new Paydata();
bo.order_id = "2020041179731O73O320";
bo.call_back_url = "https://www.ezshops.ca/wxnativeOTTPAY_callback.php"; //"https://www.ezshops.ca/wxnativeOTTPAY_callback.php"; //using your call back url
bo.biz_type = "WECHATPAY";
bo.operator_id = "0000020156"; //using your 10-digital operator number provided by OTTPAY;
bo.amount = "320";
string md5 = ott_aestool.signByMD5(bo);
string data_str = JsonConvert.SerializeObject(bo);
String encrypted = ott_aestool.EncryptAes(data_str, key, md5);
Console.WriteLine(encrypted);
payrequest vo = new payrequest();
vo.action = type;
vo.version = version;
vo.merchant_id = merchantId;
vo.data = encrypted;
vo.md5 = md5;
string reqstr = JsonConvert.SerializeObject(vo);
request.AddParameter("undefined", reqstr, ParameterType.RequestBody);
response = client.Execute(request);
payresponse results = JsonConvert.DeserializeObject<payresponse>(response.Content);
resptext = ott_aestool.DecryptAes(results.data, key, results.md5);
Console.WriteLine("data:" + resptext);
}
}
public static class ott_aestool
{
public static String sortStringByMap(Object map)
{
Type myType = map.GetType();
IList<PropertyInfo> props = new List<PropertyInfo>(myType.GetProperties());
IEnumerable<PropertyInfo> sortedEnum = props.OrderBy(f => f.Name);
IList<PropertyInfo> sortedList = sortedEnum.ToList();
String sb = "";
foreach (PropertyInfo prop in sortedList)
{
object propValue = prop.GetValue(map, null);
sb += propValue.ToString();
// Do something with propValue
}
return sb;
}
public static String signByMD5(Object paydata)
{
String result = sortStringByMap(paydata);
String sign = GetMd5Hash(result).ToUpper();
return sign;
}
public static string GetMd5Hash(string input)
{
MD5 md5Hash = MD5.Create();
// Convert the input string to a byte array and compute the hash.
byte[] data = md5Hash.ComputeHash(Encoding.UTF8.GetBytes(input));
// Create a new Stringbuilder to collect the bytes
// and create a string.
StringBuilder sBuilder = new StringBuilder();
// Loop through each byte of the hashed data
// and format each one as a hexadecimal string.
for (int i = 0; i < data.Length; i++)
{
sBuilder.Append(data[i].ToString("x2"));
}
// Return the hexadecimal string.
return sBuilder.ToString();
}
public static string EncryptAes(string plainText, string key, string md5)
{
byte[] Keybytes;
byte[] encrypted;
byte[] IV = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
string aes_key = GetMd5Hash(md5 + key).Substring(8, 16).ToUpper();
using (Aes aesAlg = Aes.Create())
{
Keybytes = Encoding.UTF8.GetBytes(aes_key);
aesAlg.Key = Keybytes;
aesAlg.IV = IV;
aesAlg.Mode = CipherMode.ECB;
aesAlg.Padding = PaddingMode.PKCS7;
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
byte[] input = Encoding.UTF8.GetBytes(plainText);
encrypted = encryptor.TransformFinalBlock(input, 0, input.Length);
string rtn_str = Convert.ToBase64String(encrypted);
// Return the encrypted string from the memory stream.
return rtn_str;
}
}
public static string DecryptAes(string decipher_str, string key, string md5)
{
byte[] Keybytes;
byte[] cipherTextCombined;
byte[] decipher;
byte[] IV = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
// Declare the string used to hold
// the decrypted text.
string plaintext = null;
// Create an Aes object
// with the specified key and IV.
string aes_key = GetMd5Hash(md5 + key).Substring(8, 16).ToUpper();
using (Aes aesAlg = Aes.Create())
{
Keybytes = Encoding.UTF8.GetBytes(aes_key);
aesAlg.Key = Keybytes;
cipherTextCombined = Convert.FromBase64String(decipher_str);
aesAlg.IV = IV;
aesAlg.Mode = CipherMode.ECB;
// Create a decrytor to perform the stream transform.
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
decipher = decryptor.TransformFinalBlock(cipherTextCombined, 0, cipherTextCombined.Length);
plaintext = Encoding.UTF8.GetString(decipher);
}
return plaintext;
}
}
public class Paydata
{
public string amount { get; set; }
public string biz_type { get; set; }
public string operator_id { get; set; }
public string order_id { get; set; }
public string call_back_url { get; set; }
public Paydata() {
//default constructor
}
}
public class payrequest
{
public string action { get; set; }
public string version { get; set; }
public string merchant_id { get; set; }
public string md5 { get; set; }
public string data { get; set; }
public payrequest()
{
//default constructor
}
}
public class payresponse
{
public string rsp_code { get; set; }
public string rsp_msg { get; set; }
public string data { get; set; }
public string md5 { get; set; }
}
}
<?php
header("Content-type: text/html; charset=utf-8");
function sendRequest($data, $url)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_TIMEOUT, 60);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json', 'Content-length:'.strlen($data)));
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_REFERER, $_SERVER['SERVER_NAME']);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLINFO_HEADER_OUT, true);
$resp = curl_exec($ch);
curl_close($ch);
return $resp;
};
class Security
{
public static function encrypt($input, $key)
{
return base64_encode(openssl_encrypt($input, 'aes-128-ecb', $key, OPENSSL_RAW_DATA));
}
public static function decrypt($sStr, $sKey)
{
return openssl_decrypt($sStr, 'aes-128-ecb', $sKey);
}
}
$data_array = array();
$data_array['amount'] = '1';
$data_array['biz_type'] = 'WECHATPAY';
$data_array['operator_id'] = '0000000437'; //using your 10-digital operator number provided by OTTPAY;
$data_array['order_id'] = "TEST".date("YmdHis"); //'TEST20171130175453';
$data_array['call_back_url'] = "http://www.ottpay.com/index.php?option=com_j2store&view=callback&method=payment_ottpay"; //using your call back url;
$temp_data_array = $data_array;
ksort($temp_data_array);
$data_str = implode(array_values($temp_data_array));
$data_md5 = strtoupper(md5($data_str));
$user_key = '61DB911B7CE7261C'; //using your Sign Key provided by OTTPAY;
$aesKeyStr = strtoupper(substr(md5($data_md5.$user_key), 8, 16));
$data_json = json_encode($data_array);
$encrypted_data = Security::encrypt($data_json, $aesKeyStr);
$params_array = array();
$params_array['action'] = 'ACTIVEPAY';
$params_array['version'] = '1.0';
$params_array['merchant_id'] = 'ON00000097'; //using your Merchant ID provided by OTTPAY;
$params_array['data'] = $encrypted_data;
$params_array['md5'] = $data_md5;
$params_json = json_encode($params_array, JSON_UNESCAPED_UNICODE);
$resp_data = sendRequest($params_json, 'https://frontapi.ottpay.com:443/processV2');
$resp_arr = (array) json_decode($resp_data, true);
$aesKeyStr = strtoupper(substr(md5($resp_arr['md5'].$user_key), 8, 16));
$decrypted_data = Security::decrypt($resp_arr['data'], $aesKeyStr);
$return_data_arr = (array) json_decode($decrypted_data, true);
$qrCode_url = $return_data_arr['code_url'];
echo 'response qrCode_url = '.$qrCode_url;
?>
//Global variables
//----------------requirejs declaration-----------------------------------------------------------------------------
requirejs.config({
baseUrl: '',
paths: {
CryptoJS:"CryptoJS/components/core-min",
CryptoJSMD5:"CryptoJS/rollups/md5",
CryptoJSAES:"CryptoJS/rollups/aes",
CryptoJSBase64:"CryptoJS/components/enc-base64",
CryptoJSModeECB:"CryptoJS/components/mode-ecb",
Uint8Array:"CryptoJS/components/Uint8Array",
md5tool: "ottpay_md5tool",
}
});
//-----------------------initialization--------------------------------------------------------------------------------
require(['md5tool','CryptoJSMD5','CryptoJSAES','CryptoJSBase64',
'CryptoJSModeECB','Uint8Array'],
function(md5tool, CryptoJSMD5, CryptoJSAES,CryptoJSBase64,
CryptoJSModeECB,Uint8Array){
$(document).ready(function(){
get_exchangerate();
});
});
//------------------------api call, retrieve get_exchangerate-----------------------------------------
function get_exchangerate(){
return new Promise(function(resolve, reject){
var newtool = new ottpay_md5tool();
var md5_str = newtool.getMD5StrFromJson('{"fee_type":"CAD"}');
var data_str = newtool.getDataStrFromJson('{"fee_type":"CAD"}');
var key = login_bean.sign_key.toString().toUpperCase();
console.log("sign-key: "+key);
var encrypted = newtool.encrypted(data_str,key,md5_str);
//console.log("test 1 encrypt: "+encrypted);
//var request = {"action":"EX_RATE_QUERY","version":"1.3.4","terminal_no":Constants.TERMINIAL_NO,"data":encrypted,"md5":md5_str}
var request = {"action":"EX_RATE_QUERY","version":"1.0","terminal_no":Constants.TERMINIAL_NO,"data":encrypted,"md5":md5_str};
console.log("request="+JSON.stringify(request));
postAjax(Constants.API_URL,JSON.stringify(request)).then(
function(responseText){
//console.log("get_exchangerate, response text="+responseText);
var response = JSON.parse(responseText);
if(response["rsp_code"]==="SUCCESS"){
//console.log("data after decipher="+newtool.decipher(response["data"],key,response["md5"]));
var exchange_rate = JSON.parse(newtool.decipher(response["data"],key,response["md5"]));
//console.log("exchange_rate", exchange_rate["rate"]);
resolve( exchange_rate["rate"]);
}else{
console.log("Error in getting exchange rate, "+response["rsp_code"]);
reject("-1");
}
},
function(error){
console.log("Get exchange rate error, ", error);
reject("-1");
});
});
//------------------------util, generic post method-----------------------------------------------------------------
function postAjax(url, data) {
return new Promise( function(resolve, reject){
var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
xhr.open('POST', url);
xhr.onload = function(){
if(xhr.readyState>3 && xhr.status==200){
resolve(xhr.responseText)
}
else{
switch(xhr.status) {
case 404:
reject(Error('Server not found'));
break;
case 500:
reject(Error('Server error'));
break;
case 0:
reject(Error('Request aborted'));
break;
default:
reject(Error('Unknown error ' + status));
}
}
}
xhr.onerror=function(){
reject(Error("Page load error"));
}
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
xhr.setRequestHeader('Content-Type', 'application/json;charset=UTF-8');
xhr.send(data);
return xhr;
});
}
}
/**
* Wrapper of CryptoJS for ottpay
* CopyRight: OTT PAY Inc.
*
*/
function ottpay_md5tool () {
this.MSstring = "";
}
ottpay_md5tool.prototype = {
getDataStrFromJson : function(jsonObject) {
return jsonObject.toString();
},
getMD5StrFromJson : function(jsonObject) {
var dataStr = this.sort(jsonObject);
//console.log('sorted str='+dataStr);
var md5Str = this.getMD5String(dataStr).toString().toUpperCase();
return md5Str;
},
sort: function(jsonObject){
//console.log('To sort jsonObject = '+jsonObject.toString());
var obj = JSON.parse(jsonObject);
//get sorted keylist
var keylist = [];
for(var key in obj){
//console.log('to sort, key='+key);
keylist.push(key);
}
keylist.sort();
//console.log('to sort, keylist='+keylist);
//get return str by retrieving value according to sorted key
var return_str = "";
for(var keyidx=0; keyidx<keylist.length; keyidx++ ){
var value_idx = obj[keylist[keyidx]];
if((typeof value_idx)=="number"){
return_str += obj[keylist[keyidx]].toString()+".0";
}else{
return_str += obj[keylist[keyidx]];
}
}
//console.log('after sort, return_str = '+return_str);
return return_str;
},
getMD5String: function(dataStr) {
return CryptoJS.MD5(dataStr);
},
decipher: function(orgData, key, md5) {
//console.log("To decrypt data = " + orgData);
//console.log("To decrypt md5 = " + md5);
//console.log("To decrypt key = " + key);
var data = orgData.replace(/[\r\n' ']/g, '');
//var data = orgData.replace(/[' ']/g, '');
var aesKeyStr = CryptoJS.MD5(md5.concat(key)).toString().substring(8, 24).toUpperCase();
//console.log("decrypt aes key = " + aesKeyStr);
var dataArray = CryptoJS.enc.Base64.parse(data);
var keyArray = CryptoJS.enc.Utf8.parse(aesKeyStr);
var decrypted = CryptoJS.AES.decrypt({ciphertext: dataArray}, keyArray, {mode: CryptoJS.mode.ECB});
//console.log("decrypted_Utf8",decrypted.toString(CryptoJS.enc.Utf8));
//console.log("decrypted_u8array",this.hex2a(CryptoJS.enc.u8array.stringify(decrypted)));
//var stb_u8array = this.sort(this.hex2a(CryptoJS.enc.u8array.stringify(decrypted)));
//var calmd5 = this.getMD5String(stb_u8array).toString();
//console.log("calculated u8array md5 = " + calmd5);
var stb_utf8 = this.sort(decrypted.toString(CryptoJS.enc.Utf8));
var calmd5 = this.getMD5String(stb_utf8).toString();
//console.log("calculated utf8 md5 = " + calmd5);
if (calmd5.toUpperCase()==md5.toUpperCase()) {
return decrypted.toString(CryptoJS.enc.Utf8);
} else {
//alert("check sign fail");
console.log("check sign fail");
return JSON.stringify({error:"-1"});
}
},
/**
* Encryption
*
* @param data json
* @param key
* @param md5
* @return
*/
encrypted: function(data, key, md5) {
var aesKeyStr = CryptoJS.MD5(md5.concat(key)).toString().substring(8, 24).toUpperCase();
//console.log("in encrypted, aes key = " + aesKeyStr);
var dataArray = CryptoJS.enc.Utf8.parse(data);
var keyArray = CryptoJS.enc.Utf8.parse(aesKeyStr);
var encrypt = CryptoJS.AES.encrypt(dataArray, keyArray, {mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7});
//console.log("encrypted, encrypt = " + encrypt.toString());
return encrypt.toString();
},
hex2a: function(hex) {
//var str = '';
//for (var i = 0; i < hex.length; i++)
// str += String.fromCharCode(hex[i]);
//str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
//return str;
var encodedString = String.fromCharCode.apply(null, hex);
return encodedString;
},
decipher_array: function(orgData, key, md5) {
//console.log("decrypt data = " + orgData);
//console.log("decrypt md5 = " + md5);
//console.log("decrypt key = " + key);
var data = orgData.replace(/[\r\n' ']/g, '');
var aesKeyStr = CryptoJS.MD5(md5.concat(key)).toString().substring(8, 24).toUpperCase();
//console.log("decrypt aes key = " + aesKeyStr);
var dataArray = CryptoJS.enc.Base64.parse(data);
var keyArray = CryptoJS.enc.Utf8.parse(aesKeyStr);
var decrypted = CryptoJS.AES.decrypt({ciphertext: dataArray}, keyArray, {mode: CryptoJS.mode.ECB});
//console.log("decrypted",this.hex2a(CryptoJS.enc.u8array.stringify(decrypted)));
var stb = this.sortarray(this.hex2a(CryptoJS.enc.u8array.stringify(decrypted)));
//console.log("stb="+stb.toString());
var calmd5 = this.getMD5String(stb).toString();
//console.log("calculated md5 = " + calmd5);
if (calmd5.toUpperCase()==md5.toUpperCase()) {
return decrypted.toString(CryptoJS.enc.Utf8);
} else {
//alert("check sign fail");
console.log("check sign fail");
return JSON.stringify({error:"-1"});
}
},
sortarray: function(jsonObject){
//console.log('jsonObject = '+jsonObject.toString());
var obj = JSON.parse(jsonObject);
//get sorted keylist
var keylist = [];
for(var key in obj){
//console.log('to sort, key='+key);
keylist.push(key);
}
keylist.sort();
//console.log('to sort, keylist='+keylist);
//get return str by retrieving value according to sorted key
var return_str = "";
for(var keyidx=0; keyidx<keylist.length; keyidx++ ){
//console.log('after sort, key = '+keylist[keyidx]);
//console.log('after sort, obj = '+JSON.stringify(obj[keylist[keyidx]]));
if(obj[keylist[keyidx]]==""||obj[keylist[keyidx]]==null){
return_str +="[]";
}else{
var tmp_str = JSON.stringify(obj[keylist[keyidx]]);
return_str += tmp_str.replace(/":"/g, '=').replace(/","/g, ', ').replace(/"/g,'').replace(/},{/g,'}, {');
}
}
//console.log('after sort, return_str = '+return_str);
return return_str;
},
}
Last updated