Jelajahi Sumber

falst

git push --force#	new file:   vendor/fakerphp/faker/src/Faker/Provider/en_GB/Person.php
kenan 1 tahun lalu
induk
melakukan
c80e6b1f98
100 mengubah file dengan 16039 tambahan dan 1 penghapusan
  1. TEMPAT SAMPAH
      Modules/.DS_Store
  2. TEMPAT SAMPAH
      Modules/Api/.DS_Store
  3. TEMPAT SAMPAH
      Modules/Api/Controllers/.DS_Store
  4. 27 0
      Modules/Api/Controllers/BaseController.php
  5. 40 0
      Modules/Api/Controllers/MerchantChannelController.php
  6. 85 0
      Modules/Api/Controllers/MerchantPayController.php
  7. 49 0
      Modules/Api/Controllers/OrderController.php
  8. 11 0
      Modules/Api/Controllers/TestController.php
  9. 50 0
      Modules/Api/Services/ConfigService.php
  10. 118 0
      Modules/Api/Services/PaymentService.php
  11. TEMPAT SAMPAH
      Modules/ManageApi/.DS_Store
  12. TEMPAT SAMPAH
      Modules/ManageApi/Controllers/.DS_Store
  13. 58 0
      Modules/ManageApi/Controllers/AuthController.php
  14. 30 0
      Modules/ManageApi/Controllers/BaseController.php
  15. 223 0
      Modules/ManageApi/Controllers/ManagerController.php
  16. 250 0
      Modules/ManageApi/Controllers/MerchantController.php
  17. 269 0
      Modules/ManageApi/Controllers/OrderController.php
  18. 538 0
      Modules/ManageApi/Controllers/PayController.php
  19. 109 0
      Modules/ManageApi/Middlewares/ManageAuthMiddleware.php
  20. 110 0
      Modules/ManageApi/Services/ManageAuthService.php
  21. 23 0
      Modules/ManageApi/Services/MerchantServices.php
  22. 95 1
      README.md
  23. TEMPAT SAMPAH
      app/.DS_Store
  24. 52 0
      app/Console/Commands/OrderExpired.php
  25. 42 0
      app/Console/Kernel.php
  26. 55 0
      app/Constants/CommonConstant.php
  27. 10 0
      app/Constants/ErrorCodeConstant.php
  28. 29 0
      app/Constants/ManageConstant.php
  29. 20 0
      app/Constants/RedisKeyConstant.php
  30. 73 0
      app/Events/PayTotalEvent.php
  31. 39 0
      app/Exceptions/ApiException.php
  32. 50 0
      app/Exceptions/Handler.php
  33. 4 0
      app/Helpers/autoload.func.php
  34. 185 0
      app/Helpers/common.func.php
  35. 28 0
      app/Helpers/helpers.func.php
  36. 13 0
      app/Http/Controllers/Controller.php
  37. 74 0
      app/Http/Kernel.php
  38. 21 0
      app/Http/Middleware/Authenticate.php
  39. 17 0
      app/Http/Middleware/EncryptCookies.php
  40. 17 0
      app/Http/Middleware/PreventRequestsDuringMaintenance.php
  41. 32 0
      app/Http/Middleware/RedirectIfAuthenticated.php
  42. 19 0
      app/Http/Middleware/TrimStrings.php
  43. 20 0
      app/Http/Middleware/TrustHosts.php
  44. 28 0
      app/Http/Middleware/TrustProxies.php
  45. 17 0
      app/Http/Middleware/VerifyCsrfToken.php
  46. 58 0
      app/Jobs/SendTgMegJob.php
  47. 71 0
      app/Listeners/PayTotalIncListener.php
  48. 8 0
      app/Models/AdminModel.php
  49. 46 0
      app/Models/BaseModel.php
  50. 8 0
      app/Models/ConfigModel.php
  51. 30 0
      app/Models/MerchantModel.php
  52. 8 0
      app/Models/MerchantPaymentTypeModel.php
  53. 23 0
      app/Models/OrderModel.php
  54. 23 0
      app/Models/PayTotalModel.php
  55. 25 0
      app/Models/PaymentAttrModel.php
  56. 25 0
      app/Models/PaymentModel.php
  57. 35 0
      app/Models/PaymentTypeModel.php
  58. 44 0
      app/Models/User.php
  59. 28 0
      app/Providers/AppServiceProvider.php
  60. 30 0
      app/Providers/AuthServiceProvider.php
  61. 21 0
      app/Providers/BroadcastServiceProvider.php
  62. 49 0
      app/Providers/EventServiceProvider.php
  63. 70 0
      app/Providers/RouteServiceProvider.php
  64. TEMPAT SAMPAH
      app/Repositories/.DS_Store
  65. 81 0
      app/Repositories/JwtConstraints/NoExpiredValidConstraint.php
  66. 118 0
      app/Repositories/OrderRepository.php
  67. TEMPAT SAMPAH
      app/Repositories/Service/.DS_Store
  68. 147 0
      app/Repositories/Service/CommonService.php
  69. 145 0
      app/Repositories/Service/Payment/DahePayment.php
  70. 37 0
      app/Repositories/Service/Payment/ErrorPayment.php
  71. 166 0
      app/Repositories/Service/Payment/FurongPayment.php
  72. 168 0
      app/Repositories/Service/Payment/GdPayment.php
  73. 185 0
      app/Repositories/Service/Payment/JingchengPayment.php
  74. 165 0
      app/Repositories/Service/Payment/JuPayment.php
  75. 166 0
      app/Repositories/Service/Payment/JutwoPayment.php
  76. 138 0
      app/Repositories/Service/Payment/KaopuPayment.php
  77. 189 0
      app/Repositories/Service/Payment/MayiPayment.php
  78. 184 0
      app/Repositories/Service/Payment/MingsenPayment.php
  79. 193 0
      app/Repositories/Service/Payment/PaymentInterface.php
  80. 161 0
      app/Repositories/Service/Payment/UsdtPayment.php
  81. 163 0
      app/Repositories/Service/Payment/WeihuiPayment.php
  82. 146 0
      app/Repositories/Service/Payment/XindaPayment.php
  83. 184 0
      app/Repositories/Service/Payment/XiyouPayment.php
  84. 187 0
      app/Repositories/Service/Payment/ZuoxiangPayment.php
  85. 248 0
      app/Repositories/Utils/AccessControllerListsRepository.php
  86. 249 0
      app/Repositories/Utils/JwtRepository.php
  87. 53 0
      artisan
  88. 55 0
      bootstrap/app.php
  89. 2 0
      bootstrap/cache/.gitignore
  90. 69 0
      composer.json
  91. 8070 0
      composer.lock
  92. 215 0
      config/app.php
  93. 111 0
      config/auth.php
  94. 67 0
      config/broadcasting.php
  95. 110 0
      config/cache.php
  96. 34 0
      config/cors.php
  97. 151 0
      config/database.php
  98. 76 0
      config/filesystems.php
  99. 52 0
      config/hashing.php
  100. 22 0
      config/jwt.php

TEMPAT SAMPAH
Modules/.DS_Store


TEMPAT SAMPAH
Modules/Api/.DS_Store


TEMPAT SAMPAH
Modules/Api/Controllers/.DS_Store


+ 27 - 0
Modules/Api/Controllers/BaseController.php

@@ -0,0 +1,27 @@
+<?php
+/**
+ * 描述
+ * @auth nj911<nj-x@gmail.com>
+ * @date: 2021-04-18 12:24
+ */
+namespace Modules\Api\Controllers;
+
+use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
+use Illuminate\Foundation\Bus\DispatchesJobs;
+use Illuminate\Foundation\Validation\ValidatesRequests;
+use Illuminate\Routing\Controller as RoutingController;
+
+class BaseController extends RoutingController
+{
+    use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
+
+    /**
+     * 获取body json数组.
+     * @return array
+     */
+    protected function getJsonData()
+    {
+        $arrJson = request()->all();
+        return $arrJson['data'] ?? [];
+    }
+}

+ 40 - 0
Modules/Api/Controllers/MerchantChannelController.php

@@ -0,0 +1,40 @@
+<?php
+
+namespace Modules\Api\Controllers;
+
+use App\Models\MerchantModel;
+use Illuminate\Support\Facades\Validator;
+use Modules\Api\Services\PaymentService;
+
+class MerchantChannelController extends BaseController
+{
+    /**
+     *  商户支付通道列表
+     *
+     * @return \Illuminate\Http\JsonResponse|mixed
+     */
+    public function channelLists()
+    {
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'merchant_code' => 'required|string',
+            'key' => 'required|string',
+        ];
+
+        $validator = Validator::make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $merchantInfo = MerchantModel::query()->where(['merchant_code' => $inputData['merchant_code'], 'key' => $inputData['key'], 'status' => 1])->first();
+        if (empty($merchantInfo)) {
+            return json_fail('该商户不存在');
+        }
+
+        $res = make(PaymentService::class)->getAllPayments($merchantInfo->uid);
+
+        return json_success($res);
+    }
+}

+ 85 - 0
Modules/Api/Controllers/MerchantPayController.php

@@ -0,0 +1,85 @@
+<?php
+
+namespace Modules\Api\Controllers;
+
+use App\Models\MerchantModel;
+use App\Models\MerchantPaymentTypeModel;
+use App\Models\PaymentTypeModel;
+use Illuminate\Support\Facades\Validator;
+use Modules\Api\Services\PaymentService;
+
+class MerchantPayController extends BaseController
+{
+    public function getPayUrl()
+    {
+        $inputData = $this->getJsonData();
+        $rules = [
+            'merchant_code' => 'required|string',
+            'payment_id' => 'required|integer',
+            'amount' => 'required',
+            'order_id' => 'required|string',
+            'notify_url' => 'required|string',
+            'redirect_url' => 'required|string',
+            'product_name' => 'nullable|string',
+            'signature' => 'required',
+            'ip' => 'required'
+        ];
+
+        $validator = Validator::make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            logger($validator->errors()->first());
+            return json_fail($validator->errors()->first());
+        }
+
+        $merchantInfo = MerchantModel::query()->where(['merchant_code' => $inputData['merchant_code'], 'status' => 1])->first();
+        if (empty($merchantInfo)) {
+            logger('该商户没有开通!');
+            return json_fail('该商户没有开通!');
+        }
+
+        $paymentTypeInfo = PaymentTypeModel::query()->where(['id' => $inputData['payment_id'], 'status' => 1])->first(['uid', 'pay_type', 'payment_uid', 'payment_attr_uid']);
+        if (empty($paymentTypeInfo)) {
+            logger('该支付通道不存在!');
+            return json_fail('该支付通道不存在!');
+        }
+
+        $mpt = MerchantPaymentTypeModel::query()->where(['merchant_uid' => $merchantInfo->uid, 'payment_type_uid' => $paymentTypeInfo->uid])->count('id');
+        if ($mpt == 0) {
+            logger('商户没有分配该通道!');
+            return json_fail('商户没有分配该通道!');
+        }
+
+        // 验签
+        $signData = [
+            'merchant_code' => $inputData['merchant_code'],
+            'order_id' => $inputData['order_id'],
+            'product_name' => $inputData['product_name'],
+            'amount' => $inputData['amount'],
+            'notify_url' => $inputData['notify_url'],
+            'redirect_url' => $inputData['redirect_url'],
+            'payment_id' => $inputData['payment_id']
+        ];
+        $sign = build_sign($signData, $merchantInfo->key);
+
+        if ($inputData['signature'] != $sign) {
+            logger('签名失败!');
+            return json_fail('签名失败!');
+        }
+
+        $order = make(PaymentService::class)->createdOrder($merchantInfo->uid, $paymentTypeInfo, $inputData, $merchantInfo->merchant_mark);
+        if (!$order) {
+            logger('订单创建失败!');
+            return json_fail('订单创建失败!');
+        }
+
+        // 获取支付链接
+        $payLink = make(PaymentService::class)->createPayLink($order);
+        if (empty($payLink)) {
+            logger('获取支付链接失败!', $order);
+            return json_fail('获取支付链接失败');
+        }
+
+        return json_success(['pay_url' => $payLink]);
+    }
+}

+ 49 - 0
Modules/Api/Controllers/OrderController.php

@@ -0,0 +1,49 @@
+<?php
+
+namespace Modules\Api\Controllers;
+
+use App\Events\PayTotalEvent;
+use App\Exceptions\ApiException;
+use App\Models\OrderModel;
+use App\Models\PaymentModel;
+use App\Repositories\OrderRepository;
+use App\Repositories\Service\Payment\PaymentInterface;
+use Illuminate\Support\Facades\Cache;
+
+class OrderController
+{
+    public function notify($orderId, $payCode)
+    {
+        logger(json_encode(request()->all()) . '发送回调信息!');
+        // 写回调记录
+        if (empty($orderId) || empty($payCode)) {
+            throw new ApiException('参数错误');
+        }
+
+        $orderId = intval($orderId);
+        $payCode = strval($payCode);
+        $orderInfo = OrderModel::query()->where('id', $orderId)->first();
+        if (empty($orderInfo)) {
+            throw new ApiException('订单异常!');
+        }
+
+        $payInterface = PaymentInterface::factory($payCode);
+        if ($orderInfo->status == 2) {
+            return $payInterface->stopNotify();
+        }
+
+        $payment = PaymentModel::query()->where('pay_code', $payCode)->first(['partner_id']);
+
+        $result = $payInterface->notify($orderSn, $tradeNo, $money, $payment->partner_id);
+        if ($result) {
+            $orderRes = app(OrderRepository::class)->doBuy($orderInfo->toArray(), $tradeNo, $money);
+
+            if ($orderRes) {
+                event(new PayTotalEvent($orderInfo->merchant_uid, $orderInfo->payment_uid, $orderInfo->payment_attr_uid, $orderInfo->real_amount, 2));
+                return $payInterface->stopNotify();
+            }
+        }
+
+        return 'nook';
+    }
+}

+ 11 - 0
Modules/Api/Controllers/TestController.php

@@ -0,0 +1,11 @@
+<?php
+
+namespace Modules\Api\Controllers;
+
+class TestController extends BaseController
+{
+    public function index()
+    {
+        payLogger(__CLASS__, '测试测试');
+    }
+}

+ 50 - 0
Modules/Api/Services/ConfigService.php

@@ -0,0 +1,50 @@
+<?php
+
+namespace Modules\Api\Services;
+
+use App\Constants\RedisKeyConstant;
+use App\Models\ConfigModel;
+use Illuminate\Support\Facades\Cache;
+
+class ConfigService
+{
+    /**
+     * 获取缓存配置
+     *
+     * @return mixed
+     */
+    public function getConfig()
+    {
+        $config = Cache::remember(RedisKeyConstant::CONFIG, 60 * 60 * 24, function () {
+            return ConfigModel::query()->get(['name', 'key', 'value'])->keyBy('key')->toArray();
+        });
+
+        return $config;
+    }
+
+    /**
+     * 获取单个缓存
+     *
+     * @param string $key
+     * @return mixed
+     */
+    public function getValue(string $key)
+    {
+
+        $keyList = $this->getConfig();
+        if (isset($keyList[$key])) {
+            return $keyList[$key]['value'];
+        } else {
+            $cacheKey = 'config_'. $key;
+            $value = Cache::get($cacheKey);
+            if (empty($value)) {
+                $confInfo = ConfigModel::query()->where('key', $key)->first('value');
+
+                Cache::put($key, $confInfo['value']);
+                return $confInfo['value'];
+            }
+
+            return $value;
+        }
+    }
+}

+ 118 - 0
Modules/Api/Services/PaymentService.php

@@ -0,0 +1,118 @@
+<?php
+declare(strict_types=1);
+
+namespace Modules\Api\Services;
+
+use App\Constants\RedisKeyConstant;
+use App\Events\PayTotalEvent;
+use App\Models\MerchantPaymentTypeModel;
+use App\Models\OrderModel;
+use App\Models\PaymentTypeModel;
+use App\Repositories\Service\Payment\PaymentInterface;
+use Illuminate\Support\Facades\Cache;
+
+class PaymentService
+{
+    /**
+     * 获取对应的商户通道
+     *
+     * @param $merchantUid
+     * @return mixed
+     */
+    public function getAllPayments($merchantUid)
+    {
+        $payment = Cache::remember(RedisKeyConstant::PAYMENT_TYPE . $merchantUid, 60 * 60 * 12, function () use ($merchantUid){
+            // 获取已经分配的支付通道
+            $relation = MerchantPaymentTypeModel::query()->where(['merchant_uid' => $merchantUid])->get(['payment_type_uid']);
+            if (empty($relation)) {
+                return [];
+            }
+
+            $paymentTypeUids = [];
+            foreach ($relation as $v){
+                $paymentTypeUids[] = $v->payment_type_uid;
+            }
+
+            $paymentLists = PaymentTypeModel::query()->whereIn('uid', $paymentTypeUids)->where('status', 1)->orderBy('weights', 'desc')->get(['id', 'name', 'pay_type', 'belong_device', 'limit', 'limit_type']);
+            if ($paymentLists->isEmpty()) {
+                return [];
+            } else {
+                return $paymentLists->toArray();
+            }
+        });
+
+        return $payment;
+    }
+
+    /**
+     * 创建系统订单
+     *
+     * @param $merchantUid  商户uid
+     * @param $paymentTypeInfo 支付类型
+     * @param $inputData 请求数据
+     * @return array|false
+     */
+    public function createdOrder(string $merchantUid, $paymentTypeInfo, $inputData, $merchantMark)
+    {
+        $data = [
+            'order_sn' => createOrderSn('PAY', $merchantMark),
+            'merchant_order_sn' => $inputData['order_id'],
+            'merchant_uid' => $merchantUid,
+            'order_amount' => $inputData['amount'],
+            'real_amount' => $inputData['amount'],
+            'status' => 1,
+            'notice_status' => 1,
+            'payment_uid' => $paymentTypeInfo->payment->uid ?? '',
+            'payment_attr_uid' => $paymentTypeInfo->paymentAttr->uid ?? '',
+            'pay_type' => $paymentTypeInfo->pay_type,
+            'notify_url' => $inputData['notify_url'],
+            'redirect_url' => $inputData['redirect_url'],
+            'product_name' => $inputData['product_name'],
+            'merchant_info' => json_encode($inputData),
+        ];
+
+        $res = OrderModel::query()->create($data);
+        if ($res) {
+            $data['pay_code'] = $paymentTypeInfo->payment->pay_code;
+            $data['pay_attr_code'] = $paymentTypeInfo->paymentAttr->code;
+            $data['order_id'] = $res->id;
+            $data['ip'] = $inputData['ip'];
+            $data['partner_id'] = $paymentTypeInfo->payment->partner_id;
+            event(new PayTotalEvent($merchantUid, $paymentTypeInfo->payment->uid, $paymentTypeInfo->paymentAttr->uid, (float)$inputData['amount'], 1));
+            return $data;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * 获取支付订单
+     *
+     * @param $order
+     * @return string
+     */
+    public function createPayLink($order)
+    {
+        $config = make(ConfigService::class)->getConfig();
+        $payUrl = $config['pay_notify_url']['value'];
+        $notifyUrl = trim($payUrl, '/');
+        $notifyUrl .= '/api/notify/' . $order['order_id'] . '/' . $order['pay_code'];
+        $payData = [
+            'order_id' => $order['order_id'],
+            'order_sn' => $order['order_sn'],
+            'money' => $order['real_amount'],
+            'pay_attr_code' => $order['pay_attr_code'],
+            'notify_url' => $notifyUrl,
+            'partner_id' => $order['partner_id'],
+            'pay_url' => $payUrl,
+            'ip' => $order['ip']
+        ];
+
+        $result = PaymentInterface::factory($order['pay_code'])->createPayLink($payData);
+        if ($result) {
+            return $result;
+        }
+
+        return '';
+    }
+}

TEMPAT SAMPAH
Modules/ManageApi/.DS_Store


TEMPAT SAMPAH
Modules/ManageApi/Controllers/.DS_Store


+ 58 - 0
Modules/ManageApi/Controllers/AuthController.php

@@ -0,0 +1,58 @@
+<?php
+
+namespace Modules\ManageApi\Controllers;
+
+use App\Models\AdminModel;
+use Modules\ManageApi\Services\ManageAuthService;
+use Validator;
+
+class AuthController extends BaseController
+{
+    /**
+     * Jwt受众.
+     *
+     * @var string
+     */
+    private $jwtAud = 'manage-users';
+
+    public function login()
+    {
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'account' => 'required|max:120',
+            'pass' => 'required|max:64',
+        ];
+        $validator = app('validator')->make($inputData, $rules);
+
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $adminInfo = AdminModel::query()
+            ->where(['account' => $inputData['account'], 'status' => 1])
+            ->first();
+        if (!$adminInfo) {
+            return json_fail('用户不存在');
+        }
+
+        $md5password = generatePassword($inputData['pass'], $adminInfo['salt']);
+        if ($md5password != $adminInfo['password']) {
+            return json_fail('密码错误,请输入正确的登陆密码');
+        }
+
+        $tokenInfo = app(ManageAuthService::class)->createSingleJwt($adminInfo['id'], $adminInfo['account']);
+
+        $data = [
+            'token' => $tokenInfo['token'],
+            'expired_at' => $tokenInfo['expired_at'],
+            'manager_id' => $adminInfo->id,
+        ];
+
+        $adminInfo->login_time = date('Y-m-d H:i:s');
+        $adminInfo->save();
+
+        return json_success($data);
+    }
+}

+ 30 - 0
Modules/ManageApi/Controllers/BaseController.php

@@ -0,0 +1,30 @@
+<?php
+
+namespace Modules\ManageApi\Controllers;
+
+use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
+use Illuminate\Foundation\Bus\DispatchesJobs;
+use Illuminate\Foundation\Validation\ValidatesRequests;
+use Illuminate\Routing\Controller as RoutingController;
+
+class BaseController extends RoutingController
+{
+    use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
+
+    /**
+     * 当前管理员UID.
+     *
+     * @var string
+     */
+    protected $ctManagerUid = '';
+
+    /**
+     * 获取
+     * @return array|mixed
+     */
+    protected function getJsonData()
+    {
+        $arrJson = request()->all();
+        return $arrJson['data'] ?? [];
+    }
+}

+ 223 - 0
Modules/ManageApi/Controllers/ManagerController.php

@@ -0,0 +1,223 @@
+<?php
+
+namespace Modules\ManageApi\Controllers;
+
+use App\Constants\CommonConstant;
+use App\Constants\RedisKeyConstant;
+use App\Models\AdminModel;
+use App\Models\ConfigModel;
+use App\Models\MerchantModel;
+use Illuminate\Support\Facades\Cache;
+
+class ManagerController extends BaseController
+{
+    public function lists()
+    {
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'page' => 'required|integer',
+            'page_size' => 'required|integer',
+        ];
+
+        $validator = app('validator')->make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $pageSize = $inputData['page_size'] ?? CommonConstant::PAGE_SIZE;
+        if ($pageSize > 500) {
+            $pageSize = CommonConstant::PAGE_SIZE;
+        }
+        $page = format_page($inputData['page'] ?? 0);
+
+        $res = AdminModel::query()->orderByDesc('created_at')->paginate($pageSize,
+            ['id', 'account', 'created_at', 'updated_at', 'login_time', 'status'], 'page', $page);
+
+        return json_success($res);
+    }
+
+    /**
+     * 添加管理员
+     *
+     * @return \Illuminate\Http\JsonResponse|mixed
+     * @throws \Illuminate\Contracts\Container\BindingResolutionException
+     */
+    public function add()
+    {
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'account' => 'required|max:120',
+            'pass' => 'required|max:64',
+        ];
+
+        $validator = app('validator')->make($inputData, $rules);
+
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $admin = AdminModel::query()->where('account', $inputData['account'])->first();
+        if ($admin) {
+            return json_fail('用户名重复,请重新创建');
+        }
+
+        $salt = random(6);
+        $data = [
+            'salt' => $salt,
+            'account' => $inputData['account'],
+            'password' => generatePassword($inputData['pass'], $salt),
+        ];
+
+        $res = AdminModel::query()->create($data);
+        if ($res) {
+            return json_success([], '创建成功');
+        } else {
+            return json_fail('创建失败');
+        }
+    }
+
+    /**
+     * 修改管理员
+     *
+     * @return \Illuminate\Http\JsonResponse|mixed
+     * @throws \Illuminate\Contracts\Container\BindingResolutionException
+     */
+    public function edit()
+    {
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'id' => 'required|integer',
+            'pass' => 'nullable|string',
+            'status' => 'required|in:1,2',
+        ];
+
+        $validator = app('validator')->make($inputData, $rules);
+
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $admin = AdminModel::query()->where('id', $inputData['id'])->first();
+        if (!$admin) {
+            return json_fail('该用户不存在');
+        }
+        if (!empty($inputData['pass'])) {
+            $admin->password = generatePassword($inputData['pass'], $admin->salt);
+        }
+        if (!empty($inputData['status'])) {
+            $admin->status = $inputData['status'];
+        }
+        $res = $admin->save();
+        if ($res) {
+            return json_success([], '修改成功');
+        } else {
+            return json_fail('修改失败');
+        }
+
+    }
+
+    /**
+     * 设置列表
+     *
+     * @return mixed
+     */
+    public function configLists()
+    {
+        $res = ConfigModel::query()->get(['name', 'key', 'value', 'id']);
+
+        return json_success($res);
+    }
+
+    /**
+     * 添加设置
+     *
+     * @return \Illuminate\Http\JsonResponse|mixed
+     * @throws \Illuminate\Contracts\Container\BindingResolutionException
+     */
+    public function configAdd()
+    {
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'name' => 'required|string',
+            'key' => 'required|string',
+            'value' => 'required|string',
+        ];
+
+        $validator = app('validator')->make($inputData, $rules);
+
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $data = [
+            'key' => $inputData['key'],
+            'value' => $inputData['value'],
+            'name' => $inputData['name']
+        ];
+
+        $res = ConfigModel::query()->create($data);
+        if ($res) {
+            Cache::forget(RedisKeyConstant::CONFIG);
+            return json_success([], '添加成功');
+        } else {
+            return json_fail('添加失败');
+        }
+    }
+
+    /**
+     * 修改设置
+     *
+     * @return \Illuminate\Http\JsonResponse|mixed
+     * @throws \Illuminate\Contracts\Container\BindingResolutionException
+     */
+    public function configEdit()
+    {
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'id' => 'required|integer',
+            'name' => 'nullable|string',
+            'key' => 'nullable|string',
+            'value' => 'nullable|string',
+        ];
+
+        $validator = app('validator')->make($inputData, $rules);
+
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $info = ConfigModel::query()->where('id', $inputData['id'])->first();
+        if (!$info) {
+            return json_fail('修改的设置不存在');
+        }
+
+        if (!empty($inputData['key'])) {
+            $info->key = $inputData['key'];
+        }
+
+        if (!empty($inputData['value'])) {
+            $info->value = $inputData['value'];
+        }
+
+        if (!empty($inputData['name'])) {
+            $info->name = $inputData['name'];
+        }
+
+        if ($info->save()) {
+            Cache::forget(RedisKeyConstant::CONFIG);
+            return json_success([], '修改成功');
+        } else {
+            return json_fail('修改失败');
+        }
+    }
+}

+ 250 - 0
Modules/ManageApi/Controllers/MerchantController.php

@@ -0,0 +1,250 @@
+<?php
+
+namespace Modules\ManageApi\Controllers;
+
+use App\Constants\CommonConstant;
+use App\Constants\RedisKeyConstant;
+use App\Models\MerchantModel;
+use App\Models\MerchantPaymentTypeModel;
+use App\Repositories\Service\CommonService;
+use Illuminate\Support\Facades\Cache;
+use Illuminate\Support\Facades\DB;
+
+class MerchantController extends BaseController
+{
+    public function lists()
+    {
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'page' => 'required|integer',
+            'page_size' => 'required|integer',
+            'username' => 'nullable|string',
+            'status' => 'nullable|in:1,2'
+        ];
+
+        $validator = app('validator')->make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $pageSize = $inputData['page_size'] ?? CommonConstant::PAGE_SIZE;
+        if ($pageSize > 500) {
+            $pageSize = CommonConstant::PAGE_SIZE;
+        }
+        $page = format_page($inputData['page'] ?? 0);
+
+        $model = MerchantModel::query();
+        if (!empty($inputData['username'])) {
+            $model = $model->where('username', 'like', '%' . $inputData['username'] . '%');
+        }
+
+        if (!empty($inputData['status'])) {
+            $model = $model->where('status', $inputData['status']);
+        }
+
+        $res = $model->orderByDesc('created_at')->paginate($pageSize, ['uid', 'username', 'account', 'status', 'merchant_code', 'key', 'cache_notice_url', 'created_at', 'updated_at', 'merchant_mark'], 'page', $page);
+
+        foreach ($res->items() as &$val) {
+            $payUid = $val->merchantPaymentType;
+            $uid = '';
+            foreach ($payUid as $uidV) {
+                $uid .= $uidV->payment_type_uid . ',';
+            }
+            $val->pay_uid = substr($uid, 0, -1);
+            unset($val->merchantPaymentType);
+        }
+
+        return json_success($res);
+    }
+
+    /**
+     * 创建商户
+     *
+     * @return \Illuminate\Http\JsonResponse|mixed
+     * @throws \Illuminate\Contracts\Container\BindingResolutionException
+     */
+    public function add()
+    {
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'username' => 'required|string',
+            'merchant_code' => 'required|string',
+            'account' => 'required|string',
+            'password' => 'required|string',
+            'cache_notice_url' => 'required|string',
+            'pay_uid' => 'required|string',
+            'merchant_mark' => 'required|string',
+        ];
+
+        $validator = app('validator')->make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $data = [
+            'username' => $inputData['username'],
+            'merchant_code' => $inputData['merchant_code'],
+            'merchant_mark' => $inputData['merchant_mark'],
+            'account' => $inputData['account'],
+            'cache_notice_url' => $inputData['cache_notice_url'],
+            'password' => generatePassword($inputData['password'], 1),
+            'key' => createKey()
+        ];
+
+        $res = MerchantModel::query()->create($data);
+        if ($res) {
+            $payUid = explode(',', $inputData['pay_uid']);
+            $mercPayData = [];
+            foreach ($payUid as $val) {
+                $mercPayData[] = [
+                    'merchant_uid' => $res['uid'],
+                    'payment_type_uid' => $val,
+                    'created_at' => date('Y-m-d H:i:s'),
+                    'updated_at' => date('Y-m-d H:i:s')
+                ];
+            }
+            $payRes = MerchantPaymentTypeModel::query()->insert($mercPayData);
+            if ($payRes) {
+                return json_success([], '创建成功');
+            } else {
+                return json_fail('创建失败');
+            }
+        } else {
+            return json_fail('创建失败');
+        }
+    }
+
+    /**
+     * 修改商户
+     *
+     * @return \Illuminate\Http\JsonResponse|mixed
+     * @throws \Illuminate\Contracts\Container\BindingResolutionException
+     */
+    public function edit()
+    {
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'username' => 'nullable|string',
+            'merchant_code' => 'nullable|string',
+            'merchant_mark' => 'nullable|string',
+            'password' => 'nullable|string',
+            'cache_notice_url' => 'nullable|string',
+            'pay_uid' => 'nullable|string',
+            'uid' => 'nullable|string',
+            'status' => 'nullable|in:1,2',
+        ];
+
+        $validator = app('validator')->make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+
+        $merchant = MerchantModel::query()->where('uid', $inputData['uid'])->first();
+        if (!$merchant) {
+            return json_fail('该商户不存在');
+        }
+
+        if (!empty($inputData['username'])) {
+            $merchant->username = $inputData['username'];
+        }
+
+        if (!empty($inputData['status'])) {
+            $merchant->status = $inputData['status'];
+        }
+
+        if (!empty($inputData['merchant_code'])) {
+            $merchant->merchant_code = $inputData['merchant_code'];
+        }
+
+        if (!empty($inputData['merchant_mark'])) {
+            $merchant->merchant_mark = $inputData['merchant_mark'];
+        }
+
+        if (!empty($inputData['cache_notice_url'])) {
+            $merchant->cache_notice_url = $inputData['cache_notice_url'];
+        }
+
+        if (!empty($inputData['password'])) {
+            $merchant->password = generatePassword($inputData['password'], 1);
+        }
+
+        if (!empty($inputData['pay_uid'])) {
+            $payUid = explode(',', $inputData['pay_uid']);
+
+            // 先删除 后插入
+            DB::beginTransaction();
+            MerchantPaymentTypeModel::query()->where('merchant_uid', $inputData['uid'])->delete();
+
+            $mercPayData = [];
+            foreach ($payUid as $val) {
+                $mercPayData[] = [
+                    'merchant_uid' => $inputData['uid'],
+                    'payment_type_uid' => $val,
+                    'created_at' => date('Y-m-d H:i:s'),
+                    'updated_at' => date('Y-m-d H:i:s')
+                ];
+            }
+            MerchantPaymentTypeModel::query()->insert($mercPayData);
+            DB::commit();
+        }
+
+        $res = $merchant->save();
+        if ($res) {
+            // 清除商户通道缓存
+            Cache::forget(RedisKeyConstant::PAYMENT_TYPE . $inputData['uid']);
+            // 清除商户缓存
+            app(CommonService::class)->clearMerchantCache($merchant['key'], $merchant['cache_notice_url']);
+            return json_success([], '修改成功');
+        } else {
+            return json_fail('修改失败');
+        }
+    }
+
+    /**
+     * 商户下拉
+     *
+     * @return mixed
+     */
+    public function dow()
+    {
+        $res = MerchantModel::query()->get(['uid', 'username'])->keyBy('uid');
+
+        return json_success($res);
+    }
+
+    /**
+     * 清除商户缓存
+     *
+     * @return \Illuminate\Http\JsonResponse|mixed
+     * @throws \Illuminate\Contracts\Container\BindingResolutionException
+     */
+    public function clearMerchantCache()
+    {
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'uid' => 'required|string',
+        ];
+        $validator = app('validator')->make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $merchant = MerchantModel::query()->where('uid', $inputData['uid'])->first();
+        if (!$merchant) {
+            return json_fail('该商户不存在');
+        }
+
+        app(CommonService::class)->clearMerchantCache($merchant['key'], $merchant['cache_notice_url']);
+
+        return json_success([]);
+    }
+}

+ 269 - 0
Modules/ManageApi/Controllers/OrderController.php

@@ -0,0 +1,269 @@
+<?php
+
+namespace Modules\ManageApi\Controllers;
+
+use App\Constants\CommonConstant;
+use App\Events\PayTotalEvent;
+use App\Models\OrderModel;
+use App\Models\PayTotalModel;
+use App\Repositories\OrderRepository;
+use Illuminate\Support\Carbon;
+
+class OrderController extends BaseController
+{
+    public function lists()
+    {
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'page' => 'required|integer',
+            'page_size' => 'required|integer',
+            'order_sn' => 'nullable|string',
+            'merchant_uid' => 'nullable|string',
+            'status' => 'nullable|in:1,2'
+        ];
+
+        $validator = app('validator')->make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $pageSize = $inputData['page_size'] ?? CommonConstant::PAGE_SIZE;
+        if ($pageSize > 500) {
+            $pageSize = CommonConstant::PAGE_SIZE;
+        }
+        $page = format_page($inputData['page'] ?? 0);
+
+        $model = OrderModel::query();
+        if (!empty($inputData['status'])) {
+            $model = $model->where('status', $inputData['status']);
+        }
+
+        if (!empty($inputData['order_sn'])) {
+            $model = $model->where('order_sn', $inputData['order_sn']);
+        }
+
+        if (!empty($inputData['merchant_order_sn'])) {
+            $model = $model->where('merchant_order_sn', $inputData['merchant_order_sn']);
+        }
+
+        if (!empty($inputData['merchant_uid'])) {
+            $model = $model->where('merchant_uid', $inputData['merchant_uid']);
+        }
+
+        if (!empty($inputData['start_date']) && !empty($inputData['end_date'])) {
+            $model = $model->whereBetween('created_at', [$inputData['start_date'], $inputData['end_date']]);
+        }
+
+        $lists = $model->orderByDesc('created_at')->paginate($pageSize, ['*'], 'page', $page);
+
+        $res = [];
+        foreach ($lists->items() as $k => $v) {
+            $res['data'][$k]['order_sn'] = $v->order_sn;
+            $res['data'][$k]['merchant_order_sn'] = $v->merchant_order_sn;
+            $res['data'][$k]['merchant_name'] = $v->merchant->username ?? '';
+            $res['data'][$k]['order_amount'] = $v->order_amount;
+            $res['data'][$k]['real_amount'] = $v->real_amount;
+            $res['data'][$k]['status'] = $v->status;
+            $res['data'][$k]['pay_type'] = $v->pay_type;
+            $res['data'][$k]['notice_status'] = $v->notice_status;
+            $res['data'][$k]['payment_name'] = $v->payment->name ?? '';
+            $res['data'][$k]['payment_attr_name'] = $v->paymentAttr->name ?? '';
+            $res['data'][$k]['created_at'] = !empty($v->created_at) ? Carbon::parse($v->created_at)->format('Y-m-d H:i:s') : $v->created_at;
+            $res['data'][$k]['updated_at'] = !empty($v->updated_at) ? Carbon::parse($v->updated_at)->format('Y-m-d H:i:s') : $v->updated_at;
+            $res['data'][$k]['notice_at'] = !empty($v->notice_at) ? Carbon::parse($v->notice_at)->format('Y-m-d H:i:s') :  $v->notice_at;
+        }
+        $res['total'] = $lists->total();
+
+        return json_success($res);
+    }
+
+    /**
+     * 补单
+     *
+     * @return \Illuminate\Http\JsonResponse|mixed
+     * @throws \Illuminate\Contracts\Container\BindingResolutionException
+     */
+    public function again()
+    {
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'order_sn' => 'required|string',
+        ];
+
+        $validator = app('validator')->make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $orderInfo = OrderModel::query()->where('order_sn', $inputData['order_sn'])->first();
+        if (empty($orderInfo)) {
+            return json_fail('订单不存在!');
+        }
+
+        if ($orderInfo->status == 2) {
+            return json_fail('请勿重复补单!');
+        }
+
+        $orderInfo->status = 2;
+        $orderInfo->remark = '手动补单';
+        if ($orderInfo->save()) {
+            // 统计事件
+            event(new PayTotalEvent($orderInfo->merchant_uid, $orderInfo->payment_uid, $orderInfo->payment_attr_uid, $orderInfo->real_amount, 2));
+            $noticeRes = app(OrderRepository::class)->sendSuccessCallback($inputData['order_sn']);
+            if ($noticeRes) {
+                return json_success('补单成功,并通知成功');
+            } else {
+                return json_success('补单成功,通知失败');
+            }
+        } else {
+            return json_fail('补单失败');
+        }
+    }
+
+    /**
+     * 发送回调
+     *
+     * @return \Illuminate\Http\JsonResponse|mixed
+     * @throws \Illuminate\Contracts\Container\BindingResolutionException
+     */
+    public function sendNotify()
+    {
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'order_sn' => 'required|string',
+        ];
+
+        $validator = app('validator')->make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $res = app(OrderRepository::class)->sendSuccessCallback($inputData['order_sn']);
+        if ($res) {
+            return json_success('通知成功');
+        } else {
+            return json_fail('通知失败');
+        }
+    }
+
+    /**
+     * 通道统计
+     *
+     * @return \Illuminate\Http\JsonResponse|mixed
+     * @throws \Illuminate\Contracts\Container\BindingResolutionException
+     */
+    public function channelTotal()
+    {
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'page' => 'required|integer',
+            'page_size' => 'required|integer',
+            'payment_uid' => 'nullable|string',
+            'start_date' => 'nullable|date',
+            'end_date' => 'nullable|date',
+        ];
+
+        $validator = app('validator')->make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $model = PayTotalModel::query()->selectRaw("sum(total_order) as total_order, sum(success_order) as success_order, sum(total_amount) as total_amount, sum(success_amount) as success_amount, merchant_uid, date, payment_attr_uid, payment_uid");
+        if (!empty($inputData['payment_uid'])) {
+            $model = $model->where('payment_uid', $inputData['payment_uid']);
+        }
+
+        if (!empty($inputData['start_date']) && !empty($inputData['end_date'])) {
+            $model = $model->whereBetween('created_at', [$inputData['start_date'], $inputData['end_date']]);
+        }
+
+        $pageSize = $inputData['page_size'] ?? CommonConstant::PAGE_SIZE;
+        if ($pageSize > 500) {
+            $pageSize = CommonConstant::PAGE_SIZE;
+        }
+        $page = format_page($inputData['page'] ?? 0);
+
+        $lists = $model->groupBy('payment_attr_uid', 'date')->orderByDesc('date')->paginate($pageSize, ['*'], 'page', $page);
+
+        $res = [];
+        foreach ($lists->items() as $k => $val) {
+            $res['data'][$k]['payment_name'] = $val->payment->name ?? '';
+            $res['data'][$k]['payment_attr_name'] = $val->paymentAttr->name ?? '';
+            $res['data'][$k]['total_order'] = $val->total_order;
+            $res['data'][$k]['success_order'] = $val->success_order;
+            $res['data'][$k]['total_amount'] = $val->total_amount;
+            $res['data'][$k]['success_amount'] = $val->success_amount;
+            $prob = round($val->success_order / $val->total_order, 2);
+            $res['data'][$k]['success_prob'] = round($prob * 100);
+            $res['data'][$k]['date'] = $val->date;
+        }
+        $res['total'] = $lists->total();
+
+        return json_success($res);
+    }
+
+    /**
+     * 商户统计
+     *
+     * @return \Illuminate\Http\JsonResponse|mixed
+     * @throws \Illuminate\Contracts\Container\BindingResolutionException
+     */
+    public function merchantTotal()
+    {
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'page' => 'required|integer',
+            'page_size' => 'required|integer',
+            'merchant_uid' => 'nullable|string',
+            'start_date' => 'nullable|date',
+            'end_date' => 'nullable|date',
+        ];
+
+        $validator = app('validator')->make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $model = PayTotalModel::query()->selectRaw("sum(total_order) as total_order, sum(success_order) as success_order, sum(total_amount) as total_amount, sum(success_amount) as success_amount, merchant_uid, date");
+        if (!empty($inputData['merchant_uid'])) {
+            $model = $model->where('merchant_uid', $inputData['merchant_uid']);
+        }
+
+        if (!empty($inputData['start_date']) && !empty($inputData['end_date'])) {
+            $model = $model->whereBetween('created_at', [$inputData['start_date'], $inputData['end_date']]);
+        }
+
+        $pageSize = $inputData['page_size'] ?? CommonConstant::PAGE_SIZE;
+        if ($pageSize > 500) {
+            $pageSize = CommonConstant::PAGE_SIZE;
+        }
+        $page = format_page($inputData['page'] ?? 0);
+
+        $lists = $model->orderByDesc('date')->groupBy('merchant_uid', 'date')->paginate($pageSize, ["*"], 'page', $page);
+
+        $res = [];
+        foreach ($lists->items() as $k => $val) {
+            $res['data'][$k]['merchant_name'] = $val->merchant->username;
+            $res['data'][$k]['total_order'] = $val->total_order;
+            $res['data'][$k]['success_order'] = $val->success_order;
+            $res['data'][$k]['total_amount'] = $val->total_amount;
+            $res['data'][$k]['success_amount'] = $val->success_amount;
+            $prob = round($val->success_order / $val->total_order, 2);
+            $res['data'][$k]['success_prob'] = round($prob * 100);
+            $res['data'][$k]['date'] = $val->date;
+        }
+        $res['total'] = $lists->total();
+
+        return json_success($res);
+    }
+}

+ 538 - 0
Modules/ManageApi/Controllers/PayController.php

@@ -0,0 +1,538 @@
+<?php
+
+namespace Modules\ManageApi\Controllers;
+
+use App\Constants\CommonConstant;
+use App\Constants\RedisKeyConstant;
+use App\Models\PaymentAttrModel;
+use App\Models\PaymentModel;
+use App\Models\PaymentTypeModel;
+use App\Repositories\Service\CommonService;
+use Illuminate\Support\Facades\Cache;
+use Modules\ManageApi\Services\MerchantServices;
+
+class PayController extends BaseController
+{
+    /**
+     * 支付方式列表
+     *
+     * @return \Illuminate\Http\JsonResponse|mixed
+     * @throws \Illuminate\Contracts\Container\BindingResolutionException
+     */
+    public function paymentList()
+    {
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'page' => 'nullable|integer',
+            'page_size' => 'nullable|integer'
+        ];
+
+        $validator = app('validator')->make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $pageSize = $inputData['page_size'] ?? CommonConstant::PAGE_SIZE;
+        if ($pageSize > 500) {
+            $pageSize = CommonConstant::PAGE_SIZE;
+        }
+        $page = format_page($inputData['page'] ?? 1);
+
+        $model = PaymentModel::query();
+
+        if (!empty($inputData['name'])) {
+            $model = $model->where('name', 'like', '%' . $inputData['name'] . '%');
+        }
+
+        if (!empty($inputData['status'])) {
+            $model = $model->where('status', $inputData['status']);
+        }
+
+        $res = $model->orderByDesc('created_at')->paginate( $pageSize, ["*"], 'page', $page);
+
+        return json_success($res);
+    }
+
+    /**
+     * 支付方式添加
+     *
+     * @return \Illuminate\Http\JsonResponse|mixed
+     * @throws \Illuminate\Contracts\Container\BindingResolutionException
+     */
+    public function paymentAdd()
+    {
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'name' => 'required|string',
+            'pay_code' => 'required|string',
+            'partner_id' => 'required|string',
+        ];
+        $validator = app('validator')->make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $setarr = [
+            'name' => $inputData['name'],
+            'pay_code' => $inputData['pay_code'],
+            'partner_id' => $inputData['partner_id'],
+        ];
+
+        $res = PaymentModel::query()->create($setarr);
+        if ($res) {
+            return json_success([], '添加成功');
+        } else {
+            return json_fail('添加失败');
+        }
+    }
+
+    /**
+     * 支付方式修改
+     *
+     * @return \Illuminate\Http\JsonResponse|mixed
+     * @throws \Illuminate\Contracts\Container\BindingResolutionException
+     */
+    public function paymentEdit()
+    {
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'name' => 'nullable|string',
+            'pay_code' => 'nullable|string',
+            'partner_id' => 'nullable|string',
+            'status' => 'nullable|in:1,2',
+            'uid' => 'required|string',
+        ];
+        $validator = app('validator')->make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $info = PaymentModel::query()->where('uid', $inputData['uid'])->first();
+        if (!$info) {
+            return json_fail('操作的支付方式不存在');
+        }
+
+        if (!empty($inputData['name'])) {
+            $info->name = $inputData['name'];
+        }
+
+        if (!empty($inputData['pay_code'])) {
+            $info->pay_code = $inputData['pay_code'];
+        }
+
+        if (!empty($inputData['partner_id'])) {
+            $info->partner_id = $inputData['partner_id'];
+        }
+
+        if (!empty($inputData['status'])) {
+            $info->status = $inputData['status'];
+        }
+
+        if ($info->save()) {
+            return json_success([], '修改成功');
+        } else {
+            return json_fail('修改失败');
+        }
+    }
+
+    /**
+     * 支付通道列表
+     *
+     * @return \Illuminate\Http\JsonResponse|mixed
+     * @throws \Illuminate\Contracts\Container\BindingResolutionException
+     */
+    public function paymentAttrList()
+    {
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'page' => 'required|integer',
+            'page_size' => 'required|integer',
+            'name' => 'nullable|string',
+            'pay_uid' => 'nullable',
+            'status' => 'nullable|in:1,2',
+        ];
+
+        $validator = app('validator')->make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $model = PaymentAttrModel::query();
+        if (!empty($inputData['pay_uid'])) {
+            $model = $model->where('pay_uid', $inputData['pay_uid']);
+        }
+
+        if (!empty($inputData['status'])) {
+            $model = $model->where('status', $inputData['status']);
+        }
+
+        if (!empty($inputData['name'])) {
+            $model = $model->where('name', 'like', '%' . $inputData['name'] . '%');
+        }
+
+        $pageSize = $inputData['page_size'] ?? CommonConstant::PAGE_SIZE;
+        if ($pageSize > 500) {
+            $pageSize = CommonConstant::PAGE_SIZE;
+        }
+        $page = format_page($inputData['page'] ?? 0);
+
+        $res = $model->orderByDesc('created_at')->paginate( $pageSize, ["*"], 'page', $page);
+
+        return json_success($res);
+    }
+
+    /**
+     * 支付通道添加
+     *
+     * @return \Illuminate\Http\JsonResponse|mixed
+     * @throws \Illuminate\Contracts\Container\BindingResolutionException
+     */
+    public function paymentAttrAdd()
+    {
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'pay_uid' => 'required|string',
+            'code' => 'required|string',
+            'name' => 'required|string',
+        ];
+        $validator = app('validator')->make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $setarr = [
+            'name' => $inputData['name'],
+            'pay_uid' => $inputData['pay_uid'],
+            'code' => $inputData['code'],
+        ];
+
+        $res = PaymentAttrModel::query()->create($setarr);
+        if ($res) {
+            return json_success([], '添加成功');
+        } else {
+            return json_fail('添加失败');
+        }
+    }
+
+    /**
+     * 支付通道修改
+     *
+     * @return \Illuminate\Http\JsonResponse|mixed
+     * @throws \Illuminate\Contracts\Container\BindingResolutionException
+     */
+    public function paymentAttrEdit()
+    {
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'pay_uid' => 'nullable|string',
+            'code' => 'nullable|string',
+            'name' => 'nullable|string',
+            'uid' => 'required|string',
+            'status' => 'nullable|in:1,2'
+        ];
+        $validator = app('validator')->make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $info = PaymentAttrModel::query()->where('uid', $inputData['uid'])->first();
+        if (!$info) {
+            return json_fail('操作的支付通道不存在');
+        }
+
+        if (!empty($inputData['name'])) {
+            $info->name = $inputData['name'];
+        }
+
+        if (!empty($inputData['pay_uid'])) {
+            $info->pay_uid = $inputData['pay_uid'];
+        }
+
+        if (!empty($inputData['code'])) {
+            $info->code = $inputData['code'];
+        }
+
+        if (!empty($inputData['status'])) {
+            $info->status = $inputData['status'];
+        }
+
+        if ($info->save()) {
+            return json_success([], '修改成功');
+        } else {
+            return json_fail('修改失败');
+        }
+    }
+
+    /**
+     * 支付类型列表
+     *
+     * @return \Illuminate\Http\JsonResponse|mixed
+     * @throws \Illuminate\Contracts\Container\BindingResolutionException
+     */
+    public function paymentTypeList()
+    {
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'page' => 'nullable|integer',
+            'page_size' => 'nullable|integer',
+            'payment_uid' => 'nullable|string',
+            'status' => 'nullable|in:1,2',
+            'pay_type' => 'nullable|in:1,2,3,4,5',
+            'belong_device' => 'nullable|in:A,I',
+        ];
+
+        $validator = app('validator')->make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $model = PaymentTypeModel::query();
+
+        if (!empty($inputData['payment_uid'])) {
+            $model->payment_uid = $model->where('payment_uid', $inputData['payment_uid']);
+        }
+
+        if (!empty($inputData['status'])) {
+            $model = $model->where('status', $inputData['status']);
+        }
+
+        if (!empty($inputData['pay_type'])) {
+            $model = $model->where('pay_type', $inputData['pay_type']);
+        }
+
+        $pageSize = $inputData['page_size'] ?? CommonConstant::PAGE_SIZE;
+        if ($pageSize > 500) {
+            $pageSize = CommonConstant::PAGE_SIZE;
+        }
+        $page = format_page($inputData['page'] ?? 1);
+
+        $res = $model->orderByDesc('weights')->paginate( $pageSize, ["*"], 'page', $page);
+        foreach ($res->items() as &$val) {
+            $val->payment_attr_code = $val->paymentAttr->code ?? '';
+            unset($val->paymentAttr);
+        }
+
+        return json_success($res);
+    }
+
+    /**
+     * 支付类型添加
+     *
+     * @return \Illuminate\Http\JsonResponse|mixed
+     * @throws \Illuminate\Contracts\Container\BindingResolutionException
+     */
+    public function paymentTypeAdd()
+    {
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'name' => 'required|string',
+            'payment_uid' => 'required|string',
+            'payment_attr_uid' => 'required|string',
+            'pay_type' => 'required|in:1,2,3,4,5',
+            'limit_type' => 'required|in:1,2',
+            'limit' => 'required|string',
+            'weights' => 'required|integer',
+            'remark' => 'nullable|string',
+            'status' => 'required|in:1,2',
+            'belong_device' => 'nullable|in:I,A',
+        ];
+        $validator = app('validator')->make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $setarr = [
+            'name' => $inputData['name'],
+            'payment_uid' => $inputData['payment_uid'],
+            'payment_attr_uid' => $inputData['payment_attr_uid'],
+            'pay_type' => $inputData['pay_type'],
+            'limit_type' => $inputData['limit_type'],
+            'limit' => $inputData['limit'],
+            'weights' => $inputData['weights'],
+            'remark' => $inputData['remark'],
+            'status' => $inputData['status'],
+            'rate' => $inputData['rate'],
+            'belong_device' => $inputData['belong_device'],
+        ];
+
+        $res = PaymentTypeModel::query()->create($setarr);
+        if ($res) {
+            return json_success([], '添加成功');
+        } else {
+            return json_fail('添加失败');
+        }
+    }
+
+    /**
+     * 支付类型修改
+     *
+     * @return \Illuminate\Http\JsonResponse|mixed
+     * @throws \Illuminate\Contracts\Container\BindingResolutionException
+     */
+    public function paymentTypeEdit()
+    {
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'name' => 'nullable|string',
+            'payment_uid' => 'nullable|string',
+            'payment_attr_uid' => 'nullable|string',
+            'pay_type' => 'nullable|in:1,2,3,4,5',
+            'limit_type' => 'nullable|in:1,2',
+            'limit' => 'nullable|string',
+            'weights' => 'nullable|integer',
+            'remark' => 'nullable|string',
+            'status' => 'nullable|in:1,2',
+            'uid' => 'required|string',
+            'belong_device' => 'nullable|in:I,A',
+        ];
+        $validator = app('validator')->make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $info = PaymentTypeModel::query()->where('uid', $inputData['uid'])->first();
+        if (!$info) {
+            return json_fail('操作的支付类型不存在');
+        }
+
+        if (!empty($inputData['name'])) {
+            $info->name = $inputData['name'];
+        }
+
+        if (!empty($inputData['payment_uid'])) {
+            $info->payment_uid = $inputData['payment_uid'];
+        }
+
+        if (!empty($inputData['payment_attr_uid'])) {
+            $info->payment_attr_uid = $inputData['payment_attr_uid'];
+        }
+
+        if (!empty($inputData['pay_type'])) {
+            $info->pay_type = $inputData['pay_type'];
+        }
+
+        if (!empty($inputData['limit_type'])) {
+            $info->limit_type = $inputData['limit_type'];
+        }
+
+        if (!empty($inputData['belong_device'])) {
+            $info->belong_device = $inputData['belong_device'];
+        }
+
+        if (!empty($inputData['limit'])) {
+            $info->limit = $inputData['limit'];
+        }
+
+        if (!empty($inputData['weights'])) {
+            $info->weights = $inputData['weights'];
+        }
+
+        if (!empty($inputData['status'])) {
+            $info->status = $inputData['status'];
+        }
+
+        if (!empty($inputData['rate'])) {
+            $info->rate = $inputData['rate'];
+        }
+
+        $info->remark = $inputData['remark'];
+
+        if ($info->save()) {
+            Cache::forget(RedisKeyConstant::PAYMENTTYPE);
+            app(MerchantServices::class)->clearCache();
+
+            app(CommonService::class)->clearAllMerchantCache();
+            return json_success([], '修改成功');
+        } else {
+            return json_fail('修改失败');
+        }
+    }
+
+    /**
+     * 支付类型删除
+     *
+     * @return \Illuminate\Http\JsonResponse|mixed
+     * @throws \Illuminate\Contracts\Container\BindingResolutionException
+     */
+    public function paymentTypeDel()
+    {
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'uid' => 'required|string',
+        ];
+        $validator = app('validator')->make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $res = PaymentTypeModel::query()->where('uid', $inputData['uid'])->delete();
+        if ($res) {
+            Cache::forget(RedisKeyConstant::PAYMENTTYPE);
+
+            app(CommonService::class)->clearAllMerchantCache();
+            return json_success([], '删除成功');
+        } else {
+            return json_fail('删除失败');
+        }
+    }
+
+    /**
+     * 下拉
+     *
+     * @return \Illuminate\Database\Eloquent\Builder[]|\Illuminate\Database\Eloquent\Collection
+     */
+    public function dropDown()
+    {
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'down_type' => 'required|in:1,2,3',
+            'pay_uid' => 'nullable|string'
+        ];
+        $validator = app('validator')->make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        if ($inputData['down_type'] == 1) {
+            $res = PaymentModel::query()->get(['uid', 'name'])->keyBy('uid');
+        } elseif ($inputData['down_type'] == 2) {
+            $model = PaymentAttrModel::query();
+            if (!empty($inputData['pay_uid'])) {
+                $model = $model->where('pay_uid', $inputData['pay_uid']);
+            }
+            $res = $model->get(['uid', 'name', 'pay_uid'])->keyBy('uid');
+        } else  {
+            $res = PaymentTypeModel::query()->where('status', 1)->get(['uid', 'name', 'payment_uid'])->keyBy('uid');
+            if ($res->isNotEmpty()) {
+                foreach ($res as &$val) {
+                    $val->name = $val->payment->name . '-' . $val->name;
+                    unset($val->payment, $val->payment_uid);
+                }
+            }
+        }
+
+        return $res;
+    }
+}

+ 109 - 0
Modules/ManageApi/Middlewares/ManageAuthMiddleware.php

@@ -0,0 +1,109 @@
+<?php
+/**
+ * 后台认证中间件.
+ *
+ * @author nj
+ * @date   2019-11-03 14:07:35
+ */
+
+namespace Modules\ManageApi\Middlewares;
+
+use App\Constants\ErrorCodeConstant;
+use Closure;
+use App\Repositories\Utils\AccessControllerListsRepository;
+use App\Repositories\Utils\JwtRepository;
+use Modules\ManageApi\Services\ManageAuthService;
+use Cache;
+
+class ManageAuthMiddleware
+{
+    protected $except = [];   //URI里排除的操作
+
+    /**
+     * Handle an incoming request.
+     *
+     * @param  \Illuminate\Http\Request  $request
+     * @param  \Closure  $next
+     * @return mixed
+     */
+    public function handle($request, Closure $next)
+    {
+        if ($this->shouldPassThrough($request)) {
+            return $next($request);
+        }
+        /**
+         * @var ManageAuthService $authService
+         */
+        $authService = app(ManageAuthService::class);
+        $token = $authService->getHeaderToken();
+        if (empty($token)) {
+            return json_fail(trans('common.err_failed_authorization'), 403);
+        }
+        /**
+         * @var JwtRepository $jwtRepo
+         */
+        $jwtRepo = make(JwtRepository::class);
+        $aud = 'manage-users';
+        $validateResult = $jwtRepo->validateToken($token, $aud);
+        if (!$validateResult['is_valid']) {
+            return json_fail(trans('common.err_failed_authorization'), 403);
+        } else {
+            if ($validateResult['is_expired']) {
+                return json_fail('认证过期', 401);
+            }
+            // 验证单点.
+            $claim = $validateResult['token_obj']->claims();
+            $tokenManagerUid = $claim->get('manager_uid');
+            $tokenIp = $claim->get('ip');
+            $clientIp = $jwtRepo->getClientIp();
+            $cacheKey = $authService->getManagerCacheKey($tokenManagerUid);
+            /**
+             * @var Cache $cache
+             */
+            $cache = make('cache');
+            $cacheToken = $cache->has($cacheKey) ? $cache->get($cacheKey) : '';
+            if (empty($cacheToken) || $cacheToken !== $token) {
+                return json_fail(trans('common.err_failed_authorization'), 403);
+            }
+            if (empty($tokenIp) || empty($clientIp) || $tokenIp !== $clientIp) {
+                return json_fail(trans('common.err_failed_authorization'), 403);
+            }
+        }
+
+        return $next($request);
+    }
+
+    private function getExcepts()
+    {
+        $uriPrefix = env('MANAGE_API_URI_PREFIX', 'manage_api');
+        $this->except = [
+            $uriPrefix . '/pong',
+            $uriPrefix .'/auth/login',
+            $uriPrefix . '/auth/refresh/token',
+            $uriPrefix . '/auth/logout',
+        ];
+
+        return $this->except;
+    }
+
+    /**
+     * Determine if the request has a URI that should pass through CSRF verification.
+     *
+     * @param  \Illuminate\Http\Request $request
+     * @return bool
+     */
+    protected function shouldPassThrough($request)
+    {
+        foreach ($this->getExcepts() as $except) {
+            if ($except !== '/') {
+                $except = trim($except, '/');
+            }
+
+            if ($request->is($except)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+}

+ 110 - 0
Modules/ManageApi/Services/ManageAuthService.php

@@ -0,0 +1,110 @@
+<?php
+/**
+ * 认证服务.
+ *
+ * @author nj
+ * @date   2021-01-22 11:36:11
+ */
+
+namespace Modules\ManageApi\Services;
+
+use App\Constants\ManageConstant;
+use App\Repositories\Utils\JwtRepository;
+use Cache;
+use Str;
+
+class ManageAuthService
+{
+    /**
+     * Jwt受众.
+     *
+     * @var string
+     */
+    private $jwtAud = 'manage-users';
+
+    /**
+     * 获取存储用户设备Token的缓存Key.
+     * @param string $managerUid
+     * @return string
+     */
+    public function getManagerCacheKey($managerUid)
+    {
+        $cacheKey = 'manager_auth_'. $managerUid;
+
+        return $cacheKey;
+    }
+
+    /**
+     * 创建单点Jwt.
+     * @param string $managerUid 管理员UID
+     * @param int $roleId 角色ID
+     * @return array
+     * @throws \Psr\SimpleCache\InvalidArgumentException
+     */
+    public function createSingleJwt($managerUid, $roleId)
+    {
+        $manager = new \stdClass();
+        $manager->manager_uid = $managerUid;
+        $manager->role_id = $roleId;
+        /**
+         * @var JwtRepository $jwtRepository
+         */
+        $jwtRepository = make(JwtRepository::class);
+        $tokenInfo = $jwtRepository->createManageJwt($manager);
+        $token = $tokenInfo['token'];
+
+        $cacheKey = $this->getManagerCacheKey($managerUid);
+        /**
+         * @var Cache $cache
+         */
+        $cache = make('cache');
+        $cacheToken = $cache->has($cacheKey) ? $cache->get($cacheKey) : '';
+        if (!empty($cacheToken)) {
+            $cache->delete($cacheKey);
+        }
+        $cache->set($cacheKey, $token, config('jwt.ttl') * 60);
+
+        return $tokenInfo;
+    }
+
+    /**
+     * 获取头部Token.
+     * @return mixed
+     */
+    public function getHeaderToken()
+    {
+        $token = request()->header(ManageConstant::MG_TOKEN_NAME, null);
+        if (empty($token)) {
+            // 支持GET/PUT获取token
+            $token = request()->input(ManageConstant::MG_TOKEN_NAME, null);
+        }
+
+        return $token;
+    }
+
+    /**
+     * 获取Token中的ManagerInfo
+     * @return array  返回manager_uid, role_id
+     */
+    public function getManagerInfo()
+    {
+        $managerInfo = [];
+        $token = $this->getHeaderToken();
+        if (!empty($token)) {
+            /**
+             * @var JwtRepository $jwtRepository
+             */
+            $jwtRepository = make(JwtRepository::class);
+            $validateResult = $jwtRepository->validateToken($token, $this->jwtAud);
+            if ($validateResult['is_valid']) {
+                $claim = $validateResult['token_obj']->claims();
+                $managerInfo = [
+                    'manager_uid' => !empty($claim->get('manager_uid')) ? $claim->get('manager_uid') : '',
+                    'role_id' => !empty($claim->get('role_id')) ? $claim->get('role_id') : 0,
+                ];
+            }
+        }
+
+        return $managerInfo;
+    }
+}

+ 23 - 0
Modules/ManageApi/Services/MerchantServices.php

@@ -0,0 +1,23 @@
+<?php
+
+namespace Modules\ManageApi\Services;
+
+use App\Constants\RedisKeyConstant;
+use App\Models\MerchantModel;
+use Illuminate\Support\Facades\Cache;
+
+class MerchantServices
+{
+    /**
+     * 清理商户缓存
+     *
+     * @return void
+     */
+    public function clearCache()
+    {
+        $data = MerchantModel::query()->where('status', 1)->get(['uid']);
+        foreach ($data as $val) {
+            Cache::forget(RedisKeyConstant::PAYMENT_TYPE . $val['uid']);
+        }
+    }
+}

+ 95 - 1
README.md

@@ -1,2 +1,96 @@
-# pay_center_server
+#支付中心
+# 区块链支付-Laravel6
 
+## 技术
+- Laravel 9.x [中文9.x文档](https://learnku.com/docs/laravel/9.x)
+
+## 环境依赖
+- php8.0
+- composer
+- ext-BCMath
+- ext-Ctype
+- ext-JSON
+- ext-Mbstring
+- ext-OpenSSL
+- ext-PDO
+- ext-Tokenizer
+- ext-XML
+- ext-redis
+
+## 项目初始化步骤
+1. 下载laravel指定版本 https://github.com/laravel/laravel/archive/refs/tags/v6.20.1.zip
+2. 安装laravel依赖
+```shell
+composer install -vvv
+```
+3. 复制本地.env文件
+```shell
+cp .env.example .env
+```
+4. 生产laravel 密钥
+```shell
+php artisan key:generate
+
+5. .gitignore 追加忽略
+
+public function register()
+    {
+        if ($this->app->isLocal()) {
+            $this->app->register(\Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class);
+        }
+    }
+
+```
+
+
+## 本地调试服务
+```shell
+php artisan serve --host=0.0.0.0 --port=8601
+# 本地处理异步队列.
+php artisan queue:listen
+
+# 权限管理DB更新.
+php artisan permission:resource
+```
+
+## 规范
+### 代码命名规范
+1. 必须使用AMZN CodeSniffer检测脚本,进行代码PSR-2规范
+2. 目录、文件命名,必须满足PSR-4规范
+3. Windows下,由于同文件或者目录名大小写书写错误,请重写不同命名进行2次commit提交
+4. 所有代码文件,文件编码必须是utf-8,其次换行符标准LF(` Mac/Linux \n ` )
+5. 业务代码逻辑相对复杂的,约定要用注释写清楚关键信息.
+6. 所有方法,尽可能注释详细(名称、类型、备注等)
+
+### .env规范
+1. .env.example,所有增加的变量,都必须先放这里
+2. 不能存放自己开发、测试、生产环境的env提交到git
+
+### 数据库规范
+1. 数据库名、表名、字段名等,必须使用蛇形半角小写风格。例如`prefix_order_logs`
+2. 数据库表业务设计必须遵从,禁止超过3张表关联
+3. MySql数据库统一引擎InnoDB,字符集`utf8mb4`,字符编码`utf8mb4_unicode_ci`
+4. 生产环境数据库账号信息,务必独立分配账号和归属schema,禁止使用root等超级用户权限.
+5. MySql时间类型字段,统一使用`datetime(0)`,存储时间格式`Y-m-d H:i:s`
+6. 业务有对外暴露的ID,务必设计成 自增ID+自定义唯一ID,禁止暴露吐出自增ID.
+7. MySql增量sql,请放置在`storage/sql/inc/`目录下,命名规则`[database name]_inc_[日期Y-m-d].sql`
+
+### 接口规范
+1. 返回数据各下标节点名,一律使用蛇形半角小写风格.
+2. 规避返回无效(没有被使用)的字段以及数据.在业务层处理过滤.
+3. 不需要严格按照RESTFUL接口方法约定规范,但如果是http-api模式,除了特殊接口,一律统一使用post请求方法.
+4. 所有接口必须提交到Yapi平台,测试通过后,勾选状态完成可用.
+
+## 项目特殊
+
+## 部署
+1. 【第一次】cp .env.example .env
+2. 【第一次】修改好.env里的配置
+3. 【第一次】chmod -R 777 ./public/attachment ./storage/ ./bootstrap/
+4. 【每次】rm -f ./composer.lock
+5. 【每次】composer install -vvv
+6. 【第一次】确认.env 中的`QUEUE_CONNECTION=redis`
+7.  【第一次】 导入  storage/sql/all_inc.sql 库
+
+## 计划任务
+* * * * * /usr/local/bin/php /var/www/pay_center_server/artisan order:status-expired > /tmp/load.log 2>&1 &

TEMPAT SAMPAH
app/.DS_Store


+ 52 - 0
app/Console/Commands/OrderExpired.php

@@ -0,0 +1,52 @@
+<?php
+
+namespace App\Console\Commands;
+
+use App\Models\OrderModel;
+use Illuminate\Console\Command;
+
+class OrderStatusExpired extends Command
+{
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'order:status-expired';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = '支付订单过期处理';
+
+    /**
+     * Create a new command instance.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        parent::__construct();
+    }
+
+    /**
+     * Execute the console command.
+     *
+     * @return mixed
+     */
+    public function handle()
+    {
+        $this->comment("开始处理过期订单");
+
+        $expiredTime = date('Y-m-d H:i:s', time() - 400);
+        $orderData = OrderModel::query()->where('created_at', '<', $expiredTime)->where('status', 1)->get(['id']);
+        if ($orderData->isNotEmpty()) {
+            foreach ($orderData as $var) {
+                OrderModel::query()->where('id', $var->id)->update(['status' => 3]);
+            }
+        }
+        $this->comment("过期订单处理完成");
+    }
+}

+ 42 - 0
app/Console/Kernel.php

@@ -0,0 +1,42 @@
+<?php
+
+namespace App\Console;
+
+use App\Console\Commands\OrderStatusExpired;
+use Illuminate\Console\Scheduling\Schedule;
+use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
+
+class Kernel extends ConsoleKernel
+{
+    /**
+     * The Artisan commands provided by your application.
+     *
+     * @var array
+     */
+    protected $commands = [
+        OrderStatusExpired::class
+    ];
+
+    /**
+     * Define the application's command schedule.
+     *
+     * @param  \Illuminate\Console\Scheduling\Schedule  $schedule
+     * @return void
+     */
+    protected function schedule(Schedule $schedule)
+    {
+        $schedule->command('order:status-expired')->everyMinute();
+    }
+
+    /**
+     * Register the commands for the application.
+     *
+     * @return void
+     */
+    protected function commands()
+    {
+        $this->load(__DIR__.'/Commands');
+
+        require base_path('routes/console.php');
+    }
+}

+ 55 - 0
app/Constants/CommonConstant.php

@@ -0,0 +1,55 @@
+<?php
+/**
+ * 描述.
+ *
+ * @author nj
+ * @date   2021-05-19 02:33:22
+ */
+
+
+namespace App\Constants;
+
+
+class CommonConstant
+{
+    const HANDSHAKE = 'v20211004';   // 返回data节点里的数据加密key的映射关系.
+
+    const PAGE_SIZE = 10;   // 分页查询一页个数.
+
+    /**
+     * 前台appToken名.
+     */
+    const X_TOKEN_NAME = 'X-TOKEN';
+
+    /**
+     * 后端Token名.
+     */
+    const MG_TOKEN_NAME = 'AUTH-TOKEN';
+
+    public static function getHandSnakeKey($flag)
+    {
+        $arrMapping = [
+            'v20211004' => [
+                'key' => 'WGwb1PhI2qkcK',
+            ],
+        ];
+        $randMaps = [
+            'key' => rand_letter(16),
+        ];
+        return data_get($arrMapping, $flag, $randMaps);
+    }
+
+    /**
+     * 获取HandShake
+     * @return string
+     */
+    public static function getHandShake()
+    {
+        $flag = self::HANDSHAKE;
+        $appENv = env('APP_ENV', 'production');
+        if ($appENv == 'local') {
+            $flag = '';
+        }
+        return $flag;
+    }
+}

+ 10 - 0
app/Constants/ErrorCodeConstant.php

@@ -0,0 +1,10 @@
+<?php
+
+namespace App\Constants;
+
+class ErrorCodeConstant
+{
+    const SERVER_ERROR = 500;
+
+    const NO_PERMISSION = 4001;
+}

+ 29 - 0
app/Constants/ManageConstant.php

@@ -0,0 +1,29 @@
+<?php
+/**
+ * 描述.
+ *
+ * @author nj
+ * @date   2021-05-19 02:35:21
+ */
+
+
+namespace App\Constants;
+
+
+class ManageConstant
+{
+    /**
+     * 头部shake名.
+     */
+    const HANDSHAKE_HEADER = 'MG-SK';
+
+    /**
+     * 标识, 前端发布版本的时候记得加这个.
+     */
+    const HANDSHAKE_FLAG = 'df71cb036';
+
+    /**
+     * 后端Token名.
+     */
+    const MG_TOKEN_NAME = 'AUTH-TOKEN';
+}

+ 20 - 0
app/Constants/RedisKeyConstant.php

@@ -0,0 +1,20 @@
+<?php
+
+namespace App\Constants;
+
+class RedisKeyConstant
+{
+    /**
+     * 设置缓存
+     */
+    const CONFIG = 'config';
+
+    /**
+     * 支付类型
+     */
+    const PAYMENTTYPE = 'payment_type';
+
+    const SESSION_AUTH_KEY = 'session_auth_key';
+
+    const PAYMENT_TYPE = 'paymentType:';
+}

+ 73 - 0
app/Events/PayTotalEvent.php

@@ -0,0 +1,73 @@
+<?php
+
+namespace App\Events;
+
+use Illuminate\Broadcasting\Channel;
+use Illuminate\Broadcasting\InteractsWithSockets;
+use Illuminate\Broadcasting\PresenceChannel;
+use Illuminate\Broadcasting\PrivateChannel;
+use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
+use Illuminate\Foundation\Events\Dispatchable;
+use Illuminate\Queue\SerializesModels;
+
+class PayTotalEvent
+{
+    use Dispatchable, InteractsWithSockets, SerializesModels;
+
+    /**
+     * 商户uid
+     *
+     * @var string
+     */
+    public $merchantUid = '';
+
+    /**
+     * 支付方式uid
+     *
+     * @var string
+     */
+    public $paymentUid = '';
+
+    /**
+     * 支付通道uid
+     *
+     * @var string
+     */
+    public $paymentAttrUid = '';
+
+    /**
+     * @var int
+     */
+    public $orderStatus = 0;
+
+    /**
+     * 金额
+     *
+     * @var int
+     */
+    public $amount = 0;
+
+    /**
+     * Create a new event instance.
+     *
+     * @return void
+     */
+    public function __construct(string $merchantUid, string $paymentUid, string $paymentAttrUid, float $amount, int $orderStatus)
+    {
+        $this->merchantUid = $merchantUid;
+        $this->paymentUid = $paymentUid;
+        $this->paymentAttrUid = $paymentAttrUid;
+        $this->amount = $amount;
+        $this->orderStatus = $orderStatus;
+    }
+
+    /**
+     * Get the channels the event should broadcast on.
+     *
+     * @return \Illuminate\Broadcasting\Channel|array
+     */
+    public function broadcastOn()
+    {
+//        return new PrivateChannel('channel-name');
+    }
+}

+ 39 - 0
app/Exceptions/ApiException.php

@@ -0,0 +1,39 @@
+<?php
+/**
+ * 描述
+ * @auth nj911<nj-x@gmail.com>
+ * @date: 2021-04-18 12:27
+ */
+
+namespace App\Exceptions;
+
+use Exception;
+use Illuminate\Http\Request;
+
+class ApiException extends Exception
+{
+    private $eMessage = '';
+
+    public function __construct($message, $code = 500)
+    {
+        $code = intval($code);
+        parent::__construct($message, $code);
+        $this->eCode = $code;
+        $this->eMessage = $message;
+    }
+
+    /**
+     * 报告异常
+     *
+     * @return void
+     */
+    public function report()
+    {
+        //
+    }
+
+    public function render(Request $request)
+    {
+        return json_fail($this->eMessage, $this->getCode());
+    }
+}

+ 50 - 0
app/Exceptions/Handler.php

@@ -0,0 +1,50 @@
+<?php
+
+namespace App\Exceptions;
+
+use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
+use Throwable;
+
+class Handler extends ExceptionHandler
+{
+    /**
+     * A list of exception types with their corresponding custom log levels.
+     *
+     * @var array<class-string<\Throwable>, \Psr\Log\LogLevel::*>
+     */
+    protected $levels = [
+        //
+    ];
+
+    /**
+     * A list of the exception types that are not reported.
+     *
+     * @var array<int, class-string<\Throwable>>
+     */
+    protected $dontReport = [
+        //
+    ];
+
+    /**
+     * A list of the inputs that are never flashed to the session on validation exceptions.
+     *
+     * @var array<int, string>
+     */
+    protected $dontFlash = [
+        'current_password',
+        'password',
+        'password_confirmation',
+    ];
+
+    /**
+     * Register the exception handling callbacks for the application.
+     *
+     * @return void
+     */
+    public function register()
+    {
+        $this->reportable(function (Throwable $e) {
+            //
+        });
+    }
+}

+ 4 - 0
app/Helpers/autoload.func.php

@@ -0,0 +1,4 @@
+<?php
+
+require __DIR__ . '/common.func.php';
+require __DIR__ . '/helpers.func.php';

+ 185 - 0
app/Helpers/common.func.php

@@ -0,0 +1,185 @@
+<?php
+/**
+ * 通用函数库
+ */
+
+use Hashids\Hashids;
+
+/**
+ * json响应成功
+ * @param mixed $data 数据
+ * @param string $message 消息
+ * @return mixed
+ */
+function json_success($data, $message = '操作成功')
+{
+    $handshake = '';
+    $data = [
+        'code' => 200,
+        'message' => $message,
+        'data' => $data,
+        'handshake' => $handshake,
+    ];
+
+    return response()->json($data);
+}
+
+/**
+ * json响应失败
+ * @param string $message 消息
+ * @param int $code 代码
+ * @return \Illuminate\Http\JsonResponse
+ */
+function json_fail($message, $code = 500, $data = [])
+{
+    $handshake = '';
+    $data = [
+        'code'    => $code,
+        'message' => $message,
+        'data'    => $data,
+        'handshake' => $handshake,
+    ];
+
+    return response()->json($data);
+}
+
+/**
+ * 生成密码.
+ * @param string $plain 明文密码.
+ * @param string $salt 盐.
+ * @return string
+ */
+function generatePassword($plain, $salt)
+{
+    return md5(md5($plain) . $salt);
+}
+
+/**
+ * 随机生成字符串
+ * @param int $length 长度
+ * @param int $numeric 纯数字
+ * @return string
+ */
+function random($length, $numeric = 0) : string
+{
+    if (version_compare(PHP_VERSION, '4.2.0', '<')) {
+        mt_srand((double)microtime() * 1000000);
+    } else {
+        mt_srand();
+    }
+    $seed = base_convert(md5(print_r($_SERVER, 1) . microtime()), 16, $numeric ? 10 : 35);
+    $seed = $numeric ? (str_replace('0', '', $seed) . '012340567890') : ($seed . 'zZ' . strtoupper($seed));
+    $hash = '';
+    $max = strlen($seed) - 1;
+    for ($i = 0; $i < $length; $i++) {
+        $hash .= $seed[mt_rand(0, $max)];
+    }
+    return $hash;
+}
+
+/**
+ * 格式化页码入参
+ * @param $page
+ * @return int
+ */
+function format_page($page)
+{
+    $page = intval($page);
+    if ($page <= 0) {
+        $page = 1;
+    }
+    if ($page > PHP_INT_MAX) {
+        $page = PHP_INT_MAX;
+    }
+
+    return $page;
+}
+
+/**
+ * 加密ID.
+ * @param $id
+ * @param int $length 长度
+ * @return string
+ */
+function hashEncodeId($id, $length = 6)
+{
+    $hashids = new Hashids(config('hashids.salt', ''), 6);
+
+    return $hashids->encode($id);
+}
+
+/**
+ * 解密ID.
+ * @param $str
+ * @return string
+ */
+function hashDecodeId($str)
+{
+    $hashids = new Hashids(config('hashids.salt', ''), 6);
+    $arr = $hashids->decode($str);
+
+    return implode('', $arr);
+}
+
+/**
+ * 生成订单流水号、可指定前缀
+ * @param string $prefix
+ * @return string
+ */
+function createOrderSn($prefix = '', $mark = '')
+{
+    if (strlen($mark) <= 2 && !empty($mark)) {
+        $prefix .= '0' . $mark;
+    } else {
+        $prefix .= $mark;
+    }
+    return $prefix . date('YmdHis') . mt_rand(1111, 9999);
+}
+
+/**
+ * 检测支付链接协议
+ *
+ * @param $payUrl
+ * @return mixed|string
+ */
+function checkPayUrl($payUrl) {
+    if (strpos($payUrl, 'alipays://') !== false) {
+        $payUrl = 'https://r.tool.py163.xyz/dk.html?_r_=' . urlencode($payUrl);
+    }
+
+    return $payUrl;
+}
+
+/**
+ * 生成key
+ *
+ * @return string
+ */
+function createKey()
+{
+    $pwd = '';
+    for ($i = 0; $i < 10; $i++) {
+        $pwd .= chr(mt_rand(33, 126));
+    }
+
+    return md5($pwd . time());
+}
+
+/**
+ * 签名
+ *
+ * @param $data
+ * @param $publicKey
+ * @return string
+ */
+function build_sign($data, $publicKey)
+{
+    ksort($data);
+    $md5str = "";
+    foreach ($data as $key => $val) {
+        $md5str = $md5str . $key . "=" . $val . "&";
+    }
+    $md5str = strtoupper(md5($md5str . 'key=' . $publicKey));
+
+    return $md5str;
+}

+ 28 - 0
app/Helpers/helpers.func.php

@@ -0,0 +1,28 @@
+<?php
+
+if (! function_exists('make')) {
+    /**
+    * @param $abstract
+    * @param  array  $parameters
+    * @return mixed
+    * @throws @\Illuminate\Contracts\Container\BindingResolutionException
+    */
+    function make($abstract, array $parameters = [])
+    {
+        return app()->make($abstract, $parameters = []);
+    }
+}
+
+if (! function_exists('payLogger')) {
+    /**
+     * @param string 支付通道
+     * @param string 信息
+     * @param array 支付错误内容
+     * @param string $level
+     */
+    function payLogger(string $payName,string $message = '请求异常', array $context = [])
+    {
+        logger($payName, [$message]);
+        dispatch(new \App\Jobs\SendTgMegJob($payName,$message,$context));
+    }
+}

+ 13 - 0
app/Http/Controllers/Controller.php

@@ -0,0 +1,13 @@
+<?php
+
+namespace App\Http\Controllers;
+
+use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
+use Illuminate\Foundation\Bus\DispatchesJobs;
+use Illuminate\Foundation\Validation\ValidatesRequests;
+use Illuminate\Routing\Controller as BaseController;
+
+class Controller extends BaseController
+{
+    use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
+}

+ 74 - 0
app/Http/Kernel.php

@@ -0,0 +1,74 @@
+<?php
+
+namespace App\Http;
+
+use Illuminate\Foundation\Http\Kernel as HttpKernel;
+use Modules\ManageApi\Middlewares\ManageAuthMiddleware;
+
+class Kernel extends HttpKernel
+{
+    /**
+     * The application's global HTTP middleware stack.
+     *
+     * These middleware are run during every request to your application.
+     *
+     * @var array<int, class-string|string>
+     */
+    protected $middleware = [
+        // \App\Http\Middleware\TrustHosts::class,
+        \App\Http\Middleware\TrustProxies::class,
+        \Illuminate\Http\Middleware\HandleCors::class,
+        \App\Http\Middleware\PreventRequestsDuringMaintenance::class,
+        \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
+        \App\Http\Middleware\TrimStrings::class,
+        \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
+    ];
+
+    /**
+     * The application's route middleware groups.
+     *
+     * @var array<string, array<int, class-string|string>>
+     */
+    protected $middlewareGroups = [
+        'web' => [
+            \App\Http\Middleware\EncryptCookies::class,
+            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
+            \Illuminate\Session\Middleware\StartSession::class,
+            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
+            \App\Http\Middleware\VerifyCsrfToken::class,
+            \Illuminate\Routing\Middleware\SubstituteBindings::class,
+        ],
+
+        'api' => [
+            // \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
+            // 'throttle:api',
+            \Illuminate\Routing\Middleware\SubstituteBindings::class,
+        ],
+
+        'manage_api' => [
+            // 'throttle:120,1',
+            \Illuminate\Routing\Middleware\SubstituteBindings::class,
+            ManageAuthMiddleware::class,
+        ]
+    ];
+
+    /**
+     * The application's route middleware.
+     *
+     * These middleware may be assigned to groups or used individually.
+     *
+     * @var array<string, class-string|string>
+     */
+    protected $routeMiddleware = [
+        'auth' => \App\Http\Middleware\Authenticate::class,
+        'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
+        'auth.session' => \Illuminate\Session\Middleware\AuthenticateSession::class,
+        'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
+        'can' => \Illuminate\Auth\Middleware\Authorize::class,
+        'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
+        'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
+        'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
+        'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
+        'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
+    ];
+}

+ 21 - 0
app/Http/Middleware/Authenticate.php

@@ -0,0 +1,21 @@
+<?php
+
+namespace App\Http\Middleware;
+
+use Illuminate\Auth\Middleware\Authenticate as Middleware;
+
+class Authenticate extends Middleware
+{
+    /**
+     * Get the path the user should be redirected to when they are not authenticated.
+     *
+     * @param  \Illuminate\Http\Request  $request
+     * @return string|null
+     */
+    protected function redirectTo($request)
+    {
+        if (! $request->expectsJson()) {
+            return route('login');
+        }
+    }
+}

+ 17 - 0
app/Http/Middleware/EncryptCookies.php

@@ -0,0 +1,17 @@
+<?php
+
+namespace App\Http\Middleware;
+
+use Illuminate\Cookie\Middleware\EncryptCookies as Middleware;
+
+class EncryptCookies extends Middleware
+{
+    /**
+     * The names of the cookies that should not be encrypted.
+     *
+     * @var array<int, string>
+     */
+    protected $except = [
+        //
+    ];
+}

+ 17 - 0
app/Http/Middleware/PreventRequestsDuringMaintenance.php

@@ -0,0 +1,17 @@
+<?php
+
+namespace App\Http\Middleware;
+
+use Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance as Middleware;
+
+class PreventRequestsDuringMaintenance extends Middleware
+{
+    /**
+     * The URIs that should be reachable while maintenance mode is enabled.
+     *
+     * @var array<int, string>
+     */
+    protected $except = [
+        //
+    ];
+}

+ 32 - 0
app/Http/Middleware/RedirectIfAuthenticated.php

@@ -0,0 +1,32 @@
+<?php
+
+namespace App\Http\Middleware;
+
+use App\Providers\RouteServiceProvider;
+use Closure;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Auth;
+
+class RedirectIfAuthenticated
+{
+    /**
+     * Handle an incoming request.
+     *
+     * @param  \Illuminate\Http\Request  $request
+     * @param  \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse)  $next
+     * @param  string|null  ...$guards
+     * @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
+     */
+    public function handle(Request $request, Closure $next, ...$guards)
+    {
+        $guards = empty($guards) ? [null] : $guards;
+
+        foreach ($guards as $guard) {
+            if (Auth::guard($guard)->check()) {
+                return redirect(RouteServiceProvider::HOME);
+            }
+        }
+
+        return $next($request);
+    }
+}

+ 19 - 0
app/Http/Middleware/TrimStrings.php

@@ -0,0 +1,19 @@
+<?php
+
+namespace App\Http\Middleware;
+
+use Illuminate\Foundation\Http\Middleware\TrimStrings as Middleware;
+
+class TrimStrings extends Middleware
+{
+    /**
+     * The names of the attributes that should not be trimmed.
+     *
+     * @var array<int, string>
+     */
+    protected $except = [
+        'current_password',
+        'password',
+        'password_confirmation',
+    ];
+}

+ 20 - 0
app/Http/Middleware/TrustHosts.php

@@ -0,0 +1,20 @@
+<?php
+
+namespace App\Http\Middleware;
+
+use Illuminate\Http\Middleware\TrustHosts as Middleware;
+
+class TrustHosts extends Middleware
+{
+    /**
+     * Get the host patterns that should be trusted.
+     *
+     * @return array<int, string|null>
+     */
+    public function hosts()
+    {
+        return [
+            $this->allSubdomainsOfApplicationUrl(),
+        ];
+    }
+}

+ 28 - 0
app/Http/Middleware/TrustProxies.php

@@ -0,0 +1,28 @@
+<?php
+
+namespace App\Http\Middleware;
+
+use Illuminate\Http\Middleware\TrustProxies as Middleware;
+use Illuminate\Http\Request;
+
+class TrustProxies extends Middleware
+{
+    /**
+     * The trusted proxies for this application.
+     *
+     * @var array<int, string>|string|null
+     */
+    protected $proxies;
+
+    /**
+     * The headers that should be used to detect proxies.
+     *
+     * @var int
+     */
+    protected $headers =
+        Request::HEADER_X_FORWARDED_FOR |
+        Request::HEADER_X_FORWARDED_HOST |
+        Request::HEADER_X_FORWARDED_PORT |
+        Request::HEADER_X_FORWARDED_PROTO |
+        Request::HEADER_X_FORWARDED_AWS_ELB;
+}

+ 17 - 0
app/Http/Middleware/VerifyCsrfToken.php

@@ -0,0 +1,17 @@
+<?php
+
+namespace App\Http\Middleware;
+
+use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
+
+class VerifyCsrfToken extends Middleware
+{
+    /**
+     * The URIs that should be excluded from CSRF verification.
+     *
+     * @var array<int, string>
+     */
+    protected $except = [
+        //
+    ];
+}

+ 58 - 0
app/Jobs/SendTgMegJob.php

@@ -0,0 +1,58 @@
+<?php
+
+namespace App\Jobs;
+
+use App\Repositories\Service\CommonService;
+use Illuminate\Bus\Queueable;
+use Illuminate\Contracts\Queue\ShouldBeUnique;
+use Illuminate\Contracts\Queue\ShouldQueue;
+use Illuminate\Foundation\Bus\Dispatchable;
+use Illuminate\Queue\InteractsWithQueue;
+use Illuminate\Queue\SerializesModels;
+use Modules\Api\Services\ConfigService;
+
+class SendTgMegJob implements ShouldQueue
+{
+    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
+
+    public $params;
+    public $name;
+    public $message;
+
+    /**
+     * Create a new job instance.
+     *
+     * @return void
+     */
+    public function __construct($name,$message,$params)
+    {
+        $this->name = $name;
+        $this->params = $params;
+        $this->message = $message;
+    }
+
+    /**
+     * Execute the job.
+     *
+     * @return void
+     */
+    public function handle()
+    {
+        $configService = make(ConfigService::class);
+        $tgMsgServer = $configService->getValue('tg_msg_server');
+        $tgApiToken = $configService->getValue('tg_api_token');
+
+        if ($tgMsgServer && $tgApiToken) {
+            $content = json_encode($this->params,JSON_UNESCAPED_UNICODE);
+            make(CommonService::class)->httpPost(
+                [
+                    'name' => $this->name,
+                    'message' => $this->message,
+                    'content'=>$content,
+                    'title' => '支付中心'
+                ],
+                $tgMsgServer . 'api/tg/send/message',
+                ['AUTH-TOKEN' => $tgApiToken]);
+        }
+    }
+}

+ 71 - 0
app/Listeners/PayTotalIncListener.php

@@ -0,0 +1,71 @@
+<?php
+
+namespace App\Listeners;
+
+use App\Events\PayTotalEvent;
+use App\Models\PayTotalModel;
+
+class PayTotalIncListener
+{
+    /**
+     * Create the event listener.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        //
+    }
+
+    /**
+     * Handle the event.
+     *
+     * @param  \App\Events\PayTotalEvent  $event
+     * @return void
+     */
+    public function handle(PayTotalEvent $event)
+    {
+        $date = date('Y-m-d');
+        $totalData = PayTotalModel::query()->where(['merchant_uid' => $event->merchantUid, 'payment_uid' => $event->paymentUid, 'payment_attr_uid' => $event->paymentAttrUid, 'date' => $date])->first();
+        if (empty($totalData)) {
+            if ($event->orderStatus == 2) {  // 跨天统计数据初始化
+                $setData = [
+                    'merchant_uid' => $event->merchantUid,
+                    'payment_uid' => $event->paymentUid,
+                    'payment_attr_uid' => $event->paymentAttrUid,
+                    'total_order' => 1,
+                    'success_order' => 1,
+                    'success_amount' => $event->amount,
+                    'total_amount' => $event->amount,
+                    'date' => $date,
+                ];
+            } else {
+                $setData = [
+                    'merchant_uid' => $event->merchantUid,
+                    'payment_uid' => $event->paymentUid,
+                    'payment_attr_uid' => $event->paymentAttrUid,
+                    'total_order' => 1,
+                    'success_order' => 0,
+                    'success_amount' => 0,
+                    'total_amount' => $event->amount,
+                    'date' => $date,
+                ];
+            }
+
+            $res = PayTotalModel::query()->create($setData);
+            if (!$res) {
+                logger('插入失败', $setData);
+            }
+        } else {
+            if ($event->orderStatus == 2) {
+                $totalData->success_order += 1;
+                $totalData->success_amount += $event->amount;
+            } else {
+                $totalData->total_order += 1;
+                $totalData->total_amount += $event->amount;
+            }
+
+            $totalData->save();
+        }
+    }
+}

+ 8 - 0
app/Models/AdminModel.php

@@ -0,0 +1,8 @@
+<?php
+
+namespace App\Models;
+
+class AdminModel extends BaseModel
+{
+    protected $table = 'admin';
+}

+ 46 - 0
app/Models/BaseModel.php

@@ -0,0 +1,46 @@
+<?php
+
+namespace App\Models;
+
+use DateTimeInterface;
+use Illuminate\Database\Eloquent\Model;
+
+class BaseModel extends Model
+{
+    /**
+     * 自动维护时间戳created_at和updated_at
+     *
+     * @var bool
+     */
+    public $timestamps = true;
+
+    /**
+     * 自增主键
+     * @var bool
+     */
+    public $incrementing = true;
+
+    /**
+     * 保护字段.
+     * @var string[]
+     */
+    protected $guarded = ['id'];
+
+    /**
+     * 定义分页尺码
+     *
+     * @var int
+     */
+    protected $perPage = 20;
+
+    /**
+     * 时间序列化修改
+     *
+     * @param DateTimeInterface $date
+     * @return string
+     */
+    protected function serializeDate(DateTimeInterface $date)
+    {
+        return $date->format("Y-m-d H:i:s");
+    }
+}

+ 8 - 0
app/Models/ConfigModel.php

@@ -0,0 +1,8 @@
+<?php
+
+namespace App\Models;
+
+class ConfigModel extends BaseModel
+{
+    protected $table = 'config';
+}

+ 30 - 0
app/Models/MerchantModel.php

@@ -0,0 +1,30 @@
+<?php
+
+namespace App\Models;
+
+class MerchantModel extends BaseModel
+{
+    protected $table = 'merchant';
+
+    /**
+     * UUID 键名
+     * @var string
+     */
+    protected $uuidKeyName = 'uid';
+
+    protected static function boot()
+    {
+        parent::boot();
+
+        self::created(function ($model) {
+            $uuid = hashEncodeId($model->id);
+            $model->{$model->uuidKeyName} = $uuid;
+            $model->save();
+        });
+    }
+
+    public function merchantPaymentType()
+    {
+        return $this->hasMany(MerchantPaymentTypeModel::class, 'merchant_uid', 'uid');
+    }
+}

+ 8 - 0
app/Models/MerchantPaymentTypeModel.php

@@ -0,0 +1,8 @@
+<?php
+
+namespace App\Models;
+
+class MerchantPaymentTypeModel extends BaseModel
+{
+    protected $table = 'merchant_payment_type';
+}

+ 23 - 0
app/Models/OrderModel.php

@@ -0,0 +1,23 @@
+<?php
+
+namespace App\Models;
+
+class OrderModel extends BaseModel
+{
+    protected $table = 'order';
+
+    public function merchant()
+    {
+        return $this->hasOne(MerchantModel::class, 'uid', 'merchant_uid');
+    }
+
+    public function payment()
+    {
+        return $this->hasOne(PaymentModel::class, 'uid', 'payment_uid');
+    }
+
+    public function paymentAttr()
+    {
+        return $this->hasOne(PaymentAttrModel::class, 'uid', 'payment_attr_uid');
+    }
+}

+ 23 - 0
app/Models/PayTotalModel.php

@@ -0,0 +1,23 @@
+<?php
+
+namespace App\Models;
+
+class PayTotalModel extends BaseModel
+{
+    protected $table = 'pay_total';
+
+    public function merchant()
+    {
+        return $this->hasOne(MerchantModel::class, 'uid', 'merchant_uid');
+    }
+
+    public function payment()
+    {
+        return $this->hasOne(PaymentModel::class, 'uid', 'payment_uid');
+    }
+
+    public function paymentAttr()
+    {
+        return $this->hasOne(PaymentAttrModel::class, 'uid', 'payment_attr_uid');
+    }
+}

+ 25 - 0
app/Models/PaymentAttrModel.php

@@ -0,0 +1,25 @@
+<?php
+
+namespace App\Models;
+
+class PaymentAttrModel extends BaseModel
+{
+    protected $table = 'payment_attr';
+
+    /**
+     * UUID 键名
+     * @var string
+     */
+    protected $uuidKeyName = 'uid';
+
+    protected static function boot()
+    {
+        parent::boot();
+
+        self::created(function ($model) {
+            $uuid = hashEncodeId($model->id);
+            $model->{$model->uuidKeyName} = $uuid;
+            $model->save();
+        });
+    }
+}

+ 25 - 0
app/Models/PaymentModel.php

@@ -0,0 +1,25 @@
+<?php
+
+namespace App\Models;
+
+class PaymentModel extends BaseModel
+{
+    protected $table = 'payment';
+
+    /**
+     * UUID 键名
+     * @var string
+     */
+    protected $uuidKeyName = 'uid';
+
+    protected static function boot()
+    {
+        parent::boot();
+
+        self::created(function ($model) {
+            $uuid = hashEncodeId($model->id);
+            $model->{$model->uuidKeyName} = $uuid;
+            $model->save();
+        });
+    }
+}

+ 35 - 0
app/Models/PaymentTypeModel.php

@@ -0,0 +1,35 @@
+<?php
+
+namespace App\Models;
+
+class PaymentTypeModel extends BaseModel
+{
+    protected $table = 'payment_type';
+
+    /**
+     * UUID 键名
+     * @var string
+     */
+    protected $uuidKeyName = 'uid';
+
+    protected static function boot()
+    {
+        parent::boot();
+
+        self::created(function ($model) {
+            $uuid = hashEncodeId($model->id);
+            $model->{$model->uuidKeyName} = $uuid;
+            $model->save();
+        });
+    }
+
+    public function payment()
+    {
+        return $this->hasOne(PaymentModel::class, 'uid', 'payment_uid');
+    }
+
+    public function paymentAttr()
+    {
+        return $this->hasOne(PaymentAttrModel::class, 'uid', 'payment_attr_uid');
+    }
+}

+ 44 - 0
app/Models/User.php

@@ -0,0 +1,44 @@
+<?php
+
+namespace App\Models;
+
+use Illuminate\Contracts\Auth\MustVerifyEmail;
+use Illuminate\Database\Eloquent\Factories\HasFactory;
+use Illuminate\Foundation\Auth\User as Authenticatable;
+use Illuminate\Notifications\Notifiable;
+use Laravel\Sanctum\HasApiTokens;
+
+class User extends Authenticatable
+{
+    use HasApiTokens, HasFactory, Notifiable;
+
+    /**
+     * The attributes that are mass assignable.
+     *
+     * @var array<int, string>
+     */
+    protected $fillable = [
+        'name',
+        'email',
+        'password',
+    ];
+
+    /**
+     * The attributes that should be hidden for serialization.
+     *
+     * @var array<int, string>
+     */
+    protected $hidden = [
+        'password',
+        'remember_token',
+    ];
+
+    /**
+     * The attributes that should be cast.
+     *
+     * @var array<string, string>
+     */
+    protected $casts = [
+        'email_verified_at' => 'datetime',
+    ];
+}

+ 28 - 0
app/Providers/AppServiceProvider.php

@@ -0,0 +1,28 @@
+<?php
+
+namespace App\Providers;
+
+use Illuminate\Support\ServiceProvider;
+
+class AppServiceProvider extends ServiceProvider
+{
+    /**
+     * Register any application services.
+     *
+     * @return void
+     */
+    public function register()
+    {
+        //
+    }
+
+    /**
+     * Bootstrap any application services.
+     *
+     * @return void
+     */
+    public function boot()
+    {
+        //
+    }
+}

+ 30 - 0
app/Providers/AuthServiceProvider.php

@@ -0,0 +1,30 @@
+<?php
+
+namespace App\Providers;
+
+use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
+use Illuminate\Support\Facades\Gate;
+
+class AuthServiceProvider extends ServiceProvider
+{
+    /**
+     * The model to policy mappings for the application.
+     *
+     * @var array<class-string, class-string>
+     */
+    protected $policies = [
+        // 'App\Models\Model' => 'App\Policies\ModelPolicy',
+    ];
+
+    /**
+     * Register any authentication / authorization services.
+     *
+     * @return void
+     */
+    public function boot()
+    {
+        $this->registerPolicies();
+
+        //
+    }
+}

+ 21 - 0
app/Providers/BroadcastServiceProvider.php

@@ -0,0 +1,21 @@
+<?php
+
+namespace App\Providers;
+
+use Illuminate\Support\Facades\Broadcast;
+use Illuminate\Support\ServiceProvider;
+
+class BroadcastServiceProvider extends ServiceProvider
+{
+    /**
+     * Bootstrap any application services.
+     *
+     * @return void
+     */
+    public function boot()
+    {
+        Broadcast::routes();
+
+        require base_path('routes/channels.php');
+    }
+}

+ 49 - 0
app/Providers/EventServiceProvider.php

@@ -0,0 +1,49 @@
+<?php
+
+namespace App\Providers;
+
+use App\Events\PayTotalEvent;
+use App\Listeners\PayTotalIncListener;
+use Illuminate\Auth\Events\Registered;
+use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
+use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
+use Illuminate\Support\Facades\Event;
+
+class EventServiceProvider extends ServiceProvider
+{
+    /**
+     * The event to listener mappings for the application.
+     *
+     * @var array<class-string, array<int, class-string>>
+     */
+    protected $listen = [
+        Registered::class => [
+            SendEmailVerificationNotification::class,
+        ],
+
+        // 支付统计事件监听
+        PayTotalEvent::class => [
+          PayTotalIncListener::class
+        ],
+    ];
+
+    /**
+     * Register any events for your application.
+     *
+     * @return void
+     */
+    public function boot()
+    {
+        //
+    }
+
+    /**
+     * Determine if events and listeners should be automatically discovered.
+     *
+     * @return bool
+     */
+    public function shouldDiscoverEvents()
+    {
+        return false;
+    }
+}

+ 70 - 0
app/Providers/RouteServiceProvider.php

@@ -0,0 +1,70 @@
+<?php
+
+namespace App\Providers;
+
+use Illuminate\Cache\RateLimiting\Limit;
+use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\RateLimiter;
+use Illuminate\Support\Facades\Route;
+
+class RouteServiceProvider extends ServiceProvider
+{
+    /**
+     * The path to the "home" route for your application.
+     *
+     * Typically, users are redirected here after authentication.
+     *
+     * @var string
+     */
+    public const HOME = '/home';
+
+    /**
+     * 管理后台API命名空间
+     * @var string
+     */
+    protected $manageApiNameSpace = 'Modules\ManageApi\Controllers';
+
+    /**
+     * api命名空间
+     * @var string
+     */
+    protected $apiNameSpace = 'Modules\Api\Controllers';
+
+    /**
+     * Define your route model bindings, pattern filters, and other route configuration.
+     *
+     * @return void
+     */
+    public function boot()
+    {
+        // $this->configureRateLimiting();
+
+        $this->routes(function () {
+            Route::middleware('api')
+                ->prefix('api')
+                ->namespace($this->apiNameSpace)
+                ->group(base_path('routes/api.php'));
+
+            Route::middleware('web')
+                ->group(base_path('routes/web.php'));
+
+            Route::prefix(env('MANAGE_API_URI_PREFIX', 'manage/api'))
+                ->middleware('manage_api')
+                ->namespace($this->manageApiNameSpace)
+                ->group(base_path('routes/manage_api.php'));
+        });
+    }
+
+    /**
+     * Configure the rate limiters for the application.
+     *
+     * @return void
+     */
+    protected function configureRateLimiting()
+    {
+        RateLimiter::for('api', function (Request $request) {
+            return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
+        });
+    }
+}

TEMPAT SAMPAH
app/Repositories/.DS_Store


+ 81 - 0
app/Repositories/JwtConstraints/NoExpiredValidConstraint.php

@@ -0,0 +1,81 @@
+<?php
+namespace App\Repositories\JwtConstraints;
+
+use Lcobucci\Clock\Clock;
+use Lcobucci\JWT\Token;
+use Lcobucci\JWT\UnencryptedToken;
+use Lcobucci\JWT\Validation\Constraint;
+use Lcobucci\JWT\Validation\Constraint\LeewayCannotBeNegative;
+use Lcobucci\JWT\Validation\ConstraintViolation;
+use DateInterval;
+use DateTimeInterface;
+
+class NoExpiredValidConstraint implements Constraint
+{
+    private Clock $clock;
+    private DateInterval $leeway;
+
+    public function __construct(Clock $clock, ?DateInterval $leeway = null)
+    {
+        $this->clock = $clock;
+        $this->leeway = $this->guardLeeway($leeway);
+    }
+
+    private function guardLeeway(?DateInterval $leeway): DateInterval
+    {
+        if ($leeway === null) {
+            return new DateInterval('PT0S');
+        }
+
+        if ($leeway->invert === 1) {
+            throw LeewayCannotBeNegative::create();
+        }
+
+        return $leeway;
+    }
+
+    public function assert(Token $token): void
+    {
+        if (!$token instanceof UnencryptedToken) {
+            throw new ConstraintViolation('You should pass a plain token');
+        }
+
+        $now = $this->clock->now();
+
+        $this->assertIssueTime($token, $now->add($this->leeway));
+        $this->assertMinimumTime($token, $now->add($this->leeway));
+        $this->assertHasExpiration($token, $now->sub($this->leeway));
+    }
+
+    /** @throws ConstraintViolation */
+    private function assertHasExpiration(UnencryptedToken $token, DateTimeInterface $now): void
+    {
+        if (!$token->claims()->has(Token\RegisteredClaims::EXPIRATION_TIME)) {
+            throw new ConstraintViolation('"Expiration Time" claim missing');
+        }
+    }
+
+    /** @throws ConstraintViolation */
+    private function assertMinimumTime(UnencryptedToken $token, DateTimeInterface $now): void
+    {
+        if (!$token->claims()->has(Token\RegisteredClaims::NOT_BEFORE)) {
+            throw new ConstraintViolation('"Not Before" claim missing');
+        }
+
+        if (!$token->isMinimumTimeBefore($now)) {
+            throw new ConstraintViolation('The token cannot be used yet');
+        }
+    }
+
+    /** @throws ConstraintViolation */
+    private function assertIssueTime(UnencryptedToken $token, DateTimeInterface $now): void
+    {
+        if (!$token->claims()->has(Token\RegisteredClaims::ISSUED_AT)) {
+            throw new ConstraintViolation('"Issued At" claim missing');
+        }
+
+        if (!$token->hasBeenIssuedBefore($now)) {
+            throw new ConstraintViolation('The token was issued in the future');
+        }
+    }
+}

+ 118 - 0
app/Repositories/OrderRepository.php

@@ -0,0 +1,118 @@
+<?php
+
+namespace App\Repositories;
+
+use App\Models\MerchantModel;
+use App\Models\OrderModel;
+use App\Repositories\Service\CommonService;
+use Carbon\Carbon;
+use Illuminate\Support\Facades\DB;
+use KZ2\Laravel\Repositories\Eloquent\Repository;
+
+class OrderRepository extends Repository
+{
+
+    public function model()
+    {
+        return OrderModel::class;
+    }
+
+    /**
+     * 发送回调信息
+     *
+     * @param $orderData
+     * @return void
+     */
+    public function sendSuccessCallback($orderSn)
+    {
+        $orderInfo = OrderModel::query()->where('order_sn', $orderSn)->first();
+        if (empty($orderInfo)) {
+            logger('订单不存在', [$orderSn]);
+            return false;
+        }
+
+        // 获取商户信息
+        $merchantInfo = MerchantModel::query()->where('uid', $orderInfo->merchant_uid)->first();
+        if (empty($merchantInfo)) {
+            logger('商户不存在', [$orderSn]);
+            return false;
+        }
+
+        $sendData = [
+            'merchant_code' => $merchantInfo->merchant_code,
+            'status' => $orderInfo->status == 2 ? 2 : 1,
+            'order_id' => $orderInfo->merchant_order_sn,
+            'trade_no' => $orderInfo->order_sn,
+            'amount' => $orderInfo->order_amount,
+            'real_amount' => $orderInfo->real_amount,
+            'product_name' => $orderInfo->product_name,
+            'notify_url' => $orderInfo->notify_url,
+            'redirect_url' => $orderInfo->redirect_url,
+        ];
+        $sendData['signature'] = build_sign($sendData, $merchantInfo->key);
+        $result = app(CommonService::class)->httpPost($sendData, $orderInfo['notify_url']);
+        $upData['callback_sent_times'] = $orderInfo['callback_sent_times'] + 1;
+        $res = false;
+        if ($result === 'SUCCESS') {
+            $upData['notice_status'] = 2;
+            $upData['notice_at'] = date('Y-m-d H:i:s');
+            $res = true;
+        } else {
+            logger('回调发送失败:' . $result, [$orderSn]);
+        }
+
+        // 增加回调次数
+        OrderModel::query()->where('id', $orderInfo['id'])->update($upData);
+        return $res;
+    }
+
+    /**
+     * 回调订单处理
+     *
+     * @param array $order
+     * @param $tradeNo
+     * @param $payMoney
+     * @return false
+     */
+    public function doBuy(array $order, $tradeNo, $payMoney)
+    {
+        try {
+            DB::beginTransaction();
+            $orderMoney = floatval($order['real_amount']);
+            $payMoney = floatval($payMoney);
+            $minusMoney = floatval($orderMoney - $payMoney);
+            $minusMoney = abs(round($minusMoney, 2));
+            if ($minusMoney > 2) {
+                logger("只允许订单金额和付款金额相差2元内", $order);
+                DB::rollBack();
+                return false;
+            }
+
+            //修改订单状态
+            $updateData = array(
+                'real_amount' => $payMoney,
+                'status' => 2,
+                'pay_at' => Carbon::now()->toDateTimeString(),
+                'trade_no' => $tradeNo,
+            );
+            $orderRes = OrderModel::query()->where('id', $order['id'])->update($updateData);
+            if (!$orderRes) {
+                logger("修改订单状态失败", $order);
+                DB::rollBack();
+                return false;
+            }
+
+            // 发送回调
+            $this->sendSuccessCallback($order['order_sn']);
+            DB::commit();
+            return true;
+        } catch (\Exception $e) {
+            logger($e->getMessage(), $order);
+            DB::rollBack();
+            return false;
+        }
+
+        DB::rollBack();
+        return false;
+    }
+}

TEMPAT SAMPAH
app/Repositories/Service/.DS_Store


+ 147 - 0
app/Repositories/Service/CommonService.php

@@ -0,0 +1,147 @@
+<?php
+
+declare(strict_types=1);
+
+namespace App\Repositories\Service;
+
+use App\Models\MerchantModel;
+use GuzzleHttp\Client;
+use Illuminate\Support\Facades\Request;
+
+class CommonService
+{
+    /**
+     * 获取当前请求的客户端IP
+     * @return string
+     */
+    public function getIp()
+    {
+        $headerIp = request()->getHeader('x-forwarded-for');
+        $headerIp2 = request()->getHeader('x-real-ip');
+        if ($headerIp2) {
+            $ip = $headerIp[0];
+        } elseif (!empty($headerIp)) {
+            $ip = $headerIp2[0];
+        } else {
+            $sever = request()->getServerParams();
+            $ip = $sever['remote_addr'] ?? '';
+        }
+        if (strpos($ip, ',')) {
+            $temp = explode(',', $ip);
+            $ip = $ip[0];
+            $temp = null;
+            unset($temp);
+        }
+        return $ip;
+    }
+
+    /**
+     * 生成订单流水号、可指定前缀
+     * @param string $prefix
+     * @return string
+     */
+    public function createOrderSn($prefix = '')
+    {
+        return $prefix . date('YmdHis') . mt_rand(1111, 9999);
+    }
+
+    /**
+     * 检测一个字符串是否为时间格式
+     * @param string $time
+     * @return false|int
+     */
+    public function isTime($time)
+    {
+        $pattern = '/[\d]{4}-[\d]{1,2}-[\d]{1,2}\s[\d]{1,2}:[\d]{1,2}:[\d]{1,2}/';
+        return preg_match($pattern, $time);
+    }
+
+    /**
+     * post请求
+     *
+     * @param $data
+     * @param $url
+     * @return string
+     * @throws \GuzzleHttp\Exception\GuzzleException
+     */
+    public function httpPost($data = [], $url = '', $headers = ['Content-Type' => 'application/json'])
+    {
+        $client = new Client();
+        $params = [
+            'headers' => $headers,
+            'json' => $data,
+        ];
+
+        try {
+            $response = $client->request('POST', $url, $params);
+            $code = $response->getStatusCode();
+            if ($code == 200) {
+                return $response->getBody()->getContents();
+            } else {
+                logger($response->getBody()->getContents());
+                return '';
+            }
+        } catch (\Exception $e) {
+            logger($e->getMessage());
+        }
+
+        return '';
+    }
+
+    /**
+     * get请求
+     *
+     * @param $data
+     * @param $url
+     * @return array|string
+     * @throws \GuzzleHttp\Exception\GuzzleException
+     */
+    public function httpGet($data = [], $url = '')
+    {
+        $client = new Client();
+        $params = [
+            'query' => $data
+        ];
+        try {
+            $response = $client->request('GET', $url, $params);
+            $code = $response->getStatusCode();
+            if ($code == 200) {
+                return $response->getBody()->getContents();
+            } else {
+                logger($response->getBody()->getContents());
+                return '';
+            }
+        } catch (\Exception $e) {
+            logger($e->getMessage());
+        }
+
+        return '';
+    }
+
+    /**
+     * 清除商户缓存
+     *
+     * @param $key
+     * @param $cacheNoticeUrl
+     * @return void
+     * @throws \GuzzleHttp\Exception\GuzzleException
+     */
+    public function clearMerchantCache($key, $cacheNoticeUrl)
+    {
+        $this->httpPost(['key' => $key], $cacheNoticeUrl);
+    }
+
+    /**
+     * 批量删除商户缓存
+     *
+     * @return void
+     * @throws \GuzzleHttp\Exception\GuzzleException
+     */
+    public function clearAllMerchantCache()
+    {
+        $data = MerchantModel::query()->where('status', 1)->get(['key', 'cache_notice_url']);
+        foreach ($data as $v) {
+            $this->clearMerchantCache($v->key, $v->cache_notice_url);
+        }
+    }
+}

+ 145 - 0
app/Repositories/Service/Payment/DahePayment.php

@@ -0,0 +1,145 @@
+<?php
+
+namespace App\Repositories\Service\Payment;
+
+class DahePayment extends PaymentInterface
+{
+
+    /**
+     * 支付请求地址
+     *
+     * @var string
+     */
+    private $payUrl = 'http://www.azhifu2022.com/Pay';
+
+    const SUCCESS_STATUS = 1;
+
+    /**
+     * 公钥
+     *
+     * @var string
+     */
+    private $publicKey = 'LzMIHCQnVlWFWYVJlrcAmLDUjSraEHhL';
+
+    /**
+     * @var null
+     */
+    private static $_instance = null;
+
+    /**
+     * @return WhiteSharkPayment|null
+     */
+    public static function getInstance()
+    {
+        if (is_null(self::$_instance)) {
+            self::$_instance = new self();
+        }
+        return self::$_instance;
+    }
+
+    public function createPayLink($data = array())
+    {
+        $payInfo = [
+            'fxid' => $data['partner_id'],  //商户号
+            'fxddh' => $data['order_sn'],
+            'fxdesc' => 'PAY',
+            'fxattch' => 'PAY',
+            'fxfee' => sprintf('%.2f', $data['money']),
+            'fxnotifyurl' => $data['notify_url'],
+            'fxbackurl' => $data['pay_url'],
+            'fxpay' => $data['pay_attr_code'],
+        ];
+        $payInfo['fxip'] = '127.0.0.1';
+        $payInfo['fxsign'] = md5($payInfo['fxid'] . $payInfo['fxddh'] . $payInfo['fxfee'] . $payInfo['fxnotifyurl'] . $this->publicKey);
+        $sRes = $this->httpPost($this->payUrl, $payInfo);
+        if (empty($sRes)) {
+            $this->_lastError = '请求异常';
+            payLogger(__CLASS__, '请求异常data=' . $sRes, $payInfo);
+            return '';
+        }
+
+        $aRes = json_decode($sRes, true);
+        if (empty($aRes)) {
+            $this->_lastError = '请求异常!';
+            payLogger(__CLASS__, '请求异常 (解析) data=' . $sRes, $payInfo);
+            return '';
+        }
+
+        if ($aRes['status'] != self::SUCCESS_STATUS) {
+            $this->_lastError = '请求异常,请求失败';
+            payLogger(__CLASS__, '请求异常,请求失败 data=' . $sRes, $payInfo);
+            return '';
+        }
+
+        return $aRes['payurl'];
+    }
+
+    public function queryOrder($data, &$orderSn, &$money, $partnerId)
+    {
+        $queryInfo = [
+            'fxid' => $partnerId,
+            'fxddh' => $orderSn,
+            'fxaction' => 'orderquery',
+        ];
+        $queryInfo['fxsign'] = md5($partnerId . $orderSn . 'orderquery' . $this->publicKey);
+
+        $sRes = $this->httpPost($this->payUrl, $queryInfo);
+        if (empty($sRes)) {
+            $this->_lastError = '查询请求异常';
+            payLogger(__CLASS__, '查询请求异常 data=' . $sRes, $queryInfo);
+            return '';
+        }
+
+        $aRes = json_decode($sRes, true);
+        if (empty($aRes)) {
+            $this->_lastError = '查询解析异常';
+            payLogger(__CLASS__, '查询解析异常 data=' . $sRes, $queryInfo);
+            return '';
+        }
+
+        if ($aRes['fxstatus'] != self::SUCCESS_STATUS) {
+            $this->_lastError = '查询订单状态异常';
+            payLogger(__CLASS__, '查询订单状态异常 data=' . $sRes, $queryInfo);
+            return '';
+        }
+
+        if ($money != $aRes['fxfee']) {
+            $this->_lastError = '查询订单金额异常';
+            payLogger(__CLASS__, '查询订单金额异常 data=' . $sRes, $queryInfo);
+            return '';
+        }
+
+        return true;
+    }
+
+    public function notify(&$orderSn, &$tradeNo, &$money, $partnerId)
+    {
+        $data = request()->all();
+        if ($data['fxstatus'] != self::SUCCESS_STATUS) {
+            payLogger(__CLASS__, '回调订单状态失败', $data);
+            return false;
+        }
+
+        $sign = md5($data['fxstatus'] . $partnerId . $data['fxddh'] . $data['fxfee'] . $this->publicKey);
+        if ($sign != $data['fxsign']) {
+            $this->_lastError = '验证失败!';
+            payLogger(__CLASS__, '验证失败 key=' . $this->publicKey, $data);
+            return false;
+        }
+
+        $money = $data['fxfee'];
+        $res = $this->queryOrder($data, $data['fxddh'], $money, $partnerId);
+        if (empty($res)) {
+            return '';
+        }
+
+        $orderSn = $data['fxddh'];
+        $tradeNo = $data['fxorder'];
+        return true;
+    }
+
+    public function stopNotify()
+    {
+        return 'success';
+    }
+}

+ 37 - 0
app/Repositories/Service/Payment/ErrorPayment.php

@@ -0,0 +1,37 @@
+<?php
+/**
+ * 支付方式相关.
+ *
+ * @author jim <jim458734573@gmail.com>
+ * @date   2020-01-22 22:37:21
+ */
+declare(strict_types=1);
+
+namespace App\Repositories\Service\Payment;
+
+class ErrorPayment extends PaymentInterface
+{
+    protected $paymentId = 0;
+
+    public function createPayLink($data = array())
+    {
+        $this->_lastError = '支付方式不存在';
+        return '';
+    }
+
+
+    public function stopNotify()
+    {
+        return 'nook';
+    }
+
+    public function queryOrder($data, &$orderSn, &$money, $partnerId)
+    {
+        return false;
+    }
+
+    public function notify(&$orderSn, &$tradeNo, &$money, $partnerId)
+    {
+        return false;
+    }
+}

+ 166 - 0
app/Repositories/Service/Payment/FurongPayment.php

@@ -0,0 +1,166 @@
+<?php
+
+namespace App\Repositories\Service\Payment;
+
+class FurongPayment extends PaymentInterface
+{
+    /**
+     * 支付请求地址
+     *
+     * @var string
+     */
+    private $payUrl = 'http://www.zhangyii.cn/api/mch/unifiedorder';
+
+    /**
+     * 查询请求地址
+     *
+     * @var string
+     */
+    private $queryUrl = 'http://www.zhangyii.cn/api/mch/query';
+
+    const SUCESS_CODE = 0;
+
+    const STATUS_CODE = 2;
+
+    /**
+     * @var null
+     */
+    private static $_instance = null;
+
+    /**
+     * 公钥
+     *
+     * @var string
+     */
+    private $publicKey = '4d232d3fd169419396aca3c0c5d4ed2c';
+
+    /**
+     * @return WhiteSharkPayment|null
+     */
+    public static function getInstance()
+    {
+        if (is_null(self::$_instance)) {
+            self::$_instance = new self();
+        }
+        return self::$_instance;
+    }
+
+    public function createPayLink($data = array())
+    {
+        $payInfo = [
+            'mch_id' => $data['partner_id'],
+            'pass_code' => $data['pay_attr_code'],
+            'subject' => 'PAY',
+            'out_trade_no' => $data['order_sn'],
+            'money' => $data['money'],
+            'client_ip' => '127.0.0.1',
+            'notify_url' => $data['notify_url'],
+            'return_url' => $data['pay_url'],
+            'timestamp' => date('Y-m-d H:i:s'),
+        ];
+        $payInfo['sign'] = strtoupper(md5($this->getSign($payInfo) . 'key=' . $this->publicKey));
+
+        $sRes = $this->httpPost($this->payUrl, $payInfo, 2);
+        if (empty($sRes)) {
+            $this->_lastError = '请求异常';
+            payLogger(__CLASS__, '请求异常data='.$sRes, $payInfo);
+            return '';
+        }
+
+        $aRes = json_decode($sRes, true);
+        if (empty($aRes)) {
+            $this->_lastError = '请求异常!';
+            payLogger(__CLASS__, '请求异常 (解析) data='.$sRes, $payInfo);
+            return '';
+        }
+
+        if ($aRes['code'] != self::SUCESS_CODE) {
+            $this->_lastError = '请求异常,请求失败';
+            payLogger(__CLASS__, '请求异常,请求失败 data='.$sRes, $payInfo);
+            return '';
+        }
+
+        return $aRes['data']['pay_url'];
+    }
+
+    public function queryOrder($data, &$orderSn, &$money, $partnerId)
+    {
+        $queryInfo = [
+            'mch_id' => $partnerId,
+            'out_trade_no' => $orderSn,
+            'timestamp' => date('Y-m-d H:i:s'),
+        ];
+
+        $queryInfo['sign'] = strtoupper(md5($this->getSign($queryInfo) . 'key=' . $this->publicKey));
+        $sRes = $this->httpPost($this->queryUrl, $queryInfo, 2);
+        if (empty($sRes)) {
+            $this->_lastError = '查询请求异常';
+            payLogger(__CLASS__, '查询请求异常 $queryInfo='.json_encode($queryInfo), $data);
+            return '';
+        }
+
+        $aRes = json_decode($sRes, true);
+        if (empty($sRes)) {
+            $this->_lastError = '查询解析异常';
+            payLogger(__CLASS__, '查询解析异常 data='.$sRes, $data);
+            return '';
+        }
+
+        if ($aRes['code'] != self::SUCESS_CODE) {
+            $this->_lastError = '查询订单状态异常';
+            payLogger(__CLASS__, '查询订单状态异常'.json_encode($data));
+            return '';
+        }
+
+        if ($money != $aRes['data']['money']) {
+            $this->_lastError = '查询订单金额异常';
+            payLogger(__CLASS__, '查询订单金额异常'.json_encode($data));
+            return '';
+        }
+
+        return true;
+    }
+
+    public function notify(&$orderSn, &$tradeNo, &$money, $partnerId)
+    {
+        $data = request()->all();
+        if ($data['state'] != self::STATUS_CODE) {
+            payLogger(__CLASS__, '回调订单状态失败!'.json_encode($data));
+            return false;
+        }
+
+        $info = [
+            'mch_id' => $partnerId,
+            'trade_no' => $data['trade_no'],
+            'out_trade_no' => $data['out_trade_no'],
+            'psg_trade_no' => $data['psg_trade_no'],
+            'money' => $data['money'],
+            'notify_time' => $data['notify_time'],
+            'subject' => $data['subject'],
+            'state' => $data['state']
+        ];
+
+        $sign = strtoupper(md5($this->getSign($info) . 'key=' . $this->publicKey));
+        if ($sign != $data['sign']) {
+            $this->_lastError = '验证失败!';
+            payLogger(json_encode($data) . ':验证失败!');
+            return false;
+        }
+
+        $authMoney = $data['money'];
+        $res = $this->queryOrder($data, $data['out_trade_no'], $authMoney, $partnerId);
+        if (empty($res)) {
+            return '';
+        }
+
+        $money = $data['money'];
+        $orderSn = $data['out_trade_no'];
+        $tradeNo = $data['trade_no'];
+        return true;
+    }
+
+    public function stopNotify()
+    {
+        return 'SUCCESS';
+    }
+}

+ 168 - 0
app/Repositories/Service/Payment/GdPayment.php

@@ -0,0 +1,168 @@
+<?php
+
+namespace App\Repositories\Service\Payment;
+
+class GdPayment extends PaymentInterface
+{
+    /**
+     * 支付请求地址
+     *
+     * @var string
+     */
+    private $payUrl = 'http://27.124.38.93:22336/api/unifiedorder';
+
+    /**
+     * 查询请求地址
+     *
+     * @var string
+     */
+    private $queryUrl = 'http://27.124.38.93:22336/api/query';
+
+    const SUCESS_CODE = 0;
+
+    const STATUS_CODE = 2;
+
+    /**
+     * @var null
+     */
+    private static $_instance = null;
+
+    /**
+     * 公钥
+     *
+     * @var string
+     */
+    private $publicKey = '21c14b558043451d8847f46d73782ef9';
+
+    /**
+     * @return WhiteSharkPayment|null
+     */
+    public static function getInstance()
+    {
+        if (is_null(self::$_instance)) {
+            self::$_instance = new self();
+        }
+        return self::$_instance;
+    }
+
+    public function createPayLink($data = array())
+    {
+        $payInfo = [
+            'mch_id' => $data['partner_id'],
+            'pass_code' => $data['pay_attr_code'],
+            'subject' => 'PAY',
+            'body' => 'Pay',
+            'out_trade_no' => $data['order_sn'],
+            'amount' => $data['money'],
+            'client_ip' => '127.0.0.1',
+            'notify_url' => $data['notify_url'],
+            'return_url' => $data['pay_url'],
+            'timestamp' => date('Y-m-d H:i:s'),
+        ];
+        $payInfo['sign'] = strtoupper(md5($this->getSign($payInfo, false) . $this->publicKey));
+
+        $sRes = $this->httpPost($this->payUrl, $payInfo, 2);
+        if (empty($sRes)) {
+            $this->_lastError = '请求异常';
+            //payLogger(__CLASS__, '请求异常data='.$sRes, $payInfo);
+            return '';
+        }
+
+        $aRes = json_decode($sRes, true);
+        if (empty($aRes)) {
+            $this->_lastError = '请求异常!';
+            payLogger(__CLASS__, '请求异常 (解析) data='.$sRes, $payInfo);
+            return '';
+        }
+
+        if ($aRes['code'] != self::SUCESS_CODE) {
+            $this->_lastError = '请求异常,请求失败';
+            payLogger(__CLASS__, '请求异常,请求失败 data='.$sRes, $payInfo);
+            return '';
+        }
+
+        return $aRes['data']['pay_url'];
+    }
+
+    public function queryOrder($data, &$orderSn, &$money, $partnerId)
+    {
+        $queryInfo = [
+            'mch_id' => $partnerId,
+            'out_trade_no' => $orderSn,
+            'timestamp' => date('Y-m-d H:i:s'),
+        ];
+
+        $queryInfo['sign'] = strtoupper(md5($this->getSign($queryInfo, false) . $this->publicKey));
+        $sRes = $this->httpPost($this->queryUrl, $queryInfo, 2);
+        if (empty($sRes)) {
+            $this->_lastError = '查询请求异常';
+            payLogger(__CLASS__, '查询请求异常 $queryInfo='.json_encode($queryInfo), $data);
+            return '';
+        }
+
+        $aRes = json_decode($sRes, true);
+        if (empty($sRes)) {
+            $this->_lastError = '查询解析异常';
+            payLogger(__CLASS__, '查询解析异常 data='.$sRes, $data);
+            return '';
+        }
+
+        if ($aRes['code'] != self::SUCESS_CODE) {
+            $this->_lastError = '查询订单状态异常';
+            payLogger(__CLASS__, '查询订单状态异常'.json_encode($data));
+            return '';
+        }
+
+        if ($money != $aRes['data']['money']) {
+            $this->_lastError = '查询订单金额异常';
+            payLogger(__CLASS__, '查询订单金额异常'.json_encode($data));
+            return '';
+        }
+
+        return true;
+    }
+
+    public function notify(&$orderSn, &$tradeNo, &$money, $partnerId)
+    {
+        $data = request()->all();
+        if ($data['status'] != self::STATUS_CODE) {
+            payLogger(__CLASS__, '回调订单状态失败!'.json_encode($data));
+            return false;
+        }
+
+        $info = [
+            'mch_id' => $partnerId,
+            'trade_no' => $data['trade_no'],
+            'out_trade_no' => $data['out_trade_no'],
+            'original_trade_no' => $data['original_trade_no'],
+            'money' => $data['money'],
+            'notify_time' => $data['notify_time'],
+            'subject' => $data['subject'],
+            'body' => $data['body'],
+            'status' => $data['status']
+        ];
+
+        $sign = strtoupper(md5($this->getSign($info, false) . $this->publicKey));
+        if ($sign != $data['sign']) {
+            $this->_lastError = '验证失败!';
+            logger(json_encode($data) . ':验证失败!');
+            return false;
+        }
+
+        $authMoney = $data['money'];
+        $res = $this->queryOrder($data, $data['out_trade_no'], $authMoney, $partnerId);
+        if (empty($res)) {
+            return '';
+        }
+
+        $money = $data['money'];
+        $orderSn = $data['out_trade_no'];
+        $tradeNo = $data['trade_no'];
+        return true;
+    }
+
+    public function stopNotify()
+    {
+        return 'SUCCESS';
+    }
+}

+ 185 - 0
app/Repositories/Service/Payment/JingchengPayment.php

@@ -0,0 +1,185 @@
+<?php
+
+namespace App\Repositories\Service\Payment;
+
+class JingchengPayment extends PaymentInterface
+{
+    /**
+     * 支付请求地址
+     *
+     * @var string
+     */
+    private $payUrl = 'http://pay.jincen888.cn/api/pay/create_order';
+
+    /**
+     * 查询请求地址
+     *
+     * @var string
+     */
+    private $queryUrl = 'http://pay.jincen888.cn/api/pay/query_order';
+
+    private $appid = 'da0615906db345a981c6c03e6029f066';
+
+    /**
+     * 公钥
+     *
+     * @var string
+     */
+    private $publicKey = 'QWWDU20FV2CATA0V7VVS8SUILQFFWHIGMVKTVEHP4ZG0KICKSMDLIFQG0OMJGZPL8TGB7FATAV0T0ADJRPFLLEACOB0HMUTDXIXNUHPL7KVUDNOICLSMMNK5JTUWC0HU';
+
+    const SUCESS_CODE = 0;
+
+    const STATUS_CODE = 2;
+
+    /**
+     * @var null
+     */
+    private static $_instance = null;
+
+    /**
+     * @return WhiteSharkPayment|null
+     */
+    public static function getInstance()
+    {
+        if (is_null(self::$_instance)) {
+            self::$_instance = new self();
+        }
+        return self::$_instance;
+    }
+
+    public function createPayLink($data = array())
+    {
+        $payInfo = [
+            'mchId' => $data['partner_id'],  //商户号
+            'appId' => $this->appid,
+            'productId' => $data['pay_attr_code'],
+            'mchOrderNo' => $data['order_sn'],
+            'currency' => 'cny',
+            'amount' => $data['money'] * 100,
+            "clientIp" => $data['ip'] ?: '127.0.0.0',
+            "device" => 'ios10.3.1',
+            'notifyUrl' => $data['notify_url'],
+            'returnUrl' => $data['pay_url'],
+            'subject' => 'shop',
+            'body' => 'CJ',
+            'reqTime' => date('YmdHis'),
+            'version' => '1.0'
+        ];
+
+        $payInfo['sign'] = strtoupper(md5($this->getSign($payInfo) . 'key=' . $this->publicKey));
+        $sRes = $this->httpPost($this->payUrl, $payInfo);
+        if (empty($sRes)) {
+            $this->_lastError = '请求异常';
+            payLogger(__CLASS__, '请求异常data='.$sRes, $payInfo);
+            return '';
+        }
+        logger($sRes);
+        $aRes = json_decode($sRes, true);
+        if (empty($aRes)) {
+            $this->_lastError = '请求异常!';
+            payLogger(__CLASS__, '请求异常 (解析) data='.$sRes, $payInfo);
+            return '';
+        }
+
+        if ($aRes['retCode'] != self::SUCESS_CODE) {
+            $this->_lastError = '请求异常,请求失败';
+            payLogger(__CLASS__, '请求异常,请求失败 data='.$sRes, $payInfo);
+            return '';
+        }
+
+        return $aRes['payJumpUrl'];
+    }
+
+    public function queryOrder($data, &$orderSn, &$money, $partnerId)
+    {
+        $queryInfo = [
+            'mchId' => $partnerId,
+            'payOrderId' => $data['payOrderId'],
+            'mchOrderNo' => $orderSn,
+            "reqTime" => date("YmdHis"),
+            "version" => '1.0'
+        ];
+
+        $queryInfo['sign'] = strtoupper(md5($this->getSign($queryInfo) . 'key=' . $this->publicKey));
+        $sRes = $this->httpPost($this->queryUrl, $queryInfo);
+        if (empty($sRes)) {
+            $this->_lastError = '查询请求异常';
+            payLogger(__CLASS__, '查询请求异常 $queryInfo='.json_encode($queryInfo), $data);
+            return '';
+        }
+
+        $aRes = json_decode($sRes, true);
+        if (empty($sRes)) {
+            $this->_lastError = '查询解析异常';
+            payLogger(__CLASS__, '查询解析异常 data='.$sRes, $data);
+            return '';
+        }
+
+        if ($aRes['retCode'] != self::SUCESS_CODE) {
+            $this->_lastError = '查询订单状态异常';
+            payLogger(__CLASS__, '查询订单状态异常'.json_encode($data));
+            return '';
+        }
+
+        if ($money != $aRes['amount']) {
+            $this->_lastError = '查询订单金额异常';
+            payLogger(__CLASS__, '查询订单金额异常'.json_encode($data));
+            return '';
+        }
+
+        return true;
+    }
+
+    public function notify(&$orderSn, &$tradeNo, &$money, $partnerId)
+    {
+        $data = request()->all();
+        if (!($data['status'] == self::STATUS_CODE || $data['status'] == 3)) {
+            payLogger(__CLASS__, '回调订单状态失败!'.json_encode($data));
+            return false;
+        }
+
+        $info = [
+            'payOrderId' => $data['payOrderId'],
+            'mchId' => $partnerId,
+            'appId' => $this->appid,
+            'productId' => $data['productId'],
+            'mchOrderNo' => $data['mchOrderNo'],
+            'amount' => $data['amount'],
+            'status' => $data['status'],
+            'income' => $data['income'],
+            'backType' => $data['backType'],
+            'reqTime' => $data['reqTime']
+        ];
+
+        if (!empty($data['paySuccTime'])) {
+            $info['paySuccTime'] = $data['paySuccTime'];
+        }
+
+        if (!empty($data['channelOrderNo'])) {
+            $info['channelOrderNo'] = $data['channelOrderNo'];
+        }
+
+        $sign = strtoupper(md5($this->getSign($info) . 'key=' . $this->publicKey));
+        if ($sign != $data['sign']) {
+            $this->_lastError = '验证失败!';
+            logger(json_encode($data) . ':验证失败!');
+            return false;
+        }
+
+        $authMoney = $data['amount'];
+        $res = $this->queryOrder($data, $data['mchOrderNo'], $authMoney, $partnerId);
+        if (empty($res)) {
+            return '';
+        }
+
+        $money = $data['amount'] / 100;
+        $orderSn = $data['mchOrderNo'];
+        $tradeNo = $data['payOrderId'];
+        return true;
+    }
+
+    public function stopNotify()
+    {
+        return 'success';
+    }
+}

+ 165 - 0
app/Repositories/Service/Payment/JuPayment.php

@@ -0,0 +1,165 @@
+<?php
+
+namespace App\Repositories\Service\Payment;
+
+class JuPayment extends PaymentInterface
+{
+    /**
+     * 支付请求地址
+     *
+     * @var string
+     */
+    private $payUrl = 'http://47.122.3.7/pay/';
+
+    /**
+     * 查询请求地址
+     *
+     * @var string
+     */
+    private $queryUrl = 'http://47.122.3.7/pay/index.php?c=merorderQuery';
+
+    /**
+     * 私钥
+     *
+     * @var string
+     */
+    private $publicKey = '362c48dbc91f7693c1';
+
+    const SUCESS_CODE = 4;
+
+    /**
+     * @var null
+     */
+    private static $_instance = null;
+
+    /**
+     * @return WhiteSharkPayment|null
+     */
+    public static function getInstance()
+    {
+        if (is_null(self::$_instance)) {
+            self::$_instance = new self();
+        }
+        return self::$_instance;
+    }
+
+    public function createPayLink($data = array())
+    {
+        $payInfo = [
+            'amount' => sprintf('%.2f', $data['money']),
+            'partnerid' => $data['partner_id'],  //商户号
+            'notifyUrl' => $data['notify_url'],
+            'out_trade_no' => $data['order_sn'],
+            'payType' => $data['pay_attr_code'],
+            'returnUrl' => $data['pay_url'],
+            'version' => '1.0',
+            'format' => 'json'
+        ];
+
+        $payInfo['sign'] = strtoupper(md5($this->getSign($payInfo) . "key=" . $this->publicKey));
+        $sRes = $this->httpPost($this->payUrl, $payInfo);
+        if (empty($sRes)) {
+            $this->_lastError = '请求异常';
+            payLogger(__CLASS__,'请求异常data='.$sRes,$payInfo);
+            return '';
+        }
+
+        $aRes = json_decode($sRes, true);
+        if (empty($aRes)) {
+            $this->_lastError = '请求异常!';
+            payLogger(__CLASS__,'请求异常 (解析) data='.$sRes,$payInfo);
+            return '';
+        }
+
+        if ((isset($aRes['code']) && $aRes['code'] != 200)) {
+            $this->_lastError = '请求异常,请求失败';
+            payLogger(__CLASS__,'请求异常,请求失败 data='.$sRes,$payInfo);
+            return '';
+        }
+
+        return $aRes['data']['url'];
+    }
+
+    public function queryOrder($data, &$orderSn, &$money, $partnerId)
+    {
+        $queryInfo = [
+            'partnerid' => $partnerId,
+            'out_trade_no' => $orderSn,
+            'version' => '1.0',
+        ];
+        $queryInfo['sign'] = strtoupper(md5($this->getSign($queryInfo) . "key=" . $this->publicKey));
+
+        $sRes = $this->httpPost($this->queryUrl, $queryInfo);
+        if (empty($sRes)) {
+            $this->_lastError = '查询请求异常';
+            payLogger(__CLASS__,'查询请求异常 $queryInfo='.json_encode($queryInfo),$data);
+            return '';
+        }
+
+        $aRes = json_decode($sRes, true);
+        if (empty($aRes)) {
+            $this->_lastError = '查询解析异常';
+            payLogger(__CLASS__,'查询解析异常 data='.$sRes,$data);
+            return '';
+        }
+
+        if ($aRes['data']['status'] != self::SUCESS_CODE || $aRes['code'] != 200) {
+            $this->_lastError = '查询订单状态异常';
+            payLogger(__CLASS__,'查询订单状态异常 data='.$sRes,$data);
+            return '';
+        }
+
+        if ($aRes['data']['money'] != $money) {
+            $this->_lastError = '查询订单金额异常';
+            payLogger(__CLASS__,'查询订单金额异常 data='.$sRes,$data);
+            return '';
+        }
+
+        return true;
+    }
+
+    public function notify(&$orderSn, &$tradeNo, &$money, $partnerId)
+    {
+        $data = request()->all();
+        if ($data['status'] != self::SUCESS_CODE) {
+            payLogger(__CLASS__,'回调订单状态失败',$data);
+            return false;
+        }
+
+        $info = [
+            'amount' => $data['amount'],
+            'callbacks' => $data['callbacks'],
+            'money' => $data['money'],
+            'out_trade_no' => $data['out_trade_no'],
+            'partnerid' => $data['partnerid'],
+            'pay_time' => $data['pay_time'],
+            'pay_type' => $data['pay_type'],
+            'status' => $data['status'],
+            'sysorderno' => $data['sysorderno'],
+            'version' => $data['version'],
+        ];
+
+        $sign = strtoupper(md5($this->getSign($info) . "key=" . $this->publicKey));
+
+        if ($sign != $data['sign']) {
+            $this->_lastError = '验证失败!';
+            payLogger(__CLASS__, '验证失败 $sign=' . $sign, $data);
+            return false;
+        }
+
+        $money = $data['amount'];
+        $res = $this->queryOrder($data, $data['out_trade_no'], $money, $partnerId);
+        if (empty($res)) {
+            return '';
+        }
+
+        $orderSn = $data['out_trade_no'];
+        $tradeNo = $data['sysorderno'];
+        return true;
+    }
+
+    public function stopNotify()
+    {
+        return 'success';
+    }
+}

+ 166 - 0
app/Repositories/Service/Payment/JutwoPayment.php

@@ -0,0 +1,166 @@
+<?php
+
+namespace App\Repositories\Service\Payment;
+
+class JutwoPayment extends PaymentInterface
+{
+    /**
+     * 支付请求地址
+     *
+     * @var string
+     */
+    private $payUrl = 'http://206.233.132.211:8080/api/mch/unifiedorder';
+
+    /**
+     * 查询请求地址
+     *
+     * @var string
+     */
+    private $queryUrl = 'http://206.233.132.211:8080/api/mch/query';
+
+    const SUCESS_CODE = 0;
+
+    const STATUS_CODE = 2;
+
+    /**
+     * @var null
+     */
+    private static $_instance = null;
+
+    /**
+     * 公钥
+     *
+     * @var string
+     */
+    private $publicKey = 'a9472550314c40439164f88e8b3e970b';
+
+    /**
+     * @return WhiteSharkPayment|null
+     */
+    public static function getInstance()
+    {
+        if (is_null(self::$_instance)) {
+            self::$_instance = new self();
+        }
+        return self::$_instance;
+    }
+
+    public function createPayLink($data = array())
+    {
+        $payInfo = [
+            'mch_id' => $data['partner_id'],
+            'pass_code' => $data['pay_attr_code'],
+            'subject' => 'PAY',
+            'out_trade_no' => $data['order_sn'],
+            'money' => $data['money'],
+            'client_ip' => '127.0.0.1',
+            'notify_url' => $data['notify_url'],
+            'return_url' => $data['pay_url'],
+            'timestamp' => date('Y-m-d H:i:s'),
+        ];
+        $payInfo['sign'] = strtoupper(md5($this->getSign($payInfo) . 'key=' . $this->publicKey));
+
+        $sRes = $this->httpPost($this->payUrl, $payInfo, 2);
+        if (empty($sRes)) {
+            $this->_lastError = '请求异常';
+            payLogger(__CLASS__, '请求异常data='.$sRes, $payInfo);
+            return '';
+        }
+
+        $aRes = json_decode($sRes, true);
+        if (empty($aRes)) {
+            $this->_lastError = '请求异常!';
+            payLogger(__CLASS__, '请求异常 (解析) data='.$sRes, $payInfo);
+            return '';
+        }
+
+        if ($aRes['code'] != self::SUCESS_CODE) {
+            $this->_lastError = '请求异常,请求失败';
+            payLogger(__CLASS__, '请求异常,请求失败 data='.$sRes, $payInfo);
+            return '';
+        }
+
+        return $aRes['data']['pay_url'];
+    }
+
+    public function queryOrder($data, &$orderSn, &$money, $partnerId)
+    {
+        $queryInfo = [
+            'mch_id' => $partnerId,
+            'out_trade_no' => $orderSn,
+            'timestamp' => date('Y-m-d H:i:s'),
+        ];
+
+        $queryInfo['sign'] = strtoupper(md5($this->getSign($queryInfo) . 'key=' . $this->publicKey));
+        $sRes = $this->httpPost($this->queryUrl, $queryInfo, 2);
+        if (empty($sRes)) {
+            $this->_lastError = '查询请求异常';
+            payLogger(__CLASS__, '查询请求异常 $queryInfo='.json_encode($queryInfo), $data);
+            return '';
+        }
+
+        $aRes = json_decode($sRes, true);
+        if (empty($sRes)) {
+            $this->_lastError = '查询解析异常';
+            payLogger(__CLASS__, '查询解析异常 data='.$sRes, $data);
+            return '';
+        }
+
+        if ($aRes['code'] != self::SUCESS_CODE) {
+            $this->_lastError = '查询订单状态异常';
+            payLogger(__CLASS__, '查询订单状态异常'.json_encode($data));
+            return '';
+        }
+
+        if ($money != $aRes['data']['money']) {
+            $this->_lastError = '查询订单金额异常';
+            payLogger(__CLASS__, '查询订单金额异常'.json_encode($data));
+            return '';
+        }
+
+        return true;
+    }
+
+    public function notify(&$orderSn, &$tradeNo, &$money, $partnerId)
+    {
+        $data = request()->all();
+        if ($data['state'] != self::STATUS_CODE) {
+            payLogger(__CLASS__, '回调订单状态失败!'.json_encode($data));
+            return false;
+        }
+
+        $info = [
+            'mch_id' => $partnerId,
+            'trade_no' => $data['trade_no'],
+            'out_trade_no' => $data['out_trade_no'],
+            'psg_trade_no' => $data['psg_trade_no'],
+            'money' => $data['money'],
+            'notify_time' => $data['notify_time'],
+            'subject' => $data['subject'],
+            'state' => $data['state']
+        ];
+
+        $sign = strtoupper(md5($this->getSign($info) . 'key=' . $this->publicKey));
+        if ($sign != $data['sign']) {
+            $this->_lastError = '验证失败!';
+            logger(json_encode($data) . ':验证失败!');
+            return false;
+        }
+
+        $authMoney = $data['money'];
+        $res = $this->queryOrder($data, $data['out_trade_no'], $authMoney, $partnerId);
+        if (empty($res)) {
+            return '';
+        }
+
+        $money = $data['money'];
+        $orderSn = $data['out_trade_no'];
+        $tradeNo = $data['trade_no'];
+        return true;
+    }
+
+    public function stopNotify()
+    {
+        return 'SUCCESS';
+    }
+}

+ 138 - 0
app/Repositories/Service/Payment/KaopuPayment.php

@@ -0,0 +1,138 @@
+<?php
+
+namespace App\Repositories\Service\Payment;
+
+class KaopuPayment extends PaymentInterface
+{
+    /**
+     * 支付请求地址
+     *
+     * @var string
+     */
+    private $payUrl = 'http://116.62.180.111:8888/WebHandler/H5PayHandler.ashx';
+
+    /**
+     * 查询请求地址
+     *
+     * @var string
+     */
+    private $queryUrl = 'http://116.62.180.111:8888/WebHandler/H5PayStateHandler.ashx';
+
+    const SUCESS_CODE = 200;
+
+    /**
+     * @var null
+     */
+    private static $_instance = null;
+
+    /**
+     * @return WhiteSharkPayment|null
+     */
+    public static function getInstance()
+    {
+        if (is_null(self::$_instance)) {
+            self::$_instance = new self();
+        }
+        return self::$_instance;
+    }
+
+    /**
+     * 私钥
+     *
+     * @var string
+     */
+    private $publicKey = 'MIZFYK0C';
+
+    public function createPayLink($data = array())
+    {
+        $payInfo = [
+            'CustomerId' => $data['partner_id'],
+            'OrderId' => $data['orderSn'],
+            'Money' => $data['money'],
+            'notifyUrl' => $data['notify_url']
+        ];
+        $md5Sign = md5("CustomerId={$data['partner_id']}&key={$this->publicKey}&Money={$data['money']}&notifyUrl={$data['notify_url']}&OrderId={$data['order_sn']}");
+
+        $sRes = $this->httpGet($this->payUrl . '?CustomerId=' . $data['partner_id'] . '&OrderId=' . $data['orderSn']. '&Money=' . $data['money'] . '&notifyUrl=' . $data['notify_url'] . '&MD5sign=' . $md5Sign);
+        if (empty($sRes)) {
+            $this->_lastError = '请求异常';
+            payLogger(__CLASS__,'请求异常data='.$sRes,$payInfo);
+            return '';
+        }
+
+        $aRes = json_decode($sRes, true);
+        if (empty($aRes)) {
+            $this->_lastError = '请求异常!';
+            payLogger(__CLASS__,'请求异常 (解析) data='.$sRes,$payInfo);
+            return '';
+        }
+
+        if ($aRes['result_state'] != 'true' || $aRes['msg'] != '成功') {
+            $this->_lastError = '请求异常,请求失败';
+            payLogger(__CLASS__,'请求异常,请求失败 data='.$sRes,$payInfo);
+            return '';
+        }
+
+        return $aRes['payUrl'];
+    }
+
+    public function queryOrder($data, &$orderSn, &$money, $partnerId)
+    {
+        $sign = strtoupper(md5("CustomerId={$partnerId}&key={$this->publicKey}&OrderId={$orderSn}"));
+
+        $queryUrl = $this->queryUrl . '?CustomerId=' . $partnerId . '&OrderId=' . $orderSn . '&MD5sign=' . $sign;
+        $sRes = $this->httpGet($queryUrl);
+        if (empty($sRes)) {
+            $this->_lastError = '查询请求异常';
+            payLogger(__CLASS__,'查询请求异常 $queryInfo=' . $queryUrl ,$data);
+            return '';
+        }
+
+        $aRes = json_decode($sRes, true);
+        if (empty($aRes)) {
+            $this->_lastError = '查询解析异常';
+            payLogger(__CLASS__,'查询解析异常 data='.$sRes,$data);
+            return '';
+        }
+
+        if ($aRes['result_state'] != true || $aRes['msg'] != '支付成功') {
+            $this->_lastError = '查询订单状态异常';
+            payLogger(__CLASS__,'查询订单状态异常 data='.$sRes,$data);
+            return '';
+        }
+
+        return true;
+    }
+
+
+    public function notify(&$orderSn, &$tradeNo, &$money, $partnerId)
+    {
+        $data = request()->all();
+        if ($data['code'] != self::SUCESS_CODE) {
+            payLogger(__CLASS__,'回调订单状态失败',$data);
+            return false;
+        }
+
+        $sign = strtoupper(md5("code={$data['code']}&key={$this->publicKey}&money={$data['money']}&orderDate={$data['orderDate']}&OrderId={$data['OrderId']}&OrderNumber={$data['OrderNumber']}"));
+        if ($sign != $data['sign']) {
+            $this->_lastError = '验证失败!';
+            payLogger(__CLASS__, '验证失败 $sign=' . $sign, $data);
+            return false;
+        }
+
+        $money = $data['money'];
+        $res = $this->queryOrder($data, $data['OrderId'], $money, $partnerId);
+        if (empty($res)) {
+            return '';
+        }
+
+        $orderSn = $data['OrderId'];
+        $tradeNo = $data['OrderNumber'];
+        return true;
+    }
+
+    public function stopNotify()
+    {
+        return 'success';
+    }
+}

+ 189 - 0
app/Repositories/Service/Payment/MayiPayment.php

@@ -0,0 +1,189 @@
+<?php
+
+namespace App\Repositories\Service\Payment;
+
+use App\Repositories\ConfigRepoistory;
+use App\Repositories\Service\CommonService;
+
+class MayiPayment extends PaymentInterface
+{
+    /**
+     * 支付请求地址
+     *
+     * @var string
+     */
+    private $payUrl = 'http://16.162.202.172:56700/api/pay/create_order';
+
+    /**
+     * 查询请求地址
+     *
+     * @var string
+     */
+    private $queryUrl = 'http://16.162.202.172:56700/api/pay/query_order';
+
+    private $appid = '889a781ef3ed4135be7257d458572c51';
+
+    /**
+     * 公钥
+     *
+     * @var string
+     */
+    private $publicKey = 'GBHZ7JGQJSFEDTNDLB0B9RLITZV0AQXKLUA35YTUH1YX4C3NB2YMWNUYNRYCSGTOZTGQ1NQFTLRPMPSQA8QXJTARP1MOKB3ZTKTGVTYDTNIZ3WHSLHJRHJYRG5W2Y3S5';
+
+    const SUCESS_CODE = 0;
+
+    const STATUS_CODE = 2;
+
+    /**
+     * @var null
+     */
+    private static $_instance = null;
+
+    /**
+     * @return WhiteSharkPayment|null
+     */
+    public static function getInstance()
+    {
+        if (is_null(self::$_instance)) {
+            self::$_instance = new self();
+        }
+        return self::$_instance;
+    }
+
+    public function createPayLink($data = array())
+    {
+
+        $payInfo = [
+            'mchId' => $data['partner_id'],  //商户号
+            'appId' => $this->appid,
+            'productId' => $data['pay_attr_code'],
+            'mchOrderNo' => $data['order_sn'],
+            'currency' => 'cny',
+            'amount' => $data['money'] * 100,
+            "clientIp" => $data['ip'] ?: '127.0.0.0',
+            "device" => 'ios10.3.1',
+            'notifyUrl' => $data['notify_url'],
+            'returnUrl' => $data['pay_url'],
+            'subject' => 'shop',
+            'body' => 'CJ',
+            'reqTime' => date('YmdHis'),
+            'version' => '1.0'
+        ];
+
+        $payInfo['sign'] = strtoupper(md5($this->getSign($payInfo) . 'key=' . $this->publicKey));
+        $sRes = $this->httpPost($this->payUrl, $payInfo);
+        if (empty($sRes)) {
+            $this->_lastError = '请求异常';
+            payLogger(__CLASS__, '请求异常data='.$sRes, $payInfo);
+            return '';
+        }
+
+        $aRes = json_decode($sRes, true);
+        if (empty($aRes)) {
+            $this->_lastError = '请求异常!';
+            payLogger(__CLASS__, '请求异常 (解析) data='.$sRes, $payInfo);
+            return '';
+        }
+
+        if ($aRes['retCode'] != self::SUCESS_CODE) {
+            $this->_lastError = '请求异常,请求失败';
+            payLogger(__CLASS__, '请求异常,请求失败 data='.$sRes, $payInfo);
+            return '';
+        }
+
+        return $aRes['payParams']['payUrl'];
+    }
+
+    public function queryOrder($data, &$orderSn, &$money, $partnerId)
+    {
+        $queryInfo = [
+            'mchId' => $partnerId,
+            'payOrderId' => $data['payOrderId'],
+            'mchOrderNo' => $orderSn,
+            "reqTime" => date("YmdHis"),
+            "version" => '1.0'
+        ];
+
+        $queryInfo['sign'] = strtoupper(md5($this->getSign($queryInfo) . 'key=' . $this->publicKey));
+        $sRes = $this->httpPost($this->queryUrl, $queryInfo);
+        if (empty($sRes)) {
+            $this->_lastError = '查询请求异常';
+            payLogger(__CLASS__, '查询请求异常 $queryInfo='.json_encode($queryInfo), $data);
+            return '';
+        }
+
+        $aRes = json_decode($sRes, true);
+        if (empty($sRes)) {
+            $this->_lastError = '查询解析异常';
+            payLogger(__CLASS__, '查询解析异常 data='.$sRes, $data);
+            return '';
+        }
+
+        if ($aRes['retCode'] != self::SUCESS_CODE) {
+            $this->_lastError = '查询订单状态异常';
+            payLogger(__CLASS__, '查询订单状态异常'.json_encode($data));
+            return '';
+        }
+
+        if ($money != $aRes['amount']) {
+            $this->_lastError = '查询订单金额异常';
+            payLogger(__CLASS__, '查询订单金额异常'.json_encode($data));
+            return '';
+        }
+
+        return true;
+    }
+
+    public function notify(&$orderSn, &$tradeNo, &$money, $partnerId)
+    {
+        $data = request()->all();
+        if (!($data['status'] == self::STATUS_CODE || $data['status'] == 3)) {
+            payLogger(__CLASS__, '回调订单状态失败!'.json_encode($data));
+            return false;
+        }
+
+        $info = [
+            'payOrderId' => $data['payOrderId'],
+            'mchId' => $partnerId,
+            'appId' => $data['appId'],
+            'productId' => $data['productId'],
+            'mchOrderNo' => $data['mchOrderNo'],
+            'amount' => $data['amount'],
+            'status' => $data['status'],
+            'income' => $data['income'],
+            'backType' => $data['backType'],
+            'reqTime' => $data['reqTime']
+        ];
+
+        if (!empty($data['paySuccTime'])) {
+            $info['paySuccTime'] = $data['paySuccTime'];
+        }
+
+        if (!empty($data['channelOrderNo'])) {
+            $info['channelOrderNo'] = $data['channelOrderNo'];
+        }
+
+        $sign = strtoupper(md5($this->getSign($info) . 'key=' . $this->publicKey));
+        if ($sign != $data['sign']) {
+            $this->_lastError = '验证失败!';
+            logger(json_encode($data) . ':验证失败!');
+            return false;
+        }
+
+        $authMoney = $data['amount'];
+        $res = $this->queryOrder($data, $data['mchOrderNo'], $authMoney, $partnerId);
+        if (empty($res)) {
+            return '';
+        }
+
+        $money = $data['amount'] / 100;
+        $orderSn = $data['mchOrderNo'];
+        $tradeNo = $data['payOrderId'];
+        return true;
+    }
+
+    public function stopNotify()
+    {
+        return 'success';
+    }
+}

+ 184 - 0
app/Repositories/Service/Payment/MingsenPayment.php

@@ -0,0 +1,184 @@
+<?php
+
+namespace App\Repositories\Service\Payment;
+
+use App\Repositories\ConfigRepoistory;
+use App\Repositories\Service\CommonService;
+
+class MingsenPayment extends PaymentInterface
+{
+    /**
+     * 查询请求地址
+     *
+     * @var string
+     */
+    private $queryUrl = 'http://akmszf.com/Pay_Trade_query.html';
+
+    /**
+     * 支付请求地址
+     *
+     * @var string
+     */
+    private $payUrl = 'http://akmszf.com/Pay_Index.html';
+
+    /**
+     * 公钥
+     *
+     * @var string
+     */
+    private $publicKey = 'k66zep0bk8cp8jrl8ts4jib2hru9xa20';
+
+    /**
+     * 公钥
+     *
+     * @var string
+     */
+
+    const SUCESS_CODE = '00';
+
+    const STATUS_SUCCESS = 'success';
+
+    /**
+     * @var null
+     */
+    private static $_instance = null;
+
+    /**
+     * @return WhiteSharkPayment|null
+     */
+    public static function getInstance()
+    {
+        if (is_null(self::$_instance)) {
+            self::$_instance = new self();
+        }
+        return self::$_instance;
+    }
+
+    public function createPayLink($data = array())
+    {
+        $payInfo = [
+            'pay_memberid' => $data['partner_id'],  //商户号
+            'pay_orderid' => $data['order_sn'],
+            'pay_applydate' => date('Y-m-d H:i:s'),
+            'pay_bankcode' => $data['pay_attr_code'],
+            'pay_notifyurl' => $data['notify_url'],
+            'pay_callbackurl' => $data['pay_url'],
+            'pay_amount' => sprintf('%.2f', $data['money']),
+        ];
+
+        $payInfo['pay_md5sign'] = strtoupper(md5($this->getSign($payInfo) . "key=" . $this->publicKey));
+        $payInfo['pay_productname'] = 'SG';
+
+        $sRes = $this->httpPost($this->payUrl, $payInfo);
+
+        $url = '';
+        if (strpos($sRes, 'http') !== false) {
+            $url = $sRes;
+        } else {
+            if (empty($sRes)) {
+                $this->_lastError = '请求异常';
+                payLogger(__CLASS__, '请求异常data='.$sRes,  $payInfo);
+                return '';
+            }
+
+            $aRes = json_decode($sRes, true);
+            if (empty($aRes)) {
+                $this->_lastError = '请求异常!';
+                payLogger(__CLASS__,'请求异常 (解析) data='.$sRes, $payInfo);
+                return '';
+            }
+
+            if ($aRes['status'] != self::STATUS_SUCCESS) {
+                $this->_lastError = '请求异常,请求失败';
+                payLogger(__CLASS__,'请求异常,请求失败 data='.$sRes,$payInfo);
+                return '';
+            }
+        }
+
+        if (empty($url)) {
+            $this->_lastError = '通道方返回为空';
+            payLogger(__CLASS__,'通道方返回为空 data='.$sRes,$payInfo);
+            return '';
+        }
+
+        return $url;
+    }
+
+    public function queryOrder($data, &$orderSn, &$money, $partnerId)
+    {
+        $queryInfo = [
+            'pay_memberid' => $partnerId,
+            'pay_orderid' => $orderSn
+        ];
+        $queryInfo['pay_md5sign'] = strtoupper(md5($this->getSign($queryInfo) . "key=" . $this->publicKey));
+
+        $sRes = $this->httpPost($this->queryUrl, $queryInfo);
+        if (empty($sRes)) {
+            $this->_lastError = '查询请求异常';
+            payLogger(__CLASS__,'查询请求异常 $queryInfo='.json_encode($queryInfo),$data);
+            return '';
+        }
+
+        $aRes = json_decode($sRes, true);
+        if (empty($aRes)) {
+            $this->_lastError = '查询解析异常';
+            payLogger(__CLASS__,'查询解析异常 data='.$sRes,$data);
+            return '';
+        }
+
+        if ($aRes['returncode'] != self::SUCESS_CODE || $aRes['trade_state'] !== 'SUCCESS') {
+            $this->_lastError = '查询订单状态异常';
+            payLogger(__CLASS__,'查询订单状态异常 data='.$sRes,$data);
+            return '';
+        }
+
+        if ($money != $aRes['amount']) {
+            $this->_lastError = '查询订单金额异常';
+            payLogger(__CLASS__,'查询订单金额异常 data='.$sRes,$data);
+            return '';
+        }
+
+        return true;
+    }
+
+    public function notify(&$orderSn, &$tradeNo, &$money, $partnerId)
+    {
+        $data = request()->all();
+        if ($data['returncode'] != self::SUCESS_CODE) {
+            payLogger(__CLASS__, '回调订单状态失败', $data);
+            return false;
+        }
+
+        $info = [
+            'memberid' => $partnerId,
+            'orderid' => $data['orderid'],
+            'amount' => $data['amount'],
+            'transaction_id' => $data['transaction_id'],
+            'datetime' => $data['datetime'],
+            'returncode' => $data['returncode']
+        ];
+
+        $sign = strtoupper(md5($this->getSign($info) . "key=" . $this->publicKey));
+
+        if ($sign != $data['sign']) {
+            $this->_lastError = '验证失败!';
+            payLogger(__CLASS__, '验证失败 $sign=' . $sign, $data);
+            return false;
+        }
+
+        $money = $data['amount'];
+        $res = $this->queryOrder($data, $data['orderid'], $money, $partnerId);
+        if (empty($res)) {
+            return '';
+        }
+
+        $orderSn = $data['orderid'];
+        $tradeNo = $data['transaction_id'];
+        return true;
+    }
+
+    public function stopNotify()
+    {
+        return 'OK';
+    }
+}

+ 193 - 0
app/Repositories/Service/Payment/PaymentInterface.php

@@ -0,0 +1,193 @@
+<?php
+
+declare(strict_types=1);
+
+namespace App\Repositories\Service\Payment;
+
+use Illuminate\Support\Facades\Http;
+
+abstract class PaymentInterface
+{
+    protected $_lastError = null;
+
+    /**
+     * 根据class,调用指定的实现类.
+     * @param string $flag
+     * @param string $payType
+     */
+    public static function factory($flag)
+    {
+        $className = self::_convertUnderline($flag);
+
+        $className = "App\\Repositories\\Service\\Payment\\{$className}";
+
+        if (class_exists($className)) {
+            return $className::getInstance();
+        } else {
+            return new ErrorPayment();
+        }
+    }
+
+    /**
+     * 获取最后一次错误信息
+     * @return
+     */
+    public function getLastError()
+    {
+        return $this->_lastError;
+    }
+
+    /**
+     * @param array $data
+     * @return string 成功时 返回支付链接、失败时返回空字符串 并给 lastError 赋值
+     */
+    abstract public function createPayLink($data = array());
+
+    /**
+     * 查单是否支付
+     * @param array $data
+     * @param string $orderSn
+     * @param float $money
+     * @param string $partnerId
+     * @return bool
+     */
+    abstract public function queryOrder($data, &$orderSn, &$money, $partnerId);
+
+    /**
+     * @param string $orderSn 我方订单号
+     * @param string $tradeNo 支付方单号
+     * @param float $money 支付金额
+     * @param string $partnerId 商户号
+     * @return bool
+     */
+    abstract public function notify(&$orderSn, &$tradeNo, &$money, $partnerId);
+
+    /**
+     * 根据支付方要求输出指定内容
+     */
+    abstract public function stopNotify();
+
+    /**
+     * 发起http get 请求
+     * @param $url
+     * @return string
+     */
+    protected function httpGet($url, $data,)
+    {
+        try {
+            $result = Http::withHeaders([
+                'timeout' => 20,    // 超时,单位秒.
+                'verify' => false,  // 是否校验SSL证书.
+                'http_errors' => false, // 如果跟随http异常头部是否中断.
+                'debug' => false,    // 是否启用调试模式.
+                'allow_redirects' => true, // 不跟随301/302跳转.
+            ])->get($url, $data);
+
+            if ($result->successful()) {
+                return $result->body();
+            }
+        } catch (\Exception $e) {
+            return $e->getMessage();
+        }
+        return '';
+    }
+
+    /**
+     * 发起 http post 请求
+     * @param $url
+     * @param $data
+     * @param $formType 1.post;2.json;
+     * @return string
+     */
+    protected function httpPost($url, $data, $formType = 1)
+    {
+        switch ($formType) {
+            case 1:
+                $asForm = 'asForm';
+                break;
+            case 2:
+                $asForm = 'asJson';
+                break;
+        }
+
+        try {
+            $result = Http::withHeaders([
+                'timeout' => 20,    // 超时,单位秒.
+                'verify' => false,  // 是否校验SSL证书.
+                'http_errors' => false, // 如果跟随http异常头部是否中断.
+                'debug' => false,    // 是否启用调试模式.
+                'allow_redirects' => true, // 不跟随301/302跳转.
+            ])->$asForm()->post($url, $data);
+
+            if ($result->successful()) {
+                return $result->body();
+            }
+        } catch (\Exception $e) {
+            logger($e->getMessage());
+        }
+        return '';
+    }
+
+    private static function _convertUnderline($str)
+    {
+
+        $string = '';
+
+        $arr = explode('_', $str);
+
+        foreach ($arr as $value) {
+            $string .= ucfirst(strtolower($value));
+        }
+
+        return $string . 'Payment';
+    }
+
+    /**
+     * 签名算法
+     *
+     * @param $data
+     * @return string
+     */
+    public function getSign($data, $splicing = true)
+    {
+        ksort($data);
+        $md5str = "";
+        foreach ($data as $key => $val) {
+            $md5str = $md5str . $key . "=" . $val . "&";
+        }
+
+        if ($splicing === false) {
+            $md5str = substr($md5str, 0, -1);
+        }
+
+        return $md5str;
+    }
+
+    /**
+     * 构建rsa私钥
+     *
+     * @param $privateKey
+     * @return false|OpenSSLAsymmetricKey
+     */
+    public function setupPrvKeySign($privateKey)
+    {
+        $pem = chunk_split($privateKey, 64, "\n");
+        $pem = "-----BEGIN RSA PRIVATE KEY-----\n" . $pem . "-----END RSA PRIVATE KEY-----\n";
+        $key = openssl_pkey_get_private($pem);
+        return $key;
+    }
+
+    /**
+     * 构建rsa共钥
+     *
+     * @param $publicKey
+     * @return false|OpenSSLAsymmetricKey
+     */
+    public function setupPubKey($publicKey)
+    {
+        $pem = chunk_split($publicKey, 64, "\n");
+        $pem = "-----BEGIN PUBLIC KEY-----\n" . $pem . "-----END PUBLIC KEY-----";
+        $key = openssl_pkey_get_public($pem);
+        return $key;
+    }
+}

+ 161 - 0
app/Repositories/Service/Payment/UsdtPayment.php

@@ -0,0 +1,161 @@
+<?php
+
+namespace App\Repositories\Service\Payment;
+
+class UsdtPayment extends PaymentInterface
+{
+    /**
+     * 支付请求地址
+     *
+     * @var string
+     */
+    private $payUrl = 'http://dtpay.vip58.xyz/api/payment/create_transaction';
+
+    /**
+     * 查询请求地址
+     *
+     * @var string
+     */
+    private $queryUrl = 'http://dtpay.vip58.xyz/api/payment/query';
+
+    /**
+     * 公钥
+     *
+     * @var string
+     */
+    private $publicKey = '4dfdea096f5efe0c2fa22bcd88fb59c9';
+
+    const STATUS_CODE = 1;
+
+    /**
+     * @var null
+     */
+    private static $_instance = null;
+
+    /**
+     * @return WhiteSharkPayment|null
+     */
+    public static function getInstance()
+    {
+        if (is_null(self::$_instance)) {
+            self::$_instance = new self();
+        }
+        return self::$_instance;
+    }
+
+
+    public function createPayLink($data = array())
+    {
+        $payInfo = [
+            'merchant_code' => $data['partner_id'],      //商户号
+            'currency' => 'CNY',                         //订单货币
+            'coin_code' => $data['pay_attr_code'],       //支付币种
+            'amount' => $data['money'],                  //支付金额
+            'order_id' => $data['order_sn'],             //订单号
+            'product_name' => "CJ",                      //商品名
+            'date' => date('Y-m-d H:i:s'),        //支付时间
+            'notify_url' => $data['notify_url'],
+            'redirect_url' => $data['pay_url'],
+        ];
+        $payInfo['signature'] = strtoupper(md5($this->getSign($payInfo) . "key=" . $this->publicKey));
+
+        $sRes = $this->httpPost($this->payUrl, $payInfo);
+        if (empty($sRes)) {
+            $this->_lastError = '请求异常';
+            payLogger(__CLASS__,'请求异常data=' . $sRes, $payInfo);
+            return '';
+        }
+
+        $aRes = json_decode($sRes, true);
+        if (empty($aRes)) {
+            $this->_lastError = '请求异常!';
+            payLogger(__CLASS__,'请求异常=' . $sRes, $payInfo);
+            return '';
+        }
+
+        if ((isset($aRes['code']) && $aRes['code'] != 200)  ||   $aRes['data']['status'] != self::STATUS_CODE) {
+            $this->_lastError = '请求异常,请求失败';
+            payLogger(__CLASS__,'请求异常,请求失败 data=' . $sRes, $payInfo);
+            return '';
+        }
+
+        return $aRes['data']['pay_url'];
+    }
+
+    public function queryOrder($data, &$orderSn, &$money, $partnerId)
+    {
+        $queryInfo = [
+            'merchant_code' => $partnerId,
+            'order_id' => $orderSn
+        ];
+        $queryInfo['signature'] = strtoupper(md5($this->getSign($queryInfo) . "key=" . $this->publicKey));
+        $sRes = $this->httpPost($this->queryUrl, $queryInfo);
+        if (empty($sRes)) {
+            $this->_lastError = '查询请求异常';
+            payLogger(__CLASS__,'查询请求异常 $queryInfo='.json_encode($queryInfo),$data);
+        }
+
+        $aRes = json_decode($sRes, true);
+        if (empty($sRes)) {
+            $this->_lastError = '查询解析异常';
+            payLogger(__CLASS__,'查询解析异常 data=' . $sRes, $data);
+            return '';
+        }
+
+        if ($aRes['data']['status'] != self::STATUS_CODE) {
+            $this->_lastError = '查询订单状态异常';
+            payLogger(__CLASS__,'查询订单状态异常 data=' . $sRes, $data);
+            return '';
+        }
+
+        if ($money != $aRes['data']['amount']) {
+            $this->_lastError = '查询订单金额异常';
+            payLogger(__CLASS__,'查询订单金额异常 data='.$sRes,$data);
+            return '';
+        }
+
+        return true;
+    }
+
+    public function notify(&$orderSn, &$tradeNo, &$money, $partnerId)
+    {
+        $data = request()->all()['data'];
+        if ((isset($aRes['code']) && $aRes['code'] != 200)  || (isset($aRes['status']) &&  $aRes['status'] != self::STATUS_CODE)) {
+            payLogger(__CLASS__,'回调订单状态失败',$data);
+            return false;
+        }
+
+        $info = [
+            'merchant_code' => $partnerId,
+            'status' => $data['status'],
+            'order_id' => $data['order_id'],
+            'amount' => $data['amount'],
+            'currency' => $data['currency'],
+            'coin_amount' => $data['coin_amount'],
+            'coin_code' => $data['coin_code'],
+            'merchant_order_sn' => $data['merchant_order_sn'],
+        ];
+
+        $sign = strtoupper(md5($this->getSign($info) . "key=" . $this->publicKey));
+        if ($sign != $data['signature']) {
+            $this->_lastError = '验证失败!';
+            logger(json_encode($data) . ':验证失败!');
+            return false;
+        }
+
+        $money = $data['amount'];
+        $res = $this->queryOrder($data, $data['merchant_order_sn'], $money, $partnerId);
+        if (empty($res)) {
+            return '';
+        }
+
+        $orderSn = $data['merchant_order_sn'];
+        $tradeNo = $data['order_id'];
+        return true;
+    }
+
+    public function stopNotify()
+    {
+        return 'SUCCESS';
+    }
+}

+ 163 - 0
app/Repositories/Service/Payment/WeihuiPayment.php

@@ -0,0 +1,163 @@
+<?php
+
+namespace App\Repositories\Service\Payment;
+
+class WeihuiPayment extends PaymentInterface
+{
+    /**
+     * 支付请求地址
+     *
+     * @var string
+     */
+    private $payUrl = 'http://154.91.35.98:8080/api/pay';
+
+    /**
+     * 查询请求地址
+     *
+     * @var string
+     */
+    private $queryUrl = 'http://154.91.35.98:8080/query/pay';
+
+    /**
+     * 公钥
+     *
+     * @var string
+     */
+    private $publicKey = 'B6B58B53535D4081991D63D531ADF5C9';
+
+    /**
+     * @var null
+     */
+    private static $_instance = null;
+
+    /**
+     * @return WhiteSharkPayment|null
+     */
+    public static function getInstance()
+    {
+        if (is_null(self::$_instance)) {
+            self::$_instance = new self();
+        }
+        return self::$_instance;
+    }
+
+    public function createPayLink($data = array())
+    {
+        $payInfo = [
+            'version' => 'v1.0',  //商户号
+            'type' => $data['pay_attr_code'],
+            'userId' => $data['partner_id'],
+            'requestNo' => $data['order_sn'],
+            'amount' => (string)(sprintf('%.2f', $data['money']) * 100),
+            'callBackURL' => $data['notify_url'],
+            'redirectUrl' => $data['pay_url'],
+        ];
+        $payInfo['sign'] = md5($this->getSign($payInfo) . "key=" . $this->publicKey);
+        $payInfo['attachData'] = 'DK';
+        $sRes = $this->httpPost($this->payUrl, $payInfo, 2);
+        if (empty($sRes)) {
+            $this->_lastError = '请求异常';
+            payLogger(__CLASS__, '请求异常data='.$sRes, $payInfo);
+            return '';
+        }
+
+        $aRes = json_decode($sRes, true);
+        if (empty($aRes)) {
+            $this->_lastError = '请求异常!';
+            payLogger(__CLASS__, '请求异常 (解析) data='.$sRes, $payInfo);
+            return '';
+        }
+
+        if ($aRes['status'] != 1 || $aRes['message'] != '000000') {
+            $this->_lastError = '请求异常,请求失败';
+            payLogger(__CLASS__, '请求异常,请求失败 data='.$sRes, $payInfo);
+            return '';
+        }
+
+        return $aRes['payUrl'];
+    }
+
+    public function queryOrder($data, &$orderSn, &$money, $partnerId)
+    {
+        $queryInfo = [
+            'userId' => $partnerId,
+            'requestNo' => $orderSn,
+            'orderNo' => $data['orderNo'],
+        ];
+        $queryInfo['sign'] = md5($this->getSign($queryInfo) . "key=" . $this->publicKey);
+
+        $sRes = $this->httpPostJson($this->queryUrl, $queryInfo);
+        if (empty($sRes)) {
+            $this->_lastError = '查询请求异常';
+            payLogger(__CLASS__, '查询请求异常 $queryInfo='.json_encode($queryInfo), $data);
+            return '';
+        }
+
+        $aRes = json_decode($sRes, true);
+        if (empty($aRes)) {
+            $this->_lastError = '查询解析异常';
+            payLogger(__CLASS__, '查询解析异常 data='.$sRes, $data);
+            return '';
+        }
+
+        if ($aRes['status'] != 3) {
+            $this->_lastError = '查询订单状态异常';
+            payLogger(__CLASS__, '查询订单状态异常 data='.$sRes, $data);
+            return '';
+        }
+
+        if ($money != $aRes['amount']) {
+            $this->_lastError = '查询订单金额异常';
+            payLogger(__CLASS__, '查询订单金额异常 data='.$sRes, $data);
+            return '';
+        }
+
+        return true;
+    }
+
+    public function notify(&$orderSn, &$tradeNo, &$money, $partnerId)
+    {
+        $data = request()->all();
+        if ($data['status'] != 3) {
+            payLogger(__CLASS__, '回调订单状态失败', $data);
+            return false;
+        }
+
+        $info = [
+            'userId' => $partnerId,
+            'status' => $data['status'],
+            'amount' => $data['amount'],
+            'payAmount' => $data['payAmount'],
+            'requestNo' => $data['requestNo'],
+            'orderNo' => $data['orderNo'],
+            'payTime' => $data['payTime'],
+        ];
+
+        if (isset($data['message'])) {
+            $info['message'] = $data['message'];
+        }
+
+        $sign = md5($this->getSign($info) . "key=" . $this->publicKey);
+
+        if ($sign != $data['sign']) {
+            $this->_lastError = '验证失败!';
+            payLogger(__CLASS__, '验证失败 $sign='.$sign, $data);
+            return false;
+        }
+
+        $money = $data['amount'] / 100;
+        $res = $this->queryOrder($data, $data['requestNo'], $data['amount'], $partnerId);
+        if (empty($res)) {
+            return '';
+        }
+
+        $orderSn = $data['requestNo'];
+        $tradeNo = $data['orderNo'];
+        return true;
+    }
+
+    public function stopNotify()
+    {
+        return '000000';
+    }
+}

+ 146 - 0
app/Repositories/Service/Payment/XindaPayment.php

@@ -0,0 +1,146 @@
+<?php
+
+namespace App\Repositories\Service\Payment;
+
+use App\Repositories\Service\CommonService;
+
+class XindaPayment extends PaymentInterface
+{
+    /**
+     * 支付请求地址
+     *
+     * @var string
+     */
+    private $payUrl = 'http://xd.88xdxd88.com/pay';
+
+    const SUCCESS_STATUS = 1;
+
+    /**
+     * 公钥
+     *
+     * @var string
+     */
+    private $publicKey = 'XckeAtPpBFcQnYxmxRyapeSTGewiZmJY';
+
+    /**
+     * @var null
+     */
+    private static $_instance = null;
+
+    /**
+     * @return WhiteSharkPayment|null
+     */
+    public static function getInstance()
+    {
+        if (is_null(self::$_instance)) {
+            self::$_instance = new self();
+        }
+        return self::$_instance;
+    }
+
+    public function createPayLink($data = array())
+    {
+        $payInfo = [
+            'fxid' => $data['partner_id'],  //商户号
+            'fxddh' => $data['order_sn'],
+            'fxdesc' => 'CJ',
+            'fxattch' => 'CJ',
+            'fxfee' => sprintf('%.2f', $data['money']),
+            'fxnotifyurl' => $data['notify_url'],
+            'fxbackurl' => $data['pay_url'],
+            'fxpay' => $data['pay_attr_code'],
+        ];
+        $payInfo['fxip'] = '127.0.0.1';
+        $payInfo['fxsign'] = md5($payInfo['fxid'] . $payInfo['fxddh'] . $payInfo['fxfee'] . $payInfo['fxnotifyurl'] . $this->publicKey);
+        $sRes = $this->httpPost($this->payUrl, $payInfo);
+        if (empty($sRes)) {
+            $this->_lastError = '请求异常';
+            payLogger(__CLASS__, '请求异常data=' . $sRes, $payInfo);
+            return '';
+        }
+
+        $aRes = json_decode($sRes, true);
+        if (empty($aRes)) {
+            $this->_lastError = '请求异常!';
+            payLogger(__CLASS__, '请求异常 (解析) data=' . $sRes, $payInfo);
+            return '';
+        }
+
+        if ($aRes['status'] != self::SUCCESS_STATUS) {
+            $this->_lastError = '请求异常,请求失败';
+            payLogger(__CLASS__, '请求异常,请求失败 data=' . $sRes, $payInfo);
+            return '';
+        }
+
+        return $aRes['payurl'];
+    }
+
+    public function queryOrder($data, &$orderSn, &$money, $partnerId)
+    {
+        $queryInfo = [
+            'fxid' => $partnerId,
+            'fxddh' => $orderSn,
+            'fxaction' => 'orderquery',
+        ];
+        $queryInfo['fxsign'] = md5($partnerId . $orderSn . 'orderquery' . $this->publicKey);
+
+        $sRes = $this->httpPost($this->payUrl, $queryInfo);
+        if (empty($sRes)) {
+            $this->_lastError = '查询请求异常';
+            payLogger(__CLASS__, '查询请求异常 data=' . $sRes, $queryInfo);
+            return '';
+        }
+
+        $aRes = json_decode($sRes, true);
+        if (empty($aRes)) {
+            $this->_lastError = '查询解析异常';
+            payLogger(__CLASS__, '查询解析异常 data=' . $sRes, $queryInfo);
+            return '';
+        }
+
+        if ($aRes['fxstatus'] != self::SUCCESS_STATUS) {
+            $this->_lastError = '查询订单状态异常';
+            payLogger(__CLASS__, '查询订单状态异常 data=' . $sRes, $queryInfo);
+            return '';
+        }
+
+        if ($money != $aRes['fxfee']) {
+            $this->_lastError = '查询订单金额异常';
+            payLogger(__CLASS__, '查询订单金额异常 data=' . $sRes, $queryInfo);
+            return '';
+        }
+
+        return true;
+    }
+
+    public function notify(&$orderSn, &$tradeNo, &$money, $partnerId)
+    {
+        $data = request()->all();
+        if ($data['fxstatus'] != self::SUCCESS_STATUS) {
+            payLogger(__CLASS__, '回调订单状态失败', $data);
+            return false;
+        }
+
+        $sign = md5($data['fxstatus'] . $partnerId . $data['fxddh'] . $data['fxfee'] . $this->publicKey);
+        if ($sign != $data['fxsign']) {
+            $this->_lastError = '验证失败!';
+            payLogger(__CLASS__, '验证失败 key=' . $this->publicKey, $data);
+            return false;
+        }
+
+        $money = $data['fxfee'];
+        $res = $this->queryOrder($data, $data['fxddh'], $money, $partnerId);
+        if (empty($res)) {
+            return '';
+        }
+
+        $orderSn = $data['fxddh'];
+        $tradeNo = $data['fxorder'];
+        return true;
+    }
+
+    public function stopNotify()
+    {
+        return 'success';
+    }
+}

+ 184 - 0
app/Repositories/Service/Payment/XiyouPayment.php

@@ -0,0 +1,184 @@
+<?php
+
+namespace App\Repositories\Service\Payment;
+
+class XiyouPayment extends PaymentInterface
+{
+    /**
+     * 支付请求地址
+     *
+     * @var string
+     */
+    private $payUrl = 'http://wed.xiyousf.top/Pay_Index.html';
+
+    /**
+     * 查询请求地址
+     *
+     * @var string
+     */
+    private $queryUrl = 'http://wed.xiyousf.top/Pay_Trade_query.html';
+
+    /**
+     * 公钥
+     *
+     * @var string
+     */
+    private $publicKey = 'jl63lzr5ddy9m9f3of9i29by8h6ft4yf';
+
+    const SUCESS_CODE = '00';
+
+    /**
+     * @var null
+     */
+    private static $_instance = null;
+
+    /**
+     * @return WhiteSharkPayment|null
+     */
+    public static function getInstance()
+    {
+        if (is_null(self::$_instance)) {
+            self::$_instance = new self();
+        }
+        return self::$_instance;
+    }
+
+    public function createPayLink($data = array())
+    {
+        $payInfo = [
+            'pay_memberid' => $data['partner_id'],  //商户号
+            'pay_orderid' => $data['order_sn'],
+            'pay_applydate' => date('Y-m-d H:i:s'),
+            'pay_bankcode' => $data['pay_attr_code'],
+            'pay_notifyurl' => $data['notify_url'],
+            'pay_callbackurl' => $data['pay_url'],
+            'pay_amount' => sprintf('%.2f', $data['money']),
+        ];
+
+        $payInfo['pay_md5sign'] = strtoupper(md5($this->getSign($payInfo) . "key=" . $this->publicKey));
+        $sRes = $this->httpPost($this->payUrl, $payInfo);
+        if (empty($sRes)) {
+            $this->_lastError = '请求异常';
+            payLogger(__CLASS__,'请求异常data='.$sRes,$payInfo);
+            return '';
+        }
+
+        $aRes = json_decode($sRes, true);
+        if (empty($aRes)) {
+            $this->_lastError = '请求异常!';
+            payLogger(__CLASS__,'请求异常 (解析) data='.$sRes,$payInfo);
+            return '';
+        }
+
+        if ((isset($aRes['code']) && $aRes['code'] != 200)  || (isset($aRes['status']) &&  $aRes['status']== 'error')) {
+            $this->_lastError = '请求异常,请求失败';
+            payLogger(__CLASS__,'请求异常,请求失败 data='.$sRes,$payInfo);
+            return '';
+        }
+
+        return $aRes['data'];
+    }
+
+    public function queryOrder($data, &$orderSn, &$money, $partnerId)
+    {
+        $queryInfo = [
+            'pay_memberid' => $partnerId,
+            'pay_orderid' => $orderSn
+        ];
+        $queryInfo['pay_md5sign'] = strtoupper(md5($this->getSign($queryInfo) . "key=" . $this->publicKey));
+
+        $sRes = $this->httpPost($this->queryUrl, $queryInfo);
+        if (empty($sRes)) {
+            $this->_lastError = '查询请求异常';
+            payLogger(__CLASS__,'查询请求异常 $queryInfo='.json_encode($queryInfo),$data);
+            return '';
+        }
+
+        $aRes = json_decode($sRes, true);
+        if (empty($aRes)) {
+            $this->_lastError = '查询解析异常';
+            payLogger(__CLASS__,'查询解析异常 data='.$sRes,$data);
+            return '';
+        }
+
+        if ($aRes['returncode'] != self::SUCESS_CODE || $aRes['trade_state'] !== 'SUCCESS') {
+            $this->_lastError = '查询订单状态异常';
+            payLogger(__CLASS__,'查询订单状态异常 data='.$sRes,$data);
+            return '';
+        }
+
+        if ($money != $aRes['amount']) {
+            $this->_lastError = '查询订单金额异常';
+            payLogger(__CLASS__,'查询订单金额异常 data='.$sRes,$data);
+            return '';
+        }
+
+        return true;
+    }
+
+    public function notify(&$orderSn, &$tradeNo, &$money, $partnerId)
+    {
+        $data = request()->all();
+        if ($data['returncode'] != self::SUCESS_CODE) {
+            payLogger(__CLASS__,'回调订单状态失败',$data);
+            return false;
+        }
+
+        $info = [
+            'memberid' => $partnerId,
+            'orderid' => $data['orderid'],
+            'amount' => $data['amount'],
+            'transaction_id' => $data['transaction_id'],
+            'datetime' => $data['datetime'],
+            'returncode' => $data['returncode']
+        ];
+
+        $sign = strtoupper(md5($this->getSign($info) . "key=" . $this->publicKey));
+
+        if ($sign != $data['sign']) {
+            $this->_lastError = '验证失败!';
+            payLogger(__CLASS__,'验证失败 $sign='.$sign,$data);
+            return false;
+        }
+
+        $money = $data['amount'];
+        $res = $this->queryOrder($data, $data['orderid'], $money, $partnerId);
+        if (empty($res)) {
+            return '';
+        }
+
+        $orderSn = $data['orderid'];
+        $tradeNo = $data['transaction_id'];
+        return true;
+    }
+
+    public function stopNotify()
+    {
+        return 'ok';
+    }
+
+    public function queryRradeNo($orderSn, $partnerId)
+    {
+        $queryInfo = [
+            'pay_memberid' => $partnerId,
+            'pay_orderid' => $orderSn
+        ];
+        $queryInfo['pay_md5sign'] = strtoupper(md5($this->getSign($queryInfo) . "key=" . $this->publicKey));
+
+        $sRes = $this->httpPost($this->queryUrl, $queryInfo);
+        if (empty($sRes)) {
+            $this->_lastError = '查询请求异常';
+            payLogger(__CLASS__,'查询请求异常 $queryInfo='.json_encode($queryInfo));
+            return '';
+        }
+
+        $aRes = json_decode($sRes, true);
+        if (empty($aRes)) {
+            $this->_lastError = '查询解析异常';
+            payLogger(__CLASS__,'查询解析异常 data='.$sRes);
+            return '';
+        }
+
+        return $aRes['transaction_id'];
+    }
+}

+ 187 - 0
app/Repositories/Service/Payment/ZuoxiangPayment.php

@@ -0,0 +1,187 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: jokin
+ * Date: 2024/4/20
+ * Time: 09:30
+ */
+
+namespace App\Repositories\Service\Payment;
+
+
+class ZuoxiangPayment extends PaymentInterface
+{
+    /**
+     * 支付请求地址
+     *
+     * @var string
+     */
+    private $payUrl = 'https://wg.kozf.xyz/api/pay/create_order';
+
+    /**
+     * 查询请求地址
+     *
+     * @var string
+     */
+    private $queryUrl = 'https://wg.kozf.xyz/api/pay/query_order';
+
+
+    /**
+     * 公钥
+     *
+     * @var string
+     */
+    private $publicKey = 'GZ3QNGZAMZKWSRUV3HGIVMJVKKZNQO3Q9IIQEBZO7S08QVZ3RMWWKH8JWGKY2HLXLYDLHB4UZVX4I0V4AONCMETJ3VANPWDB6NVNIQZ3EAKVCZHIDVQV7XNJVXMLA531';
+
+    const SUCESS_CODE = 'SUCCESS';
+
+    const STATUS_CODE = 2;
+
+    /**
+     * @var null
+     */
+    private static $_instance = null;
+
+    /**
+     * @return WhiteSharkPayment|null
+     */
+    public static function getInstance()
+    {
+        if (is_null(self::$_instance)) {
+            self::$_instance = new self();
+        }
+        return self::$_instance;
+    }
+
+    public function createPayLink($data = array())
+    {
+
+        $payInfo = [
+            'mchId' => $data['partner_id'],  //商户号
+            'productId' => $data['pay_attr_code'],
+            'mchOrderNo' => $data['order_sn'],
+            'amount' => $data['money'] * 100,
+            "clientIp" => $data['ip'] ?: '127.0.0.0',
+            "device" => 'ios10.3.1',
+            'notifyUrl' => $data['notify_url'],
+            'returnUrl' => $data['pay_url'],
+            'subject' => 'shop',
+            'body' => 'XSJ',
+            'extra' => date('YmdHis'),
+            'version' => '1.0'
+        ];
+
+        $payInfo['sign'] = strtoupper(md5($this->getSign($payInfo) . 'key=' . $this->publicKey));
+        $sRes = $this->httpPost($this->payUrl, $payInfo);
+        if (empty($sRes)) {
+            $this->_lastError = '请求异常';
+            payLogger(__CLASS__, '请求异常data='.$sRes, $payInfo);
+            return '';
+        }
+
+        $aRes = json_decode($sRes, true);
+        if (empty($aRes)) {
+            $this->_lastError = '请求异常!';
+            payLogger(__CLASS__, '请求异常 (解析) data='.$sRes, $payInfo);
+            return '';
+        }
+
+        if ($aRes['retCode'] != self::SUCESS_CODE) {
+            $this->_lastError = '请求异常,请求失败';
+            payLogger(__CLASS__, '请求异常,请求失败 data='.$sRes, $payInfo);
+            return '';
+        }
+
+        return $aRes['payParams']['payUrl'];
+    }
+
+    public function queryOrder($data, &$orderSn, &$money, $partnerId)
+    {
+        $queryInfo = [
+            'mchId' => $partnerId,
+            'payOrderId' => $data['payOrderId'],
+            'mchOrderNo' => $orderSn,
+        ];
+
+        $queryInfo['sign'] = strtoupper(md5($this->getSign($queryInfo) . 'key=' . $this->publicKey));
+        $sRes = $this->httpPost($this->queryUrl, $queryInfo);
+        if (empty($sRes)) {
+            $this->_lastError = '查询请求异常';
+            payLogger(__CLASS__, '查询请求异常 $queryInfo='.json_encode($queryInfo), $data);
+            return '';
+        }
+
+        $aRes = json_decode($sRes, true);
+        if (empty($sRes)) {
+            $this->_lastError = '查询解析异常';
+            payLogger(__CLASS__, '查询解析异常 data='.$sRes, $data);
+            return '';
+        }
+
+        if ($aRes['retCode'] != self::SUCESS_CODE) {
+            $this->_lastError = '查询订单状态异常';
+            payLogger(__CLASS__, '查询订单状态异常'.json_encode($data));
+            return '';
+        }
+
+        if ($money != $aRes['amount']) {
+            $this->_lastError = '查询订单金额异常';
+            payLogger(__CLASS__, '查询订单金额异常'.json_encode($data));
+            return '';
+        }
+
+        return true;
+    }
+
+    public function notify(&$orderSn, &$tradeNo, &$money, $partnerId)
+    {
+        $data = request()->all();
+        if (!($data['status'] == self::STATUS_CODE || $data['status'] == 3)) {
+            payLogger(__CLASS__, '回调订单状态失败!'.json_encode($data));
+            return false;
+        }
+
+        $info = [
+            'payOrderId' => $data['payOrderId'],
+            'mchId' => $partnerId,
+            'productId' => $data['productId'],
+            'mchOrderNo' => $data['mchOrderNo'],
+            'amount' => $data['amount'],
+            'income' => $data['income'],
+            'status' => $data['status'],
+            'income' => $data['income'],
+            'backType' => $data['backType'],
+        ];
+
+        if (!empty($data['paySuccTime'])) {
+            $info['paySuccTime'] = $data['paySuccTime'];
+        }
+
+        if (!empty($data['channelOrderNo'])) {
+            $info['channelOrderNo'] = $data['channelOrderNo'];
+        }
+
+        $sign = strtoupper(md5($this->getSign($info) . 'key=' . $this->publicKey));
+        if ($sign != $data['sign']) {
+            $this->_lastError = '验证失败!';
+            logger(json_encode($data) . ':验证失败!');
+            return false;
+        }
+
+        $authMoney = $data['amount'];
+        $res = $this->queryOrder($data, $data['mchOrderNo'], $authMoney, $partnerId);
+        if (empty($res)) {
+            return '';
+        }
+
+        $money = $data['amount'] / 100;
+        $orderSn = $data['mchOrderNo'];
+        $tradeNo = $data['payOrderId'];
+        return true;
+    }
+
+    public function stopNotify()
+    {
+        return 'success';
+    }
+}

+ 248 - 0
app/Repositories/Utils/AccessControllerListsRepository.php

@@ -0,0 +1,248 @@
+<?php
+/**
+ * ACL权限管理仓库.
+ *
+ * @author nj
+ * @date   2021-01-22 22:37:21
+ */
+
+namespace App\Repositories\Utils;
+
+use DB;
+use Route;
+
+class AccessControllerListsRepository
+{
+    private $config = [];
+
+    private $roleId = 0;
+
+    public function init($config = [], $role_id = 0)
+    {
+        $this->config = config('manage_acl');
+        if ($config) {
+            $this->config = array_merge($this->config, $config);
+        }
+        $this->roleId = $role_id;
+    }
+
+    public function getController(
+        $config = [],
+        $controller_path = '',
+        $controller_suffix = '',
+        $str_namespace = ''
+    ) {
+        if (empty($config)) {
+            $config = $this->config;
+        }
+        $controller_path = empty($controller_path) ? app_path() . '/Controller' : rtrim($controller_path, '/');
+        $controller_suffix = empty($controller_suffix) ? 'Controller' : $controller_suffix;
+        if ($str_namespace) {
+            $str_namespace = "\\". trim($str_namespace, "\\") . "\\";
+        }
+        $return_data = [];
+
+        if (is_dir($controller_path)) {
+            foreach (glob($controller_path . '/*'. $controller_suffix .'.php') as $filename) {
+                $class_name = basename($filename, '.php');
+                $controller = str_replace($controller_suffix, '', $class_name);
+                $class_obj_name = $str_namespace ? $str_namespace . $class_name : $class_name;
+                $class_methods = get_class_methods($class_obj_name);
+                if (is_array($class_methods)) {
+                    foreach ($class_methods as $action) {
+                        //过滤魔术方法
+                        if (substr($action, 0, 2) != '__' && !in_array($action, $config['AUTH_FILTER_METHOD'])) {
+                            $return_data[$controller][$action] = -1;
+                        }
+                    }
+                }
+            }
+        }
+        if ($return_data) {
+            foreach ($return_data as $key => $value) {
+                $data = $condition = [];
+                $data[$config['AUTH_TABLE']['resource']['field']['pid']] = 0;
+                $condition[] = [
+                    $config['AUTH_TABLE']['resource']['field']['pid'] => 0
+                ];
+                $data[$config['AUTH_TABLE']['resource']['field']['operate']] = $key;
+                $condition[] = [
+                    $config['AUTH_TABLE']['resource']['field']['operate'] => $key
+                ];
+                $db_res = DB::table($config['AUTH_TABLE']['resource']['name']);
+                if ($condition) {
+                    foreach ($condition as $k1 => $v1) {
+                        foreach ($v1 as $k2 => $v2) {
+                            $db_res->where($k2, '=', $v2);
+                        }
+                    }
+                }
+                $info = $db_res->first();
+                if (empty($info)) {
+                    $pid = DB::table($config['AUTH_TABLE']['resource']['name'])->insertGetId($data);
+                } else {
+                    $pid = $info->{$config['AUTH_TABLE']['resource']['field']['id']};
+                }
+
+                if (is_array($value)) {
+                    foreach ($value as $k2 => $v2) {
+                        $data = $condition = [];
+                        $data[$config['AUTH_TABLE']['resource']['field']['pid']] = $pid;
+                        $condition[] = [
+                            $config['AUTH_TABLE']['resource']['field']['pid'] => $pid
+                        ];
+                        $data[$config['AUTH_TABLE']['resource']['field']['operate']] = $k2;
+                        $condition[] = [
+                            $config['AUTH_TABLE']['resource']['field']['operate'] => $k2
+                        ];
+                        $db_res = DB::table($config['AUTH_TABLE']['resource']['name']);
+                        if ($condition) {
+                            foreach ($condition as $k1 => $v1) {
+                                foreach ($v1 as $k2 => $v2) {
+                                    $db_res->where($k2, '=', $v2);
+                                }
+                            }
+                        }
+                        $info = $db_res->first();
+                        if (empty($info)) {
+                            DB::table($config['AUTH_TABLE']['resource']['name'])->insertGetId($data);
+                        }
+                    }
+                }
+            }
+        }
+        return $return_data;
+    }
+
+    public function checkLogin()
+    {
+        return true;
+    }
+
+    public function getRolePower($role_id = 0)
+    {
+        if (empty($role_id) || empty($this->config['AUTH_TABLE']['role']['field']['power'])) {
+            return false;
+        }
+
+        $role_info = DB::table($this->config['AUTH_TABLE']['role']['name'])
+            ->where($this->config['AUTH_TABLE']['role']['field']['id'], '=', $role_id)->first();
+
+        if (empty($role_info)) {
+            return false;
+        }
+
+        if ($role_info->{$this->config['AUTH_TABLE']['role']['field']['power']} == -1) {
+            $power = -1;
+        } else {
+            $resource = DB::table($this->config['AUTH_TABLE']['resource']['name'])->get();
+            if (empty($resource)) {
+                return false;
+            }
+            $power_value = explode(',', $role_info->{$this->config['AUTH_TABLE']['role']['field']['power']});
+            $power = $resource2 = [];
+            foreach ($resource as $k => $v) {
+                $resource2[$v->{$this->config['AUTH_TABLE']['resource']['field']['id']}] = $v;
+            }
+            foreach ($resource2 as $k => $v) {
+                if ($v->{$this->config['AUTH_TABLE']['resource']['field']['pid']} != 0
+                    && in_array($v->{$this->config['AUTH_TABLE']['resource']['field']['id']}, $power_value)) {
+                    $controller = $resource2[$v->{$this->config['AUTH_TABLE']['resource']['field']['pid']}]
+                        ->{$this->config['AUTH_TABLE']['resource']['field']['operate']};//模块
+                    $action = $v->{$this->config['AUTH_TABLE']['resource']['field']['operate']};//操作方法
+                    $power[$controller][$action] = -1;
+                }
+            }
+        }
+
+        return $power;
+    }
+
+    private function noPower()
+    {
+        return -999;
+    }
+
+    public function check($controller_suffix = 'Controller')
+    {
+        $routeAction = self::getCurrentRouteHandler();
+        preg_match_all('/([a-z0-9A-Z]+\\\)?(\w+)@(\w+)/', $routeAction, $matchs);
+        $controller_full = $matchs[2][0];  //类名
+        $controller = str_replace($controller_suffix, '', $controller_full);
+        $action = $matchs[3][0];    //方法名
+        //不需要认证的模块,则放行
+        if (isset($this->config['AUTH_LOGIN_NO'][$controller]) && (($this->config['AUTH_LOGIN_NO'][$controller] == '*') || in_array($action, $this->config['AUTH_LOGIN_NO'][$controller]))) {
+            return true;
+        }
+        $power = $this->getRolePower($this->roleId);
+
+        // 临时的,记得删除-1 Todo.
+        // $power = -1;
+
+        if ($power == -1) {
+            return true;
+        } else {
+            $privilege = trans('permission') ? trans('permission') : [];
+            $controller = str_replace('Controller', '', $controller);
+            if ($privilege) {
+                if (isset($privilege[$controller]['power_rule']['module_hidden'])) {
+                    if ($privilege[$controller]['power_rule']['module_hidden'] == 0) {
+                        if (isset($privilege[$controller][$action])) {
+                            if (isset($privilege[$controller]['power_rule'][$action]) &&
+                                $privilege[$controller]['power_rule'][$action] == 1) {
+                                return true;
+                            } else {
+                                if (isset($power[$controller][$action]) && $power[$controller][$action] == -1) {
+                                    return true;
+                                }
+                            }
+                        } else {    //没有设置$privilege[$controller][$action]一律通过
+                            return true;
+                        }
+                    } elseif ($privilege[$controller]['power_rule']['module_hidden'] == 1) {
+                        return true;
+                    }
+                }
+            }
+        }
+        return $this->noPower();
+    }
+
+    /**
+     * 检查模块和操作权限
+     * @param string $controller   控制器
+     * @param null   $action       动作
+     * @return bool
+     */
+    public function checkPower($controller, $action = null)
+    {
+        if (empty($controller)) {
+            return false;
+        }
+
+        $power = $this->getRolePower($this->roleId);
+        if (empty($power)) {
+            return false;
+        }
+        if ($power == -1) {
+            return true;
+        }
+
+        if (empty($action) && isset($power[$controller]) && !empty($power[$controller])) {
+            return true;
+        } elseif (isset($power[$controller][$action])) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * 获取当前路由话柄.
+     * @return string
+     */
+    private function getCurrentRouteHandler()
+    {
+        return Route::currentRouteAction();
+    }
+}

+ 249 - 0
app/Repositories/Utils/JwtRepository.php

@@ -0,0 +1,249 @@
+<?php
+/**
+ * JWT仓库.
+ *
+ * @author nj
+ * @date   2021-01-21 22:58:08
+ */
+
+namespace App\Repositories\Utils;
+
+use App\Repositories\JwtConstraints\NoExpiredValidConstraint;
+use Carbon\CarbonImmutable;
+use Lcobucci\Clock\SystemClock;
+use Lcobucci\JWT\Signer\Hmac\Sha256;
+use Lcobucci\JWT\Validation\Constraint\PermittedFor;
+use Lcobucci\JWT\Signer\Key\InMemory;
+use Lcobucci\JWT\Configuration;
+use Lcobucci\JWT\Validation\Constraint\IssuedBy;
+use Lcobucci\JWT\Validation\Constraint\SignedWith;
+use DateTimeZone;
+use Lcobucci\JWT\Encoding\CannotDecodeContent;
+use Lcobucci\JWT\Token\InvalidTokenStructure;
+use Lcobucci\JWT\Token\UnsupportedHeaderFound;
+
+class JwtRepository
+{
+    /**
+     * 签发人.
+     *
+     * @var string
+     */
+    private $issue = 'https://gfw.google.com';
+
+    /**
+     * 创建一个App Token.
+     * @param array $info 用户信息,包含user_id, device_no, device_type, version
+     * @return array
+     */
+    public function createAppJwt($info)
+    {
+        $nowObj = CarbonImmutable::now();
+        $expiredObj = $nowObj->copy()->addMinutes(intval(config('jwt.ttl')));
+        $expiredAt = $expiredObj->toDateTimeString();
+        // HMAC SHA256 默认简写 HS256
+        $signer = new Sha256();
+        $key = InMemory::plainText(config('jwt.secret'));
+        $aud = 'app-users';
+        $jti = md5(implode(',', [$aud, $info['user_id'], $nowObj->timestamp, random(6)]));
+        $jwtConfig = Configuration::forSymmetricSigner($signer, $key);
+        $tokenObj = $jwtConfig->builder()
+            // payload-iss 签发人
+            ->issuedBy($this->issue)
+            ->withHeader('iss', $this->issue)
+            // payload-aud 受众
+            ->permittedFor($aud)
+            // payload->exp 过期时间,DateTimeImmutable对象.
+            ->expiresAt($expiredObj)
+            // 允许在某一个时间开始就使用
+            ->canOnlyBeUsedAfter($nowObj->modify('-30 second'))
+            // payload->jti 编号
+            ->identifiedBy($jti)
+            // payload->iat 签发时间,DateTimeImmutable对象.
+            ->issuedAt($nowObj)
+            // payload 私有信息.
+            ->withClaim('user_id', $info['user_id'])
+            ->withClaim('version', $info['version'])
+            ->withClaim('device_no', $info['device_no'])
+            ->withClaim('device_type', $info['device_type'])
+            ->withClaim('ip', $this->getClientIp())
+            ->getToken($jwtConfig->signer(), $jwtConfig->signingKey());
+
+        $token = $tokenObj->toString();
+
+        return [
+            'token' => $token,
+            'expired_at' => $expiredAt,
+        ];
+    }
+
+    /**
+     * 创建一个后台 Token.
+     * @param object $manager  用户信息,包含manager_uid,role_id
+     * @return array
+     */
+    public function createManageJwt($manager)
+    {
+        $nowObj = CarbonImmutable::now();
+        $expiredObj = $nowObj->copy()->addMinutes(intval(config('jwt.mg_ttl')));
+        $expiredAt = $expiredObj->toDateTimeString();
+        // HMAC SHA256 默认简写 HS256
+        $signer = new Sha256();
+        $key = InMemory::plainText(config('jwt.secret'));
+        $aud = 'manage-users';
+        $jti = md5(implode(',', [$aud, $manager->manager_uid, $nowObj->timestamp, random(6)]));
+        $jwtConfig = Configuration::forSymmetricSigner($signer, $key);
+        $tokenObj = $jwtConfig->builder()
+            // payload-iss 签发人
+            ->issuedBy($this->issue)
+            ->withHeader('iss', $this->issue)
+            // payload-aud 受众
+            ->permittedFor($aud)
+            // payload->exp 过期时间,DateTimeImmutable对象
+            ->expiresAt($expiredObj)
+            // 允许在某一个时间开始就使用
+            ->canOnlyBeUsedAfter($nowObj->modify('-30 second'))
+            // payload->jti 编号
+            ->identifiedBy($jti)
+            // payload->iat 签发时间,DateTimeImmutable对象.
+            ->issuedAt($nowObj)
+            // payload 私有信息.
+            ->withClaim('manager_uid', $manager->manager_uid)
+            ->withClaim('role_id', $manager->role_id)
+            ->withClaim('ip', $this->getClientIp())
+            ->getToken($jwtConfig->signer(), $jwtConfig->signingKey());
+
+        $token = $tokenObj->toString();
+
+        return [
+            'token' => $token,
+            'expired_at' => $expiredAt,
+        ];
+    }
+
+    /**
+     * 创建一个代理 Token.
+     * @param object $agent  用户信息,包含agent_uid
+     * @return array
+     */
+    public function createAgentJwt($agent)
+    {
+        $nowObj = CarbonImmutable::now();
+        $expiredObj = $nowObj->copy()->addMinutes(intval(config('jwt.ag_ttl')));
+        $expiredAt = $expiredObj->toDateTimeString();
+        // HMAC SHA256 默认简写 HS256
+        $signer = new Sha256();
+        $key = InMemory::plainText(config('jwt.secret'));
+        $aud = 'agent-users';
+        $jti = md5(implode(',', [$aud, $agent->uid, $nowObj->timestamp, random(6)]));
+        $jwtConfig = Configuration::forSymmetricSigner($signer, $key);
+        $tokenObj = $jwtConfig->builder()
+            // payload-iss 签发人
+            ->issuedBy($this->issue)
+            ->withHeader('iss', $this->issue)
+            // payload-aud 受众
+            ->permittedFor($aud)
+            // payload->exp 过期时间,DateTimeImmutable对象
+            ->expiresAt($expiredObj)
+            // 允许在某一个时间开始就使用
+            ->canOnlyBeUsedAfter($nowObj->modify('-30 second'))
+            // payload->jti 编号
+            ->identifiedBy($jti)
+            // payload->iat 签发时间,DateTimeImmutable对象.
+            ->issuedAt($nowObj)
+            // payload 私有信息.
+            ->withClaim('agent_uid', $agent->uid)
+            ->withClaim('ip', $this->getClientIp())
+            ->getToken($jwtConfig->signer(), $jwtConfig->signingKey());
+
+        $token = $tokenObj->toString();
+
+        return [
+            'token' => $token,
+            'expired_at' => $expiredAt,
+        ];
+    }
+
+    /**
+     * 校验Token (仅适用用于HS256算法).
+     * @param  string  $token
+     * @param  string  $aud  受众人.
+     * @return array
+     */
+    public function validateToken(string $token, $aud = 'app-users')
+    {
+        // 使用的时候,只需要看is_valid,如果无效的情况下需要告知是否过期,再看is_expired
+        $result = [
+            // 是否非法格式(格式错误,无法解析json).
+            'is_illegal' => false,
+            // 是否有效.
+            'is_valid' => false,
+            // 是否过期.
+            'is_expired' => false,
+            // 解析Token对象.
+            'token_obj' => null,
+        ];
+        $signer = new Sha256();
+        $key = InMemory::plainText(config('jwt.secret'));
+        $nowObj = CarbonImmutable::now();
+        $jwtConfig = Configuration::forSymmetricSigner($signer, $key);
+        $clock = new SystemClock(new DateTimeZone(config('app.timezone')));
+
+//        $jwtConfig->setValidationConstraints(
+//            new IssuedBy($this->issue),
+//            new SignedWith($signer, $key),
+//            new PermittedFor($aud)
+//            new StrictValidAt($clock)
+//        );
+
+        try {
+            // 传入的token可能格式无效.
+            $tokenObj = $jwtConfig->parser()->parse($token);
+        } catch (CannotDecodeContent $e) {
+            $result['is_illegal'] = true;
+        } catch (InvalidTokenStructure $e) {
+            $result['is_illegal'] = true;
+        } catch (UnsupportedHeaderFound $e) {
+            $result['is_illegal'] = true;
+        } catch (\Throwable $exception) {
+            $result['is_illegal'] = true;
+        } catch (\Exception $exception) {
+            $result['is_illegal'] = true;
+        }
+        if ($result['is_illegal']) {
+            return $result;
+        }
+        if ($jwtConfig->validator()->validate($tokenObj, new IssuedBy($this->issue), new SignedWith($signer, $key), new PermittedFor($aud))) {
+            $result['token_obj'] = $tokenObj;
+        } else {
+            // ConstraintViolation 所有错误.
+            $result['is_illegal'] = true;
+        }
+
+        if (!$result['is_illegal']) {
+            $result['is_valid'] = $jwtConfig->validator()->validate($tokenObj, new NoExpiredValidConstraint($clock));
+            if ($result['is_valid']) {
+                // 在有效的前提下,再检查详情是否过期.
+                $result['is_expired'] = $tokenObj->isExpired($nowObj->toDateTime());
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * 获取客户端ip.
+     * @return mixed|string
+     */
+    public function getClientIp()
+    {
+        $ip = request()->getClientIp();
+        if (strpos($ip, ',')) {
+            $temp = explode(',', $ip);
+            $ip = $ip[0];
+            $temp = null;
+            unset($temp);
+        }
+        return $ip;
+    }
+}

+ 53 - 0
artisan

@@ -0,0 +1,53 @@
+#!/usr/bin/env php
+<?php
+
+define('LARAVEL_START', microtime(true));
+
+/*
+|--------------------------------------------------------------------------
+| Register The Auto Loader
+|--------------------------------------------------------------------------
+|
+| Composer provides a convenient, automatically generated class loader
+| for our application. We just need to utilize it! We'll require it
+| into the script here so that we do not have to worry about the
+| loading of any of our classes manually. It's great to relax.
+|
+*/
+
+require __DIR__.'/vendor/autoload.php';
+
+$app = require_once __DIR__.'/bootstrap/app.php';
+
+/*
+|--------------------------------------------------------------------------
+| Run The Artisan Application
+|--------------------------------------------------------------------------
+|
+| When we run the console application, the current CLI command will be
+| executed in this console and the response sent back to a terminal
+| or another output device for the developers. Here goes nothing!
+|
+*/
+
+$kernel = $app->make(Illuminate\Contracts\Console\Kernel::class);
+
+$status = $kernel->handle(
+    $input = new Symfony\Component\Console\Input\ArgvInput,
+    new Symfony\Component\Console\Output\ConsoleOutput
+);
+
+/*
+|--------------------------------------------------------------------------
+| Shutdown The Application
+|--------------------------------------------------------------------------
+|
+| Once Artisan has finished running, we will fire off the shutdown events
+| so that any final work may be done by the application before we shut
+| down the process. This is the last thing to happen to the request.
+|
+*/
+
+$kernel->terminate($input, $status);
+
+exit($status);

+ 55 - 0
bootstrap/app.php

@@ -0,0 +1,55 @@
+<?php
+
+/*
+|--------------------------------------------------------------------------
+| Create The Application
+|--------------------------------------------------------------------------
+|
+| The first thing we will do is create a new Laravel application instance
+| which serves as the "glue" for all the components of Laravel, and is
+| the IoC container for the system binding all of the various parts.
+|
+*/
+
+$app = new Illuminate\Foundation\Application(
+    $_ENV['APP_BASE_PATH'] ?? dirname(__DIR__)
+);
+
+/*
+|--------------------------------------------------------------------------
+| Bind Important Interfaces
+|--------------------------------------------------------------------------
+|
+| Next, we need to bind some important interfaces into the container so
+| we will be able to resolve them when needed. The kernels serve the
+| incoming requests to this application from both the web and CLI.
+|
+*/
+
+$app->singleton(
+    Illuminate\Contracts\Http\Kernel::class,
+    App\Http\Kernel::class
+);
+
+$app->singleton(
+    Illuminate\Contracts\Console\Kernel::class,
+    App\Console\Kernel::class
+);
+
+$app->singleton(
+    Illuminate\Contracts\Debug\ExceptionHandler::class,
+    App\Exceptions\Handler::class
+);
+
+/*
+|--------------------------------------------------------------------------
+| Return The Application
+|--------------------------------------------------------------------------
+|
+| This script returns the application instance. The instance is given to
+| the calling script so we can separate the building of the instances
+| from the actual running of the application and sending responses.
+|
+*/
+
+return $app;

+ 2 - 0
bootstrap/cache/.gitignore

@@ -0,0 +1,2 @@
+*
+!.gitignore

+ 69 - 0
composer.json

@@ -0,0 +1,69 @@
+{
+    "name": "laravel/laravel",
+    "type": "project",
+    "description": "The Laravel Framework.",
+    "keywords": ["framework", "laravel"],
+    "license": "MIT",
+    "require": {
+        "php": "^8.0.2",
+        "guzzlehttp/guzzle": "^7.2",
+        "laravel-lang/lang": "~10.1.11",
+        "laravel/framework": "^9.11",
+        "laravel/sanctum": "^2.14.1",
+        "laravel/tinker": "^2.7",
+        "lcobucci/jwt": "4.1.5"
+    },
+    "require-dev": {
+        "fakerphp/faker": "^1.9.1",
+        "laravel/sail": "^1.0.1",
+        "mockery/mockery": "^1.4.4",
+        "nunomaduro/collision": "^6.1",
+        "phpunit/phpunit": "^9.5.10",
+        "spatie/laravel-ignition": "^1.0",
+        "hashids/hashids": "^4.1"
+    },
+    "autoload": {
+        "psr-4": {
+            "App\\": "app/",
+            "Database\\Factories\\": "database/factories/",
+            "Database\\Seeders\\": "database/seeders/",
+            "Modules\\": "Modules/",
+            "KZ2\\Laravel\\Repositories\\": "private_vendor/kz2/laravel-repositories/src/"
+        },
+        "files": [
+            "app/Helpers/autoload.func.php"
+        ]
+    },
+    "autoload-dev": {
+        "psr-4": {
+            "Tests\\": "tests/"
+        }
+    },
+    "scripts": {
+        "post-autoload-dump": [
+            "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
+            "@php artisan package:discover --ansi"
+        ],
+        "post-update-cmd": [
+            "@php artisan vendor:publish --tag=laravel-assets --ansi --force"
+        ],
+        "post-root-package-install": [
+            "@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
+        ],
+        "post-create-project-cmd": [
+            "@php artisan key:generate --ansi"
+        ]
+    },
+    "extra": {
+        "laravel": {
+            "dont-discover": []
+        }
+    },
+    "config": {
+        "optimize-autoloader": true,
+        "preferred-install": "dist",
+        "sort-packages": true
+    },
+    "minimum-stability": "dev",
+    "prefer-stable": true
+}

+ 8070 - 0
composer.lock

@@ -0,0 +1,8070 @@
+{
+    "_readme": [
+        "This file locks the dependencies of your project to a known state",
+        "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
+        "This file is @generated automatically"
+    ],
+    "content-hash": "b7bd1bf4ce034fec0536d0fadba474b7",
+    "packages": [
+        {
+            "name": "brick/math",
+            "version": "0.9.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/brick/math.git",
+                "reference": "ca57d18f028f84f777b2168cd1911b0dee2343ae"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/brick/math/zipball/ca57d18f028f84f777b2168cd1911b0dee2343ae",
+                "reference": "ca57d18f028f84f777b2168cd1911b0dee2343ae",
+                "shasum": ""
+            },
+            "require": {
+                "ext-json": "*",
+                "php": "^7.1 || ^8.0"
+            },
+            "require-dev": {
+                "php-coveralls/php-coveralls": "^2.2",
+                "phpunit/phpunit": "^7.5.15 || ^8.5 || ^9.0",
+                "vimeo/psalm": "4.9.2"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Brick\\Math\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "Arbitrary-precision arithmetic library",
+            "keywords": [
+                "Arbitrary-precision",
+                "BigInteger",
+                "BigRational",
+                "arithmetic",
+                "bigdecimal",
+                "bignum",
+                "brick",
+                "math"
+            ],
+            "support": {
+                "issues": "https://github.com/brick/math/issues",
+                "source": "https://github.com/brick/math/tree/0.9.3"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/BenMorel",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/brick/math",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-08-15T20:50:18+00:00"
+        },
+        {
+            "name": "dflydev/dot-access-data",
+            "version": "v3.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/dflydev/dflydev-dot-access-data.git",
+                "reference": "0992cc19268b259a39e86f296da5f0677841f42c"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/dflydev/dflydev-dot-access-data/zipball/0992cc19268b259a39e86f296da5f0677841f42c",
+                "reference": "0992cc19268b259a39e86f296da5f0677841f42c",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.1 || ^8.0"
+            },
+            "require-dev": {
+                "phpstan/phpstan": "^0.12.42",
+                "phpunit/phpunit": "^7.5 || ^8.5 || ^9.3",
+                "scrutinizer/ocular": "1.6.0",
+                "squizlabs/php_codesniffer": "^3.5",
+                "vimeo/psalm": "^3.14"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "3.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Dflydev\\DotAccessData\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Dragonfly Development Inc.",
+                    "email": "info@dflydev.com",
+                    "homepage": "http://dflydev.com"
+                },
+                {
+                    "name": "Beau Simensen",
+                    "email": "beau@dflydev.com",
+                    "homepage": "http://beausimensen.com"
+                },
+                {
+                    "name": "Carlos Frutos",
+                    "email": "carlos@kiwing.it",
+                    "homepage": "https://github.com/cfrutos"
+                },
+                {
+                    "name": "Colin O'Dell",
+                    "email": "colinodell@gmail.com",
+                    "homepage": "https://www.colinodell.com"
+                }
+            ],
+            "description": "Given a deep data structure, access data by dot notation.",
+            "homepage": "https://github.com/dflydev/dflydev-dot-access-data",
+            "keywords": [
+                "access",
+                "data",
+                "dot",
+                "notation"
+            ],
+            "support": {
+                "issues": "https://github.com/dflydev/dflydev-dot-access-data/issues",
+                "source": "https://github.com/dflydev/dflydev-dot-access-data/tree/v3.0.1"
+            },
+            "time": "2021-08-13T13:06:58+00:00"
+        },
+        {
+            "name": "doctrine/inflector",
+            "version": "2.0.4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/doctrine/inflector.git",
+                "reference": "8b7ff3e4b7de6b2c84da85637b59fd2880ecaa89"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/doctrine/inflector/zipball/8b7ff3e4b7de6b2c84da85637b59fd2880ecaa89",
+                "reference": "8b7ff3e4b7de6b2c84da85637b59fd2880ecaa89",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.2 || ^8.0"
+            },
+            "require-dev": {
+                "doctrine/coding-standard": "^8.2",
+                "phpstan/phpstan": "^0.12",
+                "phpstan/phpstan-phpunit": "^0.12",
+                "phpstan/phpstan-strict-rules": "^0.12",
+                "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0",
+                "vimeo/psalm": "^4.10"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Doctrine\\Inflector\\": "lib/Doctrine/Inflector"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Guilherme Blanco",
+                    "email": "guilhermeblanco@gmail.com"
+                },
+                {
+                    "name": "Roman Borschel",
+                    "email": "roman@code-factory.org"
+                },
+                {
+                    "name": "Benjamin Eberlei",
+                    "email": "kontakt@beberlei.de"
+                },
+                {
+                    "name": "Jonathan Wage",
+                    "email": "jonwage@gmail.com"
+                },
+                {
+                    "name": "Johannes Schmitt",
+                    "email": "schmittjoh@gmail.com"
+                }
+            ],
+            "description": "PHP Doctrine Inflector is a small library that can perform string manipulations with regard to upper/lowercase and singular/plural forms of words.",
+            "homepage": "https://www.doctrine-project.org/projects/inflector.html",
+            "keywords": [
+                "inflection",
+                "inflector",
+                "lowercase",
+                "manipulation",
+                "php",
+                "plural",
+                "singular",
+                "strings",
+                "uppercase",
+                "words"
+            ],
+            "support": {
+                "issues": "https://github.com/doctrine/inflector/issues",
+                "source": "https://github.com/doctrine/inflector/tree/2.0.4"
+            },
+            "funding": [
+                {
+                    "url": "https://www.doctrine-project.org/sponsorship.html",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://www.patreon.com/phpdoctrine",
+                    "type": "patreon"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finflector",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-10-22T20:16:43+00:00"
+        },
+        {
+            "name": "doctrine/lexer",
+            "version": "1.2.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/doctrine/lexer.git",
+                "reference": "c268e882d4dbdd85e36e4ad69e02dc284f89d229"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/doctrine/lexer/zipball/c268e882d4dbdd85e36e4ad69e02dc284f89d229",
+                "reference": "c268e882d4dbdd85e36e4ad69e02dc284f89d229",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.1 || ^8.0"
+            },
+            "require-dev": {
+                "doctrine/coding-standard": "^9.0",
+                "phpstan/phpstan": "^1.3",
+                "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
+                "vimeo/psalm": "^4.11"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Doctrine\\Common\\Lexer\\": "lib/Doctrine/Common/Lexer"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Guilherme Blanco",
+                    "email": "guilhermeblanco@gmail.com"
+                },
+                {
+                    "name": "Roman Borschel",
+                    "email": "roman@code-factory.org"
+                },
+                {
+                    "name": "Johannes Schmitt",
+                    "email": "schmittjoh@gmail.com"
+                }
+            ],
+            "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.",
+            "homepage": "https://www.doctrine-project.org/projects/lexer.html",
+            "keywords": [
+                "annotations",
+                "docblock",
+                "lexer",
+                "parser",
+                "php"
+            ],
+            "support": {
+                "issues": "https://github.com/doctrine/lexer/issues",
+                "source": "https://github.com/doctrine/lexer/tree/1.2.3"
+            },
+            "funding": [
+                {
+                    "url": "https://www.doctrine-project.org/sponsorship.html",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://www.patreon.com/phpdoctrine",
+                    "type": "patreon"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2022-02-28T11:07:21+00:00"
+        },
+        {
+            "name": "dragonmantank/cron-expression",
+            "version": "v3.3.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/dragonmantank/cron-expression.git",
+                "reference": "be85b3f05b46c39bbc0d95f6c071ddff669510fa"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/be85b3f05b46c39bbc0d95f6c071ddff669510fa",
+                "reference": "be85b3f05b46c39bbc0d95f6c071ddff669510fa",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.2|^8.0",
+                "webmozart/assert": "^1.0"
+            },
+            "replace": {
+                "mtdowling/cron-expression": "^1.0"
+            },
+            "require-dev": {
+                "phpstan/extension-installer": "^1.0",
+                "phpstan/phpstan": "^1.0",
+                "phpstan/phpstan-webmozart-assert": "^1.0",
+                "phpunit/phpunit": "^7.0|^8.0|^9.0"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Cron\\": "src/Cron/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Chris Tankersley",
+                    "email": "chris@ctankersley.com",
+                    "homepage": "https://github.com/dragonmantank"
+                }
+            ],
+            "description": "CRON for PHP: Calculate the next or previous run date and determine if a CRON expression is due",
+            "keywords": [
+                "cron",
+                "schedule"
+            ],
+            "support": {
+                "issues": "https://github.com/dragonmantank/cron-expression/issues",
+                "source": "https://github.com/dragonmantank/cron-expression/tree/v3.3.1"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/dragonmantank",
+                    "type": "github"
+                }
+            ],
+            "time": "2022-01-18T15:43:28+00:00"
+        },
+        {
+            "name": "egulias/email-validator",
+            "version": "3.1.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/egulias/EmailValidator.git",
+                "reference": "ee0db30118f661fb166bcffbf5d82032df484697"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/ee0db30118f661fb166bcffbf5d82032df484697",
+                "reference": "ee0db30118f661fb166bcffbf5d82032df484697",
+                "shasum": ""
+            },
+            "require": {
+                "doctrine/lexer": "^1.2",
+                "php": ">=7.2",
+                "symfony/polyfill-intl-idn": "^1.15"
+            },
+            "require-dev": {
+                "php-coveralls/php-coveralls": "^2.2",
+                "phpunit/phpunit": "^8.5.8|^9.3.3",
+                "vimeo/psalm": "^4"
+            },
+            "suggest": {
+                "ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Egulias\\EmailValidator\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Eduardo Gulias Davis"
+                }
+            ],
+            "description": "A library for validating emails against several RFCs",
+            "homepage": "https://github.com/egulias/EmailValidator",
+            "keywords": [
+                "email",
+                "emailvalidation",
+                "emailvalidator",
+                "validation",
+                "validator"
+            ],
+            "support": {
+                "issues": "https://github.com/egulias/EmailValidator/issues",
+                "source": "https://github.com/egulias/EmailValidator/tree/3.1.2"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/egulias",
+                    "type": "github"
+                }
+            ],
+            "time": "2021-10-11T09:18:27+00:00"
+        },
+        {
+            "name": "fruitcake/php-cors",
+            "version": "v1.2.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/fruitcake/php-cors.git",
+                "reference": "58571acbaa5f9f462c9c77e911700ac66f446d4e"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/fruitcake/php-cors/zipball/58571acbaa5f9f462c9c77e911700ac66f446d4e",
+                "reference": "58571acbaa5f9f462c9c77e911700ac66f446d4e",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.4|^8.0",
+                "symfony/http-foundation": "^4.4|^5.4|^6"
+            },
+            "require-dev": {
+                "phpstan/phpstan": "^1.4",
+                "phpunit/phpunit": "^9",
+                "squizlabs/php_codesniffer": "^3.5"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "1.1-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Fruitcake\\Cors\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fruitcake",
+                    "homepage": "https://fruitcake.nl"
+                },
+                {
+                    "name": "Barryvdh",
+                    "email": "barryvdh@gmail.com"
+                }
+            ],
+            "description": "Cross-origin resource sharing library for the Symfony HttpFoundation",
+            "homepage": "https://github.com/fruitcake/php-cors",
+            "keywords": [
+                "cors",
+                "laravel",
+                "symfony"
+            ],
+            "support": {
+                "issues": "https://github.com/fruitcake/php-cors/issues",
+                "source": "https://github.com/fruitcake/php-cors/tree/v1.2.0"
+            },
+            "funding": [
+                {
+                    "url": "https://fruitcake.nl",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/barryvdh",
+                    "type": "github"
+                }
+            ],
+            "time": "2022-02-20T15:07:15+00:00"
+        },
+        {
+            "name": "graham-campbell/result-type",
+            "version": "v1.0.4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/GrahamCampbell/Result-Type.git",
+                "reference": "0690bde05318336c7221785f2a932467f98b64ca"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/0690bde05318336c7221785f2a932467f98b64ca",
+                "reference": "0690bde05318336c7221785f2a932467f98b64ca",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.0 || ^8.0",
+                "phpoption/phpoption": "^1.8"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^6.5.14 || ^7.5.20 || ^8.5.19 || ^9.5.8"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "GrahamCampbell\\ResultType\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Graham Campbell",
+                    "email": "hello@gjcampbell.co.uk",
+                    "homepage": "https://github.com/GrahamCampbell"
+                }
+            ],
+            "description": "An Implementation Of The Result Type",
+            "keywords": [
+                "Graham Campbell",
+                "GrahamCampbell",
+                "Result Type",
+                "Result-Type",
+                "result"
+            ],
+            "support": {
+                "issues": "https://github.com/GrahamCampbell/Result-Type/issues",
+                "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.0.4"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/GrahamCampbell",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/graham-campbell/result-type",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-11-21T21:41:47+00:00"
+        },
+        {
+            "name": "guzzlehttp/guzzle",
+            "version": "7.4.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/guzzle/guzzle.git",
+                "reference": "ac1ec1cd9b5624694c3a40be801d94137afb12b4"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/guzzle/guzzle/zipball/ac1ec1cd9b5624694c3a40be801d94137afb12b4",
+                "reference": "ac1ec1cd9b5624694c3a40be801d94137afb12b4",
+                "shasum": ""
+            },
+            "require": {
+                "ext-json": "*",
+                "guzzlehttp/promises": "^1.5",
+                "guzzlehttp/psr7": "^1.8.3 || ^2.1",
+                "php": "^7.2.5 || ^8.0",
+                "psr/http-client": "^1.0",
+                "symfony/deprecation-contracts": "^2.2 || ^3.0"
+            },
+            "provide": {
+                "psr/http-client-implementation": "1.0"
+            },
+            "require-dev": {
+                "bamarni/composer-bin-plugin": "^1.4.1",
+                "ext-curl": "*",
+                "php-http/client-integration-tests": "^3.0",
+                "phpunit/phpunit": "^8.5.5 || ^9.3.5",
+                "psr/log": "^1.1 || ^2.0 || ^3.0"
+            },
+            "suggest": {
+                "ext-curl": "Required for CURL handler support",
+                "ext-intl": "Required for Internationalized Domain Name (IDN) support",
+                "psr/log": "Required for using the Log middleware"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "7.4-dev"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "src/functions_include.php"
+                ],
+                "psr-4": {
+                    "GuzzleHttp\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Graham Campbell",
+                    "email": "hello@gjcampbell.co.uk",
+                    "homepage": "https://github.com/GrahamCampbell"
+                },
+                {
+                    "name": "Michael Dowling",
+                    "email": "mtdowling@gmail.com",
+                    "homepage": "https://github.com/mtdowling"
+                },
+                {
+                    "name": "Jeremy Lindblom",
+                    "email": "jeremeamia@gmail.com",
+                    "homepage": "https://github.com/jeremeamia"
+                },
+                {
+                    "name": "George Mponos",
+                    "email": "gmponos@gmail.com",
+                    "homepage": "https://github.com/gmponos"
+                },
+                {
+                    "name": "Tobias Nyholm",
+                    "email": "tobias.nyholm@gmail.com",
+                    "homepage": "https://github.com/Nyholm"
+                },
+                {
+                    "name": "Márk Sági-Kazár",
+                    "email": "mark.sagikazar@gmail.com",
+                    "homepage": "https://github.com/sagikazarmark"
+                },
+                {
+                    "name": "Tobias Schultze",
+                    "email": "webmaster@tubo-world.de",
+                    "homepage": "https://github.com/Tobion"
+                }
+            ],
+            "description": "Guzzle is a PHP HTTP client library",
+            "keywords": [
+                "client",
+                "curl",
+                "framework",
+                "http",
+                "http client",
+                "psr-18",
+                "psr-7",
+                "rest",
+                "web service"
+            ],
+            "support": {
+                "issues": "https://github.com/guzzle/guzzle/issues",
+                "source": "https://github.com/guzzle/guzzle/tree/7.4.2"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/GrahamCampbell",
+                    "type": "github"
+                },
+                {
+                    "url": "https://github.com/Nyholm",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2022-03-20T14:16:28+00:00"
+        },
+        {
+            "name": "guzzlehttp/promises",
+            "version": "1.5.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/guzzle/promises.git",
+                "reference": "fe752aedc9fd8fcca3fe7ad05d419d32998a06da"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/guzzle/promises/zipball/fe752aedc9fd8fcca3fe7ad05d419d32998a06da",
+                "reference": "fe752aedc9fd8fcca3fe7ad05d419d32998a06da",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.5"
+            },
+            "require-dev": {
+                "symfony/phpunit-bridge": "^4.4 || ^5.1"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.5-dev"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "src/functions_include.php"
+                ],
+                "psr-4": {
+                    "GuzzleHttp\\Promise\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Graham Campbell",
+                    "email": "hello@gjcampbell.co.uk",
+                    "homepage": "https://github.com/GrahamCampbell"
+                },
+                {
+                    "name": "Michael Dowling",
+                    "email": "mtdowling@gmail.com",
+                    "homepage": "https://github.com/mtdowling"
+                },
+                {
+                    "name": "Tobias Nyholm",
+                    "email": "tobias.nyholm@gmail.com",
+                    "homepage": "https://github.com/Nyholm"
+                },
+                {
+                    "name": "Tobias Schultze",
+                    "email": "webmaster@tubo-world.de",
+                    "homepage": "https://github.com/Tobion"
+                }
+            ],
+            "description": "Guzzle promises library",
+            "keywords": [
+                "promise"
+            ],
+            "support": {
+                "issues": "https://github.com/guzzle/promises/issues",
+                "source": "https://github.com/guzzle/promises/tree/1.5.1"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/GrahamCampbell",
+                    "type": "github"
+                },
+                {
+                    "url": "https://github.com/Nyholm",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-10-22T20:56:57+00:00"
+        },
+        {
+            "name": "guzzlehttp/psr7",
+            "version": "2.2.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/guzzle/psr7.git",
+                "reference": "c94a94f120803a18554c1805ef2e539f8285f9a2"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/guzzle/psr7/zipball/c94a94f120803a18554c1805ef2e539f8285f9a2",
+                "reference": "c94a94f120803a18554c1805ef2e539f8285f9a2",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.2.5 || ^8.0",
+                "psr/http-factory": "^1.0",
+                "psr/http-message": "^1.0",
+                "ralouphie/getallheaders": "^3.0"
+            },
+            "provide": {
+                "psr/http-factory-implementation": "1.0",
+                "psr/http-message-implementation": "1.0"
+            },
+            "require-dev": {
+                "bamarni/composer-bin-plugin": "^1.4.1",
+                "http-interop/http-factory-tests": "^0.9",
+                "phpunit/phpunit": "^8.5.8 || ^9.3.10"
+            },
+            "suggest": {
+                "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.2-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "GuzzleHttp\\Psr7\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Graham Campbell",
+                    "email": "hello@gjcampbell.co.uk",
+                    "homepage": "https://github.com/GrahamCampbell"
+                },
+                {
+                    "name": "Michael Dowling",
+                    "email": "mtdowling@gmail.com",
+                    "homepage": "https://github.com/mtdowling"
+                },
+                {
+                    "name": "George Mponos",
+                    "email": "gmponos@gmail.com",
+                    "homepage": "https://github.com/gmponos"
+                },
+                {
+                    "name": "Tobias Nyholm",
+                    "email": "tobias.nyholm@gmail.com",
+                    "homepage": "https://github.com/Nyholm"
+                },
+                {
+                    "name": "Márk Sági-Kazár",
+                    "email": "mark.sagikazar@gmail.com",
+                    "homepage": "https://github.com/sagikazarmark"
+                },
+                {
+                    "name": "Tobias Schultze",
+                    "email": "webmaster@tubo-world.de",
+                    "homepage": "https://github.com/Tobion"
+                },
+                {
+                    "name": "Márk Sági-Kazár",
+                    "email": "mark.sagikazar@gmail.com",
+                    "homepage": "https://sagikazarmark.hu"
+                }
+            ],
+            "description": "PSR-7 message implementation that also provides common utility methods",
+            "keywords": [
+                "http",
+                "message",
+                "psr-7",
+                "request",
+                "response",
+                "stream",
+                "uri",
+                "url"
+            ],
+            "support": {
+                "issues": "https://github.com/guzzle/psr7/issues",
+                "source": "https://github.com/guzzle/psr7/tree/2.2.1"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/GrahamCampbell",
+                    "type": "github"
+                },
+                {
+                    "url": "https://github.com/Nyholm",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2022-03-20T21:55:58+00:00"
+        },
+        {
+            "name": "laravel-lang/lang",
+            "version": "10.1.12",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/Laravel-Lang/lang.git",
+                "reference": "dbb5b475cc48d906427255f9a6e557d5075413a1"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/Laravel-Lang/lang/zipball/dbb5b475cc48d906427255f9a6e557d5075413a1",
+                "reference": "dbb5b475cc48d906427255f9a6e557d5075413a1",
+                "shasum": ""
+            },
+            "require": {
+                "ext-json": "*"
+            },
+            "require-dev": {
+                "dragon-code/pretty-array": "^3.0",
+                "dragon-code/support": "^5.0",
+                "guzzlehttp/guzzle": "^7.3",
+                "laravel/breeze": "^1.2",
+                "laravel/fortify": "^1.7",
+                "laravel/jetstream": "^2.3",
+                "php": "^8.0",
+                "phpunit/phpunit": "^9.5",
+                "symfony/finder": "^5.2",
+                "symfony/var-dumper": "^5.2",
+                "vlucas/phpdotenv": "^5.3"
+            },
+            "suggest": {
+                "arcanedev/laravel-lang": "Translations manager and checker for Laravel 5",
+                "laravel-lang/publisher": "Easy installation and update of translation files for your project",
+                "overtrue/laravel-lang": "Command to add languages in your project"
+            },
+            "type": "library",
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Laravel-Lang Team",
+                    "homepage": "https://github.com/Laravel-Lang"
+                }
+            ],
+            "description": "Languages for Laravel",
+            "keywords": [
+                "lang",
+                "languages",
+                "laravel",
+                "lpm"
+            ],
+            "support": {
+                "issues": "https://github.com/Laravel-Lang/lang/issues",
+                "source": "https://github.com/Laravel-Lang/lang"
+            },
+            "time": "2021-12-28T19:42:13+00:00"
+        },
+        {
+            "name": "laravel/framework",
+            "version": "v9.13.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/laravel/framework.git",
+                "reference": "87b6cc8bc41d1cf85c7c1401cddde8570a3b95bb"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/laravel/framework/zipball/87b6cc8bc41d1cf85c7c1401cddde8570a3b95bb",
+                "reference": "87b6cc8bc41d1cf85c7c1401cddde8570a3b95bb",
+                "shasum": ""
+            },
+            "require": {
+                "doctrine/inflector": "^2.0",
+                "dragonmantank/cron-expression": "^3.1",
+                "egulias/email-validator": "^3.1",
+                "ext-mbstring": "*",
+                "ext-openssl": "*",
+                "fruitcake/php-cors": "^1.2",
+                "laravel/serializable-closure": "^1.0",
+                "league/commonmark": "^2.2",
+                "league/flysystem": "^3.0",
+                "monolog/monolog": "^2.0",
+                "nesbot/carbon": "^2.53.1",
+                "php": "^8.0.2",
+                "psr/container": "^1.1.1|^2.0.1",
+                "psr/log": "^1.0|^2.0|^3.0",
+                "psr/simple-cache": "^1.0|^2.0|^3.0",
+                "ramsey/uuid": "^4.2.2",
+                "symfony/console": "^6.0",
+                "symfony/error-handler": "^6.0",
+                "symfony/finder": "^6.0",
+                "symfony/http-foundation": "^6.0",
+                "symfony/http-kernel": "^6.0",
+                "symfony/mailer": "^6.0",
+                "symfony/mime": "^6.0",
+                "symfony/process": "^6.0",
+                "symfony/routing": "^6.0",
+                "symfony/var-dumper": "^6.0",
+                "tijsverkoyen/css-to-inline-styles": "^2.2.2",
+                "vlucas/phpdotenv": "^5.4.1",
+                "voku/portable-ascii": "^2.0"
+            },
+            "conflict": {
+                "tightenco/collect": "<5.5.33"
+            },
+            "provide": {
+                "psr/container-implementation": "1.1|2.0",
+                "psr/simple-cache-implementation": "1.0|2.0|3.0"
+            },
+            "replace": {
+                "illuminate/auth": "self.version",
+                "illuminate/broadcasting": "self.version",
+                "illuminate/bus": "self.version",
+                "illuminate/cache": "self.version",
+                "illuminate/collections": "self.version",
+                "illuminate/conditionable": "self.version",
+                "illuminate/config": "self.version",
+                "illuminate/console": "self.version",
+                "illuminate/container": "self.version",
+                "illuminate/contracts": "self.version",
+                "illuminate/cookie": "self.version",
+                "illuminate/database": "self.version",
+                "illuminate/encryption": "self.version",
+                "illuminate/events": "self.version",
+                "illuminate/filesystem": "self.version",
+                "illuminate/hashing": "self.version",
+                "illuminate/http": "self.version",
+                "illuminate/log": "self.version",
+                "illuminate/macroable": "self.version",
+                "illuminate/mail": "self.version",
+                "illuminate/notifications": "self.version",
+                "illuminate/pagination": "self.version",
+                "illuminate/pipeline": "self.version",
+                "illuminate/queue": "self.version",
+                "illuminate/redis": "self.version",
+                "illuminate/routing": "self.version",
+                "illuminate/session": "self.version",
+                "illuminate/support": "self.version",
+                "illuminate/testing": "self.version",
+                "illuminate/translation": "self.version",
+                "illuminate/validation": "self.version",
+                "illuminate/view": "self.version"
+            },
+            "require-dev": {
+                "aws/aws-sdk-php": "^3.198.1",
+                "doctrine/dbal": "^2.13.3|^3.1.4",
+                "fakerphp/faker": "^1.9.2",
+                "guzzlehttp/guzzle": "^7.2",
+                "league/flysystem-aws-s3-v3": "^3.0",
+                "league/flysystem-ftp": "^3.0",
+                "league/flysystem-sftp-v3": "^3.0",
+                "mockery/mockery": "^1.4.4",
+                "orchestra/testbench-core": "^7.1",
+                "pda/pheanstalk": "^4.0",
+                "phpstan/phpstan": "^1.4.7",
+                "phpunit/phpunit": "^9.5.8",
+                "predis/predis": "^1.1.9",
+                "symfony/cache": "^6.0"
+            },
+            "suggest": {
+                "ably/ably-php": "Required to use the Ably broadcast driver (^1.0).",
+                "aws/aws-sdk-php": "Required to use the SQS queue driver, DynamoDb failed job storage, and SES mail driver (^3.198.1).",
+                "brianium/paratest": "Required to run tests in parallel (^6.0).",
+                "doctrine/dbal": "Required to rename columns and drop SQLite columns (^2.13.3|^3.1.4).",
+                "ext-bcmath": "Required to use the multiple_of validation rule.",
+                "ext-ftp": "Required to use the Flysystem FTP driver.",
+                "ext-gd": "Required to use Illuminate\\Http\\Testing\\FileFactory::image().",
+                "ext-memcached": "Required to use the memcache cache driver.",
+                "ext-pcntl": "Required to use all features of the queue worker.",
+                "ext-posix": "Required to use all features of the queue worker.",
+                "ext-redis": "Required to use the Redis cache and queue drivers (^4.0|^5.0).",
+                "fakerphp/faker": "Required to use the eloquent factory builder (^1.9.1).",
+                "filp/whoops": "Required for friendly error pages in development (^2.14.3).",
+                "guzzlehttp/guzzle": "Required to use the HTTP Client and the ping methods on schedules (^7.2).",
+                "laravel/tinker": "Required to use the tinker console command (^2.0).",
+                "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^3.0).",
+                "league/flysystem-ftp": "Required to use the Flysystem FTP driver (^3.0).",
+                "league/flysystem-sftp-v3": "Required to use the Flysystem SFTP driver (^3.0).",
+                "mockery/mockery": "Required to use mocking (^1.4.4).",
+                "nyholm/psr7": "Required to use PSR-7 bridging features (^1.2).",
+                "pda/pheanstalk": "Required to use the beanstalk queue driver (^4.0).",
+                "phpunit/phpunit": "Required to use assertions and run tests (^9.5.8).",
+                "predis/predis": "Required to use the predis connector (^1.1.9).",
+                "psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).",
+                "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^6.0|^7.0).",
+                "symfony/cache": "Required to PSR-6 cache bridge (^6.0).",
+                "symfony/filesystem": "Required to enable support for relative symbolic links (^6.0).",
+                "symfony/http-client": "Required to enable support for the Symfony API mail transports (^6.0).",
+                "symfony/mailgun-mailer": "Required to enable support for the Mailgun mail transport (^6.0).",
+                "symfony/postmark-mailer": "Required to enable support for the Postmark mail transport (^6.0).",
+                "symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^2.0)."
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "9.x-dev"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "src/Illuminate/Collections/helpers.php",
+                    "src/Illuminate/Events/functions.php",
+                    "src/Illuminate/Foundation/helpers.php",
+                    "src/Illuminate/Support/helpers.php"
+                ],
+                "psr-4": {
+                    "Illuminate\\": "src/Illuminate/",
+                    "Illuminate\\Support\\": [
+                        "src/Illuminate/Macroable/",
+                        "src/Illuminate/Collections/",
+                        "src/Illuminate/Conditionable/"
+                    ]
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Taylor Otwell",
+                    "email": "taylor@laravel.com"
+                }
+            ],
+            "description": "The Laravel Framework.",
+            "homepage": "https://laravel.com",
+            "keywords": [
+                "framework",
+                "laravel"
+            ],
+            "support": {
+                "issues": "https://github.com/laravel/framework/issues",
+                "source": "https://github.com/laravel/framework"
+            },
+            "time": "2022-05-17T14:07:43+00:00"
+        },
+        {
+            "name": "laravel/sanctum",
+            "version": "v2.15.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/laravel/sanctum.git",
+                "reference": "31fbe6f85aee080c4dc2f9b03dc6dd5d0ee72473"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/laravel/sanctum/zipball/31fbe6f85aee080c4dc2f9b03dc6dd5d0ee72473",
+                "reference": "31fbe6f85aee080c4dc2f9b03dc6dd5d0ee72473",
+                "shasum": ""
+            },
+            "require": {
+                "ext-json": "*",
+                "illuminate/console": "^6.9|^7.0|^8.0|^9.0",
+                "illuminate/contracts": "^6.9|^7.0|^8.0|^9.0",
+                "illuminate/database": "^6.9|^7.0|^8.0|^9.0",
+                "illuminate/support": "^6.9|^7.0|^8.0|^9.0",
+                "php": "^7.2|^8.0"
+            },
+            "require-dev": {
+                "mockery/mockery": "^1.0",
+                "orchestra/testbench": "^4.0|^5.0|^6.0|^7.0",
+                "phpunit/phpunit": "^8.0|^9.3"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.x-dev"
+                },
+                "laravel": {
+                    "providers": [
+                        "Laravel\\Sanctum\\SanctumServiceProvider"
+                    ]
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Laravel\\Sanctum\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Taylor Otwell",
+                    "email": "taylor@laravel.com"
+                }
+            ],
+            "description": "Laravel Sanctum provides a featherweight authentication system for SPAs and simple APIs.",
+            "keywords": [
+                "auth",
+                "laravel",
+                "sanctum"
+            ],
+            "support": {
+                "issues": "https://github.com/laravel/sanctum/issues",
+                "source": "https://github.com/laravel/sanctum"
+            },
+            "time": "2022-04-08T13:39:49+00:00"
+        },
+        {
+            "name": "laravel/serializable-closure",
+            "version": "v1.2.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/laravel/serializable-closure.git",
+                "reference": "09f0e9fb61829f628205b7c94906c28740ff9540"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/09f0e9fb61829f628205b7c94906c28740ff9540",
+                "reference": "09f0e9fb61829f628205b7c94906c28740ff9540",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.3|^8.0"
+            },
+            "require-dev": {
+                "pestphp/pest": "^1.18",
+                "phpstan/phpstan": "^0.12.98",
+                "symfony/var-dumper": "^5.3"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Laravel\\SerializableClosure\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Taylor Otwell",
+                    "email": "taylor@laravel.com"
+                },
+                {
+                    "name": "Nuno Maduro",
+                    "email": "nuno@laravel.com"
+                }
+            ],
+            "description": "Laravel Serializable Closure provides an easy and secure way to serialize closures in PHP.",
+            "keywords": [
+                "closure",
+                "laravel",
+                "serializable"
+            ],
+            "support": {
+                "issues": "https://github.com/laravel/serializable-closure/issues",
+                "source": "https://github.com/laravel/serializable-closure"
+            },
+            "time": "2022-05-16T17:09:47+00:00"
+        },
+        {
+            "name": "laravel/tinker",
+            "version": "v2.7.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/laravel/tinker.git",
+                "reference": "dff39b661e827dae6e092412f976658df82dbac5"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/laravel/tinker/zipball/dff39b661e827dae6e092412f976658df82dbac5",
+                "reference": "dff39b661e827dae6e092412f976658df82dbac5",
+                "shasum": ""
+            },
+            "require": {
+                "illuminate/console": "^6.0|^7.0|^8.0|^9.0",
+                "illuminate/contracts": "^6.0|^7.0|^8.0|^9.0",
+                "illuminate/support": "^6.0|^7.0|^8.0|^9.0",
+                "php": "^7.2.5|^8.0",
+                "psy/psysh": "^0.10.4|^0.11.1",
+                "symfony/var-dumper": "^4.3.4|^5.0|^6.0"
+            },
+            "require-dev": {
+                "mockery/mockery": "~1.3.3|^1.4.2",
+                "phpunit/phpunit": "^8.5.8|^9.3.3"
+            },
+            "suggest": {
+                "illuminate/database": "The Illuminate Database package (^6.0|^7.0|^8.0|^9.0)."
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.x-dev"
+                },
+                "laravel": {
+                    "providers": [
+                        "Laravel\\Tinker\\TinkerServiceProvider"
+                    ]
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Laravel\\Tinker\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Taylor Otwell",
+                    "email": "taylor@laravel.com"
+                }
+            ],
+            "description": "Powerful REPL for the Laravel framework.",
+            "keywords": [
+                "REPL",
+                "Tinker",
+                "laravel",
+                "psysh"
+            ],
+            "support": {
+                "issues": "https://github.com/laravel/tinker/issues",
+                "source": "https://github.com/laravel/tinker/tree/v2.7.2"
+            },
+            "time": "2022-03-23T12:38:24+00:00"
+        },
+        {
+            "name": "lcobucci/clock",
+            "version": "2.2.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/lcobucci/clock.git",
+                "reference": "fb533e093fd61321bfcbac08b131ce805fe183d3"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/lcobucci/clock/zipball/fb533e093fd61321bfcbac08b131ce805fe183d3",
+                "reference": "fb533e093fd61321bfcbac08b131ce805fe183d3",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^8.0",
+                "stella-maris/clock": "^0.1.4"
+            },
+            "require-dev": {
+                "infection/infection": "^0.26",
+                "lcobucci/coding-standard": "^8.0",
+                "phpstan/extension-installer": "^1.1",
+                "phpstan/phpstan": "^0.12",
+                "phpstan/phpstan-deprecation-rules": "^0.12",
+                "phpstan/phpstan-phpunit": "^0.12",
+                "phpstan/phpstan-strict-rules": "^0.12",
+                "phpunit/phpunit": "^9.5"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Lcobucci\\Clock\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Luís Cobucci",
+                    "email": "lcobucci@gmail.com"
+                }
+            ],
+            "description": "Yet another clock abstraction",
+            "support": {
+                "issues": "https://github.com/lcobucci/clock/issues",
+                "source": "https://github.com/lcobucci/clock/tree/2.2.0"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/lcobucci",
+                    "type": "github"
+                },
+                {
+                    "url": "https://www.patreon.com/lcobucci",
+                    "type": "patreon"
+                }
+            ],
+            "time": "2022-04-19T19:34:17+00:00"
+        },
+        {
+            "name": "lcobucci/jwt",
+            "version": "4.1.5",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/lcobucci/jwt.git",
+                "reference": "fe2d89f2eaa7087af4aa166c6f480ef04e000582"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/lcobucci/jwt/zipball/fe2d89f2eaa7087af4aa166c6f480ef04e000582",
+                "reference": "fe2d89f2eaa7087af4aa166c6f480ef04e000582",
+                "shasum": ""
+            },
+            "require": {
+                "ext-hash": "*",
+                "ext-json": "*",
+                "ext-mbstring": "*",
+                "ext-openssl": "*",
+                "ext-sodium": "*",
+                "lcobucci/clock": "^2.0",
+                "php": "^7.4 || ^8.0"
+            },
+            "require-dev": {
+                "infection/infection": "^0.21",
+                "lcobucci/coding-standard": "^6.0",
+                "mikey179/vfsstream": "^1.6.7",
+                "phpbench/phpbench": "^1.0",
+                "phpstan/extension-installer": "^1.0",
+                "phpstan/phpstan": "^0.12",
+                "phpstan/phpstan-deprecation-rules": "^0.12",
+                "phpstan/phpstan-phpunit": "^0.12",
+                "phpstan/phpstan-strict-rules": "^0.12",
+                "phpunit/php-invoker": "^3.1",
+                "phpunit/phpunit": "^9.5"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Lcobucci\\JWT\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Luís Cobucci",
+                    "email": "lcobucci@gmail.com",
+                    "role": "Developer"
+                }
+            ],
+            "description": "A simple library to work with JSON Web Token and JSON Web Signature",
+            "keywords": [
+                "JWS",
+                "jwt"
+            ],
+            "support": {
+                "issues": "https://github.com/lcobucci/jwt/issues",
+                "source": "https://github.com/lcobucci/jwt/tree/4.1.5"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/lcobucci",
+                    "type": "github"
+                },
+                {
+                    "url": "https://www.patreon.com/lcobucci",
+                    "type": "patreon"
+                }
+            ],
+            "time": "2021-09-28T19:34:56+00:00"
+        },
+        {
+            "name": "league/commonmark",
+            "version": "2.3.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/thephpleague/commonmark.git",
+                "reference": "cb36fee279f7fca01d5d9399ddd1b37e48e2eca1"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/cb36fee279f7fca01d5d9399ddd1b37e48e2eca1",
+                "reference": "cb36fee279f7fca01d5d9399ddd1b37e48e2eca1",
+                "shasum": ""
+            },
+            "require": {
+                "ext-mbstring": "*",
+                "league/config": "^1.1.1",
+                "php": "^7.4 || ^8.0",
+                "psr/event-dispatcher": "^1.0",
+                "symfony/deprecation-contracts": "^2.1 || ^3.0",
+                "symfony/polyfill-php80": "^1.16"
+            },
+            "require-dev": {
+                "cebe/markdown": "^1.0",
+                "commonmark/cmark": "0.30.0",
+                "commonmark/commonmark.js": "0.30.0",
+                "composer/package-versions-deprecated": "^1.8",
+                "embed/embed": "^4.4",
+                "erusev/parsedown": "^1.0",
+                "ext-json": "*",
+                "github/gfm": "0.29.0",
+                "michelf/php-markdown": "^1.4",
+                "nyholm/psr7": "^1.5",
+                "phpstan/phpstan": "^0.12.88 || ^1.0.0",
+                "phpunit/phpunit": "^9.5.5",
+                "scrutinizer/ocular": "^1.8.1",
+                "symfony/finder": "^5.3",
+                "symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0",
+                "unleashedtech/php-coding-standard": "^3.1",
+                "vimeo/psalm": "^4.7.3"
+            },
+            "suggest": {
+                "symfony/yaml": "v2.3+ required if using the Front Matter extension"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "2.4-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "League\\CommonMark\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Colin O'Dell",
+                    "email": "colinodell@gmail.com",
+                    "homepage": "https://www.colinodell.com",
+                    "role": "Lead Developer"
+                }
+            ],
+            "description": "Highly-extensible PHP Markdown parser which fully supports the CommonMark spec and GitHub-Flavored Markdown (GFM)",
+            "homepage": "https://commonmark.thephpleague.com",
+            "keywords": [
+                "commonmark",
+                "flavored",
+                "gfm",
+                "github",
+                "github-flavored",
+                "markdown",
+                "md",
+                "parser"
+            ],
+            "support": {
+                "docs": "https://commonmark.thephpleague.com/",
+                "forum": "https://github.com/thephpleague/commonmark/discussions",
+                "issues": "https://github.com/thephpleague/commonmark/issues",
+                "rss": "https://github.com/thephpleague/commonmark/releases.atom",
+                "source": "https://github.com/thephpleague/commonmark"
+            },
+            "funding": [
+                {
+                    "url": "https://www.colinodell.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://www.paypal.me/colinpodell/10.00",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/colinodell",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/league/commonmark",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2022-05-14T15:37:39+00:00"
+        },
+        {
+            "name": "league/config",
+            "version": "v1.1.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/thephpleague/config.git",
+                "reference": "a9d39eeeb6cc49d10a6e6c36f22c4c1f4a767f3e"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/thephpleague/config/zipball/a9d39eeeb6cc49d10a6e6c36f22c4c1f4a767f3e",
+                "reference": "a9d39eeeb6cc49d10a6e6c36f22c4c1f4a767f3e",
+                "shasum": ""
+            },
+            "require": {
+                "dflydev/dot-access-data": "^3.0.1",
+                "nette/schema": "^1.2",
+                "php": "^7.4 || ^8.0"
+            },
+            "require-dev": {
+                "phpstan/phpstan": "^0.12.90",
+                "phpunit/phpunit": "^9.5.5",
+                "scrutinizer/ocular": "^1.8.1",
+                "unleashedtech/php-coding-standard": "^3.1",
+                "vimeo/psalm": "^4.7.3"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "1.2-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "League\\Config\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Colin O'Dell",
+                    "email": "colinodell@gmail.com",
+                    "homepage": "https://www.colinodell.com",
+                    "role": "Lead Developer"
+                }
+            ],
+            "description": "Define configuration arrays with strict schemas and access values with dot notation",
+            "homepage": "https://config.thephpleague.com",
+            "keywords": [
+                "array",
+                "config",
+                "configuration",
+                "dot",
+                "dot-access",
+                "nested",
+                "schema"
+            ],
+            "support": {
+                "docs": "https://config.thephpleague.com/",
+                "issues": "https://github.com/thephpleague/config/issues",
+                "rss": "https://github.com/thephpleague/config/releases.atom",
+                "source": "https://github.com/thephpleague/config"
+            },
+            "funding": [
+                {
+                    "url": "https://www.colinodell.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://www.paypal.me/colinpodell/10.00",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/colinodell",
+                    "type": "github"
+                }
+            ],
+            "time": "2021-08-14T12:15:32+00:00"
+        },
+        {
+            "name": "league/flysystem",
+            "version": "3.0.19",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/thephpleague/flysystem.git",
+                "reference": "670df21225d68d165a8df38587ac3f41caf608f8"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/670df21225d68d165a8df38587ac3f41caf608f8",
+                "reference": "670df21225d68d165a8df38587ac3f41caf608f8",
+                "shasum": ""
+            },
+            "require": {
+                "league/mime-type-detection": "^1.0.0",
+                "php": "^8.0.2"
+            },
+            "conflict": {
+                "aws/aws-sdk-php": "3.209.31 || 3.210.0",
+                "guzzlehttp/guzzle": "<7.0",
+                "guzzlehttp/ringphp": "<1.1.1",
+                "symfony/http-client": "<5.2"
+            },
+            "require-dev": {
+                "async-aws/s3": "^1.5",
+                "async-aws/simple-s3": "^1.0",
+                "aws/aws-sdk-php": "^3.198.1",
+                "composer/semver": "^3.0",
+                "ext-fileinfo": "*",
+                "ext-ftp": "*",
+                "ext-zip": "*",
+                "friendsofphp/php-cs-fixer": "^3.5",
+                "google/cloud-storage": "^1.23",
+                "microsoft/azure-storage-blob": "^1.1",
+                "phpseclib/phpseclib": "^2.0",
+                "phpstan/phpstan": "^0.12.26",
+                "phpunit/phpunit": "^9.5.11",
+                "sabre/dav": "^4.3.1"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "League\\Flysystem\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Frank de Jonge",
+                    "email": "info@frankdejonge.nl"
+                }
+            ],
+            "description": "File storage abstraction for PHP",
+            "keywords": [
+                "WebDAV",
+                "aws",
+                "cloud",
+                "file",
+                "files",
+                "filesystem",
+                "filesystems",
+                "ftp",
+                "s3",
+                "sftp",
+                "storage"
+            ],
+            "support": {
+                "issues": "https://github.com/thephpleague/flysystem/issues",
+                "source": "https://github.com/thephpleague/flysystem/tree/3.0.19"
+            },
+            "funding": [
+                {
+                    "url": "https://offset.earth/frankdejonge",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/frankdejonge",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/league/flysystem",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2022-05-03T21:19:02+00:00"
+        },
+        {
+            "name": "league/mime-type-detection",
+            "version": "1.11.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/thephpleague/mime-type-detection.git",
+                "reference": "ff6248ea87a9f116e78edd6002e39e5128a0d4dd"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/ff6248ea87a9f116e78edd6002e39e5128a0d4dd",
+                "reference": "ff6248ea87a9f116e78edd6002e39e5128a0d4dd",
+                "shasum": ""
+            },
+            "require": {
+                "ext-fileinfo": "*",
+                "php": "^7.2 || ^8.0"
+            },
+            "require-dev": {
+                "friendsofphp/php-cs-fixer": "^3.2",
+                "phpstan/phpstan": "^0.12.68",
+                "phpunit/phpunit": "^8.5.8 || ^9.3"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "League\\MimeTypeDetection\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Frank de Jonge",
+                    "email": "info@frankdejonge.nl"
+                }
+            ],
+            "description": "Mime-type detection for Flysystem",
+            "support": {
+                "issues": "https://github.com/thephpleague/mime-type-detection/issues",
+                "source": "https://github.com/thephpleague/mime-type-detection/tree/1.11.0"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/frankdejonge",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/league/flysystem",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2022-04-17T13:12:02+00:00"
+        },
+        {
+            "name": "monolog/monolog",
+            "version": "2.6.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/Seldaek/monolog.git",
+                "reference": "247918972acd74356b0a91dfaa5adcaec069b6c0"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/Seldaek/monolog/zipball/247918972acd74356b0a91dfaa5adcaec069b6c0",
+                "reference": "247918972acd74356b0a91dfaa5adcaec069b6c0",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.2",
+                "psr/log": "^1.0.1 || ^2.0 || ^3.0"
+            },
+            "provide": {
+                "psr/log-implementation": "1.0.0 || 2.0.0 || 3.0.0"
+            },
+            "require-dev": {
+                "aws/aws-sdk-php": "^2.4.9 || ^3.0",
+                "doctrine/couchdb": "~1.0@dev",
+                "elasticsearch/elasticsearch": "^7 || ^8",
+                "ext-json": "*",
+                "graylog2/gelf-php": "^1.4.2",
+                "guzzlehttp/guzzle": "^7.4",
+                "guzzlehttp/psr7": "^2.2",
+                "mongodb/mongodb": "^1.8",
+                "php-amqplib/php-amqplib": "~2.4 || ^3",
+                "php-console/php-console": "^3.1.3",
+                "phpspec/prophecy": "^1.15",
+                "phpstan/phpstan": "^0.12.91",
+                "phpunit/phpunit": "^8.5.14",
+                "predis/predis": "^1.1",
+                "rollbar/rollbar": "^1.3 || ^2 || ^3",
+                "ruflin/elastica": "^7",
+                "swiftmailer/swiftmailer": "^5.3|^6.0",
+                "symfony/mailer": "^5.4 || ^6",
+                "symfony/mime": "^5.4 || ^6"
+            },
+            "suggest": {
+                "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB",
+                "doctrine/couchdb": "Allow sending log messages to a CouchDB server",
+                "elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client",
+                "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)",
+                "ext-curl": "Required to send log messages using the IFTTTHandler, the LogglyHandler, the SendGridHandler, the SlackWebhookHandler or the TelegramBotHandler",
+                "ext-mbstring": "Allow to work properly with unicode symbols",
+                "ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)",
+                "ext-openssl": "Required to send log messages using SSL",
+                "ext-sockets": "Allow sending log messages to a Syslog server (via UDP driver)",
+                "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server",
+                "mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)",
+                "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib",
+                "php-console/php-console": "Allow sending log messages to Google Chrome",
+                "rollbar/rollbar": "Allow sending log messages to Rollbar",
+                "ruflin/elastica": "Allow sending log messages to an Elastic Search server"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "2.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Monolog\\": "src/Monolog"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Jordi Boggiano",
+                    "email": "j.boggiano@seld.be",
+                    "homepage": "https://seld.be"
+                }
+            ],
+            "description": "Sends your logs to files, sockets, inboxes, databases and various web services",
+            "homepage": "https://github.com/Seldaek/monolog",
+            "keywords": [
+                "log",
+                "logging",
+                "psr-3"
+            ],
+            "support": {
+                "issues": "https://github.com/Seldaek/monolog/issues",
+                "source": "https://github.com/Seldaek/monolog/tree/2.6.0"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/Seldaek",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/monolog/monolog",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2022-05-10T09:36:00+00:00"
+        },
+        {
+            "name": "nesbot/carbon",
+            "version": "2.58.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/briannesbitt/Carbon.git",
+                "reference": "97a34af22bde8d0ac20ab34b29d7bfe360902055"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/97a34af22bde8d0ac20ab34b29d7bfe360902055",
+                "reference": "97a34af22bde8d0ac20ab34b29d7bfe360902055",
+                "shasum": ""
+            },
+            "require": {
+                "ext-json": "*",
+                "php": "^7.1.8 || ^8.0",
+                "symfony/polyfill-mbstring": "^1.0",
+                "symfony/polyfill-php80": "^1.16",
+                "symfony/translation": "^3.4 || ^4.0 || ^5.0 || ^6.0"
+            },
+            "require-dev": {
+                "doctrine/dbal": "^2.0 || ^3.0",
+                "doctrine/orm": "^2.7",
+                "friendsofphp/php-cs-fixer": "^3.0",
+                "kylekatarnls/multi-tester": "^2.0",
+                "phpmd/phpmd": "^2.9",
+                "phpstan/extension-installer": "^1.0",
+                "phpstan/phpstan": "^0.12.54 || ^1.0",
+                "phpunit/php-file-iterator": "^2.0.5",
+                "phpunit/phpunit": "^7.5.20 || ^8.5.23",
+                "squizlabs/php_codesniffer": "^3.4"
+            },
+            "bin": [
+                "bin/carbon"
+            ],
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-3.x": "3.x-dev",
+                    "dev-master": "2.x-dev"
+                },
+                "laravel": {
+                    "providers": [
+                        "Carbon\\Laravel\\ServiceProvider"
+                    ]
+                },
+                "phpstan": {
+                    "includes": [
+                        "extension.neon"
+                    ]
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Carbon\\": "src/Carbon/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Brian Nesbitt",
+                    "email": "brian@nesbot.com",
+                    "homepage": "https://markido.com"
+                },
+                {
+                    "name": "kylekatarnls",
+                    "homepage": "https://github.com/kylekatarnls"
+                }
+            ],
+            "description": "An API extension for DateTime that supports 281 different languages.",
+            "homepage": "https://carbon.nesbot.com",
+            "keywords": [
+                "date",
+                "datetime",
+                "time"
+            ],
+            "support": {
+                "docs": "https://carbon.nesbot.com/docs",
+                "issues": "https://github.com/briannesbitt/Carbon/issues",
+                "source": "https://github.com/briannesbitt/Carbon"
+            },
+            "funding": [
+                {
+                    "url": "https://opencollective.com/Carbon",
+                    "type": "open_collective"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/nesbot/carbon",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2022-04-25T19:31:17+00:00"
+        },
+        {
+            "name": "nette/schema",
+            "version": "v1.2.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/nette/schema.git",
+                "reference": "9a39cef03a5b34c7de64f551538cbba05c2be5df"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/nette/schema/zipball/9a39cef03a5b34c7de64f551538cbba05c2be5df",
+                "reference": "9a39cef03a5b34c7de64f551538cbba05c2be5df",
+                "shasum": ""
+            },
+            "require": {
+                "nette/utils": "^2.5.7 || ^3.1.5 ||  ^4.0",
+                "php": ">=7.1 <8.2"
+            },
+            "require-dev": {
+                "nette/tester": "^2.3 || ^2.4",
+                "phpstan/phpstan-nette": "^0.12",
+                "tracy/tracy": "^2.7"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.2-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause",
+                "GPL-2.0-only",
+                "GPL-3.0-only"
+            ],
+            "authors": [
+                {
+                    "name": "David Grudl",
+                    "homepage": "https://davidgrudl.com"
+                },
+                {
+                    "name": "Nette Community",
+                    "homepage": "https://nette.org/contributors"
+                }
+            ],
+            "description": "📐 Nette Schema: validating data structures against a given Schema.",
+            "homepage": "https://nette.org",
+            "keywords": [
+                "config",
+                "nette"
+            ],
+            "support": {
+                "issues": "https://github.com/nette/schema/issues",
+                "source": "https://github.com/nette/schema/tree/v1.2.2"
+            },
+            "time": "2021-10-15T11:40:02+00:00"
+        },
+        {
+            "name": "nette/utils",
+            "version": "v3.2.7",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/nette/utils.git",
+                "reference": "0af4e3de4df9f1543534beab255ccf459e7a2c99"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/nette/utils/zipball/0af4e3de4df9f1543534beab255ccf459e7a2c99",
+                "reference": "0af4e3de4df9f1543534beab255ccf459e7a2c99",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.2 <8.2"
+            },
+            "conflict": {
+                "nette/di": "<3.0.6"
+            },
+            "require-dev": {
+                "nette/tester": "~2.0",
+                "phpstan/phpstan": "^1.0",
+                "tracy/tracy": "^2.3"
+            },
+            "suggest": {
+                "ext-gd": "to use Image",
+                "ext-iconv": "to use Strings::webalize(), toAscii(), chr() and reverse()",
+                "ext-intl": "to use Strings::webalize(), toAscii(), normalize() and compare()",
+                "ext-json": "to use Nette\\Utils\\Json",
+                "ext-mbstring": "to use Strings::lower() etc...",
+                "ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()",
+                "ext-xml": "to use Strings::length() etc. when mbstring is not available"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.2-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause",
+                "GPL-2.0-only",
+                "GPL-3.0-only"
+            ],
+            "authors": [
+                {
+                    "name": "David Grudl",
+                    "homepage": "https://davidgrudl.com"
+                },
+                {
+                    "name": "Nette Community",
+                    "homepage": "https://nette.org/contributors"
+                }
+            ],
+            "description": "🛠  Nette Utils: lightweight utilities for string & array manipulation, image handling, safe JSON encoding/decoding, validation, slug or strong password generating etc.",
+            "homepage": "https://nette.org",
+            "keywords": [
+                "array",
+                "core",
+                "datetime",
+                "images",
+                "json",
+                "nette",
+                "paginator",
+                "password",
+                "slugify",
+                "string",
+                "unicode",
+                "utf-8",
+                "utility",
+                "validation"
+            ],
+            "support": {
+                "issues": "https://github.com/nette/utils/issues",
+                "source": "https://github.com/nette/utils/tree/v3.2.7"
+            },
+            "time": "2022-01-24T11:29:14+00:00"
+        },
+        {
+            "name": "nikic/php-parser",
+            "version": "v4.13.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/nikic/PHP-Parser.git",
+                "reference": "210577fe3cf7badcc5814d99455df46564f3c077"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/210577fe3cf7badcc5814d99455df46564f3c077",
+                "reference": "210577fe3cf7badcc5814d99455df46564f3c077",
+                "shasum": ""
+            },
+            "require": {
+                "ext-tokenizer": "*",
+                "php": ">=7.0"
+            },
+            "require-dev": {
+                "ircmaxell/php-yacc": "^0.0.7",
+                "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0"
+            },
+            "bin": [
+                "bin/php-parse"
+            ],
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "4.9-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "PhpParser\\": "lib/PhpParser"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Nikita Popov"
+                }
+            ],
+            "description": "A PHP parser written in PHP",
+            "keywords": [
+                "parser",
+                "php"
+            ],
+            "support": {
+                "issues": "https://github.com/nikic/PHP-Parser/issues",
+                "source": "https://github.com/nikic/PHP-Parser/tree/v4.13.2"
+            },
+            "time": "2021-11-30T19:35:32+00:00"
+        },
+        {
+            "name": "phpoption/phpoption",
+            "version": "1.8.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/schmittjoh/php-option.git",
+                "reference": "eab7a0df01fe2344d172bff4cd6dbd3f8b84ad15"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/eab7a0df01fe2344d172bff4cd6dbd3f8b84ad15",
+                "reference": "eab7a0df01fe2344d172bff4cd6dbd3f8b84ad15",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.0 || ^8.0"
+            },
+            "require-dev": {
+                "bamarni/composer-bin-plugin": "^1.4.1",
+                "phpunit/phpunit": "^6.5.14 || ^7.5.20 || ^8.5.19 || ^9.5.8"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.8-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "PhpOption\\": "src/PhpOption/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "Apache-2.0"
+            ],
+            "authors": [
+                {
+                    "name": "Johannes M. Schmitt",
+                    "email": "schmittjoh@gmail.com",
+                    "homepage": "https://github.com/schmittjoh"
+                },
+                {
+                    "name": "Graham Campbell",
+                    "email": "hello@gjcampbell.co.uk",
+                    "homepage": "https://github.com/GrahamCampbell"
+                }
+            ],
+            "description": "Option Type for PHP",
+            "keywords": [
+                "language",
+                "option",
+                "php",
+                "type"
+            ],
+            "support": {
+                "issues": "https://github.com/schmittjoh/php-option/issues",
+                "source": "https://github.com/schmittjoh/php-option/tree/1.8.1"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/GrahamCampbell",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-12-04T23:24:31+00:00"
+        },
+        {
+            "name": "psr/container",
+            "version": "2.0.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/container.git",
+                "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963",
+                "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.4.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Container\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "https://www.php-fig.org/"
+                }
+            ],
+            "description": "Common Container Interface (PHP FIG PSR-11)",
+            "homepage": "https://github.com/php-fig/container",
+            "keywords": [
+                "PSR-11",
+                "container",
+                "container-interface",
+                "container-interop",
+                "psr"
+            ],
+            "support": {
+                "issues": "https://github.com/php-fig/container/issues",
+                "source": "https://github.com/php-fig/container/tree/2.0.2"
+            },
+            "time": "2021-11-05T16:47:00+00:00"
+        },
+        {
+            "name": "psr/event-dispatcher",
+            "version": "1.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/event-dispatcher.git",
+                "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0",
+                "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.2.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Psr\\EventDispatcher\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "http://www.php-fig.org/"
+                }
+            ],
+            "description": "Standard interfaces for event handling.",
+            "keywords": [
+                "events",
+                "psr",
+                "psr-14"
+            ],
+            "support": {
+                "issues": "https://github.com/php-fig/event-dispatcher/issues",
+                "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0"
+            },
+            "time": "2019-01-08T18:20:26+00:00"
+        },
+        {
+            "name": "psr/http-client",
+            "version": "1.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/http-client.git",
+                "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
+                "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.0 || ^8.0",
+                "psr/http-message": "^1.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Http\\Client\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "http://www.php-fig.org/"
+                }
+            ],
+            "description": "Common interface for HTTP clients",
+            "homepage": "https://github.com/php-fig/http-client",
+            "keywords": [
+                "http",
+                "http-client",
+                "psr",
+                "psr-18"
+            ],
+            "support": {
+                "source": "https://github.com/php-fig/http-client/tree/master"
+            },
+            "time": "2020-06-29T06:28:15+00:00"
+        },
+        {
+            "name": "psr/http-factory",
+            "version": "1.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/http-factory.git",
+                "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/http-factory/zipball/12ac7fcd07e5b077433f5f2bee95b3a771bf61be",
+                "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.0.0",
+                "psr/http-message": "^1.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Http\\Message\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "http://www.php-fig.org/"
+                }
+            ],
+            "description": "Common interfaces for PSR-7 HTTP message factories",
+            "keywords": [
+                "factory",
+                "http",
+                "message",
+                "psr",
+                "psr-17",
+                "psr-7",
+                "request",
+                "response"
+            ],
+            "support": {
+                "source": "https://github.com/php-fig/http-factory/tree/master"
+            },
+            "time": "2019-04-30T12:38:16+00:00"
+        },
+        {
+            "name": "psr/http-message",
+            "version": "1.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/http-message.git",
+                "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
+                "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Http\\Message\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "http://www.php-fig.org/"
+                }
+            ],
+            "description": "Common interface for HTTP messages",
+            "homepage": "https://github.com/php-fig/http-message",
+            "keywords": [
+                "http",
+                "http-message",
+                "psr",
+                "psr-7",
+                "request",
+                "response"
+            ],
+            "support": {
+                "source": "https://github.com/php-fig/http-message/tree/master"
+            },
+            "time": "2016-08-06T14:39:51+00:00"
+        },
+        {
+            "name": "psr/log",
+            "version": "3.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/log.git",
+                "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001",
+                "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.0.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Log\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "https://www.php-fig.org/"
+                }
+            ],
+            "description": "Common interface for logging libraries",
+            "homepage": "https://github.com/php-fig/log",
+            "keywords": [
+                "log",
+                "psr",
+                "psr-3"
+            ],
+            "support": {
+                "source": "https://github.com/php-fig/log/tree/3.0.0"
+            },
+            "time": "2021-07-14T16:46:02+00:00"
+        },
+        {
+            "name": "psr/simple-cache",
+            "version": "3.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/simple-cache.git",
+                "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865",
+                "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.0.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Psr\\SimpleCache\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "https://www.php-fig.org/"
+                }
+            ],
+            "description": "Common interfaces for simple caching",
+            "keywords": [
+                "cache",
+                "caching",
+                "psr",
+                "psr-16",
+                "simple-cache"
+            ],
+            "support": {
+                "source": "https://github.com/php-fig/simple-cache/tree/3.0.0"
+            },
+            "time": "2021-10-29T13:26:27+00:00"
+        },
+        {
+            "name": "psy/psysh",
+            "version": "v0.11.4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/bobthecow/psysh.git",
+                "reference": "05c544b339b112226ad14803e1e5b09a61957454"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/bobthecow/psysh/zipball/05c544b339b112226ad14803e1e5b09a61957454",
+                "reference": "05c544b339b112226ad14803e1e5b09a61957454",
+                "shasum": ""
+            },
+            "require": {
+                "ext-json": "*",
+                "ext-tokenizer": "*",
+                "nikic/php-parser": "^4.0 || ^3.1",
+                "php": "^8.0 || ^7.0.8",
+                "symfony/console": "^6.0 || ^5.0 || ^4.0 || ^3.4",
+                "symfony/var-dumper": "^6.0 || ^5.0 || ^4.0 || ^3.4"
+            },
+            "conflict": {
+                "symfony/console": "4.4.37 || 5.3.14 || 5.3.15 || 5.4.3 || 5.4.4 || 6.0.3 || 6.0.4"
+            },
+            "require-dev": {
+                "bamarni/composer-bin-plugin": "^1.2"
+            },
+            "suggest": {
+                "ext-pcntl": "Enabling the PCNTL extension makes PsySH a lot happier :)",
+                "ext-pdo-sqlite": "The doc command requires SQLite to work.",
+                "ext-posix": "If you have PCNTL, you'll want the POSIX extension as well.",
+                "ext-readline": "Enables support for arrow-key history navigation, and showing and manipulating command history."
+            },
+            "bin": [
+                "bin/psysh"
+            ],
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "0.11.x-dev"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "src/functions.php"
+                ],
+                "psr-4": {
+                    "Psy\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Justin Hileman",
+                    "email": "justin@justinhileman.info",
+                    "homepage": "http://justinhileman.com"
+                }
+            ],
+            "description": "An interactive shell for modern PHP.",
+            "homepage": "http://psysh.org",
+            "keywords": [
+                "REPL",
+                "console",
+                "interactive",
+                "shell"
+            ],
+            "support": {
+                "issues": "https://github.com/bobthecow/psysh/issues",
+                "source": "https://github.com/bobthecow/psysh/tree/v0.11.4"
+            },
+            "time": "2022-05-06T12:49:14+00:00"
+        },
+        {
+            "name": "ralouphie/getallheaders",
+            "version": "3.0.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/ralouphie/getallheaders.git",
+                "reference": "120b605dfeb996808c31b6477290a714d356e822"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822",
+                "reference": "120b605dfeb996808c31b6477290a714d356e822",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.6"
+            },
+            "require-dev": {
+                "php-coveralls/php-coveralls": "^2.1",
+                "phpunit/phpunit": "^5 || ^6.5"
+            },
+            "type": "library",
+            "autoload": {
+                "files": [
+                    "src/getallheaders.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Ralph Khattar",
+                    "email": "ralph.khattar@gmail.com"
+                }
+            ],
+            "description": "A polyfill for getallheaders.",
+            "support": {
+                "issues": "https://github.com/ralouphie/getallheaders/issues",
+                "source": "https://github.com/ralouphie/getallheaders/tree/develop"
+            },
+            "time": "2019-03-08T08:55:37+00:00"
+        },
+        {
+            "name": "ramsey/collection",
+            "version": "1.2.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/ramsey/collection.git",
+                "reference": "cccc74ee5e328031b15640b51056ee8d3bb66c0a"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/ramsey/collection/zipball/cccc74ee5e328031b15640b51056ee8d3bb66c0a",
+                "reference": "cccc74ee5e328031b15640b51056ee8d3bb66c0a",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.3 || ^8",
+                "symfony/polyfill-php81": "^1.23"
+            },
+            "require-dev": {
+                "captainhook/captainhook": "^5.3",
+                "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0",
+                "ergebnis/composer-normalize": "^2.6",
+                "fakerphp/faker": "^1.5",
+                "hamcrest/hamcrest-php": "^2",
+                "jangregor/phpstan-prophecy": "^0.8",
+                "mockery/mockery": "^1.3",
+                "phpspec/prophecy-phpunit": "^2.0",
+                "phpstan/extension-installer": "^1",
+                "phpstan/phpstan": "^0.12.32",
+                "phpstan/phpstan-mockery": "^0.12.5",
+                "phpstan/phpstan-phpunit": "^0.12.11",
+                "phpunit/phpunit": "^8.5 || ^9",
+                "psy/psysh": "^0.10.4",
+                "slevomat/coding-standard": "^6.3",
+                "squizlabs/php_codesniffer": "^3.5",
+                "vimeo/psalm": "^4.4"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Ramsey\\Collection\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Ben Ramsey",
+                    "email": "ben@benramsey.com",
+                    "homepage": "https://benramsey.com"
+                }
+            ],
+            "description": "A PHP library for representing and manipulating collections.",
+            "keywords": [
+                "array",
+                "collection",
+                "hash",
+                "map",
+                "queue",
+                "set"
+            ],
+            "support": {
+                "issues": "https://github.com/ramsey/collection/issues",
+                "source": "https://github.com/ramsey/collection/tree/1.2.2"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/ramsey",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/ramsey/collection",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-10-10T03:01:02+00:00"
+        },
+        {
+            "name": "ramsey/uuid",
+            "version": "4.3.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/ramsey/uuid.git",
+                "reference": "8505afd4fea63b81a85d3b7b53ac3cb8dc347c28"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/ramsey/uuid/zipball/8505afd4fea63b81a85d3b7b53ac3cb8dc347c28",
+                "reference": "8505afd4fea63b81a85d3b7b53ac3cb8dc347c28",
+                "shasum": ""
+            },
+            "require": {
+                "brick/math": "^0.8 || ^0.9",
+                "ext-ctype": "*",
+                "ext-json": "*",
+                "php": "^8.0",
+                "ramsey/collection": "^1.0"
+            },
+            "replace": {
+                "rhumsaa/uuid": "self.version"
+            },
+            "require-dev": {
+                "captainhook/captainhook": "^5.10",
+                "captainhook/plugin-composer": "^5.3",
+                "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0",
+                "doctrine/annotations": "^1.8",
+                "ergebnis/composer-normalize": "^2.15",
+                "mockery/mockery": "^1.3",
+                "moontoast/math": "^1.1",
+                "paragonie/random-lib": "^2",
+                "php-mock/php-mock": "^2.2",
+                "php-mock/php-mock-mockery": "^1.3",
+                "php-parallel-lint/php-parallel-lint": "^1.1",
+                "phpbench/phpbench": "^1.0",
+                "phpstan/extension-installer": "^1.0",
+                "phpstan/phpstan": "^0.12",
+                "phpstan/phpstan-mockery": "^0.12",
+                "phpstan/phpstan-phpunit": "^0.12",
+                "phpunit/phpunit": "^8.5 || ^9",
+                "slevomat/coding-standard": "^7.0",
+                "squizlabs/php_codesniffer": "^3.5",
+                "vimeo/psalm": "^4.9"
+            },
+            "suggest": {
+                "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.",
+                "ext-ctype": "Enables faster processing of character classification using ctype functions.",
+                "ext-gmp": "Enables faster math with arbitrary-precision integers using GMP.",
+                "ext-uuid": "Enables the use of PeclUuidTimeGenerator and PeclUuidRandomGenerator.",
+                "paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter",
+                "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type."
+            },
+            "type": "library",
+            "extra": {
+                "captainhook": {
+                    "force-install": true
+                }
+            },
+            "autoload": {
+                "files": [
+                    "src/functions.php"
+                ],
+                "psr-4": {
+                    "Ramsey\\Uuid\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "A PHP library for generating and working with universally unique identifiers (UUIDs).",
+            "keywords": [
+                "guid",
+                "identifier",
+                "uuid"
+            ],
+            "support": {
+                "issues": "https://github.com/ramsey/uuid/issues",
+                "source": "https://github.com/ramsey/uuid/tree/4.3.1"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/ramsey",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/ramsey/uuid",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2022-03-27T21:42:02+00:00"
+        },
+        {
+            "name": "stella-maris/clock",
+            "version": "0.1.4",
+            "source": {
+                "type": "git",
+                "url": "https://gitlab.com/stella-maris/clock.git",
+                "reference": "8a0a967896df4c63417385dc69328a0aec84d9cf"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://gitlab.com/api/v4/projects/stella-maris%2Fclock/repository/archive.zip?sha=8a0a967896df4c63417385dc69328a0aec84d9cf",
+                "reference": "8a0a967896df4c63417385dc69328a0aec84d9cf",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.0|^8.0"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "StellaMaris\\Clock\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Andreas Heigl",
+                    "role": "Maintainer"
+                }
+            ],
+            "description": "A pre-release of the proposed PSR-20 Clock-Interface",
+            "homepage": "https://gitlab.com/stella-maris/clock",
+            "keywords": [
+                "clock",
+                "datetime",
+                "point in time",
+                "psr20"
+            ],
+            "support": {
+                "issues": "https://gitlab.com/stella-maris/clock/-/issues",
+                "source": "https://gitlab.com/stella-maris/clock/-/tree/0.1.4"
+            },
+            "time": "2022-04-17T14:12:26+00:00"
+        },
+        {
+            "name": "symfony/console",
+            "version": "v6.0.8",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/console.git",
+                "reference": "0d00aa289215353aa8746a31d101f8e60826285c"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/console/zipball/0d00aa289215353aa8746a31d101f8e60826285c",
+                "reference": "0d00aa289215353aa8746a31d101f8e60826285c",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.0.2",
+                "symfony/polyfill-mbstring": "~1.0",
+                "symfony/service-contracts": "^1.1|^2|^3",
+                "symfony/string": "^5.4|^6.0"
+            },
+            "conflict": {
+                "symfony/dependency-injection": "<5.4",
+                "symfony/dotenv": "<5.4",
+                "symfony/event-dispatcher": "<5.4",
+                "symfony/lock": "<5.4",
+                "symfony/process": "<5.4"
+            },
+            "provide": {
+                "psr/log-implementation": "1.0|2.0|3.0"
+            },
+            "require-dev": {
+                "psr/log": "^1|^2|^3",
+                "symfony/config": "^5.4|^6.0",
+                "symfony/dependency-injection": "^5.4|^6.0",
+                "symfony/event-dispatcher": "^5.4|^6.0",
+                "symfony/lock": "^5.4|^6.0",
+                "symfony/process": "^5.4|^6.0",
+                "symfony/var-dumper": "^5.4|^6.0"
+            },
+            "suggest": {
+                "psr/log": "For using the console logger",
+                "symfony/event-dispatcher": "",
+                "symfony/lock": "",
+                "symfony/process": ""
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\Console\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Eases the creation of beautiful and testable command line interfaces",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "cli",
+                "command line",
+                "console",
+                "terminal"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/console/tree/v6.0.8"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2022-04-20T15:01:42+00:00"
+        },
+        {
+            "name": "symfony/css-selector",
+            "version": "v6.0.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/css-selector.git",
+                "reference": "1955d595c12c111629cc814d3f2a2ff13580508a"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/css-selector/zipball/1955d595c12c111629cc814d3f2a2ff13580508a",
+                "reference": "1955d595c12c111629cc814d3f2a2ff13580508a",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.0.2"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\CssSelector\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Jean-François Simon",
+                    "email": "jeanfrancois.simon@sensiolabs.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Converts CSS selectors to XPath expressions",
+            "homepage": "https://symfony.com",
+            "support": {
+                "source": "https://github.com/symfony/css-selector/tree/v6.0.3"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2022-01-02T09:55:41+00:00"
+        },
+        {
+            "name": "symfony/deprecation-contracts",
+            "version": "v3.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/deprecation-contracts.git",
+                "reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/26954b3d62a6c5fd0ea8a2a00c0353a14978d05c",
+                "reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.0.2"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "3.0-dev"
+                },
+                "thanks": {
+                    "name": "symfony/contracts",
+                    "url": "https://github.com/symfony/contracts"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "function.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "A generic function and convention to trigger deprecation notices",
+            "homepage": "https://symfony.com",
+            "support": {
+                "source": "https://github.com/symfony/deprecation-contracts/tree/v3.0.1"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2022-01-02T09:55:41+00:00"
+        },
+        {
+            "name": "symfony/error-handler",
+            "version": "v6.0.8",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/error-handler.git",
+                "reference": "5e2795163acbd13b3cd46835c9f8f6c5d0a3a280"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/error-handler/zipball/5e2795163acbd13b3cd46835c9f8f6c5d0a3a280",
+                "reference": "5e2795163acbd13b3cd46835c9f8f6c5d0a3a280",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.0.2",
+                "psr/log": "^1|^2|^3",
+                "symfony/var-dumper": "^5.4|^6.0"
+            },
+            "require-dev": {
+                "symfony/deprecation-contracts": "^2.1|^3",
+                "symfony/http-kernel": "^5.4|^6.0",
+                "symfony/serializer": "^5.4|^6.0"
+            },
+            "bin": [
+                "Resources/bin/patch-type-declarations"
+            ],
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\ErrorHandler\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Provides tools to manage errors and ease debugging PHP code",
+            "homepage": "https://symfony.com",
+            "support": {
+                "source": "https://github.com/symfony/error-handler/tree/v6.0.8"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2022-04-12T16:11:42+00:00"
+        },
+        {
+            "name": "symfony/event-dispatcher",
+            "version": "v6.0.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/event-dispatcher.git",
+                "reference": "6472ea2dd415e925b90ca82be64b8bc6157f3934"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/6472ea2dd415e925b90ca82be64b8bc6157f3934",
+                "reference": "6472ea2dd415e925b90ca82be64b8bc6157f3934",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.0.2",
+                "symfony/event-dispatcher-contracts": "^2|^3"
+            },
+            "conflict": {
+                "symfony/dependency-injection": "<5.4"
+            },
+            "provide": {
+                "psr/event-dispatcher-implementation": "1.0",
+                "symfony/event-dispatcher-implementation": "2.0|3.0"
+            },
+            "require-dev": {
+                "psr/log": "^1|^2|^3",
+                "symfony/config": "^5.4|^6.0",
+                "symfony/dependency-injection": "^5.4|^6.0",
+                "symfony/error-handler": "^5.4|^6.0",
+                "symfony/expression-language": "^5.4|^6.0",
+                "symfony/http-foundation": "^5.4|^6.0",
+                "symfony/service-contracts": "^1.1|^2|^3",
+                "symfony/stopwatch": "^5.4|^6.0"
+            },
+            "suggest": {
+                "symfony/dependency-injection": "",
+                "symfony/http-kernel": ""
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\EventDispatcher\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them",
+            "homepage": "https://symfony.com",
+            "support": {
+                "source": "https://github.com/symfony/event-dispatcher/tree/v6.0.3"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2022-01-02T09:55:41+00:00"
+        },
+        {
+            "name": "symfony/event-dispatcher-contracts",
+            "version": "v3.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/event-dispatcher-contracts.git",
+                "reference": "7bc61cc2db649b4637d331240c5346dcc7708051"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/7bc61cc2db649b4637d331240c5346dcc7708051",
+                "reference": "7bc61cc2db649b4637d331240c5346dcc7708051",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.0.2",
+                "psr/event-dispatcher": "^1"
+            },
+            "suggest": {
+                "symfony/event-dispatcher-implementation": ""
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "3.0-dev"
+                },
+                "thanks": {
+                    "name": "symfony/contracts",
+                    "url": "https://github.com/symfony/contracts"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Contracts\\EventDispatcher\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Generic abstractions related to dispatching event",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "abstractions",
+                "contracts",
+                "decoupling",
+                "interfaces",
+                "interoperability",
+                "standards"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.0.1"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2022-01-02T09:55:41+00:00"
+        },
+        {
+            "name": "symfony/finder",
+            "version": "v6.0.8",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/finder.git",
+                "reference": "af7edab28d17caecd1f40a9219fc646ae751c21f"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/finder/zipball/af7edab28d17caecd1f40a9219fc646ae751c21f",
+                "reference": "af7edab28d17caecd1f40a9219fc646ae751c21f",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.0.2"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\Finder\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Finds files and directories via an intuitive fluent interface",
+            "homepage": "https://symfony.com",
+            "support": {
+                "source": "https://github.com/symfony/finder/tree/v6.0.8"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2022-04-15T08:07:58+00:00"
+        },
+        {
+            "name": "symfony/http-foundation",
+            "version": "v6.0.8",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/http-foundation.git",
+                "reference": "c9c86b02d7ef6f44f3154acc7de42831518afe7c"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/http-foundation/zipball/c9c86b02d7ef6f44f3154acc7de42831518afe7c",
+                "reference": "c9c86b02d7ef6f44f3154acc7de42831518afe7c",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.0.2",
+                "symfony/deprecation-contracts": "^2.1|^3",
+                "symfony/polyfill-mbstring": "~1.1"
+            },
+            "require-dev": {
+                "predis/predis": "~1.0",
+                "symfony/cache": "^5.4|^6.0",
+                "symfony/expression-language": "^5.4|^6.0",
+                "symfony/mime": "^5.4|^6.0"
+            },
+            "suggest": {
+                "symfony/mime": "To use the file extension guesser"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\HttpFoundation\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Defines an object-oriented layer for the HTTP specification",
+            "homepage": "https://symfony.com",
+            "support": {
+                "source": "https://github.com/symfony/http-foundation/tree/v6.0.8"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2022-04-22T08:18:02+00:00"
+        },
+        {
+            "name": "symfony/http-kernel",
+            "version": "v6.0.8",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/http-kernel.git",
+                "reference": "7aaf1cdc9cc2ad47e926f624efcb679883a39ca7"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/http-kernel/zipball/7aaf1cdc9cc2ad47e926f624efcb679883a39ca7",
+                "reference": "7aaf1cdc9cc2ad47e926f624efcb679883a39ca7",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.0.2",
+                "psr/log": "^1|^2|^3",
+                "symfony/error-handler": "^5.4|^6.0",
+                "symfony/event-dispatcher": "^5.4|^6.0",
+                "symfony/http-foundation": "^5.4|^6.0",
+                "symfony/polyfill-ctype": "^1.8"
+            },
+            "conflict": {
+                "symfony/browser-kit": "<5.4",
+                "symfony/cache": "<5.4",
+                "symfony/config": "<5.4",
+                "symfony/console": "<5.4",
+                "symfony/dependency-injection": "<5.4",
+                "symfony/doctrine-bridge": "<5.4",
+                "symfony/form": "<5.4",
+                "symfony/http-client": "<5.4",
+                "symfony/mailer": "<5.4",
+                "symfony/messenger": "<5.4",
+                "symfony/translation": "<5.4",
+                "symfony/twig-bridge": "<5.4",
+                "symfony/validator": "<5.4",
+                "twig/twig": "<2.13"
+            },
+            "provide": {
+                "psr/log-implementation": "1.0|2.0|3.0"
+            },
+            "require-dev": {
+                "psr/cache": "^1.0|^2.0|^3.0",
+                "symfony/browser-kit": "^5.4|^6.0",
+                "symfony/config": "^5.4|^6.0",
+                "symfony/console": "^5.4|^6.0",
+                "symfony/css-selector": "^5.4|^6.0",
+                "symfony/dependency-injection": "^5.4|^6.0",
+                "symfony/dom-crawler": "^5.4|^6.0",
+                "symfony/expression-language": "^5.4|^6.0",
+                "symfony/finder": "^5.4|^6.0",
+                "symfony/http-client-contracts": "^1.1|^2|^3",
+                "symfony/process": "^5.4|^6.0",
+                "symfony/routing": "^5.4|^6.0",
+                "symfony/stopwatch": "^5.4|^6.0",
+                "symfony/translation": "^5.4|^6.0",
+                "symfony/translation-contracts": "^1.1|^2|^3",
+                "twig/twig": "^2.13|^3.0.4"
+            },
+            "suggest": {
+                "symfony/browser-kit": "",
+                "symfony/config": "",
+                "symfony/console": "",
+                "symfony/dependency-injection": ""
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\HttpKernel\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Provides a structured process for converting a Request into a Response",
+            "homepage": "https://symfony.com",
+            "support": {
+                "source": "https://github.com/symfony/http-kernel/tree/v6.0.8"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2022-04-27T17:26:02+00:00"
+        },
+        {
+            "name": "symfony/mailer",
+            "version": "v6.0.8",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/mailer.git",
+                "reference": "706af6b3e99ebcbc639c9c664f5579aaa869409b"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/mailer/zipball/706af6b3e99ebcbc639c9c664f5579aaa869409b",
+                "reference": "706af6b3e99ebcbc639c9c664f5579aaa869409b",
+                "shasum": ""
+            },
+            "require": {
+                "egulias/email-validator": "^2.1.10|^3",
+                "php": ">=8.0.2",
+                "psr/event-dispatcher": "^1",
+                "psr/log": "^1|^2|^3",
+                "symfony/event-dispatcher": "^5.4|^6.0",
+                "symfony/mime": "^5.4|^6.0",
+                "symfony/service-contracts": "^1.1|^2|^3"
+            },
+            "conflict": {
+                "symfony/http-kernel": "<5.4"
+            },
+            "require-dev": {
+                "symfony/http-client-contracts": "^1.1|^2|^3",
+                "symfony/messenger": "^5.4|^6.0"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\Mailer\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Helps sending emails",
+            "homepage": "https://symfony.com",
+            "support": {
+                "source": "https://github.com/symfony/mailer/tree/v6.0.8"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2022-04-27T17:10:30+00:00"
+        },
+        {
+            "name": "symfony/mime",
+            "version": "v6.0.8",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/mime.git",
+                "reference": "c1701e88ad0ca49fc6ad6cdf360bc0e1209fb5e1"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/mime/zipball/c1701e88ad0ca49fc6ad6cdf360bc0e1209fb5e1",
+                "reference": "c1701e88ad0ca49fc6ad6cdf360bc0e1209fb5e1",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.0.2",
+                "symfony/polyfill-intl-idn": "^1.10",
+                "symfony/polyfill-mbstring": "^1.0"
+            },
+            "conflict": {
+                "egulias/email-validator": "~3.0.0",
+                "phpdocumentor/reflection-docblock": "<3.2.2",
+                "phpdocumentor/type-resolver": "<1.4.0",
+                "symfony/mailer": "<5.4"
+            },
+            "require-dev": {
+                "egulias/email-validator": "^2.1.10|^3.1",
+                "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0",
+                "symfony/dependency-injection": "^5.4|^6.0",
+                "symfony/property-access": "^5.4|^6.0",
+                "symfony/property-info": "^5.4|^6.0",
+                "symfony/serializer": "^5.4|^6.0"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\Mime\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Allows manipulating MIME messages",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "mime",
+                "mime-type"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/mime/tree/v6.0.8"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2022-04-12T16:11:42+00:00"
+        },
+        {
+            "name": "symfony/polyfill-ctype",
+            "version": "v1.25.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-ctype.git",
+                "reference": "30885182c981ab175d4d034db0f6f469898070ab"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/30885182c981ab175d4d034db0f6f469898070ab",
+                "reference": "30885182c981ab175d4d034db0f6f469898070ab",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.1"
+            },
+            "provide": {
+                "ext-ctype": "*"
+            },
+            "suggest": {
+                "ext-ctype": "For best performance"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "1.23-dev"
+                },
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "bootstrap.php"
+                ],
+                "psr-4": {
+                    "Symfony\\Polyfill\\Ctype\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Gert de Pagter",
+                    "email": "BackEndTea@gmail.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill for ctype functions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "ctype",
+                "polyfill",
+                "portable"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/polyfill-ctype/tree/v1.25.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-10-20T20:35:02+00:00"
+        },
+        {
+            "name": "symfony/polyfill-intl-grapheme",
+            "version": "v1.25.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-intl-grapheme.git",
+                "reference": "81b86b50cf841a64252b439e738e97f4a34e2783"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/81b86b50cf841a64252b439e738e97f4a34e2783",
+                "reference": "81b86b50cf841a64252b439e738e97f4a34e2783",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.1"
+            },
+            "suggest": {
+                "ext-intl": "For best performance"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "1.23-dev"
+                },
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "bootstrap.php"
+                ],
+                "psr-4": {
+                    "Symfony\\Polyfill\\Intl\\Grapheme\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill for intl's grapheme_* functions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "grapheme",
+                "intl",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.25.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-11-23T21:10:46+00:00"
+        },
+        {
+            "name": "symfony/polyfill-intl-idn",
+            "version": "v1.25.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-intl-idn.git",
+                "reference": "749045c69efb97c70d25d7463abba812e91f3a44"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/749045c69efb97c70d25d7463abba812e91f3a44",
+                "reference": "749045c69efb97c70d25d7463abba812e91f3a44",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.1",
+                "symfony/polyfill-intl-normalizer": "^1.10",
+                "symfony/polyfill-php72": "^1.10"
+            },
+            "suggest": {
+                "ext-intl": "For best performance"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "1.23-dev"
+                },
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "bootstrap.php"
+                ],
+                "psr-4": {
+                    "Symfony\\Polyfill\\Intl\\Idn\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Laurent Bassin",
+                    "email": "laurent@bassin.info"
+                },
+                {
+                    "name": "Trevor Rowbotham",
+                    "email": "trevor.rowbotham@pm.me"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "idn",
+                "intl",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.25.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-09-14T14:02:44+00:00"
+        },
+        {
+            "name": "symfony/polyfill-intl-normalizer",
+            "version": "v1.25.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-intl-normalizer.git",
+                "reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8590a5f561694770bdcd3f9b5c69dde6945028e8",
+                "reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.1"
+            },
+            "suggest": {
+                "ext-intl": "For best performance"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "1.23-dev"
+                },
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "bootstrap.php"
+                ],
+                "psr-4": {
+                    "Symfony\\Polyfill\\Intl\\Normalizer\\": ""
+                },
+                "classmap": [
+                    "Resources/stubs"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill for intl's Normalizer class and related functions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "intl",
+                "normalizer",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.25.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-02-19T12:13:01+00:00"
+        },
+        {
+            "name": "symfony/polyfill-mbstring",
+            "version": "v1.25.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-mbstring.git",
+                "reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/0abb51d2f102e00a4eefcf46ba7fec406d245825",
+                "reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.1"
+            },
+            "provide": {
+                "ext-mbstring": "*"
+            },
+            "suggest": {
+                "ext-mbstring": "For best performance"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "1.23-dev"
+                },
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "bootstrap.php"
+                ],
+                "psr-4": {
+                    "Symfony\\Polyfill\\Mbstring\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill for the Mbstring extension",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "mbstring",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.25.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-11-30T18:21:41+00:00"
+        },
+        {
+            "name": "symfony/polyfill-php72",
+            "version": "v1.25.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-php72.git",
+                "reference": "9a142215a36a3888e30d0a9eeea9766764e96976"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/9a142215a36a3888e30d0a9eeea9766764e96976",
+                "reference": "9a142215a36a3888e30d0a9eeea9766764e96976",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.1"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "1.23-dev"
+                },
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "bootstrap.php"
+                ],
+                "psr-4": {
+                    "Symfony\\Polyfill\\Php72\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/polyfill-php72/tree/v1.25.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-05-27T09:17:38+00:00"
+        },
+        {
+            "name": "symfony/polyfill-php80",
+            "version": "v1.25.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-php80.git",
+                "reference": "4407588e0d3f1f52efb65fbe92babe41f37fe50c"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/4407588e0d3f1f52efb65fbe92babe41f37fe50c",
+                "reference": "4407588e0d3f1f52efb65fbe92babe41f37fe50c",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.1"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "1.23-dev"
+                },
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "bootstrap.php"
+                ],
+                "psr-4": {
+                    "Symfony\\Polyfill\\Php80\\": ""
+                },
+                "classmap": [
+                    "Resources/stubs"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Ion Bazan",
+                    "email": "ion.bazan@gmail.com"
+                },
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/polyfill-php80/tree/v1.25.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2022-03-04T08:16:47+00:00"
+        },
+        {
+            "name": "symfony/polyfill-php81",
+            "version": "v1.25.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-php81.git",
+                "reference": "5de4ba2d41b15f9bd0e19b2ab9674135813ec98f"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/5de4ba2d41b15f9bd0e19b2ab9674135813ec98f",
+                "reference": "5de4ba2d41b15f9bd0e19b2ab9674135813ec98f",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.1"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "1.23-dev"
+                },
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "bootstrap.php"
+                ],
+                "psr-4": {
+                    "Symfony\\Polyfill\\Php81\\": ""
+                },
+                "classmap": [
+                    "Resources/stubs"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/polyfill-php81/tree/v1.25.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-09-13T13:58:11+00:00"
+        },
+        {
+            "name": "symfony/process",
+            "version": "v6.0.8",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/process.git",
+                "reference": "d074154ea8b1443a96391f6e39f9e547b2dd01b9"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/process/zipball/d074154ea8b1443a96391f6e39f9e547b2dd01b9",
+                "reference": "d074154ea8b1443a96391f6e39f9e547b2dd01b9",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.0.2"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\Process\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Executes commands in sub-processes",
+            "homepage": "https://symfony.com",
+            "support": {
+                "source": "https://github.com/symfony/process/tree/v6.0.8"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2022-04-12T16:11:42+00:00"
+        },
+        {
+            "name": "symfony/routing",
+            "version": "v6.0.8",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/routing.git",
+                "reference": "74c40c9fc334acc601a32fcf4274e74fb3bac11e"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/routing/zipball/74c40c9fc334acc601a32fcf4274e74fb3bac11e",
+                "reference": "74c40c9fc334acc601a32fcf4274e74fb3bac11e",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.0.2"
+            },
+            "conflict": {
+                "doctrine/annotations": "<1.12",
+                "symfony/config": "<5.4",
+                "symfony/dependency-injection": "<5.4",
+                "symfony/yaml": "<5.4"
+            },
+            "require-dev": {
+                "doctrine/annotations": "^1.12",
+                "psr/log": "^1|^2|^3",
+                "symfony/config": "^5.4|^6.0",
+                "symfony/dependency-injection": "^5.4|^6.0",
+                "symfony/expression-language": "^5.4|^6.0",
+                "symfony/http-foundation": "^5.4|^6.0",
+                "symfony/yaml": "^5.4|^6.0"
+            },
+            "suggest": {
+                "symfony/config": "For using the all-in-one router or any loader",
+                "symfony/expression-language": "For using expression matching",
+                "symfony/http-foundation": "For using a Symfony Request object",
+                "symfony/yaml": "For using the YAML loader"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\Routing\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Maps an HTTP request to a set of configuration variables",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "router",
+                "routing",
+                "uri",
+                "url"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/routing/tree/v6.0.8"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2022-04-22T08:18:02+00:00"
+        },
+        {
+            "name": "symfony/service-contracts",
+            "version": "v3.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/service-contracts.git",
+                "reference": "e517458f278c2131ca9f262f8fbaf01410f2c65c"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/service-contracts/zipball/e517458f278c2131ca9f262f8fbaf01410f2c65c",
+                "reference": "e517458f278c2131ca9f262f8fbaf01410f2c65c",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.0.2",
+                "psr/container": "^2.0"
+            },
+            "conflict": {
+                "ext-psr": "<1.1|>=2"
+            },
+            "suggest": {
+                "symfony/service-implementation": ""
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "3.0-dev"
+                },
+                "thanks": {
+                    "name": "symfony/contracts",
+                    "url": "https://github.com/symfony/contracts"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Contracts\\Service\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Generic abstractions related to writing services",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "abstractions",
+                "contracts",
+                "decoupling",
+                "interfaces",
+                "interoperability",
+                "standards"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/service-contracts/tree/v3.0.1"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2022-03-13T20:10:05+00:00"
+        },
+        {
+            "name": "symfony/string",
+            "version": "v6.0.8",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/string.git",
+                "reference": "ac0aa5c2282e0de624c175b68d13f2c8f2e2649d"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/string/zipball/ac0aa5c2282e0de624c175b68d13f2c8f2e2649d",
+                "reference": "ac0aa5c2282e0de624c175b68d13f2c8f2e2649d",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.0.2",
+                "symfony/polyfill-ctype": "~1.8",
+                "symfony/polyfill-intl-grapheme": "~1.0",
+                "symfony/polyfill-intl-normalizer": "~1.0",
+                "symfony/polyfill-mbstring": "~1.0"
+            },
+            "conflict": {
+                "symfony/translation-contracts": "<2.0"
+            },
+            "require-dev": {
+                "symfony/error-handler": "^5.4|^6.0",
+                "symfony/http-client": "^5.4|^6.0",
+                "symfony/translation-contracts": "^2.0|^3.0",
+                "symfony/var-exporter": "^5.4|^6.0"
+            },
+            "type": "library",
+            "autoload": {
+                "files": [
+                    "Resources/functions.php"
+                ],
+                "psr-4": {
+                    "Symfony\\Component\\String\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "grapheme",
+                "i18n",
+                "string",
+                "unicode",
+                "utf-8",
+                "utf8"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/string/tree/v6.0.8"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2022-04-22T08:18:02+00:00"
+        },
+        {
+            "name": "symfony/translation",
+            "version": "v6.0.8",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/translation.git",
+                "reference": "3d38cf8f8834148c4457681d539bc204de701501"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/translation/zipball/3d38cf8f8834148c4457681d539bc204de701501",
+                "reference": "3d38cf8f8834148c4457681d539bc204de701501",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.0.2",
+                "symfony/polyfill-mbstring": "~1.0",
+                "symfony/translation-contracts": "^2.3|^3.0"
+            },
+            "conflict": {
+                "symfony/config": "<5.4",
+                "symfony/console": "<5.4",
+                "symfony/dependency-injection": "<5.4",
+                "symfony/http-kernel": "<5.4",
+                "symfony/twig-bundle": "<5.4",
+                "symfony/yaml": "<5.4"
+            },
+            "provide": {
+                "symfony/translation-implementation": "2.3|3.0"
+            },
+            "require-dev": {
+                "psr/log": "^1|^2|^3",
+                "symfony/config": "^5.4|^6.0",
+                "symfony/console": "^5.4|^6.0",
+                "symfony/dependency-injection": "^5.4|^6.0",
+                "symfony/finder": "^5.4|^6.0",
+                "symfony/http-client-contracts": "^1.1|^2.0|^3.0",
+                "symfony/http-kernel": "^5.4|^6.0",
+                "symfony/intl": "^5.4|^6.0",
+                "symfony/polyfill-intl-icu": "^1.21",
+                "symfony/service-contracts": "^1.1.2|^2|^3",
+                "symfony/yaml": "^5.4|^6.0"
+            },
+            "suggest": {
+                "psr/log-implementation": "To use logging capability in translator",
+                "symfony/config": "",
+                "symfony/yaml": ""
+            },
+            "type": "library",
+            "autoload": {
+                "files": [
+                    "Resources/functions.php"
+                ],
+                "psr-4": {
+                    "Symfony\\Component\\Translation\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Provides tools to internationalize your application",
+            "homepage": "https://symfony.com",
+            "support": {
+                "source": "https://github.com/symfony/translation/tree/v6.0.8"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2022-04-22T08:18:02+00:00"
+        },
+        {
+            "name": "symfony/translation-contracts",
+            "version": "v3.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/translation-contracts.git",
+                "reference": "c4183fc3ef0f0510893cbeedc7718fb5cafc9ac9"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/c4183fc3ef0f0510893cbeedc7718fb5cafc9ac9",
+                "reference": "c4183fc3ef0f0510893cbeedc7718fb5cafc9ac9",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.0.2"
+            },
+            "suggest": {
+                "symfony/translation-implementation": ""
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "3.0-dev"
+                },
+                "thanks": {
+                    "name": "symfony/contracts",
+                    "url": "https://github.com/symfony/contracts"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Contracts\\Translation\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Generic abstractions related to translation",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "abstractions",
+                "contracts",
+                "decoupling",
+                "interfaces",
+                "interoperability",
+                "standards"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/translation-contracts/tree/v3.0.1"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2022-01-02T09:55:41+00:00"
+        },
+        {
+            "name": "symfony/var-dumper",
+            "version": "v6.0.8",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/var-dumper.git",
+                "reference": "fa61dfb4bd3068df2492013dc65f3190e9f550c0"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/var-dumper/zipball/fa61dfb4bd3068df2492013dc65f3190e9f550c0",
+                "reference": "fa61dfb4bd3068df2492013dc65f3190e9f550c0",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.0.2",
+                "symfony/polyfill-mbstring": "~1.0"
+            },
+            "conflict": {
+                "phpunit/phpunit": "<5.4.3",
+                "symfony/console": "<5.4"
+            },
+            "require-dev": {
+                "ext-iconv": "*",
+                "symfony/console": "^5.4|^6.0",
+                "symfony/process": "^5.4|^6.0",
+                "symfony/uid": "^5.4|^6.0",
+                "twig/twig": "^2.13|^3.0.4"
+            },
+            "suggest": {
+                "ext-iconv": "To convert non-UTF-8 strings to UTF-8 (or symfony/polyfill-iconv in case ext-iconv cannot be used).",
+                "ext-intl": "To show region name in time zone dump",
+                "symfony/console": "To use the ServerDumpCommand and/or the bin/var-dump-server script"
+            },
+            "bin": [
+                "Resources/bin/var-dump-server"
+            ],
+            "type": "library",
+            "autoload": {
+                "files": [
+                    "Resources/functions/dump.php"
+                ],
+                "psr-4": {
+                    "Symfony\\Component\\VarDumper\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Provides mechanisms for walking through any arbitrary PHP variable",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "debug",
+                "dump"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/var-dumper/tree/v6.0.8"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2022-04-26T13:22:23+00:00"
+        },
+        {
+            "name": "tijsverkoyen/css-to-inline-styles",
+            "version": "2.2.4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/tijsverkoyen/CssToInlineStyles.git",
+                "reference": "da444caae6aca7a19c0c140f68c6182e337d5b1c"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/da444caae6aca7a19c0c140f68c6182e337d5b1c",
+                "reference": "da444caae6aca7a19c0c140f68c6182e337d5b1c",
+                "shasum": ""
+            },
+            "require": {
+                "ext-dom": "*",
+                "ext-libxml": "*",
+                "php": "^5.5 || ^7.0 || ^8.0",
+                "symfony/css-selector": "^2.7 || ^3.0 || ^4.0 || ^5.0 || ^6.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0 || ^7.5 || ^8.5.21 || ^9.5.10"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.2.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "TijsVerkoyen\\CssToInlineStyles\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Tijs Verkoyen",
+                    "email": "css_to_inline_styles@verkoyen.eu",
+                    "role": "Developer"
+                }
+            ],
+            "description": "CssToInlineStyles is a class that enables you to convert HTML-pages/files into HTML-pages/files with inline styles. This is very useful when you're sending emails.",
+            "homepage": "https://github.com/tijsverkoyen/CssToInlineStyles",
+            "support": {
+                "issues": "https://github.com/tijsverkoyen/CssToInlineStyles/issues",
+                "source": "https://github.com/tijsverkoyen/CssToInlineStyles/tree/2.2.4"
+            },
+            "time": "2021-12-08T09:12:39+00:00"
+        },
+        {
+            "name": "vlucas/phpdotenv",
+            "version": "v5.4.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/vlucas/phpdotenv.git",
+                "reference": "264dce589e7ce37a7ba99cb901eed8249fbec92f"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/264dce589e7ce37a7ba99cb901eed8249fbec92f",
+                "reference": "264dce589e7ce37a7ba99cb901eed8249fbec92f",
+                "shasum": ""
+            },
+            "require": {
+                "ext-pcre": "*",
+                "graham-campbell/result-type": "^1.0.2",
+                "php": "^7.1.3 || ^8.0",
+                "phpoption/phpoption": "^1.8",
+                "symfony/polyfill-ctype": "^1.23",
+                "symfony/polyfill-mbstring": "^1.23.1",
+                "symfony/polyfill-php80": "^1.23.1"
+            },
+            "require-dev": {
+                "bamarni/composer-bin-plugin": "^1.4.1",
+                "ext-filter": "*",
+                "phpunit/phpunit": "^7.5.20 || ^8.5.21 || ^9.5.10"
+            },
+            "suggest": {
+                "ext-filter": "Required to use the boolean validator."
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "5.4-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Dotenv\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Graham Campbell",
+                    "email": "hello@gjcampbell.co.uk",
+                    "homepage": "https://github.com/GrahamCampbell"
+                },
+                {
+                    "name": "Vance Lucas",
+                    "email": "vance@vancelucas.com",
+                    "homepage": "https://github.com/vlucas"
+                }
+            ],
+            "description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.",
+            "keywords": [
+                "dotenv",
+                "env",
+                "environment"
+            ],
+            "support": {
+                "issues": "https://github.com/vlucas/phpdotenv/issues",
+                "source": "https://github.com/vlucas/phpdotenv/tree/v5.4.1"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/GrahamCampbell",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-12-12T23:22:04+00:00"
+        },
+        {
+            "name": "voku/portable-ascii",
+            "version": "2.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/voku/portable-ascii.git",
+                "reference": "b56450eed252f6801410d810c8e1727224ae0743"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/voku/portable-ascii/zipball/b56450eed252f6801410d810c8e1727224ae0743",
+                "reference": "b56450eed252f6801410d810c8e1727224ae0743",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.0.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "~6.0 || ~7.0 || ~9.0"
+            },
+            "suggest": {
+                "ext-intl": "Use Intl for transliterator_transliterate() support"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "voku\\": "src/voku/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Lars Moelleken",
+                    "homepage": "http://www.moelleken.org/"
+                }
+            ],
+            "description": "Portable ASCII library - performance optimized (ascii) string functions for php.",
+            "homepage": "https://github.com/voku/portable-ascii",
+            "keywords": [
+                "ascii",
+                "clean",
+                "php"
+            ],
+            "support": {
+                "issues": "https://github.com/voku/portable-ascii/issues",
+                "source": "https://github.com/voku/portable-ascii/tree/2.0.1"
+            },
+            "funding": [
+                {
+                    "url": "https://www.paypal.me/moelleken",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/voku",
+                    "type": "github"
+                },
+                {
+                    "url": "https://opencollective.com/portable-ascii",
+                    "type": "open_collective"
+                },
+                {
+                    "url": "https://www.patreon.com/voku",
+                    "type": "patreon"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/voku/portable-ascii",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2022-03-08T17:03:00+00:00"
+        },
+        {
+            "name": "webmozart/assert",
+            "version": "1.10.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/webmozarts/assert.git",
+                "reference": "6964c76c7804814a842473e0c8fd15bab0f18e25"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/webmozarts/assert/zipball/6964c76c7804814a842473e0c8fd15bab0f18e25",
+                "reference": "6964c76c7804814a842473e0c8fd15bab0f18e25",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.2 || ^8.0",
+                "symfony/polyfill-ctype": "^1.8"
+            },
+            "conflict": {
+                "phpstan/phpstan": "<0.12.20",
+                "vimeo/psalm": "<4.6.1 || 4.6.2"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^8.5.13"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.10-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Webmozart\\Assert\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Bernhard Schussek",
+                    "email": "bschussek@gmail.com"
+                }
+            ],
+            "description": "Assertions to validate method input/output with nice error messages.",
+            "keywords": [
+                "assert",
+                "check",
+                "validate"
+            ],
+            "support": {
+                "issues": "https://github.com/webmozarts/assert/issues",
+                "source": "https://github.com/webmozarts/assert/tree/1.10.0"
+            },
+            "time": "2021-03-09T10:59:23+00:00"
+        }
+    ],
+    "packages-dev": [
+        {
+            "name": "doctrine/instantiator",
+            "version": "1.4.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/doctrine/instantiator.git",
+                "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/doctrine/instantiator/zipball/10dcfce151b967d20fde1b34ae6640712c3891bc",
+                "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.1 || ^8.0"
+            },
+            "require-dev": {
+                "doctrine/coding-standard": "^9",
+                "ext-pdo": "*",
+                "ext-phar": "*",
+                "phpbench/phpbench": "^0.16 || ^1",
+                "phpstan/phpstan": "^1.4",
+                "phpstan/phpstan-phpunit": "^1",
+                "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
+                "vimeo/psalm": "^4.22"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Marco Pivetta",
+                    "email": "ocramius@gmail.com",
+                    "homepage": "https://ocramius.github.io/"
+                }
+            ],
+            "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors",
+            "homepage": "https://www.doctrine-project.org/projects/instantiator.html",
+            "keywords": [
+                "constructor",
+                "instantiate"
+            ],
+            "support": {
+                "issues": "https://github.com/doctrine/instantiator/issues",
+                "source": "https://github.com/doctrine/instantiator/tree/1.4.1"
+            },
+            "funding": [
+                {
+                    "url": "https://www.doctrine-project.org/sponsorship.html",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://www.patreon.com/phpdoctrine",
+                    "type": "patreon"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2022-03-03T08:28:38+00:00"
+        },
+        {
+            "name": "facade/ignition-contracts",
+            "version": "1.0.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/facade/ignition-contracts.git",
+                "reference": "3c921a1cdba35b68a7f0ccffc6dffc1995b18267"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/facade/ignition-contracts/zipball/3c921a1cdba35b68a7f0ccffc6dffc1995b18267",
+                "reference": "3c921a1cdba35b68a7f0ccffc6dffc1995b18267",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.3|^8.0"
+            },
+            "require-dev": {
+                "friendsofphp/php-cs-fixer": "^v2.15.8",
+                "phpunit/phpunit": "^9.3.11",
+                "vimeo/psalm": "^3.17.1"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Facade\\IgnitionContracts\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Freek Van der Herten",
+                    "email": "freek@spatie.be",
+                    "homepage": "https://flareapp.io",
+                    "role": "Developer"
+                }
+            ],
+            "description": "Solution contracts for Ignition",
+            "homepage": "https://github.com/facade/ignition-contracts",
+            "keywords": [
+                "contracts",
+                "flare",
+                "ignition"
+            ],
+            "support": {
+                "issues": "https://github.com/facade/ignition-contracts/issues",
+                "source": "https://github.com/facade/ignition-contracts/tree/1.0.2"
+            },
+            "time": "2020-10-16T08:27:54+00:00"
+        },
+        {
+            "name": "fakerphp/faker",
+            "version": "v1.19.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/FakerPHP/Faker.git",
+                "reference": "d7f08a622b3346766325488aa32ddc93ccdecc75"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/d7f08a622b3346766325488aa32ddc93ccdecc75",
+                "reference": "d7f08a622b3346766325488aa32ddc93ccdecc75",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.1 || ^8.0",
+                "psr/container": "^1.0 || ^2.0",
+                "symfony/deprecation-contracts": "^2.2 || ^3.0"
+            },
+            "conflict": {
+                "fzaninotto/faker": "*"
+            },
+            "require-dev": {
+                "bamarni/composer-bin-plugin": "^1.4.1",
+                "doctrine/persistence": "^1.3 || ^2.0",
+                "ext-intl": "*",
+                "symfony/phpunit-bridge": "^4.4 || ^5.2"
+            },
+            "suggest": {
+                "doctrine/orm": "Required to use Faker\\ORM\\Doctrine",
+                "ext-curl": "Required by Faker\\Provider\\Image to download images.",
+                "ext-dom": "Required by Faker\\Provider\\HtmlLorem for generating random HTML.",
+                "ext-iconv": "Required by Faker\\Provider\\ru_RU\\Text::realText() for generating real Russian text.",
+                "ext-mbstring": "Required for multibyte Unicode string functionality."
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "v1.19-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Faker\\": "src/Faker/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "François Zaninotto"
+                }
+            ],
+            "description": "Faker is a PHP library that generates fake data for you.",
+            "keywords": [
+                "data",
+                "faker",
+                "fixtures"
+            ],
+            "support": {
+                "issues": "https://github.com/FakerPHP/Faker/issues",
+                "source": "https://github.com/FakerPHP/Faker/tree/v1.19.0"
+            },
+            "time": "2022-02-02T17:38:57+00:00"
+        },
+        {
+            "name": "filp/whoops",
+            "version": "2.14.5",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/filp/whoops.git",
+                "reference": "a63e5e8f26ebbebf8ed3c5c691637325512eb0dc"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/filp/whoops/zipball/a63e5e8f26ebbebf8ed3c5c691637325512eb0dc",
+                "reference": "a63e5e8f26ebbebf8ed3c5c691637325512eb0dc",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.5.9 || ^7.0 || ^8.0",
+                "psr/log": "^1.0.1 || ^2.0 || ^3.0"
+            },
+            "require-dev": {
+                "mockery/mockery": "^0.9 || ^1.0",
+                "phpunit/phpunit": "^4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.3",
+                "symfony/var-dumper": "^2.6 || ^3.0 || ^4.0 || ^5.0"
+            },
+            "suggest": {
+                "symfony/var-dumper": "Pretty print complex values better with var-dumper available",
+                "whoops/soap": "Formats errors as SOAP responses"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.7-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Whoops\\": "src/Whoops/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Filipe Dobreira",
+                    "homepage": "https://github.com/filp",
+                    "role": "Developer"
+                }
+            ],
+            "description": "php error handling for cool kids",
+            "homepage": "https://filp.github.io/whoops/",
+            "keywords": [
+                "error",
+                "exception",
+                "handling",
+                "library",
+                "throwable",
+                "whoops"
+            ],
+            "support": {
+                "issues": "https://github.com/filp/whoops/issues",
+                "source": "https://github.com/filp/whoops/tree/2.14.5"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/denis-sokolov",
+                    "type": "github"
+                }
+            ],
+            "time": "2022-01-07T12:00:00+00:00"
+        },
+        {
+            "name": "hamcrest/hamcrest-php",
+            "version": "v2.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/hamcrest/hamcrest-php.git",
+                "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/8c3d0a3f6af734494ad8f6fbbee0ba92422859f3",
+                "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.3|^7.0|^8.0"
+            },
+            "replace": {
+                "cordoval/hamcrest-php": "*",
+                "davedevelopment/hamcrest-php": "*",
+                "kodova/hamcrest-php": "*"
+            },
+            "require-dev": {
+                "phpunit/php-file-iterator": "^1.4 || ^2.0",
+                "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.1-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "hamcrest"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "description": "This is the PHP port of Hamcrest Matchers",
+            "keywords": [
+                "test"
+            ],
+            "support": {
+                "issues": "https://github.com/hamcrest/hamcrest-php/issues",
+                "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.0.1"
+            },
+            "time": "2020-07-09T08:09:16+00:00"
+        },
+        {
+            "name": "hashids/hashids",
+            "version": "4.1.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/vinkla/hashids.git",
+                "reference": "8cab111f78e0bd9c76953b082919fc9e251761be"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/vinkla/hashids/zipball/8cab111f78e0bd9c76953b082919fc9e251761be",
+                "reference": "8cab111f78e0bd9c76953b082919fc9e251761be",
+                "shasum": ""
+            },
+            "require": {
+                "ext-mbstring": "*",
+                "php": "^7.2 || ^8.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^8.0 || ^9.4",
+                "squizlabs/php_codesniffer": "^3.5"
+            },
+            "suggest": {
+                "ext-bcmath": "Required to use BC Math arbitrary precision mathematics (*).",
+                "ext-gmp": "Required to use GNU multiple precision mathematics (*)."
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "4.1-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Hashids\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Ivan Akimov",
+                    "email": "ivan@barreleye.com"
+                },
+                {
+                    "name": "Vincent Klaiber",
+                    "email": "hello@doubledip.se"
+                }
+            ],
+            "description": "Generate short, unique, non-sequential ids (like YouTube and Bitly) from numbers",
+            "homepage": "https://hashids.org/php",
+            "keywords": [
+                "bitly",
+                "decode",
+                "encode",
+                "hash",
+                "hashid",
+                "hashids",
+                "ids",
+                "obfuscate",
+                "youtube"
+            ],
+            "support": {
+                "issues": "https://github.com/vinkla/hashids/issues",
+                "source": "https://github.com/vinkla/hashids/tree/4.1.0"
+            },
+            "time": "2020-11-26T19:24:33+00:00"
+        },
+        {
+            "name": "laravel/sail",
+            "version": "v1.14.6",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/laravel/sail.git",
+                "reference": "f00f3a8f83e71436d473cda7700ae7b4b68d26cc"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/laravel/sail/zipball/f00f3a8f83e71436d473cda7700ae7b4b68d26cc",
+                "reference": "f00f3a8f83e71436d473cda7700ae7b4b68d26cc",
+                "shasum": ""
+            },
+            "require": {
+                "illuminate/console": "^8.0|^9.0",
+                "illuminate/contracts": "^8.0|^9.0",
+                "illuminate/support": "^8.0|^9.0",
+                "php": "^7.3|^8.0"
+            },
+            "bin": [
+                "bin/sail"
+            ],
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.x-dev"
+                },
+                "laravel": {
+                    "providers": [
+                        "Laravel\\Sail\\SailServiceProvider"
+                    ]
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Laravel\\Sail\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Taylor Otwell",
+                    "email": "taylor@laravel.com"
+                }
+            ],
+            "description": "Docker files for running a basic Laravel application.",
+            "keywords": [
+                "docker",
+                "laravel"
+            ],
+            "support": {
+                "issues": "https://github.com/laravel/sail/issues",
+                "source": "https://github.com/laravel/sail"
+            },
+            "time": "2022-05-18T15:56:59+00:00"
+        },
+        {
+            "name": "mockery/mockery",
+            "version": "1.5.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/mockery/mockery.git",
+                "reference": "c10a5f6e06fc2470ab1822fa13fa2a7380f8fbac"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/mockery/mockery/zipball/c10a5f6e06fc2470ab1822fa13fa2a7380f8fbac",
+                "reference": "c10a5f6e06fc2470ab1822fa13fa2a7380f8fbac",
+                "shasum": ""
+            },
+            "require": {
+                "hamcrest/hamcrest-php": "^2.0.1",
+                "lib-pcre": ">=7.0",
+                "php": "^7.3 || ^8.0"
+            },
+            "conflict": {
+                "phpunit/phpunit": "<8.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^8.5 || ^9.3"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.4.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-0": {
+                    "Mockery": "library/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Pádraic Brady",
+                    "email": "padraic.brady@gmail.com",
+                    "homepage": "http://blog.astrumfutura.com"
+                },
+                {
+                    "name": "Dave Marshall",
+                    "email": "dave.marshall@atstsolutions.co.uk",
+                    "homepage": "http://davedevelopment.co.uk"
+                }
+            ],
+            "description": "Mockery is a simple yet flexible PHP mock object framework",
+            "homepage": "https://github.com/mockery/mockery",
+            "keywords": [
+                "BDD",
+                "TDD",
+                "library",
+                "mock",
+                "mock objects",
+                "mockery",
+                "stub",
+                "test",
+                "test double",
+                "testing"
+            ],
+            "support": {
+                "issues": "https://github.com/mockery/mockery/issues",
+                "source": "https://github.com/mockery/mockery/tree/1.5.0"
+            },
+            "time": "2022-01-20T13:18:17+00:00"
+        },
+        {
+            "name": "myclabs/deep-copy",
+            "version": "1.11.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/myclabs/DeepCopy.git",
+                "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/14daed4296fae74d9e3201d2c4925d1acb7aa614",
+                "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.1 || ^8.0"
+            },
+            "conflict": {
+                "doctrine/collections": "<1.6.8",
+                "doctrine/common": "<2.13.3 || >=3,<3.2.2"
+            },
+            "require-dev": {
+                "doctrine/collections": "^1.6.8",
+                "doctrine/common": "^2.13.3 || ^3.2.2",
+                "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13"
+            },
+            "type": "library",
+            "autoload": {
+                "files": [
+                    "src/DeepCopy/deep_copy.php"
+                ],
+                "psr-4": {
+                    "DeepCopy\\": "src/DeepCopy/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "Create deep copies (clones) of your objects",
+            "keywords": [
+                "clone",
+                "copy",
+                "duplicate",
+                "object",
+                "object graph"
+            ],
+            "support": {
+                "issues": "https://github.com/myclabs/DeepCopy/issues",
+                "source": "https://github.com/myclabs/DeepCopy/tree/1.11.0"
+            },
+            "funding": [
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2022-03-03T13:19:32+00:00"
+        },
+        {
+            "name": "nunomaduro/collision",
+            "version": "v6.2.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/nunomaduro/collision.git",
+                "reference": "c379636dc50e829edb3a8bcb944a01aa1aed8f25"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/nunomaduro/collision/zipball/c379636dc50e829edb3a8bcb944a01aa1aed8f25",
+                "reference": "c379636dc50e829edb3a8bcb944a01aa1aed8f25",
+                "shasum": ""
+            },
+            "require": {
+                "facade/ignition-contracts": "^1.0.2",
+                "filp/whoops": "^2.14.5",
+                "php": "^8.0.0",
+                "symfony/console": "^6.0.2"
+            },
+            "require-dev": {
+                "brianium/paratest": "^6.4.1",
+                "laravel/framework": "^9.7",
+                "nunomaduro/larastan": "^1.0.2",
+                "nunomaduro/mock-final-classes": "^1.1.0",
+                "orchestra/testbench": "^7.3.0",
+                "phpunit/phpunit": "^9.5.11"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-develop": "6.x-dev"
+                },
+                "laravel": {
+                    "providers": [
+                        "NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider"
+                    ]
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "NunoMaduro\\Collision\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nuno Maduro",
+                    "email": "enunomaduro@gmail.com"
+                }
+            ],
+            "description": "Cli error handling for console/command-line PHP applications.",
+            "keywords": [
+                "artisan",
+                "cli",
+                "command-line",
+                "console",
+                "error",
+                "handling",
+                "laravel",
+                "laravel-zero",
+                "php",
+                "symfony"
+            ],
+            "support": {
+                "issues": "https://github.com/nunomaduro/collision/issues",
+                "source": "https://github.com/nunomaduro/collision"
+            },
+            "funding": [
+                {
+                    "url": "https://www.paypal.com/paypalme/enunomaduro",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/nunomaduro",
+                    "type": "github"
+                },
+                {
+                    "url": "https://www.patreon.com/nunomaduro",
+                    "type": "patreon"
+                }
+            ],
+            "time": "2022-04-05T15:31:38+00:00"
+        },
+        {
+            "name": "phar-io/manifest",
+            "version": "2.0.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/phar-io/manifest.git",
+                "reference": "97803eca37d319dfa7826cc2437fc020857acb53"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53",
+                "reference": "97803eca37d319dfa7826cc2437fc020857acb53",
+                "shasum": ""
+            },
+            "require": {
+                "ext-dom": "*",
+                "ext-phar": "*",
+                "ext-xmlwriter": "*",
+                "phar-io/version": "^3.0.1",
+                "php": "^7.2 || ^8.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.0.x-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Arne Blankerts",
+                    "email": "arne@blankerts.de",
+                    "role": "Developer"
+                },
+                {
+                    "name": "Sebastian Heuer",
+                    "email": "sebastian@phpeople.de",
+                    "role": "Developer"
+                },
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "Developer"
+                }
+            ],
+            "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)",
+            "support": {
+                "issues": "https://github.com/phar-io/manifest/issues",
+                "source": "https://github.com/phar-io/manifest/tree/2.0.3"
+            },
+            "time": "2021-07-20T11:28:43+00:00"
+        },
+        {
+            "name": "phar-io/version",
+            "version": "3.2.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/phar-io/version.git",
+                "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74",
+                "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.2 || ^8.0"
+            },
+            "type": "library",
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Arne Blankerts",
+                    "email": "arne@blankerts.de",
+                    "role": "Developer"
+                },
+                {
+                    "name": "Sebastian Heuer",
+                    "email": "sebastian@phpeople.de",
+                    "role": "Developer"
+                },
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "Developer"
+                }
+            ],
+            "description": "Library for handling version information and constraints",
+            "support": {
+                "issues": "https://github.com/phar-io/version/issues",
+                "source": "https://github.com/phar-io/version/tree/3.2.1"
+            },
+            "time": "2022-02-21T01:04:05+00:00"
+        },
+        {
+            "name": "phpdocumentor/reflection-common",
+            "version": "2.2.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/phpDocumentor/ReflectionCommon.git",
+                "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b",
+                "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.2 || ^8.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-2.x": "2.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "phpDocumentor\\Reflection\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Jaap van Otterdijk",
+                    "email": "opensource@ijaap.nl"
+                }
+            ],
+            "description": "Common reflection classes used by phpdocumentor to reflect the code structure",
+            "homepage": "http://www.phpdoc.org",
+            "keywords": [
+                "FQSEN",
+                "phpDocumentor",
+                "phpdoc",
+                "reflection",
+                "static analysis"
+            ],
+            "support": {
+                "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues",
+                "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x"
+            },
+            "time": "2020-06-27T09:03:43+00:00"
+        },
+        {
+            "name": "phpdocumentor/reflection-docblock",
+            "version": "5.3.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
+                "reference": "622548b623e81ca6d78b721c5e029f4ce664f170"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170",
+                "reference": "622548b623e81ca6d78b721c5e029f4ce664f170",
+                "shasum": ""
+            },
+            "require": {
+                "ext-filter": "*",
+                "php": "^7.2 || ^8.0",
+                "phpdocumentor/reflection-common": "^2.2",
+                "phpdocumentor/type-resolver": "^1.3",
+                "webmozart/assert": "^1.9.1"
+            },
+            "require-dev": {
+                "mockery/mockery": "~1.3.2",
+                "psalm/phar": "^4.8"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "5.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "phpDocumentor\\Reflection\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Mike van Riel",
+                    "email": "me@mikevanriel.com"
+                },
+                {
+                    "name": "Jaap van Otterdijk",
+                    "email": "account@ijaap.nl"
+                }
+            ],
+            "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
+            "support": {
+                "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues",
+                "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0"
+            },
+            "time": "2021-10-19T17:43:47+00:00"
+        },
+        {
+            "name": "phpdocumentor/type-resolver",
+            "version": "1.6.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/phpDocumentor/TypeResolver.git",
+                "reference": "77a32518733312af16a44300404e945338981de3"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/77a32518733312af16a44300404e945338981de3",
+                "reference": "77a32518733312af16a44300404e945338981de3",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.2 || ^8.0",
+                "phpdocumentor/reflection-common": "^2.0"
+            },
+            "require-dev": {
+                "ext-tokenizer": "*",
+                "psalm/phar": "^4.8"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-1.x": "1.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "phpDocumentor\\Reflection\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Mike van Riel",
+                    "email": "me@mikevanriel.com"
+                }
+            ],
+            "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names",
+            "support": {
+                "issues": "https://github.com/phpDocumentor/TypeResolver/issues",
+                "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.6.1"
+            },
+            "time": "2022-03-15T21:29:03+00:00"
+        },
+        {
+            "name": "phpspec/prophecy",
+            "version": "v1.15.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/phpspec/prophecy.git",
+                "reference": "bbcd7380b0ebf3961ee21409db7b38bc31d69a13"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/phpspec/prophecy/zipball/bbcd7380b0ebf3961ee21409db7b38bc31d69a13",
+                "reference": "bbcd7380b0ebf3961ee21409db7b38bc31d69a13",
+                "shasum": ""
+            },
+            "require": {
+                "doctrine/instantiator": "^1.2",
+                "php": "^7.2 || ~8.0, <8.2",
+                "phpdocumentor/reflection-docblock": "^5.2",
+                "sebastian/comparator": "^3.0 || ^4.0",
+                "sebastian/recursion-context": "^3.0 || ^4.0"
+            },
+            "require-dev": {
+                "phpspec/phpspec": "^6.0 || ^7.0",
+                "phpunit/phpunit": "^8.0 || ^9.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Prophecy\\": "src/Prophecy"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Konstantin Kudryashov",
+                    "email": "ever.zet@gmail.com",
+                    "homepage": "http://everzet.com"
+                },
+                {
+                    "name": "Marcello Duarte",
+                    "email": "marcello.duarte@gmail.com"
+                }
+            ],
+            "description": "Highly opinionated mocking framework for PHP 5.3+",
+            "homepage": "https://github.com/phpspec/prophecy",
+            "keywords": [
+                "Double",
+                "Dummy",
+                "fake",
+                "mock",
+                "spy",
+                "stub"
+            ],
+            "support": {
+                "issues": "https://github.com/phpspec/prophecy/issues",
+                "source": "https://github.com/phpspec/prophecy/tree/v1.15.0"
+            },
+            "time": "2021-12-08T12:19:24+00:00"
+        },
+        {
+            "name": "phpunit/php-code-coverage",
+            "version": "9.2.15",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
+                "reference": "2e9da11878c4202f97915c1cb4bb1ca318a63f5f"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/2e9da11878c4202f97915c1cb4bb1ca318a63f5f",
+                "reference": "2e9da11878c4202f97915c1cb4bb1ca318a63f5f",
+                "shasum": ""
+            },
+            "require": {
+                "ext-dom": "*",
+                "ext-libxml": "*",
+                "ext-xmlwriter": "*",
+                "nikic/php-parser": "^4.13.0",
+                "php": ">=7.3",
+                "phpunit/php-file-iterator": "^3.0.3",
+                "phpunit/php-text-template": "^2.0.2",
+                "sebastian/code-unit-reverse-lookup": "^2.0.2",
+                "sebastian/complexity": "^2.0",
+                "sebastian/environment": "^5.1.2",
+                "sebastian/lines-of-code": "^1.0.3",
+                "sebastian/version": "^3.0.1",
+                "theseer/tokenizer": "^1.2.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9.3"
+            },
+            "suggest": {
+                "ext-pcov": "*",
+                "ext-xdebug": "*"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "9.2-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
+            "homepage": "https://github.com/sebastianbergmann/php-code-coverage",
+            "keywords": [
+                "coverage",
+                "testing",
+                "xunit"
+            ],
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
+                "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.15"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2022-03-07T09:28:20+00:00"
+        },
+        {
+            "name": "phpunit/php-file-iterator",
+            "version": "3.0.6",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/php-file-iterator.git",
+                "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf",
+                "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9.3"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "FilterIterator implementation that filters files based on a list of suffixes.",
+            "homepage": "https://github.com/sebastianbergmann/php-file-iterator/",
+            "keywords": [
+                "filesystem",
+                "iterator"
+            ],
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues",
+                "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2021-12-02T12:48:52+00:00"
+        },
+        {
+            "name": "phpunit/php-invoker",
+            "version": "3.1.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/php-invoker.git",
+                "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67",
+                "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.3"
+            },
+            "require-dev": {
+                "ext-pcntl": "*",
+                "phpunit/phpunit": "^9.3"
+            },
+            "suggest": {
+                "ext-pcntl": "*"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.1-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "Invoke callables with a timeout",
+            "homepage": "https://github.com/sebastianbergmann/php-invoker/",
+            "keywords": [
+                "process"
+            ],
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/php-invoker/issues",
+                "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-09-28T05:58:55+00:00"
+        },
+        {
+            "name": "phpunit/php-text-template",
+            "version": "2.0.4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/php-text-template.git",
+                "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28",
+                "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9.3"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "Simple template engine.",
+            "homepage": "https://github.com/sebastianbergmann/php-text-template/",
+            "keywords": [
+                "template"
+            ],
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/php-text-template/issues",
+                "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-10-26T05:33:50+00:00"
+        },
+        {
+            "name": "phpunit/php-timer",
+            "version": "5.0.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/php-timer.git",
+                "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2",
+                "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9.3"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "5.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "Utility class for timing",
+            "homepage": "https://github.com/sebastianbergmann/php-timer/",
+            "keywords": [
+                "timer"
+            ],
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/php-timer/issues",
+                "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-10-26T13:16:10+00:00"
+        },
+        {
+            "name": "phpunit/phpunit",
+            "version": "9.5.20",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/phpunit.git",
+                "reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/12bc8879fb65aef2138b26fc633cb1e3620cffba",
+                "reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba",
+                "shasum": ""
+            },
+            "require": {
+                "doctrine/instantiator": "^1.3.1",
+                "ext-dom": "*",
+                "ext-json": "*",
+                "ext-libxml": "*",
+                "ext-mbstring": "*",
+                "ext-xml": "*",
+                "ext-xmlwriter": "*",
+                "myclabs/deep-copy": "^1.10.1",
+                "phar-io/manifest": "^2.0.3",
+                "phar-io/version": "^3.0.2",
+                "php": ">=7.3",
+                "phpspec/prophecy": "^1.12.1",
+                "phpunit/php-code-coverage": "^9.2.13",
+                "phpunit/php-file-iterator": "^3.0.5",
+                "phpunit/php-invoker": "^3.1.1",
+                "phpunit/php-text-template": "^2.0.3",
+                "phpunit/php-timer": "^5.0.2",
+                "sebastian/cli-parser": "^1.0.1",
+                "sebastian/code-unit": "^1.0.6",
+                "sebastian/comparator": "^4.0.5",
+                "sebastian/diff": "^4.0.3",
+                "sebastian/environment": "^5.1.3",
+                "sebastian/exporter": "^4.0.3",
+                "sebastian/global-state": "^5.0.1",
+                "sebastian/object-enumerator": "^4.0.3",
+                "sebastian/resource-operations": "^3.0.3",
+                "sebastian/type": "^3.0",
+                "sebastian/version": "^3.0.2"
+            },
+            "require-dev": {
+                "ext-pdo": "*",
+                "phpspec/prophecy-phpunit": "^2.0.1"
+            },
+            "suggest": {
+                "ext-soap": "*",
+                "ext-xdebug": "*"
+            },
+            "bin": [
+                "phpunit"
+            ],
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "9.5-dev"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "src/Framework/Assert/Functions.php"
+                ],
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "The PHP Unit Testing framework.",
+            "homepage": "https://phpunit.de/",
+            "keywords": [
+                "phpunit",
+                "testing",
+                "xunit"
+            ],
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/phpunit/issues",
+                "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.20"
+            },
+            "funding": [
+                {
+                    "url": "https://phpunit.de/sponsors.html",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2022-04-01T12:37:26+00:00"
+        },
+        {
+            "name": "sebastian/cli-parser",
+            "version": "1.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/cli-parser.git",
+                "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2",
+                "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9.3"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "Library for parsing CLI options",
+            "homepage": "https://github.com/sebastianbergmann/cli-parser",
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/cli-parser/issues",
+                "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-09-28T06:08:49+00:00"
+        },
+        {
+            "name": "sebastian/code-unit",
+            "version": "1.0.8",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/code-unit.git",
+                "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120",
+                "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9.3"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "Collection of value objects that represent the PHP code units",
+            "homepage": "https://github.com/sebastianbergmann/code-unit",
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/code-unit/issues",
+                "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-10-26T13:08:54+00:00"
+        },
+        {
+            "name": "sebastian/code-unit-reverse-lookup",
+            "version": "2.0.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git",
+                "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5",
+                "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9.3"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                }
+            ],
+            "description": "Looks up which function or method a line of code belongs to",
+            "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/",
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues",
+                "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-09-28T05:30:19+00:00"
+        },
+        {
+            "name": "sebastian/comparator",
+            "version": "4.0.6",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/comparator.git",
+                "reference": "55f4261989e546dc112258c7a75935a81a7ce382"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/55f4261989e546dc112258c7a75935a81a7ce382",
+                "reference": "55f4261989e546dc112258c7a75935a81a7ce382",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.3",
+                "sebastian/diff": "^4.0",
+                "sebastian/exporter": "^4.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9.3"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "4.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                },
+                {
+                    "name": "Jeff Welch",
+                    "email": "whatthejeff@gmail.com"
+                },
+                {
+                    "name": "Volker Dusch",
+                    "email": "github@wallbash.com"
+                },
+                {
+                    "name": "Bernhard Schussek",
+                    "email": "bschussek@2bepublished.at"
+                }
+            ],
+            "description": "Provides the functionality to compare PHP values for equality",
+            "homepage": "https://github.com/sebastianbergmann/comparator",
+            "keywords": [
+                "comparator",
+                "compare",
+                "equality"
+            ],
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/comparator/issues",
+                "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.6"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-10-26T15:49:45+00:00"
+        },
+        {
+            "name": "sebastian/complexity",
+            "version": "2.0.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/complexity.git",
+                "reference": "739b35e53379900cc9ac327b2147867b8b6efd88"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88",
+                "reference": "739b35e53379900cc9ac327b2147867b8b6efd88",
+                "shasum": ""
+            },
+            "require": {
+                "nikic/php-parser": "^4.7",
+                "php": ">=7.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9.3"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "Library for calculating the complexity of PHP code units",
+            "homepage": "https://github.com/sebastianbergmann/complexity",
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/complexity/issues",
+                "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-10-26T15:52:27+00:00"
+        },
+        {
+            "name": "sebastian/diff",
+            "version": "4.0.4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/diff.git",
+                "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d",
+                "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9.3",
+                "symfony/process": "^4.2 || ^5"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "4.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                },
+                {
+                    "name": "Kore Nordmann",
+                    "email": "mail@kore-nordmann.de"
+                }
+            ],
+            "description": "Diff implementation",
+            "homepage": "https://github.com/sebastianbergmann/diff",
+            "keywords": [
+                "diff",
+                "udiff",
+                "unidiff",
+                "unified diff"
+            ],
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/diff/issues",
+                "source": "https://github.com/sebastianbergmann/diff/tree/4.0.4"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-10-26T13:10:38+00:00"
+        },
+        {
+            "name": "sebastian/environment",
+            "version": "5.1.4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/environment.git",
+                "reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/1b5dff7bb151a4db11d49d90e5408e4e938270f7",
+                "reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9.3"
+            },
+            "suggest": {
+                "ext-posix": "*"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "5.1-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                }
+            ],
+            "description": "Provides functionality to handle HHVM/PHP environments",
+            "homepage": "http://www.github.com/sebastianbergmann/environment",
+            "keywords": [
+                "Xdebug",
+                "environment",
+                "hhvm"
+            ],
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/environment/issues",
+                "source": "https://github.com/sebastianbergmann/environment/tree/5.1.4"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2022-04-03T09:37:03+00:00"
+        },
+        {
+            "name": "sebastian/exporter",
+            "version": "4.0.4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/exporter.git",
+                "reference": "65e8b7db476c5dd267e65eea9cab77584d3cfff9"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/65e8b7db476c5dd267e65eea9cab77584d3cfff9",
+                "reference": "65e8b7db476c5dd267e65eea9cab77584d3cfff9",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.3",
+                "sebastian/recursion-context": "^4.0"
+            },
+            "require-dev": {
+                "ext-mbstring": "*",
+                "phpunit/phpunit": "^9.3"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "4.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                },
+                {
+                    "name": "Jeff Welch",
+                    "email": "whatthejeff@gmail.com"
+                },
+                {
+                    "name": "Volker Dusch",
+                    "email": "github@wallbash.com"
+                },
+                {
+                    "name": "Adam Harvey",
+                    "email": "aharvey@php.net"
+                },
+                {
+                    "name": "Bernhard Schussek",
+                    "email": "bschussek@gmail.com"
+                }
+            ],
+            "description": "Provides the functionality to export PHP variables for visualization",
+            "homepage": "https://www.github.com/sebastianbergmann/exporter",
+            "keywords": [
+                "export",
+                "exporter"
+            ],
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/exporter/issues",
+                "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.4"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2021-11-11T14:18:36+00:00"
+        },
+        {
+            "name": "sebastian/global-state",
+            "version": "5.0.5",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/global-state.git",
+                "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/0ca8db5a5fc9c8646244e629625ac486fa286bf2",
+                "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.3",
+                "sebastian/object-reflector": "^2.0",
+                "sebastian/recursion-context": "^4.0"
+            },
+            "require-dev": {
+                "ext-dom": "*",
+                "phpunit/phpunit": "^9.3"
+            },
+            "suggest": {
+                "ext-uopz": "*"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "5.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                }
+            ],
+            "description": "Snapshotting of global state",
+            "homepage": "http://www.github.com/sebastianbergmann/global-state",
+            "keywords": [
+                "global state"
+            ],
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/global-state/issues",
+                "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.5"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2022-02-14T08:28:10+00:00"
+        },
+        {
+            "name": "sebastian/lines-of-code",
+            "version": "1.0.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/lines-of-code.git",
+                "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc",
+                "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc",
+                "shasum": ""
+            },
+            "require": {
+                "nikic/php-parser": "^4.6",
+                "php": ">=7.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9.3"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "Library for counting the lines of code in PHP source code",
+            "homepage": "https://github.com/sebastianbergmann/lines-of-code",
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/lines-of-code/issues",
+                "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-11-28T06:42:11+00:00"
+        },
+        {
+            "name": "sebastian/object-enumerator",
+            "version": "4.0.4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/object-enumerator.git",
+                "reference": "5c9eeac41b290a3712d88851518825ad78f45c71"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71",
+                "reference": "5c9eeac41b290a3712d88851518825ad78f45c71",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.3",
+                "sebastian/object-reflector": "^2.0",
+                "sebastian/recursion-context": "^4.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9.3"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "4.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                }
+            ],
+            "description": "Traverses array structures and object graphs to enumerate all referenced objects",
+            "homepage": "https://github.com/sebastianbergmann/object-enumerator/",
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/object-enumerator/issues",
+                "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-10-26T13:12:34+00:00"
+        },
+        {
+            "name": "sebastian/object-reflector",
+            "version": "2.0.4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/object-reflector.git",
+                "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7",
+                "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9.3"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                }
+            ],
+            "description": "Allows reflection of object attributes, including inherited and non-public ones",
+            "homepage": "https://github.com/sebastianbergmann/object-reflector/",
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/object-reflector/issues",
+                "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-10-26T13:14:26+00:00"
+        },
+        {
+            "name": "sebastian/recursion-context",
+            "version": "4.0.4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/recursion-context.git",
+                "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/cd9d8cf3c5804de4341c283ed787f099f5506172",
+                "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9.3"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "4.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                },
+                {
+                    "name": "Jeff Welch",
+                    "email": "whatthejeff@gmail.com"
+                },
+                {
+                    "name": "Adam Harvey",
+                    "email": "aharvey@php.net"
+                }
+            ],
+            "description": "Provides functionality to recursively process PHP variables",
+            "homepage": "http://www.github.com/sebastianbergmann/recursion-context",
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/recursion-context/issues",
+                "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.4"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-10-26T13:17:30+00:00"
+        },
+        {
+            "name": "sebastian/resource-operations",
+            "version": "3.0.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/resource-operations.git",
+                "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8",
+                "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                }
+            ],
+            "description": "Provides a list of PHP built-in functions that operate on resources",
+            "homepage": "https://www.github.com/sebastianbergmann/resource-operations",
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/resource-operations/issues",
+                "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-09-28T06:45:17+00:00"
+        },
+        {
+            "name": "sebastian/type",
+            "version": "3.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/type.git",
+                "reference": "b233b84bc4465aff7b57cf1c4bc75c86d00d6dad"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/b233b84bc4465aff7b57cf1c4bc75c86d00d6dad",
+                "reference": "b233b84bc4465aff7b57cf1c4bc75c86d00d6dad",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9.5"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "Collection of value objects that represent the types of the PHP type system",
+            "homepage": "https://github.com/sebastianbergmann/type",
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/type/issues",
+                "source": "https://github.com/sebastianbergmann/type/tree/3.0.0"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2022-03-15T09:54:48+00:00"
+        },
+        {
+            "name": "sebastian/version",
+            "version": "3.0.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/version.git",
+                "reference": "c6c1022351a901512170118436c764e473f6de8c"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c",
+                "reference": "c6c1022351a901512170118436c764e473f6de8c",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.3"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "Library that helps with managing the version number of Git-hosted PHP projects",
+            "homepage": "https://github.com/sebastianbergmann/version",
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/version/issues",
+                "source": "https://github.com/sebastianbergmann/version/tree/3.0.2"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-09-28T06:39:44+00:00"
+        },
+        {
+            "name": "spatie/backtrace",
+            "version": "1.2.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/spatie/backtrace.git",
+                "reference": "4ee7d41aa5268107906ea8a4d9ceccde136dbd5b"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/spatie/backtrace/zipball/4ee7d41aa5268107906ea8a4d9ceccde136dbd5b",
+                "reference": "4ee7d41aa5268107906ea8a4d9ceccde136dbd5b",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.3|^8.0"
+            },
+            "require-dev": {
+                "ext-json": "*",
+                "phpunit/phpunit": "^9.3",
+                "symfony/var-dumper": "^5.1"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Spatie\\Backtrace\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Freek Van de Herten",
+                    "email": "freek@spatie.be",
+                    "homepage": "https://spatie.be",
+                    "role": "Developer"
+                }
+            ],
+            "description": "A better backtrace",
+            "homepage": "https://github.com/spatie/backtrace",
+            "keywords": [
+                "Backtrace",
+                "spatie"
+            ],
+            "support": {
+                "issues": "https://github.com/spatie/backtrace/issues",
+                "source": "https://github.com/spatie/backtrace/tree/1.2.1"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/sponsors/spatie",
+                    "type": "github"
+                },
+                {
+                    "url": "https://spatie.be/open-source/support-us",
+                    "type": "other"
+                }
+            ],
+            "time": "2021-11-09T10:57:15+00:00"
+        },
+        {
+            "name": "spatie/flare-client-php",
+            "version": "1.2.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/spatie/flare-client-php.git",
+                "reference": "86a380f5b1ce839af04a08f1c8f2697184cdf23f"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/spatie/flare-client-php/zipball/86a380f5b1ce839af04a08f1c8f2697184cdf23f",
+                "reference": "86a380f5b1ce839af04a08f1c8f2697184cdf23f",
+                "shasum": ""
+            },
+            "require": {
+                "illuminate/pipeline": "^8.0|^9.0",
+                "php": "^8.0",
+                "spatie/backtrace": "^1.2",
+                "symfony/http-foundation": "^5.0|^6.0",
+                "symfony/mime": "^5.2|^6.0",
+                "symfony/process": "^5.2|^6.0",
+                "symfony/var-dumper": "^5.2|^6.0"
+            },
+            "require-dev": {
+                "dms/phpunit-arraysubset-asserts": "^0.3.0",
+                "pestphp/pest": "^1.20",
+                "phpstan/extension-installer": "^1.1",
+                "phpstan/phpstan-deprecation-rules": "^1.0",
+                "phpstan/phpstan-phpunit": "^1.0",
+                "spatie/phpunit-snapshot-assertions": "^4.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "1.1.x-dev"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "src/helpers.php"
+                ],
+                "psr-4": {
+                    "Spatie\\FlareClient\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "Send PHP errors to Flare",
+            "homepage": "https://github.com/spatie/flare-client-php",
+            "keywords": [
+                "exception",
+                "flare",
+                "reporting",
+                "spatie"
+            ],
+            "support": {
+                "issues": "https://github.com/spatie/flare-client-php/issues",
+                "source": "https://github.com/spatie/flare-client-php/tree/1.2.0"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/spatie",
+                    "type": "github"
+                }
+            ],
+            "time": "2022-05-16T12:13:39+00:00"
+        },
+        {
+            "name": "spatie/ignition",
+            "version": "1.3.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/spatie/ignition.git",
+                "reference": "997363fbcce809b1e55f571997d49017f9c623d9"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/spatie/ignition/zipball/997363fbcce809b1e55f571997d49017f9c623d9",
+                "reference": "997363fbcce809b1e55f571997d49017f9c623d9",
+                "shasum": ""
+            },
+            "require": {
+                "ext-json": "*",
+                "ext-mbstring": "*",
+                "monolog/monolog": "^2.0",
+                "php": "^8.0",
+                "spatie/flare-client-php": "^1.1",
+                "symfony/console": "^5.4|^6.0",
+                "symfony/var-dumper": "^5.4|^6.0"
+            },
+            "require-dev": {
+                "mockery/mockery": "^1.4",
+                "pestphp/pest": "^1.20",
+                "phpstan/extension-installer": "^1.1",
+                "phpstan/phpstan-deprecation-rules": "^1.0",
+                "phpstan/phpstan-phpunit": "^1.0",
+                "symfony/process": "^5.4|^6.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "1.2.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Spatie\\Ignition\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Spatie",
+                    "email": "info@spatie.be",
+                    "role": "Developer"
+                }
+            ],
+            "description": "A beautiful error page for PHP applications.",
+            "homepage": "https://flareapp.io/ignition",
+            "keywords": [
+                "error",
+                "flare",
+                "laravel",
+                "page"
+            ],
+            "support": {
+                "docs": "https://flareapp.io/docs/ignition-for-laravel/introduction",
+                "forum": "https://twitter.com/flareappio",
+                "issues": "https://github.com/spatie/ignition/issues",
+                "source": "https://github.com/spatie/ignition"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/spatie",
+                    "type": "github"
+                }
+            ],
+            "time": "2022-05-16T13:16:07+00:00"
+        },
+        {
+            "name": "spatie/laravel-ignition",
+            "version": "1.2.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/spatie/laravel-ignition.git",
+                "reference": "51e5daaa7e43c154fe57f1ddfbba862f9fe57646"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/spatie/laravel-ignition/zipball/51e5daaa7e43c154fe57f1ddfbba862f9fe57646",
+                "reference": "51e5daaa7e43c154fe57f1ddfbba862f9fe57646",
+                "shasum": ""
+            },
+            "require": {
+                "ext-curl": "*",
+                "ext-json": "*",
+                "ext-mbstring": "*",
+                "illuminate/support": "^8.77|^9.0",
+                "monolog/monolog": "^2.3",
+                "php": "^8.0",
+                "spatie/flare-client-php": "^1.0.1",
+                "spatie/ignition": "^1.2.4",
+                "symfony/console": "^5.0|^6.0",
+                "symfony/var-dumper": "^5.0|^6.0"
+            },
+            "require-dev": {
+                "filp/whoops": "^2.14",
+                "livewire/livewire": "^2.8|dev-develop",
+                "mockery/mockery": "^1.4",
+                "nunomaduro/larastan": "^1.0",
+                "orchestra/testbench": "^6.23|^7.0",
+                "pestphp/pest": "^1.20",
+                "phpstan/extension-installer": "^1.1",
+                "phpstan/phpstan-deprecation-rules": "^1.0",
+                "phpstan/phpstan-phpunit": "^1.0",
+                "spatie/laravel-ray": "^1.27"
+            },
+            "type": "library",
+            "extra": {
+                "laravel": {
+                    "providers": [
+                        "Spatie\\LaravelIgnition\\IgnitionServiceProvider"
+                    ],
+                    "aliases": {
+                        "Flare": "Spatie\\LaravelIgnition\\Facades\\Flare"
+                    }
+                }
+            },
+            "autoload": {
+                "files": [
+                    "src/helpers.php"
+                ],
+                "psr-4": {
+                    "Spatie\\LaravelIgnition\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Spatie",
+                    "email": "info@spatie.be",
+                    "role": "Developer"
+                }
+            ],
+            "description": "A beautiful error page for Laravel applications.",
+            "homepage": "https://flareapp.io/ignition",
+            "keywords": [
+                "error",
+                "flare",
+                "laravel",
+                "page"
+            ],
+            "support": {
+                "docs": "https://flareapp.io/docs/ignition-for-laravel/introduction",
+                "forum": "https://twitter.com/flareappio",
+                "issues": "https://github.com/spatie/laravel-ignition/issues",
+                "source": "https://github.com/spatie/laravel-ignition"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/spatie",
+                    "type": "github"
+                }
+            ],
+            "time": "2022-05-05T15:53:24+00:00"
+        },
+        {
+            "name": "theseer/tokenizer",
+            "version": "1.2.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/theseer/tokenizer.git",
+                "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e",
+                "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e",
+                "shasum": ""
+            },
+            "require": {
+                "ext-dom": "*",
+                "ext-tokenizer": "*",
+                "ext-xmlwriter": "*",
+                "php": "^7.2 || ^8.0"
+            },
+            "type": "library",
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Arne Blankerts",
+                    "email": "arne@blankerts.de",
+                    "role": "Developer"
+                }
+            ],
+            "description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
+            "support": {
+                "issues": "https://github.com/theseer/tokenizer/issues",
+                "source": "https://github.com/theseer/tokenizer/tree/1.2.1"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/theseer",
+                    "type": "github"
+                }
+            ],
+            "time": "2021-07-28T10:34:58+00:00"
+        }
+    ],
+    "aliases": [],
+    "minimum-stability": "dev",
+    "stability-flags": [],
+    "prefer-stable": true,
+    "prefer-lowest": false,
+    "platform": {
+        "php": "^8.0.2"
+    },
+    "platform-dev": [],
+    "plugin-api-version": "2.1.0"
+}

+ 215 - 0
config/app.php

@@ -0,0 +1,215 @@
+<?php
+
+use Illuminate\Support\Facades\Facade;
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | Application Name
+    |--------------------------------------------------------------------------
+    |
+    | This value is the name of your application. This value is used when the
+    | framework needs to place the application's name in a notification or
+    | any other location as required by the application or its packages.
+    |
+    */
+
+    'name' => env('APP_NAME', 'Laravel'),
+
+    /*
+    |--------------------------------------------------------------------------
+    | Application Environment
+    |--------------------------------------------------------------------------
+    |
+    | This value determines the "environment" your application is currently
+    | running in. This may determine how you prefer to configure various
+    | services the application utilizes. Set this in your ".env" file.
+    |
+    */
+
+    'env' => env('APP_ENV', 'production'),
+
+    /*
+    |--------------------------------------------------------------------------
+    | Application Debug Mode
+    |--------------------------------------------------------------------------
+    |
+    | When your application is in debug mode, detailed error messages with
+    | stack traces will be shown on every error that occurs within your
+    | application. If disabled, a simple generic error page is shown.
+    |
+    */
+
+    'debug' => (bool) env('APP_DEBUG', false),
+
+    /*
+    |--------------------------------------------------------------------------
+    | Application URL
+    |--------------------------------------------------------------------------
+    |
+    | This URL is used by the console to properly generate URLs when using
+    | the Artisan command line tool. You should set this to the root of
+    | your application so that it is used when running Artisan tasks.
+    |
+    */
+
+    'url' => env('APP_URL', 'http://localhost'),
+
+    'asset_url' => env('ASSET_URL'),
+
+    /*
+    |--------------------------------------------------------------------------
+    | Application Timezone
+    |--------------------------------------------------------------------------
+    |
+    | Here you may specify the default timezone for your application, which
+    | will be used by the PHP date and date-time functions. We have gone
+    | ahead and set this to a sensible default for you out of the box.
+    |
+    */
+
+    'timezone' => env('APP_TIMEZONE', 'Asia/Shanghai'),
+
+    /*
+    |--------------------------------------------------------------------------
+    | Application Locale Configuration
+    |--------------------------------------------------------------------------
+    |
+    | The application locale determines the default locale that will be used
+    | by the translation service provider. You are free to set this value
+    | to any of the locales which will be supported by the application.
+    |
+    */
+
+    'locale' => 'en',
+
+    /*
+    |--------------------------------------------------------------------------
+    | Application Fallback Locale
+    |--------------------------------------------------------------------------
+    |
+    | The fallback locale determines the locale to use when the current one
+    | is not available. You may change the value to correspond to any of
+    | the language folders that are provided through your application.
+    |
+    */
+
+    'fallback_locale' => 'en',
+
+    /*
+    |--------------------------------------------------------------------------
+    | Faker Locale
+    |--------------------------------------------------------------------------
+    |
+    | This locale will be used by the Faker PHP library when generating fake
+    | data for your database seeds. For example, this will be used to get
+    | localized telephone numbers, street address information and more.
+    |
+    */
+
+    'faker_locale' => 'en_US',
+
+    /*
+    |--------------------------------------------------------------------------
+    | Encryption Key
+    |--------------------------------------------------------------------------
+    |
+    | This key is used by the Illuminate encrypter service and should be set
+    | to a random, 32 character string, otherwise these encrypted strings
+    | will not be safe. Please do this before deploying an application!
+    |
+    */
+
+    'key' => env('APP_KEY'),
+
+    'cipher' => 'AES-256-CBC',
+
+    /*
+    |--------------------------------------------------------------------------
+    | Maintenance Mode Driver
+    |--------------------------------------------------------------------------
+    |
+    | These configuration options determine the driver used to determine and
+    | manage Laravel's "maintenance mode" status. The "cache" driver will
+    | allow maintenance mode to be controlled across multiple machines.
+    |
+    | Supported drivers: "file", "cache"
+    |
+    */
+
+    'maintenance' => [
+        'driver' => 'file',
+        // 'store'  => 'redis',
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | Autoloaded Service Providers
+    |--------------------------------------------------------------------------
+    |
+    | The service providers listed here will be automatically loaded on the
+    | request to your application. Feel free to add your own services to
+    | this array to grant expanded functionality to your applications.
+    |
+    */
+
+    'providers' => [
+
+        /*
+         * Laravel Framework Service Providers...
+         */
+        Illuminate\Auth\AuthServiceProvider::class,
+        Illuminate\Broadcasting\BroadcastServiceProvider::class,
+        Illuminate\Bus\BusServiceProvider::class,
+        Illuminate\Cache\CacheServiceProvider::class,
+        Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class,
+        Illuminate\Cookie\CookieServiceProvider::class,
+        Illuminate\Database\DatabaseServiceProvider::class,
+        Illuminate\Encryption\EncryptionServiceProvider::class,
+        Illuminate\Filesystem\FilesystemServiceProvider::class,
+        Illuminate\Foundation\Providers\FoundationServiceProvider::class,
+        Illuminate\Hashing\HashServiceProvider::class,
+        Illuminate\Mail\MailServiceProvider::class,
+        Illuminate\Notifications\NotificationServiceProvider::class,
+        Illuminate\Pagination\PaginationServiceProvider::class,
+        Illuminate\Pipeline\PipelineServiceProvider::class,
+        Illuminate\Queue\QueueServiceProvider::class,
+        Illuminate\Redis\RedisServiceProvider::class,
+        Illuminate\Auth\Passwords\PasswordResetServiceProvider::class,
+        Illuminate\Session\SessionServiceProvider::class,
+        Illuminate\Translation\TranslationServiceProvider::class,
+        Illuminate\Validation\ValidationServiceProvider::class,
+        Illuminate\View\ViewServiceProvider::class,
+
+        /*
+         * Package Service Providers...
+         */
+
+        /*
+         * Application Service Providers...
+         */
+        App\Providers\AppServiceProvider::class,
+        App\Providers\AuthServiceProvider::class,
+        // App\Providers\BroadcastServiceProvider::class,
+        App\Providers\EventServiceProvider::class,
+        App\Providers\RouteServiceProvider::class,
+
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | Class Aliases
+    |--------------------------------------------------------------------------
+    |
+    | This array of class aliases will be registered when this application
+    | is started. However, feel free to register as many as you wish as
+    | the aliases are "lazy" loaded so they don't hinder performance.
+    |
+    */
+
+    'aliases' => Facade::defaultAliases()->merge([
+        // 'ExampleClass' => App\Example\ExampleClass::class,
+    ])->toArray(),
+
+];

+ 111 - 0
config/auth.php

@@ -0,0 +1,111 @@
+<?php
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | Authentication Defaults
+    |--------------------------------------------------------------------------
+    |
+    | This option controls the default authentication "guard" and password
+    | reset options for your application. You may change these defaults
+    | as required, but they're a perfect start for most applications.
+    |
+    */
+
+    'defaults' => [
+        'guard' => 'web',
+        'passwords' => 'users',
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | Authentication Guards
+    |--------------------------------------------------------------------------
+    |
+    | Next, you may define every authentication guard for your application.
+    | Of course, a great default configuration has been defined for you
+    | here which uses session storage and the Eloquent user provider.
+    |
+    | All authentication drivers have a user provider. This defines how the
+    | users are actually retrieved out of your database or other storage
+    | mechanisms used by this application to persist your user's data.
+    |
+    | Supported: "session"
+    |
+    */
+
+    'guards' => [
+        'web' => [
+            'driver' => 'session',
+            'provider' => 'users',
+        ],
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | User Providers
+    |--------------------------------------------------------------------------
+    |
+    | All authentication drivers have a user provider. This defines how the
+    | users are actually retrieved out of your database or other storage
+    | mechanisms used by this application to persist your user's data.
+    |
+    | If you have multiple user tables or models you may configure multiple
+    | sources which represent each model / table. These sources may then
+    | be assigned to any extra authentication guards you have defined.
+    |
+    | Supported: "database", "eloquent"
+    |
+    */
+
+    'providers' => [
+        'users' => [
+            'driver' => 'eloquent',
+            'model' => App\Models\User::class,
+        ],
+
+        // 'users' => [
+        //     'driver' => 'database',
+        //     'table' => 'users',
+        // ],
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | Resetting Passwords
+    |--------------------------------------------------------------------------
+    |
+    | You may specify multiple password reset configurations if you have more
+    | than one user table or model in the application and you want to have
+    | separate password reset settings based on the specific user types.
+    |
+    | The expire time is the number of minutes that each reset token will be
+    | considered valid. This security feature keeps tokens short-lived so
+    | they have less time to be guessed. You may change this as needed.
+    |
+    */
+
+    'passwords' => [
+        'users' => [
+            'provider' => 'users',
+            'table' => 'password_resets',
+            'expire' => 60,
+            'throttle' => 60,
+        ],
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | Password Confirmation Timeout
+    |--------------------------------------------------------------------------
+    |
+    | Here you may define the amount of seconds before a password confirmation
+    | times out and the user is prompted to re-enter their password via the
+    | confirmation screen. By default, the timeout lasts for three hours.
+    |
+    */
+
+    'password_timeout' => 10800,
+
+];

+ 67 - 0
config/broadcasting.php

@@ -0,0 +1,67 @@
+<?php
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | Default Broadcaster
+    |--------------------------------------------------------------------------
+    |
+    | This option controls the default broadcaster that will be used by the
+    | framework when an event needs to be broadcast. You may set this to
+    | any of the connections defined in the "connections" array below.
+    |
+    | Supported: "pusher", "ably", "redis", "log", "null"
+    |
+    */
+
+    'default' => env('BROADCAST_DRIVER', 'null'),
+
+    /*
+    |--------------------------------------------------------------------------
+    | Broadcast Connections
+    |--------------------------------------------------------------------------
+    |
+    | Here you may define all of the broadcast connections that will be used
+    | to broadcast events to other systems or over websockets. Samples of
+    | each available type of connection are provided inside this array.
+    |
+    */
+
+    'connections' => [
+
+        'pusher' => [
+            'driver' => 'pusher',
+            'key' => env('PUSHER_APP_KEY'),
+            'secret' => env('PUSHER_APP_SECRET'),
+            'app_id' => env('PUSHER_APP_ID'),
+            'options' => [
+                'cluster' => env('PUSHER_APP_CLUSTER'),
+                'useTLS' => true,
+            ],
+            'client_options' => [
+                // Guzzle client options: https://docs.guzzlephp.org/en/stable/request-options.html
+            ],
+        ],
+
+        'ably' => [
+            'driver' => 'ably',
+            'key' => env('ABLY_KEY'),
+        ],
+
+        'redis' => [
+            'driver' => 'redis',
+            'connection' => 'default',
+        ],
+
+        'log' => [
+            'driver' => 'log',
+        ],
+
+        'null' => [
+            'driver' => 'null',
+        ],
+
+    ],
+
+];

+ 110 - 0
config/cache.php

@@ -0,0 +1,110 @@
+<?php
+
+use Illuminate\Support\Str;
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | Default Cache Store
+    |--------------------------------------------------------------------------
+    |
+    | This option controls the default cache connection that gets used while
+    | using this caching library. This connection is used when another is
+    | not explicitly specified when executing a given caching function.
+    |
+    */
+
+    'default' => env('CACHE_DRIVER', 'file'),
+
+    /*
+    |--------------------------------------------------------------------------
+    | Cache Stores
+    |--------------------------------------------------------------------------
+    |
+    | Here you may define all of the cache "stores" for your application as
+    | well as their drivers. You may even define multiple stores for the
+    | same cache driver to group types of items stored in your caches.
+    |
+    | Supported drivers: "apc", "array", "database", "file",
+    |         "memcached", "redis", "dynamodb", "octane", "null"
+    |
+    */
+
+    'stores' => [
+
+        'apc' => [
+            'driver' => 'apc',
+        ],
+
+        'array' => [
+            'driver' => 'array',
+            'serialize' => false,
+        ],
+
+        'database' => [
+            'driver' => 'database',
+            'table' => 'cache',
+            'connection' => null,
+            'lock_connection' => null,
+        ],
+
+        'file' => [
+            'driver' => 'file',
+            'path' => storage_path('framework/cache/data'),
+        ],
+
+        'memcached' => [
+            'driver' => 'memcached',
+            'persistent_id' => env('MEMCACHED_PERSISTENT_ID'),
+            'sasl' => [
+                env('MEMCACHED_USERNAME'),
+                env('MEMCACHED_PASSWORD'),
+            ],
+            'options' => [
+                // Memcached::OPT_CONNECT_TIMEOUT => 2000,
+            ],
+            'servers' => [
+                [
+                    'host' => env('MEMCACHED_HOST', '127.0.0.1'),
+                    'port' => env('MEMCACHED_PORT', 11211),
+                    'weight' => 100,
+                ],
+            ],
+        ],
+
+        'redis' => [
+            'driver' => 'redis',
+            'connection' => 'cache',
+            'lock_connection' => 'default',
+        ],
+
+        'dynamodb' => [
+            'driver' => 'dynamodb',
+            'key' => env('AWS_ACCESS_KEY_ID'),
+            'secret' => env('AWS_SECRET_ACCESS_KEY'),
+            'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
+            'table' => env('DYNAMODB_CACHE_TABLE', 'cache'),
+            'endpoint' => env('DYNAMODB_ENDPOINT'),
+        ],
+
+        'octane' => [
+            'driver' => 'octane',
+        ],
+
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | Cache Key Prefix
+    |--------------------------------------------------------------------------
+    |
+    | When utilizing the APC, database, memcached, Redis, or DynamoDB cache
+    | stores there might be other applications using the same cache. For
+    | that reason, you may prefix every cache key to avoid collisions.
+    |
+    */
+
+    'prefix' => env('CACHE_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_cache_'),
+
+];

+ 34 - 0
config/cors.php

@@ -0,0 +1,34 @@
+<?php
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | Cross-Origin Resource Sharing (CORS) Configuration
+    |--------------------------------------------------------------------------
+    |
+    | Here you may configure your settings for cross-origin resource sharing
+    | or "CORS". This determines what cross-origin operations may execute
+    | in web browsers. You are free to adjust these settings as needed.
+    |
+    | To learn more: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
+    |
+    */
+
+    'paths' => ['api/*', 'sanctum/csrf-cookie'],
+
+    'allowed_methods' => ['*'],
+
+    'allowed_origins' => ['*'],
+
+    'allowed_origins_patterns' => [],
+
+    'allowed_headers' => ['*'],
+
+    'exposed_headers' => [],
+
+    'max_age' => 0,
+
+    'supports_credentials' => false,
+
+];

+ 151 - 0
config/database.php

@@ -0,0 +1,151 @@
+<?php
+
+use Illuminate\Support\Str;
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | Default Database Connection Name
+    |--------------------------------------------------------------------------
+    |
+    | Here you may specify which of the database connections below you wish
+    | to use as your default connection for all database work. Of course
+    | you may use many connections at once using the Database library.
+    |
+    */
+
+    'default' => env('DB_CONNECTION', 'mysql'),
+
+    /*
+    |--------------------------------------------------------------------------
+    | Database Connections
+    |--------------------------------------------------------------------------
+    |
+    | Here are each of the database connections setup for your application.
+    | Of course, examples of configuring each database platform that is
+    | supported by Laravel is shown below to make development simple.
+    |
+    |
+    | All database work in Laravel is done through the PHP PDO facilities
+    | so make sure you have the driver for your particular database of
+    | choice installed on your machine before you begin development.
+    |
+    */
+
+    'connections' => [
+
+        'sqlite' => [
+            'driver' => 'sqlite',
+            'url' => env('DATABASE_URL'),
+            'database' => env('DB_DATABASE', database_path('database.sqlite')),
+            'prefix' => '',
+            'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true),
+        ],
+
+        'mysql' => [
+            'driver' => 'mysql',
+            'url' => env('DATABASE_URL'),
+            'host' => env('DB_HOST', '127.0.0.1'),
+            'port' => env('DB_PORT', '3306'),
+            'database' => env('DB_DATABASE', 'forge'),
+            'username' => env('DB_USERNAME', 'forge'),
+            'password' => env('DB_PASSWORD', ''),
+            'unix_socket' => env('DB_SOCKET', ''),
+            'charset' => 'utf8mb4',
+            'collation' => 'utf8mb4_unicode_ci',
+            'prefix' => '',
+            'prefix_indexes' => true,
+            'strict' => false,
+            'engine' => null,
+            'options' => extension_loaded('pdo_mysql') ? array_filter([
+                PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
+            ]) : [],
+        ],
+
+        'pgsql' => [
+            'driver' => 'pgsql',
+            'url' => env('DATABASE_URL'),
+            'host' => env('DB_HOST', '127.0.0.1'),
+            'port' => env('DB_PORT', '5432'),
+            'database' => env('DB_DATABASE', 'forge'),
+            'username' => env('DB_USERNAME', 'forge'),
+            'password' => env('DB_PASSWORD', ''),
+            'charset' => 'utf8',
+            'prefix' => '',
+            'prefix_indexes' => true,
+            'search_path' => 'public',
+            'sslmode' => 'prefer',
+        ],
+
+        'sqlsrv' => [
+            'driver' => 'sqlsrv',
+            'url' => env('DATABASE_URL'),
+            'host' => env('DB_HOST', 'localhost'),
+            'port' => env('DB_PORT', '1433'),
+            'database' => env('DB_DATABASE', 'forge'),
+            'username' => env('DB_USERNAME', 'forge'),
+            'password' => env('DB_PASSWORD', ''),
+            'charset' => 'utf8',
+            'prefix' => '',
+            'prefix_indexes' => true,
+            // 'encrypt' => env('DB_ENCRYPT', 'yes'),
+            // 'trust_server_certificate' => env('DB_TRUST_SERVER_CERTIFICATE', 'false'),
+        ],
+
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | Migration Repository Table
+    |--------------------------------------------------------------------------
+    |
+    | This table keeps track of all the migrations that have already run for
+    | your application. Using this information, we can determine which of
+    | the migrations on disk haven't actually been run in the database.
+    |
+    */
+
+    'migrations' => 'migrations',
+
+    /*
+    |--------------------------------------------------------------------------
+    | Redis Databases
+    |--------------------------------------------------------------------------
+    |
+    | Redis is an open source, fast, and advanced key-value store that also
+    | provides a richer body of commands than a typical key-value system
+    | such as APC or Memcached. Laravel makes it easy to dig right in.
+    |
+    */
+
+    'redis' => [
+
+        'client' => env('REDIS_CLIENT', 'phpredis'),
+
+        'options' => [
+            'cluster' => env('REDIS_CLUSTER', 'redis'),
+            'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'),
+        ],
+
+        'default' => [
+            'url' => env('REDIS_URL'),
+            'host' => env('REDIS_HOST', '127.0.0.1'),
+            'username' => env('REDIS_USERNAME'),
+            'password' => env('REDIS_PASSWORD'),
+            'port' => env('REDIS_PORT', '6379'),
+            'database' => env('REDIS_DB', '0'),
+        ],
+
+        'cache' => [
+            'url' => env('REDIS_URL'),
+            'host' => env('REDIS_HOST', '127.0.0.1'),
+            'username' => env('REDIS_USERNAME'),
+            'password' => env('REDIS_PASSWORD'),
+            'port' => env('REDIS_PORT', '6379'),
+            'database' => env('REDIS_CACHE_DB', '1'),
+        ],
+
+    ],
+
+];

+ 76 - 0
config/filesystems.php

@@ -0,0 +1,76 @@
+<?php
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | Default Filesystem Disk
+    |--------------------------------------------------------------------------
+    |
+    | Here you may specify the default filesystem disk that should be used
+    | by the framework. The "local" disk, as well as a variety of cloud
+    | based disks are available to your application. Just store away!
+    |
+    */
+
+    'default' => env('FILESYSTEM_DISK', 'local'),
+
+    /*
+    |--------------------------------------------------------------------------
+    | Filesystem Disks
+    |--------------------------------------------------------------------------
+    |
+    | Here you may configure as many filesystem "disks" as you wish, and you
+    | may even configure multiple disks of the same driver. Defaults have
+    | been set up for each driver as an example of the required values.
+    |
+    | Supported Drivers: "local", "ftp", "sftp", "s3"
+    |
+    */
+
+    'disks' => [
+
+        'local' => [
+            'driver' => 'local',
+            'root' => storage_path('app'),
+            'throw' => false,
+        ],
+
+        'public' => [
+            'driver' => 'local',
+            'root' => storage_path('app/public'),
+            'url' => env('APP_URL').'/storage',
+            'visibility' => 'public',
+            'throw' => false,
+        ],
+
+        's3' => [
+            'driver' => 's3',
+            'key' => env('AWS_ACCESS_KEY_ID'),
+            'secret' => env('AWS_SECRET_ACCESS_KEY'),
+            'region' => env('AWS_DEFAULT_REGION'),
+            'bucket' => env('AWS_BUCKET'),
+            'url' => env('AWS_URL'),
+            'endpoint' => env('AWS_ENDPOINT'),
+            'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false),
+            'throw' => false,
+        ],
+
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | Symbolic Links
+    |--------------------------------------------------------------------------
+    |
+    | Here you may configure the symbolic links that will be created when the
+    | `storage:link` Artisan command is executed. The array keys should be
+    | the locations of the links and the values should be their targets.
+    |
+    */
+
+    'links' => [
+        public_path('storage') => storage_path('app/public'),
+    ],
+
+];

+ 52 - 0
config/hashing.php

@@ -0,0 +1,52 @@
+<?php
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | Default Hash Driver
+    |--------------------------------------------------------------------------
+    |
+    | This option controls the default hash driver that will be used to hash
+    | passwords for your application. By default, the bcrypt algorithm is
+    | used; however, you remain free to modify this option if you wish.
+    |
+    | Supported: "bcrypt", "argon", "argon2id"
+    |
+    */
+
+    'driver' => 'bcrypt',
+
+    /*
+    |--------------------------------------------------------------------------
+    | Bcrypt Options
+    |--------------------------------------------------------------------------
+    |
+    | Here you may specify the configuration options that should be used when
+    | passwords are hashed using the Bcrypt algorithm. This will allow you
+    | to control the amount of time it takes to hash the given password.
+    |
+    */
+
+    'bcrypt' => [
+        'rounds' => env('BCRYPT_ROUNDS', 10),
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | Argon Options
+    |--------------------------------------------------------------------------
+    |
+    | Here you may specify the configuration options that should be used when
+    | passwords are hashed using the Argon algorithm. These will allow you
+    | to control the amount of time it takes to hash the given password.
+    |
+    */
+
+    'argon' => [
+        'memory' => 65536,
+        'threads' => 1,
+        'time' => 4,
+    ],
+
+];

+ 22 - 0
config/jwt.php

@@ -0,0 +1,22 @@
+<?php
+/**
+ * JWT配置.
+ *
+ * @author nj
+ * @date   2021-01-21 22:56:52
+ */
+declare(strict_types=1);
+
+return [
+    'secret' => env('JWT_SECRET', 'UepPolVbXgj91EIy7man6cMfTQuCWrdJ'),
+    // 过期时间分钟数, 单位分钟.
+    'ttl' => env('JWT_TTL', 720),
+    // 能刷新的有效分钟数, 单位分钟.
+    'refresh_ttl' => 20160,
+    // Hash算法.
+    'algo' => 'HS256',
+    // 管理员后台的过期分钟数, 单位分钟.
+    'mg_ttl' => env('JWT_MG_TTL', 720),
+    // 代理后台的过期分钟数, 单位分钟.
+    'ag_ttl' => env('JWT_AG_TTL', 720),
+];

Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini