mirror of
https://github.com/v2board/v2board.git
synced 2025-08-02 21:38:49 +08:00
Compare commits
158 Commits
Author | SHA1 | Date | |
---|---|---|---|
1a9b8b09bb | |||
cb621a93ae | |||
fb449b490e | |||
e35ec76f81 | |||
5a60380765 | |||
f409d89c4a | |||
de045c79f5 | |||
6a336d4253 | |||
0ce3948c12 | |||
7338c4a294 | |||
9417623fcf | |||
4f29264de9 | |||
34d8f0d5f0 | |||
b585038916 | |||
5abb642277 | |||
01cf486137 | |||
cb811bda5a | |||
2306e38d0c | |||
2798c1df06 | |||
a727745b43 | |||
af901291a5 | |||
42d755c7d9 | |||
56a4ce5fe4 | |||
67ab0d1f22 | |||
230470c037 | |||
77aec7d553 | |||
88948eb8ee | |||
4d38ce3968 | |||
a88121626b | |||
90409b1107 | |||
04615688f7 | |||
f48de9f07d | |||
4722379e79 | |||
9aed2554ee | |||
7eb3d9fed7 | |||
567acdd03b | |||
a0d18d93d3 | |||
1c419283c0 | |||
d25f6bff65 | |||
0b97dd0995 | |||
6f90c6b878 | |||
5b8591fde9 | |||
dfeec044ea | |||
70b47ec4b0 | |||
cd85fba9c7 | |||
e01e951f7f | |||
0284e47155 | |||
a4e1ba4016 | |||
f95deb3f16 | |||
dfef6d2d94 | |||
efc8419eb5 | |||
aecfa85efd | |||
9db5de09f2 | |||
b174403a2a | |||
0f488540f4 | |||
d00ad94c46 | |||
5fa7c534ff | |||
386c1339f5 | |||
6509091e4f | |||
078dfbf339 | |||
de6ff1dca9 | |||
044d1e9b7f | |||
9c711b4ea6 | |||
760d248e4e | |||
1a2e8e2966 | |||
b00b58d73d | |||
43027660be | |||
fe69a5976b | |||
ecebba1d51 | |||
efc43dc45e | |||
c5d88bfbfc | |||
6928fd3fef | |||
e4f178e167 | |||
343c93ad8e | |||
f80af8efdc | |||
e5b998ee8d | |||
d510064732 | |||
c5e2ec1d12 | |||
ee2ca23487 | |||
a5532490ba | |||
2f175323a3 | |||
f896370e64 | |||
0313c35dbe | |||
232cb18a25 | |||
04a05a6ba4 | |||
e2b4df592d | |||
668663f978 | |||
2e7544c71c | |||
1fb3f62cff | |||
02a1728bff | |||
4ea71d85be | |||
afe8bb3171 | |||
ef17be2046 | |||
53dea06a6c | |||
229a3022ac | |||
554dc4c12b | |||
6175e59e6f | |||
9df6e52438 | |||
63acb4c581 | |||
f54c82dcf9 | |||
b92b38a635 | |||
ddab443d75 | |||
8f806e6de7 | |||
f1c62e2732 | |||
3275b96a0a | |||
240555104a | |||
30ff71bd11 | |||
b17bbad745 | |||
a301b8461e | |||
3e354bf5af | |||
b9796f462c | |||
dc6317db97 | |||
b3b0988730 | |||
4070761cd0 | |||
2c408a2f56 | |||
2cfaeb2fb9 | |||
ef1c0b6091 | |||
8e1a313709 | |||
d5cf1bc735 | |||
e1c63bd556 | |||
324e218767 | |||
04b5d16457 | |||
2431ffaba7 | |||
6b31c39e6e | |||
2769a6adf4 | |||
aa78761115 | |||
3e60cbf968 | |||
84583bd384 | |||
0333d62e6f | |||
3bfa2180e6 | |||
22ee741200 | |||
58b27cdd50 | |||
743311f2f7 | |||
9c04f75b85 | |||
c7d7dfda28 | |||
35362689c4 | |||
b740998760 | |||
488eafdd67 | |||
ff30d3dcb2 | |||
cbe5882e01 | |||
4e2e3cd2a0 | |||
326fb5d918 | |||
a49faf3e1f | |||
e4b76a705f | |||
a03125ee6a | |||
58a63ae819 | |||
ab8ebd593f | |||
11b6c8448a | |||
9ce9af4917 | |||
550f0fee37 | |||
ab9c6c85bb | |||
0e6f6358d8 | |||
b0ddf7d45f | |||
f8a851d464 | |||
da8bff5609 | |||
d3150cadac | |||
cdddbae19a | |||
7c40a146a9 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -18,3 +18,4 @@ composer.phar
|
||||
composer.lock
|
||||
yarn.lock
|
||||
docker-compose.yml
|
||||
.DS_Store
|
||||
|
16
.styleci.yml
16
.styleci.yml
@ -1,16 +0,0 @@
|
||||
php:
|
||||
preset: laravel
|
||||
enabled:
|
||||
- alpha_ordered_imports
|
||||
disabled:
|
||||
- length_ordered_imports
|
||||
- unused_use
|
||||
finder:
|
||||
not-name:
|
||||
- index.php
|
||||
- server.php
|
||||
js:
|
||||
finder:
|
||||
not-name:
|
||||
- webpack.mix.js
|
||||
css: true
|
@ -49,7 +49,7 @@ class CheckCommission extends Command
|
||||
if ((int)config('v2board.commission_auto_check_enable', 1)) {
|
||||
Order::where('commission_status', 0)
|
||||
->where('invite_user_id', '!=', NULL)
|
||||
->whereIn('status', [3, 4])
|
||||
->whereNotIn('status', [0, 2])
|
||||
->where('updated_at', '<=', strtotime('-3 day', time()))
|
||||
->update([
|
||||
'commission_status' => 1
|
||||
|
@ -42,7 +42,9 @@ class CheckOrder extends Command
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$orders = Order::get();
|
||||
ini_set('memory_limit', -1);
|
||||
$orders = Order::whereIn('status', [0, 1])
|
||||
->get();
|
||||
foreach ($orders as $item) {
|
||||
$orderService = new OrderService($item);
|
||||
switch ($item->status) {
|
||||
|
65
app/Console/Commands/CheckServer.php
Normal file
65
app/Console/Commands/CheckServer.php
Normal file
@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Services\ServerService;
|
||||
use App\Services\TelegramService;
|
||||
use App\Utils\CacheKey;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
class CheckServer extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'check:server';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = '节点检查任务';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->checkOffline();
|
||||
}
|
||||
|
||||
private function checkOffline()
|
||||
{
|
||||
$serverService = new ServerService();
|
||||
$servers = $serverService->getAllServers();
|
||||
foreach ($servers as $server) {
|
||||
if ($server['parent_id']) continue;
|
||||
if ($server['last_check_at'] && (time() - $server['last_check_at']) > 1800) {
|
||||
$telegramService = new TelegramService();
|
||||
$message = sprintf(
|
||||
"节点掉线通知\r\n----\r\n节点名称:%s\r\n节点地址:%s\r\n",
|
||||
$server['name'],
|
||||
$server['host']
|
||||
);
|
||||
$telegramService->sendMessageWithAdmin($message);
|
||||
Cache::forget(CacheKey::get(sprintf("SERVER_%s_LAST_CHECK_AT", strtoupper($server['type'])), $server->id));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@ namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class ResetTraffic extends Command
|
||||
{
|
||||
@ -41,6 +42,7 @@ class ResetTraffic extends Command
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
DB::beginTransaction();
|
||||
$resetTrafficMethod = config('v2board.reset_traffic_method', 0);
|
||||
switch ((int)$resetTrafficMethod) {
|
||||
// 1 a month
|
||||
@ -52,6 +54,7 @@ class ResetTraffic extends Command
|
||||
$this->resetByExpireDay();
|
||||
break;
|
||||
}
|
||||
DB::commit();
|
||||
}
|
||||
|
||||
private function resetByMonthFirstDay():void
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Order;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class Test extends Command
|
||||
|
@ -9,7 +9,7 @@ use App\Models\StatOrder;
|
||||
use App\Models\ServerLog;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class V2BoardStatistics extends Command
|
||||
class V2boardStatistics extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
@ -52,11 +52,10 @@ class V2BoardStatistics extends Command
|
||||
$startAt = strtotime('-1 day', $endAt);
|
||||
$builder = Order::where('created_at', '>=', $startAt)
|
||||
->where('created_at', '<', $endAt)
|
||||
->whereIn('status', [3, 4]);
|
||||
->whereNotIn('status', [0, 2]);
|
||||
$orderCount = $builder->count();
|
||||
$orderAmount = $builder->sum('total_amount');
|
||||
$builder = $builder->where('commission_balance', '!=', NULL)
|
||||
->whereIn('commission_status', [1, 2]);
|
||||
$builder = $builder->where('commission_balance', '!=', 0);
|
||||
$commissionCount = $builder->count();
|
||||
$commissionAmount = $builder->sum('commission_balance');
|
||||
$data = [
|
||||
|
@ -25,7 +25,7 @@ class Kernel extends ConsoleKernel
|
||||
protected function schedule(Schedule $schedule)
|
||||
{
|
||||
// v2board
|
||||
$schedule->command('v2board:statistics')->daily();
|
||||
$schedule->command('v2board:statistics')->dailyAt('0:10');
|
||||
// check
|
||||
$schedule->command('check:order')->everyMinute();
|
||||
$schedule->command('check:commission')->everyMinute();
|
||||
|
@ -21,6 +21,17 @@ class ConfigController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
public function getThemeTemplate()
|
||||
{
|
||||
$path = public_path('theme/');
|
||||
$files = array_map(function ($item) use ($path) {
|
||||
return str_replace($path, '', $item);
|
||||
}, glob($path . '*'));
|
||||
return response([
|
||||
'data' => $files
|
||||
]);
|
||||
}
|
||||
|
||||
public function setTelegramWebhook(Request $request)
|
||||
{
|
||||
$telegramService = new TelegramService($request->input('telegram_bot_token'));
|
||||
@ -66,13 +77,16 @@ class ConfigController extends Controller
|
||||
'email_gmail_limit_enable' => config('v2board.email_gmail_limit_enable', 0),
|
||||
'recaptcha_enable' => (int)config('v2board.recaptcha_enable', 0),
|
||||
'recaptcha_key' => config('v2board.recaptcha_key'),
|
||||
'recaptcha_site_key' => config('v2board.recaptcha_site_key')
|
||||
'recaptcha_site_key' => config('v2board.recaptcha_site_key'),
|
||||
'tos_url' => config('v2board.tos_url')
|
||||
],
|
||||
'subscribe' => [
|
||||
'plan_change_enable' => (int)config('v2board.plan_change_enable', 1),
|
||||
'reset_traffic_method' => (int)config('v2board.reset_traffic_method', 0),
|
||||
'renew_reset_traffic_enable' => (int)config('v2board.renew_reset_traffic_enable', 0),
|
||||
'surplus_enable' => (int)config('v2board.surplus_enable', 1)
|
||||
'surplus_enable' => (int)config('v2board.surplus_enable', 1),
|
||||
'new_order_event_id' => (int)config('v2board.new_order_event_id', 0),
|
||||
'renew_order_event_id' => (int)config('v2board.renew_order_event_id', 0),
|
||||
'change_order_event_id' => (int)config('v2board.change_order_event_id', 0),
|
||||
],
|
||||
'pay' => [
|
||||
// alipay
|
||||
@ -106,11 +120,14 @@ class ConfigController extends Controller
|
||||
'epay_key' => config('v2board.epay_key'),
|
||||
],
|
||||
'frontend' => [
|
||||
'frontend_theme' => config('v2board.frontend_theme', 'v2board'),
|
||||
'frontend_theme_sidebar' => config('v2board.frontend_theme_sidebar', 'light'),
|
||||
'frontend_theme_header' => config('v2board.frontend_theme_header', 'dark'),
|
||||
'frontend_theme_color' => config('v2board.frontend_theme_color', 'default'),
|
||||
'frontend_background_url' => config('v2board.frontend_background_url'),
|
||||
'frontend_admin_path' => config('v2board.frontend_admin_path', 'admin')
|
||||
'frontend_admin_path' => config('v2board.frontend_admin_path', 'admin'),
|
||||
'frontend_customer_service_method' => config('v2board.frontend_customer_service_method', 0),
|
||||
'frontend_customer_service_id' => config('v2board.frontend_customer_service_id'),
|
||||
],
|
||||
'server' => [
|
||||
'server_token' => config('v2board.server_token'),
|
||||
|
@ -77,11 +77,15 @@ class KnowledgeController extends Controller
|
||||
public function sort(KnowledgeSort $request)
|
||||
{
|
||||
DB::beginTransaction();
|
||||
foreach ($request->input('knowledge_ids') as $k => $v) {
|
||||
if (!Knowledge::find($v)->update(['sort' => $k + 1])) {
|
||||
DB::rollBack();
|
||||
abort(500, '保存失败');
|
||||
try {
|
||||
foreach ($request->input('knowledge_ids') as $k => $v) {
|
||||
$knowledge = Knowledge::find($v);
|
||||
$knowledge->timestamps = false;
|
||||
$knowledge->update(['sort' => $k + 1]);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
DB::rollBack();
|
||||
abort(500, '保存失败');
|
||||
}
|
||||
DB::commit();
|
||||
return response([
|
||||
|
@ -4,6 +4,7 @@ namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Requests\Admin\OrderAssign;
|
||||
use App\Http\Requests\Admin\OrderUpdate;
|
||||
use App\Http\Requests\Admin\OrderFetch;
|
||||
use App\Services\OrderService;
|
||||
use App\Utils\Helper;
|
||||
use Illuminate\Http\Request;
|
||||
@ -15,25 +16,36 @@ use Illuminate\Support\Facades\DB;
|
||||
|
||||
class OrderController extends Controller
|
||||
{
|
||||
public function fetch(Request $request)
|
||||
private function filter(Request $request, &$builder)
|
||||
{
|
||||
if ($request->input('filter')) {
|
||||
foreach ($request->input('filter') as $filter) {
|
||||
if ($filter['key'] === 'email') {
|
||||
$user = User::where('email', "%{$filter['value']}%")->first();
|
||||
if (!$user) continue;
|
||||
$builder->where('user_id', $user->id);
|
||||
continue;
|
||||
}
|
||||
if ($filter['condition'] === '模糊') {
|
||||
$filter['condition'] = 'like';
|
||||
$filter['value'] = "%{$filter['value']}%";
|
||||
}
|
||||
$builder->where($filter['key'], $filter['condition'], $filter['value']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function fetch(OrderFetch $request)
|
||||
{
|
||||
$current = $request->input('current') ? $request->input('current') : 1;
|
||||
$pageSize = $request->input('pageSize') >= 10 ? $request->input('pageSize') : 10;
|
||||
$orderModel = Order::orderBy('created_at', 'DESC');
|
||||
if ($request->input('trade_no')) {
|
||||
$orderModel->where('trade_no', $request->input('trade_no'));
|
||||
}
|
||||
if ($request->input('is_commission')) {
|
||||
$orderModel->where('invite_user_id', '!=', NULL);
|
||||
$orderModel->whereIn('status', [3, 4]);
|
||||
$orderModel->whereNotIn('status', [0, 2]);
|
||||
$orderModel->where('commission_balance', '>', 0);
|
||||
}
|
||||
if ($request->input('id')) {
|
||||
$orderModel->where('id', $request->input('id'));
|
||||
}
|
||||
if ($request->input('user_id')) {
|
||||
$orderModel->where('user_id', $request->input('user_id'));
|
||||
}
|
||||
$this->filter($request, $orderModel);
|
||||
$total = $orderModel->count();
|
||||
$res = $orderModel->forPage($current, $pageSize)
|
||||
->get();
|
||||
|
78
app/Http/Controllers/Admin/PaymentController.php
Normal file
78
app/Http/Controllers/Admin/PaymentController.php
Normal file
@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Services\PaymentService;
|
||||
use App\Utils\Helper;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Payment;
|
||||
|
||||
class PaymentController extends Controller
|
||||
{
|
||||
public function getPaymentMethods()
|
||||
{
|
||||
$methods = [];
|
||||
foreach (glob(base_path('app//Payments') . '/*.php') as $file) {
|
||||
array_push($methods, pathinfo($file)['filename']);
|
||||
}
|
||||
return response([
|
||||
'data' => $methods
|
||||
]);
|
||||
}
|
||||
|
||||
public function fetch()
|
||||
{
|
||||
$payments = Payment::all();
|
||||
foreach ($payments as $k => $v) {
|
||||
$payments[$k]['notify_url'] = url("/api/v1/guest/payment/notify/{$v->payment}/{$v->uuid}");
|
||||
}
|
||||
return response([
|
||||
'data' => $payments
|
||||
]);
|
||||
}
|
||||
|
||||
public function getPaymentForm(Request $request)
|
||||
{
|
||||
$paymentService = new PaymentService($request->input('payment'), $request->input('id'));
|
||||
return response([
|
||||
'data' => $paymentService->form()
|
||||
]);
|
||||
}
|
||||
|
||||
public function save(Request $request)
|
||||
{
|
||||
if ($request->input('id')) {
|
||||
$payment = Payment::find($request->input('id'));
|
||||
if (!$payment) abort(500, '支付方式不存在');
|
||||
try {
|
||||
$payment->update($request->input());
|
||||
} catch (\Exception $e) {
|
||||
abort(500, '更新失败');
|
||||
}
|
||||
return response([
|
||||
'data' => true
|
||||
]);
|
||||
}
|
||||
if (!Payment::create([
|
||||
'name' => $request->input('name'),
|
||||
'payment' => $request->input('payment'),
|
||||
'config' => $request->input('config'),
|
||||
'uuid' => Helper::guid()
|
||||
])) {
|
||||
abort(500, '保存失败');
|
||||
}
|
||||
return response([
|
||||
'data' => true
|
||||
]);
|
||||
}
|
||||
|
||||
public function drop(Request $request)
|
||||
{
|
||||
$payment = Payment::find($request->input('id'));
|
||||
if (!$payment) abort(500, '支付方式不存在');
|
||||
return response([
|
||||
'data' => $payment->delete()
|
||||
]);
|
||||
}
|
||||
}
|
@ -52,8 +52,8 @@ class PlanController extends Controller
|
||||
// update user group id and transfer
|
||||
try {
|
||||
User::where('plan_id', $plan->id)->update([
|
||||
'group_id' => $plan->group_id,
|
||||
'transfer_enable' => $plan->transfer_enable * 1073741824
|
||||
'group_id' => $params['group_id'],
|
||||
'transfer_enable' => $params['transfer_enable'] * 1073741824
|
||||
]);
|
||||
$plan->update($params);
|
||||
} catch (\Exception $e) {
|
||||
|
@ -2,13 +2,9 @@
|
||||
|
||||
namespace App\Http\Controllers\Admin\Server;
|
||||
|
||||
use App\Http\Requests\Admin\ServerTrojanSort;
|
||||
use App\Models\Plan;
|
||||
use App\Models\Server;
|
||||
use App\Models\ServerGroup;
|
||||
use App\Models\ServerShadowsocks;
|
||||
use App\Models\ServerTrojan;
|
||||
use App\Models\User;
|
||||
use App\Services\ServerService;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
@ -19,15 +15,8 @@ class ManageController extends Controller
|
||||
public function getNodes(Request $request)
|
||||
{
|
||||
$serverService = new ServerService();
|
||||
$servers = array_merge(
|
||||
$serverService->getShadowsocksServers(),
|
||||
$serverService->getV2rayServers(),
|
||||
$serverService->getTrojanServers()
|
||||
);
|
||||
$tmp = array_column($servers, 'sort');
|
||||
array_multisort($tmp, SORT_ASC, $servers);
|
||||
return response([
|
||||
'data' => $servers
|
||||
'data' => $serverService->getAllServers()
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -3,15 +3,11 @@
|
||||
namespace App\Http\Controllers\Admin\Server;
|
||||
|
||||
use App\Http\Requests\Admin\ServerShadowsocksSave;
|
||||
use App\Http\Requests\Admin\ServerShadowsocksSort;
|
||||
use App\Http\Requests\Admin\ServerShadowsocksUpdate;
|
||||
use App\Models\ServerShadowsocks;
|
||||
use App\Utils\CacheKey;
|
||||
use App\Services\ServerService;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Server;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class ShadowsocksController extends Controller
|
||||
{
|
||||
|
@ -3,15 +3,11 @@
|
||||
namespace App\Http\Controllers\Admin\Server;
|
||||
|
||||
use App\Http\Requests\Admin\ServerTrojanSave;
|
||||
use App\Http\Requests\Admin\ServerTrojanSort;
|
||||
use App\Http\Requests\Admin\ServerTrojanUpdate;
|
||||
use App\Services\ServerService;
|
||||
use App\Utils\CacheKey;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\ServerTrojan;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class TrojanController extends Controller
|
||||
{
|
||||
|
@ -3,15 +3,11 @@
|
||||
namespace App\Http\Controllers\Admin\Server;
|
||||
|
||||
use App\Http\Requests\Admin\ServerV2raySave;
|
||||
use App\Http\Requests\Admin\ServerV2raySort;
|
||||
use App\Http\Requests\Admin\ServerV2rayUpdate;
|
||||
use App\Services\ServerService;
|
||||
use App\Utils\CacheKey;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Server;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class V2rayController extends Controller
|
||||
{
|
||||
|
@ -26,7 +26,7 @@ class StatController extends Controller
|
||||
'data' => [
|
||||
'month_income' => Order::where('created_at', '>=', strtotime(date('Y-m-1')))
|
||||
->where('created_at', '<', time())
|
||||
->whereIn('status', [3, 4])
|
||||
->whereNotIn('status', [0, 2])
|
||||
->sum('total_amount'),
|
||||
'month_register_total' => User::where('created_at', '>=', strtotime(date('Y-m-1')))
|
||||
->where('created_at', '<', time())
|
||||
@ -35,16 +35,16 @@ class StatController extends Controller
|
||||
->count(),
|
||||
'commission_pendding_total' => Order::where('commission_status', 0)
|
||||
->where('invite_user_id', '!=', NULL)
|
||||
->where('status', [3, 4])
|
||||
->whereNotIn('status', [0, 2])
|
||||
->where('commission_balance', '>', 0)
|
||||
->count(),
|
||||
'day_income' => Order::where('created_at', '>=', strtotime(date('Y-m-d')))
|
||||
->where('created_at', '<', time())
|
||||
->whereIn('status', [3, 4])
|
||||
->whereNotIn('status', [0, 2])
|
||||
->sum('total_amount'),
|
||||
'last_month_income' => Order::where('created_at', '>=', strtotime('-1 month', strtotime(date('Y-m-1'))))
|
||||
->where('created_at', '<', strtotime(date('Y-m-1')))
|
||||
->whereIn('status', [3, 4])
|
||||
->whereNotIn('status', [0, 2])
|
||||
->sum('total_amount')
|
||||
]
|
||||
]);
|
||||
@ -99,12 +99,13 @@ class StatController extends Controller
|
||||
'server_id',
|
||||
'server_type',
|
||||
'u',
|
||||
'd'
|
||||
'd',
|
||||
DB::raw('(u+d) as total')
|
||||
])
|
||||
->where('record_at', '>=', $timestamp)
|
||||
->where('record_type', 'd')
|
||||
->limit(10)
|
||||
->orderBy('record_at', 'DESC')
|
||||
->orderBy('total', 'DESC')
|
||||
->get()
|
||||
->toArray();
|
||||
foreach ($statistics as $k => $v) {
|
||||
@ -113,7 +114,7 @@ class StatController extends Controller
|
||||
$statistics[$k]['server_name'] = $server['name'];
|
||||
}
|
||||
}
|
||||
$statistics[$k]['total'] = ($v['u'] + $v['d']) / 1073741824;
|
||||
$statistics[$k]['total'] = $statistics[$k]['total'] / 1073741824;
|
||||
}
|
||||
array_multisort(array_column($statistics, 'total'), SORT_DESC, $statistics);
|
||||
return response([
|
||||
|
@ -7,10 +7,10 @@ use App\Http\Requests\Admin\UserGenerate;
|
||||
use App\Http\Requests\Admin\UserSendMail;
|
||||
use App\Http\Requests\Admin\UserUpdate;
|
||||
use App\Jobs\SendEmailJob;
|
||||
use App\Services\UserService;
|
||||
use App\Utils\Helper;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Order;
|
||||
use App\Models\User;
|
||||
use App\Models\Plan;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
@ -81,8 +81,12 @@ class UserController extends Controller
|
||||
if (empty($request->input('id'))) {
|
||||
abort(500, '参数错误');
|
||||
}
|
||||
$user = User::find($request->input('id'));
|
||||
if ($user->invite_user_id) {
|
||||
$user['invite_user'] = User::find($user->invite_user_id);
|
||||
}
|
||||
return response([
|
||||
'data' => User::find($request->input('id'))
|
||||
'data' => $user
|
||||
]);
|
||||
}
|
||||
|
||||
@ -109,6 +113,14 @@ class UserController extends Controller
|
||||
}
|
||||
$params['group_id'] = $plan->group_id;
|
||||
}
|
||||
if ($request->input('invite_user_email')) {
|
||||
$inviteUser = User::where('email', $request->input('invite_user_email'))->first();
|
||||
if ($inviteUser) {
|
||||
$params['invite_user_id'] = $inviteUser->id;
|
||||
}
|
||||
} else {
|
||||
$params['invite_user_id'] = null;
|
||||
}
|
||||
|
||||
try {
|
||||
$user->update($params);
|
||||
|
@ -8,6 +8,7 @@ use App\Services\UserService;
|
||||
use App\Utils\Clash;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\Server;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
class AppController extends Controller
|
||||
@ -25,21 +26,27 @@ class AppController extends Controller
|
||||
$serverService = new ServerService();
|
||||
$servers = $serverService->getAvailableServers($user);
|
||||
}
|
||||
$config = Yaml::parseFile(base_path() . '/resources/rules/app.clash.yaml');
|
||||
$defaultConfig = base_path() . '/resources/rules/app.clash.yaml';
|
||||
$customConfig = base_path() . '/resources/rules/custom.app.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, Clash::buildShadowsocks($user['uuid'], $item));
|
||||
array_push($proxy, Protocols\Clash::buildShadowsocks($user['uuid'], $item));
|
||||
array_push($proxies, $item['name']);
|
||||
}
|
||||
if ($item['type'] === 'v2ray') {
|
||||
array_push($proxy, Clash::buildVmess($user['uuid'], $item));
|
||||
array_push($proxy, Protocols\Clash::buildVmess($user['uuid'], $item));
|
||||
array_push($proxies, $item['name']);
|
||||
}
|
||||
if ($item['type'] === 'trojan') {
|
||||
array_push($proxy, Clash::buildTrojan($user['uuid'], $item));
|
||||
array_push($proxy, Protocols\Clash::buildTrojan($user['uuid'], $item));
|
||||
array_push($proxies, $item['name']);
|
||||
}
|
||||
}
|
||||
@ -84,62 +91,4 @@ class AppController extends Controller
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
public function config(Request $request)
|
||||
{
|
||||
if (empty($request->input('server_id'))) {
|
||||
abort(500, '参数错误');
|
||||
}
|
||||
$user = $request->user;
|
||||
if ($user->expired_at < time() && $user->expired_at !== NULL) {
|
||||
abort(500, '订阅计划已过期');
|
||||
}
|
||||
$server = Server::where('show', 1)
|
||||
->where('id', $request->input('server_id'))
|
||||
->first();
|
||||
if (!$server) {
|
||||
abort(500, '服务器不存在');
|
||||
}
|
||||
$json = json_decode(self::CLIENT_CONFIG);
|
||||
//socks
|
||||
$json->inbound->port = (int)self::SOCKS_PORT;
|
||||
//http
|
||||
$json->inboundDetour[0]->port = (int)self::HTTP_PORT;
|
||||
//other
|
||||
$json->outbound->settings->vnext[0]->address = (string)$server->host;
|
||||
$json->outbound->settings->vnext[0]->port = (int)$server->port;
|
||||
$json->outbound->settings->vnext[0]->users[0]->id = (string)$user->uuid;
|
||||
$json->outbound->settings->vnext[0]->users[0]->alterId = (int)$server->alter_id;
|
||||
$json->outbound->settings->vnext[0]->remark = (string)$server->name;
|
||||
$json->outbound->streamSettings->network = $server->network;
|
||||
if ($server->networkSettings) {
|
||||
switch ($server->network) {
|
||||
case 'tcp':
|
||||
$json->outbound->streamSettings->tcpSettings = json_decode($server->networkSettings);
|
||||
break;
|
||||
case 'kcp':
|
||||
$json->outbound->streamSettings->kcpSettings = json_decode($server->networkSettings);
|
||||
break;
|
||||
case 'ws':
|
||||
$json->outbound->streamSettings->wsSettings = json_decode($server->networkSettings);
|
||||
break;
|
||||
case 'http':
|
||||
$json->outbound->streamSettings->httpSettings = json_decode($server->networkSettings);
|
||||
break;
|
||||
case 'domainsocket':
|
||||
$json->outbound->streamSettings->dsSettings = json_decode($server->networkSettings);
|
||||
break;
|
||||
case 'quic':
|
||||
$json->outbound->streamSettings->quicSettings = json_decode($server->networkSettings);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($request->input('is_global')) {
|
||||
$json->routing->settings->rules[0]->outboundTag = 'proxy';
|
||||
}
|
||||
if ($server->tls) {
|
||||
$json->outbound->streamSettings->security = "tls";
|
||||
}
|
||||
die(json_encode($json, JSON_UNESCAPED_UNICODE));
|
||||
}
|
||||
}
|
||||
|
@ -2,18 +2,10 @@
|
||||
|
||||
namespace App\Http\Controllers\Client;
|
||||
|
||||
use App\Http\Controllers\Client\Protocols\V2rayN;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Services\ServerService;
|
||||
use App\Utils\Clash;
|
||||
use App\Utils\QuantumultX;
|
||||
use App\Utils\Shadowrocket;
|
||||
use App\Utils\Surge;
|
||||
use App\Utils\Surfboard;
|
||||
use App\Utils\URLSchemes;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\Server;
|
||||
use App\Utils\Helper;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
use App\Services\UserService;
|
||||
|
||||
class ClientController extends Controller
|
||||
@ -32,251 +24,18 @@ class ClientController extends Controller
|
||||
$serverService = new ServerService();
|
||||
$servers = $serverService->getAvailableServers($user);
|
||||
if ($flag) {
|
||||
if (strpos($flag, 'quantumult%20x') !== false) {
|
||||
die($this->quantumultX($user, $servers));
|
||||
}
|
||||
if (strpos($flag, 'quantumult') !== false) {
|
||||
die($this->quantumult($user, $servers));
|
||||
}
|
||||
if (strpos($flag, 'clash') !== false) {
|
||||
die($this->clash($user, $servers));
|
||||
}
|
||||
if (strpos($flag, 'surfboard') !== false) {
|
||||
die($this->surfboard($user, $servers));
|
||||
}
|
||||
if (strpos($flag, 'surge') !== false) {
|
||||
die($this->surge($user, $servers));
|
||||
}
|
||||
if (strpos($flag, 'shadowrocket') !== false) {
|
||||
die($this->shadowrocket($user, $servers));
|
||||
}
|
||||
if (strpos($flag, 'shadowsocks') !== false) {
|
||||
die($this->shaodowsocksSIP008($user, $servers));
|
||||
}
|
||||
}
|
||||
die($this->origin($user, $servers));
|
||||
}
|
||||
}
|
||||
// TODO: Ready to stop support
|
||||
private function quantumult($user, $servers = [])
|
||||
{
|
||||
$uri = '';
|
||||
header('subscription-userinfo: upload=' . $user['u'] . '; download=' . $user['d'] . ';total=' . $user['transfer_enable']);
|
||||
foreach ($servers as $item) {
|
||||
if ($item['type'] === 'v2ray') {
|
||||
$str = '';
|
||||
$str .= $item['name'] . '= vmess, ' . $item['host'] . ', ' . $item['port'] . ', chacha20-ietf-poly1305, "' . $user['uuid'] . '", over-tls=' . ($item['tls'] ? "true" : "false") . ', certificate=0, group=' . config('v2board.app_name', 'V2Board');
|
||||
if ($item['network'] === 'ws') {
|
||||
$str .= ', obfs=ws';
|
||||
if ($item['networkSettings']) {
|
||||
$wsSettings = json_decode($item['networkSettings'], true);
|
||||
if (isset($wsSettings['path'])) $str .= ', obfs-path="' . $wsSettings['path'] . '"';
|
||||
if (isset($wsSettings['headers']['Host'])) $str .= ', obfs-header="Host:' . $wsSettings['headers']['Host'] . '"';
|
||||
foreach (glob(app_path('Http//Controllers//Client//Protocols') . '/*.php') as $file) {
|
||||
$file = 'App\\Http\\Controllers\\Client\\Protocols\\' . basename($file, '.php');
|
||||
$class = new $file($user, $servers);
|
||||
if (strpos($flag, $class->flag) !== false) {
|
||||
die($class->handle());
|
||||
}
|
||||
}
|
||||
$uri .= "vmess://" . base64_encode($str) . "\r\n";
|
||||
}
|
||||
// todo 1.5.3 remove
|
||||
$class = new V2rayN($user, $servers);
|
||||
die($class->handle());
|
||||
die('该客户端暂不支持进行订阅');
|
||||
}
|
||||
return base64_encode($uri);
|
||||
}
|
||||
|
||||
private function shadowrocket($user, $servers = [])
|
||||
{
|
||||
$uri = '';
|
||||
//display remaining traffic and expire date
|
||||
$upload = round($user['u'] / (1024*1024*1024), 2);
|
||||
$download = round($user['d'] / (1024*1024*1024), 2);
|
||||
$totalTraffic = round($user['transfer_enable'] / (1024*1024*1024), 2);
|
||||
$expiredDate = date('Y-m-d', $user['expired_at']);
|
||||
$uri .= "STATUS=🚀↑:{$upload}GB,↓:{$download}GB,TOT:{$totalTraffic}GB💡Expires:{$expiredDate}\r\n";
|
||||
foreach ($servers as $item) {
|
||||
if ($item['type'] === 'shadowsocks') {
|
||||
$uri .= Shadowrocket::buildShadowsocks($user['uuid'], $item);
|
||||
}
|
||||
if ($item['type'] === 'v2ray') {
|
||||
$uri .= Shadowrocket::buildVmess($user['uuid'], $item);
|
||||
}
|
||||
if ($item['type'] === 'trojan') {
|
||||
$uri .= Shadowrocket::buildTrojan($user['uuid'], $item);
|
||||
}
|
||||
}
|
||||
return base64_encode($uri);
|
||||
}
|
||||
|
||||
private function quantumultX($user, $servers = [])
|
||||
{
|
||||
$uri = '';
|
||||
header("subscription-userinfo: upload={$user['u']}; download={$user['d']}; total={$user['transfer_enable']}; expire={$user['expired_at']}");
|
||||
foreach ($servers as $item) {
|
||||
if ($item['type'] === 'shadowsocks') {
|
||||
$uri .= QuantumultX::buildShadowsocks($user['uuid'], $item);
|
||||
}
|
||||
if ($item['type'] === 'v2ray') {
|
||||
$uri .= QuantumultX::buildVmess($user['uuid'], $item);
|
||||
}
|
||||
if ($item['type'] === 'trojan') {
|
||||
$uri .= QuantumultX::buildTrojan($user['uuid'], $item);
|
||||
}
|
||||
}
|
||||
return base64_encode($uri);
|
||||
}
|
||||
|
||||
private function origin($user, $servers = [])
|
||||
{
|
||||
$uri = '';
|
||||
foreach ($servers as $item) {
|
||||
if ($item['type'] === 'shadowsocks') {
|
||||
$uri .= URLSchemes::buildShadowsocks($item, $user);
|
||||
}
|
||||
if ($item['type'] === 'v2ray') {
|
||||
$uri .= URLSchemes::buildVmess($item, $user);
|
||||
}
|
||||
if ($item['type'] === 'trojan') {
|
||||
$uri .= URLSchemes::buildTrojan($item, $user);
|
||||
}
|
||||
}
|
||||
return base64_encode($uri);
|
||||
}
|
||||
|
||||
private function shaodowsocksSIP008($user, $servers = [])
|
||||
{
|
||||
$configs = [];
|
||||
$subs = [];
|
||||
$subs['servers'] = [];
|
||||
$subs['bytes_used'] = '';
|
||||
$subs['bytes_remaining'] = '';
|
||||
|
||||
$bytesUsed = $user['u'] + $user['d'];
|
||||
$bytesRemaining = $user['transfer_enable'] - $bytesUsed;
|
||||
|
||||
foreach ($servers as $item) {
|
||||
if ($item['type'] === 'shadowsocks') {
|
||||
array_push($configs, URLSchemes::buildShadowsocksSIP008($item, $user));
|
||||
}
|
||||
}
|
||||
|
||||
$subs['version'] = 1;
|
||||
$subs['bytes_used'] = $bytesUsed;
|
||||
$subs['bytes_remaining'] = $bytesRemaining;
|
||||
$subs['servers'] = array_merge($subs['servers'] ? $subs['servers'] : [], $configs);
|
||||
|
||||
return json_encode($subs, JSON_UNESCAPED_SLASHES|JSON_PRETTY_PRINT);
|
||||
}
|
||||
|
||||
private function surge($user, $servers = [])
|
||||
{
|
||||
$proxies = '';
|
||||
$proxyGroup = '';
|
||||
|
||||
foreach ($servers as $item) {
|
||||
if ($item['type'] === 'shadowsocks') {
|
||||
// [Proxy]
|
||||
$proxies .= Surge::buildShadowsocks($user['uuid'], $item);
|
||||
// [Proxy Group]
|
||||
$proxyGroup .= $item['name'] . ', ';
|
||||
}
|
||||
if ($item['type'] === 'v2ray') {
|
||||
// [Proxy]
|
||||
$proxies .= Surge::buildVmess($user['uuid'], $item);
|
||||
// [Proxy Group]
|
||||
$proxyGroup .= $item['name'] . ', ';
|
||||
}
|
||||
if ($item['type'] === 'trojan') {
|
||||
// [Proxy]
|
||||
$proxies .= Surge::buildTrojan($user['uuid'], $item);
|
||||
// [Proxy Group]
|
||||
$proxyGroup .= $item['name'] . ', ';
|
||||
}
|
||||
}
|
||||
|
||||
$defaultConfig = base_path() . '/resources/rules/default.surge.conf';
|
||||
$customConfig = base_path() . '/resources/rules/custom.surge.conf';
|
||||
if (\File::exists($customConfig)) {
|
||||
$config = file_get_contents("$customConfig");
|
||||
} else {
|
||||
$config = file_get_contents("$defaultConfig");
|
||||
}
|
||||
|
||||
// Subscription link
|
||||
$subsURL = config('v2board.subscribe_url', config('v2board.app_url', env('APP_URL'))) . '/api/v1/client/subscribe?token=' . $user['token'];
|
||||
|
||||
$config = str_replace('$subs_link', $subsURL, $config);
|
||||
$config = str_replace('$proxies', $proxies, $config);
|
||||
$config = str_replace('$proxy_group', rtrim($proxyGroup, ', '), $config);
|
||||
return $config;
|
||||
}
|
||||
|
||||
private function surfboard($user, $servers = [])
|
||||
{
|
||||
$proxies = '';
|
||||
$proxyGroup = '';
|
||||
|
||||
foreach ($servers as $item) {
|
||||
if ($item['type'] === 'shadowsocks') {
|
||||
// [Proxy]
|
||||
$proxies .= Surfboard::buildShadowsocks($user['uuid'], $item);
|
||||
// [Proxy Group]
|
||||
$proxyGroup .= $item['name'] . ', ';
|
||||
}
|
||||
if ($item['type'] === 'v2ray') {
|
||||
// [Proxy]
|
||||
$proxies .= Surfboard::buildVmess($user['uuid'], $item);
|
||||
// [Proxy Group]
|
||||
$proxyGroup .= $item['name'] . ', ';
|
||||
}
|
||||
}
|
||||
|
||||
$defaultConfig = base_path() . '/resources/rules/default.surfboard.conf';
|
||||
$customConfig = base_path() . '/resources/rules/custom.surfboard.conf';
|
||||
if (\File::exists($customConfig)) {
|
||||
$config = file_get_contents("$customConfig");
|
||||
} else {
|
||||
$config = file_get_contents("$defaultConfig");
|
||||
}
|
||||
|
||||
// Subscription link
|
||||
$subsURL = config('v2board.subscribe_url', config('v2board.app_url', env('APP_URL'))) . '/api/v1/client/subscribe?token=' . $user['token'];
|
||||
|
||||
$config = str_replace('$subs_link', $subsURL, $config);
|
||||
$config = str_replace('$proxies', $proxies, $config);
|
||||
$config = str_replace('$proxy_group', rtrim($proxyGroup, ', '), $config);
|
||||
return $config;
|
||||
}
|
||||
|
||||
private function clash($user, $servers = [])
|
||||
{
|
||||
$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, Clash::buildShadowsocks($user['uuid'], $item));
|
||||
array_push($proxies, $item['name']);
|
||||
}
|
||||
if ($item['type'] === 'v2ray') {
|
||||
array_push($proxy, Clash::buildVmess($user['uuid'], $item));
|
||||
array_push($proxies, $item['name']);
|
||||
}
|
||||
if ($item['type'] === 'trojan') {
|
||||
array_push($proxy, Clash::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;
|
||||
$config['proxy-groups'][$k]['proxies'] = array_merge($config['proxy-groups'][$k]['proxies'], $proxies);
|
||||
}
|
||||
$yaml = Yaml::dump($config);
|
||||
$yaml = str_replace('$app_name', config('v2board.app_name', 'V2Board'), $yaml);
|
||||
return $yaml;
|
||||
}
|
||||
}
|
||||
|
93
app/Http/Controllers/Client/Protocols/AnXray.php
Normal file
93
app/Http/Controllers/Client/Protocols/AnXray.php
Normal file
@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Client\Protocols;
|
||||
|
||||
class AnXray
|
||||
{
|
||||
public $flag = 'anxray';
|
||||
private $servers;
|
||||
private $user;
|
||||
|
||||
public function __construct($user, $servers)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->servers = $servers;
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$servers = $this->servers;
|
||||
$user = $this->user;
|
||||
$uri = '';
|
||||
|
||||
foreach ($servers as $item) {
|
||||
if ($item['type'] === 'v2ray') {
|
||||
$uri .= self::buildVmess($user['uuid'], $item);
|
||||
}
|
||||
if ($item['type'] === 'shadowsocks') {
|
||||
$uri .= self::buildShadowsocks($user['uuid'], $item);
|
||||
}
|
||||
if ($item['type'] === 'trojan') {
|
||||
$uri .= self::buildTrojan($user['uuid'], $item);
|
||||
}
|
||||
}
|
||||
return base64_encode($uri);
|
||||
}
|
||||
|
||||
public static function buildShadowsocks($uuid, $server)
|
||||
{
|
||||
$name = rawurlencode($server['name']);
|
||||
$str = str_replace(
|
||||
['+', '/', '='],
|
||||
['-', '_', ''],
|
||||
base64_encode("{$server['cipher']}:{$uuid}")
|
||||
);
|
||||
return "ss://{$str}@{$server['host']}:{$server['port']}#{$name}\r\n";
|
||||
}
|
||||
|
||||
public static function buildShadowsocksSIP008($uuid, $server)
|
||||
{
|
||||
$config = [
|
||||
"id" => $server['id'],
|
||||
"remarks" => $server['name'],
|
||||
"server" => $server['host'],
|
||||
"server_port" => $server['port'],
|
||||
"password" => $uuid,
|
||||
"method" => $server['cipher']
|
||||
];
|
||||
return $config;
|
||||
}
|
||||
|
||||
public static function buildVmess($uuid, $server)
|
||||
{
|
||||
$config = [
|
||||
"encryption" => "none",
|
||||
"type" => urlencode($server['network']),
|
||||
"security" => $server['tls'] ? "tls" : "",
|
||||
"sni" => $server['tls'] ? urlencode(json_decode($server['tlsSettings'], true)['serverName']) : ""
|
||||
];
|
||||
if ((string)$server['network'] === 'ws') {
|
||||
$wsSettings = json_decode($server['networkSettings'], true);
|
||||
if (isset($wsSettings['path'])) $config['path'] = urlencode($wsSettings['path']);
|
||||
if (isset($wsSettings['headers']['Host'])) $config['host'] = urlencode($wsSettings['headers']['Host']);
|
||||
}
|
||||
if ((string)$server['network'] === 'grpc') {
|
||||
$grpcSettings = json_decode($server['networkSettings'], true);
|
||||
if (isset($grpcSettings['serviceName'])) $config['serviceName'] = urlencode($grpcSettings['serviceName']);
|
||||
}
|
||||
return "vmess://" . $uuid . "@" . $server['host'] . ":" . $server['port'] . "?" . http_build_query($config) . "#" . urlencode($server['name']) . "\r\n";
|
||||
}
|
||||
|
||||
public static function buildTrojan($uuid, $server)
|
||||
{
|
||||
$name = rawurlencode($server['name']);
|
||||
$query = http_build_query([
|
||||
'allowInsecure' => $server['allow_insecure'],
|
||||
'peer' => $server['server_name'],
|
||||
'sni' => $server['server_name']
|
||||
]);
|
||||
$uri = "trojan://{$uuid}@{$server['host']}:{$server['port']}?{$query}#{$name}";
|
||||
$uri .= "\r\n";
|
||||
return $uri;
|
||||
}
|
||||
}
|
@ -1,10 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace App\Utils;
|
||||
namespace App\Http\Controllers\Client\Protocols;
|
||||
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
class Clash
|
||||
{
|
||||
public $flag = 'clash';
|
||||
private $servers;
|
||||
private $user;
|
||||
|
||||
public function __construct($user, $servers)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->servers = $servers;
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$servers = $this->servers;
|
||||
$user = $this->user;
|
||||
header("subscription-userinfo: upload={$user['u']}; download={$user['d']}; total={$user['transfer_enable']}; expire={$user['expired_at']}");
|
||||
$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;
|
||||
$config['proxy-groups'][$k]['proxies'] = array_merge($config['proxy-groups'][$k]['proxies'], $proxies);
|
||||
}
|
||||
$yaml = Yaml::dump($config);
|
||||
$yaml = str_replace('$app_name', config('v2board.app_name', 'V2Board'), $yaml);
|
||||
return $yaml;
|
||||
}
|
||||
|
||||
public static function buildShadowsocks($uuid, $server)
|
||||
{
|
||||
$array = [];
|
||||
@ -50,6 +101,14 @@ class Clash
|
||||
$array['ws-headers'] = ['Host' => $wsSettings['headers']['Host']];
|
||||
}
|
||||
}
|
||||
if ($server['network'] === 'grpc') {
|
||||
$array['network'] = 'grpc';
|
||||
if ($server['networkSettings']) {
|
||||
$grpcObject = json_decode($server['networkSettings'], true);
|
||||
$array['grpc-opts'] = [];
|
||||
$array['grpc-opts']['grpc-service-name'] = $grpcObject['serviceName'];
|
||||
}
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
96
app/Http/Controllers/Client/Protocols/Passwall.php
Normal file
96
app/Http/Controllers/Client/Protocols/Passwall.php
Normal file
@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Client\Protocols;
|
||||
|
||||
|
||||
class Passwall
|
||||
{
|
||||
public $flag = 'passwall';
|
||||
private $servers;
|
||||
private $user;
|
||||
|
||||
public function __construct($user, $servers)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->servers = $servers;
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$servers = $this->servers;
|
||||
$user = $this->user;
|
||||
$uri = '';
|
||||
|
||||
foreach ($servers as $item) {
|
||||
if ($item['type'] === 'v2ray') {
|
||||
$uri .= self::buildVmess($user['uuid'], $item);
|
||||
}
|
||||
if ($item['type'] === 'shadowsocks') {
|
||||
$uri .= self::buildShadowsocks($user['uuid'], $item);
|
||||
}
|
||||
if ($item['type'] === 'trojan') {
|
||||
$uri .= self::buildTrojan($user['uuid'], $item);
|
||||
}
|
||||
}
|
||||
return base64_encode($uri);
|
||||
}
|
||||
|
||||
public static function buildShadowsocks($password, $server)
|
||||
{
|
||||
$name = rawurlencode($server['name']);
|
||||
$str = str_replace(
|
||||
['+', '/', '='],
|
||||
['-', '_', ''],
|
||||
base64_encode("{$server['cipher']}:{$password}")
|
||||
);
|
||||
return "ss://{$str}@{$server['host']}:{$server['port']}#{$name}\r\n";
|
||||
}
|
||||
|
||||
public static function buildVmess($uuid, $server)
|
||||
{
|
||||
$config = [
|
||||
"v" => "2",
|
||||
"ps" => $server['name'],
|
||||
"add" => $server['host'],
|
||||
"port" => (string)$server['port'],
|
||||
"id" => $uuid,
|
||||
"aid" => (string)$server['alter_id'],
|
||||
"net" => $server['network'],
|
||||
"type" => "none",
|
||||
"host" => "",
|
||||
"path" => "",
|
||||
"tls" => $server['tls'] ? "tls" : "",
|
||||
];
|
||||
if ($server['tls']) {
|
||||
if ($server['tlsSettings']) {
|
||||
$tlsSettings = json_decode($server['tlsSettings'], true);
|
||||
if (isset($tlsSettings['serverName']) && !empty($tlsSettings['serverName']))
|
||||
$config['sni'] = $tlsSettings['serverName'];
|
||||
}
|
||||
}
|
||||
if ((string)$server['network'] === 'ws') {
|
||||
$wsSettings = json_decode($server['networkSettings'], true);
|
||||
if (isset($wsSettings['path'])) $config['path'] = $wsSettings['path'];
|
||||
if (isset($wsSettings['headers']['Host'])) $config['host'] = $wsSettings['headers']['Host'];
|
||||
}
|
||||
if ((string)$server['network'] === 'grpc') {
|
||||
$grpcSettings = json_decode($server['networkSettings'], true);
|
||||
if (isset($grpcSettings['path'])) $config['path'] = $grpcSettings['serviceName'];
|
||||
}
|
||||
return "vmess://" . base64_encode(json_encode($config)) . "\r\n";
|
||||
}
|
||||
|
||||
public static function buildTrojan($password, $server)
|
||||
{
|
||||
$name = rawurlencode($server['name']);
|
||||
$query = http_build_query([
|
||||
'allowInsecure' => $server['allow_insecure'],
|
||||
'peer' => $server['server_name'],
|
||||
'sni' => $server['server_name']
|
||||
]);
|
||||
$uri = "trojan://{$password}@{$server['host']}:{$server['port']}?{$query}#{$name}";
|
||||
$uri .= "\r\n";
|
||||
return $uri;
|
||||
}
|
||||
|
||||
}
|
@ -1,10 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace App\Utils;
|
||||
namespace App\Http\Controllers\Client\Protocols;
|
||||
|
||||
|
||||
class QuantumultX
|
||||
{
|
||||
public $flag = 'quantumult%20x';
|
||||
private $servers;
|
||||
private $user;
|
||||
|
||||
public function __construct($user, $servers)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->servers = $servers;
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$servers = $this->servers;
|
||||
$user = $this->user;
|
||||
$uri = '';
|
||||
header("subscription-userinfo: upload={$user['u']}; download={$user['d']}; total={$user['transfer_enable']}; expire={$user['expired_at']}");
|
||||
foreach ($servers as $item) {
|
||||
if ($item['type'] === 'shadowsocks') {
|
||||
$uri .= self::buildShadowsocks($user['uuid'], $item);
|
||||
}
|
||||
if ($item['type'] === 'v2ray') {
|
||||
$uri .= self::buildVmess($user['uuid'], $item);
|
||||
}
|
||||
if ($item['type'] === 'trojan') {
|
||||
$uri .= self::buildTrojan($user['uuid'], $item);
|
||||
}
|
||||
}
|
||||
return base64_encode($uri);
|
||||
}
|
||||
|
||||
public static function buildShadowsocks($password, $server)
|
||||
{
|
||||
$config = [
|
96
app/Http/Controllers/Client/Protocols/SSRPlus.php
Normal file
96
app/Http/Controllers/Client/Protocols/SSRPlus.php
Normal file
@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Client\Protocols;
|
||||
|
||||
|
||||
class SSRPlus
|
||||
{
|
||||
public $flag = 'ssrplus';
|
||||
private $servers;
|
||||
private $user;
|
||||
|
||||
public function __construct($user, $servers)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->servers = $servers;
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$servers = $this->servers;
|
||||
$user = $this->user;
|
||||
$uri = '';
|
||||
|
||||
foreach ($servers as $item) {
|
||||
if ($item['type'] === 'v2ray') {
|
||||
$uri .= self::buildVmess($user['uuid'], $item);
|
||||
}
|
||||
if ($item['type'] === 'shadowsocks') {
|
||||
$uri .= self::buildShadowsocks($user['uuid'], $item);
|
||||
}
|
||||
if ($item['type'] === 'trojan') {
|
||||
$uri .= self::buildTrojan($user['uuid'], $item);
|
||||
}
|
||||
}
|
||||
return base64_encode($uri);
|
||||
}
|
||||
|
||||
public static function buildShadowsocks($password, $server)
|
||||
{
|
||||
$name = rawurlencode($server['name']);
|
||||
$str = str_replace(
|
||||
['+', '/', '='],
|
||||
['-', '_', ''],
|
||||
base64_encode("{$server['cipher']}:{$password}")
|
||||
);
|
||||
return "ss://{$str}@{$server['host']}:{$server['port']}#{$name}\r\n";
|
||||
}
|
||||
|
||||
public static function buildVmess($uuid, $server)
|
||||
{
|
||||
$config = [
|
||||
"v" => "2",
|
||||
"ps" => $server['name'],
|
||||
"add" => $server['host'],
|
||||
"port" => (string)$server['port'],
|
||||
"id" => $uuid,
|
||||
"aid" => (string)$server['alter_id'],
|
||||
"net" => $server['network'],
|
||||
"type" => "none",
|
||||
"host" => "",
|
||||
"path" => "",
|
||||
"tls" => $server['tls'] ? "tls" : "",
|
||||
];
|
||||
if ($server['tls']) {
|
||||
if ($server['tlsSettings']) {
|
||||
$tlsSettings = json_decode($server['tlsSettings'], true);
|
||||
if (isset($tlsSettings['serverName']) && !empty($tlsSettings['serverName']))
|
||||
$config['sni'] = $tlsSettings['serverName'];
|
||||
}
|
||||
}
|
||||
if ((string)$server['network'] === 'ws') {
|
||||
$wsSettings = json_decode($server['networkSettings'], true);
|
||||
if (isset($wsSettings['path'])) $config['path'] = $wsSettings['path'];
|
||||
if (isset($wsSettings['headers']['Host'])) $config['host'] = $wsSettings['headers']['Host'];
|
||||
}
|
||||
if ((string)$server['network'] === 'grpc') {
|
||||
$grpcSettings = json_decode($server['networkSettings'], true);
|
||||
if (isset($grpcSettings['path'])) $config['path'] = $grpcSettings['serviceName'];
|
||||
}
|
||||
return "vmess://" . base64_encode(json_encode($config)) . "\r\n";
|
||||
}
|
||||
|
||||
public static function buildTrojan($password, $server)
|
||||
{
|
||||
$name = rawurlencode($server['name']);
|
||||
$query = http_build_query([
|
||||
'allowInsecure' => $server['allow_insecure'],
|
||||
'peer' => $server['server_name'],
|
||||
'sni' => $server['server_name']
|
||||
]);
|
||||
$uri = "trojan://{$password}@{$server['host']}:{$server['port']}?{$query}#{$name}";
|
||||
$uri .= "\r\n";
|
||||
return $uri;
|
||||
}
|
||||
|
||||
}
|
@ -1,10 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace App\Utils;
|
||||
|
||||
namespace App\Http\Controllers\Client\Protocols;
|
||||
|
||||
class Shadowrocket
|
||||
{
|
||||
public $flag = 'shadowrocket';
|
||||
private $servers;
|
||||
private $user;
|
||||
|
||||
public function __construct($user, $servers)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->servers = $servers;
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$servers = $this->servers;
|
||||
$user = $this->user;
|
||||
|
||||
$uri = '';
|
||||
//display remaining traffic and expire date
|
||||
$upload = round($user['u'] / (1024*1024*1024), 2);
|
||||
$download = round($user['d'] / (1024*1024*1024), 2);
|
||||
$totalTraffic = round($user['transfer_enable'] / (1024*1024*1024), 2);
|
||||
$expiredDate = date('Y-m-d', $user['expired_at']);
|
||||
$uri .= "STATUS=🚀↑:{$upload}GB,↓:{$download}GB,TOT:{$totalTraffic}GB💡Expires:{$expiredDate}\r\n";
|
||||
foreach ($servers as $item) {
|
||||
if ($item['type'] === 'shadowsocks') {
|
||||
$uri .= self::buildShadowsocks($user['uuid'], $item);
|
||||
}
|
||||
if ($item['type'] === 'v2ray') {
|
||||
$uri .= self::buildVmess($user['uuid'], $item);
|
||||
}
|
||||
if ($item['type'] === 'trojan') {
|
||||
$uri .= self::buildTrojan($user['uuid'], $item);
|
||||
}
|
||||
}
|
||||
return base64_encode($uri);
|
||||
}
|
||||
|
||||
|
||||
public static function buildShadowsocks($password, $server)
|
||||
{
|
||||
$name = rawurlencode($server['name']);
|
||||
@ -44,6 +80,19 @@ class Shadowrocket
|
||||
$config['obfsParam'] = $wsSettings['headers']['Host'];
|
||||
}
|
||||
}
|
||||
if ($server['network'] === 'grpc') {
|
||||
$config['obfs'] = "grpc";
|
||||
if ($server['networkSettings']) {
|
||||
$grpcSettings = json_decode($server['networkSettings'], true);
|
||||
if (isset($grpcSettings['serviceName']) && !empty($grpcSettings['serviceName']))
|
||||
$config['path'] = $grpcSettings['serviceName'];
|
||||
}
|
||||
if (isset($tlsSettings)) {
|
||||
$config['host'] = $tlsSettings['serverName'];
|
||||
} else {
|
||||
$config['host'] = $server['host'];
|
||||
}
|
||||
}
|
||||
$query = http_build_query($config, '', '&', PHP_QUERY_RFC3986);
|
||||
$uri = "vmess://{$userinfo}?{$query}";
|
||||
$uri .= "\r\n";
|
57
app/Http/Controllers/Client/Protocols/Shadowsocks.php
Normal file
57
app/Http/Controllers/Client/Protocols/Shadowsocks.php
Normal file
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Client\Protocols;
|
||||
|
||||
class Shadowsocks
|
||||
{
|
||||
public $flag = 'shadowsocks';
|
||||
private $servers;
|
||||
private $user;
|
||||
|
||||
public function __construct($user, $servers)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->servers = $servers;
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$servers = $this->servers;
|
||||
$user = $this->user;
|
||||
|
||||
$configs = [];
|
||||
$subs = [];
|
||||
$subs['servers'] = [];
|
||||
$subs['bytes_used'] = '';
|
||||
$subs['bytes_remaining'] = '';
|
||||
|
||||
$bytesUsed = $user['u'] + $user['d'];
|
||||
$bytesRemaining = $user['transfer_enable'] - $bytesUsed;
|
||||
|
||||
foreach ($servers as $item) {
|
||||
if ($item['type'] === 'shadowsocks') {
|
||||
array_push($configs, self::SIP008($item, $user));
|
||||
}
|
||||
}
|
||||
|
||||
$subs['version'] = 1;
|
||||
$subs['bytes_used'] = $bytesUsed;
|
||||
$subs['bytes_remaining'] = $bytesRemaining;
|
||||
$subs['servers'] = array_merge($subs['servers'] ? $subs['servers'] : [], $configs);
|
||||
|
||||
return json_encode($subs, JSON_UNESCAPED_SLASHES|JSON_PRETTY_PRINT);
|
||||
}
|
||||
|
||||
public static function SIP008($server, $user)
|
||||
{
|
||||
$config = [
|
||||
"id" => $server['id'],
|
||||
"remarks" => $server['name'],
|
||||
"server" => $server['host'],
|
||||
"server_port" => $server['port'],
|
||||
"password" => $user['uuid'],
|
||||
"method" => $server['cipher']
|
||||
];
|
||||
return $config;
|
||||
}
|
||||
}
|
@ -1,10 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace App\Utils;
|
||||
namespace App\Http\Controllers\Client\Protocols;
|
||||
|
||||
|
||||
class Surfboard
|
||||
{
|
||||
public $flag = 'surfboard';
|
||||
private $servers;
|
||||
private $user;
|
||||
|
||||
public function __construct($user, $servers)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->servers = $servers;
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$servers = $this->servers;
|
||||
$user = $this->user;
|
||||
|
||||
$proxies = '';
|
||||
$proxyGroup = '';
|
||||
|
||||
foreach ($servers as $item) {
|
||||
if ($item['type'] === 'shadowsocks') {
|
||||
// [Proxy]
|
||||
$proxies .= Surfboard::buildShadowsocks($user['uuid'], $item);
|
||||
// [Proxy Group]
|
||||
$proxyGroup .= $item['name'] . ', ';
|
||||
}
|
||||
if ($item['type'] === 'v2ray') {
|
||||
// [Proxy]
|
||||
$proxies .= Surfboard::buildVmess($user['uuid'], $item);
|
||||
// [Proxy Group]
|
||||
$proxyGroup .= $item['name'] . ', ';
|
||||
}
|
||||
}
|
||||
|
||||
$defaultConfig = base_path() . '/resources/rules/default.surfboard.conf';
|
||||
$customConfig = base_path() . '/resources/rules/custom.surfboard.conf';
|
||||
if (\File::exists($customConfig)) {
|
||||
$config = file_get_contents("$customConfig");
|
||||
} else {
|
||||
$config = file_get_contents("$defaultConfig");
|
||||
}
|
||||
|
||||
// Subscription link
|
||||
$subsURL = config('v2board.subscribe_url', config('v2board.app_url', env('APP_URL'))) . '/api/v1/client/subscribe?token=' . $user['token'];
|
||||
|
||||
$config = str_replace('$subs_link', $subsURL, $config);
|
||||
$config = str_replace('$proxies', $proxies, $config);
|
||||
$config = str_replace('$proxy_group', rtrim($proxyGroup, ', '), $config);
|
||||
return $config;
|
||||
}
|
||||
|
||||
|
||||
public static function buildShadowsocks($password, $server)
|
||||
{
|
||||
$config = [
|
@ -1,10 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace App\Utils;
|
||||
|
||||
namespace App\Http\Controllers\Client\Protocols;
|
||||
|
||||
class Surge
|
||||
{
|
||||
public $flag = 'surge';
|
||||
private $servers;
|
||||
private $user;
|
||||
|
||||
public function __construct($user, $servers)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->servers = $servers;
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$servers = $this->servers;
|
||||
$user = $this->user;
|
||||
|
||||
$proxies = '';
|
||||
$proxyGroup = '';
|
||||
|
||||
foreach ($servers as $item) {
|
||||
if ($item['type'] === 'shadowsocks') {
|
||||
// [Proxy]
|
||||
$proxies .= self::buildShadowsocks($user['uuid'], $item);
|
||||
// [Proxy Group]
|
||||
$proxyGroup .= $item['name'] . ', ';
|
||||
}
|
||||
if ($item['type'] === 'v2ray') {
|
||||
// [Proxy]
|
||||
$proxies .= self::buildVmess($user['uuid'], $item);
|
||||
// [Proxy Group]
|
||||
$proxyGroup .= $item['name'] . ', ';
|
||||
}
|
||||
if ($item['type'] === 'trojan') {
|
||||
// [Proxy]
|
||||
$proxies .= self::buildTrojan($user['uuid'], $item);
|
||||
// [Proxy Group]
|
||||
$proxyGroup .= $item['name'] . ', ';
|
||||
}
|
||||
}
|
||||
|
||||
$defaultConfig = base_path() . '/resources/rules/default.surge.conf';
|
||||
$customConfig = base_path() . '/resources/rules/custom.surge.conf';
|
||||
if (\File::exists($customConfig)) {
|
||||
$config = file_get_contents("$customConfig");
|
||||
} else {
|
||||
$config = file_get_contents("$defaultConfig");
|
||||
}
|
||||
|
||||
// Subscription link
|
||||
$subsURL = config('v2board.subscribe_url', config('v2board.app_url', env('APP_URL'))) . '/api/v1/client/subscribe?token=' . $user['token'];
|
||||
|
||||
$config = str_replace('$subs_link', $subsURL, $config);
|
||||
$config = str_replace('$proxies', $proxies, $config);
|
||||
$config = str_replace('$proxy_group', rtrim($proxyGroup, ', '), $config);
|
||||
return $config;
|
||||
}
|
||||
|
||||
|
||||
public static function buildShadowsocks($password, $server)
|
||||
{
|
||||
$config = [
|
96
app/Http/Controllers/Client/Protocols/V2rayN.php
Normal file
96
app/Http/Controllers/Client/Protocols/V2rayN.php
Normal file
@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Client\Protocols;
|
||||
|
||||
|
||||
class V2rayN
|
||||
{
|
||||
public $flag = 'v2rayn';
|
||||
private $servers;
|
||||
private $user;
|
||||
|
||||
public function __construct($user, $servers)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->servers = $servers;
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$servers = $this->servers;
|
||||
$user = $this->user;
|
||||
$uri = '';
|
||||
|
||||
foreach ($servers as $item) {
|
||||
if ($item['type'] === 'v2ray') {
|
||||
$uri .= self::buildVmess($user['uuid'], $item);
|
||||
}
|
||||
if ($item['type'] === 'shadowsocks') {
|
||||
$uri .= self::buildShadowsocks($user['uuid'], $item);
|
||||
}
|
||||
if ($item['type'] === 'trojan') {
|
||||
$uri .= self::buildTrojan($user['uuid'], $item);
|
||||
}
|
||||
}
|
||||
return base64_encode($uri);
|
||||
}
|
||||
|
||||
public static function buildShadowsocks($password, $server)
|
||||
{
|
||||
$name = rawurlencode($server['name']);
|
||||
$str = str_replace(
|
||||
['+', '/', '='],
|
||||
['-', '_', ''],
|
||||
base64_encode("{$server['cipher']}:{$password}")
|
||||
);
|
||||
return "ss://{$str}@{$server['host']}:{$server['port']}#{$name}\r\n";
|
||||
}
|
||||
|
||||
public static function buildVmess($uuid, $server)
|
||||
{
|
||||
$config = [
|
||||
"v" => "2",
|
||||
"ps" => $server['name'],
|
||||
"add" => $server['host'],
|
||||
"port" => (string)$server['port'],
|
||||
"id" => $uuid,
|
||||
"aid" => (string)$server['alter_id'],
|
||||
"net" => $server['network'],
|
||||
"type" => "none",
|
||||
"host" => "",
|
||||
"path" => "",
|
||||
"tls" => $server['tls'] ? "tls" : "",
|
||||
];
|
||||
if ($server['tls']) {
|
||||
if ($server['tlsSettings']) {
|
||||
$tlsSettings = json_decode($server['tlsSettings'], true);
|
||||
if (isset($tlsSettings['serverName']) && !empty($tlsSettings['serverName']))
|
||||
$config['sni'] = $tlsSettings['serverName'];
|
||||
}
|
||||
}
|
||||
if ((string)$server['network'] === 'ws') {
|
||||
$wsSettings = json_decode($server['networkSettings'], true);
|
||||
if (isset($wsSettings['path'])) $config['path'] = $wsSettings['path'];
|
||||
if (isset($wsSettings['headers']['Host'])) $config['host'] = $wsSettings['headers']['Host'];
|
||||
}
|
||||
if ((string)$server['network'] === 'grpc') {
|
||||
$grpcSettings = json_decode($server['networkSettings'], true);
|
||||
if (isset($grpcSettings['path'])) $config['path'] = $grpcSettings['serviceName'];
|
||||
}
|
||||
return "vmess://" . base64_encode(json_encode($config)) . "\r\n";
|
||||
}
|
||||
|
||||
public static function buildTrojan($password, $server)
|
||||
{
|
||||
$name = rawurlencode($server['name']);
|
||||
$query = http_build_query([
|
||||
'allowInsecure' => $server['allow_insecure'],
|
||||
'peer' => $server['server_name'],
|
||||
'sni' => $server['server_name']
|
||||
]);
|
||||
$uri = "trojan://{$password}@{$server['host']}:{$server['port']}?{$query}#{$name}";
|
||||
$uri .= "\r\n";
|
||||
return $uri;
|
||||
}
|
||||
|
||||
}
|
97
app/Http/Controllers/Client/Protocols/V2rayNG.php
Normal file
97
app/Http/Controllers/Client/Protocols/V2rayNG.php
Normal file
@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Client\Protocols;
|
||||
|
||||
|
||||
class V2rayNG
|
||||
{
|
||||
public $flag = 'v2rayng';
|
||||
private $servers;
|
||||
private $user;
|
||||
|
||||
public function __construct($user, $servers)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->servers = $servers;
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$servers = $this->servers;
|
||||
$user = $this->user;
|
||||
$uri = '';
|
||||
|
||||
foreach ($servers as $item) {
|
||||
if ($item['type'] === 'v2ray') {
|
||||
$uri .= self::buildVmess($user['uuid'], $item);
|
||||
}
|
||||
if ($item['type'] === 'shadowsocks') {
|
||||
$uri .= self::buildShadowsocks($user['uuid'], $item);
|
||||
}
|
||||
if ($item['type'] === 'trojan') {
|
||||
$uri .= self::buildTrojan($user['uuid'], $item);
|
||||
}
|
||||
}
|
||||
return base64_encode($uri);
|
||||
}
|
||||
|
||||
public static function buildShadowsocks($password, $server)
|
||||
{
|
||||
$name = rawurlencode($server['name']);
|
||||
$str = str_replace(
|
||||
['+', '/', '='],
|
||||
['-', '_', ''],
|
||||
base64_encode("{$server['cipher']}:{$password}")
|
||||
);
|
||||
return "ss://{$str}@{$server['host']}:{$server['port']}#{$name}\r\n";
|
||||
}
|
||||
|
||||
public static function buildVmess($uuid, $server)
|
||||
{
|
||||
$config = [
|
||||
"v" => "2",
|
||||
"ps" => $server['name'],
|
||||
"add" => $server['host'],
|
||||
"port" => (string)$server['port'],
|
||||
"id" => $uuid,
|
||||
"aid" => (string)$server['alter_id'],
|
||||
"net" => $server['network'],
|
||||
"type" => "none",
|
||||
"host" => "",
|
||||
"path" => "",
|
||||
"tls" => $server['tls'] ? "tls" : "",
|
||||
];
|
||||
if ($server['tls']) {
|
||||
if ($server['tlsSettings']) {
|
||||
$tlsSettings = json_decode($server['tlsSettings'], true);
|
||||
if (isset($tlsSettings['serverName']) && !empty($tlsSettings['serverName']))
|
||||
$config['sni'] = $tlsSettings['serverName'];
|
||||
}
|
||||
}
|
||||
if ((string)$server['network'] === 'ws') {
|
||||
$wsSettings = json_decode($server['networkSettings'], true);
|
||||
if (isset($wsSettings['path'])) $config['path'] = $wsSettings['path'];
|
||||
if (isset($wsSettings['headers']['Host'])) $config['host'] = $wsSettings['headers']['Host'];
|
||||
}
|
||||
if ((string)$server['network'] === 'grpc') {
|
||||
$grpcSettings = json_decode($server['networkSettings'], true);
|
||||
if (isset($grpcSettings['path'])) $config['path'] = $grpcSettings['serviceName'];
|
||||
}
|
||||
return "vmess://" . base64_encode(json_encode($config)) . "\r\n";
|
||||
}
|
||||
|
||||
public static function buildTrojan($password, $server)
|
||||
{
|
||||
$name = rawurlencode($server['name']);
|
||||
$query = http_build_query([
|
||||
'allowInsecure' => $server['allow_insecure'],
|
||||
'peer' => $server['server_name'],
|
||||
'sni' => $server['server_name']
|
||||
]);
|
||||
$uri = "trojan://{$password}@{$server['host']}:{$server['port']}?{$query}#{$name}";
|
||||
$uri .= "\r\n";
|
||||
return $uri;
|
||||
}
|
||||
|
||||
|
||||
}
|
36
app/Http/Controllers/Guest/CommController.php
Normal file
36
app/Http/Controllers/Guest/CommController.php
Normal file
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Guest;
|
||||
|
||||
use App\Utils\Dict;
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
class CommController extends Controller
|
||||
{
|
||||
public function config()
|
||||
{
|
||||
return response([
|
||||
'data' => [
|
||||
'tos_url' => config('v2board.tos_url'),
|
||||
'is_email_verify' => (int)config('v2board.email_verify', 0) ? 1 : 0,
|
||||
'is_invite_force' => (int)config('v2board.invite_force', 0) ? 1 : 0,
|
||||
'email_whitelist_suffix' => (int)config('v2board.email_whitelist_enable', 0)
|
||||
? $this->getEmailSuffix()
|
||||
: 0,
|
||||
'is_recaptcha' => (int)config('v2board.recaptcha_enable', 0) ? 1 : 0,
|
||||
'recaptcha_site_key' => config('v2board.recaptcha_site_key'),
|
||||
'app_description' => config('v2board.app_description'),
|
||||
'app_url' => config('v2board.app_url')
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
private function getEmailSuffix()
|
||||
{
|
||||
$suffix = config('v2board.email_whitelist_suffix', Dict::EMAIL_WHITELIST_SUFFIX_DEFAULT);
|
||||
if (!is_array($suffix)) {
|
||||
return preg_split('/,/', $suffix);
|
||||
}
|
||||
return $suffix;
|
||||
}
|
||||
}
|
@ -1,177 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Guest;
|
||||
|
||||
use App\Services\OrderService;
|
||||
use App\Services\TelegramService;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Order;
|
||||
use Library\Epay;
|
||||
use Omnipay\Omnipay;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Library\BitpayX;
|
||||
use Library\MGate;
|
||||
|
||||
class OrderController extends Controller
|
||||
{
|
||||
public function alipayNotify(Request $request)
|
||||
{
|
||||
// Log::info('alipayNotifyData: ' . json_encode($_POST));
|
||||
$gateway = Omnipay::create('Alipay_AopF2F');
|
||||
$gateway->setSignType('RSA2'); //RSA/RSA2
|
||||
$gateway->setAppId(config('v2board.alipay_appid'));
|
||||
$gateway->setPrivateKey(config('v2board.alipay_privkey')); // 可以是路径,也可以是密钥内容
|
||||
$gateway->setAlipayPublicKey(config('v2board.alipay_pubkey')); // 可以是路径,也可以是密钥内容
|
||||
$request = $gateway->completePurchase();
|
||||
$request->setParams($_POST); //Optional
|
||||
try {
|
||||
/** @var \Omnipay\Alipay\Responses\AopCompletePurchaseResponse $response */
|
||||
$response = $request->send();
|
||||
|
||||
if ($response->isPaid()) {
|
||||
/**
|
||||
* Payment is successful
|
||||
*/
|
||||
if (!$this->handle($_POST['out_trade_no'], $_POST['trade_no'])) {
|
||||
abort(500, 'fail');
|
||||
}
|
||||
|
||||
die('success'); //The response should be 'success' only
|
||||
} else {
|
||||
/**
|
||||
* Payment is not successful
|
||||
*/
|
||||
die('fail');
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
/**
|
||||
* Payment is not successful
|
||||
*/
|
||||
die('fail');
|
||||
}
|
||||
}
|
||||
|
||||
public function stripeNotify(Request $request)
|
||||
{
|
||||
// Log::info('stripeNotifyData: ' . json_encode($request->input()));
|
||||
|
||||
\Stripe\Stripe::setApiKey(config('v2board.stripe_sk_live'));
|
||||
try {
|
||||
$event = \Stripe\Webhook::constructEvent(
|
||||
file_get_contents('php://input'),
|
||||
$_SERVER['HTTP_STRIPE_SIGNATURE'],
|
||||
config('v2board.stripe_webhook_key')
|
||||
);
|
||||
} catch (\Stripe\Error\SignatureVerification $e) {
|
||||
abort(400);
|
||||
}
|
||||
switch ($event->type) {
|
||||
case 'source.chargeable':
|
||||
$object = $event->data->object;
|
||||
\Stripe\Charge::create([
|
||||
'amount' => $object->amount,
|
||||
'currency' => $object->currency,
|
||||
'source' => $object->id,
|
||||
'metadata' => json_decode($object->metadata, true)
|
||||
]);
|
||||
die('success');
|
||||
break;
|
||||
case 'charge.succeeded':
|
||||
$object = $event->data->object;
|
||||
if ($object->status === 'succeeded') {
|
||||
$metaData = isset($object->metadata->out_trade_no) ? $object->metadata : $object->source->metadata;
|
||||
$tradeNo = $metaData->out_trade_no;
|
||||
if (!$tradeNo) {
|
||||
abort(500, 'trade no is not found in metadata');
|
||||
}
|
||||
if (!$this->handle($tradeNo, $object->balance_transaction)) {
|
||||
abort(500, 'fail');
|
||||
}
|
||||
die('success');
|
||||
}
|
||||
break;
|
||||
default:
|
||||
abort(500, 'event is not support');
|
||||
}
|
||||
}
|
||||
|
||||
public function bitpayXNotify(Request $request)
|
||||
{
|
||||
$inputString = file_get_contents('php://input', 'r');
|
||||
// Log::info('bitpayXNotifyData: ' . $inputString);
|
||||
$inputStripped = str_replace(array("\r", "\n", "\t", "\v"), '', $inputString);
|
||||
$inputJSON = json_decode($inputStripped, true); //convert JSON into array
|
||||
|
||||
$bitpayX = new BitpayX(config('v2board.bitpayx_appsecret'));
|
||||
$params = [
|
||||
'status' => $inputJSON['status'],
|
||||
'order_id' => $inputJSON['order_id'],
|
||||
'merchant_order_id' => $inputJSON['merchant_order_id'],
|
||||
'price_amount' => $inputJSON['price_amount'],
|
||||
'price_currency' => $inputJSON['price_currency'],
|
||||
'pay_amount' => $inputJSON['pay_amount'],
|
||||
'pay_currency' => $inputJSON['pay_currency'],
|
||||
'created_at_t' => $inputJSON['created_at_t']
|
||||
];
|
||||
$strToSign = $bitpayX->prepareSignId($inputJSON['merchant_order_id']);
|
||||
if (!$bitpayX->verify($strToSign, $inputJSON['token'])) {
|
||||
abort(500, 'sign error');
|
||||
}
|
||||
if ($params['status'] !== 'PAID') {
|
||||
abort(500, 'order is not paid');
|
||||
}
|
||||
if (!$this->handle($params['merchant_order_id'], $params['order_id'])) {
|
||||
abort(500, 'order process fail');
|
||||
}
|
||||
die(json_encode([
|
||||
'status' => 200
|
||||
]));
|
||||
}
|
||||
|
||||
public function mgateNotify(Request $request)
|
||||
{
|
||||
$mgate = new MGate(config('v2board.mgate_url'), config('v2board.mgate_app_id'), config('v2board.mgate_app_secret'));
|
||||
if (!$mgate->verify($request->input())) {
|
||||
abort(500, 'fail');
|
||||
}
|
||||
if (!$this->handle($request->input('out_trade_no'), $request->input('trade_no'))) {
|
||||
abort(500, 'fail');
|
||||
}
|
||||
die('success');
|
||||
}
|
||||
|
||||
public function epayNotify(Request $request)
|
||||
{
|
||||
$epay = new Epay(config('v2board.epay_url'), config('v2board.epay_pid'), config('v2board.epay_key'));
|
||||
if (!$epay->verify($request->input())) {
|
||||
abort(500, 'fail');
|
||||
}
|
||||
if (!$this->handle($request->input('out_trade_no'), $request->input('trade_no'))) {
|
||||
abort(500, 'fail');
|
||||
}
|
||||
die('success');
|
||||
}
|
||||
|
||||
private function handle($tradeNo, $callbackNo)
|
||||
{
|
||||
$order = Order::where('trade_no', $tradeNo)->first();
|
||||
if ($order->status === 1) return true;
|
||||
if (!$order) {
|
||||
abort(500, 'order is not found');
|
||||
}
|
||||
$orderService = new OrderService($order);
|
||||
if (!$orderService->success($callbackNo)) {
|
||||
return false;
|
||||
}
|
||||
$telegramService = new TelegramService();
|
||||
$message = sprintf(
|
||||
"💰成功收款%s元\n———————————————\n订单号:%s",
|
||||
$order->total_amount / 100,
|
||||
$order->trade_no
|
||||
);
|
||||
$telegramService->sendMessageWithAdmin($message);
|
||||
return true;
|
||||
}
|
||||
}
|
49
app/Http/Controllers/Guest/PaymentController.php
Normal file
49
app/Http/Controllers/Guest/PaymentController.php
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Guest;
|
||||
|
||||
use App\Models\Order;
|
||||
use App\Services\OrderService;
|
||||
use App\Services\PaymentService;
|
||||
use App\Services\TelegramService;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
class PaymentController extends Controller
|
||||
{
|
||||
public function notify($method, $uuid, Request $request)
|
||||
{
|
||||
try {
|
||||
$paymentService = new PaymentService($method, null, $uuid);
|
||||
$verify = $paymentService->notify($request->input());
|
||||
if (!$verify) abort(500, 'verify error');
|
||||
if (!$this->handle($verify['trade_no'], $verify['callback_no'])) {
|
||||
abort(500, 'handle error');
|
||||
}
|
||||
die(isset($paymentService->customResult) ? $paymentService->customResult : 'success');
|
||||
} catch (\Exception $e) {
|
||||
abort(500, 'fail');
|
||||
}
|
||||
}
|
||||
|
||||
private function handle($tradeNo, $callbackNo)
|
||||
{
|
||||
$order = Order::where('trade_no', $tradeNo)->first();
|
||||
if (!$order) {
|
||||
abort(500, 'order is not found');
|
||||
}
|
||||
if ($order->status === 1) return true;
|
||||
$orderService = new OrderService($order);
|
||||
if (!$orderService->success($callbackNo)) {
|
||||
return false;
|
||||
}
|
||||
$telegramService = new TelegramService();
|
||||
$message = sprintf(
|
||||
"💰成功收款%s元\n———————————————\n订单号:%s",
|
||||
$order->total_amount / 100,
|
||||
$order->trade_no
|
||||
);
|
||||
$telegramService->sendMessageWithAdmin($message);
|
||||
return true;
|
||||
}
|
||||
}
|
@ -193,6 +193,7 @@ class TelegramController extends Controller
|
||||
}
|
||||
$telegramService = new TelegramService();
|
||||
$telegramService->sendMessage($msg->chat_id, "#`{$ticketId}` 的工单已回复成功", 'markdown');
|
||||
$telegramService->sendMessageWithAdmin("#`{$ticketId}` 的工单已由 {$user->email} 进行回复", true);
|
||||
}
|
||||
|
||||
|
||||
|
@ -24,7 +24,7 @@ class AuthController extends Controller
|
||||
$recaptcha = new ReCaptcha(config('v2board.recaptcha_key'));
|
||||
$recaptchaResp = $recaptcha->verify($request->input('recaptcha_data'));
|
||||
if (!$recaptchaResp->isSuccess()) {
|
||||
abort(500, '验证码有误');
|
||||
abort(500, __('Invalid code is incorrect'));
|
||||
}
|
||||
}
|
||||
if ((int)config('v2board.email_whitelist_enable', 0)) {
|
||||
@ -32,36 +32,36 @@ class AuthController extends Controller
|
||||
$request->input('email'),
|
||||
config('v2board.email_whitelist_suffix', Dict::EMAIL_WHITELIST_SUFFIX_DEFAULT))
|
||||
) {
|
||||
abort(500, '邮箱后缀不处于白名单中');
|
||||
abort(500, __('Email suffix is not in the Whitelist'));
|
||||
}
|
||||
}
|
||||
if ((int)config('v2board.email_gmail_limit_enable', 0)) {
|
||||
$prefix = explode('@', $request->input('email'))[0];
|
||||
if (strpos($prefix, '.') !== false || strpos($prefix, '+') !== false) {
|
||||
abort(500, '不支持Gmail别名邮箱');
|
||||
abort(500, __('Gmail alias is not supported'));
|
||||
}
|
||||
}
|
||||
if ((int)config('v2board.stop_register', 0)) {
|
||||
abort(500, '本站已关闭注册');
|
||||
abort(500, __('Registration has closed'));
|
||||
}
|
||||
if ((int)config('v2board.invite_force', 0)) {
|
||||
if (empty($request->input('invite_code'))) {
|
||||
abort(500, '必须使用邀请码才可以注册');
|
||||
abort(500, __('You must use the invitation code to register'));
|
||||
}
|
||||
}
|
||||
if ((int)config('v2board.email_verify', 0)) {
|
||||
if (empty($request->input('email_code'))) {
|
||||
abort(500, '邮箱验证码不能为空');
|
||||
abort(500, __('Email verification code cannot be empty'));
|
||||
}
|
||||
if (Cache::get(CacheKey::get('EMAIL_VERIFY_CODE', $request->input('email'))) !== $request->input('email_code')) {
|
||||
abort(500, '邮箱验证码有误');
|
||||
abort(500, __('Incorrect email verification code'));
|
||||
}
|
||||
}
|
||||
$email = $request->input('email');
|
||||
$password = $request->input('password');
|
||||
$exist = User::where('email', $email)->first();
|
||||
if ($exist) {
|
||||
abort(500, '邮箱已存在系统中');
|
||||
abort(500, __('Email already exists'));
|
||||
}
|
||||
$user = new User();
|
||||
$user->email = $email;
|
||||
@ -74,7 +74,7 @@ class AuthController extends Controller
|
||||
->first();
|
||||
if (!$inviteCode) {
|
||||
if ((int)config('v2board.invite_force', 0)) {
|
||||
abort(500, '邀请码无效');
|
||||
abort(500, __('Invalid invitation code'));
|
||||
}
|
||||
} else {
|
||||
$user->invite_user_id = $inviteCode->user_id ? $inviteCode->user_id : null;
|
||||
@ -97,7 +97,7 @@ class AuthController extends Controller
|
||||
}
|
||||
|
||||
if (!$user->save()) {
|
||||
abort(500, '注册失败');
|
||||
abort(500, __('Register failed'));
|
||||
}
|
||||
if ((int)config('v2board.email_verify', 0)) {
|
||||
Cache::forget(CacheKey::get('EMAIL_VERIFY_CODE', $request->input('email')));
|
||||
@ -116,22 +116,23 @@ class AuthController extends Controller
|
||||
|
||||
$user = User::where('email', $email)->first();
|
||||
if (!$user) {
|
||||
abort(500, '用户名或密码错误');
|
||||
abort(500, __('Incorrect email or password'));
|
||||
}
|
||||
if (!Helper::multiPasswordVerify(
|
||||
$user->password_algo,
|
||||
$password,
|
||||
$user->password)
|
||||
) {
|
||||
abort(500, '用户名或密码错误');
|
||||
abort(500, __('Incorrect email or password'));
|
||||
}
|
||||
|
||||
if ($user->banned) {
|
||||
abort(500, '该账户已被停止使用');
|
||||
abort(500, __('Your account has been suspended'));
|
||||
}
|
||||
|
||||
$data = [
|
||||
'token' => $user->token
|
||||
'token' => $user->token,
|
||||
'auth_data' => base64_encode("{$user->email}:{$user->password}")
|
||||
];
|
||||
$request->session()->put('email', $user->email);
|
||||
$request->session()->put('id', $user->id);
|
||||
@ -164,14 +165,14 @@ class AuthController extends Controller
|
||||
$key = CacheKey::get('TEMP_TOKEN', $request->input('verify'));
|
||||
$userId = Cache::get($key);
|
||||
if (!$userId) {
|
||||
abort(500, '令牌有误');
|
||||
abort(500, __('Token error'));
|
||||
}
|
||||
$user = User::find($userId);
|
||||
if (!$user) {
|
||||
abort(500, '用户不存在');
|
||||
abort(500, __('The user does not '));
|
||||
}
|
||||
if ($user->banned) {
|
||||
abort(500, '该账户已被停止使用');
|
||||
abort(500, __('Your account has been suspended'));
|
||||
}
|
||||
$request->session()->put('email', $user->email);
|
||||
$request->session()->put('id', $user->id);
|
||||
@ -189,7 +190,7 @@ class AuthController extends Controller
|
||||
{
|
||||
$user = User::where('token', $request->input('token'))->first();
|
||||
if (!$user) {
|
||||
abort(500, '令牌有误');
|
||||
abort(500, __('Token error'));
|
||||
}
|
||||
|
||||
$code = Helper::guid();
|
||||
@ -202,9 +203,13 @@ class AuthController extends Controller
|
||||
|
||||
public function getQuickLoginUrl(Request $request)
|
||||
{
|
||||
$user = User::where('token', $request->input('token'))->first();
|
||||
$authData = explode(':', base64_decode($request->input('auth_data')));
|
||||
if (!isset($authData[0])) abort(403, __('Token error'));
|
||||
$user = User::where('email', $authData[0])
|
||||
->where('password', $authData[1])
|
||||
->first();
|
||||
if (!$user) {
|
||||
abort(500, '令牌有误');
|
||||
abort(500, __('Token error'));
|
||||
}
|
||||
|
||||
$code = Helper::guid();
|
||||
@ -237,16 +242,16 @@ class AuthController extends Controller
|
||||
public function forget(AuthForget $request)
|
||||
{
|
||||
if (Cache::get(CacheKey::get('EMAIL_VERIFY_CODE', $request->input('email'))) !== $request->input('email_code')) {
|
||||
abort(500, '邮箱验证码有误');
|
||||
abort(500, __('Incorrect email verification code'));
|
||||
}
|
||||
$user = User::where('email', $request->input('email'))->first();
|
||||
if (!$user) {
|
||||
abort(500, '该邮箱不存在系统中');
|
||||
abort(500, __('This email is not registered in the system'));
|
||||
}
|
||||
$user->password = password_hash($request->input('password'), PASSWORD_DEFAULT);
|
||||
$user->password_algo = NULL;
|
||||
if (!$user->save()) {
|
||||
abort(500, '重置失败');
|
||||
abort(500, __('Reset failed'));
|
||||
}
|
||||
Cache::forget(CacheKey::get('EMAIL_VERIFY_CODE', $request->input('email')));
|
||||
return response([
|
||||
|
@ -17,6 +17,7 @@ use ReCaptcha\ReCaptcha;
|
||||
|
||||
class CommController extends Controller
|
||||
{
|
||||
// TODO: remove on 1.5.5
|
||||
public function config()
|
||||
{
|
||||
return response([
|
||||
@ -47,15 +48,15 @@ class CommController extends Controller
|
||||
$recaptcha = new ReCaptcha(config('v2board.recaptcha_key'));
|
||||
$recaptchaResp = $recaptcha->verify($request->input('recaptcha_data'));
|
||||
if (!$recaptchaResp->isSuccess()) {
|
||||
abort(500, '验证码有误');
|
||||
abort(500, __('Invalid code is incorrect'));
|
||||
}
|
||||
}
|
||||
$email = $request->input('email');
|
||||
if (Cache::get(CacheKey::get('LAST_SEND_EMAIL_VERIFY_TIMESTAMP', $email))) {
|
||||
abort(500, '验证码已发送,请过一会再请求');
|
||||
abort(500, __('Email verification code has been sent, please request again later'));
|
||||
}
|
||||
$code = rand(100000, 999999);
|
||||
$subject = config('v2board.app_name', 'V2Board') . '邮箱验证码';
|
||||
$subject = config('v2board.app_name', 'V2Board') . __('Email verification code');
|
||||
|
||||
SendEmailJob::dispatch([
|
||||
'email' => $email,
|
||||
|
@ -74,6 +74,7 @@ class DeepbworkController extends Controller
|
||||
$data = file_get_contents('php://input');
|
||||
$data = json_decode($data, true);
|
||||
Cache::put(CacheKey::get('SERVER_V2RAY_ONLINE_USER', $server->id), count($data), 3600);
|
||||
Cache::put(CacheKey::get('SERVER_V2RAY_LAST_PUSH_AT', $server->id), time(), 3600);
|
||||
$userService = new UserService();
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
|
@ -67,6 +67,7 @@ class PoseidonController extends Controller
|
||||
$data = file_get_contents('php://input');
|
||||
$data = json_decode($data, true);
|
||||
Cache::put(CacheKey::get('SERVER_V2RAY_ONLINE_USER', $server->id), count($data), 3600);
|
||||
Cache::put(CacheKey::get('SERVER_V2RAY_LAST_PUSH_AT', $server->id), time(), 3600);
|
||||
$userService = new UserService();
|
||||
foreach ($data as $item) {
|
||||
$u = $item['u'] * $server->rate;
|
||||
|
@ -70,6 +70,7 @@ class ShadowsocksTidalabController extends Controller
|
||||
$data = file_get_contents('php://input');
|
||||
$data = json_decode($data, true);
|
||||
Cache::put(CacheKey::get('SERVER_SHADOWSOCKS_ONLINE_USER', $server->id), count($data), 3600);
|
||||
Cache::put(CacheKey::get('SERVER_SHADOWSOCKS_LAST_PUSH_AT', $server->id), time(), 3600);
|
||||
$userService = new UserService();
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
|
@ -71,6 +71,7 @@ class TrojanTidalabController extends Controller
|
||||
$data = file_get_contents('php://input');
|
||||
$data = json_decode($data, true);
|
||||
Cache::put(CacheKey::get('SERVER_TROJAN_ONLINE_USER', $server->id), count($data), 3600);
|
||||
Cache::put(CacheKey::get('SERVER_TROJAN_LAST_PUSH_AT', $server->id), time(), 3600);
|
||||
$userService = new UserService();
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
|
@ -17,8 +17,13 @@ class UserController extends Controller
|
||||
if (empty($request->input('id'))) {
|
||||
abort(500, '参数错误');
|
||||
}
|
||||
$user = User::where('is_admin', 0)
|
||||
->where('id', $request->input('id'))
|
||||
->where('is_staff', 0)
|
||||
->first();
|
||||
if (!$user) abort(500, '用户不存在');
|
||||
return response([
|
||||
'data' => User::find($request->input('id'))
|
||||
'data' => $user
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Http\Controllers\User;
|
||||
|
||||
use App\Models\Payment;
|
||||
use App\Utils\Dict;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
@ -19,4 +20,16 @@ class CommController extends Controller
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
public function getStripePublicKey(Request $request)
|
||||
{
|
||||
$payment = Payment::where('id', $request->input('id'))
|
||||
->where('payment', 'StripeCredit')
|
||||
->first();
|
||||
if (!$payment) abort(500, 'payment is not found');
|
||||
$config = json_decode($payment->config, true);
|
||||
return response([
|
||||
'data' => $config['stripe_pk_live']
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -11,25 +11,25 @@ class CouponController extends Controller
|
||||
public function check(Request $request)
|
||||
{
|
||||
if (empty($request->input('code'))) {
|
||||
abort(500, __('user.coupon.check.coupon_not_empty'));
|
||||
abort(500, __('Coupon cannot be empty'));
|
||||
}
|
||||
$coupon = Coupon::where('code', $request->input('code'))->first();
|
||||
if (!$coupon) {
|
||||
abort(500, __('user.coupon.check.coupon_invalid'));
|
||||
abort(500, __('Invalid coupon'));
|
||||
}
|
||||
if ($coupon->limit_use <= 0 && $coupon->limit_use !== NULL) {
|
||||
abort(500, __('user.coupon.check.coupon_not_available_by_number'));
|
||||
abort(500, __('This coupon is no longer available'));
|
||||
}
|
||||
if (time() < $coupon->started_at) {
|
||||
abort(500, __('user.coupon.check.coupon_not_available_by_time'));
|
||||
abort(500, __('This coupon has not yet started'));
|
||||
}
|
||||
if (time() > $coupon->ended_at) {
|
||||
abort(500, __('user.coupon.check.coupon_expired'));
|
||||
abort(500, __('This coupon has expired'));
|
||||
}
|
||||
if ($coupon->limit_plan_ids) {
|
||||
$limitPlanIds = json_decode($coupon->limit_plan_ids);
|
||||
if (!in_array($request->input('plan_id'), $limitPlanIds)) {
|
||||
abort(500, __('user.coupon.check.coupon_limit_plan'));
|
||||
abort(500, __('The coupon code cannot be used for this subscription'));
|
||||
}
|
||||
}
|
||||
return response([
|
||||
|
@ -14,7 +14,7 @@ class InviteController extends Controller
|
||||
public function save(Request $request)
|
||||
{
|
||||
if (InviteCode::where('user_id', $request->session()->get('id'))->where('status', 0)->count() >= config('v2board.invite_gen_limit', 5)) {
|
||||
abort(500, __('user.invite.save.invite_create_limit'));
|
||||
abort(500, __('The maximum number of creations has been reached'));
|
||||
}
|
||||
$inviteCode = new InviteCode();
|
||||
$inviteCode->user_id = $request->session()->get('id');
|
||||
|
@ -17,12 +17,23 @@ class KnowledgeController extends Controller
|
||||
->where('show', 1)
|
||||
->first()
|
||||
->toArray();
|
||||
if (!$knowledge) abort(500, __('user.knowledge.fetch.knowledge_not_exist'));
|
||||
if (!$knowledge) abort(500, __('Article does not exist'));
|
||||
$user = User::find($request->session()->get('id'));
|
||||
$userService = new UserService();
|
||||
$appleId = $userService->isAvailable($user) ? config('v2board.apple_id') : __('user.knowledge.fetch.apple_id_must_be_plan');
|
||||
$appleIdPassword = $userService->isAvailable($user) ? config('v2board.apple_id_password') : __('user.knowledge.fetch.apple_id_must_be_plan');
|
||||
$subscribeUrl = config('v2board.subscribe_url', config('v2board.app_url', env('APP_URL'))) . '/api/v1/client/subscribe?token=' . $user['token'];
|
||||
if ($userService->isAvailable($user)) {
|
||||
$appleId = config('v2board.apple_id');
|
||||
$appleIdPassword = config('v2board.apple_id_password');
|
||||
} else {
|
||||
$appleId = __('No active subscription. Unable to use our provided Apple ID');
|
||||
$appleIdPassword = __('No active subscription. Unable to use our provided Apple ID');
|
||||
$this->formatAccessData($knowledge['body']);
|
||||
}
|
||||
$subscribeUrl = config('v2board.app_url', env('APP_URL'));
|
||||
$subscribeUrls = explode(',', config('v2board.subscribe_url'));
|
||||
if ($subscribeUrls) {
|
||||
$subscribeUrl = $subscribeUrls[rand(0, count($subscribeUrls) - 1)];
|
||||
}
|
||||
$subscribeUrl = "{$subscribeUrl}/api/v1/client/subscribe?token={$user['token']}";
|
||||
$knowledge['body'] = str_replace('{{siteName}}', config('v2board.app_name', 'V2Board'), $knowledge['body']);
|
||||
$knowledge['body'] = str_replace('{{appleId}}', $appleId, $knowledge['body']);
|
||||
$knowledge['body'] = str_replace('{{appleIdPassword}}', $appleIdPassword, $knowledge['body']);
|
||||
@ -51,4 +62,13 @@ class KnowledgeController extends Controller
|
||||
'data' => $knowledges
|
||||
]);
|
||||
}
|
||||
|
||||
private function formatAccessData(&$body)
|
||||
{
|
||||
function getBetween($input, $start, $end){$substr = substr($input, strlen($start)+strpos($input, $start),(strlen($input) - strpos($input, $end))*(-1));return $substr;}
|
||||
$accessData = getBetween($body, '<!--access start-->', '<!--access end-->');
|
||||
if ($accessData) {
|
||||
$body = str_replace($accessData, '<div class="v2board-no-access">'. __('You must have a valid subscription to view content in this area') .'</div>', $body);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,8 +4,10 @@ namespace App\Http\Controllers\User;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\User\OrderSave;
|
||||
use App\Models\Payment;
|
||||
use App\Services\CouponService;
|
||||
use App\Services\OrderService;
|
||||
use App\Services\PaymentService;
|
||||
use App\Services\UserService;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
@ -40,7 +42,7 @@ class OrderController extends Controller
|
||||
}
|
||||
}
|
||||
return response([
|
||||
'data' => $order
|
||||
'data' => $order->makeHidden(['id', 'user_id'])
|
||||
]);
|
||||
}
|
||||
|
||||
@ -50,12 +52,12 @@ class OrderController extends Controller
|
||||
->where('trade_no', $request->input('trade_no'))
|
||||
->first();
|
||||
if (!$order) {
|
||||
abort(500, __('user.order.details.order_not_exist'));
|
||||
abort(500, __('Order does not exist or has been paid'));
|
||||
}
|
||||
$order['plan'] = Plan::find($order->plan_id);
|
||||
$order['try_out_plan_id'] = (int)config('v2board.try_out_plan_id');
|
||||
if (!$order['plan']) {
|
||||
abort(500, __('user.order.details.plan_not_exist'));
|
||||
abort(500, __('Subscription plan does not exist'));
|
||||
}
|
||||
return response([
|
||||
'data' => $order
|
||||
@ -66,38 +68,38 @@ class OrderController extends Controller
|
||||
{
|
||||
$userService = new UserService();
|
||||
if ($userService->isNotCompleteOrderByUserId($request->session()->get('id'))) {
|
||||
abort(500, __('user.order.save.exist_open_order'));
|
||||
abort(500, __('You have an unpaid or pending order, please try again later or cancel it'));
|
||||
}
|
||||
|
||||
$plan = Plan::find($request->input('plan_id'));
|
||||
$user = User::find($request->session()->get('id'));
|
||||
|
||||
if (!$plan) {
|
||||
abort(500, __('user.order.save.plan_not_exist'));
|
||||
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, __('user.order.save.plan_stop_sell'));
|
||||
abort(500, __('This subscription has been sold out, please choose another subscription'));
|
||||
}
|
||||
}
|
||||
|
||||
if (!$plan->renew && $user->plan_id == $plan->id && $request->input('cycle') !== 'reset_price') {
|
||||
abort(500, __('user.order.save.plan_stop_renew'));
|
||||
abort(500, __('This subscription cannot be renewed, please change to another subscription'));
|
||||
}
|
||||
|
||||
if ($plan[$request->input('cycle')] === NULL) {
|
||||
abort(500, __('user.order.save.plan_stop'));
|
||||
abort(500, __('This payment cycle cannot be purchased, please choose another cycle'));
|
||||
}
|
||||
|
||||
if ($request->input('cycle') === 'reset_price') {
|
||||
if ($user->expired_at <= time() || !$user->plan_id) {
|
||||
abort(500, __('user.order.save.plan_exist_not_buy_package'));
|
||||
abort(500, __('Subscription has expired or no active subscription, unable to purchase Data Reset Package'));
|
||||
}
|
||||
}
|
||||
|
||||
if (!$plan->show && $plan->renew && !$userService->isAvailable($user)) {
|
||||
abort(500, __('user.order.save.plan_expired'));
|
||||
abort(500, __('This subscription has expired, please change to another subscription'));
|
||||
}
|
||||
|
||||
DB::beginTransaction();
|
||||
@ -113,7 +115,7 @@ class OrderController extends Controller
|
||||
$couponService = new CouponService($request->input('coupon_code'));
|
||||
if (!$couponService->use($order)) {
|
||||
DB::rollBack();
|
||||
abort(500, __('user.order.save.coupon_use_failed'));
|
||||
abort(500, __('Coupon failed'));
|
||||
}
|
||||
$order->coupon_id = $couponService->getId();
|
||||
}
|
||||
@ -128,14 +130,14 @@ class OrderController extends Controller
|
||||
if ($remainingBalance > 0) {
|
||||
if (!$userService->addBalance($order->user_id, - $order->total_amount)) {
|
||||
DB::rollBack();
|
||||
abort(500, __('user.order.save.insufficient_balance'));
|
||||
abort(500, __('Insufficient balance'));
|
||||
}
|
||||
$order->balance_amount = $order->total_amount;
|
||||
$order->total_amount = 0;
|
||||
} else {
|
||||
if (!$userService->addBalance($order->user_id, - $user->balance)) {
|
||||
DB::rollBack();
|
||||
abort(500, __('user.order.save.insufficient_balance'));
|
||||
abort(500, __('Insufficient balance'));
|
||||
}
|
||||
$order->balance_amount = $user->balance;
|
||||
$order->total_amount = $order->total_amount - $user->balance;
|
||||
@ -144,7 +146,7 @@ class OrderController extends Controller
|
||||
|
||||
if (!$order->save()) {
|
||||
DB::rollback();
|
||||
abort(500, __('user.order.save.order_create_failed'));
|
||||
abort(500, __('Failed to create order'));
|
||||
}
|
||||
|
||||
DB::commit();
|
||||
@ -163,7 +165,7 @@ class OrderController extends Controller
|
||||
->where('status', 0)
|
||||
->first();
|
||||
if (!$order) {
|
||||
abort(500, __('user.order.checkout.order_not_exist_or_paid'));
|
||||
abort(500, __('Order does not exist or has been paid'));
|
||||
}
|
||||
// free process
|
||||
if ($order->total_amount <= 0) {
|
||||
@ -175,71 +177,20 @@ class OrderController extends Controller
|
||||
'data' => true
|
||||
]);
|
||||
}
|
||||
switch ($method) {
|
||||
// return type => 0: QRCode / 1: URL / 2: No action
|
||||
case 0:
|
||||
// alipayF2F
|
||||
if (!(int)config('v2board.alipay_enable')) {
|
||||
abort(500, __('user.order.checkout.pay_method_not_use'));
|
||||
}
|
||||
return response([
|
||||
'type' => 0,
|
||||
'data' => $this->alipayF2F($tradeNo, $order->total_amount)
|
||||
]);
|
||||
case 2:
|
||||
// stripeAlipay
|
||||
if (!(int)config('v2board.stripe_alipay_enable')) {
|
||||
abort(500, __('user.order.checkout.pay_method_not_use'));
|
||||
}
|
||||
return response([
|
||||
'type' => 1,
|
||||
'data' => $this->stripeAlipay($order)
|
||||
]);
|
||||
case 3:
|
||||
// stripeWepay
|
||||
if (!(int)config('v2board.stripe_wepay_enable')) {
|
||||
abort(500, __('user.order.checkout.pay_method_not_use'));
|
||||
}
|
||||
return response([
|
||||
'type' => 0,
|
||||
'data' => $this->stripeWepay($order)
|
||||
]);
|
||||
case 4:
|
||||
// bitpayX
|
||||
if (!(int)config('v2board.bitpayx_enable')) {
|
||||
abort(500, __('user.order.checkout.pay_method_not_use'));
|
||||
}
|
||||
return response([
|
||||
'type' => 1,
|
||||
'data' => $this->bitpayX($order)
|
||||
]);
|
||||
case 5:
|
||||
if (!(int)config('v2board.mgate_enable')) {
|
||||
abort(500, __('user.order.checkout.pay_method_not_use'));
|
||||
}
|
||||
return response([
|
||||
'type' => 1,
|
||||
'data' => $this->mgate($order)
|
||||
]);
|
||||
case 6:
|
||||
if (!(int)config('v2board.stripe_card_enable')) {
|
||||
abort(500, __('user.order.checkout.pay_method_not_use'));
|
||||
}
|
||||
return response([
|
||||
'type' => 2,
|
||||
'data' => $this->stripeCard($order, $request->input('token'))
|
||||
]);
|
||||
case 7:
|
||||
if (!(int)config('v2board.epay_enable')) {
|
||||
abort(500, __('user.order.checkout.pay_method_not_use'));
|
||||
}
|
||||
return response([
|
||||
'type' => 1,
|
||||
'data' => $this->epay($order)
|
||||
]);
|
||||
default:
|
||||
abort(500, __('user.order.checkout.pay_method_not_use'));
|
||||
}
|
||||
$payment = Payment::find($method);
|
||||
if (!$payment || $payment->enable !== 1) abort(500, __('Payment method is not available'));
|
||||
$paymentService = new PaymentService($payment->payment, $payment->id);
|
||||
$result = $paymentService->pay([
|
||||
'trade_no' => $tradeNo,
|
||||
'total_amount' => $order->total_amount,
|
||||
'user_id' => $order->user_id,
|
||||
'stripe_token' => $request->input('token')
|
||||
]);
|
||||
$order->update(['payment_id' => $method]);
|
||||
return response([
|
||||
'type' => $result['type'],
|
||||
'data' => $result['data']
|
||||
]);
|
||||
}
|
||||
|
||||
public function check(Request $request)
|
||||
@ -249,7 +200,7 @@ class OrderController extends Controller
|
||||
->where('user_id', $request->session()->get('id'))
|
||||
->first();
|
||||
if (!$order) {
|
||||
abort(500, __('user.order.check.order_not_exist'));
|
||||
abort(500, __('Order does not exist'));
|
||||
}
|
||||
return response([
|
||||
'data' => $order->status
|
||||
@ -258,241 +209,38 @@ class OrderController extends Controller
|
||||
|
||||
public function getPaymentMethod()
|
||||
{
|
||||
$data = [];
|
||||
if ((int)config('v2board.alipay_enable')) {
|
||||
$alipayF2F = new \StdClass();
|
||||
$alipayF2F->name = '支付宝';
|
||||
$alipayF2F->method = 0;
|
||||
$alipayF2F->icon = 'alipay';
|
||||
array_push($data, $alipayF2F);
|
||||
}
|
||||
|
||||
if ((int)config('v2board.stripe_alipay_enable')) {
|
||||
$stripeAlipay = new \StdClass();
|
||||
$stripeAlipay->name = '支付宝';
|
||||
$stripeAlipay->method = 2;
|
||||
$stripeAlipay->icon = 'alipay';
|
||||
array_push($data, $stripeAlipay);
|
||||
}
|
||||
|
||||
if ((int)config('v2board.stripe_wepay_enable')) {
|
||||
$stripeWepay = new \StdClass();
|
||||
$stripeWepay->name = '微信';
|
||||
$stripeWepay->method = 3;
|
||||
$stripeWepay->icon = 'wechat';
|
||||
array_push($data, $stripeWepay);
|
||||
}
|
||||
|
||||
if ((int)config('v2board.bitpayx_enable')) {
|
||||
$bitpayX = new \StdClass();
|
||||
$bitpayX->name = config('v2board.bitpayx_name', '在线支付');
|
||||
$bitpayX->method = 4;
|
||||
$bitpayX->icon = 'wallet';
|
||||
array_push($data, $bitpayX);
|
||||
}
|
||||
|
||||
if ((int)config('v2board.mgate_enable')) {
|
||||
$obj = new \StdClass();
|
||||
$obj->name = config('v2board.mgate_name', '在线支付');
|
||||
$obj->method = 5;
|
||||
$obj->icon = 'wallet';
|
||||
array_push($data, $obj);
|
||||
}
|
||||
|
||||
if ((int)config('v2board.stripe_card_enable')) {
|
||||
$obj = new \StdClass();
|
||||
$obj->name = '信用卡';
|
||||
$obj->method = 6;
|
||||
$obj->icon = 'card';
|
||||
array_push($data, $obj);
|
||||
}
|
||||
|
||||
if ((int)config('v2board.epay_enable')) {
|
||||
$obj = new \StdClass();
|
||||
$obj->name = config('v2board.epay_name', '在线支付');
|
||||
$obj->method = 7;
|
||||
$obj->icon = 'wallet';
|
||||
array_push($data, $obj);
|
||||
}
|
||||
$methods = Payment::select([
|
||||
'id',
|
||||
'name',
|
||||
'payment'
|
||||
])
|
||||
->where('enable', 1)->get();
|
||||
|
||||
return response([
|
||||
'data' => $data
|
||||
'data' => $methods
|
||||
]);
|
||||
}
|
||||
|
||||
public function cancel(Request $request)
|
||||
{
|
||||
if (empty($request->input('trade_no'))) {
|
||||
abort(500, __('user.order.cancel.params_wrong'));
|
||||
abort(500, __('Invalid parameter'));
|
||||
}
|
||||
$order = Order::where('trade_no', $request->input('trade_no'))
|
||||
->where('user_id', $request->session()->get('id'))
|
||||
->first();
|
||||
if (!$order) {
|
||||
abort(500, __('user.order.cancel.order_not_exist'));
|
||||
abort(500, __('Order does not exist'));
|
||||
}
|
||||
if ($order->status !== 0) {
|
||||
abort(500, __('user.order.cancel.only_cancel_pending_order'));
|
||||
abort(500, __('You can only cancel pending orders'));
|
||||
}
|
||||
$orderService = new OrderService($order);
|
||||
if (!$orderService->cancel()) {
|
||||
abort(500, __('user.order.cancel.cancel_failed'));
|
||||
abort(500, __('Cancel failed'));
|
||||
}
|
||||
return response([
|
||||
'data' => true
|
||||
]);
|
||||
}
|
||||
|
||||
private function alipayF2F($tradeNo, $totalAmount)
|
||||
{
|
||||
$gateway = Omnipay::create('Alipay_AopF2F');
|
||||
$gateway->setSignType('RSA2'); //RSA/RSA2
|
||||
$gateway->setAppId(config('v2board.alipay_appid'));
|
||||
$gateway->setPrivateKey(config('v2board.alipay_privkey')); // 可以是路径,也可以是密钥内容
|
||||
$gateway->setAlipayPublicKey(config('v2board.alipay_pubkey')); // 可以是路径,也可以是密钥内容
|
||||
$gateway->setNotifyUrl(url('/api/v1/guest/order/alipayNotify'));
|
||||
$request = $gateway->purchase();
|
||||
$request->setBizContent([
|
||||
'subject' => config('v2board.app_name', 'V2Board') . ' - 订阅',
|
||||
'out_trade_no' => $tradeNo,
|
||||
'total_amount' => $totalAmount / 100
|
||||
]);
|
||||
/** @var \Omnipay\Alipay\Responses\AopTradePreCreateResponse $response */
|
||||
$response = $request->send();
|
||||
$result = $response->getAlipayResponse();
|
||||
if ($result['code'] !== '10000') {
|
||||
abort(500, $result['sub_msg']);
|
||||
}
|
||||
// 获取收款二维码内容
|
||||
return $response->getQrCode();
|
||||
}
|
||||
|
||||
private function stripeAlipay($order)
|
||||
{
|
||||
$currency = config('v2board.stripe_currency', 'hkd');
|
||||
$exchange = Helper::exchange('CNY', strtoupper($currency));
|
||||
if (!$exchange) {
|
||||
abort(500, __('user.order.stripeAlipay.currency_convert_timeout'));
|
||||
}
|
||||
Stripe::setApiKey(config('v2board.stripe_sk_live'));
|
||||
$source = Source::create([
|
||||
'amount' => floor($order->total_amount * $exchange),
|
||||
'currency' => $currency,
|
||||
'type' => 'alipay',
|
||||
'statement_descriptor' => $order->trade_no,
|
||||
'metadata' => [
|
||||
'user_id' => $order->user_id,
|
||||
'out_trade_no' => $order->trade_no,
|
||||
'identifier' => ''
|
||||
],
|
||||
'redirect' => [
|
||||
'return_url' => config('v2board.app_url', env('APP_URL')) . '/#/order'
|
||||
]
|
||||
]);
|
||||
if (!$source['redirect']['url']) {
|
||||
abort(500, __('user.order.stripeAlipay.gateway_request_failed'));
|
||||
}
|
||||
return $source['redirect']['url'];
|
||||
}
|
||||
|
||||
private function stripeWepay($order)
|
||||
{
|
||||
$currency = config('v2board.stripe_currency', 'hkd');
|
||||
$exchange = Helper::exchange('CNY', strtoupper($currency));
|
||||
if (!$exchange) {
|
||||
abort(500, __('user.order.stripeWepay.currency_convert_timeout'));
|
||||
}
|
||||
Stripe::setApiKey(config('v2board.stripe_sk_live'));
|
||||
$source = Source::create([
|
||||
'amount' => floor($order->total_amount * $exchange),
|
||||
'currency' => $currency,
|
||||
'type' => 'wechat',
|
||||
'metadata' => [
|
||||
'user_id' => $order->user_id,
|
||||
'out_trade_no' => $order->trade_no,
|
||||
'identifier' => ''
|
||||
],
|
||||
'redirect' => [
|
||||
'return_url' => config('v2board.app_url', env('APP_URL')) . '/#/order'
|
||||
]
|
||||
]);
|
||||
if (!$source['wechat']['qr_code_url']) {
|
||||
abort(500, __('user.order.stripeWepay.gateway_request_failed'));
|
||||
}
|
||||
return $source['wechat']['qr_code_url'];
|
||||
}
|
||||
|
||||
private function stripeCard($order, string $token)
|
||||
{
|
||||
$currency = config('v2board.stripe_currency', 'hkd');
|
||||
$exchange = Helper::exchange('CNY', strtoupper($currency));
|
||||
if (!$exchange) {
|
||||
abort(500, __('user.order.stripeCard.currency_convert_timeout'));
|
||||
}
|
||||
Stripe::setApiKey(config('v2board.stripe_sk_live'));
|
||||
try {
|
||||
$charge = \Stripe\Charge::create([
|
||||
'amount' => floor($order->total_amount * $exchange),
|
||||
'currency' => $currency,
|
||||
'source' => $token,
|
||||
'metadata' => [
|
||||
'user_id' => $order->user_id,
|
||||
'out_trade_no' => $order->trade_no,
|
||||
'identifier' => ''
|
||||
]
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
abort(500, __('user.order.stripeCard.was_problem'));
|
||||
}
|
||||
info($charge);
|
||||
if (!$charge->paid) {
|
||||
abort(500, __('user.order.stripeCard.deduction_failed'));
|
||||
}
|
||||
return $charge->paid;
|
||||
}
|
||||
|
||||
private function bitpayX($order)
|
||||
{
|
||||
$bitpayX = new BitpayX(config('v2board.bitpayx_appsecret'));
|
||||
$params = [
|
||||
'merchant_order_id' => $order->trade_no,
|
||||
'price_amount' => $order->total_amount / 100,
|
||||
'price_currency' => 'CNY',
|
||||
'title' => '支付单号:' . $order->trade_no,
|
||||
'description' => '充值:' . $order->total_amount / 100 . ' 元',
|
||||
'callback_url' => url('/api/v1/guest/order/bitpayXNotify'),
|
||||
'success_url' => config('v2board.app_url', env('APP_URL')) . '/#/order',
|
||||
'cancel_url' => config('v2board.app_url', env('APP_URL')) . '/#/order'
|
||||
];
|
||||
$strToSign = $bitpayX->prepareSignId($params['merchant_order_id']);
|
||||
$params['token'] = $bitpayX->sign($strToSign);
|
||||
$result = $bitpayX->mprequest($params);
|
||||
// Log::info('bitpayXSubmit: ' . json_encode($result));
|
||||
return isset($result['payment_url']) ? $result['payment_url'] : false;
|
||||
}
|
||||
|
||||
private function mgate($order)
|
||||
{
|
||||
$mgate = new MGate(config('v2board.mgate_url'), config('v2board.mgate_app_id'), config('v2board.mgate_app_secret'));
|
||||
$result = $mgate->pay([
|
||||
'app_id' => config('v2board.mgate_app_id'),
|
||||
'out_trade_no' => $order->trade_no,
|
||||
'total_amount' => $order->total_amount,
|
||||
'notify_url' => url('/api/v1/guest/order/mgateNotify'),
|
||||
'return_url' => config('v2board.app_url', env('APP_URL')) . '/#/order'
|
||||
]);
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function epay($order)
|
||||
{
|
||||
$epay = new Epay(config('v2board.epay_url'), config('v2board.epay_pid'), config('v2board.epay_key'));
|
||||
return $epay->pay([
|
||||
'money' => $order->total_amount / 100,
|
||||
'name' => $order->trade_no,
|
||||
'notify_url' => url('/api/v1/guest/order/epayNotify'),
|
||||
'return_url' => config('v2board.app_url', env('APP_URL')) . '/#/order',
|
||||
'out_trade_no' => $order->trade_no
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ class PlanController extends Controller
|
||||
$plan = Plan::where('id', $request->input('id'))
|
||||
->first();
|
||||
if (!$plan) {
|
||||
abort(500, __('user.plan.fetch.plan_not_exist'));
|
||||
abort(500, __('Subscription plan does not exist'));
|
||||
}
|
||||
return response([
|
||||
'data' => $plan
|
||||
|
@ -3,7 +3,9 @@
|
||||
namespace App\Http\Controllers\User;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\User;
|
||||
use App\Services\TelegramService;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class TelegramController extends Controller
|
||||
{
|
||||
@ -17,4 +19,9 @@ class TelegramController extends Controller
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
public function unbind(Request $request)
|
||||
{
|
||||
$user = User::where('user_id', $request->session()->get('id'))->first();
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ class TicketController extends Controller
|
||||
->where('user_id', $request->session()->get('id'))
|
||||
->first();
|
||||
if (!$ticket) {
|
||||
abort(500, __('user.ticket.fetch.ticket_not_exist'));
|
||||
abort(500, __('Ticket does not exist'));
|
||||
}
|
||||
$ticket['message'] = TicketMessage::where('ticket_id', $ticket->id)->get();
|
||||
for ($i = 0; $i < count($ticket['message']); $i++) {
|
||||
@ -56,7 +56,7 @@ class TicketController extends Controller
|
||||
{
|
||||
DB::beginTransaction();
|
||||
if ((int)Ticket::where('status', 0)->where('user_id', $request->session()->get('id'))->count()) {
|
||||
abort(500, __('user.ticket.save.exist_other_open_ticket'));
|
||||
abort(500, __('There are other unresolved tickets'));
|
||||
}
|
||||
$ticket = Ticket::create(array_merge($request->only([
|
||||
'subject',
|
||||
@ -67,7 +67,7 @@ class TicketController extends Controller
|
||||
]));
|
||||
if (!$ticket) {
|
||||
DB::rollback();
|
||||
abort(500, __('user.ticket.save.ticket_create_failed'));
|
||||
abort(500, __('Failed to open ticket'));
|
||||
}
|
||||
$ticketMessage = TicketMessage::create([
|
||||
'user_id' => $request->session()->get('id'),
|
||||
@ -76,7 +76,7 @@ class TicketController extends Controller
|
||||
]);
|
||||
if (!$ticketMessage) {
|
||||
DB::rollback();
|
||||
abort(500, __('user.ticket.save.ticket_create_failed'));
|
||||
abort(500, __('Failed to open ticket'));
|
||||
}
|
||||
DB::commit();
|
||||
$this->sendNotify($ticket, $ticketMessage);
|
||||
@ -88,22 +88,22 @@ class TicketController extends Controller
|
||||
public function reply(Request $request)
|
||||
{
|
||||
if (empty($request->input('id'))) {
|
||||
abort(500, __('user.ticket.reply.params_wrong'));
|
||||
abort(500, __('Invalid parameter'));
|
||||
}
|
||||
if (empty($request->input('message'))) {
|
||||
abort(500, __('user.ticket.reply.message_not_empty'));
|
||||
abort(500, __('Message cannot be empty'));
|
||||
}
|
||||
$ticket = Ticket::where('id', $request->input('id'))
|
||||
->where('user_id', $request->session()->get('id'))
|
||||
->first();
|
||||
if (!$ticket) {
|
||||
abort(500, __('user.ticket.reply.ticket_not_exist'));
|
||||
abort(500, __('Ticket does not exist'));
|
||||
}
|
||||
if ($ticket->status) {
|
||||
abort(500, __('user.ticket.reply.ticket_close_not_reply'));
|
||||
abort(500, __('The ticket is closed and cannot be replied'));
|
||||
}
|
||||
if ($request->session()->get('id') == $this->getLastMessage($ticket->id)->user_id) {
|
||||
abort(500, __('user.ticket.reply.wait_reply'));
|
||||
abort(500, __('Please wait for the technical enginneer to reply'));
|
||||
}
|
||||
DB::beginTransaction();
|
||||
$ticketMessage = TicketMessage::create([
|
||||
@ -114,7 +114,7 @@ class TicketController extends Controller
|
||||
$ticket->last_reply_user_id = $request->session()->get('id');
|
||||
if (!$ticketMessage || !$ticket->save()) {
|
||||
DB::rollback();
|
||||
abort(500, __('user.ticket.reply.ticket_reply_failed'));
|
||||
abort(500, __('Ticket reply failed'));
|
||||
}
|
||||
DB::commit();
|
||||
$this->sendNotify($ticket, $ticketMessage);
|
||||
@ -127,17 +127,17 @@ class TicketController extends Controller
|
||||
public function close(Request $request)
|
||||
{
|
||||
if (empty($request->input('id'))) {
|
||||
abort(500, __('user.ticket.close.params_wrong'));
|
||||
abort(500, __('Invalid parameter'));
|
||||
}
|
||||
$ticket = Ticket::where('id', $request->input('id'))
|
||||
->where('user_id', $request->session()->get('id'))
|
||||
->first();
|
||||
if (!$ticket) {
|
||||
abort(500, __('user.ticket.close.ticket_not_exist'));
|
||||
abort(500, __('Ticket does not exist'));
|
||||
}
|
||||
$ticket->status = 1;
|
||||
if (!$ticket->save()) {
|
||||
abort(500, __('user.ticket.close.close_failed'));
|
||||
abort(500, __('Close failed'));
|
||||
}
|
||||
return response([
|
||||
'data' => true
|
||||
@ -163,15 +163,15 @@ class TicketController extends Controller
|
||||
Dict::WITHDRAW_METHOD_WHITELIST_DEFAULT
|
||||
)
|
||||
)) {
|
||||
abort(500, __('user.ticket.withdraw.not_support_withdraw_method'));
|
||||
abort(500, __('Unsupported withdrawal method'));
|
||||
}
|
||||
$user = User::find($request->session()->get('id'));
|
||||
$limit = config('v2board.commission_withdraw_limit', 100);
|
||||
if ($limit > ($user->commission_balance / 100)) {
|
||||
abort(500, __('user.ticket.withdraw.system_require_withdraw_limit', ['limit' => $limit]));
|
||||
abort(500, __('The current required minimum withdrawal commission is', ['limit' => $limit]));
|
||||
}
|
||||
DB::beginTransaction();
|
||||
$subject = __('user.ticket.withdraw.ticket_subject');
|
||||
$subject = __('[Commission Withdrawal Request] This ticket is opened by the system');
|
||||
$ticket = Ticket::create([
|
||||
'subject' => $subject,
|
||||
'level' => 2,
|
||||
@ -180,12 +180,12 @@ class TicketController extends Controller
|
||||
]);
|
||||
if (!$ticket) {
|
||||
DB::rollback();
|
||||
abort(500, __('user.ticket.withdraw.ticket_create_failed'));
|
||||
abort(500, __('Failed to open ticket'));
|
||||
}
|
||||
$message = __('user.ticket.withdraw.ticket_message', [
|
||||
'method' => $request->input('withdraw_method'),
|
||||
'account' => $request->input('withdraw_account')
|
||||
]);
|
||||
$message = sprintf("%s\r\n%s",
|
||||
__('Withdrawal method') . ":" . $request->input('withdraw_method'),
|
||||
__('Withdrawal account') . ":" . $request->input('withdraw_account')
|
||||
);
|
||||
$ticketMessage = TicketMessage::create([
|
||||
'user_id' => $request->session()->get('id'),
|
||||
'ticket_id' => $ticket->id,
|
||||
@ -193,7 +193,7 @@ class TicketController extends Controller
|
||||
]);
|
||||
if (!$ticketMessage) {
|
||||
DB::rollback();
|
||||
abort(500, __('user.ticket.withdraw.ticket_create_failed'));
|
||||
abort(500, __('Failed to open ticket'));
|
||||
}
|
||||
DB::commit();
|
||||
$this->sendNotify($ticket, $ticketMessage);
|
||||
|
@ -3,6 +3,7 @@
|
||||
namespace App\Http\Controllers\User;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\User\UserTransfer;
|
||||
use App\Http\Requests\User\UserUpdate;
|
||||
use App\Http\Requests\User\UserChangePassword;
|
||||
use Illuminate\Http\Request;
|
||||
@ -28,19 +29,19 @@ class UserController extends Controller
|
||||
{
|
||||
$user = User::find($request->session()->get('id'));
|
||||
if (!$user) {
|
||||
abort(500, __('user.user.changePassword.user_not_exist'));
|
||||
abort(500, __('The user does not exist'));
|
||||
}
|
||||
if (!Helper::multiPasswordVerify(
|
||||
$user->password_algo,
|
||||
$request->input('old_password'),
|
||||
$user->password)
|
||||
) {
|
||||
abort(500, __('user.user.changePassword.old_password_wrong'));
|
||||
abort(500, __('The old password is wrong'));
|
||||
}
|
||||
$user->password = password_hash($request->input('new_password'), PASSWORD_DEFAULT);
|
||||
$user->password_algo = NULL;
|
||||
if (!$user->save()) {
|
||||
abort(500, __('user.user.changePassword.save_failed'));
|
||||
abort(500, __('Save failed'));
|
||||
}
|
||||
$request->session()->flush();
|
||||
return response([
|
||||
@ -69,7 +70,7 @@ class UserController extends Controller
|
||||
])
|
||||
->first();
|
||||
if (!$user) {
|
||||
abort(500, __('user.user.info.user_not_exist'));
|
||||
abort(500, __('The user does not exist'));
|
||||
}
|
||||
$user['avatar_url'] = 'https://cdn.v2ex.com/gravatar/' . md5($user->email) . '?s=64&d=identicon';
|
||||
return response([
|
||||
@ -109,15 +110,20 @@ class UserController extends Controller
|
||||
])
|
||||
->first();
|
||||
if (!$user) {
|
||||
abort(500, __('user.user.getSubscribe.user_not_exist'));
|
||||
abort(500, __('The user does not exist'));
|
||||
}
|
||||
if ($user->plan_id) {
|
||||
$user['plan'] = Plan::find($user->plan_id);
|
||||
if (!$user['plan']) {
|
||||
abort(500, __('user.user.getSubscribe.plan_not_exist'));
|
||||
abort(500, __('Subscription plan does not exist'));
|
||||
}
|
||||
}
|
||||
$user['subscribe_url'] = config('v2board.subscribe_url', config('v2board.app_url', env('APP_URL'))) . '/api/v1/client/subscribe?token=' . $user['token'];
|
||||
$subscribeUrl = config('v2board.app_url', env('APP_URL'));
|
||||
$subscribeUrls = explode(',', config('v2board.subscribe_url'));
|
||||
if ($subscribeUrls) {
|
||||
$subscribeUrl = $subscribeUrls[rand(0, count($subscribeUrls) - 1)];
|
||||
}
|
||||
$user['subscribe_url'] = "{$subscribeUrl}/api/v1/client/subscribe?token={$user['token']}";
|
||||
$user['reset_day'] = $this->getResetDay($user);
|
||||
return response([
|
||||
'data' => $user
|
||||
@ -128,12 +134,12 @@ class UserController extends Controller
|
||||
{
|
||||
$user = User::find($request->session()->get('id'));
|
||||
if (!$user) {
|
||||
abort(500, __('user.user.resetSecurity.user_not_exist'));
|
||||
abort(500, __('The user does not exist'));
|
||||
}
|
||||
$user->uuid = Helper::guid(true);
|
||||
$user->token = Helper::guid();
|
||||
if (!$user->save()) {
|
||||
abort(500, __('user.user.resetSecurity.reset_failed'));
|
||||
abort(500, __('Reset failed'));
|
||||
}
|
||||
return response([
|
||||
'data' => config('v2board.subscribe_url', config('v2board.app_url', env('APP_URL'))) . '/api/v1/client/subscribe?token=' . $user->token
|
||||
@ -149,12 +155,12 @@ class UserController extends Controller
|
||||
|
||||
$user = User::find($request->session()->get('id'));
|
||||
if (!$user) {
|
||||
abort(500, __('user.user.update.user_not_exist'));
|
||||
abort(500, __('The user does not exist'));
|
||||
}
|
||||
try {
|
||||
$user->update($updateData);
|
||||
} catch (\Exception $e) {
|
||||
abort(500, __('user.user.update.save_failed'));
|
||||
abort(500, __('Save failed'));
|
||||
}
|
||||
|
||||
return response([
|
||||
@ -162,22 +168,19 @@ class UserController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
public function transfer(Request $request)
|
||||
public function transfer(UserTransfer $request)
|
||||
{
|
||||
$user = User::find($request->session()->get('id'));
|
||||
if (!$user) {
|
||||
abort(500, __('user.user.transfer.user_not_exist'));
|
||||
}
|
||||
if ($request->input('transfer_amount') <= 0) {
|
||||
abort(500, __('user.user.transfer.params_wrong'));
|
||||
abort(500, __('The user does not exist'));
|
||||
}
|
||||
if ($request->input('transfer_amount') > $user->commission_balance) {
|
||||
abort(500, __('user.user.transfer.insufficient_commission_balance'));
|
||||
abort(500, __('Insufficient commission balance'));
|
||||
}
|
||||
$user->commission_balance = $user->commission_balance - $request->input('transfer_amount');
|
||||
$user->balance = $user->balance + $request->input('transfer_amount');
|
||||
if (!$user->save()) {
|
||||
abort(500, __('user.user.transfer.transfer_failed'));
|
||||
abort(500, __('Transfer failed'));
|
||||
}
|
||||
return response([
|
||||
'data' => true
|
||||
|
@ -15,19 +15,16 @@ class User
|
||||
*/
|
||||
public function handle($request, Closure $next)
|
||||
{
|
||||
if ($request->input('access_token')) {
|
||||
$user = \App\Models\User::where('token', $request->input('access_token'))->first();
|
||||
if ($user) {
|
||||
$request->session()->put('email', $user->email);
|
||||
$request->session()->put('id', $user->id);
|
||||
}
|
||||
if ($request->input('auth_data')) {
|
||||
$authData = explode(':', base64_decode($request->input('auth_data')));
|
||||
if (!isset($authData[1]) || !isset($authData[0])) abort(403, '鉴权失败,请重新登入');
|
||||
$user = \App\Models\User::where('password', $authData[1])
|
||||
->where('email', $authData[0])
|
||||
->first();
|
||||
if (!$user) abort(403, '鉴权失败,请重新登入');
|
||||
$request->session()->put('email', $user->email);
|
||||
$request->session()->put('id', $user->id);
|
||||
}
|
||||
// if ($request->input('lang')) {
|
||||
// $request->session()->put('lang', $request->input('lang'));
|
||||
// }
|
||||
// if ($request->session()->get('lang')) {
|
||||
// App::setLocale($request->session()->get('lang'));
|
||||
// }
|
||||
if (!$request->session()->get('id')) {
|
||||
abort(403, '未登录或登陆已过期');
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ class ConfigSave extends FormRequest
|
||||
'app_name' => '',
|
||||
'app_description' => '',
|
||||
'app_url' => 'nullable|url',
|
||||
'subscribe_url' => 'nullable|url',
|
||||
'subscribe_url' => 'nullable',
|
||||
'try_out_enable' => 'in:0,1',
|
||||
'try_out_plan_id' => 'integer',
|
||||
'try_out_hour' => 'numeric',
|
||||
@ -41,11 +41,14 @@ class ConfigSave extends FormRequest
|
||||
'recaptcha_enable' => 'in:0,1',
|
||||
'recaptcha_key' => '',
|
||||
'recaptcha_site_key' => '',
|
||||
'tos_url' => 'nullable|url',
|
||||
// subscribe
|
||||
'plan_change_enable' => 'in:0,1',
|
||||
'reset_traffic_method' => 'in:0,1',
|
||||
'renew_reset_traffic_enable' => 'in:0,1',
|
||||
'surplus_enable' => 'in:0,1',
|
||||
'new_order_event_id' => 'in:0,1',
|
||||
'renew_order_event_id' => 'in:0,1',
|
||||
'change_order_event_id' => 'in:0,1',
|
||||
// server
|
||||
'server_token' => 'nullable|min:16',
|
||||
'server_license' => 'nullable',
|
||||
@ -82,11 +85,14 @@ class ConfigSave extends FormRequest
|
||||
'epay_pid' => '',
|
||||
'epay_key' => '',
|
||||
// frontend
|
||||
'frontend_theme' => '',
|
||||
'frontend_theme_sidebar' => 'in:dark,light',
|
||||
'frontend_theme_header' => 'in:dark,light',
|
||||
'frontend_theme_color' => 'in:default,darkblue,black',
|
||||
'frontend_background_url' => 'nullable|url',
|
||||
'frontend_admin_path' => '',
|
||||
'frontend_customer_service_method' => '',
|
||||
'frontend_customer_service_id' => '',
|
||||
// tutorial
|
||||
'apple_id' => 'nullable|email',
|
||||
'apple_id_password' => '',
|
||||
@ -118,7 +124,9 @@ class ConfigSave extends FormRequest
|
||||
// illiteracy prompt
|
||||
return [
|
||||
'app_url.url' => '站点URL格式不正确,必须携带http(s)://',
|
||||
'subscribe_url.url' => '订阅URL格式不正确,必须携带http(s)://'
|
||||
'subscribe_url.url' => '订阅URL格式不正确,必须携带http(s)://',
|
||||
'server_token.min' => '通讯密钥长度必须大于16位',
|
||||
'tos_url.url' => '服务条款URL格式不正确'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
32
app/Http/Requests/Admin/OrderFetch.php
Normal file
32
app/Http/Requests/Admin/OrderFetch.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\Admin;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class OrderFetch extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'filter.*.key' => 'required|in:email,trade_no,status,commission_status,user_id,invite_user_id',
|
||||
'filter.*.condition' => 'required|in:>,<,=,>=,<=,模糊,!=',
|
||||
'filter.*.value' => ''
|
||||
];
|
||||
}
|
||||
|
||||
public function messages()
|
||||
{
|
||||
return [
|
||||
'filter.*.key.required' => '过滤键不能为空',
|
||||
'filter.*.key.in' => '过滤键参数有误',
|
||||
'filter.*.condition.required' => '过滤条件不能为空',
|
||||
'filter.*.condition.in' => '过滤条件参数有误',
|
||||
];
|
||||
}
|
||||
}
|
@ -25,7 +25,7 @@ class ServerV2raySave extends FormRequest
|
||||
'tags' => 'nullable|array',
|
||||
'rate' => 'required|numeric',
|
||||
'alter_id' => 'required|integer',
|
||||
'network' => 'required|in:tcp,kcp,ws,http,domainsocket,quic',
|
||||
'network' => 'required|in:tcp,kcp,ws,http,domainsocket,quic,grpc',
|
||||
'networkSettings' => '',
|
||||
'ruleSettings' => '',
|
||||
'tlsSettings' => '',
|
||||
|
@ -14,8 +14,8 @@ class UserFetch extends FormRequest
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'filter.*.key' => 'required|in:id,email,transfer_enable,d,expired_at,uuid,token,invite_by_email,invite_user_id,plan_id,banned',
|
||||
'filter.*.condition' => 'required|in:>,<,=,>=,<=,模糊',
|
||||
'filter.*.key' => 'required|in:id,email,transfer_enable,d,expired_at,uuid,token,invite_by_email,invite_user_id,plan_id,banned,remarks',
|
||||
'filter.*.condition' => 'required|in:>,<,=,>=,<=,模糊,!=',
|
||||
'filter.*.value' => 'required'
|
||||
];
|
||||
}
|
||||
@ -23,6 +23,11 @@ class UserFetch extends FormRequest
|
||||
public function messages()
|
||||
{
|
||||
return [
|
||||
'filter.*.key.required' => '过滤键不能为空',
|
||||
'filter.*.key.in' => '过滤键参数有误',
|
||||
'filter.*.condition.required' => '过滤条件不能为空',
|
||||
'filter.*.condition.in' => '过滤条件参数有误',
|
||||
'filter.*.value.required' => '过滤值不能为空'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ class UserUpdate extends FormRequest
|
||||
{
|
||||
return [
|
||||
'email' => 'required|email',
|
||||
'password' => 'nullable',
|
||||
'password' => 'nullable|min:8',
|
||||
'transfer_enable' => 'numeric',
|
||||
'expired_at' => 'nullable|integer',
|
||||
'banned' => 'required|in:0,1',
|
||||
@ -27,6 +27,7 @@ class UserUpdate extends FormRequest
|
||||
'u' => 'integer',
|
||||
'd' => 'integer',
|
||||
'balance' => 'integer',
|
||||
'commission_type' => 'integer',
|
||||
'commission_balance' => 'integer',
|
||||
'remarks' => 'nullable'
|
||||
];
|
||||
@ -57,7 +58,8 @@ class UserUpdate extends FormRequest
|
||||
'u.integer' => '上行流量格式不正确',
|
||||
'd.integer' => '下行流量格式不正确',
|
||||
'balance.integer' => '余额格式不正确',
|
||||
'commission_balance.integer' => '佣金格式不正确'
|
||||
'commission_balance.integer' => '佣金格式不正确',
|
||||
'password.min' => '密码长度最小8位'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -23,11 +23,11 @@ class AuthForget extends FormRequest
|
||||
public function messages()
|
||||
{
|
||||
return [
|
||||
'email.required' => '邮箱不能为空',
|
||||
'email.email' => '邮箱格式不正确',
|
||||
'password.required' => '密码不能为空',
|
||||
'password.min' => '密码必须大于8位数',
|
||||
'email_code.required' => '邮箱验证码不能为空'
|
||||
'email.required' => __('Email can not be empty'),
|
||||
'email.email' => __('Email format is incorrect'),
|
||||
'password.required' => __('Password can not be empty'),
|
||||
'password.min' => __('Password must be greater than 8 digits'),
|
||||
'email_code.required' => __('Email verification code cannot be empty')
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -22,10 +22,10 @@ class AuthLogin extends FormRequest
|
||||
public function messages()
|
||||
{
|
||||
return [
|
||||
'email.required' => '邮箱不能为空',
|
||||
'email.email' => '邮箱格式不正确',
|
||||
'password.required' => '密码不能为空',
|
||||
'password.min' => '密码必须大于8位数'
|
||||
'email.required' => __('Email can not be empty'),
|
||||
'email.email' => __('Email format is incorrect'),
|
||||
'password.required' => __('Password can not be empty'),
|
||||
'password.min' => __('Password must be greater than 8 digits')
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -22,10 +22,10 @@ class AuthRegister extends FormRequest
|
||||
public function messages()
|
||||
{
|
||||
return [
|
||||
'email.required' => '邮箱不能为空',
|
||||
'email.email' => '邮箱格式不正确',
|
||||
'password.required' => '密码不能为空',
|
||||
'password.min' => '密码必须大于8位数'
|
||||
'email.required' => __('Email can not be empty'),
|
||||
'email.email' => __('Email format is incorrect'),
|
||||
'password.required' => __('Password can not be empty'),
|
||||
'password.min' => __('Password must be greater than 8 digits')
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -21,8 +21,8 @@ class CommSendEmailVerify extends FormRequest
|
||||
public function messages()
|
||||
{
|
||||
return [
|
||||
'email.required' => '邮箱不能为空',
|
||||
'email.email' => '邮箱格式不正确'
|
||||
'email.required' => __('Email can not be empty'),
|
||||
'email.email' => __('Email format is incorrect')
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -22,9 +22,9 @@ class OrderSave extends FormRequest
|
||||
public function messages()
|
||||
{
|
||||
return [
|
||||
'plan_id.required' => '套餐ID不能为空',
|
||||
'cycle.required' => '套餐周期不能为空',
|
||||
'cycle.in' => '套餐周期有误'
|
||||
'plan_id.required' => __('Plan ID cannot be empty'),
|
||||
'cycle.required' => __('Plan cycle cannot be empty'),
|
||||
'cycle.in' => __('Wrong plan cycle')
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -23,10 +23,10 @@ class TicketSave extends FormRequest
|
||||
public function messages()
|
||||
{
|
||||
return [
|
||||
'subject.required' => '工单主题不能为空',
|
||||
'level.required' => '工单级别不能为空',
|
||||
'level.in' => '工单级别格式不正确',
|
||||
'message.required' => '消息不能为空'
|
||||
'subject.required' => __('Ticket subject cannot be empty'),
|
||||
'level.required' => __('Ticket level cannot be empty'),
|
||||
'level.in' => __('Incorrect ticket level format'),
|
||||
'message.required' => __('Message cannot be empty')
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -22,9 +22,8 @@ class TicketWithdraw extends FormRequest
|
||||
public function messages()
|
||||
{
|
||||
return [
|
||||
'withdraw_method.required' => '提现方式不能为空',
|
||||
'withdraw_method.in' => '提现方式不支持',
|
||||
'withdraw_account.required' => '提现账号不能为空'
|
||||
'withdraw_method.required' => __('The withdrawal method cannot be empty'),
|
||||
'withdraw_account.required' => __('The withdrawal account cannot be empty')
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -22,9 +22,9 @@ class UserChangePassword extends FormRequest
|
||||
public function messages()
|
||||
{
|
||||
return [
|
||||
'old_password.required' => '旧密码不能为空',
|
||||
'new_password.required' => '新密码不能为空',
|
||||
'new_password.min' => '密码必须大于8位数'
|
||||
'old_password.required' => __('Old password cannot be empty'),
|
||||
'new_password.required' => __('New password cannot be empty'),
|
||||
'new_password.min' => __('Password must be greater than 8 digits')
|
||||
];
|
||||
}
|
||||
}
|
||||
|
29
app/Http/Requests/User/UserTransfer.php
Normal file
29
app/Http/Requests/User/UserTransfer.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\User;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class UserTransfer extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'transfer_amount' => 'required|integer|min:1'
|
||||
];
|
||||
}
|
||||
|
||||
public function messages()
|
||||
{
|
||||
return [
|
||||
'transfer_amount.required' => __('The transfer amount cannot be empty'),
|
||||
'transfer_amount.integer' => __('The transfer amount parameter is wrong'),
|
||||
'transfer_amount.min' => __('The transfer amount parameter is wrong')
|
||||
];
|
||||
}
|
||||
}
|
@ -22,8 +22,8 @@ class UserUpdate extends FormRequest
|
||||
public function messages()
|
||||
{
|
||||
return [
|
||||
'show.in' => '过期提醒格式不正确',
|
||||
'renew.in' => '流量提醒格式不正确'
|
||||
'show.in' => __('Incorrect format of expiration reminder'),
|
||||
'renew.in' => __('Incorrect traffic alert format')
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ class AdminRoute
|
||||
$router->get ('/config/fetch', 'Admin\\ConfigController@fetch');
|
||||
$router->post('/config/save', 'Admin\\ConfigController@save');
|
||||
$router->get ('/config/getEmailTemplate', 'Admin\\ConfigController@getEmailTemplate');
|
||||
$router->get ('/config/getThemeTemplate', 'Admin\\ConfigController@getThemeTemplate');
|
||||
$router->post('/config/setTelegramWebhook', 'Admin\\ConfigController@setTelegramWebhook');
|
||||
// Plan
|
||||
$router->get ('/plan/fetch', 'Admin\\PlanController@fetch');
|
||||
@ -74,6 +75,7 @@ class AdminRoute
|
||||
$router->post('/user/sendMail', 'Admin\\UserController@sendMail');
|
||||
$router->post('/user/ban', 'Admin\\UserController@ban');
|
||||
$router->post('/user/resetSecret', 'Admin\\UserController@resetSecret');
|
||||
$router->post('/user/setInviteUser', 'Admin\\UserController@setInviteUser');
|
||||
// StatOrder
|
||||
$router->get ('/stat/getOverride', 'Admin\\StatController@getOverride');
|
||||
$router->get ('/stat/getServerLastRank', 'Admin\\StatController@getServerLastRank');
|
||||
@ -98,6 +100,12 @@ class AdminRoute
|
||||
$router->post('/knowledge/show', 'Admin\\KnowledgeController@show');
|
||||
$router->post('/knowledge/drop', 'Admin\\KnowledgeController@drop');
|
||||
$router->post('/knowledge/sort', 'Admin\\KnowledgeController@sort');
|
||||
// Payment
|
||||
$router->get ('/payment/fetch', 'Admin\\PaymentController@fetch');
|
||||
$router->get ('/payment/getPaymentMethods', 'Admin\\PaymentController@getPaymentMethods');
|
||||
$router->post('/payment/getPaymentForm', 'Admin\\PaymentController@getPaymentForm');
|
||||
$router->post('/payment/save', 'Admin\\PaymentController@save');
|
||||
$router->post('/payment/drop', 'Admin\\PaymentController@drop');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -12,14 +12,12 @@ class GuestRoute
|
||||
], function ($router) {
|
||||
// Plan
|
||||
$router->get ('/plan/fetch', 'Guest\\PlanController@fetch');
|
||||
// Order
|
||||
$router->post('/order/alipayNotify', 'Guest\\OrderController@alipayNotify');
|
||||
$router->post('/order/stripeNotify', 'Guest\\OrderController@stripeNotify');
|
||||
$router->post('/order/bitpayXNotify', 'Guest\\OrderController@bitpayXNotify');
|
||||
$router->post('/order/mgateNotify', 'Guest\\OrderController@mgateNotify');
|
||||
$router->post('/order/epayNotify', 'Guest\\OrderController@epayNotify');
|
||||
// Telegram
|
||||
$router->post('/telegram/webhook', 'Guest\\TelegramController@webhook');
|
||||
// Payment
|
||||
$router->match(['get', 'post'], '/payment/notify/{method}/{uuid}', 'Guest\\PaymentController@notify');
|
||||
// Comm
|
||||
$router->get ('/comm/config', 'Guest\\CommController@config');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -51,6 +51,7 @@ class UserRoute
|
||||
$router->get ('/telegram/getBotInfo', 'User\\TelegramController@getBotInfo');
|
||||
// Comm
|
||||
$router->get ('/comm/config', 'User\\CommController@config');
|
||||
$router->Post('/comm/getStripePublicKey', 'User\\CommController@getStripePublicKey');
|
||||
// Knowledge
|
||||
$router->get ('/knowledge/fetch', 'User\\KnowledgeController@fetch');
|
||||
$router->get ('/knowledge/getCategory', 'User\\KnowledgeController@getCategory');
|
||||
|
12
app/Models/Payment.php
Normal file
12
app/Models/Payment.php
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Payment extends Model
|
||||
{
|
||||
protected $table = 'v2_payment';
|
||||
protected $dateFormat = 'U';
|
||||
protected $guarded = ['id'];
|
||||
}
|
96
app/Payments/AlipayF2F.php
Normal file
96
app/Payments/AlipayF2F.php
Normal file
@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 自己写别抄,抄NMB抄
|
||||
*/
|
||||
namespace App\Payments;
|
||||
|
||||
use Omnipay\Omnipay;
|
||||
|
||||
class AlipayF2F {
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public function form()
|
||||
{
|
||||
return [
|
||||
'app_id' => [
|
||||
'label' => '支付宝APPID',
|
||||
'description' => '',
|
||||
'type' => 'input',
|
||||
],
|
||||
'private_key' => [
|
||||
'label' => '支付宝私钥',
|
||||
'description' => '',
|
||||
'type' => 'input',
|
||||
],
|
||||
'public_key' => [
|
||||
'label' => '支付宝公钥',
|
||||
'description' => '',
|
||||
'type' => 'input',
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
public function pay($order)
|
||||
{
|
||||
$gateway = Omnipay::create('Alipay_AopF2F');
|
||||
$gateway->setSignType('RSA2'); //RSA/RSA2
|
||||
$gateway->setAppId($this->config['app_id']);
|
||||
$gateway->setPrivateKey($this->config['private_key']); // 可以是路径,也可以是密钥内容
|
||||
$gateway->setAlipayPublicKey($this->config['public_key']); // 可以是路径,也可以是密钥内容
|
||||
$gateway->setNotifyUrl($order['notify_url']);
|
||||
$request = $gateway->purchase();
|
||||
$request->setBizContent([
|
||||
'subject' => config('v2board.app_name', 'V2Board') . ' - 订阅',
|
||||
'out_trade_no' => $order['trade_no'],
|
||||
'total_amount' => $order['total_amount'] / 100
|
||||
]);
|
||||
/** @var \Omnipay\Alipay\Responses\AopTradePreCreateResponse $response */
|
||||
$response = $request->send();
|
||||
$result = $response->getAlipayResponse();
|
||||
if ($result['code'] !== '10000') {
|
||||
abort(500, $result['sub_msg']);
|
||||
}
|
||||
return [
|
||||
'type' => 0, // 0:qrcode 1:url
|
||||
'data' => $response->getQrCode()
|
||||
];
|
||||
}
|
||||
|
||||
public function notify($params)
|
||||
{
|
||||
$gateway = Omnipay::create('Alipay_AopF2F');
|
||||
$gateway->setSignType('RSA2'); //RSA/RSA2
|
||||
$gateway->setAppId($this->config['app_id']);
|
||||
$gateway->setPrivateKey($this->config['private_key']); // 可以是路径,也可以是密钥内容
|
||||
$gateway->setAlipayPublicKey($this->config['public_key']); // 可以是路径,也可以是密钥内容
|
||||
$request = $gateway->completePurchase();
|
||||
$request->setParams($_POST); //Optional
|
||||
try {
|
||||
/** @var \Omnipay\Alipay\Responses\AopCompletePurchaseResponse $response */
|
||||
$response = $request->send();
|
||||
if ($response->isPaid()) {
|
||||
/**
|
||||
* Payment is successful
|
||||
*/
|
||||
return [
|
||||
'trade_no' => $params['out_trade_no'],
|
||||
'callback_no' => $params['trade_no']
|
||||
];
|
||||
} else {
|
||||
/**
|
||||
* Payment is not successful
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
/**
|
||||
* Payment is not successful
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
69
app/Payments/EPay.php
Normal file
69
app/Payments/EPay.php
Normal file
@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace App\Payments;
|
||||
|
||||
class EPay {
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public function form()
|
||||
{
|
||||
return [
|
||||
'url' => [
|
||||
'label' => 'URL',
|
||||
'description' => '',
|
||||
'type' => 'input',
|
||||
],
|
||||
'pid' => [
|
||||
'label' => 'PID',
|
||||
'description' => '',
|
||||
'type' => 'input',
|
||||
],
|
||||
'key' => [
|
||||
'label' => 'KEY',
|
||||
'description' => '',
|
||||
'type' => 'input',
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
public function pay($order)
|
||||
{
|
||||
$params = [
|
||||
'money' => $order['total_amount'] / 100,
|
||||
'name' => $order['trade_no'],
|
||||
'notify_url' => $order['notify_url'],
|
||||
'return_url' => $order['return_url'],
|
||||
'out_trade_no' => $order['trade_no'],
|
||||
'pid' => $this->config['pid']
|
||||
];
|
||||
ksort($params);
|
||||
reset($params);
|
||||
$str = stripslashes(urldecode(http_build_query($params))) . $this->config['key'];
|
||||
$params['sign'] = md5($str);
|
||||
$params['sign_type'] = 'MD5';
|
||||
return [
|
||||
'type' => 1, // 0:qrcode 1:url
|
||||
'data' => $this->config['url'] . '/submit.php?' . http_build_query($params)
|
||||
];
|
||||
}
|
||||
|
||||
public function notify($params)
|
||||
{
|
||||
$sign = $params['sign'];
|
||||
unset($params['sign']);
|
||||
unset($params['sign_type']);
|
||||
ksort($params);
|
||||
reset($params);
|
||||
$str = stripslashes(urldecode(http_build_query($params))) . $this->config['key'];
|
||||
if ($sign !== md5($str)) {
|
||||
return false;
|
||||
}
|
||||
return [
|
||||
'trade_no' => $params['out_trade_no'],
|
||||
'callback_no' => $params['trade_no']
|
||||
];
|
||||
}
|
||||
}
|
90
app/Payments/MGate.php
Normal file
90
app/Payments/MGate.php
Normal file
@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 自己写别抄,抄NMB抄
|
||||
*/
|
||||
namespace App\Payments;
|
||||
|
||||
use \Curl\Curl;
|
||||
|
||||
class MGate {
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public function form()
|
||||
{
|
||||
return [
|
||||
'mgate_url' => [
|
||||
'label' => 'API地址',
|
||||
'description' => '',
|
||||
'type' => 'input',
|
||||
],
|
||||
'mgate_app_id' => [
|
||||
'label' => 'APPID',
|
||||
'description' => '',
|
||||
'type' => 'input',
|
||||
],
|
||||
'mgate_app_secret' => [
|
||||
'label' => 'AppSecret',
|
||||
'description' => '',
|
||||
'type' => 'input',
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
public function pay($order)
|
||||
{
|
||||
$params = [
|
||||
'out_trade_no' => $order['trade_no'],
|
||||
'total_amount' => $order['total_amount'],
|
||||
'notify_url' => $order['notify_url'],
|
||||
'return_url' => $order['return_url']
|
||||
];
|
||||
$params['app_id'] = $this->config['mgate_app_id'];
|
||||
ksort($params);
|
||||
$str = http_build_query($params) . $this->config['mgate_app_secret'];
|
||||
$params['sign'] = md5($str);
|
||||
$curl = new Curl();
|
||||
$curl->post($this->config['mgate_url'] . '/v1/gateway/fetch', http_build_query($params));
|
||||
$result = $curl->response;
|
||||
if (!$result) {
|
||||
abort(500, '网络异常');
|
||||
}
|
||||
if ($curl->error) {
|
||||
if (isset($result->errors)) {
|
||||
$errors = (array)$result->errors;
|
||||
abort(500, $errors[array_keys($errors)[0]][0]);
|
||||
}
|
||||
if (isset($result->message)) {
|
||||
abort(500, $result->message);
|
||||
}
|
||||
abort(500, '未知错误');
|
||||
}
|
||||
$curl->close();
|
||||
if (!isset($result->data->trade_no)) {
|
||||
abort(500, '接口请求失败');
|
||||
}
|
||||
return [
|
||||
'type' => 1, // 0:qrcode 1:url
|
||||
'data' => $result->data->pay_url
|
||||
];
|
||||
}
|
||||
|
||||
public function notify($params)
|
||||
{
|
||||
$sign = $params['sign'];
|
||||
unset($params['sign']);
|
||||
ksort($params);
|
||||
reset($params);
|
||||
$str = http_build_query($params) . $this->config['mgate_app_secret'];
|
||||
if ($sign !== md5($str)) {
|
||||
return false;
|
||||
}
|
||||
return [
|
||||
'trade_no' => $params['out_trade_no'],
|
||||
'callback_no' => $params['trade_no']
|
||||
];
|
||||
}
|
||||
}
|
114
app/Payments/StripeAlipay.php
Normal file
114
app/Payments/StripeAlipay.php
Normal file
@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 自己写别抄,抄NMB抄
|
||||
*/
|
||||
namespace App\Payments;
|
||||
|
||||
use Stripe\Source;
|
||||
use Stripe\Stripe;
|
||||
|
||||
class StripeAlipay {
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public function form()
|
||||
{
|
||||
return [
|
||||
'currency' => [
|
||||
'label' => '货币单位',
|
||||
'description' => '',
|
||||
'type' => 'input',
|
||||
],
|
||||
'stripe_sk_live' => [
|
||||
'label' => 'SK_LIVE',
|
||||
'description' => '',
|
||||
'type' => 'input',
|
||||
],
|
||||
'stripe_webhook_key' => [
|
||||
'label' => 'WebHook密钥签名',
|
||||
'description' => '',
|
||||
'type' => 'input',
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
public function pay($order)
|
||||
{
|
||||
$currency = $this->config['currency'];
|
||||
$exchange = $this->exchange('CNY', strtoupper($currency));
|
||||
if (!$exchange) {
|
||||
abort(500, __('user.order.stripeAlipay.currency_convert_timeout'));
|
||||
}
|
||||
Stripe::setApiKey($this->config['stripe_sk_live']);
|
||||
$source = Source::create([
|
||||
'amount' => floor($order['total_amount'] * $exchange),
|
||||
'currency' => $currency,
|
||||
'type' => 'alipay',
|
||||
'statement_descriptor' => $order['trade_no'],
|
||||
'metadata' => [
|
||||
'user_id' => $order['user_id'],
|
||||
'out_trade_no' => $order['trade_no'],
|
||||
'identifier' => ''
|
||||
],
|
||||
'redirect' => [
|
||||
'return_url' => $order['return_url']
|
||||
]
|
||||
]);
|
||||
if (!$source['redirect']['url']) {
|
||||
abort(500, __('user.order.stripeAlipay.gateway_request_failed'));
|
||||
}
|
||||
return [
|
||||
'type' => 1,
|
||||
'data' => $source['redirect']['url']
|
||||
];
|
||||
}
|
||||
|
||||
public function notify($params)
|
||||
{
|
||||
\Stripe\Stripe::setApiKey($this->config['stripe_sk_live']);
|
||||
try {
|
||||
$event = \Stripe\Webhook::constructEvent(
|
||||
file_get_contents('php://input'),
|
||||
$_SERVER['HTTP_STRIPE_SIGNATURE'],
|
||||
$this->config['stripe_webhook_key']
|
||||
);
|
||||
} catch (\Stripe\Error\SignatureVerification $e) {
|
||||
abort(400);
|
||||
}
|
||||
switch ($event->type) {
|
||||
case 'source.chargeable':
|
||||
$object = $event->data->object;
|
||||
\Stripe\Charge::create([
|
||||
'amount' => $object->amount,
|
||||
'currency' => $object->currency,
|
||||
'source' => $object->id,
|
||||
'metadata' => json_decode($object->metadata, true)
|
||||
]);
|
||||
break;
|
||||
case 'charge.succeeded':
|
||||
$object = $event->data->object;
|
||||
if ($object->status === 'succeeded') {
|
||||
$metaData = isset($object->metadata->out_trade_no) ? $object->metadata : $object->source->metadata;
|
||||
$tradeNo = $metaData->out_trade_no;
|
||||
return [
|
||||
'trade_no' => $tradeNo,
|
||||
'callback_no' => $object->balance_transaction
|
||||
];
|
||||
}
|
||||
break;
|
||||
default:
|
||||
abort(500, 'event is not support');
|
||||
}
|
||||
die('success');
|
||||
}
|
||||
|
||||
private function exchange($from, $to)
|
||||
{
|
||||
$result = file_get_contents('https://api.exchangerate.host/latest?symbols=' . $to . '&base=' . $from);
|
||||
$result = json_decode($result, true);
|
||||
return $result['rates'][$to];
|
||||
}
|
||||
}
|
121
app/Payments/StripeCredit.php
Normal file
121
app/Payments/StripeCredit.php
Normal file
@ -0,0 +1,121 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 自己写别抄,抄NMB抄
|
||||
*/
|
||||
namespace App\Payments;
|
||||
|
||||
use Stripe\Source;
|
||||
use Stripe\Stripe;
|
||||
|
||||
class StripeCredit {
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public function form()
|
||||
{
|
||||
return [
|
||||
'currency' => [
|
||||
'label' => '货币单位',
|
||||
'description' => '',
|
||||
'type' => 'input',
|
||||
],
|
||||
'stripe_sk_live' => [
|
||||
'label' => 'SK_LIVE',
|
||||
'description' => '',
|
||||
'type' => 'input',
|
||||
],
|
||||
'stripe_pk_live' => [
|
||||
'label' => 'PK_LIVE',
|
||||
'description' => '',
|
||||
'type' => 'input',
|
||||
],
|
||||
'stripe_webhook_key' => [
|
||||
'label' => 'WebHook密钥签名',
|
||||
'description' => '',
|
||||
'type' => 'input',
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
public function pay($order)
|
||||
{
|
||||
info($order);
|
||||
$currency = $this->config['currency'];
|
||||
$exchange = $this->exchange('CNY', strtoupper($currency));
|
||||
if (!$exchange) {
|
||||
abort(500, __('user.order.stripeCard.currency_convert_timeout'));
|
||||
}
|
||||
Stripe::setApiKey($this->config['stripe_sk_live']);
|
||||
try {
|
||||
$charge = \Stripe\Charge::create([
|
||||
'amount' => floor($order['total_amount'] * $exchange),
|
||||
'currency' => $currency,
|
||||
'source' => $order['stripe_token'],
|
||||
'metadata' => [
|
||||
'user_id' => $order['user_id'],
|
||||
'out_trade_no' => $order['trade_no'],
|
||||
'identifier' => ''
|
||||
]
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
info($e);
|
||||
abort(500, __('user.order.stripeCard.was_problem'));
|
||||
}
|
||||
if (!$charge->paid) {
|
||||
abort(500, __('user.order.stripeCard.deduction_failed'));
|
||||
}
|
||||
return [
|
||||
'type' => 2,
|
||||
'data' => $charge->paid
|
||||
];
|
||||
}
|
||||
|
||||
public function notify($params)
|
||||
{
|
||||
\Stripe\Stripe::setApiKey($this->config['stripe_sk_live']);
|
||||
try {
|
||||
$event = \Stripe\Webhook::constructEvent(
|
||||
file_get_contents('php://input'),
|
||||
$_SERVER['HTTP_STRIPE_SIGNATURE'],
|
||||
$this->config['stripe_webhook_key']
|
||||
);
|
||||
} catch (\Stripe\Error\SignatureVerification $e) {
|
||||
abort(400);
|
||||
}
|
||||
switch ($event->type) {
|
||||
case 'source.chargeable':
|
||||
$object = $event->data->object;
|
||||
\Stripe\Charge::create([
|
||||
'amount' => $object->amount,
|
||||
'currency' => $object->currency,
|
||||
'source' => $object->id,
|
||||
'metadata' => json_decode($object->metadata, true)
|
||||
]);
|
||||
break;
|
||||
case 'charge.succeeded':
|
||||
$object = $event->data->object;
|
||||
if ($object->status === 'succeeded') {
|
||||
$metaData = isset($object->metadata->out_trade_no) ? $object->metadata : $object->source->metadata;
|
||||
$tradeNo = $metaData->out_trade_no;
|
||||
return [
|
||||
'trade_no' => $tradeNo,
|
||||
'callback_no' => $object->balance_transaction
|
||||
];
|
||||
}
|
||||
break;
|
||||
default:
|
||||
abort(500, 'event is not support');
|
||||
}
|
||||
die('success');
|
||||
}
|
||||
|
||||
private function exchange($from, $to)
|
||||
{
|
||||
$result = file_get_contents('https://api.exchangerate.host/latest?symbols=' . $to . '&base=' . $from);
|
||||
$result = json_decode($result, true);
|
||||
return $result['rates'][$to];
|
||||
}
|
||||
}
|
114
app/Payments/StripeWepay.php
Normal file
114
app/Payments/StripeWepay.php
Normal file
@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 自己写别抄,抄NMB抄
|
||||
*/
|
||||
namespace App\Payments;
|
||||
|
||||
use Stripe\Source;
|
||||
use Stripe\Stripe;
|
||||
|
||||
class StripeWepay {
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public function form()
|
||||
{
|
||||
return [
|
||||
'currency' => [
|
||||
'label' => '货币单位',
|
||||
'description' => '',
|
||||
'type' => 'input',
|
||||
],
|
||||
'stripe_sk_live' => [
|
||||
'label' => 'SK_LIVE',
|
||||
'description' => '',
|
||||
'type' => 'input',
|
||||
],
|
||||
'stripe_webhook_key' => [
|
||||
'label' => 'WebHook密钥签名',
|
||||
'description' => '',
|
||||
'type' => 'input',
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
public function pay($order)
|
||||
{
|
||||
$currency = $this->config['currency'];
|
||||
$exchange = $this->exchange('CNY', strtoupper($currency));
|
||||
if (!$exchange) {
|
||||
abort(500, __('user.order.stripeAlipay.currency_convert_timeout'));
|
||||
}
|
||||
Stripe::setApiKey($this->config['stripe_sk_live']);
|
||||
$source = Source::create([
|
||||
'amount' => floor($order['total_amount'] * $exchange),
|
||||
'currency' => $currency,
|
||||
'type' => 'wechat',
|
||||
'statement_descriptor' => $order['trade_no'],
|
||||
'metadata' => [
|
||||
'user_id' => $order['user_id'],
|
||||
'out_trade_no' => $order['trade_no'],
|
||||
'identifier' => ''
|
||||
],
|
||||
'redirect' => [
|
||||
'return_url' => $order['return_url']
|
||||
]
|
||||
]);
|
||||
if (!$source['wechat']['qr_code_url']) {
|
||||
abort(500, __('user.order.stripeWepay.gateway_request_failed'));
|
||||
}
|
||||
return [
|
||||
'type' => 0,
|
||||
'data' => $source['wechat']['qr_code_url']
|
||||
];
|
||||
}
|
||||
|
||||
public function notify($params)
|
||||
{
|
||||
\Stripe\Stripe::setApiKey($this->config['stripe_sk_live']);
|
||||
try {
|
||||
$event = \Stripe\Webhook::constructEvent(
|
||||
file_get_contents('php://input'),
|
||||
$_SERVER['HTTP_STRIPE_SIGNATURE'],
|
||||
$this->config['stripe_webhook_key']
|
||||
);
|
||||
} catch (\Stripe\Error\SignatureVerification $e) {
|
||||
abort(400);
|
||||
}
|
||||
switch ($event->type) {
|
||||
case 'source.chargeable':
|
||||
$object = $event->data->object;
|
||||
\Stripe\Charge::create([
|
||||
'amount' => $object->amount,
|
||||
'currency' => $object->currency,
|
||||
'source' => $object->id,
|
||||
'metadata' => json_decode($object->metadata, true)
|
||||
]);
|
||||
break;
|
||||
case 'charge.succeeded':
|
||||
$object = $event->data->object;
|
||||
if ($object->status === 'succeeded') {
|
||||
$metaData = isset($object->metadata->out_trade_no) ? $object->metadata : $object->source->metadata;
|
||||
$tradeNo = $metaData->out_trade_no;
|
||||
return [
|
||||
'trade_no' => $tradeNo,
|
||||
'callback_no' => $object->balance_transaction
|
||||
];
|
||||
}
|
||||
break;
|
||||
default:
|
||||
abort(500, 'event is not support');
|
||||
}
|
||||
die('success');
|
||||
}
|
||||
|
||||
private function exchange($from, $to)
|
||||
{
|
||||
$result = file_get_contents('https://api.exchangerate.host/latest?symbols=' . $to . '&base=' . $from);
|
||||
$result = json_decode($result, true);
|
||||
return $result['rates'][$to];
|
||||
}
|
||||
}
|
84
app/Payments/WechatPayNative.php
Normal file
84
app/Payments/WechatPayNative.php
Normal file
@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
namespace App\Payments;
|
||||
|
||||
use Omnipay\Omnipay;
|
||||
use Omnipay\WechatPay\Helper;
|
||||
|
||||
class WechatPayNative {
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->customResult = '<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';
|
||||
}
|
||||
|
||||
public function form()
|
||||
{
|
||||
return [
|
||||
'app_id' => [
|
||||
'label' => 'APPID',
|
||||
'description' => '绑定微信支付商户的APPID',
|
||||
'type' => 'input',
|
||||
],
|
||||
'mch_id' => [
|
||||
'label' => '商户号',
|
||||
'description' => '微信支付商户号',
|
||||
'type' => 'input',
|
||||
],
|
||||
'api_key' => [
|
||||
'label' => 'APIKEY(v1)',
|
||||
'description' => '',
|
||||
'type' => 'input',
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
public function pay($order)
|
||||
{
|
||||
$gateway = Omnipay::create('WechatPay_Native');
|
||||
$gateway->setAppId($this->config['app_id']);
|
||||
$gateway->setMchId($this->config['mch_id']);
|
||||
$gateway->setApiKey($this->config['api_key']);
|
||||
$gateway->setNotifyUrl($order['notify_url']);
|
||||
|
||||
$params = [
|
||||
'body' => $order['trade_no'],
|
||||
'out_trade_no' => $order['trade_no'],
|
||||
'total_fee' => $order['total_amount'],
|
||||
'spbill_create_ip' => '0.0.0.0',
|
||||
'fee_type' => 'CNY'
|
||||
];
|
||||
|
||||
$request = $gateway->purchase($params);
|
||||
$response = $request->send();
|
||||
$response = $response->getData();
|
||||
if ($response['return_code'] !== 'SUCCESS') {
|
||||
abort(500, $response['return_msg']);
|
||||
}
|
||||
return [
|
||||
'type' => 0,
|
||||
'data' => $response['code_url']
|
||||
];
|
||||
}
|
||||
|
||||
public function notify($params)
|
||||
{
|
||||
$data = Helper::xml2array(file_get_contents('php://input'));
|
||||
$gateway = Omnipay::create('WechatPay');
|
||||
$gateway->setAppId($this->config['app_id']);
|
||||
$gateway->setMchId($this->config['mch_id']);
|
||||
$gateway->setApiKey($this->config['api_key']);
|
||||
$response = $gateway->completePurchase([
|
||||
'request_params' => file_get_contents('php://input')
|
||||
])->send();
|
||||
|
||||
if (!$response->isPaid()) {
|
||||
die('FAIL');
|
||||
}
|
||||
|
||||
return [
|
||||
'trade_no' => $data['out_trade_no'],
|
||||
'callback_no' => $data['transaction_id']
|
||||
];
|
||||
}
|
||||
}
|
@ -23,6 +23,6 @@ class AppServiceProvider extends ServiceProvider
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
//
|
||||
$this->app['view']->addNamespace('theme', public_path() . '/theme');
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ class OrderService
|
||||
'three_year_price' => 36
|
||||
];
|
||||
public $order;
|
||||
public $user;
|
||||
|
||||
public function __construct(Order $order)
|
||||
{
|
||||
@ -27,11 +28,11 @@ class OrderService
|
||||
public function open()
|
||||
{
|
||||
$order = $this->order;
|
||||
$user = User::find($order->user_id);
|
||||
$this->user = User::find($order->user_id);
|
||||
$plan = Plan::find($order->plan_id);
|
||||
|
||||
if ($order->refund_amount) {
|
||||
$user->balance = $user->balance + $order->refund_amount;
|
||||
$this->user->balance = $this->user->balance + $order->refund_amount;
|
||||
}
|
||||
DB::beginTransaction();
|
||||
if ($order->surplus_order_ids) {
|
||||
@ -46,18 +47,28 @@ class OrderService
|
||||
}
|
||||
switch ((string)$order->cycle) {
|
||||
case 'onetime_price':
|
||||
$this->buyByOneTime($user, $plan);
|
||||
$this->buyByOneTime($plan);
|
||||
break;
|
||||
case 'reset_price':
|
||||
$this->buyByResetTraffic($user);
|
||||
$this->buyByResetTraffic();
|
||||
break;
|
||||
default:
|
||||
$this->buyByCycle($order, $user, $plan);
|
||||
$this->buyByCycle($order, $plan);
|
||||
}
|
||||
|
||||
if ((int)config('v2board.renew_reset_traffic_enable', 0)) $this->buyByResetTraffic($user);
|
||||
switch ((int)$order->type) {
|
||||
case 1:
|
||||
$this->openEvent(config('v2board.new_order_event_id', 0));
|
||||
break;
|
||||
case 2:
|
||||
$this->openEvent(config('v2board.renew_order_event_id', 0));
|
||||
break;
|
||||
case 3:
|
||||
$this->openEvent(config('v2board.change_order_event_id', 0));
|
||||
break;
|
||||
}
|
||||
|
||||
if (!$user->save()) {
|
||||
if (!$this->user->save()) {
|
||||
DB::rollBack();
|
||||
abort(500, '开通失败');
|
||||
}
|
||||
@ -121,13 +132,26 @@ class OrderService
|
||||
$order->total_amount = $order->total_amount - $order->discount_amount;
|
||||
}
|
||||
|
||||
public function setInvite(User $user)
|
||||
public function setInvite(User $user):void
|
||||
{
|
||||
$order = $this->order;
|
||||
if ($user->invite_user_id && $order->total_amount > 0) {
|
||||
$order->invite_user_id = $user->invite_user_id;
|
||||
$commissionFirstTime = (int)config('v2board.commission_first_time_enable', 1);
|
||||
if (!$commissionFirstTime || ($commissionFirstTime && !$this->haveValidOrder($user))) {
|
||||
$isCommission = false;
|
||||
switch ((int)$user->commission_type) {
|
||||
case 0:
|
||||
$commissionFirstTime = (int)config('v2board.commission_first_time_enable', 1);
|
||||
$isCommission = (!$commissionFirstTime || ($commissionFirstTime && !$this->haveValidOrder($user)));
|
||||
break;
|
||||
case 1:
|
||||
$isCommission = true;
|
||||
break;
|
||||
case 2:
|
||||
$isCommission = !$this->haveValidOrder($user);
|
||||
break;
|
||||
}
|
||||
|
||||
if ($isCommission) {
|
||||
$inviter = User::find($user->invite_user_id);
|
||||
if ($inviter && $inviter->commission_rate) {
|
||||
$order->commission_balance = $order->total_amount * ($inviter->commission_rate / 100);
|
||||
@ -141,7 +165,7 @@ class OrderService
|
||||
private function haveValidOrder(User $user)
|
||||
{
|
||||
return Order::where('user_id', $user->id)
|
||||
->whereIn('status', [3, 4])
|
||||
->whereNotIn('status', [0, 2])
|
||||
->first();
|
||||
}
|
||||
|
||||
@ -191,7 +215,7 @@ class OrderService
|
||||
if ($item->cycle === 'onetime_price') continue;
|
||||
if ($this->orderIsUsed($item)) continue;
|
||||
$orderSurplusMonth = $orderSurplusMonth + self::STR_TO_TIME[$item->cycle];
|
||||
$orderSurplusAmount = $orderSurplusAmount + ($item['total_amount'] + $item['balance_amount']);
|
||||
$orderSurplusAmount = $orderSurplusAmount + ($item['total_amount'] + $item['balance_amount'] + $item['surplus_amount'] - $item['refund_amount']);
|
||||
}
|
||||
if (!$orderSurplusMonth || !$orderSurplusAmount) return;
|
||||
$monthUnitPrice = $orderSurplusAmount / $orderSurplusMonth;
|
||||
@ -220,35 +244,35 @@ class OrderService
|
||||
}
|
||||
|
||||
|
||||
private function buyByResetTraffic(User $user)
|
||||
private function buyByResetTraffic()
|
||||
{
|
||||
$user->u = 0;
|
||||
$user->d = 0;
|
||||
$this->user->u = 0;
|
||||
$this->user->d = 0;
|
||||
}
|
||||
|
||||
private function buyByCycle(Order $order, User $user, Plan $plan)
|
||||
private function buyByCycle(Order $order, Plan $plan)
|
||||
{
|
||||
// change plan process
|
||||
if ((int)$order->type === 3) {
|
||||
$user->expired_at = time();
|
||||
$this->user->expired_at = time();
|
||||
}
|
||||
$user->transfer_enable = $plan->transfer_enable * 1073741824;
|
||||
$this->user->transfer_enable = $plan->transfer_enable * 1073741824;
|
||||
// 从一次性转换到循环
|
||||
if ($user->expired_at === NULL) $this->buyByResetTraffic($user);
|
||||
if ($this->user->expired_at === NULL) $this->buyByResetTraffic();
|
||||
// 新购
|
||||
if ($order->type === 1) $this->buyByResetTraffic($user);
|
||||
$user->plan_id = $plan->id;
|
||||
$user->group_id = $plan->group_id;
|
||||
$user->expired_at = $this->getTime($order->cycle, $user->expired_at);
|
||||
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);
|
||||
}
|
||||
|
||||
private function buyByOneTime(User $user, Plan $plan)
|
||||
private function buyByOneTime(Plan $plan)
|
||||
{
|
||||
$this->buyByResetTraffic($user);
|
||||
$user->transfer_enable = $plan->transfer_enable * 1073741824;
|
||||
$user->plan_id = $plan->id;
|
||||
$user->group_id = $plan->group_id;
|
||||
$user->expired_at = NULL;
|
||||
$this->buyByResetTraffic();
|
||||
$this->user->transfer_enable = $plan->transfer_enable * 1073741824;
|
||||
$this->user->plan_id = $plan->id;
|
||||
$this->user->group_id = $plan->group_id;
|
||||
$this->user->expired_at = NULL;
|
||||
}
|
||||
|
||||
private function getTime($str, $timestamp)
|
||||
@ -271,4 +295,15 @@ class OrderService
|
||||
return strtotime('+36 month', $timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
private function openEvent($eventId)
|
||||
{
|
||||
switch ((int) $eventId) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
$this->buyByResetTraffic();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
61
app/Services/PaymentService.php
Normal file
61
app/Services/PaymentService.php
Normal file
@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
|
||||
use App\Models\Payment;
|
||||
|
||||
class PaymentService
|
||||
{
|
||||
public $method;
|
||||
public $customResult;
|
||||
protected $class;
|
||||
protected $config;
|
||||
protected $payment;
|
||||
|
||||
public function __construct($method, $id = NULL, $uuid = NULL)
|
||||
{
|
||||
$this->method = $method;
|
||||
$this->class = '\\App\\Payments\\' . $this->method;
|
||||
if (!class_exists($this->class)) abort(500, 'gate is not found');
|
||||
if ($id) $payment = Payment::find($id)->toArray();
|
||||
if ($uuid) $payment = Payment::where('uuid', $uuid)->first()->toArray();
|
||||
$this->config = [];
|
||||
if (isset($payment)) {
|
||||
$this->config = json_decode($payment['config'], true);
|
||||
$this->config['enable'] = $payment['enable'];
|
||||
$this->config['id'] = $payment['id'];
|
||||
$this->config['uuid'] = $payment['uuid'];
|
||||
};
|
||||
$this->payment = new $this->class($this->config);
|
||||
if (isset($this->payment->customResult)) $this->customResult = $this->payment->customResult;
|
||||
}
|
||||
|
||||
public function notify($params)
|
||||
{
|
||||
if (!$this->config['enable']) abort(500, 'gate is not enable');
|
||||
return $this->payment->notify($params);
|
||||
}
|
||||
|
||||
public function pay($order)
|
||||
{
|
||||
return $this->payment->pay([
|
||||
'notify_url' => url("/api/v1/guest/payment/notify/{$this->method}/{$this->config['uuid']}"),
|
||||
'return_url' => config('v2board.app_url', env('APP_URL')) . '/#/order/' . $order['trade_no'],
|
||||
'trade_no' => $order['trade_no'],
|
||||
'total_amount' => $order['total_amount'],
|
||||
'user_id' => $order['user_id'],
|
||||
'stripe_token' => $order['stripe_token']
|
||||
]);
|
||||
}
|
||||
|
||||
public function form()
|
||||
{
|
||||
$form = $this->payment->form();
|
||||
$keys = array_keys($form);
|
||||
foreach ($keys as $key) {
|
||||
if (isset($this->config[$key])) $form[$key]['value'] = $this->config[$key];
|
||||
}
|
||||
return $form;
|
||||
}
|
||||
}
|
@ -8,8 +8,6 @@ use App\Models\User;
|
||||
use App\Models\Server;
|
||||
use App\Models\ServerTrojan;
|
||||
use App\Utils\CacheKey;
|
||||
use App\Utils\Helper;
|
||||
use App\Utils\URLSchemes;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
class ServerService
|
||||
@ -29,7 +27,6 @@ class ServerService
|
||||
$v2ray[$i]['type'] = 'v2ray';
|
||||
$groupId = json_decode($v2ray[$i]['group_id']);
|
||||
if (in_array($user->group_id, $groupId)) {
|
||||
$v2ray[$i]['link'] = URLSchemes::buildVmess($v2ray[$i], $user);
|
||||
if ($v2ray[$i]['parent_id']) {
|
||||
$v2ray[$i]['last_check_at'] = Cache::get(CacheKey::get('SERVER_V2RAY_LAST_CHECK_AT', $v2ray[$i]['parent_id']));
|
||||
} else {
|
||||
@ -54,7 +51,6 @@ class ServerService
|
||||
for ($i = 0; $i < count($trojan); $i++) {
|
||||
$trojan[$i]['type'] = 'trojan';
|
||||
$groupId = json_decode($trojan[$i]['group_id']);
|
||||
$trojan[$i]['link'] = URLSchemes::buildTrojan($trojan[$i], $user);
|
||||
if (in_array($user->group_id, $groupId)) {
|
||||
if ($trojan[$i]['parent_id']) {
|
||||
$trojan[$i]['last_check_at'] = Cache::get(CacheKey::get('SERVER_TROJAN_LAST_CHECK_AT', $trojan[$i]['parent_id']));
|
||||
@ -78,7 +74,6 @@ class ServerService
|
||||
for ($i = 0; $i < count($shadowsocks); $i++) {
|
||||
$shadowsocks[$i]['type'] = 'shadowsocks';
|
||||
$groupId = json_decode($shadowsocks[$i]['group_id']);
|
||||
$shadowsocks[$i]['link'] = URLSchemes::buildShadowsocks($shadowsocks[$i], $user);
|
||||
if (in_array($user->group_id, $groupId)) {
|
||||
if ($shadowsocks[$i]['parent_id']) {
|
||||
$shadowsocks[$i]['last_check_at'] = Cache::get(CacheKey::get('SERVER_SHADOWSOCKS_LAST_CHECK_AT', $shadowsocks[$i]['parent_id']));
|
||||
@ -196,6 +191,9 @@ class ServerService
|
||||
case 'quic':
|
||||
$json->inbound->streamSettings->quicSettings = json_decode($server->networkSettings);
|
||||
break;
|
||||
case 'grpc':
|
||||
$json->inbound->streamSettings->grpcSettings = json_decode($server->networkSettings);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -298,13 +296,6 @@ class ServerService
|
||||
$server[$i]['tags'] = json_decode($server[$i]['tags']);
|
||||
}
|
||||
$server[$i]['group_id'] = json_decode($server[$i]['group_id']);
|
||||
$server[$i]['online'] = Cache::get(CacheKey::get('SERVER_SHADOWSOCKS_ONLINE_USER', $server[$i]['parent_id'] ? $server[$i]['parent_id'] : $server[$i]['id']));
|
||||
if ($server[$i]['parent_id']) {
|
||||
$server[$i]['last_check_at'] = Cache::get(CacheKey::get('SERVER_SHADOWSOCKS_LAST_CHECK_AT', $server[$i]['parent_id']));
|
||||
} else {
|
||||
$server[$i]['last_check_at'] = Cache::get(CacheKey::get('SERVER_SHADOWSOCKS_LAST_CHECK_AT', $server[$i]['id']));
|
||||
}
|
||||
$server[$i]['available'] = (time() - 300) < $server[$i]['last_check_at'];
|
||||
}
|
||||
return $server->toArray();
|
||||
}
|
||||
@ -327,13 +318,6 @@ class ServerService
|
||||
$server[$i]['ruleSettings'] = json_decode($server[$i]['ruleSettings']);
|
||||
}
|
||||
$server[$i]['group_id'] = json_decode($server[$i]['group_id']);
|
||||
$server[$i]['online'] = Cache::get(CacheKey::get('SERVER_V2RAY_ONLINE_USER', $server[$i]['parent_id'] ? $server[$i]['parent_id'] : $server[$i]['id']));
|
||||
if ($server[$i]['parent_id']) {
|
||||
$server[$i]['last_check_at'] = Cache::get(CacheKey::get('SERVER_V2RAY_LAST_CHECK_AT', $server[$i]['parent_id']));
|
||||
} else {
|
||||
$server[$i]['last_check_at'] = Cache::get(CacheKey::get('SERVER_V2RAY_LAST_CHECK_AT', $server[$i]['id']));
|
||||
}
|
||||
$server[$i]['available'] = (time() - 300) < $server[$i]['last_check_at'];
|
||||
}
|
||||
return $server->toArray();
|
||||
}
|
||||
@ -347,14 +331,42 @@ class ServerService
|
||||
$server[$i]['tags'] = json_decode($server[$i]['tags']);
|
||||
}
|
||||
$server[$i]['group_id'] = json_decode($server[$i]['group_id']);
|
||||
$server[$i]['online'] = Cache::get(CacheKey::get('SERVER_TROJAN_ONLINE_USER', $server[$i]['parent_id'] ? $server[$i]['parent_id'] : $server[$i]['id']));
|
||||
if ($server[$i]['parent_id']) {
|
||||
$server[$i]['last_check_at'] = Cache::get(CacheKey::get('SERVER_TROJAN_LAST_CHECK_AT', $server[$i]['parent_id']));
|
||||
} else {
|
||||
$server[$i]['last_check_at'] = Cache::get(CacheKey::get('SERVER_TROJAN_LAST_CHECK_AT', $server[$i]['id']));
|
||||
}
|
||||
$server[$i]['available'] = (time() - 300) < $server[$i]['last_check_at'];
|
||||
}
|
||||
return $server->toArray();
|
||||
}
|
||||
|
||||
public function mergeData(&$servers)
|
||||
{
|
||||
foreach ($servers as $k => $v) {
|
||||
$serverType = strtoupper($servers[$k]['type']);
|
||||
$servers[$k]['online'] = Cache::get(CacheKey::get("SERVER_{$serverType}_ONLINE_USER", $servers[$k]['parent_id'] ? $servers[$k]['parent_id'] : $servers[$k]['id']));
|
||||
if ($servers[$k]['parent_id']) {
|
||||
$servers[$k]['last_check_at'] = Cache::get(CacheKey::get("SERVER_{$serverType}_LAST_CHECK_AT", $servers[$k]['parent_id']));
|
||||
$servers[$k]['last_push_at'] = Cache::get(CacheKey::get("SERVER_{$serverType}_LAST_PUSH_AT", $servers[$k]['parent_id']));
|
||||
} else {
|
||||
$servers[$k]['last_check_at'] = Cache::get(CacheKey::get("SERVER_{$serverType}_LAST_CHECK_AT", $servers[$k]['id']));
|
||||
$servers[$k]['last_push_at'] = Cache::get(CacheKey::get("SERVER_{$serverType}_LAST_PUSH_AT", $servers[$k]['id']));
|
||||
}
|
||||
if ((time() - 300) >= $servers[$k]['last_check_at']) {
|
||||
$servers[$k]['available_status'] = 0;
|
||||
} else if ((time() - 300) >= $servers[$k]['last_push_at']) {
|
||||
$servers[$k]['available_status'] = 1;
|
||||
} else {
|
||||
$servers[$k]['available_status'] = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getAllServers()
|
||||
{
|
||||
$servers = array_merge(
|
||||
$this->getShadowsocksServers(),
|
||||
$this->getV2rayServers(),
|
||||
$this->getTrojanServers()
|
||||
);
|
||||
$this->mergeData($servers);
|
||||
$tmp = array_column($servers, 'sort');
|
||||
array_multisort($tmp, SORT_ASC, $servers);
|
||||
return $servers;
|
||||
}
|
||||
}
|
||||
|
@ -2,9 +2,12 @@
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\InviteCode;
|
||||
use App\Models\Order;
|
||||
use App\Models\Server;
|
||||
use App\Models\Ticket;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class UserService
|
||||
{
|
||||
@ -52,7 +55,7 @@ class UserService
|
||||
|
||||
public function addBalance(int $userId, int $balance):bool
|
||||
{
|
||||
$user = User::find($userId);
|
||||
$user = User::lockForUpdate()->find($userId);
|
||||
if (!$user) {
|
||||
return false;
|
||||
}
|
||||
|
@ -9,10 +9,13 @@ class CacheKey
|
||||
'LAST_SEND_EMAIL_VERIFY_TIMESTAMP' => '最后一次发送邮箱验证码时间',
|
||||
'SERVER_V2RAY_ONLINE_USER' => '节点在线用户',
|
||||
'SERVER_V2RAY_LAST_CHECK_AT' => '节点最后检查时间',
|
||||
'SERVER_V2RAY_LAST_PUSH_AT' => '节点最后推送时间',
|
||||
'SERVER_TROJAN_ONLINE_USER' => 'trojan节点在线用户',
|
||||
'SERVER_TROJAN_LAST_CHECK_AT' => 'trojan节点最后检查时间',
|
||||
'SERVER_TROJAN_LAST_PUSH_AT' => 'trojan节点最后推送时间',
|
||||
'SERVER_SHADOWSOCKS_ONLINE_USER' => 'ss节点在线用户',
|
||||
'SERVER_SHADOWSOCKS_LAST_CHECK_AT' => 'ss节点最后检查时间',
|
||||
'SERVER_SHADOWSOCKS_LAST_PUSH_AT' => 'ss节点最后推送时间',
|
||||
'TEMP_TOKEN' => '临时令牌',
|
||||
'LAST_SEND_EMAIL_REMIND_TRAFFIC'
|
||||
];
|
||||
|
@ -25,7 +25,7 @@ class Helper
|
||||
|
||||
public static function exchange($from, $to)
|
||||
{
|
||||
$result = file_get_contents('https://api.exchangeratesapi.io/latest?symbols=' . $to . '&base=' . $from);
|
||||
$result = file_get_contents('https://api.exchangerate.host/latest?symbols=' . $to . '&base=' . $from);
|
||||
$result = json_decode($result, true);
|
||||
return $result['rates'][$to];
|
||||
}
|
||||
|
@ -1,68 +0,0 @@
|
||||
<?php
|
||||
namespace App\Utils;
|
||||
|
||||
use App\Models\Server;
|
||||
use App\Models\User;
|
||||
|
||||
class URLSchemes
|
||||
{
|
||||
public static function buildShadowsocks($server, User $user)
|
||||
{
|
||||
$name = rawurlencode($server['name']);
|
||||
$str = str_replace(
|
||||
['+', '/', '='],
|
||||
['-', '_', ''],
|
||||
base64_encode("{$server['cipher']}:{$user['uuid']}")
|
||||
);
|
||||
return "ss://{$str}@{$server['host']}:{$server['port']}#{$name}\r\n";
|
||||
}
|
||||
|
||||
public static function buildShadowsocksSIP008($server, User $user)
|
||||
{
|
||||
$config = [
|
||||
"id" => $server['id'],
|
||||
"remarks" => $server['name'],
|
||||
"server" => $server['host'],
|
||||
"server_port" => $server['port'],
|
||||
"password" => $user['uuid'],
|
||||
"method" => $server['cipher']
|
||||
];
|
||||
return $config;
|
||||
}
|
||||
|
||||
public static function buildVmess($server, User $user)
|
||||
{
|
||||
$config = [
|
||||
"v" => "2",
|
||||
"ps" => $server['name'],
|
||||
"add" => $server['host'],
|
||||
"port" => $server['port'],
|
||||
"id" => $user['uuid'],
|
||||
"aid" => $server['alter_id'],
|
||||
"net" => $server['network'],
|
||||
"type" => "none",
|
||||
"host" => "",
|
||||
"path" => "",
|
||||
"tls" => $server['tls'] ? "tls" : ""
|
||||
];
|
||||
if ((string)$server['network'] === 'ws') {
|
||||
$wsSettings = json_decode($server['networkSettings'], true);
|
||||
if (isset($wsSettings['path'])) $config['path'] = $wsSettings['path'];
|
||||
if (isset($wsSettings['headers']['Host'])) $config['host'] = $wsSettings['headers']['Host'];
|
||||
}
|
||||
return "vmess://" . base64_encode(json_encode($config)) . "\r\n";
|
||||
}
|
||||
|
||||
public static function buildTrojan($server, User $user)
|
||||
{
|
||||
$name = rawurlencode($server['name']);
|
||||
$query = http_build_query([
|
||||
'allowInsecure' => $server['allow_insecure'],
|
||||
'peer' => $server['server_name'],
|
||||
'sni' => $server['server_name']
|
||||
]);
|
||||
$uri = "trojan://{$user['uuid']}@{$server['host']}:{$server['port']}?{$query}#{$name}";
|
||||
$uri .= "\r\n";
|
||||
return $uri;
|
||||
}
|
||||
}
|
@ -15,6 +15,7 @@
|
||||
"laravel/framework": "^6.0",
|
||||
"laravel/tinker": "^1.0",
|
||||
"lokielse/omnipay-alipay": "3.0.6",
|
||||
"lokielse/omnipay-wechatpay": "^3.0",
|
||||
"php-curl-class/php-curl-class": "^8.6",
|
||||
"stripe/stripe-php": "^7.36.1",
|
||||
"symfony/yaml": "^4.3"
|
||||
@ -64,11 +65,5 @@
|
||||
"post-create-project-cmd": [
|
||||
"@php artisan key:generate --ansi"
|
||||
]
|
||||
},
|
||||
"repositories": {
|
||||
"packagist": {
|
||||
"type": "composer",
|
||||
"url": "https://mirrors.aliyun.com/composer/"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -236,5 +236,5 @@ return [
|
||||
| The only modification by laravel config
|
||||
|
|
||||
*/
|
||||
'version' => '1.4.3.1612347430'
|
||||
'version' => '1.5.2.1627559775390'
|
||||
];
|
||||
|
@ -1,4 +1,4 @@
|
||||
-- Adminer 4.7.8 MySQL dump
|
||||
-- Adminer 4.8.0 MySQL 5.7.29 dump
|
||||
|
||||
SET NAMES utf8;
|
||||
SET time_zone = '+00:00';
|
||||
@ -14,7 +14,7 @@ CREATE TABLE `failed_jobs` (
|
||||
`queue` text COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`payload` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`exception` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`failed_at` timestamp NOT NULL DEFAULT current_timestamp(),
|
||||
`failed_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
@ -41,8 +41,8 @@ CREATE TABLE `v2_invite_code` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`user_id` int(11) NOT NULL,
|
||||
`code` char(32) NOT NULL,
|
||||
`status` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`pv` int(11) NOT NULL DEFAULT 0,
|
||||
`status` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`pv` int(11) NOT NULL DEFAULT '0',
|
||||
`created_at` int(11) NOT NULL,
|
||||
`updated_at` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
@ -57,7 +57,7 @@ CREATE TABLE `v2_knowledge` (
|
||||
`title` varchar(255) NOT NULL COMMENT '標題',
|
||||
`body` text NOT NULL COMMENT '內容',
|
||||
`sort` int(11) DEFAULT NULL COMMENT '排序',
|
||||
`show` tinyint(1) NOT NULL DEFAULT 0 COMMENT '顯示',
|
||||
`show` tinyint(1) NOT NULL DEFAULT '0' COMMENT '顯示',
|
||||
`created_at` int(11) NOT NULL COMMENT '創建時間',
|
||||
`updated_at` int(11) NOT NULL COMMENT '更新時間',
|
||||
PRIMARY KEY (`id`)
|
||||
@ -70,7 +70,7 @@ CREATE TABLE `v2_mail_log` (
|
||||
`email` varchar(64) NOT NULL,
|
||||
`subject` varchar(255) NOT NULL,
|
||||
`template_name` varchar(255) NOT NULL,
|
||||
`error` text DEFAULT NULL,
|
||||
`error` text,
|
||||
`created_at` int(11) NOT NULL,
|
||||
`updated_at` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
@ -96,6 +96,7 @@ CREATE TABLE `v2_order` (
|
||||
`user_id` int(11) NOT NULL,
|
||||
`plan_id` int(11) NOT NULL,
|
||||
`coupon_id` int(11) DEFAULT NULL,
|
||||
`payment_id` int(11) DEFAULT NULL,
|
||||
`type` int(11) NOT NULL COMMENT '1新购2续费3升级',
|
||||
`cycle` varchar(255) NOT NULL,
|
||||
`trade_no` varchar(36) NOT NULL,
|
||||
@ -105,26 +106,41 @@ CREATE TABLE `v2_order` (
|
||||
`surplus_amount` int(11) DEFAULT NULL COMMENT '剩余价值',
|
||||
`refund_amount` int(11) DEFAULT NULL COMMENT '退款金额',
|
||||
`balance_amount` int(11) DEFAULT NULL COMMENT '使用余额',
|
||||
`surplus_order_ids` text DEFAULT NULL COMMENT '折抵订单',
|
||||
`status` tinyint(1) NOT NULL DEFAULT 0 COMMENT '0待支付1开通中2已取消3已完成4已折抵',
|
||||
`commission_status` tinyint(1) NOT NULL DEFAULT 0 COMMENT '0待确认1发放中2有效3无效',
|
||||
`commission_balance` int(11) NOT NULL DEFAULT 0,
|
||||
`surplus_order_ids` text COMMENT '折抵订单',
|
||||
`status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '0待支付1开通中2已取消3已完成4已折抵',
|
||||
`commission_status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '0待确认1发放中2有效3无效',
|
||||
`commission_balance` int(11) NOT NULL DEFAULT '0',
|
||||
`created_at` int(11) NOT NULL,
|
||||
`updated_at` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `v2_payment`;
|
||||
CREATE TABLE `v2_payment` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`uuid` char(32) NOT NULL,
|
||||
`payment` varchar(16) NOT NULL,
|
||||
`name` varchar(255) NOT NULL,
|
||||
`config` text NOT NULL,
|
||||
`enable` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`sort` int(11) DEFAULT NULL,
|
||||
`created_at` int(11) NOT NULL,
|
||||
`updated_at` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `v2_plan`;
|
||||
CREATE TABLE `v2_plan` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`group_id` int(11) NOT NULL,
|
||||
`transfer_enable` int(11) NOT NULL,
|
||||
`name` varchar(255) NOT NULL,
|
||||
`show` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`show` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`sort` int(11) DEFAULT NULL,
|
||||
`renew` tinyint(1) NOT NULL DEFAULT 1,
|
||||
`content` text DEFAULT NULL,
|
||||
`renew` tinyint(1) NOT NULL DEFAULT '1',
|
||||
`content` text,
|
||||
`month_price` int(11) DEFAULT NULL,
|
||||
`quarter_price` int(11) DEFAULT NULL,
|
||||
`half_year_price` int(11) DEFAULT NULL,
|
||||
@ -148,18 +164,18 @@ CREATE TABLE `v2_server` (
|
||||
`host` varchar(255) NOT NULL,
|
||||
`port` int(11) NOT NULL,
|
||||
`server_port` int(11) NOT NULL,
|
||||
`tls` tinyint(4) NOT NULL DEFAULT 0,
|
||||
`tls` tinyint(4) NOT NULL DEFAULT '0',
|
||||
`tags` varchar(255) DEFAULT NULL,
|
||||
`rate` varchar(11) NOT NULL,
|
||||
`network` text NOT NULL,
|
||||
`alter_id` int(11) NOT NULL DEFAULT 1,
|
||||
`settings` text DEFAULT NULL,
|
||||
`rules` text DEFAULT NULL,
|
||||
`networkSettings` text DEFAULT NULL,
|
||||
`tlsSettings` text DEFAULT NULL,
|
||||
`ruleSettings` text DEFAULT NULL,
|
||||
`dnsSettings` text DEFAULT NULL,
|
||||
`show` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`alter_id` int(11) NOT NULL DEFAULT '1',
|
||||
`settings` text,
|
||||
`rules` text,
|
||||
`networkSettings` text,
|
||||
`tlsSettings` text,
|
||||
`ruleSettings` text,
|
||||
`dnsSettings` text,
|
||||
`show` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`sort` int(11) DEFAULT NULL,
|
||||
`created_at` int(11) NOT NULL,
|
||||
`updated_at` int(11) NOT NULL,
|
||||
@ -206,7 +222,7 @@ CREATE TABLE `v2_server_shadowsocks` (
|
||||
`port` int(11) NOT NULL,
|
||||
`server_port` int(11) NOT NULL,
|
||||
`cipher` varchar(255) NOT NULL,
|
||||
`show` tinyint(4) NOT NULL DEFAULT 0,
|
||||
`show` tinyint(4) NOT NULL DEFAULT '0',
|
||||
`sort` int(11) DEFAULT NULL,
|
||||
`created_at` int(11) NOT NULL,
|
||||
`updated_at` int(11) NOT NULL,
|
||||
@ -225,9 +241,9 @@ CREATE TABLE `v2_server_trojan` (
|
||||
`host` varchar(255) NOT NULL COMMENT '主机名',
|
||||
`port` int(11) NOT NULL COMMENT '连接端口',
|
||||
`server_port` int(11) NOT NULL COMMENT '服务端口',
|
||||
`allow_insecure` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否允许不安全',
|
||||
`allow_insecure` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否允许不安全',
|
||||
`server_name` varchar(255) DEFAULT NULL,
|
||||
`show` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否显示',
|
||||
`show` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否显示',
|
||||
`sort` int(11) DEFAULT NULL,
|
||||
`created_at` int(11) NOT NULL,
|
||||
`updated_at` int(11) NOT NULL,
|
||||
@ -276,7 +292,7 @@ CREATE TABLE `v2_ticket` (
|
||||
`last_reply_user_id` int(11) NOT NULL,
|
||||
`subject` varchar(255) NOT NULL,
|
||||
`level` tinyint(1) NOT NULL,
|
||||
`status` tinyint(1) NOT NULL DEFAULT 0 COMMENT '0:已开启 1:已关闭',
|
||||
`status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '0:已开启 1:已关闭',
|
||||
`created_at` int(11) NOT NULL,
|
||||
`updated_at` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
@ -303,27 +319,28 @@ CREATE TABLE `v2_user` (
|
||||
`email` varchar(64) NOT NULL,
|
||||
`password` varchar(64) NOT NULL,
|
||||
`password_algo` char(10) DEFAULT NULL,
|
||||
`balance` int(11) NOT NULL DEFAULT 0,
|
||||
`balance` int(11) NOT NULL DEFAULT '0',
|
||||
`discount` int(11) DEFAULT NULL,
|
||||
`commission_type` tinyint(4) NOT NULL DEFAULT '0' COMMENT '0: system 1: cycle 2: onetime',
|
||||
`commission_rate` int(11) DEFAULT NULL,
|
||||
`commission_balance` int(11) NOT NULL DEFAULT 0,
|
||||
`t` int(11) NOT NULL DEFAULT 0,
|
||||
`u` bigint(20) NOT NULL DEFAULT 0,
|
||||
`d` bigint(20) NOT NULL DEFAULT 0,
|
||||
`transfer_enable` bigint(20) NOT NULL DEFAULT 0,
|
||||
`banned` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`is_admin` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`is_staff` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`commission_balance` int(11) NOT NULL DEFAULT '0',
|
||||
`t` int(11) NOT NULL DEFAULT '0',
|
||||
`u` bigint(20) NOT NULL DEFAULT '0',
|
||||
`d` bigint(20) NOT NULL DEFAULT '0',
|
||||
`transfer_enable` bigint(20) NOT NULL DEFAULT '0',
|
||||
`banned` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`is_admin` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`is_staff` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`last_login_at` int(11) DEFAULT NULL,
|
||||
`last_login_ip` int(11) DEFAULT NULL,
|
||||
`uuid` varchar(36) NOT NULL,
|
||||
`group_id` int(11) DEFAULT NULL,
|
||||
`plan_id` int(11) DEFAULT NULL,
|
||||
`remind_expire` tinyint(4) DEFAULT 1,
|
||||
`remind_traffic` tinyint(4) DEFAULT 1,
|
||||
`remind_expire` tinyint(4) DEFAULT '1',
|
||||
`remind_traffic` tinyint(4) DEFAULT '1',
|
||||
`token` char(32) NOT NULL,
|
||||
`remarks` text DEFAULT NULL,
|
||||
`expired_at` bigint(20) DEFAULT 0,
|
||||
`remarks` text,
|
||||
`expired_at` bigint(20) DEFAULT '0',
|
||||
`created_at` int(11) NOT NULL,
|
||||
`updated_at` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
@ -331,4 +348,4 @@ CREATE TABLE `v2_user` (
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
-- 2021-01-21 14:25:17
|
||||
-- 2021-07-13 13:50:52
|
||||
|
@ -394,3 +394,34 @@ DROP `enable`;
|
||||
|
||||
ALTER TABLE `v2_user`
|
||||
ADD `remarks` text COLLATE 'utf8_general_ci' NULL AFTER `token`;
|
||||
|
||||
CREATE TABLE `v2_payment` (
|
||||
`id` int NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
`payment` varchar(16) NOT NULL,
|
||||
`name` varchar(255) NOT NULL,
|
||||
`config` text NOT NULL,
|
||||
`enable` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`sort` int(11) DEFAULT NULL,
|
||||
`created_at` int(11) NOT NULL,
|
||||
`updated_at` int(11) NOT NULL
|
||||
) COLLATE 'utf8mb4_general_ci';
|
||||
|
||||
ALTER TABLE `v2_order`
|
||||
ADD `payment_id` int(11) NULL AFTER `coupon_id`;
|
||||
|
||||
ALTER TABLE `v2_payment`
|
||||
ADD `uuid` char(32) NOT NULL AFTER `id`;
|
||||
|
||||
ALTER TABLE `v2_user`
|
||||
ADD UNIQUE `email_deleted_at` (`email`, `deleted_at`),
|
||||
DROP INDEX `email`;
|
||||
|
||||
ALTER TABLE `v2_user`
|
||||
DROP `deleted_at`;
|
||||
|
||||
ALTER TABLE `v2_user`
|
||||
ADD UNIQUE `email` (`email`),
|
||||
DROP INDEX `email_deleted_at`;
|
||||
|
||||
ALTER TABLE `v2_user`
|
||||
ADD `commission_type` tinyint NOT NULL DEFAULT '0' COMMENT '0: system 1: cycle 2: onetime' AFTER `discount`;
|
||||
|
2
init.sh
2
init.sh
@ -1,3 +1,3 @@
|
||||
wget https://getcomposer.org/download/1.9.0/composer.phar
|
||||
wget https://getcomposer.org/download/2.0.13/composer.phar
|
||||
php composer.phar install -vvv
|
||||
php artisan v2board:install
|
||||
|
@ -1,93 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Library;
|
||||
|
||||
class BitpayX
|
||||
{
|
||||
private $bitpayxAppSecret;
|
||||
private $bitpayxGatewayUri;
|
||||
|
||||
/**
|
||||
* 签名初始化
|
||||
* @param merKey 签名密钥
|
||||
*/
|
||||
public function __construct($bitpayxAppSecret)
|
||||
{
|
||||
$this->bitpayxAppSecret = $bitpayxAppSecret;
|
||||
$this->bitpayxGatewayUri = 'https://api.mugglepay.com/v1/';
|
||||
}
|
||||
|
||||
public function prepareSignId($tradeno)
|
||||
{
|
||||
$data_sign = array();
|
||||
$data_sign['merchant_order_id'] = $tradeno;
|
||||
$data_sign['secret'] = $this->bitpayxAppSecret;
|
||||
$data_sign['type'] = 'FIAT';
|
||||
ksort($data_sign);
|
||||
return http_build_query($data_sign);
|
||||
}
|
||||
|
||||
public function sign($data)
|
||||
{
|
||||
return strtolower(md5(md5($data) . $this->bitpayxAppSecret));
|
||||
}
|
||||
|
||||
public function verify($data, $signature)
|
||||
{
|
||||
$mySign = $this->sign($data);
|
||||
return $mySign === $signature;
|
||||
}
|
||||
|
||||
public function mprequest($data)
|
||||
{
|
||||
$headers = array('content-type: application/json', 'token: ' . $this->bitpayxAppSecret);
|
||||
$curl = curl_init();
|
||||
$url = $this->bitpayxGatewayUri . 'orders';
|
||||
curl_setopt($curl, CURLOPT_URL, $url);
|
||||
curl_setopt($curl, CURLOPT_POST, 1);
|
||||
$data_string = json_encode($data);
|
||||
curl_setopt($curl, CURLOPT_POSTFIELDS, $data_string);
|
||||
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
|
||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
|
||||
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
|
||||
$data = curl_exec($curl);
|
||||
curl_close($curl);
|
||||
return json_decode($data, true);
|
||||
}
|
||||
|
||||
public function mpcheckout($orderId, $data)
|
||||
{
|
||||
$headers = array('content-type: application/json', 'token: ' . $this->bitpayxAppSecret);
|
||||
$curl = curl_init();
|
||||
$url = $this->bitpayxGatewayUri . 'orders/' . $orderId . '/checkout';
|
||||
curl_setopt($curl, CURLOPT_URL, $url);
|
||||
curl_setopt($curl, CURLOPT_POST, 1);
|
||||
$data_string = json_encode($data);
|
||||
curl_setopt($curl, CURLOPT_POSTFIELDS, $data_string);
|
||||
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
|
||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
|
||||
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
|
||||
$data = curl_exec($curl);
|
||||
curl_close($curl);
|
||||
return json_decode($data, true);
|
||||
}
|
||||
|
||||
public function refund($merchantTradeNo)
|
||||
{
|
||||
// TODO
|
||||
return true;
|
||||
}
|
||||
|
||||
public function buildHtml($params, $method = 'post', $target = '_self')
|
||||
{
|
||||
// var_dump($params);exit;
|
||||
$html = "<form id='submit' name='submit' action='" . $this->gatewayUri . "' method='$method' target='$target'>";
|
||||
foreach ($params as $key => $value) {
|
||||
$html .= "<input type='hidden' name='$key' value='$value'/>";
|
||||
}
|
||||
$html .= "</form><script>document.forms['submit'].submit();</script>";
|
||||
return $html;
|
||||
}
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Library;
|
||||
|
||||
class Epay
|
||||
{
|
||||
private $pid;
|
||||
private $key;
|
||||
private $url;
|
||||
|
||||
public function __construct($url, $pid, $key)
|
||||
{
|
||||
$this->pid = $pid;
|
||||
$this->key = $key;
|
||||
$this->url = $url;
|
||||
}
|
||||
|
||||
public function pay($params)
|
||||
{
|
||||
$params['pid'] = $this->pid;
|
||||
ksort($params);
|
||||
reset($params);
|
||||
$str = stripslashes(urldecode(http_build_query($params))) . $this->key;
|
||||
$params['sign'] = md5($str);
|
||||
$params['sign_type'] = 'MD5';
|
||||
return $this->url . '/submit.php?' . http_build_query($params);
|
||||
}
|
||||
|
||||
public function verify($params)
|
||||
{
|
||||
$sign = $params['sign'];
|
||||
unset($params['sign']);
|
||||
unset($params['sign_type']);
|
||||
ksort($params);
|
||||
reset($params);
|
||||
$str = stripslashes(urldecode(http_build_query($params))) . $this->key;
|
||||
if ($sign !== md5($str)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
@ -1,60 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Library;
|
||||
|
||||
use \Curl\Curl;
|
||||
|
||||
class MGate
|
||||
{
|
||||
private $appId;
|
||||
private $appSecret;
|
||||
private $url;
|
||||
|
||||
public function __construct($url, $appId, $appSecret)
|
||||
{
|
||||
$this->appId = $appId;
|
||||
$this->appSecret = $appSecret;
|
||||
$this->url = $url;
|
||||
}
|
||||
|
||||
public function pay($params)
|
||||
{
|
||||
ksort($params);
|
||||
$str = http_build_query($params) . $this->appSecret;
|
||||
$params['sign'] = md5($str);
|
||||
$curl = new Curl();
|
||||
$curl->post($this->url . '/v1/gateway/fetch', http_build_query($params));
|
||||
$result = $curl->response;
|
||||
if (!$result) {
|
||||
abort(500, '网络异常');
|
||||
}
|
||||
if ($curl->error) {
|
||||
if (isset($result->errors)) {
|
||||
$errors = (array)$result->errors;
|
||||
abort(500, $errors[array_keys($errors)[0]][0]);
|
||||
}
|
||||
if (isset($result->message)) {
|
||||
abort(500, $result->message);
|
||||
}
|
||||
abort(500, '未知错误');
|
||||
}
|
||||
$curl->close();
|
||||
if (!isset($result->data->trade_no)) {
|
||||
abort(500, '接口请求失败');
|
||||
}
|
||||
return $result->data->pay_url;
|
||||
}
|
||||
|
||||
public function verify($params)
|
||||
{
|
||||
$sign = $params['sign'];
|
||||
unset($params['sign']);
|
||||
ksort($params);
|
||||
reset($params);
|
||||
$str = http_build_query($params) . $this->appSecret;
|
||||
if ($sign !== md5($str)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
21
package.json
21
package.json
@ -1,21 +0,0 @@
|
||||
{
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "npm run development",
|
||||
"development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
|
||||
"watch": "npm run development -- --watch",
|
||||
"watch-poll": "npm run watch -- --watch-poll",
|
||||
"hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js",
|
||||
"prod": "npm run production",
|
||||
"production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"axios": "^0.19",
|
||||
"cross-env": "^5.1",
|
||||
"laravel-mix": "^4.0.7",
|
||||
"lodash": "^4.17.13",
|
||||
"resolve-url-loader": "^2.3.1",
|
||||
"sass": "^1.15.2",
|
||||
"sass-loader": "^7.1.0"
|
||||
}
|
||||
}
|
2
public/assets/admin/components.async.js
vendored
2
public/assets/admin/components.async.js
vendored
File diff suppressed because one or more lines are too long
16
public/assets/admin/components.chunk.css
vendored
16
public/assets/admin/components.chunk.css
vendored
File diff suppressed because one or more lines are too long
2
public/assets/admin/umi.css
vendored
2
public/assets/admin/umi.css
vendored
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user