validated(); $result = $otp->verify($data['phone'], $data['purpose'], $data['code']); if (! $result->isOk()) { return $this->fail( $this->messageFor($result->status), ['code' => [$result->status]], 422, ); } $user = User::where('phone', $data['phone'])->first(); if ($user && in_array($data['purpose'], [OtpCode::PURPOSE_REGISTER, OtpCode::PURPOSE_PHONE_VERIFY], true)) { $user->forceFill([ 'phone_verified_at' => now(), 'status' => $user->status === User::STATUS_PENDING ? User::STATUS_ACTIVE : $user->status, ])->save(); } $payload = [ 'verified' => true, 'purpose' => $data['purpose'], ]; if ($data['purpose'] === OtpCode::PURPOSE_LOGIN && $user) { $token = $user->createToken($request->userAgent() ?? 'otp-login'); $payload['user'] = new UserResource($user); $payload['token'] = $token->plainTextToken; $payload['token_type'] = 'Bearer'; } elseif ($user) { $payload['user'] = new UserResource($user); } return $this->ok($payload, 'OTP verified'); } private function messageFor(string $status): string { return match ($status) { OtpVerifyResult::STATUS_INVALID => 'Invalid code', OtpVerifyResult::STATUS_EXPIRED => 'Code expired', OtpVerifyResult::STATUS_EXHAUSTED => 'Too many attempts. Request a new code.', OtpVerifyResult::STATUS_NOT_FOUND => 'No active code for this destination', default => 'Verification failed', }; } }