e
Size: a a a
e
Ц
Ц
R
e
A
SF
ДХ
e
e
e
e
e
<?php
namespace App\Services\PhoneVerification;
use App\Models\Home\PhoneVerificationCode;
use App\Models\User;
use App\Services\Sms\SmsService;
use Carbon\Carbon;
class PhoneVerification
{
const VERIFICATION_CODE_LENGTH = 4;
const VERIFICATION_CODE_LIFETIME_SECONDS = 300;
private SmsService $smsService;
private User $user;
private string $newPhone;
public function __construct()
{
$this->smsService = new SmsService;
}
/**
* Отправка нового кода подтверждения
*
* @param User $user
* @param string|null $newPhone
* @return bool
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function sendVerificationCode(User $user, string $newPhone = null): bool
{
$this->user = $user;
if ($newPhone) {
$this->newPhone = $newPhone;
}
if (!$this->checkInterval()) {
return false;
}
$code = $this->createVerificationCode();
return $this->smsService->sms($this->getPhone(), $code)->send();
}
/**
* Создать новый код
*
* @param User $user
* @return string
*/
private function createVerificationCode(): string
{
$newVerificationCode = $this->user->phoneVerificationCodes()->create([
'code' => $this->generateVerificationCode(),
'expiration' => $this->calculateVerificationCodeExpiration(),
'phone' => $this->getPhone()
]);
return $newVerificationCode->code;
}
/**
* Генерирует код подтверждения с учетом установленной длины
*
* @param int $length
* @return string
*/
private function generateVerificationCode(int $length = self::VERIFICATION_CODE_LENGTH): string
{
config('auth.phone_verification_code.length') ??
$length = config('auth.phone_verification_code.length');
$code = '';
for($i = 0; $i < $length; $i++) {
$code .= mt_rand(0, 9);
}
return $code;
}
/**
* Рассчитывает время истечения актуальности кода подтверждения
*
* @param int $lifetimeSeconds
* @return \DateTime
*/
private function calculateVerificationCodeExpiration(int $lifetimeSeconds = self::VERIFICATION_CODE_LIFETIME_SECONDS): \DateTime
{
config('auth.phone_verification_code.lifetime') ??
$lifetimeSeconds = config('auth.phone_verification_code.lifetime');
return Carbon::now()->addSeconds($lifetimeSeconds)->toDateTime();
}
private function getPhone(): ?string
{
return empty($this->newPhone) ? $this->user->phone : $this->newPhone;
}
private function checkInterval(): bool
{
$interval = config('auth.phone_verification_code.interval');
return PhoneVerificationCode::where([
['user_id', $this->user->id],
['created_at', '>', Carbon::now()->subSeconds($interval)],
])->doesntExist();
}
}
e
<?php
namespace App\Services\Sms;
use GuzzleHttp\Client as GuzzleClient;
use Illuminate\Support\Facades\Log;
class SmsService
{
const SENDER_NAME = 'NAME';
private Sms $sms;
private GuzzleClient $httpClient;
public function __construct()
{
$this->httpClient = new GuzzleClient(['base_uri' => 'https://api.turbosms.ua']);
}
/**
* Новый экземпляр сообщения
*
* @param string $recipient
* @param string $text
* @return $this
*/
public function sms(string $recipient, string $text): SmsService
{
$this->sms = new Sms($recipient, $text);
return $this;
}
/**
* Отправка смс
*
* @throws \GuzzleHttp\Exception\GuzzleException
* @return bool
*/
public function send(): bool
{
$response = $this->httpClient->request('POST', '/message/send.json', [
'json' => $this->buildSms($this->sms),
'headers' => [
'Authorization' => 'Bearer ' . env('TURBO_SMS_API_TOKEN'),
'Content-Type' => 'application/json'
]
]);
$response = json_decode($response->getBody()->getContents(), true);
if (($response['response_code'] == 0 and $response['response_status'] == "OK") or
($response['response_code'] == 801 and $response['response_status'] == "SUCCESS_MESSAGE_SENT")) {
Log::channel('sms_log')
->info('Смс отправлено ' . $this->sms->recipient. ': ' . $this->sms->text);
return true;
} else {
Log::channel('sms_log')
->info('Смс НЕ отправлено ' . $this->sms->recipient. ': code: ' .
$response['response_code'] . ' status ' . $response['response_status']);
return false;
}
}
/**
* Преобразование смс в нужный формат
*
* @param Sms $sms
* @return array
*/
private function buildSms(Sms $sms): array
{
$smsText = $sms->text;
if (env('APP_ENV') !== 'production') {
$smsText .= ' (' . env('APP_ENV') . ')';
}
return [
'recipients' => [
(string) $sms->recipient
],
'sms' => [
'sender' => self::SENDER_NAME,
'text' => $smsText
]
];
}
}
e
e
e
G
ВЕ
PG