Przeglądaj źródła

Merge pull request #439 from v2board/dev

1.5.1
tokumeikoi 4 lat temu
rodzic
commit
229a3022ac
64 zmienionych plików z 1132 dodań i 791 usunięć
  1. 1 1
      app/Console/Commands/CheckCommission.php
  2. 3 0
      app/Console/Commands/Test.php
  3. 4 4
      app/Console/Commands/V2boardStatistics.php
  4. 5 2
      app/Http/Controllers/Admin/ConfigController.php
  5. 23 11
      app/Http/Controllers/Admin/OrderController.php
  6. 78 0
      app/Http/Controllers/Admin/PaymentController.php
  7. 1 4
      app/Http/Controllers/Admin/Server/ManageController.php
  8. 1 5
      app/Http/Controllers/Admin/Server/ShadowsocksController.php
  9. 0 4
      app/Http/Controllers/Admin/Server/TrojanController.php
  10. 0 4
      app/Http/Controllers/Admin/Server/V2rayController.php
  11. 8 7
      app/Http/Controllers/Admin/StatController.php
  12. 26 0
      app/Http/Controllers/Admin/UserController.php
  13. 18 0
      app/Http/Controllers/Guest/CommController.php
  14. 0 192
      app/Http/Controllers/Guest/OrderController.php
  15. 49 0
      app/Http/Controllers/Guest/PaymentController.php
  16. 6 2
      app/Http/Controllers/Passport/AuthController.php
  17. 1 0
      app/Http/Controllers/Server/DeepbworkController.php
  18. 1 0
      app/Http/Controllers/Server/PoseidonController.php
  19. 1 0
      app/Http/Controllers/Server/ShadowsocksTidalabController.php
  20. 1 0
      app/Http/Controllers/Server/TrojanTidalabController.php
  21. 13 0
      app/Http/Controllers/User/CommController.php
  22. 17 2
      app/Http/Controllers/User/KnowledgeController.php
  23. 24 276
      app/Http/Controllers/User/OrderController.php
  24. 5 2
      app/Http/Middleware/User.php
  25. 6 1
      app/Http/Requests/Admin/ConfigSave.php
  26. 32 0
      app/Http/Requests/Admin/OrderFetch.php
  27. 6 1
      app/Http/Requests/Admin/UserFetch.php
  28. 3 2
      app/Http/Requests/Admin/UserUpdate.php
  29. 7 0
      app/Http/Routes/AdminRoute.php
  30. 4 6
      app/Http/Routes/GuestRoute.php
  31. 1 0
      app/Http/Routes/UserRoute.php
  32. 12 0
      app/Models/Payment.php
  33. 96 0
      app/Payments/AlipayF2F.php
  34. 69 0
      app/Payments/EPay.php
  35. 90 0
      app/Payments/MGate.php
  36. 114 0
      app/Payments/StripeAlipay.php
  37. 121 0
      app/Payments/StripeCredit.php
  38. 114 0
      app/Payments/StripeWepay.php
  39. 1 1
      app/Services/OrderService.php
  40. 54 0
      app/Services/PaymentService.php
  41. 21 20
      app/Services/ServerService.php
  42. 3 0
      app/Utils/CacheKey.php
  43. 1 1
      app/Utils/Helper.php
  44. 2 2
      app/Utils/URLSchemes.php
  45. 1 1
      config/app.php
  46. 56 40
      database/install.sql
  47. 17 0
      database/update.sql
  48. 1 1
      init.sh
  49. 0 93
      library/BitpayX.php
  50. 0 42
      library/Epay.php
  51. 0 60
      library/MGate.php
  52. 0 0
      public/assets/admin/components.async.js
  53. 0 0
      public/assets/admin/components.chunk.css
  54. 0 0
      public/assets/admin/umi.js
  55. 0 0
      public/assets/admin/vendors.async.js
  56. 0 0
      public/assets/user/components.async.js
  57. 3 1
      public/assets/user/env.example.js
  58. 0 0
      public/assets/user/umi.css
  59. 0 0
      public/assets/user/umi.js
  60. 0 0
      public/assets/user/vendors.async.js
  61. 4 1
      resources/lang/en-US/user.php
  62. 3 0
      resources/lang/zh-CN/user.php
  63. 2 1
      resources/views/app.blade.php
  64. 2 1
      routes/web.php

+ 1 - 1
app/Console/Commands/CheckCommission.php

@@ -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

+ 3 - 0
app/Console/Commands/Test.php

@@ -2,6 +2,7 @@
 
 namespace App\Console\Commands;
 
+use App\Services\PaymentService;
 use Illuminate\Console\Command;
 
 class Test extends Command
@@ -37,5 +38,7 @@ class Test extends Command
      */
     public function handle()
     {
+        $paymentService = new PaymentService('MGate');
+        var_dump($paymentService->form());
     }
 }

+ 4 - 4
app/Console/Commands/V2boardStatistics.php

@@ -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,11 @@ 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)
+            ->where('commission_status', 0);
         $commissionCount = $builder->count();
         $commissionAmount = $builder->sum('commission_balance');
         $data = [

+ 5 - 2
app/Http/Controllers/Admin/ConfigController.php

@@ -66,7 +66,8 @@ 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),
@@ -110,7 +111,9 @@ class ConfigController extends Controller
                     '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'),

+ 23 - 11
app/Http/Controllers/Admin/OrderController.php

@@ -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 - 0
app/Http/Controllers/Admin/PaymentController.php

@@ -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()
+        ]);
+    }
+}

+ 1 - 4
app/Http/Controllers/Admin/Server/ManageController.php

@@ -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;
@@ -24,6 +20,7 @@ class ManageController extends Controller
             $serverService->getV2rayServers(),
             $serverService->getTrojanServers()
         );
+        $serverService->mergeData($servers);
         $tmp = array_column($servers, 'sort');
         array_multisort($tmp, SORT_ASC, $servers);
         return response([

+ 1 - 5
app/Http/Controllers/Admin/Server/ShadowsocksController.php

@@ -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
 {

+ 0 - 4
app/Http/Controllers/Admin/Server/TrojanController.php

@@ -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
 {

+ 0 - 4
app/Http/Controllers/Admin/Server/V2rayController.php

@@ -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
 {

+ 8 - 7
app/Http/Controllers/Admin/StatController.php

@@ -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([

+ 26 - 0
app/Http/Controllers/Admin/UserController.php

@@ -265,4 +265,30 @@ class UserController extends Controller
             'data' => true
         ]);
     }
+
+    public function setInviteUser(Request $request)
+    {
+        $request->validate([
+            'user_id' => 'required|integer',
+            'invite_user' => 'required',
+        ], [
+            'user_id.required' => '用户ID不能为空',
+            'user_id.integer' => '用户ID参数有误',
+            'invite_user.required' => '邀请人不能为空'
+        ]);
+
+        $user = User::find($request->input('user_id'));
+        if (!$user) abort(500, '用户不存在');
+        if (strpos($request->input('invite_user'), '@') !== -1) {
+            $inviteUser = User::where('email', $request->input('invite_user'))->first();
+        } else {
+            $inviteUser = User::find($request->input('invite_user'));
+        }
+        if (!$inviteUser) abort(500, '邀请人不存在');
+        $user->invite_user_id = $inviteUser->id;
+
+        return response([
+            'data' => $user->save()
+        ]);
+    }
 }

+ 18 - 0
app/Http/Controllers/Guest/CommController.php

@@ -0,0 +1,18 @@
+<?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')
+            ]
+        ]);
+    }
+}

+ 0 - 192
app/Http/Controllers/Guest/OrderController.php

@@ -1,192 +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)
-    {
-        if (!(int)config('v2board.alipay_enable')) {
-            die('fail');
-        }
-        // 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()));
-
-        if (!(int)config('v2board.stripe_alipay_enable') && !(int)config('v2board.stripe_wepay_enable')) {
-            die('fail');
-        }
-        \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)
-    {
-        if (!(int)config('v2board.bitpayx_enable')) {
-            die('fail');
-        }
-        $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)
-    {
-        if (!(int)config('v2board.mgate_enable')) {
-            die('fail');
-        }
-        $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)
-    {
-        if (!(int)config('v2board.epay_enable')) {
-            die('fail');
-        }
-        $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 - 0
app/Http/Controllers/Guest/PaymentController.php

@@ -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('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;
+    }
+}

+ 6 - 2
app/Http/Controllers/Passport/AuthController.php

@@ -131,7 +131,8 @@ class AuthController extends Controller
         }
 
         $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);
@@ -202,7 +203,10 @@ 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')));
+        $user = User::where('email', $authData[0])
+            ->where('password', $authData[1])
+            ->first();
         if (!$user) {
             abort(500, '令牌有误');
         }

+ 1 - 0
app/Http/Controllers/Server/DeepbworkController.php

@@ -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 {

+ 1 - 0
app/Http/Controllers/Server/PoseidonController.php

@@ -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;

+ 1 - 0
app/Http/Controllers/Server/ShadowsocksTidalabController.php

@@ -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 {

+ 1 - 0
app/Http/Controllers/Server/TrojanTidalabController.php

@@ -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 {

+ 13 - 0
app/Http/Controllers/User/CommController.php

@@ -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']
+        ]);
+    }
 }

+ 17 - 2
app/Http/Controllers/User/KnowledgeController.php

@@ -20,8 +20,14 @@ class KnowledgeController extends Controller
             if (!$knowledge) abort(500, __('user.knowledge.fetch.knowledge_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');
+            if ($userService->isAvailable($user)) {
+                $appleId = config('v2board.apple_id');
+                $appleIdPassword = config('v2board.apple_id_password');
+            } else {
+                $appleId = __('user.knowledge.fetch.apple_id_must_be_plan');
+                $appleIdPassword = __('user.knowledge.fetch.apple_id_must_be_plan');
+                $this->formatAccessData($knowledge['body']);
+            }
             $subscribeUrl = config('v2board.subscribe_url', config('v2board.app_url', env('APP_URL'))) . '/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']);
@@ -51,4 +57,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">'. __('user.knowledge.formatAccessData.no_access') .'</div>', $body);
+        }
+    }
 }

+ 24 - 276
app/Http/Controllers/User/OrderController.php

@@ -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'])
         ]);
     }
 
@@ -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, __('user.order.checkout.pay_method_not_use'));
+        $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)
@@ -258,65 +209,15 @@ 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
         ]);
     }
 
@@ -342,157 +243,4 @@ class OrderController extends Controller
             '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
-        ]);
-    }
 }

+ 5 - 2
app/Http/Middleware/User.php

@@ -15,8 +15,11 @@ 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 ($request->input('auth_data')) {
+            $authData = explode(':', base64_decode($request->input('auth_data')));
+            $user = \App\Models\User::where('password', $authData[1])
+                ->where('email', $authData[0])
+                ->first();
             if ($user) {
                 $request->session()->put('email', $user->email);
                 $request->session()->put('id', $user->id);

+ 6 - 1
app/Http/Requests/Admin/ConfigSave.php

@@ -41,6 +41,7 @@ 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',
@@ -87,6 +88,8 @@ class ConfigSave extends FormRequest
             '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 +121,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 - 0
app/Http/Requests/Admin/OrderFetch.php

@@ -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' => '过滤条件参数有误',
+        ];
+    }
+}

+ 6 - 1
app/Http/Requests/Admin/UserFetch.php

@@ -15,7 +15,7 @@ class UserFetch extends FormRequest
     {
         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.*.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' => '过滤值不能为空'
         ];
     }
 }

+ 3 - 2
app/Http/Requests/Admin/UserUpdate.php

@@ -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',
@@ -57,7 +57,8 @@ class UserUpdate extends FormRequest
             'u.integer' => '上行流量格式不正确',
             'd.integer' => '下行流量格式不正确',
             'balance.integer' => '余额格式不正确',
-            'commission_balance.integer' => '佣金格式不正确'
+            'commission_balance.integer' => '佣金格式不正确',
+            'password.min' => '密码长度最小8位'
         ];
     }
 }

+ 7 - 0
app/Http/Routes/AdminRoute.php

@@ -74,6 +74,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 +99,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');
         });
     }
 }

+ 4 - 6
app/Http/Routes/GuestRoute.php

@@ -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');
         });
     }
 }

+ 1 - 0
app/Http/Routes/UserRoute.php

@@ -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 - 0
app/Models/Payment.php

@@ -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 - 0
app/Payments/AlipayF2F.php

@@ -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 - 0
app/Payments/EPay.php

@@ -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 - 0
app/Payments/MGate.php

@@ -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 - 0
app/Payments/StripeAlipay.php

@@ -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 - 0
app/Payments/StripeCredit.php

@@ -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 - 0
app/Payments/StripeWepay.php

@@ -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];
+    }
+}

+ 1 - 1
app/Services/OrderService.php

@@ -141,7 +141,7 @@ class OrderService
     private function haveValidOrder(User $user)
     {
         return Order::where('user_id', $user->id)
-            ->whereIn('status', [3, 4])
+            ->whereNotIn('status', [0, 2])
             ->first();
     }
 

+ 54 - 0
app/Services/PaymentService.php

@@ -0,0 +1,54 @@
+<?php
+
+namespace App\Services;
+
+
+use App\Models\Payment;
+
+class PaymentService
+{
+    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);
+    }
+
+    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;
+    }
+}

+ 21 - 20
app/Services/ServerService.php

@@ -298,13 +298,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 +320,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 +333,29 @@ 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']));
+        }
+        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 {
-                $server[$i]['last_check_at'] = Cache::get(CacheKey::get('SERVER_TROJAN_LAST_CHECK_AT', $server[$i]['id']));
+                $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;
             }
-            $server[$i]['available'] = (time() - 300) < $server[$i]['last_check_at'];
         }
-        return $server->toArray();
     }
 }

+ 3 - 0
app/Utils/CacheKey.php

@@ -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'
     ];

+ 1 - 1
app/Utils/Helper.php

@@ -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];
     }

+ 2 - 2
app/Utils/URLSchemes.php

@@ -36,9 +36,9 @@ class URLSchemes
             "v" => "2",
             "ps" => $server['name'],
             "add" => $server['host'],
-            "port" => $server['port'],
+            "port" => (string)$server['port'],
             "id" => $user['uuid'],
-            "aid" => $server['alter_id'],
+            "aid" => (string)$server['alter_id'],
             "net" => $server['network'],
             "type" => "none",
             "host" => "",

+ 1 - 1
config/app.php

@@ -236,5 +236,5 @@ return [
     | The only modification by laravel config
     |
     */
-    'version' => '1.5.0'
+    'version' => '1.5.1.1621339182281'
 ];

+ 56 - 40
database/install.sql

@@ -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,27 @@ 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_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 +347,4 @@ CREATE TABLE `v2_user` (
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
 
--- 2021-01-21 14:25:17
+-- 2021-05-06 16:14:04

+ 17 - 0
database/update.sql

@@ -394,3 +394,20 @@ 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`;

+ 1 - 1
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

+ 0 - 93
library/BitpayX.php

@@ -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;
-    }
-}

+ 0 - 42
library/Epay.php

@@ -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;
-    }
-}

+ 0 - 60
library/MGate.php

@@ -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;
-    }
-}

Plik diff jest za duży
+ 0 - 0
public/assets/admin/components.async.js


Plik diff jest za duży
+ 0 - 0
public/assets/admin/components.chunk.css


Plik diff jest za duży
+ 0 - 0
public/assets/admin/umi.js


Plik diff jest za duży
+ 0 - 0
public/assets/admin/vendors.async.js


Plik diff jest za duży
+ 0 - 0
public/assets/user/components.async.js


+ 3 - 1
public/assets/user/env.example.js

@@ -12,5 +12,7 @@ window.settings = {
     color: 'default'
   },
   // 背景
-  background_url: ''
+  background_url: '',
+  // crisp
+  crisp_id: ''
 }

Plik diff jest za duży
+ 0 - 0
public/assets/user/umi.css


Plik diff jest za duży
+ 0 - 0
public/assets/user/umi.js


Plik diff jest za duży
+ 0 - 0
public/assets/user/vendors.async.js


+ 4 - 1
resources/lang/en-US/user.php

@@ -104,7 +104,7 @@ return [
         ],
         'stripeCard' => [
             'currency_convert_timeout' => 'Currency conversion has timed out, please try again later',
-            'was_problem' => 'Oops, there's a problem... Please refresh the page and try again later',
+            'was_problem' => "Oops, there's a problem... Please refresh the page and try again later",
             'deduction_failed' => 'Payment failed. Please check your credit card information'
         ]
     ],
@@ -112,6 +112,9 @@ return [
         'fetch' => [
             'knowledge_not_exist' => 'Article does not exist',
             'apple_id_must_be_plan' => 'No active subscription. Unable to use our provided Apple ID'
+        ],
+        'formatAccessData' => [
+            'no_access' => 'You must have a valid subscription to view content in this area'
         ]
     ],
     'invite' => [

+ 3 - 0
resources/lang/zh-CN/user.php

@@ -112,6 +112,9 @@ return [
         'fetch' => [
             'knowledge_not_exist' => '文章不存在',
             'apple_id_must_be_plan' => '无有效订阅,无法使用本站提供的 AppleID'
+        ],
+        'formatAccessData' => [
+            'no_access' => '你必须拥有有效的订阅才可以查看该区域的内容'
         ]
     ],
     'invite' => [

+ 2 - 1
resources/views/app.blade.php

@@ -20,7 +20,8 @@
             },
             verison: '{{$verison}}',
             background_url: '{{$backgroun_url}}',
-            description: '{{$description}}'
+            description: '{{$description}}',
+            crisp_id: '{{$crisp_id}}'
         }
     </script>
 </head>

+ 2 - 1
routes/web.php

@@ -26,7 +26,8 @@ Route::get('/', function (Request $request) {
         'theme_color' => config('v2board.frontend_theme_color', 'default'),
         'backgroun_url' => config('v2board.frontend_background_url'),
         'verison' => config('app.version'),
-        'description' => config('v2board.app_description', 'V2Board is best')
+        'description' => config('v2board.app_description', 'V2Board is best'),
+        'crisp_id' => config('v2board.frontend_customer_service_method') === 'crisp' ? config('v2board.frontend_customer_service_id') : ''
     ]);
 });
 

Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików