userInfo = (new AccountManager())->getCurrentUser(); $this->domain = $_SERVER['HTTP_HOST']; $httpSchema = $_SERVER['REQUEST_SCHEME'] ?? 'http'; $prefix = $prefix2 = "{$httpSchema}://{$this->domain}"; $this->paymentName = $dataAccess->paymentName; if ($dataAccess->paymentName) { if (!$this->userInfo) Render('', -4001); $this->getConfig(); $dataAccess->orderSn = $this->paymentConfig['order_prefix'] . OrderID(); if ($this->paymentConfig['notify_url']) { $_prefix = self::getDomain($this->paymentConfig['notify_url']); if ($_prefix) $prefix = $_prefix; } if ($this->paymentConfig['callback_url']) { $_prefix = self::getDomain($this->paymentConfig['callback_url']); if ($_prefix) $prefix2 = $_prefix; } $bla = explode('||', $this->paymentConfig['black_list']); if (in_array($this->userInfo->account, $bla)) { Render('', -5990); } } $this->notifyUrl = "{$prefix}/Payment-Pay/Notify.{$dataAccess->paymentName}"; $this->redirectUrl = "{$prefix2}/Payment-Pay/Redirect.{$dataAccess->paymentName}"; $this->dataAccess = $dataAccess; $this->isMobile = is_mobile(); $this->config(); $this->init(); } /** * 设置返回类型 * @param string $type */ abstract function setReturnType($type = 'HTML'); /** * 发起支付函数 * @return mixed */ abstract function goPay(); /** * 返回DATAACCESS */ public function DataAccess() { return $this->dataAccess; } /** * 获取配置信息 * @return void [type] [description] * @throws Exception */ public function getConfig() { if (empty($this->paymentName)) { throw new Exception("支付名不能为空", 1); } $this->paymentConfig = M('view_payment')->where('gateway_identifier', $this->paymentName)->find(); if (!$this->paymentConfig) { throw new Exception("支付不存在", 1); } $this->paymentConfig['notify_url'] = self::getDomain($this->paymentConfig['notify_url']); $this->paymentConfig['callback_url'] = self::getDomain($this->paymentConfig['callback_url']); } /** * 系统初始化 * @return void [type] [description] */ public function init() { } /** * 更新用户账户余额函数 * @param string $order_no * @param string $payer * @param string $channel_name * @param float $price * @param float $real_price * @param string $bill_no * @return mixed */ protected function handleUserMoney(string $order_no, string $payer, string $channel_name, float $price, float $real_price = 0, $bill_no = '') { if (!$order_no) { return -5997; } $order_info = (new Pay_order())->where('pay_orders.order_no', $order_no) ->leftJoin('pay_channels', 'pay_channels.id', '=', 'pay_orders.channel_id') ->select('pay_orders.*', 'pay_channels.gift_price_rate', 'pay_channels.gift_black_list') ->first(); if ($order_info->status == 1) return 2;//如已确认订单直接返回 $data['status'] = 1; if (!empty($bill_no)) { $data['bill_no'] = $bill_no; } $data['notify_query'] = 'REQUEST=>' . http_build_query($_REQUEST) . '||json=>' . file_get_contents('php://input'); $data['updated_at'] = date('Y-m-d H:i:s'); if ($real_price) { $data['real_price'] = $real_price; } $operator = "auto"; $reason = "在线充值"; $detail = sprintf("用户【%s】于%s完成在线支付,订单号:%s,支付金额:%s元。", $payer, date('Y-m-d H:i:s'), $order_no, $price); $aoc = new AddOrCutMoney(); _beginTransaction(); $ret = lm('Pay_order', 'commons')->where('order_no', $order_no)->update($data); if ($ret !== 1) { _rollBack(); return $ret; } $accountDetail = new AccountDetail(); $account_identity = $order_info->payer_id; $accountDetail = $accountDetail->where('account_identity', $account_identity)->first(); if (!$accountDetail) return -5991; $moneyRecharge = lm('MoneyRecharge', 'api')->where('order_id', $order_no)->first(); if (!$moneyRecharge) $ret = $aoc->addMoneyRechargeRec($accountDetail, $order_info->payer, $order_no, $order_info->price, '在线充值', $operator, $reason, $reason, 1, 1); else $ret = lm('MoneyRecharge', 'api')->where('order_id', $order_no)->update(['status' => 1]); if (is_int($ret) && $ret < 1) { _rollBack(); return $ret; } $ret = $aoc->addMoneyDetailRec($accountDetail, $order_info->payer, $order_no, $order_info->price, 1, 6, $operator, $detail, $reason); if (is_int($ret)) { _rollBack(); return $ret; } $ret = $aoc->incAccountDetailMoney($accountDetail, $order_info->price); if ($ret !== 1) { _rollBack(); return $ret; } $gift_price_rate = explode('||', $order_info->gift_price_rate); if (!empty($gift_price_rate)) { $gbla = explode('||', $order_info->gift_black_list); $num = intval($gift_price_rate['0']) ?? 0; $rate = intval($gift_price_rate['1']) ?? 0; if (!in_array($payer, $gbla) && $rate && $num <= $price) { $gift_price = $price * $rate / 100; $gift_price = floatval($gift_price); $gift_order_no = $order_no . "-G"; $reason = '在线充值赠送'; $detail = sprintf("用户【%s】于%s获得充值赠送,订单号:%s,赠送金额:%s元。", $payer, date('Y-m-d H:i:s'), $gift_order_no, $gift_price); $accountDetail->cash = floatval($accountDetail->cash + $order_info->price); $ret = $aoc->addMoneyRechargeRec($accountDetail, $order_info->payer, $gift_order_no, $gift_price, '在线充值赠送', $operator, $reason, $reason);//资金明细 if (is_int($ret)) { _rollBack(); return $ret; } $ret = $aoc->addMoneyDetailRec($accountDetail, $order_info->payer, $gift_order_no, $gift_price, 1, 13, $operator, $detail, $reason);//流水记录 if (is_int($ret)) { _rollBack(); return $ret; } $ret = $aoc->incAccountDetailMoney($accountDetail, $gift_price); if ($ret !== 1) { _rollBack(); return $ret; } } } _commit(); return 1; } /** * 模拟表单提交处理函数 * @param array $order_info * @param string $action 提交方式 * @return string */ public function handlePost(array $order_info, $action = "POST") { if ($this->debug) { $inputType = "text"; $script = ''; } else { $inputType = "hidden"; $script = 'setTimeout(()=>{document.getElementById("submitForm").submit();},200)'; } $orderInfo = $order_info ? $order_info : $this->buildOrder(); $url = $this->apiUrl; if (is_array($orderInfo) && count($orderInfo) > 0) { $forms = ''; foreach ($orderInfo as $k => $v) { $forms .= ""; } /* $forms = trim($forms, PHP_EOL . "\t\t\t");*/ } if (empty($url)) { $url = $this->getWay; } $str = " Payment By CreditCard online
{$forms}
"; //toLog($str); return $str; } /** * 提取证书中的公钥和私钥KEy,以数据形式返回 * @return array [type] [description] * @throws Exception */ public function certPatchKey() { $priContent = file_get_contents($this->privateCertPath); $pubContent = file_get_contents($this->publicCertPath); $priKey = openssl_get_privatekey($priContent); $pubKey = openssl_pkey_get_public($pubContent); if (empty($priKey) || empty($pubKey)) { throw new Exception("支付的私钥和公钥不能为空", 1); } return array('private' => $priKey, 'public' => $pubKey); } /** * 处理CURL提交函数 * @param [type] $url [description] * @param array $data [description] * @param array $header * @param bool $ssl * @return bool|int|string [type] [description] */ function goPayCurl($url, $data = array(), $header = array(), $ssl = false) { if (is_array($data)) { $data = http_build_query($data); } $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, $ssl); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $ssl); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, $data); if (is_array($header) && count($header) > 0) { curl_setopt($ch, CURLOPT_HTTPHEADER, $header); curl_setopt($ch, CURLOPT_HEADER, 0); } curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); // curl_setopt($ch, CURLOPT_HTTPHEADER, 1); $result = curl_exec($ch); // $errno = curl_errno($ch); curl_close($ch); if ($result == NULL) { return 0; } return $result; } /** * 提示信息 * @param [type] $msg [description] * @param integer $code [description] * @return void [type] [description] */ function showNotice($msg, $code = -1) { if (strtolower($this->returnType) == 'json') { JsonReturn('', $code, $msg); } else { exit($msg); } } /** * 成功信息 * @param [type] $data [description] * @return void [type] [description] */ function jsonSuccess($data) { if ($this->returnType == 'json') { JsonReturn($data, 1, 'success'); } } /** * 获取ip信息 */ function getRealIp() { $ip = false; if (!empty($_SERVER["HTTP_CLIENT_IP"])) { $ip = $_SERVER["HTTP_CLIENT_IP"]; } if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { $ips = explode(", ", $_SERVER['HTTP_X_FORWARDED_FOR']); if ($ip) { array_unshift($ips, $ip); $ip = FALSE; } for ($i = 0; $i < count($ips); $i++) { if (!eregi("^(10|172\.16|192\.168)\.", $ips[$i])) { $ip = $ips[$i]; break; } } } return ($ip ? $ip : $_SERVER['REMOTE_ADDR']); } /** * 订单写入数据表(pay_orders) * @param array $orderInfo * @return int|void */ function addOrder(array $orderInfo) { $this->log($orderInfo['gateway_name'] . '-Pay', '开始写入订单信息'); if ($this->debug) return; if (empty($orderInfo['order_no']) || empty($orderInfo['payer']) || empty($orderInfo['price']) || !$orderInfo['payer_id']) { JsonReturn('', -1, '传参有误!'); } $order_info = lm('Pay_order', 'Commons')->where('order_no', $orderInfo['order_no'])->find(); if ($order_info) { JsonReturn('', -2, '您已成功提交该订单,请不要重复操作!'); } $payer = $orderInfo['payer']; $price = $orderInfo['price']; $order_no = $orderInfo['order_no']; $operator = 'auto'; $reason = '在线充值'; $accountDetail = new AccountDetail(); // $account_info = (new Account())->getAccountByToken(); $account_identity = $this->userInfo['account_identity']; $accountDetail = $accountDetail->where('account_identity', $account_identity)->first(); if (!$accountDetail) return -5991; $aoc = new AddOrCutMoney(); _beginTransaction(); $res = lm('Pay_order', 'Commons')->insert($orderInfo); if ($res < 1) { _rollBack(); $this->log($orderInfo['gateway_name'] . '-Pay', '订单写入失败,请尝试重新提交!'); JsonReturn('', -3, '订单写入失败,请尝试重新提交!'); } $res = $aoc->addMoneyRechargeRec($accountDetail, $payer, $order_no, $price, '在线充值', $operator, $reason, $reason, 1, 0); if (is_int($res)) { _rollBack(); $this->log($orderInfo['gateway_name'] . '-Pay', '订单REMARK写入失败,请尝试重新提交!'); JsonReturn('', -4, '订单REMARK写入失败,请尝试重新提交!'); } _commit(); $this->log($orderInfo['gateway_name'] . '-Pay', '订单写入成功!订单号:' . $orderInfo['order_no'] . ',金额:' . $orderInfo['price']); return 1; } /** * 获取订单信息函数 * @param string $order_no * @return Pay_order||null */ protected function getOrder(string $order_no) { return $channel_info = lm('Pay_order', 'commons') ->leftJoin('pay_gateways', 'pay_gateways.id', '=', 'pay_orders.gateway_id') ->where('pay_orders.order_no', $order_no) ->first(); } /** * 获取订单信息函数 * @param string $bill_no * @return Pay_order||null */ protected function getOrderByBillNo(string $bill_no) { return $channel_info = lm('Pay_order', 'commons') ->leftJoin('pay_gateways', 'pay_gateways.id', '=', 'pay_orders.gateway_id') ->where('bill_order_no', $bill_no) ->first(); } /** * 生成订单表基本数据 * @param array $orderInfo * @return array */ function buildOrderInfo(array $orderInfo = []): array { return [ 'order_no' => $this->dataAccess->orderSn, 'payer' => ($orderInfo['payer'] ?? $this->userInfo->account) ?? 'guest', 'payer_id' => ($orderInfo['payer_id'] ?? $this->userInfo->account_identity) ?? null, 'price' => $this->dataAccess->money, 'status' => 0, 'remark' => $this->dataAccess->extra ?? '', 'ip' => $this->getRealIp(), 'created_at' => date('Y-m-d H:i:s'), 'operator' => 'auto', 'gateway_id' => $this->dataAccess->gateway_id, 'channel_id' => $this->dataAccess->channel_id, ]; } /** * 返回支付通道信息 * @param string $gateway_code * @param string $gateway_identifier * @return View_payment|null */ protected function getChannelInfo(string $gateway_code, string $gateway_identifier) { return $channel_info = lm('View_payment', 'commons') ->where('gateway_identifier', $gateway_identifier) ->where('gateway_code', $gateway_code) ->first(); } /** * 同步跳转 * @param array $data * @return void [type] [description] */ function callback(array $data = []) { $html = <<setTimeout(function() { window.history.go(-2); },3000); EOT; echo '支付已受理。如有疑问请联系客服!3秒后返回支付页,请稍候...'; echo $html; } /** * 验证签名函数 * @param array $param * @param string $sign * @param string $order_no * @param float $price * @param float $real_price * @param bool $is_bill_no * @param string $bill_no * @return void */ protected function checkSign(array $param, string $sign, string $order_no, float $price, float $real_price = 0, bool $is_bill_no = false, $bill_no = '') { if (!$price) { die(Lang('Errors', 'Api')->get('error-5993')); } $order_info = $is_bill_no ? $this->getOrderByBillNo($order_no) : $this->getOrder($order_no); if (floatval($price) != floatval($order_info->price)) { $this->log($order_info->gateway_name . '-Notify', $order_no . ':' . Lang('Errors', 'Api')->get('error-5996')); die(Lang('Errors', 'Api')->get('error-5996')); } $order_no = $order_info->order_no; $gateway_code = $order_info->gateway_code; $gateway_identifier = $order_info->gateway_identifier; // dd($gateway_code, $gateway_identifier, $order_info); $channel_info = $this->getChannelInfo($gateway_code, $gateway_identifier); if (!$channel_info || !$order_no) { $this->log($gateway_identifier . '-Notify', $order_no . ':' . Lang('Errors', 'Api')->get('error-5994')); die(Lang('Errors', 'Api')->get('error-5994')); } if (!$bill_no) $bill_no = $order_no; $merchant_secret = $channel_info->merchant_md5_secret; $this->merchant_id = $channel_info->merchant_id; $newSign = $this->buildSign($param, $merchant_secret); if ($newSign === $sign) { $this->log($gateway_identifier . '-Notify', '订单' . $order_no . '已校验成功'); $ret = $this->handleUserMoney($order_no, $order_info->payer, $channel_info->channel_name, $price, $real_price, $bill_no); if ($ret === 1) { $this->log($gateway_identifier . '-Notify', '订单已被确认!订单号:' . $order_no . ',订单金额:' . $price . '元。'); } elseif ($ret === 2) { //已确认过 } else { //更新账户金额出现异常 $this->log($gateway_identifier . '-Notify', '订单确认失败:' . Lang('Errors', 'Api')->get('error' . $ret) . '!订单号:' . $order_no . ',订单金额:' . $price . '元,状态码:' . $ret); } die($this->success); } else { $obj = new Pay_order(); $order_info = $obj->where('order_no', $order_no)->first(); if ($order_info && $order_info->status == 0) { $obj->update(['notify_query' => 'REQUEST=>' . http_build_query($_REQUEST) . '||json=>' . file_get_contents('php://input')]) ->where(['order_no' => $order_no]); } $this->log($gateway_identifier . '-Notify', '订单信息被篡改!订单号:' . $order_no . ',订单金额:' . $price . '元,来自IP:' . $this->getRealIp()); die($this->errMsg ?? '订单信息被篡改,如有疑问请联系客服!'); } } /** * 订单入库,返回订单预支付信息函数 * @param bool $skip_order * @param string $qrcode_url * @param string $type * @return void */ protected function prePay(bool $skip_order = false, string $qrcode_url = '', $type = '') { $orderInfo = $skip_order ? new stdClass() : $this->buildOrder(); $sysOrderInfo = $this->buildOrderInfo(); $payInfo = new PayInfo(); $payInfo->debug = $this->debug; $payInfo->orderInfo = $orderInfo; $payInfo->apiUrl = $this->apiUrl; $payInfo->jump = $skip_order ? 1 : 0; if ($qrcode_url) { $payInfo->orderInfo = $sysOrderInfo; $payInfo->orderInfo['qrcode_url'] = $qrcode_url; $payInfo->orderInfo['type'] = $type; ksort($payInfo->orderInfo); $payInfo->orderInfo['qrcode_sign'] = md5(http_build_query($payInfo->orderInfo)); $httpSchema = $_SERVER['REQUEST_SCHEME'] ?? 'http'; $payInfo->apiUrl = "{$httpSchema}://{$this->domain}/Payment-Pay/Qrcode"; $payInfo->jump = 0; } !$this->method ?: $payInfo->method = $this->method; $payInfo->jump_url = (!$this->paymentConfig['en_jump']) ? '' : $this->paymentConfig->jump_url; $this->payInfo = $payInfo; //跳转对方网关时直接订单入库 if ($this->paymentConfig['rand_price']) $this->dataAccess->money -= $this->dataAccess->rand_price; $this->log($this->paymentConfig['gateway_identifier'] . '-Pay', '订单提交数据:' . json_encode($orderInfo)); $this->addOrder($sysOrderInfo); if ($this->jump) { $this->jump($payInfo); exit; } Render($this->payInfo, 1); } /** * 支付日志操作函数 * @param $file_name * @param $contents * @return bool|int */ protected function log($file_name, $contents) { $file_path = ROOT_PATH . '/Cache/log/' . $file_name . '.log'; $contents = PHP_EOL . date('Y-m-d H:i:s') . ':' . $contents . PHP_EOL; return file_put_contents($file_path, $contents, FILE_APPEND); } /** * 格式化RSA密钥函数 * @param $secretString * @param bool $is_pub_key * @return string */ function formatRsaSecret($secretString, $is_pub_key = false) { $secretString = str_replace(['-----BEGIN PUBLIC KEY-----', '-----BEGIN PRIVATE KEY-----', '-----END PRIVATE KEY-----', '-----END PUBLIC KEY-----'], '', $secretString); $secretString = str_replace(['\n', '\r', '\t'], '', $secretString); $body = ''; $pos = 0; $read = true; while ($read) { $sub = substr($secretString, $pos, 64); $pos += 64; $body .= $sub; $read = $sub ? true : false; } $header = $is_pub_key ? '-----BEGIN PUBLIC KEY-----' : '-----BEGIN PRIVATE KEY-----'; $footer = $is_pub_key ? '-----END PUBLIC KEY-----' : '-----END PRIVATE KEY-----'; return $header . PHP_EOL . $body . PHP_EOL . $footer; } /** * 默认的二次跳转类网关处理函数 * @param PayInfo $pay_info */ protected function jump(PayInfo $pay_info) { $order_info = $pay_info->orderInfo; echo $this->handlePost($order_info, $this->method); } /** * 返回网址域名部份函数 * @param string $url * @return string */ protected function getDomain(string $url) { preg_match('/http(s)?:\/\/[\d\w]+([\-]?[\w\d]+)?(\.[\d\w]+([\-]?[\w\d]+)?[:\d+]?)+\//is', $url, $found); return $found ? $found[0] : ''; } } /** * 预支付订单信息类 */ class PayInfo { public $debug = 0; public $orderInfo; public $apiUrl; public $method = "POST"; public $jump = 0; public $jump_url = ""; }