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
";
//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 = "";
}