ci =& get_instance(); $this->ci->load->helper('payment'); $this->ci->config->load('wxpay', true); // $this->ci->load->model('WxpayQueryContentBuilder', 'query_builder'); } private function init_api() { $this->wx_site_config = $this->ci->config->item($GLOBALS['__WX_SITE_NAME__'], 'wxpay'); $this->api_info_arr['appid'] = $this->wx_site_config['app_id']; $this->api_info_arr['mch_id'] = $this->wx_site_config['mch_id']; $this->api_info_arr['key'] = $this->wx_site_config['key']; $this->api_info_arr['app_secret'] = $this->wx_site_config['app_secret']; } public function queryorder($query_content) { $ret = array('status'=>false, 'data'=>array()); $this->init_api(); unset($this->api_info_arr['key']); unset($this->api_info_arr['app_secret']); $url = "https://api.mch.weixin.qq.com/pay/orderquery"; $query_content_input = $query_content->getBizContent(true); $this->api_info_arr['nonce_str'] = ($this->get_nonce_str());//随机字符串 $this->api_info_arr['sign'] = $this->make_sign(array_merge($this->api_info_arr, $query_content_input));//签名 $xml = to_xml(array_merge($this->api_info_arr, $query_content_input)); $response = $this->post_xml_curl($this->api_info_arr, $xml, $url, 20); $ret['status'] = true; $ret['data'] = from_xml($response); return $ret; } /** * 下载对账单,WxPayDownloadBill中bill_date为必填参数 * appid、mchid、spbill_create_ip、nonce_str不需要填入 * @param WxPayDownloadBill $inputObj * @param int $timeOut * @throws WxPayException * @return 成功时返回,其他抛异常 */ public function get_bill_data($query_content) { $ret = array('status'=>false, 'data'=>array()); $this->init_api(); // return false; $url = "https://api.mch.weixin.qq.com/pay/downloadbill"; $query_content_input = $query_content->getBizContent(true); //检测必填参数 if(!$query_content_input['bill_date']) { throw new WxPayException("对账单接口中,缺少必填参数bill_date!"); } $this->api_info_arr['nonce_str'] = ($this->get_nonce_str());//随机字符串 $this->api_info_arr['sign'] = $this->make_sign(array_merge($this->api_info_arr, $query_content_input));//签名 $xml = to_xml(array_merge($this->api_info_arr, $query_content_input)); $response = $this->post_xml_curl($this->api_info_arr, $xml, $url, 20); if(substr($response, 0 , 5) == ""){ log_message('error',"Wxpay bill error \r\n" . var_export($response, 1)); return $ret; } $ret['status'] = true; $response_arr = explode("\n",$response); $data_title = $this->bill_data_title(); for ($i=1; $i < count($response_arr)-1; $i++) { if (empty($response_arr[$i])) { continue; } $row = explode(",", $response_arr[$i]); $row_arr = array(); foreach ($data_title as $key => $title) { if ( ! isset($row[$key])) { continue; } $row_arr[$title] = str_replace("`","",trim($row[$key])); } !empty($row_arr['trade_type']) ? $ret['data'][] = $row_arr : false; } return $ret; } /*! * @Author: LYT * @Date: 2019-06-18 15:23:15 * @Desc: 账单数据的格式 * 交易时间,公众账号ID,商户号,特约商户号,设备号,微信订单号,商户订单号,用户标识,交易类型,交易状态,付款银行,货币种类,应结订单金额,代金券金额,微信退款单号,商户退款单号,退款金额,充值券退款金额,退款类型,退款状态,商品名称,商户数据包,手续费,费率,订单金额,申请退款金额,费率备注 */ private function bill_data_title() { return array( "complete_time", // 交易时间 "app_id", // 公众账号ID "mch_id", // 商户号 "special_mch_id", // 特约商户号 "device_info", // 设备号 "transaction_id", // 微信订单号 "out_trade_no", // 商户订单号 "openid", // 用户标识 "trade_type", // 交易类型 "trade_state", // 交易状态 "bank_type", // 付款银行 "currency_type", // 货币种类 "settlement_total_fee", // 应结订单金额 "coupon_fee", // 代金券金额 "refund_id", // 微信退款单号 "out_refund_id",// 商户退款单号 "settlement_refund_fee", // 退款金额 "refund_coupon_fee", // 充值券退款金额 "refund_type", // 退款类型 "refund_status", // 退款状态 "item_name", // 商品名称 "attach", // 商户数据包 "wxpay_fee", // 手续费 "rate", // 费率 "total_fee", // 订单金额 "call_refund_fee", // 申请退款金额 "rate_memo" // 费率备注 ); } /** * 以post方式提交xml到对应的接口url * * @param WxPayConfigInterface $config 配置对象 * @param string $xml 需要post的xml数据 * @param string $url url * @param bool $useCert 是否需要证书,默认不需要 * @param int $second url执行超时时间,默认30s * @throws WxPayException */ private function post_xml_curl($config, $xml, $url, $second = 30) { $ch = curl_init(); $curlVersion = curl_version(); $ua = "WXPaySDK/3.0.9 (".PHP_OS.") PHP/".PHP_VERSION." CURL/".$curlVersion['version']." " .$config['mch_id']; //设置超时 curl_setopt($ch, CURLOPT_TIMEOUT, $second); curl_setopt($ch,CURLOPT_URL, $url); // curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,TRUE); // curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,2);//严格校验 if(stripos($url,"https://")!==FALSE){ curl_setopt($ch, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE); } else { curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,TRUE); curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,2);//严格校验 } curl_setopt($ch,CURLOPT_USERAGENT, $ua); //设置header curl_setopt($ch, CURLOPT_HEADER, FALSE); //要求结果为字符串且输出到屏幕上 curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); //post提交方式 curl_setopt($ch, CURLOPT_POST, TRUE); curl_setopt($ch, CURLOPT_POSTFIELDS, $xml); //运行curl $data = curl_exec($ch); //返回结果 if($data){ curl_close($ch); return $data; } else { $error = curl_errno($ch); log_message('error',"curl出错,错误码:$error " . curl_error($ch)); curl_close($ch); } return; } public function check_sign($xml_arr) { if ( ! array_key_exists('sign', $xml_arr)) { log_message('error','Wxpay notify error: no sign.'); return false; } if ($this->make_sign($xml_arr) !== $xml_arr['sign']) { log_message('error','Wxpay notify error: sign.' . $this->make_sign($xml_arr)); return false; } if ($this->wx_site_config['app_id'] !== $xml_arr['appid']) { log_message('error','Wxpay notify error: appid.'); return false; } if ($this->wx_site_config['mch_id'] !== $xml_arr['mch_id']) { log_message('error','Wxpay notify error: mch_id.'); return false; } return true; } public function make_sign($xml_arr, $needSignType = false) { //签名步骤一:按字典序排序参数 ksort($xml_arr); $string = $this->to_url_params($xml_arr); //签名步骤二:在string后加入KEY $string = $string . "&key=" . $this->wx_site_config['key']; //签名步骤三:MD5加密或者HMAC-SHA256 if(!isset($xml_arr['sign']) || strlen($xml_arr['sign']) <= 32){ //如果签名小于等于32个,则使用md5验证 $string = md5($string); } else { //是用sha256校验 $string = hash_hmac("sha256", $string , $this->wx_site_config['key']); } //签名步骤四:所有字符转为大写 $result = strtoupper($string); return $result; } /** * 格式化参数格式化成url参数 */ public function to_url_params($xml_arr) { $buff = ""; foreach ($xml_arr as $k => $v) { if($k != "sign" && $v != "" && !is_array($v)){ $buff .= $k . "=" . $v . "&"; } } $buff = trim($buff, "&"); return $buff; } /** * 产生随机字符串,不长于32位 * @param int $length * @return 产生的随机字符串 */ public static function get_nonce_str($length = 32) { $chars = "abcdefghijklmnopqrstuvwxyz0123456789"; $str =""; for ( $i = 0; $i < $length; $i++ ) { $str .= substr($chars, mt_rand(0, strlen($chars)-1), 1); } return $str; } protected function curl($url, $postFields = null) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_FAILONERROR, false); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); $postBodyString = ""; if (is_array($postFields) && 0 < count($postFields)) { foreach ($postFields as $k => $v) { if ("@" != substr($v, 0, 1)) //判断是不是文件上传 { $postBodyString .= "$k=" . urlencode($this->characet($v, "UTF-8")) . "&"; } } unset($k, $v); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, substr($postBodyString, 0, -1)); } $headers = array('content-type: application/x-www-form-urlencoded;charset=UTF-8'); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); $reponse = curl_exec($ch); if (curl_errno($ch)) { log_message('error', " iPayLinks curl error code: ".curl_error($ch)."; curl postBodyString: ".substr($postBodyString, 0, -1)); } else { $httpStatusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); if (200 !== $httpStatusCode) { log_message('error', " iPayLinks Request html Status Code: ".$httpStatusCode."; curl postBodyString: ".substr($postBodyString, 0, -1)); } } curl_close($ch); return $reponse; } public function read_settlement_excel($filePath, $sheetIndex=0) { $tarr1=array(); $this->ci->load->library('PHPExcel'); $PHPExcel = new PHPExcel(); /**默认用excel2007读取excel,若格式不对,则用之前的版本进行读取*/ $PHPReader = new PHPExcel_Reader_Excel2007(); if(!$PHPReader->canRead($filePath)){ $PHPReader = new PHPExcel_Reader_Excel5(); if(!$PHPReader->canRead($filePath)){ echo 'no Excel'; return ; } } $PHPExcel = $PHPReader->load($filePath); /**读取excel文件中的第一个工作表*/ $currentSheet = $PHPExcel->getSheet($sheetIndex); /**取得最大的列号*/ $allColumn = $currentSheet->getHighestColumn(); /**取得一共有多少行*/ $allRow = $currentSheet->getHighestRow(); $col_titles = $this->settlement_excel_title(); /**从第二行开始输出,因为excel表中第一行为列名*/ for($currentRow = 2;$currentRow <= $allRow;$currentRow++){ $row_tmp = array(); /**从第A列开始输出*/ $columnNum = 0; // for($currentColumn= 'A';$currentColumn<= "AD"; $currentColumn++){ // 列标题Z之后的不能直接用逻辑运算 for($currentColumn= 'A';str_pad($currentColumn, 2, "0", STR_PAD_LEFT)<= $allColumn; $currentColumn++){ /**ord()将字符转为十进制数*/ // $val = trim($currentSheet->getCellByColumnAndRow(ord($currentColumn) - 65,$currentRow)->getValue()); // 列标题Z之后不能直接用ord计算ASCII码,因ord仅返回第一个字符的结果导致列AA的值读取到列A $val = trim($currentSheet->getCellByColumnAndRow($columnNum,$currentRow)->getValue()); if ( ! isset($col_titles[$currentColumn])) { continue; } if ($currentColumn==='A' && trim($val)=='') { break; } $col_mean = $col_titles[$currentColumn]; $row_tmp[$col_mean] = trim($val)==='-' ? '' : str_replace('`','',$val); $columnNum++; } if ( ! empty($row_tmp)) { $tarr1[] = $row_tmp; } } return $tarr1; } public function save_settlement_excel($excel_data) { $settle_cnt = 0; foreach ($excel_data as $settle) { $save_column = array(); $save_column['OPN_accountMethod'] = $this->ci->config->item('method_code', 'wxpay'); $save_column['OPN_noticeType'] = trim($settle['trade_type']) === 'SUCCESS' ? 'pay' : 'refund'; $save_column['OPN_fundSource'] = 'cht'; $save_column['OPN_transactionId'] = $settle['transaction_id']; // $save_column['OPN_relatedId'] = str_ireplace("-", '', $settle['related_id']); $save_column['OPN_orderAmount'] = bcsub($settle['trade_amount'],$settle['refund_amount']); $save_column['OPN_orderId'] = $settle['orderid']; $note_exists = $this->ci->note_model->if_note_exists($settle['transaction_id']); // test: if (true === $note_exists) { continue; } // test: 月账单太长 if ($settle_cnt > 999) { break; } $order_info = $this->ci->account_model ->get_gai( $save_column['OPN_transactionId'], $save_column['OPN_orderId'], $save_column['OPN_orderAmount'], $save_column['OPN_accountMethod'] ); if ($order_info['data'] !== null) { $save_column['OPN_orderId'] = $order_info['data']->orderId; $save_column['OPN_accountType'] = $order_info['type']; $save_column['OPN_accountStatus'] = 'recorded'; } $save_column['OPN_subject'] = $settle['orderid']; $save_column['OPN_payAmount'] = $save_column['OPN_orderAmount']; $save_column['OPN_noticeSendStatus'] = 'closed'; $save_column['OPN_transactionResult'] = 'completed'; $save_column['OPN_rawOrderId'] = $settle['orderid']; $save_column['OPN_invoiceId'] = $settle['orderid']; $save_column['OPN_currency'] = $settle['trade_currency']; $save_column['OPN_acquiringTime'] = $settle['complete_date']; $save_column['OPN_completeTime'] = $settle['complete_date']; $save_column['OPN_remark'] = $settle['fund_source']; $save_column['OPN_rawContent'] = json_encode($settle, JSON_UNESCAPED_UNICODE); $save_column['OPN_noticeTime'] = date('Y-m-d H:i:s'); $note_exists = $this->ci->note_model->if_note_exists($settle['transaction_id'], $settle['orderid']); if (true === $note_exists) { // update $update_where = " OPN_transactionId='" . $settle['transaction_id'] . "' AND OPN_rawOrderId='" . $settle['orderid'] . "' "; $this->ci->note_model->update_note($update_where, $save_column) ; $settle_cnt++; } elseif (false === $note_exists) { // insert $this->ci->note_model->insert_note($save_column) ; $settle_cnt++; } } return $settle_cnt; } /** * 交易时间 商户号 微信订单号 商户订单号 交易状态 付款银行 货币种类 应结订单金额 退款金额 手续费 费率 结算金额 */ private function settlement_excel_title() { return array( "A" => "complete_date", "B" => "mch_id", "C" => "transaction_id", "D" => "orderid", "E" => "trade_source", "F" => "trade_type", "G" => "fund_source", "H" => "trade_currency", "I" => "trade_amount", "J" => "refund_amount", "K" => "trade_fee", "L" => "fee_rate", "M" => "settlement_amount" ); } } /* End of file Wxpay_call.php */