From 55b2223435466e8036f8753aef221d9cb6375af2 Mon Sep 17 00:00:00 2001 From: AVMG20 Date: Sat, 10 Jul 2021 08:58:11 +0200 Subject: [PATCH] Added Voucher system --- app/Http/Controllers/Admin/UserController.php | 2 +- .../Controllers/Admin/VoucherController.php | 63 +++++++++++++++--- app/Models/User.php | 47 +++++++++++++ app/Models/Voucher.php | 25 ++++++- ...0_062140_update_credits_to_users_table.php | 32 +++++++++ resources/views/admin/users/edit.blade.php | 2 +- .../views/admin/vouchers/create.blade.php | 6 +- .../models/redeem_voucher_modal.blade.php | 66 +++++++++++++++++-- routes/web.php | 8 +++ 9 files changed, 229 insertions(+), 22 deletions(-) create mode 100644 database/migrations/2021_07_10_062140_update_credits_to_users_table.php diff --git a/app/Http/Controllers/Admin/UserController.php b/app/Http/Controllers/Admin/UserController.php index e49923ba..496c1bf3 100644 --- a/app/Http/Controllers/Admin/UserController.php +++ b/app/Http/Controllers/Admin/UserController.php @@ -78,7 +78,7 @@ class UserController extends Controller "name" => "required|string|min:4|max:30", "pterodactyl_id" => "required|numeric|unique:users,pterodactyl_id,{$user->id}", "email" => "required|string|email", - "credits" => "required|numeric|min:0|max:999999", + "credits" => "required|numeric|min:0|max:99999999", "server_limit" => "required|numeric|min:0|max:1000000", "role" => Rule::in(['admin', 'mod', 'client', 'member']), ]); diff --git a/app/Http/Controllers/Admin/VoucherController.php b/app/Http/Controllers/Admin/VoucherController.php index 9d439f4a..ec742092 100644 --- a/app/Http/Controllers/Admin/VoucherController.php +++ b/app/Http/Controllers/Admin/VoucherController.php @@ -7,9 +7,13 @@ use App\Models\Voucher; use Illuminate\Contracts\Foundation\Application; use Illuminate\Contracts\View\Factory; use Illuminate\Contracts\View\View; +use Illuminate\Http\JsonResponse; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Http\Response; +use Illuminate\Support\Facades\Validator; +use Illuminate\Validation\ValidationData; +use Illuminate\Validation\ValidationException; class VoucherController extends Controller { @@ -42,11 +46,11 @@ class VoucherController extends Controller public function store(Request $request) { $request->validate([ - 'memo' => 'sometimes|string|max:191', - 'code' => 'required|string|alpha_dash|max:36', - 'uses' => 'required|numeric|max:2147483647', - 'credits' => 'required|numeric|between:0,99999999', - 'expires_at' => 'required|date|after:today', + 'memo' => 'sometimes|string|max:191', + 'code' => 'required|string|alpha_dash|max:36', + 'uses' => 'required|numeric|max:2147483647', + 'credits' => 'required|numeric|between:0,99999999', + 'expires_at' => 'nullable|date|after:1 hour', ]); Voucher::create($request->except('_token')); @@ -100,9 +104,49 @@ class VoucherController extends Controller return redirect()->back()->with('success', 'voucher has been removed!'); } + /** + * @param Request $request + * @return JsonResponse + * @throws ValidationException + */ + public function redeem(Request $request) + { + #general validations + $request->validate([ + 'code' => 'required|exists:vouchers,code' + ]); + + #get voucher by code + $voucher = Voucher::where('code' , '=' , $request->input('code'))->firstOrFail(); + + #extra validations + if ($voucher->getStatus() == 'USES_LIMIT_REACHED') throw ValidationException::withMessages([ + 'code' => 'This voucher has reached the maximum amount of uses' + ]); + + if ($voucher->getStatus() == 'EXPIRED') throw ValidationException::withMessages([ + 'code' => 'This voucher has expired' + ]); + + if (!$request->user()->vouchers()->where('id' , '=' , $voucher->id)->get()->isEmpty()) throw ValidationException::withMessages([ + 'code' => 'You already redeemed this voucher code' + ]); + + if ($request->user()->credits + $voucher->credits >= 99999999) throw ValidationException::withMessages([ + 'code' => "You can't redeem a voucher with this many credits" + ]); + + #redeem voucher + $voucher->redeem($request->user()); + + return response()->json([ + 'success' => "{$voucher->credits} credits have been added to your balance!" + ]); + } + public function dataTable() { - $query = Voucher::with(['users']); + $query = Voucher::query(); return datatables($query) ->addColumn('actions', function (Voucher $voucher) { @@ -120,7 +164,7 @@ class VoucherController extends Controller ->addColumn('status', function (Voucher $voucher) { $color = 'success'; if ($voucher->getStatus() != 'VALID') $color = 'danger'; - return ''. $voucher->getStatus() .''; + return '' . $voucher->getStatus() . ''; }) ->editColumn('uses', function (Voucher $voucher) { $userCount = $voucher->users()->count(); @@ -130,12 +174,13 @@ class VoucherController extends Controller return number_format($voucher->credits, 2, '.', ''); }) ->editColumn('expires_at', function (Voucher $voucher) { + if (!$voucher->expires_at) return ""; return $voucher->expires_at ? $voucher->expires_at->diffForHumans() : ''; }) - ->editColumn('code' , function (Voucher $voucher) { + ->editColumn('code', function (Voucher $voucher) { return "{$voucher->code}"; }) - ->rawColumns(['actions' , 'code' , 'status']) + ->rawColumns(['actions', 'code', 'status']) ->make(); } diff --git a/app/Models/User.php b/app/Models/User.php index 61605aea..a6547099 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -7,17 +7,30 @@ use App\Notifications\Auth\QueuedVerifyEmail; use App\Notifications\WelcomeMessage; use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Database\Eloquent\Factories\HasFactory; +use Illuminate\Database\Eloquent\Relations\BelongsToMany; +use Illuminate\Database\Eloquent\Relations\HasMany; +use Illuminate\Database\Eloquent\Relations\HasOne; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; use Spatie\Activitylog\Traits\CausesActivity; use Spatie\Activitylog\Traits\LogsActivity; +/** + * Class User + * @package App\Models + */ class User extends Authenticatable implements MustVerifyEmail { use HasFactory, Notifiable, LogsActivity, CausesActivity; + /** + * @var string[] + */ protected static $logAttributes = ['name', 'email']; + /** + * @var string[] + */ protected static $ignoreChangedAttributes = [ 'remember_token', 'credits', @@ -68,6 +81,9 @@ class User extends Authenticatable implements MustVerifyEmail 'last_seen' => 'datetime', ]; + /** + * + */ public static function boot() { parent::boot(); @@ -93,20 +109,32 @@ class User extends Authenticatable implements MustVerifyEmail }); } + /** + * + */ public function sendEmailVerificationNotification() { $this->notify(new QueuedVerifyEmail); } + /** + * @return string + */ public function credits() { return number_format($this->credits, 2, '.', ''); } + /** + * @return string + */ public function getAvatar(){ return "https://www.gravatar.com/avatar/" . md5(strtolower(trim($this->email))); } + /** + * @return string + */ public function creditUsage() { $usage = 0; @@ -118,6 +146,9 @@ class User extends Authenticatable implements MustVerifyEmail return number_format($usage, 2, '.', ''); } + /** + * @return array|string|string[] + */ public function getVerifiedStatus(){ $status = ''; if ($this->hasVerifiedEmail()) $status .= 'email '; @@ -126,15 +157,31 @@ class User extends Authenticatable implements MustVerifyEmail return $status; } + /** + * @return BelongsToMany + */ + public function vouchers(){ + return $this->belongsToMany(Voucher::class); + } + + /** + * @return HasOne + */ public function discordUser(){ return $this->hasOne(DiscordUser::class); } + /** + * @return HasMany + */ public function servers() { return $this->hasMany(Server::class); } + /** + * @return HasMany + */ public function payments() { return $this->hasMany(Payment::class); diff --git a/app/Models/Voucher.php b/app/Models/Voucher.php index 9071a9a2..0315c8d9 100644 --- a/app/Models/Voucher.php +++ b/app/Models/Voucher.php @@ -41,14 +41,35 @@ class Voucher extends Model }); } + /** + * @return string + */ public function getStatus(){ if ($this->users()->count() >= $this->uses) return 'USES_LIMIT_REACHED'; - if ($this->expires_at->isPast()) return 'EXPIRED'; + if (!is_null($this->expires_at)){ + if ($this->expires_at->isPast()) return 'EXPIRED'; + } + return 'VALID'; } /** - * @return BelongsToMany + * @param User $user + * @return float + */ + public function redeem(User $user){ + try { + $user->increment('credits' , $this->credits); + $this->users()->attach($user); + }catch (\Exception $exception) { + throw $exception; + } + + return $this->credits; + } + + /** + * @return \Illuminate\Database\Eloquent\Relations\HasMany */ public function users() { diff --git a/database/migrations/2021_07_10_062140_update_credits_to_users_table.php b/database/migrations/2021_07_10_062140_update_credits_to_users_table.php new file mode 100644 index 00000000..bce7b344 --- /dev/null +++ b/database/migrations/2021_07_10_062140_update_credits_to_users_table.php @@ -0,0 +1,32 @@ +unsignedFloat('credits', 10)->change(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('users', function (Blueprint $table) { + $table->unsignedFloat('credits')->change(); + }); + } +} diff --git a/resources/views/admin/users/edit.blade.php b/resources/views/admin/users/edit.blade.php index 0148cb9a..0358f189 100644 --- a/resources/views/admin/users/edit.blade.php +++ b/resources/views/admin/users/edit.blade.php @@ -72,7 +72,7 @@
@error('credits') diff --git a/resources/views/admin/vouchers/create.blade.php b/resources/views/admin/vouchers/create.blade.php index 33b875a2..94ff8028 100644 --- a/resources/views/admin/vouchers/create.blade.php +++ b/resources/views/admin/vouchers/create.blade.php @@ -38,7 +38,7 @@ @csrf
- + @@ -53,7 +53,7 @@ @error('credits')
@@ -84,7 +84,7 @@
- +
@@ -34,7 +36,7 @@
@@ -43,9 +45,61 @@ diff --git a/routes/web.php b/routes/web.php index 7313c70f..d4658b71 100644 --- a/routes/web.php +++ b/routes/web.php @@ -66,6 +66,14 @@ Route::middleware('auth')->group(function () { Route::get('/auth/redirect', [SocialiteController::class, 'redirect'])->name('auth.redirect'); Route::get('/auth/callback', [SocialiteController::class, 'callback'])->name('auth.callback'); + #voucher redeem + Route::post('/voucher/redeem' , [VoucherController::class , 'redeem'])->name('voucher.redeem'); + + Route::get('/test' , function (Request $request) { + $voucher = \App\Models\Voucher::first(); + dd($request->user()->vouchers()->where('id' , '=' , $voucher->id)->get()->isEmpty()); + }); + #admin Route::prefix('admin')->name('admin.')->middleware('admin')->group(function () {