diff --git a/.gitignore b/.gitignore index 42bce322..1a9d7eda 100644 --- a/.gitignore +++ b/.gitignore @@ -17,5 +17,5 @@ download_statement /application/libraries/composer /application/config/database.php - +**/*cert diff --git a/webht/third_party/pay/config/1353239702_cert/证书使用说明.txt b/webht/third_party/pay/config/1353239702_cert/证书使用说明.txt new file mode 100644 index 00000000..041befb4 --- /dev/null +++ b/webht/third_party/pay/config/1353239702_cert/证书使用说明.txt @@ -0,0 +1,18 @@ +欢迎使用微信支付! +附件中的三份文件(证书pkcs12格式、证书pem格式、证书密钥pem格式),为接口中强制要求时需携带的证书文件。 +证书属于敏感信息,请妥善保管不要泄露和被他人复制。 +不同开发语言下的证书格式不同,以下为说明指引: + 证书pkcs12格式(apiclient_cert.p12) + 包含了私钥信息的证书文件,为p12(pfx)格式,由微信支付签发给您用来标识和界定您的身份 + 部分安全性要求较高的API需要使用该证书来确认您的调用身份 + windows上可以直接双击导入系统,导入过程中会提示输入证书密码,证书密码默认为您的商户号(如:1900006031) + 证书pem格式(apiclient_cert.pem) + 从apiclient_cert.p12中导出证书部分的文件,为pem格式,请妥善保管不要泄漏和被他人复制 + 部分开发语言和环境,不能直接使用p12文件,而需要使用pem,所以为了方便您使用,已为您直接提供 + 您也可以使用openssl命令来自己导出:openssl pkcs12 -clcerts -nokeys -in apiclient_cert.p12 -out apiclient_cert.pem + 证书密钥pem格式(apiclient_key.pem) + 从apiclient_cert.p12中导出密钥部分的文件,为pem格式 + 部分开发语言和环境,不能直接使用p12文件,而需要使用pem,所以为了方便您使用,已为您直接提供 + 您也可以使用openssl命令来自己导出:openssl pkcs12 -nocerts -in apiclient_cert.p12 -out apiclient_key.pem +备注说明: + 由于绝大部分操作系统已内置了微信支付服务器证书的根CA证书, 2018年3月6日后, 不再提供CA证书文件(rootca.pem)下载 \ No newline at end of file diff --git a/webht/third_party/pay/config/wxpay.php b/webht/third_party/pay/config/wxpay.php index 7401ce27..38c2953a 100644 --- a/webht/third_party/pay/config/wxpay.php +++ b/webht/third_party/pay/config/wxpay.php @@ -21,10 +21,12 @@ $config['trippest']["app_id"] = "wx7e605820faf98a05"; $config['trippest']["mch_id"] = "1528541381"; $config['trippest']["key"] = "b6f4db121410468e814d812c6c641efd"; $config['trippest']["app_secret"] = ""; +$config['trippest']["cert_path"] = dirname(__FILE__) . "/1528541381_cert"; // ChinaHighlights = China train booking $config['cht']["notify_url"] = "https://www.mycht.cn/webht.php/apps/pay/wxpayservice/notify/cht"; $config['cht']["app_id"] = "wxd6c8dd69af5128cd"; $config['cht']["mch_id"] = "1353239702"; $config['cht']["key"] = "aada7476b3fecc2c6e33a7c765298516"; $config['cht']["app_secret"] = ""; +$config['cht']["cert_path"] = dirname(__FILE__) . "/1353239702_cert"; // wx5d01021a6d515098 花梨鹰小程序 diff --git a/webht/third_party/pay/controllers/PaymentService.php b/webht/third_party/pay/controllers/PaymentService.php index 97a288cc..8bca1f84 100644 --- a/webht/third_party/pay/controllers/PaymentService.php +++ b/webht/third_party/pay/controllers/PaymentService.php @@ -87,7 +87,7 @@ class PaymentService extends CI_Controller { "wx5d01021a6d515098" => "HLY", // "花梨鹰小程序", // 交行收款码 "wxd6c8dd69af5128cd" => "", // "NATIVE", "wx7e605820faf98a05" => "Trippest-NATIVE", - "0" => "unknown", + "0" => "", ); public function set_brandname(&$ele) { @@ -761,10 +761,10 @@ class PaymentService extends CI_Controller { * 15号之后开始检查 * 换美元汇率: https://www.chinahighlights.com/api/cht/currency/getCurrencydata.php **/ - public function check_paypal_limit() + public function check_paypal_limit($notify = '1') { $this->load->helper('file'); - if (date('d') < '15') { + if (date('d') < '15' && $notify == '1') { return false; } $this_month = date('Y-m-02'); @@ -776,7 +776,7 @@ class PaymentService extends CI_Controller { mkdir(APPPATH . 'cache/', 0755, TRUE); } $read_next_check = read_file($filename); - if ($today < $read_next_check) { + if ($today < $read_next_check && $notify == '1') { echo 'Next Check Date: ' . $read_next_check . '
'; return false; } @@ -804,15 +804,17 @@ class PaymentService extends CI_Controller { write_file($filename, $next_month); // 达到了 120 万美元, 就发邮件, 提醒切换渠道 $time_set = date('Y-m-d_H_i_s'); - $fromName = 'PayPal 额度提醒'; - $fromEmail = 'lyt@hainatravel.com'; - $toName = 'LOT, fgy'; // 'fgy'; - $toEmail = 'lyt@hainatravel.com, fgy@hainatravel.com'; // 'fgy@hainatravel.com'; - $subject = $fromName; - $body = "PayPal收款额度已达, 可切换信用卡渠道.
当前总额: {$current_total} 万 USD
------------
支付系统自动发送
". date("Y-m-d H:i"). "
"; - $M_AddTime = $time_set; - $M_State = 0; - $this->account_model->save_automail($fromName, $fromEmail, $toName, $toEmail, $subject, $body, 0, $M_State, $M_AddTime, $fromName, $fromName); + if ($notify === '1') { + $fromName = 'PayPal 额度提醒'; + $fromEmail = 'lyt@hainatravel.com'; + $toName = 'LOT, fgy'; // 'fgy'; + $toEmail = 'lyt@hainatravel.com, fgy@hainatravel.com'; // 'fgy@hainatravel.com'; + $subject = $fromName; + $body = "PayPal收款额度已达, 可切换信用卡渠道.
当前总额: {$current_total} 万 USD
------------
支付系统自动发送
". date("Y-m-d H:i"). "
"; + $M_AddTime = $time_set; + $M_State = 0; + $this->account_model->save_automail($fromName, $fromEmail, $toName, $toEmail, $subject, $body, 0, $M_State, $M_AddTime, $fromName, $fromName); + } echo $toEmail.'
'; echo $body; return false; @@ -822,7 +824,7 @@ class PaymentService extends CI_Controller { * 检查PayPal的费率 * **/ - public function check_paypal_rate() + public function check_paypal_rate($notify = '1') { if (date('d') !== '2') { echo '不是2号,不检查'; @@ -839,15 +841,17 @@ class PaymentService extends CI_Controller { $current_ACDC = isset($ACDC[0]) ? $ACDC[0]['calc_rate']*100 : ''; // BCDC 钱包渠道大于 3.2% $time_set = date('Y-m-d_H_i_s'); - $fromName = 'PayPal 费率提醒'; - $fromEmail = 'lyt@hainatravel.com'; - $toName = 'LOT, fgy'; // 'fgy'; - $toEmail = 'lyt@hainatravel.com, fgy@hainatravel.com'; // 'fgy@hainatravel.com'; - $subject = $fromName; - $body = "PayPal费率高于3.2%,请及时处理.
当前费率:
PayPal钱包渠道: {$current_BCDC}%
PayPal ACDC渠道(GooglePay, ApplePay): {$current_ACDC}%
------------
支付系统自动发送
". date("Y-m-d H:i"). "
"; - $M_AddTime = $time_set; - $M_State = 0; - $this->account_model->save_automail($fromName, $fromEmail, $toName, $toEmail, $subject, $body, 0, $M_State, $M_AddTime, $fromName, $fromName); + if ($notify === '1') { + $fromName = 'PayPal 费率提醒'; + $fromEmail = 'lyt@hainatravel.com'; + $toName = 'LOT, fgy'; // 'fgy'; + $toEmail = 'lyt@hainatravel.com, fgy@hainatravel.com'; // 'fgy@hainatravel.com'; + $subject = $fromName; + $body = "PayPal费率高于3.2%,请及时处理.
当前费率:
PayPal钱包渠道: {$current_BCDC}%
PayPal ACDC渠道(GooglePay, ApplePay): {$current_ACDC}%
------------
支付系统自动发送
". date("Y-m-d H:i"). "
"; + $M_AddTime = $time_set; + $M_State = 0; + $this->account_model->save_automail($fromName, $fromEmail, $toName, $toEmail, $subject, $body, 0, $M_State, $M_AddTime, $fromName, $fromName); + } echo $toEmail.'
'; echo $body; return false; diff --git a/webht/third_party/pay/controllers/WxpayService.php b/webht/third_party/pay/controllers/WxpayService.php index c139f948..921a0925 100644 --- a/webht/third_party/pay/controllers/WxpayService.php +++ b/webht/third_party/pay/controllers/WxpayService.php @@ -126,10 +126,12 @@ class WxpayService extends CI_Controller { $this->query_builder->set_bill_type($bill_type); $this->load->library('Wxpay_call'); $result = $this->wxpay_call->get_bill_data($this->query_builder); + $fund_result = $this->wxpay_call->get_fund_bill_data($this->query_builder); if ($result['status'] !== true) { log_message('error',"get wxpay bill failed. $target_account $bill_date"); } - foreach ($result['data'] as $key => $row) { + $all_bill = array_merge($result['data'], $fund_result['data']); + foreach ($all_bill as $key => $row) { $save_column = array(); $save_column['OPN_accountMethod'] = $this->config->item('method_code', 'wxpay'); if ($row['refund_id'] != '0') { @@ -146,7 +148,7 @@ class WxpayService extends CI_Controller { $save_column['OPN_entryAmountCNY'] = floatval($ssje); // $save_column['OPN_netAmount']; $save_column['OPN_noticeType'] = 'refund'; - $save_column['OPN_relatedId'] = $row['transaction_id']; + $save_column['OPN_relatedId'] = isset($row['fund_transaction_id']) ? $row['fund_transaction_id'] : $row['transaction_id']; $save_column['OPN_transactionResult'] = $row['refund_status'] === 'SUCCESS' ? 'completed' : strtolower($row['refund_status']); } else { // 收款 @@ -180,6 +182,7 @@ class WxpayService extends CI_Controller { $save_column['OPN_rawContent'] = json_encode($row); $save_column['OPN_noticeTime'] = date('Y-m-d H:i:s'); + // var_dump($save_column); $this->note_model->insert_note($save_column, true); } return; diff --git a/webht/third_party/pay/libraries/wxpay_call.php b/webht/third_party/pay/libraries/wxpay_call.php index f632b733..14845342 100644 --- a/webht/third_party/pay/libraries/wxpay_call.php +++ b/webht/third_party/pay/libraries/wxpay_call.php @@ -8,6 +8,7 @@ class Wxpay_call private $api_info_arr = array(); private $merchant_account = ''; + private $wx_site_config = array(); public function __construct($account = '') { @@ -16,6 +17,7 @@ class Wxpay_call $this->ci->config->load('wxpay', true); // $this->ci->load->model('WxpayQueryContentBuilder', 'query_builder'); $this->merchant_account = $account; + bcscale(2); } private function init_api($account = '') @@ -95,7 +97,7 @@ class Wxpay_call $query_content_input = $query_content->getBizContent(true); //检测必填参数 if(!$query_content_input['bill_date']) { - throw new WxPayException("对账单接口中,缺少必填参数bill_date!"); + throw new Exception("对账单接口中,缺少必填参数bill_date!"); } $this->api_info_arr['nonce_str'] = ($this->get_nonce_str());//随机字符串 @@ -127,6 +129,92 @@ class Wxpay_call return $ret; } + /** + * 获取资金账单数据 + */ + public function get_fund_bill_data($query_content) + { + $ret = array('status'=>false, 'data'=>array()); + $this->init_api(); + // return false; + $url = "https://api.mch.weixin.qq.com/pay/downloadfundflow"; + $query_content_input = $query_content->getBizContent(true); + //检测必填参数 + if(!$query_content_input['bill_date']) { + throw new Exception("对账单接口中,缺少必填参数bill_date!", 1); + } + unset($query_content_input['bill_type']); + $query_content_input['account_type'] = 'Basic'; + $this->api_info_arr['nonce_str'] = ($this->get_nonce_str());//随机字符串 + + $ready_api = array_merge($this->api_info_arr, $query_content_input); + $this->api_info_arr['sign'] = $this->make_sign($ready_api, 'HMAC-SHA256');//签名 + $ready_api['sign'] = $this->api_info_arr['sign']; + $xml = to_xml($ready_api); + + $response = $this->post_xml_curl($this->api_info_arr, $xml, $url, 20); + if(substr($response, 0 , 5) == ""){ + log_message('error',"Wxpay fund bill error \r\n" . var_export($response, 1)); + return $ret; + } + $ret['status'] = true; + $response_arr = explode("\n",$response); + // var_dump($response_arr); + // die(); + $data_title = $this->fund_bill_data_title(); + $data_arr = []; + 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])); + } + $row_arr['wxpay_fee'] = ''; + !empty($row_arr['trade_direction']) ? $data_arr[] = $row_arr : false; + } + $ret_g = array_reduce($data_arr, function($carry, $item) { + $item['currency_type'] = 'CNY'; + $item['trade_state'] = $item['trade_type']; + $item['trade_type'] = 'BILL'; + $item['bank_type'] = $item['item_name'] = $item['attach'] = $item['openid'] = ''; + if (isset($carry[$item['transaction_id']]) && !empty($carry[$item['transaction_id']])) { + $fee = $item['trade_direction'] == '支出' && stripos($item['sub_trade_type'], '手续费') !== false ? $item['settlement_total_fee'] : 0; + $net_amount = bcsub($carry[$item['transaction_id']][0]['settlement_total_fee'], $fee); + $carry[$item['transaction_id']][0]['net_amount'] = $net_amount; + $carry[$item['transaction_id']][0]['wxpay_fee'] = $fee; + $carry[$item['transaction_id']][0]['refund_id'] = '0'; + } else { + if ($item['sub_trade_type'] == '退款') { + $item['net_amount'] = $item['settlement_total_fee']; + preg_match_all('/(\d+\.\d+)/', $item['remark'], $matches); + $amount = isset($matches[1][0]) ? $matches[1][0] : $item['settlement_total_fee']; + $fee = isset($matches[1][1]) ? $matches[1][1] * -1 : ''; + $item['settlement_refund_fee'] = $amount; + $item['call_refund_fee'] = $amount; + $item['wxpay_fee'] = $fee; + $item['out_refund_id'] = $item['out_trade_no']; + $item['refund_id'] = $item['transaction_id']; + $item['refund_status'] = 'SUCCESS'; + $item['out_trade_no'] = ''; + } + $item['item_name'] = preg_match('/^[A-Z0-9]{6}-[A-Z0-9]{7}-[A-Z0-9]{4}$/', $item['out_trade_no']) ? '收钱码收款' : ''; + $carry[$item['transaction_id']][] = $item; + } + return $carry; + }, []); + $ret['data'] = array_reduce(array_values($ret_g), function($carry, $item) { + $carry[] = $item[0]; + return $carry; + }, []); + return $ret; + } + /*! * @Author: LYT * @Date: 2019-06-18 15:23:15 @@ -166,6 +254,27 @@ class Wxpay_call ); } + /** + * 资金账单数据的格式 + * 记账时间,微信支付业务单号,资金流水单号,业务名称,业务类型,收支类型,收支金额(元),账户结余(元),资金变更提交申请人,备注,业务凭证号 + */ + private function fund_bill_data_title() + { + return array( + "complete_time", // 记账时间 + "transaction_id", // 微信支付业务单号 + "fund_transaction_id", // 资金流水单号 + "trade_type", // 业务名称 + "sub_trade_type", // 业务类型 + "trade_direction", // 收支类型 + "settlement_total_fee", // 收支金额(元) + "balance", // 账户结余(元) + "handler", // 资金变更提交申请人 + "remark", // 备注 + "out_trade_no", // 业务凭证号, 退款是out_refund_id + ); + } + /** * 以post方式提交xml到对应的接口url * @@ -197,6 +306,14 @@ class Wxpay_call curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,TRUE); curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,2);//严格校验 } + if (is_dir($this->wx_site_config['cert_path'])) { + curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM'); + curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM'); + curl_setopt($ch, CURLOPT_SSLKEYPASSWD, $this->wx_site_config['mch_id']); + curl_setopt($ch, CURLOPT_SSLCERT, $this->wx_site_config['cert_path'] . "/apiclient_cert.pem"); + curl_setopt($ch, CURLOPT_SSLKEY, $this->wx_site_config['cert_path'] . "/apiclient_key.pem"); + } + curl_setopt($ch,CURLOPT_USERAGENT, $ua); //设置header curl_setopt($ch, CURLOPT_HEADER, FALSE); @@ -242,7 +359,11 @@ class Wxpay_call return true; } - public function make_sign($xml_arr, $needSignType = false) + /** + * @param mixed $xml_arr + * @param string $SignType 签名方式,MD5 或 HMAC-SHA256 + */ + public function make_sign($xml_arr, $SignType = 'MD5') { //签名步骤一:按字典序排序参数 ksort($xml_arr); @@ -250,12 +371,17 @@ class Wxpay_call //签名步骤二:在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']); + switch ($SignType) { + case 'MD5': + $string = md5($string); + break; + case 'HMAC-SHA256': + $string = hash_hmac("sha256", $string, $this->wx_site_config['key']); + break; + + default: + $string = md5($string); + break; } //签名步骤四:所有字符转为大写 $result = strtoupper($string); @@ -294,6 +420,9 @@ class Wxpay_call return $str; } + /** + * @deprecated + */ protected function curl($url, $postFields = null) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); diff --git a/webht/third_party/pay/models/Online_payment_note_model.php b/webht/third_party/pay/models/Online_payment_note_model.php index 75082daf..58d32e10 100644 --- a/webht/third_party/pay/models/Online_payment_note_model.php +++ b/webht/third_party/pay/models/Online_payment_note_model.php @@ -39,7 +39,7 @@ class Online_payment_note_model extends CI_Model { $query = $this->info->query($sql); if ($query->num_rows() > 0) { $update = array( - 'OPN_payFee' => isset($column['OPN_payFee']) ? bcsub(0,$column['OPN_payFee']) : null, + 'OPN_payFee' => isset($column['OPN_payFee']) ? $column['OPN_payFee'] : null, 'OPN_netAmount' => $column['OPN_netAmount'], 'OPN_entryAmountCNY' => $column['OPN_entryAmountCNY'], );