397 lines
17 KiB
PHP
397 lines
17 KiB
PHP
<?php
|
|
// +----------------------------------------------------------------------
|
|
// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
|
|
// +----------------------------------------------------------------------
|
|
// | Copyright (c) 2017~2023 https://www.yiovo.com All rights reserved.
|
|
// +----------------------------------------------------------------------
|
|
// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
|
|
// +----------------------------------------------------------------------
|
|
// | Author: 萤火科技 <admin@yiovo.com>
|
|
// +----------------------------------------------------------------------
|
|
declare (strict_types=1);
|
|
|
|
namespace app\api\controller;
|
|
|
|
use app\api\model\Code as CodeModel;
|
|
use app\api\model\Notify as NotifyModel;
|
|
use app\api\model\Shop as ShopModel;
|
|
use app\api\model\Order as OrderModel;
|
|
use think\cache\driver\Redis;
|
|
use cores\exception\BaseException;
|
|
use think\facade\Cache;
|
|
use think\response\Json;
|
|
use GuzzleHttp\Client;
|
|
use GuzzleHttp\Psr7\Request;
|
|
use GuzzleHttp\Cookie\CookieJar;
|
|
|
|
/**
|
|
* 产码控制器
|
|
* Class Code
|
|
* @package app\api\controller
|
|
*/
|
|
class Code extends Controller
|
|
{
|
|
/**
|
|
* 查询支付状态
|
|
* @throws \app\common\exception\BaseException
|
|
* @throws \think\exception\DbException
|
|
*/
|
|
public function payStatus()
|
|
{
|
|
$data = $this->request->get();
|
|
$zone = isset($data['zone'])?$data['zone']:1; //订单分区
|
|
$this->saveLog("进入支付查询");
|
|
//开始
|
|
$model = new CodeModel();
|
|
$orderModel = new OrderModel();
|
|
$NotifyModel = new NotifyModel();
|
|
|
|
$array = $model->getList($zone);
|
|
|
|
$this->saveLog("进入支付查询,分区:".$zone.",订单数量:".count($array));
|
|
//获取IP组
|
|
$iPGroup = $this->getIPGroup();
|
|
foreach ($array as $k=>$val) {
|
|
$payTotal = $orderModel->getZoneOrderTotal([['code_id', '=', $val['code_id']],['order_status', '=', 20]]);
|
|
$waitPayTotal = $orderModel->getZoneOrderTotal([['code_id', '=', $val['code_id']],['order_status', '=', 10]]);
|
|
$modelOr = OrderModel::detail([['code_id', '=', $val['code_id']],['order_status', '=', 10]]);
|
|
$text = $val['passageway'] == 'ZFBH5'?'订单已支付':'该订单已完成支付';
|
|
if (isset($modelOr['order_id']) && !empty($modelOr['order_id']) && $payTotal <= 0 && $waitPayTotal == 1) {
|
|
if(isset($iPGroup[0]['ip'])) {
|
|
$res = $this->checkStatusProxy(htmlspecialchars_decode($val['search_url']), $val['passageway'], $iPGroup[0]['ip'], $iPGroup[0]['port'], $text);
|
|
}else {
|
|
$res = $this->checkStatus(htmlspecialchars_decode($val['search_url']), $val['passageway'], $text);
|
|
}
|
|
if ($res !== false) { //支付成功
|
|
$pass = $modelOr['callback'] == 1 ? $NotifyModel->add($modelOr) : true;
|
|
if ($pass) {
|
|
$model->update([
|
|
'order_id' => $modelOr['order_id'],
|
|
'pay_status' => 1,
|
|
],['code_id'=>$val['code_id']]);
|
|
$modelOr->save([
|
|
'pay_status' => 20,
|
|
'pay_time' => time(),
|
|
'order_status' => 20,
|
|
]);
|
|
return $this->renderSuccess('操作成功');
|
|
} else {
|
|
return $this->renderError($NotifyModel->getError() ?: '操作失败');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
$this->saveLog("支付查询结束,分区:".$zone.",订单数量:".count($array));
|
|
}
|
|
|
|
/**
|
|
* 查询失效状态
|
|
* @throws \app\common\exception\BaseException
|
|
* @throws \think\exception\DbException
|
|
*/
|
|
public function lapseStatus()
|
|
{
|
|
//连接本地的 Redis 服务
|
|
// 获取分布式锁
|
|
$lock_key = 'lapseStatus_lock';
|
|
$max_wait_time = 10; // 最大等待时间(秒)
|
|
$retry_interval = 100; // 重试间隔时间(毫秒)
|
|
$lock_ttl = 600; // 锁定时间(秒)
|
|
$lock_count = 0; // 锁的重试次数
|
|
// 获取分布式锁
|
|
$redis = Cache::store('redis');
|
|
$lock = false;
|
|
$wait_time = 0;
|
|
$lock_value = '';
|
|
while ($wait_time <= $max_wait_time) {
|
|
$lock_value = uniqid(); // 生成唯一的锁值
|
|
if ($redis->set($lock_key, $lock_value, 'EX', $lock_ttl, 'NX')) {
|
|
$lock = true;
|
|
break;
|
|
} else {
|
|
// 获取锁失败,等待一段时间后重试
|
|
$lock_count++;
|
|
usleep($retry_interval * 1000);
|
|
$wait_time += ($retry_interval / 1000);
|
|
if ($lock_count > 40) {
|
|
// 重试次数超过限制,抛出异常
|
|
$this->saveLog("失效查询重试次数超过限制");
|
|
}
|
|
}
|
|
}
|
|
if($lock) {
|
|
try {
|
|
// TODO: 需要加锁的业务逻辑
|
|
// 字符串操作
|
|
$conn = new \think\cache\driver\Redis(config('cache.stores.redis'));
|
|
$codesIp = $conn->get('codesIp');
|
|
$codesPort = $conn->get('codesPort');
|
|
$codeNum = $conn->get('codeNum');
|
|
if (empty($codesIp) || empty($codeNum) || $codeNum <= 0) {
|
|
//获取IP组
|
|
$iPGroup = $this->getIPGroup();
|
|
$codesIp = isset($iPGroup[0]['ip']) ? $iPGroup[0]['ip'] : '';
|
|
$codesPort = isset($iPGroup[0]['port']) ? $iPGroup[0]['port'] : '';
|
|
$codeNum = 20;
|
|
$conn->set('codesIp', $codesIp);
|
|
$conn->set('codesPort', $codesPort);
|
|
}
|
|
$modelOr = new OrderModel();
|
|
$model = new CodeModel();
|
|
$this->saveLog("失效查询开始");
|
|
//非失效码的状态更新
|
|
$nonFailure = $model->getNonFailure();
|
|
foreach ($nonFailure as $val) {
|
|
$codeM = CodeModel::detail($val['code_id']);
|
|
$create_time = is_numeric($val['create_time']) ? $val['create_time'] : strtotime($val['create_time']);
|
|
//支付码失效
|
|
if ($create_time + $val['expiration_time'] < time()) {
|
|
$codeM->save([
|
|
'status' => 3,
|
|
]);
|
|
//分配时间过长未支付,订单超时,支付码释放
|
|
} elseif ($val['allot_time'] + 300 < time()) {
|
|
$text = $val['passageway'] == 'ZFBH5' ? '订单已支付' : '该订单已完成支付';
|
|
if (!empty($codesIp)) {
|
|
$conn->set('codeNum', $codeNum - 1);
|
|
$res = $this->checkStatusProxy(htmlspecialchars_decode($val['search_url']), $val['passageway'], $codesIp, $codesPort, $text);
|
|
} else {
|
|
$res = $this->checkStatus(htmlspecialchars_decode($val['search_url']), $val['passageway'], $text);
|
|
}
|
|
$status = 1;
|
|
$pay_status = 0;
|
|
if ($res !== false) { //已支付 更改状态
|
|
$status = 3;
|
|
$pay_status = 1;
|
|
} else {
|
|
//重新写入队列左边(为的是后进先出,失败的一般代表比较前面的码,所以放在左边)
|
|
$code_queue = ['code_id' => (int)$val['code_id'], 'expire_time' => $create_time + (float)$val['expiration_time']]; // 过期时间
|
|
//先清楚队列再加入,避免重复
|
|
Cache::store('redis')->lRem($val['passageway'] . '_' . ($val['money'] * 1), json_encode($code_queue, 320), 1);
|
|
Cache::store('redis')->lPush($val['passageway'] . '_' . ($val['money'] * 1), json_encode($code_queue, 320));
|
|
$this->saveLog("回码:".json_encode($code_queue, 320));
|
|
}
|
|
|
|
$codeM->save([
|
|
'allot_time' => 0,
|
|
'back_time' => time(),
|
|
'status' => $status,
|
|
'pay_status' => $pay_status,
|
|
]);
|
|
}
|
|
if ($val['code_id']) {
|
|
$modelOr->update([
|
|
'code_id' => 0,
|
|
'order_status' => 30,
|
|
'old_code_id' => $val['code_id']
|
|
], ['code_id' => $val['code_id'],
|
|
'order_status' => 10]);
|
|
}
|
|
}
|
|
//处理未匹配上码的订单为超时
|
|
$modelOr->update([
|
|
'order_status' => 30,
|
|
], [['code_id', '=', 0],
|
|
['order_status', '=', 10], ['create_time', '<', time() - 120]]);
|
|
$this->saveLog("失效查询结束");
|
|
} finally {
|
|
// 释放锁
|
|
$lock_value_in_redis = $redis->get($lock_key);
|
|
if ($lock_value_in_redis === $lock_value) { // 如果当前锁的值和刚才设置的值相等,则说明当前线程持有该锁,可以进行释放
|
|
$redis->del($lock_key); // 删除锁
|
|
}
|
|
}
|
|
}
|
|
//未使用超过调起次数的支付码
|
|
/*$modelNew = new CodeModel();
|
|
$modelNew->setStatusLose([],['status' => array('=', 1),
|
|
'num' => array('>=', 100)]);*/
|
|
}
|
|
|
|
public function getIPGroup(): array
|
|
{
|
|
$html = @file_get_contents("http://http.tiqu.letecs.com/getip3?num=1&type=2&pro=&city=0&yys=0&port=1&time=1&ts=0&ys=0&cs=0&lb=1&sb=0&pb=4&mr=1®ions=");
|
|
$result = json_decode($html,true);
|
|
$ips = is_array($result['data'])?$result['data']:[];
|
|
return $ips;
|
|
}
|
|
|
|
public function test(){
|
|
$data = $this->request->get();
|
|
$queue_name = $data['passageway'].'_'.$data['money'];
|
|
$queue_length = Cache::store('redis')->lLen($queue_name);
|
|
var_dump($queue_length);
|
|
// 获取队列中的所有值
|
|
$queue_values = Cache::store('redis')->lRange($queue_name, 0, $queue_length - 1);
|
|
var_dump($queue_values);
|
|
die();
|
|
/*return $this->renderSuccess([], '提交成功');
|
|
$html = @file_get_contents("http://http.tiqu.letecs.com/getip3?num=1&type=2&pro=&city=0&yys=0&port=1&pack=301270&ts=0&ys=0&cs=0&lb=1&sb=0&pb=4&mr=1®ions=");
|
|
$result = json_decode($html,true);
|
|
$ips = $result['data'];
|
|
var_dump(date("Y-m-d H:i:s"));*/
|
|
$data = $this->request->get();
|
|
$order_id = $data['order_id'];
|
|
var_dump($this->checkQueue($order_id));die();
|
|
$val = OrderModel::detail($order_id);
|
|
$codesIp = '';
|
|
$codesPort = '';
|
|
//获取IP组
|
|
$iPGroup = $this->getIPGroup();
|
|
var_dump($iPGroup);
|
|
$codesIp = isset($iPGroup[0]['ip'])?$iPGroup[0]['ip']:'';
|
|
$codesPort = isset($iPGroup[0]['port'])?$iPGroup[0]['port']:'';
|
|
/*$aa = str_replace("&", "%26", $val['search_url']);
|
|
$bb = str_replace("amp;", "", $aa);
|
|
$stream_opts = [
|
|
"ssl" => [
|
|
"verify_peer" => false,
|
|
"verify_peer_name" => false,
|
|
]
|
|
];
|
|
$html = @file_get_contents(urldecode($bb), false, stream_context_create($stream_opts));
|
|
$res = strpos($html, '该订单已完成支付');*/
|
|
$text = $val['passageway'] == 'ZFBH5'?'订单已支付':'该订单已完成支付';
|
|
/*if(!empty($codesIp)) {
|
|
$res = $this->checkStatusProxy(htmlspecialchars_decode($val['search_url']), $val['passageway'], $codesIp, $codesPort, $text);
|
|
}else {
|
|
$res = $this->checkStatus(htmlspecialchars_decode($val['search_url']), $val['passageway'], $text);
|
|
}*/
|
|
$res = $this->checkStatusProxy(htmlspecialchars_decode($val['search_url']), $val['passageway'], $codesIp, $codesPort, $text);
|
|
//$res = $this->checkStatus(htmlspecialchars_decode($val['search_url']));
|
|
var_dump($res);
|
|
var_dump(date("Y-m-d H:i:s"));
|
|
}
|
|
|
|
/**
|
|
* 插入队列判断当前是否可执行
|
|
* @param $code_id
|
|
* @return
|
|
*/
|
|
public function checkQueue($code_id)
|
|
{
|
|
$message = Cache::store('redis')->lPop("queue:$code_id");
|
|
// 如果队列为空,则写入队列
|
|
if ($message === null || $message === false) {
|
|
// 消息内容
|
|
$message = 1;
|
|
// 监控客户的消息 Hash
|
|
Cache::store('redis')->watch("code:$code_id");
|
|
// 开始事务
|
|
Cache::store('redis')->multi();
|
|
// 将消息添加到队列末尾
|
|
Cache::store('redis')->rPush("queue:$code_id", $message);
|
|
// 提交事务
|
|
$result = Cache::store('redis')->exec();
|
|
if ($result !== null && $result !== false) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public function checkStatus($url,$passageway,$text='该订单已完成支付'){
|
|
if(in_array($passageway,['WX','SNZFBH5'])){
|
|
$ch = curl_init();
|
|
curl_setopt ($ch, CURLOPT_URL, $url);
|
|
curl_setopt ($ch, CURLOPT_RETURNTRANSFER, 1);
|
|
curl_setopt ($ch, CURLOPT_CONNECTTIMEOUT,10);
|
|
curl_setopt ($ch, CURLOPT_FOLLOWLOCATION, true);
|
|
curl_setopt ($ch, CURLOPT_MAXREDIRS, 3);
|
|
$content = curl_exec($ch);
|
|
}else {
|
|
$publicPath = app()->getRootPath() . 'public/';
|
|
$escaped_url = escapeshellarg($url);
|
|
$command = "node {$publicPath}myscript.js {$escaped_url} 2>&1";
|
|
$content = shell_exec($command);
|
|
}
|
|
//$res = strpos((string)$content,$text);
|
|
return (string)$content;
|
|
}
|
|
|
|
public function checkStatusProxy($targetUrl,$passageway,$ip,$port,$text='该订单已完成支付'){
|
|
if(in_array($passageway,['WX','SNZFBH5'])){
|
|
// 代理服务器
|
|
$proxyServer = "http://" . $ip . ":" . $port;
|
|
// 隧道身份信息
|
|
$ch = curl_init();
|
|
|
|
curl_setopt($ch, CURLOPT_URL, $targetUrl);
|
|
|
|
curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, false);
|
|
|
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
|
|
|
// 设置代理服务器
|
|
//curl_setopt($ch, CURLOPT_PROXYTYPE, 0); //http
|
|
|
|
curl_setopt($ch, CURLOPT_PROXYTYPE, 5); //sock5
|
|
|
|
curl_setopt($ch, CURLOPT_PROXY, $proxyServer);
|
|
|
|
// 设置隧道验证信息
|
|
curl_setopt($ch, CURLOPT_PROXYAUTH, CURLAUTH_BASIC);
|
|
|
|
curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727;)");
|
|
|
|
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 3);
|
|
|
|
curl_setopt($ch, CURLOPT_TIMEOUT, 5);
|
|
|
|
curl_setopt($ch, CURLOPT_HEADER, true);
|
|
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
|
|
$result = curl_exec($ch);
|
|
|
|
curl_close($ch);
|
|
}else{
|
|
$publicPath = app()->getRootPath() . 'public/';
|
|
$escaped_url = escapeshellarg($targetUrl);
|
|
//$command = "node {$publicPath}myscript_proxy.js {$escaped_url} {$ip} {$port} 2>&1";
|
|
$command = "node {$publicPath}myscript.js {$escaped_url} 2>&1";
|
|
$result = shell_exec($command);
|
|
}
|
|
$res = strpos((string)$result,$text);
|
|
return $res;
|
|
}
|
|
|
|
public function jysx(){
|
|
$res = $this->checkStatus('https://t-h5.einrkv.com/qAvYzu2Uraqq/ak7X8TC8Vq6TCkRvtlHzJZE2%252FeIBI0HjIVvQc0IIwXhcng%252FC2I3D0bicsYRpOpUqmtASUp6v7MiNCtPpPPV%2BKgeNvxF%2BQ%252FEXaPT886oB785uqOkFAlj6KDk8hEzwLQ4pjMuZ1xnPeU5fljvX84bwZPx9J%2Bb7nMmsvF2yTahh6FU%3D', 'zfb');
|
|
var_dump($res);
|
|
return;
|
|
$this->saveLog("码校验查询开始");
|
|
//开始
|
|
$model = new CodeModel();
|
|
//连接本地的 Redis 服务
|
|
$conn = new \Redis(); // 实例化
|
|
$conn->connect('127.0.0.1','6379');
|
|
$codepage = $conn->get('codepagejy');
|
|
$codepage = empty($codepage)?1:$codepage;
|
|
$array = $model->getSx($codepage);
|
|
|
|
$this->saveLog("进入码校验查询,页码:".$codepage.",码数:".count($array));
|
|
//获取IP组
|
|
$iPGroup = $this->getIPGroup();
|
|
foreach ($array as $k=>$val) {
|
|
$text = $val['passageway'] == 'ZFBH5'?'订单已支付':'该订单已完成支付';
|
|
if(isset($iPGroup[0]['ip'])) {
|
|
$res = $this->checkStatusProxy(htmlspecialchars_decode($val['search_url']), $iPGroup[0]['ip'], $iPGroup[0]['port'], $text);
|
|
}else {
|
|
$res = $this->checkStatus(htmlspecialchars_decode($val['search_url']), $text);
|
|
}
|
|
if ($res !== false) { //支付成功
|
|
$model->update([
|
|
'status' => 3,
|
|
],['code_id'=>$val['code_id']]);
|
|
}
|
|
}
|
|
if(count($array) < 20){
|
|
$conn->set('codepagejy',1);
|
|
}else{
|
|
$conn->set('codepagejy',$codepage+1);
|
|
}
|
|
$this->saveLog("码校验查询结束");
|
|
}
|
|
}
|