mirror of
https://github.com/v2board/v2board.git
synced 2025-06-16 22:57:47 +08:00
@ -45,6 +45,7 @@ class CheckOrder extends Command
|
||||
{
|
||||
ini_set('memory_limit', -1);
|
||||
$orders = Order::whereIn('status', [0, 1])
|
||||
->orderBy('created_at', 'ASC')
|
||||
->get();
|
||||
foreach ($orders as $order) {
|
||||
OrderHandleJob::dispatch($order->trade_no);
|
||||
|
@ -64,12 +64,12 @@ class ResetTraffic extends Command
|
||||
break;
|
||||
}
|
||||
case 0: {
|
||||
$builder = $this->builder->where('plan_id', $plan->id);
|
||||
$builder = with(clone($this->builder))->where('plan_id', $plan->id);
|
||||
$this->resetByMonthFirstDay($builder);
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
$builder = $this->builder->where('plan_id', $plan->id);
|
||||
$builder = with(clone($this->builder))->where('plan_id', $plan->id);
|
||||
$this->resetByExpireDay($builder);
|
||||
break;
|
||||
}
|
||||
|
@ -4,8 +4,10 @@ namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Order;
|
||||
use App\Models\User;
|
||||
use App\Utils\CacheKey;
|
||||
use App\Utils\Helper;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Matriphe\Larinfo;
|
||||
|
||||
class Test extends Command
|
||||
|
@ -48,7 +48,7 @@ class V2boardInstall extends Command
|
||||
$this->info(" \ V / / __/| |_) | (_) | (_| | | | (_| | ");
|
||||
$this->info(" \_/ |_____|____/ \___/ \__,_|_| \__,_| ");
|
||||
if (\File::exists(base_path() . '/.env')) {
|
||||
abort(500, 'V2board 已安装,如需重新安装请删除目录下.lock文件');
|
||||
abort(500, 'V2board 已安装,如需重新安装请删除目录下.env文件');
|
||||
}
|
||||
|
||||
if (!copy(base_path() . '/.env.example', base_path() . '/.env')) {
|
||||
@ -99,7 +99,6 @@ class V2boardInstall extends Command
|
||||
|
||||
$this->info('一切就绪');
|
||||
$this->info('访问 http(s)://你的站点/admin 进入管理面板');
|
||||
\File::put(base_path() . '/.lock', time());
|
||||
} catch (\Exception $e) {
|
||||
$this->error($e->getMessage());
|
||||
}
|
||||
|
@ -3,10 +3,12 @@
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Requests\Admin\ConfigSave;
|
||||
use App\Jobs\SendEmailJob;
|
||||
use App\Services\TelegramService;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Utils\Dict;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
|
||||
class ConfigController extends Controller
|
||||
{
|
||||
@ -32,6 +34,24 @@ class ConfigController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
public function testSendMail(Request $request)
|
||||
{
|
||||
$obj = new SendEmailJob([
|
||||
'email' => $request->session()->get('email'),
|
||||
'subject' => 'This is v2board test email',
|
||||
'template_name' => 'notify',
|
||||
'template_value' => [
|
||||
'name' => config('v2board.app_name', 'V2Board'),
|
||||
'content' => 'This is v2board test email',
|
||||
'url' => config('v2board.app_url')
|
||||
]
|
||||
]);
|
||||
return response([
|
||||
'data' => true,
|
||||
'log' => $obj->handle()
|
||||
]);
|
||||
}
|
||||
|
||||
public function setTelegramWebhook(Request $request)
|
||||
{
|
||||
$telegramService = new TelegramService($request->input('telegram_bot_token'));
|
||||
@ -82,7 +102,9 @@ class ConfigController extends Controller
|
||||
'recaptcha_enable' => (int)config('v2board.recaptcha_enable', 0),
|
||||
'recaptcha_key' => config('v2board.recaptcha_key'),
|
||||
'recaptcha_site_key' => config('v2board.recaptcha_site_key'),
|
||||
'tos_url' => config('v2board.tos_url')
|
||||
'tos_url' => config('v2board.tos_url'),
|
||||
'currency' => config('v2board.currency', 'CNY'),
|
||||
'currency_symbol' => config('v2board.currency_symbol', '¥')
|
||||
],
|
||||
'subscribe' => [
|
||||
'plan_change_enable' => (int)config('v2board.plan_change_enable', 1),
|
||||
@ -151,7 +173,8 @@ class ConfigController extends Controller
|
||||
],
|
||||
'telegram' => [
|
||||
'telegram_bot_enable' => config('v2board.telegram_bot_enable', 0),
|
||||
'telegram_bot_token' => config('v2board.telegram_bot_token')
|
||||
'telegram_bot_token' => config('v2board.telegram_bot_token'),
|
||||
'telegram_discuss_link' => config('v2board.telegram_discuss_link')
|
||||
],
|
||||
'app' => [
|
||||
'windows_version' => config('v2board.windows_version'),
|
||||
|
@ -19,7 +19,7 @@ class CouponController extends Controller
|
||||
$current = $request->input('current') ? $request->input('current') : 1;
|
||||
$pageSize = $request->input('pageSize') >= 10 ? $request->input('pageSize') : 10;
|
||||
$sortType = in_array($request->input('sort_type'), ['ASC', 'DESC']) ? $request->input('sort_type') : 'DESC';
|
||||
$sort = $request->input('sort') ? $request->input('sort') : 'created_at';
|
||||
$sort = $request->input('sort') ? $request->input('sort') : 'id';
|
||||
$builder = Coupon::orderBy($sort, $sortType);
|
||||
$total = $builder->count();
|
||||
$coupons = $builder->forPage($current, $pageSize)
|
||||
@ -64,6 +64,7 @@ class CouponController extends Controller
|
||||
$coupon = $request->validated();
|
||||
$coupon['created_at'] = $coupon['updated_at'] = time();
|
||||
$coupon['limit_plan_ids'] = json_encode($coupon['limit_plan_ids']);
|
||||
$coupon['limit_period'] = json_encode($coupon['limit_period']);
|
||||
unset($coupon['generate_count']);
|
||||
for ($i = 0;$i < $request->input('generate_count');$i++) {
|
||||
$coupon['code'] = Helper::randomChar(8);
|
||||
|
@ -146,11 +146,11 @@ class OrderController extends Controller
|
||||
$orderService = new OrderService($order);
|
||||
$order->user_id = $user->id;
|
||||
$order->plan_id = $plan->id;
|
||||
$order->cycle = $request->input('cycle');
|
||||
$order->period = $request->input('period');
|
||||
$order->trade_no = Helper::guid();
|
||||
$order->total_amount = $request->input('total_amount');
|
||||
|
||||
if ($order->cycle === 'reset_price') {
|
||||
if ($order->period === 'reset_price') {
|
||||
$order->type = 4;
|
||||
} else if ($user->plan_id !== NULL && $order->plan_id !== $user->plan_id) {
|
||||
$order->type = 3;
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Requests\Admin\PaymentSave;
|
||||
use App\Services\PaymentService;
|
||||
use App\Utils\Helper;
|
||||
use Illuminate\Http\Request;
|
||||
@ -57,8 +58,18 @@ class PaymentController extends Controller
|
||||
'data' => true
|
||||
]);
|
||||
}
|
||||
$request->validate([
|
||||
'name' => 'required',
|
||||
'payment' => 'required',
|
||||
'config' => 'required'
|
||||
], [
|
||||
'name.required' => '显示名称不能为空',
|
||||
'payment.required' => '网关参数不能为空',
|
||||
'config.required' => '配置参数不能为空'
|
||||
]);
|
||||
if (!Payment::create([
|
||||
'name' => $request->input('name'),
|
||||
'icon' => $request->input('icon'),
|
||||
'payment' => $request->input('payment'),
|
||||
'config' => $request->input('config'),
|
||||
'uuid' => Helper::randomChar(8)
|
||||
|
@ -3,9 +3,12 @@
|
||||
namespace App\Http\Controllers\Admin\Server;
|
||||
|
||||
use App\Models\Plan;
|
||||
use App\Models\ServerShadowsocks;
|
||||
use App\Models\ServerTrojan;
|
||||
use App\Models\ServerV2ray;
|
||||
use App\Models\ServerGroup;
|
||||
use App\Models\User;
|
||||
use App\Services\ServerService;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
@ -18,8 +21,20 @@ class GroupController extends Controller
|
||||
'data' => [ServerGroup::find($request->input('group_id'))]
|
||||
]);
|
||||
}
|
||||
$serverGroups = ServerGroup::get();
|
||||
$serverService = new ServerService();
|
||||
$servers = $serverService->getAllServers();
|
||||
foreach ($serverGroups as $k => $v) {
|
||||
$serverGroups[$k]['user_count'] = User::where('group_id', $v['id'])->count();
|
||||
$serverGroups[$k]['server_count'] = 0;
|
||||
foreach ($servers as $server) {
|
||||
if (in_array($v['id'], $server['group_id'])) {
|
||||
$serverGroups[$k]['server_count'] = $serverGroups[$k]['server_count']+1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return response([
|
||||
'data' => ServerGroup::get()
|
||||
'data' => $serverGroups
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -22,6 +22,7 @@ class ManageController extends Controller
|
||||
|
||||
public function sort(Request $request)
|
||||
{
|
||||
ini_set('post_max_size', '1m');
|
||||
DB::beginTransaction();
|
||||
foreach ($request->input('sorts') as $k => $v) {
|
||||
switch ($v['key']) {
|
||||
|
@ -30,20 +30,22 @@ class UserController extends Controller
|
||||
|
||||
private function filter(Request $request, $builder)
|
||||
{
|
||||
if ($request->input('filter')) {
|
||||
foreach ($request->input('filter') as $filter) {
|
||||
if ($filter['key'] === 'invite_by_email') {
|
||||
$user = User::where('email', $filter['value'])->first();
|
||||
if (!$user) continue;
|
||||
$builder->where('invite_user_id', $user->id);
|
||||
continue;
|
||||
$filters = $request->input('filter');
|
||||
if ($filters) {
|
||||
foreach ($filters as $k => $filter) {
|
||||
if ($filter['condition'] === '模糊') {
|
||||
$filter['condition'] = 'like';
|
||||
$filter['value'] = "%{$filter['value']}%";
|
||||
}
|
||||
if ($filter['key'] === 'd' || $filter['key'] === 'transfer_enable') {
|
||||
$filter['value'] = $filter['value'] * 1073741824;
|
||||
}
|
||||
if ($filter['condition'] === '模糊') {
|
||||
$filter['condition'] = 'like';
|
||||
$filter['value'] = "%{$filter['value']}%";
|
||||
if ($filter['key'] === 'invite_by_email') {
|
||||
$user = User::where('email', $filter['condition'], $filter['value'])->first();
|
||||
$inviteUserId = isset($user->id) ? $user->id : 0;
|
||||
$builder->where('invite_user_id', $inviteUserId);
|
||||
unset($filters[$k]);
|
||||
continue;
|
||||
}
|
||||
$builder->where($filter['key'], $filter['condition'], $filter['value']);
|
||||
}
|
||||
@ -179,6 +181,9 @@ class UserController extends Controller
|
||||
'uuid' => Helper::guid(true),
|
||||
'token' => Helper::guid()
|
||||
];
|
||||
if (User::where('email', $user['email'])->first()) {
|
||||
abort(500, '邮箱已存在于系统中');
|
||||
}
|
||||
$user['password'] = password_hash($request->input('password') ?? $user['email'], PASSWORD_DEFAULT);
|
||||
if (!User::create($user)) {
|
||||
abort(500, '生成失败');
|
||||
@ -251,7 +256,8 @@ class UserController extends Controller
|
||||
'url' => config('v2board.app_url'),
|
||||
'content' => $request->input('content')
|
||||
]
|
||||
]);
|
||||
],
|
||||
'send_email_mass');
|
||||
}
|
||||
|
||||
return response([
|
||||
|
@ -52,7 +52,19 @@ class Clash
|
||||
$config['proxies'] = array_merge($config['proxies'] ? $config['proxies'] : [], $proxy);
|
||||
foreach ($config['proxy-groups'] as $k => $v) {
|
||||
if (!is_array($config['proxy-groups'][$k]['proxies'])) continue;
|
||||
$config['proxy-groups'][$k]['proxies'] = array_merge($config['proxy-groups'][$k]['proxies'], $proxies);
|
||||
$isFilter = false;
|
||||
foreach ($config['proxy-groups'][$k]['proxies'] as $src) {
|
||||
foreach ($proxies as $dst) {
|
||||
if ($this->isMatch($src, $dst)) {
|
||||
$isFilter = true;
|
||||
$config['proxy-groups'][$k]['proxies'] = array_diff($config['proxy-groups'][$k]['proxies'], [$src]);
|
||||
array_push($config['proxy-groups'][$k]['proxies'], $dst);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!$isFilter) {
|
||||
$config['proxy-groups'][$k]['proxies'] = array_merge($config['proxy-groups'][$k]['proxies'], $proxies);
|
||||
}
|
||||
}
|
||||
// Force the current subscription domain to be a direct rule
|
||||
$subsDomain = $_SERVER['SERVER_NAME'];
|
||||
@ -134,4 +146,13 @@ class Clash
|
||||
if (!empty($server['allow_insecure'])) $array['skip-cert-verify'] = ($server['allow_insecure'] ? true : false);
|
||||
return $array;
|
||||
}
|
||||
|
||||
private function isMatch($exp, $str)
|
||||
{
|
||||
try {
|
||||
return preg_match($exp, $str);
|
||||
} catch (\Exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
159
app/Http/Controllers/Client/Protocols/Stash.php
Normal file
159
app/Http/Controllers/Client/Protocols/Stash.php
Normal file
@ -0,0 +1,159 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Client\Protocols;
|
||||
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
class Stash
|
||||
{
|
||||
public $flag = 'stash';
|
||||
private $servers;
|
||||
private $user;
|
||||
|
||||
public function __construct($user, $servers)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->servers = $servers;
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$servers = $this->servers;
|
||||
$user = $this->user;
|
||||
$appName = config('v2board.app_name', 'V2Board');
|
||||
header("subscription-userinfo: upload={$user['u']}; download={$user['d']}; total={$user['transfer_enable']}; expire={$user['expired_at']}");
|
||||
header('profile-update-interval: 24');
|
||||
header("content-disposition: filename={$appName}");
|
||||
// 暂时使用clash配置文件,后续根据Stash更新情况更新
|
||||
$defaultConfig = base_path() . '/resources/rules/default.clash.yaml';
|
||||
$customConfig = base_path() . '/resources/rules/custom.clash.yaml';
|
||||
if (\File::exists($customConfig)) {
|
||||
$config = Yaml::parseFile($customConfig);
|
||||
} else {
|
||||
$config = Yaml::parseFile($defaultConfig);
|
||||
}
|
||||
$proxy = [];
|
||||
$proxies = [];
|
||||
|
||||
foreach ($servers as $item) {
|
||||
if ($item['type'] === 'shadowsocks') {
|
||||
array_push($proxy, self::buildShadowsocks($user['uuid'], $item));
|
||||
array_push($proxies, $item['name']);
|
||||
}
|
||||
if ($item['type'] === 'v2ray') {
|
||||
array_push($proxy, self::buildVmess($user['uuid'], $item));
|
||||
array_push($proxies, $item['name']);
|
||||
}
|
||||
if ($item['type'] === 'trojan') {
|
||||
array_push($proxy, self::buildTrojan($user['uuid'], $item));
|
||||
array_push($proxies, $item['name']);
|
||||
}
|
||||
}
|
||||
|
||||
$config['proxies'] = array_merge($config['proxies'] ? $config['proxies'] : [], $proxy);
|
||||
foreach ($config['proxy-groups'] as $k => $v) {
|
||||
if (!is_array($config['proxy-groups'][$k]['proxies'])) continue;
|
||||
$isFilter = false;
|
||||
foreach ($config['proxy-groups'][$k]['proxies'] as $src) {
|
||||
foreach ($proxies as $dst) {
|
||||
if ($this->isMatch($src, $dst)) {
|
||||
$isFilter = true;
|
||||
$config['proxy-groups'][$k]['proxies'] = array_diff($config['proxy-groups'][$k]['proxies'], [$src]);
|
||||
array_push($config['proxy-groups'][$k]['proxies'], $dst);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!$isFilter) {
|
||||
$config['proxy-groups'][$k]['proxies'] = array_merge($config['proxy-groups'][$k]['proxies'], $proxies);
|
||||
}
|
||||
}
|
||||
// Force the current subscription domain to be a direct rule
|
||||
$subsDomain = $_SERVER['SERVER_NAME'];
|
||||
$subsDomainRule = "DOMAIN,{$subsDomain},DIRECT";
|
||||
array_unshift($config['rules'], $subsDomainRule);
|
||||
|
||||
$yaml = Yaml::dump($config);
|
||||
$yaml = str_replace('$app_name', config('v2board.app_name', 'V2Board'), $yaml);
|
||||
return $yaml;
|
||||
}
|
||||
|
||||
public static function buildShadowsocks($uuid, $server)
|
||||
{
|
||||
$array = [];
|
||||
$array['name'] = $server['name'];
|
||||
$array['type'] = 'ss';
|
||||
$array['server'] = $server['host'];
|
||||
$array['port'] = $server['port'];
|
||||
$array['cipher'] = $server['cipher'];
|
||||
$array['password'] = $uuid;
|
||||
$array['udp'] = true;
|
||||
return $array;
|
||||
}
|
||||
|
||||
public static function buildVmess($uuid, $server)
|
||||
{
|
||||
$array = [];
|
||||
$array['name'] = $server['name'];
|
||||
$array['type'] = 'vmess';
|
||||
$array['server'] = $server['host'];
|
||||
$array['port'] = $server['port'];
|
||||
$array['uuid'] = $uuid;
|
||||
$array['alterId'] = $server['alter_id'];
|
||||
$array['cipher'] = 'auto';
|
||||
$array['udp'] = true;
|
||||
|
||||
if ($server['tls']) {
|
||||
$array['tls'] = true;
|
||||
if ($server['tlsSettings']) {
|
||||
$tlsSettings = $server['tlsSettings'];
|
||||
if (isset($tlsSettings['allowInsecure']) && !empty($tlsSettings['allowInsecure']))
|
||||
$array['skip-cert-verify'] = ($tlsSettings['allowInsecure'] ? true : false);
|
||||
if (isset($tlsSettings['serverName']) && !empty($tlsSettings['serverName']))
|
||||
$array['servername'] = $tlsSettings['serverName'];
|
||||
}
|
||||
}
|
||||
if ($server['network'] === 'ws') {
|
||||
$array['network'] = 'ws';
|
||||
if ($server['networkSettings']) {
|
||||
$wsSettings = $server['networkSettings'];
|
||||
if (isset($wsSettings['path']) && !empty($wsSettings['path']))
|
||||
$array['ws-path'] = $wsSettings['path'];
|
||||
if (isset($wsSettings['headers']['Host']) && !empty($wsSettings['headers']['Host']))
|
||||
$array['ws-headers'] = ['Host' => $wsSettings['headers']['Host']];
|
||||
}
|
||||
}
|
||||
if ($server['network'] === 'grpc') {
|
||||
$array['network'] = 'grpc';
|
||||
if ($server['networkSettings']) {
|
||||
$grpcObject = $server['networkSettings'];
|
||||
$array['grpc-opts'] = [];
|
||||
$array['grpc-opts']['grpc-service-name'] = $grpcObject['serviceName'];
|
||||
}
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
public static function buildTrojan($password, $server)
|
||||
{
|
||||
$array = [];
|
||||
$array['name'] = $server['name'];
|
||||
$array['type'] = 'trojan';
|
||||
$array['server'] = $server['host'];
|
||||
$array['port'] = $server['port'];
|
||||
$array['password'] = $password;
|
||||
$array['udp'] = true;
|
||||
if (!empty($server['server_name'])) $array['sni'] = $server['server_name'];
|
||||
if (!empty($server['allow_insecure'])) $array['skip-cert-verify'] = ($server['allow_insecure'] ? true : false);
|
||||
return $array;
|
||||
}
|
||||
|
||||
private function isMatch($exp, $str)
|
||||
{
|
||||
try {
|
||||
return preg_match($exp, $str);
|
||||
} catch (\Exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -67,12 +67,11 @@ class Surfboard
|
||||
public static function buildShadowsocks($password, $server)
|
||||
{
|
||||
$config = [
|
||||
"{$server['name']}=custom",
|
||||
"{$server['name']}=ss",
|
||||
"{$server['host']}",
|
||||
"{$server['port']}",
|
||||
"{$server['cipher']}",
|
||||
"{$password}",
|
||||
'https://raw.githubusercontent.com/Hackl0us/proxy-tool-backup/master/SSEncrypt.module',
|
||||
"encrypt-method={$server['cipher']}",
|
||||
"password={$password}",
|
||||
'tfo=true',
|
||||
'udp-relay=true'
|
||||
];
|
||||
|
@ -14,9 +14,12 @@ class CommController extends Controller
|
||||
return response([
|
||||
'data' => [
|
||||
'is_telegram' => (int)config('v2board.telegram_bot_enable', 0),
|
||||
'telegram_discuss_link' => config('v2board.telegram_discuss_link'),
|
||||
'stripe_pk' => config('v2board.stripe_pk_live'),
|
||||
'withdraw_methods' => config('v2board.commission_withdraw_method', Dict::WITHDRAW_METHOD_WHITELIST_DEFAULT),
|
||||
'withdraw_close' => (int)config('v2board.withdraw_close_enable', 0)
|
||||
'withdraw_close' => (int)config('v2board.withdraw_close_enable', 0),
|
||||
'currency' => config('v2board.currency', 'CNY'),
|
||||
'currency_symbol' => config('v2board.currency_symbol', '¥')
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ class InviteController extends Controller
|
||||
return response([
|
||||
'data' => Order::where('invite_user_id', $request->session()->get('id'))
|
||||
->where('commission_balance', '>', 0)
|
||||
->where('status', 3)
|
||||
->whereIn('status', [3, 4])
|
||||
->select([
|
||||
'id',
|
||||
'commission_status',
|
||||
|
@ -78,26 +78,27 @@ class OrderController extends Controller
|
||||
abort(500, __('Subscription plan does not exist'));
|
||||
}
|
||||
|
||||
if ((!$plan->show && !$plan->renew) || (!$plan->show && $user->plan_id !== $plan->id)) {
|
||||
if ($request->input('cycle') !== 'reset_price') {
|
||||
abort(500, __('This subscription has been sold out, please choose another subscription'));
|
||||
}
|
||||
if ($plan[$request->input('period')] === NULL) {
|
||||
abort(500, __('This payment period cannot be purchased, please choose another period'));
|
||||
}
|
||||
|
||||
if (!$plan->renew && $user->plan_id == $plan->id && $request->input('cycle') !== 'reset_price') {
|
||||
abort(500, __('This subscription cannot be renewed, please change to another subscription'));
|
||||
}
|
||||
|
||||
if ($plan[$request->input('cycle')] === NULL) {
|
||||
abort(500, __('This payment cycle cannot be purchased, please choose another cycle'));
|
||||
}
|
||||
|
||||
if ($request->input('cycle') === 'reset_price') {
|
||||
if ($request->input('period') === 'reset_price') {
|
||||
if ($user->expired_at <= time() || !$user->plan_id) {
|
||||
abort(500, __('Subscription has expired or no active subscription, unable to purchase Data Reset Package'));
|
||||
}
|
||||
}
|
||||
|
||||
if ((!$plan->show && !$plan->renew) || (!$plan->show && $user->plan_id !== $plan->id)) {
|
||||
if ($request->input('period') !== 'reset_price') {
|
||||
abort(500, __('This subscription has been sold out, please choose another subscription'));
|
||||
}
|
||||
}
|
||||
|
||||
if (!$plan->renew && $user->plan_id == $plan->id && $request->input('period') !== 'reset_price') {
|
||||
abort(500, __('This subscription cannot be renewed, please change to another subscription'));
|
||||
}
|
||||
|
||||
|
||||
if (!$plan->show && $plan->renew && !$userService->isAvailable($user)) {
|
||||
abort(500, __('This subscription has expired, please change to another subscription'));
|
||||
}
|
||||
@ -107,9 +108,9 @@ class OrderController extends Controller
|
||||
$orderService = new OrderService($order);
|
||||
$order->user_id = $request->session()->get('id');
|
||||
$order->plan_id = $plan->id;
|
||||
$order->cycle = $request->input('cycle');
|
||||
$order->period = $request->input('period');
|
||||
$order->trade_no = Helper::generateOrderNo();
|
||||
$order->total_amount = $plan[$request->input('cycle')];
|
||||
$order->total_amount = $plan[$request->input('period')];
|
||||
|
||||
if ($request->input('coupon_code')) {
|
||||
$couponService = new CouponService($request->input('coupon_code'));
|
||||
@ -211,7 +212,8 @@ class OrderController extends Controller
|
||||
$methods = Payment::select([
|
||||
'id',
|
||||
'name',
|
||||
'payment'
|
||||
'payment',
|
||||
'icon'
|
||||
])
|
||||
->where('enable', 1)->get();
|
||||
|
||||
|
@ -70,7 +70,8 @@ class UserController extends Controller
|
||||
'plan_id',
|
||||
'discount',
|
||||
'commission_rate',
|
||||
'telegram_id'
|
||||
'telegram_id',
|
||||
'uuid'
|
||||
])
|
||||
->first();
|
||||
if (!$user) {
|
||||
@ -103,7 +104,6 @@ class UserController extends Controller
|
||||
{
|
||||
$user = User::where('id', $request->session()->get('id'))
|
||||
->select([
|
||||
'id',
|
||||
'plan_id',
|
||||
'token',
|
||||
'expired_at',
|
||||
@ -189,6 +189,8 @@ class UserController extends Controller
|
||||
private function getResetDay(User $user)
|
||||
{
|
||||
if ($user->expired_at <= time() || $user->expired_at === NULL) return null;
|
||||
// if reset method is not reset
|
||||
if (isset($user->plan->reset_traffic_method) && $user->plan->reset_traffic_method === 2) return null;
|
||||
$day = date('d', $user->expired_at);
|
||||
$today = date('d');
|
||||
$lastDay = date('d', strtotime('last day of +0 months'));
|
||||
|
@ -46,6 +46,8 @@ class ConfigSave extends FormRequest
|
||||
'recaptcha_key' => '',
|
||||
'recaptcha_site_key' => '',
|
||||
'tos_url' => 'nullable|url',
|
||||
'currency' => '',
|
||||
'currency_symbol' => '',
|
||||
// subscribe
|
||||
'plan_change_enable' => 'in:0,1',
|
||||
'reset_traffic_method' => 'in:0,1,2',
|
||||
@ -110,6 +112,7 @@ class ConfigSave extends FormRequest
|
||||
'telegram_bot_token' => '',
|
||||
'telegram_discuss_id' => '',
|
||||
'telegram_channel_id' => '',
|
||||
'telegram_discuss_link' => 'nullable|url',
|
||||
// app
|
||||
'windows_version' => '',
|
||||
'windows_download_url' => '',
|
||||
@ -127,7 +130,8 @@ class ConfigSave extends FormRequest
|
||||
'app_url.url' => '站点URL格式不正确,必须携带http(s)://',
|
||||
'subscribe_url.url' => '订阅URL格式不正确,必须携带http(s)://',
|
||||
'server_token.min' => '通讯密钥长度必须大于16位',
|
||||
'tos_url.url' => '服务条款URL格式不正确'
|
||||
'tos_url.url' => '服务条款URL格式不正确,必须携带http(s)://',
|
||||
'telegram_discuss_link.url' => 'Telegram群组地址必须为URL格式,必须携带http(s)://'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ class CouponGenerate extends FormRequest
|
||||
'limit_use' => 'nullable|integer',
|
||||
'limit_use_with_user' => 'nullable|integer',
|
||||
'limit_plan_ids' => 'nullable|array',
|
||||
'limit_period' => 'nullable|array',
|
||||
'code' => ''
|
||||
];
|
||||
}
|
||||
@ -43,7 +44,8 @@ class CouponGenerate extends FormRequest
|
||||
'ended_at.integer' => '结束时间格式有误',
|
||||
'limit_use.integer' => '最大使用次数格式有误',
|
||||
'limit_use_with_user.integer' => '限制用户使用次数格式有误',
|
||||
'limit_plan_ids.array' => '指定订阅格式有误'
|
||||
'limit_plan_ids.array' => '指定订阅格式有误',
|
||||
'limit_period.array' => '指定周期格式有误'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ class OrderAssign extends FormRequest
|
||||
'plan_id' => 'required',
|
||||
'email' => 'required',
|
||||
'total_amount' => 'required',
|
||||
'cycle' => 'required|in:month_price,quarter_price,half_year_price,year_price,two_year_price,three_year_price,onetime_price,reset_price'
|
||||
'period' => 'required|in:month_price,quarter_price,half_year_price,year_price,two_year_price,three_year_price,onetime_price,reset_price'
|
||||
];
|
||||
}
|
||||
|
||||
@ -27,8 +27,8 @@ class OrderAssign extends FormRequest
|
||||
'plan_id.required' => '订阅不能为空',
|
||||
'email.required' => '邮箱不能为空',
|
||||
'total_amount.required' => '支付金额不能为空',
|
||||
'cycle.required' => '订阅周期不能为空',
|
||||
'cycle.in' => '订阅周期格式有误'
|
||||
'period.required' => '订阅周期不能为空',
|
||||
'period.in' => '订阅周期格式有误'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ class OrderFetch extends FormRequest
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'filter.*.key' => 'required|in:email,trade_no,status,commission_status,user_id,invite_user_id',
|
||||
'filter.*.key' => 'required|in:email,trade_no,status,commission_status,user_id,invite_user_id,callback_no',
|
||||
'filter.*.condition' => 'required|in:>,<,=,>=,<=,模糊,!=',
|
||||
'filter.*.value' => ''
|
||||
];
|
||||
|
@ -15,7 +15,7 @@ class OrderSave extends FormRequest
|
||||
{
|
||||
return [
|
||||
'plan_id' => 'required',
|
||||
'cycle' => 'required|in:month_price,quarter_price,half_year_price,year_price,two_year_price,three_year_price,onetime_price,reset_price'
|
||||
'period' => 'required|in:month_price,quarter_price,half_year_price,year_price,two_year_price,three_year_price,onetime_price,reset_price'
|
||||
];
|
||||
}
|
||||
|
||||
@ -23,8 +23,8 @@ class OrderSave extends FormRequest
|
||||
{
|
||||
return [
|
||||
'plan_id.required' => __('Plan ID cannot be empty'),
|
||||
'cycle.required' => __('Plan cycle cannot be empty'),
|
||||
'cycle.in' => __('Wrong plan cycle')
|
||||
'period.required' => __('Plan period cannot be empty'),
|
||||
'period.in' => __('Wrong plan period')
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ class AdminRoute
|
||||
$router->get ('/config/getEmailTemplate', 'Admin\\ConfigController@getEmailTemplate');
|
||||
$router->get ('/config/getThemeTemplate', 'Admin\\ConfigController@getThemeTemplate');
|
||||
$router->post('/config/setTelegramWebhook', 'Admin\\ConfigController@setTelegramWebhook');
|
||||
$router->post('/config/testSendMail', 'Admin\\ConfigController@testSendMail');
|
||||
// Plan
|
||||
$router->get ('/plan/fetch', 'Admin\\PlanController@fetch');
|
||||
$router->post('/plan/save', 'Admin\\PlanController@save');
|
||||
|
@ -21,10 +21,9 @@ class SendEmailJob implements ShouldQueue
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($params)
|
||||
public function __construct($params, $queue = 'send_email')
|
||||
{
|
||||
$this->delay(now()->addSecond(2));
|
||||
$this->onQueue('send_email');
|
||||
$this->onQueue($queue);
|
||||
$this->params = $params;
|
||||
}
|
||||
|
||||
@ -60,11 +59,15 @@ class SendEmailJob implements ShouldQueue
|
||||
$error = $e->getMessage();
|
||||
}
|
||||
|
||||
MailLog::create([
|
||||
$log = [
|
||||
'email' => $params['email'],
|
||||
'subject' => $params['subject'],
|
||||
'template_name' => $params['template_name'],
|
||||
'error' => isset($error) ? $error : NULL
|
||||
]);
|
||||
];
|
||||
|
||||
MailLog::create($log);
|
||||
$log['config'] = config('mail');
|
||||
return $log;
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ class Coupon extends Model
|
||||
protected $casts = [
|
||||
'created_at' => 'timestamp',
|
||||
'updated_at' => 'timestamp',
|
||||
'limit_plan_ids' => 'array'
|
||||
'limit_plan_ids' => 'array',
|
||||
'limit_period' => 'array'
|
||||
];
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ class CouponService
|
||||
public $coupon;
|
||||
public $planId;
|
||||
public $userId;
|
||||
public $period;
|
||||
|
||||
public function __construct($code)
|
||||
{
|
||||
@ -21,6 +22,7 @@ class CouponService
|
||||
{
|
||||
$this->setPlanId($order->plan_id);
|
||||
$this->setUserId($order->user_id);
|
||||
$this->setPeriod($order->period);
|
||||
$this->check();
|
||||
switch ($this->coupon->type) {
|
||||
case 1:
|
||||
@ -30,6 +32,9 @@ class CouponService
|
||||
$order->discount_amount = $order->total_amount * ($this->coupon->value / 100);
|
||||
break;
|
||||
}
|
||||
if ($order->discount_amount > $order->total_amount) {
|
||||
$order->discount_amount = $order->total_amount;
|
||||
}
|
||||
if ($this->coupon->limit_use !== NULL) {
|
||||
$this->coupon->limit_use = $this->coupon->limit_use - 1;
|
||||
if (!$this->coupon->save()) {
|
||||
@ -59,6 +64,11 @@ class CouponService
|
||||
$this->userId = $userId;
|
||||
}
|
||||
|
||||
public function setPeriod($period)
|
||||
{
|
||||
$this->period = $period;
|
||||
}
|
||||
|
||||
public function checkLimitUseWithUser():bool
|
||||
{
|
||||
$usedCount = Order::where('coupon_id', $this->coupon->id)
|
||||
@ -88,6 +98,11 @@ class CouponService
|
||||
abort(500, __('The coupon code cannot be used for this subscription'));
|
||||
}
|
||||
}
|
||||
if ($this->coupon->limit_period && $this->period) {
|
||||
if (!in_array($this->period, $this->coupon->limit_period)) {
|
||||
abort(500, __('The coupon code cannot be used for this period'));
|
||||
}
|
||||
}
|
||||
if ($this->coupon->limit_use_with_user !== NULL && $this->userId) {
|
||||
if (!$this->checkLimitUseWithUser()) {
|
||||
abort(500, __('The coupon can only be used :limit_use_with_user per person', [
|
||||
|
@ -46,7 +46,7 @@ class OrderService
|
||||
abort(500, '开通失败');
|
||||
}
|
||||
}
|
||||
switch ((string)$order->cycle) {
|
||||
switch ((string)$order->period) {
|
||||
case 'onetime_price':
|
||||
$this->buyByOneTime($plan);
|
||||
break;
|
||||
@ -54,7 +54,7 @@ class OrderService
|
||||
$this->buyByResetTraffic();
|
||||
break;
|
||||
default:
|
||||
$this->buyByCycle($order, $plan);
|
||||
$this->buyByPeriod($order, $plan);
|
||||
}
|
||||
|
||||
switch ((int)$order->type) {
|
||||
@ -86,7 +86,7 @@ class OrderService
|
||||
public function setOrderType(User $user)
|
||||
{
|
||||
$order = $this->order;
|
||||
if ($order->cycle === 'reset_price') {
|
||||
if ($order->period === 'reset_price') {
|
||||
$order->type = 4;
|
||||
} else if ($user->plan_id !== NULL && $order->plan_id !== $user->plan_id && ($user->expired_at > time() || $user->expired_at === NULL)) {
|
||||
if (!(int)config('v2board.plan_change_enable', 1)) abort(500, '目前不允许更改订阅,请联系客服或提交工单操作');
|
||||
@ -156,7 +156,7 @@ class OrderService
|
||||
if ($user->expired_at === NULL) {
|
||||
$this->getSurplusValueByOneTime($user, $order);
|
||||
} else {
|
||||
$this->getSurplusValueByCycle($user, $order);
|
||||
$this->getSurplusValueByPeriod($user, $order);
|
||||
}
|
||||
}
|
||||
|
||||
@ -170,23 +170,23 @@ class OrderService
|
||||
}
|
||||
$notUsedTraffic = $plan->transfer_enable - (($user->u + $user->d) / 1073741824);
|
||||
$result = $trafficUnitPrice * $notUsedTraffic;
|
||||
$orderModel = Order::where('user_id', $user->id)->where('cycle', '!=', 'reset_price')->where('status', 3);
|
||||
$orderModel = Order::where('user_id', $user->id)->where('period', '!=', 'reset_price')->where('status', 3);
|
||||
$order->surplus_amount = $result > 0 ? $result : 0;
|
||||
$order->surplus_order_ids = array_column($orderModel->get()->toArray(), 'id');
|
||||
}
|
||||
|
||||
private function orderIsUsed(Order $order):bool
|
||||
{
|
||||
$month = self::STR_TO_TIME[$order->cycle];
|
||||
$month = self::STR_TO_TIME[$order->period];
|
||||
$orderExpireDay = strtotime('+' . $month . ' month', $order->created_at);
|
||||
if ($orderExpireDay < time()) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
private function getSurplusValueByCycle(User $user, Order $order)
|
||||
private function getSurplusValueByPeriod(User $user, Order $order)
|
||||
{
|
||||
$orderModel = Order::where('user_id', $user->id)
|
||||
->where('cycle', '!=', 'reset_price')
|
||||
->where('period', '!=', 'reset_price')
|
||||
->where('status', 3);
|
||||
$orders = $orderModel->get();
|
||||
$orderSurplusMonth = 0;
|
||||
@ -194,9 +194,9 @@ class OrderService
|
||||
$userSurplusMonth = ($user->expired_at - time()) / 2678400;
|
||||
foreach ($orders as $k => $item) {
|
||||
// 兼容历史余留问题
|
||||
if ($item->cycle === 'onetime_price') continue;
|
||||
if ($item->period === 'onetime_price') continue;
|
||||
if ($this->orderIsUsed($item)) continue;
|
||||
$orderSurplusMonth = $orderSurplusMonth + self::STR_TO_TIME[$item->cycle];
|
||||
$orderSurplusMonth = $orderSurplusMonth + self::STR_TO_TIME[$item->period];
|
||||
$orderSurplusAmount = $orderSurplusAmount + ($item['total_amount'] + $item['balance_amount'] + $item['surplus_amount'] - $item['refund_amount']);
|
||||
}
|
||||
if (!$orderSurplusMonth || !$orderSurplusAmount) return;
|
||||
@ -252,7 +252,7 @@ class OrderService
|
||||
$this->user->d = 0;
|
||||
}
|
||||
|
||||
private function buyByCycle(Order $order, Plan $plan)
|
||||
private function buyByPeriod(Order $order, Plan $plan)
|
||||
{
|
||||
// change plan process
|
||||
if ((int)$order->type === 3) {
|
||||
@ -265,7 +265,7 @@ class OrderService
|
||||
if ($order->type === 1) $this->buyByResetTraffic();
|
||||
$this->user->plan_id = $plan->id;
|
||||
$this->user->group_id = $plan->group_id;
|
||||
$this->user->expired_at = $this->getTime($order->cycle, $this->user->expired_at);
|
||||
$this->user->expired_at = $this->getTime($order->period, $this->user->expired_at);
|
||||
}
|
||||
|
||||
private function buyByOneTime(Plan $plan)
|
||||
|
@ -4,6 +4,7 @@ namespace App\Services;
|
||||
use App\Jobs\SendTelegramJob;
|
||||
use App\Models\User;
|
||||
use \Curl\Curl;
|
||||
use Illuminate\Mail\Markdown;
|
||||
|
||||
class TelegramService {
|
||||
protected $api;
|
||||
@ -15,6 +16,9 @@ class TelegramService {
|
||||
|
||||
public function sendMessage(int $chatId, string $text, string $parseMode = '')
|
||||
{
|
||||
if ($parseMode === 'markdown') {
|
||||
$text = str_replace('_', '\_', $text);
|
||||
}
|
||||
$this->request('sendMessage', [
|
||||
'chat_id' => $chatId,
|
||||
'text' => $text,
|
||||
|
Reference in New Issue
Block a user