config = self::config();
}
static public function config()
{
return array(
'CMCX_URL' => 'http://58.61.30.110/CmbBank_B2B/UI/DIDI/DoBusiness.ashx', //签约协议查询接口
'NTBNBR' => 'P0026365',//协议商户企业编号 //请填写自己的商户企业编号
'MchNo' => 'P0026365',//协议商户企业编号 //请填写自己的商户企业编号
'pfxFile' => self::pfx_path, //pfx格式证书 通过jks转换 用于openssl签名
'password' => '12345678',//pfx证书密码
'BranchID' => '0021',//开户分行号 4位 请自己配置
'CoNo' => '000140', //商户号 6位 请自己配置
'singKey' => '',//签名密钥 查询单笔订单需要 测试环境为空
'DirectRequestX' => 'http://218.17.27.197/netpayment/basehttp.dll?DirectRequestX',//查询订单接口
'RefundNoDupOperator' => '9999',//退款操作员编号
'RefundNoDupPassword' => '000140', //测试环境密码和商户号相同
);
}
/**
* 支付协议查询
* 查询成功 BUSDATA 字段返回数据描述
* merch_date String 8 可选 商户交易日期(YYYYMMDD), 成功交易返回
* merch_time String 6 可选 商户交易时间(HHmmSS), 成功交易返回
* merch_serial String 24 必填 商户交易流水号
* respcod String 7 必填 交易结果 CMBMB99表示成功,其他为失败
* respmsg String 80 必填 返回交易信息
* cust_argno String 32 可选 客户协议号,成功交易返回
* bank_name String 40 可选 签约银行名称,成功交易返回
* open_time String 14 可选 签约时间 YYYYMMDD HHmmSS,成功交易返回
* cust_open_d_pay String 1 可选 保留使用:”N”,成功交易返回
* cust_pidty String 1 可选 证件类型 目前只有’1’,表示身份证,成功交易返回
* cust_pid_v String 30 可选 证件号映射的30位hash值,成功交易返回
*/
public function cmcx($data)
{
$xmlBusData = "{$data['merch_date']}{$data['merch_time']}{$data['merch_serial']}{$data['cust_argno']}";
$BUSDAT = base64_encode($xmlBusData);//业务数据包 报文数据必须经过base64编
$NTBNBR = $this->config['NTBNBR'];//企业网银编号
$TRSCOD = 'CMCX';//交易码
$DATLEN = strlen($BUSDAT);// 字段BUSDAT长度
$COMMID = date("YmdHis",time()).mt_rand(100000, 999999);//返回的报文头会包含该信息,仅用作单次通讯的请求和响应报文的对应。银行业务数据不存储该ID。
$SIGTIM = date("YmdHis",time()).'0000';//签名时间
$signOrigin = "NTBNBR={$NTBNBR}&TRSCOD={$TRSCOD}&COMMID={$COMMID}&SIGTIM={$SIGTIM}&BUSDAT={$BUSDAT}";
$SIGDAT = $this->opensslSign($signOrigin);
$requestData = array(
'NTBNBR' => $NTBNBR,
'TRSCOD' => $TRSCOD,
'COMMID' => $COMMID,
'DATLEN' => $DATLEN,
'BUSDAT' => $BUSDAT,
'SIGTIM' => $SIGTIM,
'SIGDAT' => $SIGDAT,
);
$jsonData = json_encode($requestData);
$url = $this->config['CMCX_URL'];
$postArray = array();
$postArray[0] = $url;
$postArray[1]['data'] = array('RequestData' => $jsonData);
$json = $this->execCurl("post",$postArray);
$cmcxData = json_decode($json,true);
$xml = base64_decode($cmcxData['BUSDAT']);
$xmlArray = (array)simplexml_load_string($xml);
//查询协议日志
$dateTime = date("Y-m-d H:i:s",time());
$logContent = "日志时间:{$dateTime} 请求原文JSON:{$jsonData} 请求XML:{$xmlBusData} 响应原文json:{$json} 响应XML:{$xml}".PHP_EOL.PHP_EOL;
$this->writeLog('cmcx',$logContent);
return $xmlArray;
}
/**
* 获取查询协议数据
* 模拟查询协议用的数据
* 查询不同的协议请使用不同的数据进行模拟
* 这些模拟数据都是真实存在银行系统内的数据不能乱写
*/
public function getTestCmcxInfo()
{
$data = array();
$data['merch_date'] = '20160811';//签约协议日期 年月日
$data['merch_time'] = '154021';//签约协议时间 时分秒
$data['merch_serial'] = '2016081143751'; //签约协议流水号
$data['cust_argno'] = '20160811154059441120';//协议号
return $data;
}
/**
* 支付协议查询
* 查询成功 BUSDATA 字段返回数据描述
* merch_date String 8 必填 商户交易日期(YYYYMMDD)
* merch_time String 6 必填 商户交易时间(HHmmSS)
* merch_serial String 24 必填 商户交易流水号,全局唯一
* bank_serial String 30 必填 银行交易流水号,全局唯一
* respcod String 7 必填 交易结果 CMBMB99表示成功,其他为失败
* respmsg String 80 必填 返回交易信息
*/
public function cmqx($data)
{
$xmlBusData = "{$data['merch_date']}{$data['merch_time']}{$data['merch_serial']}{$data['cust_argno']}";
$BUSDAT = base64_encode($xmlBusData);//业务数据包 报文数据必须经过base64编
$NTBNBR = $this->config['NTBNBR'];//企业网银编号
$TRSCOD = 'CMQX';//交易码
$DATLEN = strlen($BUSDAT);// 字段BUSDAT长度
$COMMID = date("YmdHis",time()).mt_rand(100000, 999999);//返回的报文头会包含该信息,仅用作单次通讯的请求和响应报文的对应。银行业务数据不存储该ID。
$SIGTIM = date("YmdHis",time()).'0000';//签名时间
$signOrigin = "NTBNBR={$NTBNBR}&TRSCOD={$TRSCOD}&COMMID={$COMMID}&SIGTIM={$SIGTIM}&BUSDAT={$BUSDAT}";
$SIGDAT = $this->opensslSign($signOrigin);
$requestData = array(
'NTBNBR' => $NTBNBR,
'TRSCOD' => $TRSCOD,
'COMMID' => $COMMID,
'DATLEN' => $DATLEN,
'BUSDAT' => $BUSDAT,
'SIGTIM' => $SIGTIM,
'SIGDAT' => $SIGDAT,
);
$jsonData = json_encode($requestData);
$url = $this->config['CMCX_URL'];//查询和取消走的都是同一个接口
$postArray = array();
$postArray[0] = $url;
$postArray[1]['data'] = array('RequestData' => $jsonData);
$json = $this->execCurl("post",$postArray);
$cmcxData = json_decode($json,true);
$xml = base64_decode($cmcxData['BUSDAT']);
$xmlArray = (array)simplexml_load_string($xml);
//取消协议日志
$dateTime = date("Y-m-d H:i:s",time());
$logContent = "日志时间:{$dateTime} 请求原文JSON:{$jsonData} 请求XML:{$xmlBusData} 响应原文json:{$json} 响应XML:{$xml}".PHP_EOL.PHP_EOL;
$this->writeLog('cmqx',$logContent);
return $xmlArray;
}
/**
* 获取查询协议数据
* 模拟查询协议用的数据
* 如果提供的数据不存在于银行系统内,返回错误提示
*/
public function getTestCmqxInfo()
{
$data = array();
$data['merch_date'] = '20160811';//签约协议日期 年月日
$data['merch_time'] = '151709';//签约协议时间 时分秒
$data['merch_serial'] = '2016081132532'; //签约协议流水号
$data['cust_argno'] = '20160811151403121298';//协议号
return $data;
}
/**
* 查询支付协议签名
* php基于sha1withRSA算法的签名(pfx证书)
* @param type $data
* @return type
*/
public function opensslSign($data)
{
$certs = array();
//其中password为证书密码
openssl_pkcs12_read(file_get_contents($this->config['pfxFile']), $certs, $this->config['password']);
if(!$certs) return ;
$signature = '';
openssl_sign($data, $signature, $certs['pkey']);
return base64_encode($signature);//返回base64编码的密钥
}
/**
* 获取银行订单信息
*/
public function getOneOrderInfo($data)
{
$startTime = strtotime("2000-01-01 00:00:00");
$currentTime = time();
$diffTime = $currentTime - $startTime;
$BranchNo = $this->config['BranchID'];//开户分行号
$MerchantNo = $this->config['CoNo'];//商户号
$TimeStamp = $diffTime * 1000;//请求发起的时间,精确到毫秒
$Date = $data['Date'];//商户定单日期 年月日 YYYYMMDD
$BillNo = $data['BillNo'];//订单号 6位或10位订单号
$singKey = $this->config['singKey'];//签名密钥 测试情况下为空
//SHA1 哈希签名
$HashOrigin = "{$singKey}{$BranchNo}{$MerchantNo}{$TimeStamp}QuerySingleOrder{$Date}{$BillNo}";
$Hash = sha1($HashOrigin);//使用sha1加密
//单笔订单请求报文
$postData = "{$BranchNo}{$MerchantNo}{$TimeStamp}QuerySingleOrder{$Date}{$BillNo}{$Hash}";
//接口地址
$array = array();
$array[0] = $this->config['DirectRequestX'];
$array[1]['data'] = array(
'Request' => $postData,
);
$xmlString = $this->execCurl('post',$array);
//查询单笔订单日志
$dateTime = date("Y-m-d H:i:s",time());
$logContent = "日志时间:{$dateTime} 请求原文:{$postData} 响应原文:{$xmlString}".PHP_EOL.PHP_EOL;
$this->writeLog('singleOrder',$logContent);
$xmlArray = simplexml_load_string($xmlString);
return $this->objectToArray($xmlArray);
}
/**
* 模拟获取订单信息
*/
public function getTestOneOrder()
{
$BillNo = '0003041211'; //银行订单号
$Date = '20160812'; //订单日期
return array('BillNo'=>$BillNo,'Date'=>$Date);
}
/**
* 订单查询 共用接口 该方法可以接受三个接口参数调用
* 通过Command字段区分调用
* 1.商户入账查询接口 QueryTransact
* 2.按商户日期查询已结账订单接口 QuerySettledOrderByMerchantDate
* 3.按结账日期查询已结账订单接口 QuerySettledOrderBySettledDate
*/
public function getQueryOrderData($data)
{
$startTime = strtotime("2000-01-01 00:00:00");
$currentTime = time();
$diffTime = $currentTime - $startTime;
$BranchNo = $this->config['BranchID'];//开户分行号
$MerchantNo = $this->config['CoNo'];//商户号
$TimeStamp = $diffTime * 1000;//请求发起的时间,精确到毫秒
$BeginDate = $data['BeginDate'];//起始日期
$EndDate = $data['EndDate'];//结束日期
$Count = $data['Count'];//查询条数
$Operator = $data['Operator'];//操作员号
$pos = $data['Pos']; //续传包
$singKey = $this->config['singKey'];//签名密钥 测试情况下为空
$Command = $data['Command']; //接口名称 说明见注释
//SHA1 哈希签名
$HashOrigin = "{$singKey}{$BranchNo}{$MerchantNo}{$TimeStamp}{$Command}{$BeginDate}{$EndDate}{$Count}{$Operator}{$pos}";
$Hash = sha1($HashOrigin);
//商户入账查询请求报文
$postData = "{$BranchNo}{$MerchantNo}{$TimeStamp}{$Command}{$BeginDate}{$EndDate}{$Count}{$Operator}{$pos}{$Hash}";
//接口地址
$array = array();
$array[0] = $this->config['DirectRequestX'];
$array[1]['data'] = array(
'Request' => $postData,
);
$xmlString = $this->execCurl('post',$array);
//接口命令
$CommandArray = array(
'QuerySettledOrderByMerchantDate' => '商户日期查询已结账订单接口',
'QuerySettledOrderBySettledDate' => '结账日期查询已结账订单接口',
'QueryTransact' => '商户入账查询接口',
);
//入账 已结账日志
$dateTime = date("Y-m-d H:i:s",time());
$logContent = "日志时间:{$dateTime} 请求命令:{$Command} 接口名称:{$CommandArray[$Command]} 请求原文:{$postData} 响应原文:{$xmlString}".PHP_EOL.PHP_EOL;
$this->writeLog('querySettledOrder',$logContent);
$xmlArray = simplexml_load_string($xmlString);
return $this->objectToArray($xmlArray);
}
/**
* 模拟获取商户入账数据
* 模拟测试数据 生产环境请用正式数据代替
*/
public function getTestQueryTransact()
{
$BeginDate = '20150923';//起始日期
$EndDate = '20160924';//结束日期
$Count = '10';//查询条数
$Operator = '9999';//操作员号
$pos = ''; //续传包
$Command = 'QueryTransact';//接口命令 不同的命令
$data = array(
'BeginDate' => $BeginDate,
'EndDate' => $EndDate,
'Count' => $Count,
'Operator' => $Operator,
'pos' => $pos,
'Command' => $Command,
);
return $data;
}
/**
* 按结账日期查询已结账订单接口
* 模拟测试数据 生产环境请用正式数据代替
*/
public function getTestQuerySettledOrderBySettledDate()
{
$BeginDate = '20160801';//起始日期
$EndDate = '20160810';//结束日期
$Count = '20';//查询条数
$Operator = '9999';//操作员号
$pos = ''; //续传包
$Command = 'QuerySettledOrderBySettledDate';//接口命令 不同的命令
$data = array(
'BeginDate' => $BeginDate,
'EndDate' => $EndDate,
'Count' => $Count,
'Operator' => $Operator,
'pos' => $pos,
'Command' => $Command,
);
return $data;
}
/**
* 按商户日期查询已结账订单接口
* 模拟测试数据 生产环境请用正式数据代替
*/
public function getTestQuerySettledOrderByMerchantDate()
{
$BeginDate = '20150923';//起始日期
$EndDate = '20160924';//结束日期
$Count = '10';//查询条数
$Operator = '9999';//操作员号
$pos = ''; //续传包
$Command = 'QuerySettledOrderByMerchantDate';//接口命令 不同的命令
$data = array(
'BeginDate' => $BeginDate,
'EndDate' => $EndDate,
'Count' => $Count,
'Operator' => $Operator,
'pos' => $pos,
'Command' => $Command,
);
return $data;
}
/**
* 执行退款操作
*/
public function getRefundNoDupData($data)
{
$startTime = strtotime("2000-01-01 00:00:00");
$currentTime = time();
$diffTime = $currentTime - $startTime;
$BranchNo = $this->config['BranchID'];//开户分行号
$MerchantNo = $this->config['CoNo'];//商户号
$TimeStamp = $diffTime * 1000;//请求发起的时间,精确到毫秒
$Date = $data['Date'];//原订单日期
$BillNo = $data['BillNo']; //原订单号
$RefundNo = $data['RefundNo'];//退款流水号
$Operator = $this->config['RefundNoDupOperator'];//操作员号
$Password = $this->config['RefundNoDupPassword'];//操作员密码
$Amount = $data['Amount'];//退款金额
$Desc = $data['Desc'];//退款描述
$singKey = $this->config['singKey'];//签名密钥 测试情况下为空
$Command = 'Refund_No_Dup'; //接口名称 说明见注释
//SHA1 哈希签名
$HashOrigin = "{$singKey}{$BranchNo}{$MerchantNo}{$Operator}{$Password}{$TimeStamp}{$Command}{$Date}{$BillNo}{$RefundNo}{$Amount}{$Desc}";
$Hash = sha1($HashOrigin);
//商户入账查询请求报文
$postData = "{$BranchNo}{$MerchantNo}{$Operator}{$Password}{$TimeStamp}{$Command}{$Date}{$BillNo}{$RefundNo}{$Amount}{$Desc}{$Hash}";
//接口地址
$array = array();
$array[0] = $this->config['DirectRequestX'];
$array[1]['data'] = array(
'Request' => $postData,
);
$xmlString = $this->execCurl('post',$array);
//入账 已结账日志
$dateTime = date("Y-m-d H:i:s",time());
$logContent = "日志时间:{$dateTime} 请求命令:Refund_No_Dup 接口名称:直连退款 请求原文:{$postData} 响应原文:{$xmlString}".PHP_EOL.PHP_EOL;
$this->writeLog('refundNoDup',$logContent);
$xmlArray = simplexml_load_string($xmlString);
return $this->objectToArray($xmlArray);
}
/**
* 模拟退款数据
*/
public function getTestRefundNoDup()
{
$Date = '20160812';//订单日期
$BillNo = '0001416027';//订单号
$RefundNo = 'TK'.date("YmdHis",time()). mt_rand(1000,9999);//退款流水号
$Amount = '98.00';//退款金额
$Desc = 'tuikuan desc';//
$data = array(
'Date' => $Date,
'BillNo' => $BillNo,
'RefundNo' => $RefundNo,
'Amount' => $Amount,
'Desc' => $Desc,
);
return $data;
}
/**
* 递归转换XML为数组
* @param type $data
* @return type
*/
public function objectToArray($e)
{
$_arr = is_object($e) ? get_object_vars($e) : $e;
foreach ($_arr as $key => $val) {
$val = (is_array($val) || is_object($val)) ? $this->objectToArray($val) : $val;
$arr[$key] = !$val ? (string)$val : $val;
}
return $arr;
}
/**
* CURL函数
* @param type $method
* @param type $args
* @return type
* @throws \Exception
*/
public function execCurl($method, $args)
{
//解析参数
$url = isset($args[0]) ? $args[0] : "";
$multi = isset($args[1]['multi']) ? $args[1]['multi'] : "";
$data = isset($args[1]['data']) ? $args[1]['data'] : "";
$ajax = isset($args[1]['ajax']) ? $args[1]['ajax'] : "";
$timeout = isset($args[1]['timeout']) ? $args[1]['timeout'] : 30;
$files = isset($args[1]['files']) ? $args[1]['files'] : "";
$referer = isset($args[1]['referer']) ? $args[1]['referer'] : "";
$proxy = isset($args[1]['proxy']) ? $args[1]['proxy'] : "";
$headers = isset($args[1]['headers']) ? $args[1]['headers'] : "";
//如果环境变量的浏览器信息不存在,就是用手动设置的浏览器信息
$userAgent = isset($_SERVER['HTTP_USER_AGENT'])?
$_SERVER['HTTP_USER_AGENT']:
'Mozilla/5.0 (Windows NT 6.2; WOW64; rv:23.0)
Gecko/20100101 Firefox/23.0';
//检测url必须参数 不能为空
if (!$url) {
throw new \Exception("错误:curl请求地址不能为空");
}
//设置curl选项
$options = array(
CURLOPT_URL => $url, //目标url
CURLOPT_TIMEOUT => $timeout, //超时
CURLOPT_RETURNTRANSFER => 1, //输出数据流
CURLOPT_FOLLOWLOCATION => 1, //自动跳转追踪
CURLOPT_AUTOREFERER => 1, //自动设置来路信息
CURLOPT_SSL_VERIFYPEER => 0, //认证证书检查
CURLOPT_SSL_VERIFYHOST => 0, //检查SSL加密算法
CURLOPT_HEADER => 0, //禁止头文件输出
CURLOPT_NOSIGNAL => 1, //忽略所有传递的信号
CURLOPT_USERAGENT => $userAgent,//浏览器环境字符串
CURLOPT_IPRESOLVE => CURL_IPRESOLVE_V4, //ipv4寻址方式
CURLOPT_ENCODING => 'gzip', //解析使用gzip压缩的网页
);
//设置代理 必须是数组并且非空
if (is_array($proxy) && !empty($proxy)) {
$options[CURLOPT_PROXY] = $proxy['host'];
$options[CURLOPT_PROXYPORT] = $proxy['port'];
$options[CURLOPT_PROXYUSERPWD] =
$proxy['user'] . ':' . $proxy['pass'];
}
//检测是否未启用自定义urlencode编码
if (!isset($args[1]['build'])) {
if ($data && $method == "post" && is_array($data)) {
$data = http_build_query($data, '', '&');
}
}
//检测是否含有上传文件
if ($files && $method == "post" && is_array($files)) {
foreach ($files as $k => $v) {
$files[$k] = '@' . $v;
}
parse_str($data, $data);
$data = $data + $files;
}
//检测判断是否是post请求
if ($method == 'post') {
//发送一个常规的POST请求
$options[CURLOPT_POST] = 1;
//使用HTTP协议中的"POST"操作来发送数据,支持键值对数组定义
$options[CURLOPT_POSTFIELDS] = $data;
}
//初始化header数组
$headerOptions = array();
//检测是否是ajax提交
if ($ajax) {
$headerOptions['X-Requested-With'] = 'XMLHttpRequest';
}
//设置来路
if ($referer) {
$options[CURLOPT_REFERER] = $referer;
}
//合并header
if (!empty($headers) && is_array($headers)) {
foreach ($headers as $k => $v) {
$headerOptions[$k] = $v;
}
}
//转换header选项为浏览器header格式
if (!empty($headerOptions) && is_array($headerOptions)) {
$array = array();
foreach($headerOptions as $k => $v) {
$array[] = $k . ": " . $v;
}
$options[CURLOPT_HTTPHEADER] = $array;
}
//创建curl句柄
$ch = curl_init();
//设置curl选项
curl_setopt_array($ch, $options);
//获取返回内容
$content = curl_exec($ch);
//关闭curl句柄
curl_close($ch);
//返回内容
return $content;
}
/**
* 写日志
* @param type $fileName 文件名
* @param type $filePath 文件路径
* @param type $logContent 日志内容
*/
public function writeLog($dirName, $logContent)
{
$currentPath = dirname(__FILE__) .'/log/' . $dirName ;
if (!file_exists($currentPath)) {
@mkdir($currentPath,0777,true);
}
$filename = date("dmY",time()) . '.log';
$writeFileName = $currentPath . '/' . $filename;
file_put_contents($writeFileName, $logContent,FILE_APPEND);
}
}