| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171 |
- <?php
- namespace App\Services;
- use App\Enums\IdentityVerificationStatusEnum;
- use App\Enums\UserTypeEnum;
- use App\Http\Resources\IdentityVerificationResource;
- use App\Models\Client;
- use App\Models\IdentityVerification;
- use App\Models\Provider;
- use App\Models\User;
- use App\Services\Didit\DiditSessionService;
- use Illuminate\Database\Eloquent\Model;
- use Illuminate\Support\Facades\DB;
- use RuntimeException;
- class IdentityVerificationService
- {
- public function __construct(
- private readonly DiditSessionService $sessionService,
- private readonly ProviderService $providerService,
- ) {}
- public function startFor(User $user, bool $native = false): array
- {
- $verification = $this->sessionService->createSession($user, $native);
- return [
- 'session_id' => $verification->session_id,
- 'verification_url' => $this->sessionService->verificationUrl($verification),
- 'status' => $verification->didit_status,
- 'attempt' => $verification->attempt,
- 'attempts_left' => $this->attemptsLeft($this->profileFor($user)),
- ];
- }
- public function statusFor(User $user): array
- {
- $profile = $this->profileFor($user);
- $verification = $user->latestIdentityVerification()->first();
- return [
- 'identity_verification_status' => $profile?->identity_verification_status?->value
- ?? IdentityVerificationStatusEnum::NOT_STARTED->value,
- 'identity_verified_at' => $profile?->identity_verified_at,
- 'required' => (bool) ($profile?->identity_verification_required ?? false),
- 'attempts_left' => $this->attemptsLeft($profile),
- 'can_retry' => $this->canRetry($profile),
- 'has_open_session' => $verification !== null
- && in_array($verification->didit_status, ['Not Started', 'In Progress'], true),
- 'last_verification' => $verification
- ? new IdentityVerificationResource($verification)
- : null,
- ];
- }
- public function pending(int $perPage, int $page): array
- {
- $paginator = IdentityVerification::query()
- ->with('user')
- ->whereIn('didit_status', ['In Review', 'Declined'])
- ->orderBy('completed_at')
- ->paginate(perPage: $perPage, page: $page);
- return [
- 'data' => IdentityVerificationResource::collection($paginator->items()),
- 'total' => $paginator->total(),
- 'from' => $paginator->firstItem(),
- 'to' => $paginator->lastItem(),
- ];
- }
- /**
- * Busca o laudo com as imagens frescas: as URLs do Didit sao presignadas e de
- * vida curta, por isso nao sao persistidas e sim buscadas na hora de exibir.
- */
- public function findWithFreshDecision(int $id): ?array
- {
- $verification = IdentityVerification::with('user')->find($id);
- if (! $verification) {
- return null;
- }
- $decision = null;
- try {
- $decision = $this->sessionService->getDecision($verification->session_id);
- } catch (\Throwable) {
- // Laudo persistido ja basta para a decisao; so as imagens ficam indisponiveis.
- }
- return [
- 'verification' => new IdentityVerificationResource($verification),
- 'decision' => $decision ?? $verification->decision,
- 'decision_fresh' => $decision !== null,
- ];
- }
- public function review(int $id, bool $approved, User $reviewer, ?string $comment): IdentityVerification
- {
- $verification = IdentityVerification::with('user')->find($id);
- if (! $verification) {
- throw new RuntimeException(__('identity_verification.not_found'));
- }
- $user = $verification->user;
- $profile = $user ? $this->profileFor($user) : null;
- if (! $profile) {
- throw new RuntimeException(__('identity_verification.profile_missing'));
- }
- DB::transaction(function () use ($verification, $profile, $approved, $reviewer, $comment) {
- $verification->forceFill([
- 'reviewed_by' => $reviewer->id,
- 'reviewed_at' => now(),
- 'review_comment' => $comment,
- ])->save();
- $profile->forceFill([
- 'identity_verification_status' => $approved
- ? IdentityVerificationStatusEnum::APPROVED->value
- : IdentityVerificationStatusEnum::DECLINED->value,
- 'identity_verified_at' => $approved ? now() : null,
- ]);
- if ($approved && $profile instanceof Provider) {
- $profile->document_verified = true;
- }
- $profile->save();
- });
- if ($approved && $profile instanceof Provider) {
- $this->providerService->approve($profile->id);
- }
- return $verification->refresh();
- }
- //
- private function attemptsLeft(?Model $profile): int
- {
- $max = (int) config('services.didit.max_attempts', 3);
- return max(0, $max - (int) ($profile?->identity_verification_attempts ?? 0));
- }
- private function canRetry(?Model $profile): bool
- {
- if (! $profile) {
- return false;
- }
- $status = $profile->identity_verification_status;
- return $status instanceof IdentityVerificationStatusEnum
- && $status->allowsNewAttempt()
- && $this->attemptsLeft($profile) > 0;
- }
- private function profileFor(User $user): Provider|Client|null
- {
- return $user->type === UserTypeEnum::PROVIDER
- ? $user->provider
- : $user->client;
- }
- }
|