Initial commit - WASL Digital Wallet

This commit is contained in:
Hamza-Ayed
2026-06-20 21:55:06 +03:00
commit 7306c47368
61 changed files with 4157 additions and 0 deletions

View File

@@ -0,0 +1,86 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
/**
* @property string $purpose login/transfer/pin_change/phone_change/kyc/registration
* @property string $code_hash NEVER plain text — always hash
* @property string $channel sms/authenticator
* @property int $attempts
* @property \Illuminate\Support\Carbon $expires_at
* @property \Illuminate\Support\Carbon|null $used_at
*/
class OtpCode extends BaseModel
{
use HasFactory;
protected $fillable = [
'user_id',
'purpose',
'code_hash',
'channel',
'attempts',
'expires_at',
'used_at',
];
protected $casts = [
'attempts' => 'integer',
'expires_at' => 'datetime',
'used_at' => 'datetime',
];
// ── Relationships ──
public function user()
{
return $this->belongsTo(User::class);
}
// ── Scopes ──
public function scopeValid($query)
{
return $query->where('expires_at', '>', now())
->whereNull('used_at');
}
public function scopeForPurpose($query, string $purpose)
{
return $query->where('purpose', $purpose);
}
public function scopeForUser($query, int $userId)
{
return $query->where('user_id', $userId);
}
// ── Helpers ──
public function isExpired(): bool
{
return $this->expires_at->isPast();
}
public function isUsed(): bool
{
return !is_null($this->used_at);
}
public function hasAttemptsLeft(int $max): bool
{
return $this->attempts < $max;
}
public function incrementAttempts(): bool
{
return $this->increment('attempts');
}
public function markUsed(): bool
{
return $this->update(['used_at' => now()]);
}
}