|
|
<?php
|
|
|
|
|
|
defined('BASEPATH') OR exit('No direct script access allowed');
|
|
|
|
|
|
class Wxpay_call
|
|
|
{
|
|
|
protected $ci;
|
|
|
|
|
|
private $api_info_arr = array();
|
|
|
|
|
|
public function __construct()
|
|
|
{
|
|
|
$this->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) == "<xml>"){
|
|
|
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 */
|