Files
2025-11-30 11:57:22 +01:00

124 lines
3.2 KiB
PHP

<?php
namespace Paymenter\Extensions\Others\ComplianceTools\Services;
use App\Helpers\ExtensionHelper;
use App\Models\User;
use Illuminate\Support\Facades\Hash;
use Paymenter\Extensions\Others\ComplianceTools\Models\SupportPin;
class SupportPinService
{
public function __construct(protected AuditLogService $auditLog)
{
}
public function generate(User $user): array
{
$extension = ExtensionHelper::getExtension('other', 'ComplianceTools');
$length = (int) ($extension?->config('pin_length') ?? 6);
$expiryMinutes = (int) ($extension?->config('pin_expiry_minutes') ?? 15);
$plainPin = $this->randomPin($length);
$lookup = hash('sha256', $plainPin);
SupportPin::query()
->where('user_id', $user->getKey())
->whereNull('revoked_at')
->update(['revoked_at' => now()]);
$pin = SupportPin::create([
'user_id' => $user->getKey(),
'pin_hash' => Hash::make($plainPin),
'pin_lookup' => $lookup,
'expires_at' => now()->addMinutes($expiryMinutes),
'created_ip' => request()->ip(),
'created_user_agent' => request()->header('User-Agent'),
]);
$this->auditLog->record(
category: 'support_pin',
event: 'generated',
context: [
'support_pin_id' => $pin->getKey(),
'expires_at' => $pin->expires_at?->toAtomString(),
],
subject: $user
);
return [$pin, $plainPin];
}
public function revoke(SupportPin $pin, ?User $actor = null): SupportPin
{
if ($pin->revoked_at) {
return $pin;
}
$pin->forceFill(['revoked_at' => now()])->save();
$this->auditLog->record(
category: 'support_pin',
event: 'revoked',
context: ['support_pin_id' => $pin->getKey()],
subject: $pin->user,
actor: $actor
);
return $pin;
}
public function validate(string $plainPin): ?SupportPin
{
$plainPin = strtoupper(trim($plainPin));
if ($plainPin === '') {
return null;
}
$lookup = hash('sha256', $plainPin);
$pin = SupportPin::query()
->where('pin_lookup', $lookup)
->first();
if (!$pin instanceof SupportPin) {
return null;
}
if (!Hash::check($plainPin, $pin->pin_hash)) {
return null;
}
if ($pin->revoked_at || $pin->expires_at->isPast()) {
return null;
}
$pin->forceFill(['last_validated_at' => now()])->save();
$this->auditLog->record(
category: 'support_pin',
event: 'validated',
context: [
'support_pin_id' => $pin->getKey(),
],
subject: $pin->user
);
return $pin;
}
protected function randomPin(int $length): string
{
$alphabet = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';
$poolLength = strlen($alphabet) - 1;
$pin = '';
for ($i = 0; $i < $length; $i++) {
$pin .= $alphabet[random_int(0, $poolLength)];
}
return $pin;
}
}