DiditSessionService.php 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. <?php
  2. namespace App\Services\Didit;
  3. use App\Data\Didit\Session\Parts\Request\ContactDetailsData;
  4. use App\Data\Didit\Session\Parts\Request\ExpectedDetailsData;
  5. use App\Data\Didit\Session\SessionRequestData;
  6. use App\Data\Didit\Session\SessionResponseData;
  7. use App\Enums\IdentityVerificationStatusEnum;
  8. use App\Enums\UserTypeEnum;
  9. use App\Models\Client;
  10. use App\Models\IdentityVerification;
  11. use App\Models\Provider;
  12. use App\Models\User;
  13. use App\Services\Didit\Concerns\SendsDiditRequests;
  14. use Illuminate\Database\Eloquent\Model;
  15. use Illuminate\Support\Facades\DB;
  16. use RuntimeException;
  17. class DiditSessionService
  18. {
  19. use SendsDiditRequests;
  20. public function createSession(User $user, bool $native = false): IdentityVerification
  21. {
  22. $profile = $this->profileFor($user);
  23. if (! $profile) {
  24. throw new RuntimeException(__('identity_verification.profile_missing'));
  25. }
  26. if ($profile->identity_verification_status === IdentityVerificationStatusEnum::APPROVED) {
  27. throw new RuntimeException(__('identity_verification.already_approved'));
  28. }
  29. $maxAttempts = (int) config('services.didit.max_attempts', 3);
  30. if ((int) $profile->identity_verification_attempts >= $maxAttempts) {
  31. throw new RuntimeException(__('identity_verification.attempts_exhausted'));
  32. }
  33. $open = IdentityVerification::query()
  34. ->where('user_id', $user->id)
  35. ->whereIn('didit_status', ['Not Started', 'In Progress'])
  36. ->latest('id')
  37. ->first();
  38. if ($open) {
  39. return $open;
  40. }
  41. $response = $this->diditRequest(
  42. method: 'POST',
  43. path: '/v3/session/',
  44. errorMessage: __('identity_verification.session_failed'),
  45. payload: $this->buildRequest($user, $profile, $native),
  46. );
  47. $session = SessionResponseData::fromResponse($response);
  48. return DB::transaction(function () use ($user, $profile, $session) {
  49. $attempt = (int) $profile->identity_verification_attempts + 1;
  50. $profile->forceFill([
  51. 'identity_verification_status' => IdentityVerificationStatusEnum::PENDING->value,
  52. ])->save();
  53. return IdentityVerification::create([
  54. 'user_id' => $user->id,
  55. 'session_id' => $session->sessionId,
  56. 'session_token' => $session->sessionToken,
  57. 'workflow_id' => $session->workflowId,
  58. 'didit_status' => $session->status ?? 'Not Started',
  59. 'attempt' => $attempt,
  60. 'started_at' => now(),
  61. ]);
  62. });
  63. }
  64. public function getDecision(string $sessionId): array
  65. {
  66. return $this->diditRequest(
  67. method: 'GET',
  68. path: "/v3/session/{$sessionId}/decision/",
  69. errorMessage: __('identity_verification.decision_failed'),
  70. );
  71. }
  72. public function verificationUrl(IdentityVerification $verification): string
  73. {
  74. return 'https://verify.didit.me/pt/session/'.$verification->session_token;
  75. }
  76. //
  77. private function buildRequest(User $user, Model $profile, bool $native = false): SessionRequestData
  78. {
  79. return new SessionRequestData(
  80. workflowId: $this->workflowFor($user),
  81. vendorData: (string) $user->id,
  82. callback: $this->callbackFor($user, $native),
  83. language: 'pt',
  84. expectedDetails: ExpectedDetailsData::fromName(
  85. fullName: $user->name,
  86. dateOfBirth: $profile->birth_date?->format('Y-m-d'),
  87. document: $profile->document,
  88. ),
  89. contactDetails: new ContactDetailsData(
  90. email: $user->email,
  91. phone: $user->phone,
  92. ),
  93. );
  94. }
  95. private const APP_SCHEMES = [
  96. 'provider' => 'br.com.diarista.provider',
  97. 'client' => 'br.com.diarista.client',
  98. ];
  99. private function callbackFor(User $user, bool $native = false): ?string
  100. {
  101. $app = $user->type === UserTypeEnum::PROVIDER ? 'provider' : 'client';
  102. if ($native) {
  103. return self::APP_SCHEMES[$app].'://verification/done';
  104. }
  105. $callback = config('services.didit.callback_url');
  106. if (! is_string($callback) || $callback === '') {
  107. return null;
  108. }
  109. return $callback.(str_contains($callback, '?') ? '&' : '?').'app='.$app;
  110. }
  111. private function workflowFor(User $user): string
  112. {
  113. $workflow = $user->type === UserTypeEnum::PROVIDER
  114. ? config('services.didit.workflow_provider')
  115. : config('services.didit.workflow_client');
  116. if (! is_string($workflow) || $workflow === '') {
  117. throw new RuntimeException(__('identity_verification.workflow_missing'));
  118. }
  119. return $workflow;
  120. }
  121. private function profileFor(User $user): Provider|Client|null
  122. {
  123. return $user->type === UserTypeEnum::PROVIDER
  124. ? $user->provider
  125. : $user->client;
  126. }
  127. }