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); } }