https and sms job add

This commit is contained in:
Devit
2026-03-28 23:15:36 +05:00
parent fb03d05de9
commit c02f5d5bf8
6 changed files with 266 additions and 1 deletions

174
app/Jobs/SendSmsJob.php Normal file
View File

@@ -0,0 +1,174 @@
<?php
namespace App\Jobs;
use App\Models\EskizToken;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
class SendSmsJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected string $phoneNumber;
protected int $code;
public function __construct(string $phoneNumber, int $code)
{
$this->phoneNumber = $phoneNumber;
$this->code = $code;
}
public function handle(): void
{
$phoneNumber = $this->normalizePhoneNumber($this->phoneNumber);
// Bazadagi tokenni olish
$token = $this->getTokenFromDb();
// Token yo'q yoki expired bo'lsa yangilash
if (!$token) {
$token = $this->refreshToken();
if (!$token) {
Log::error('Eskiz token olish imkonsiz', ['phone' => $phoneNumber]);
return;
}
}
$postData = [
'mobile_phone' => $phoneNumber,
'message' => "DELGO hisobiga kirish uchun kod: {$this->code} Ushbu kodni ulashmang! #WBcz5ARFKcp",
// 'message' => "DELGO hisobiga kirish uchun kod: {$this->code} Ushbu kodni ulashmang! #WBcz5ARFKcp",
'from' => "4546"
];
$response = $this->sendSmsRequest($token, $postData, $phoneNumber);
// Token eskirgan bolsa qayta olish
if ($response['http_code'] === 401) {
Log::warning("Eskiz token muddati tugagan, yangidan olinmoqda...");
$token = $this->refreshToken();
if ($token) {
$this->sendSmsRequest($token, $postData, $phoneNumber);
}
}
}
private function sendSmsRequest(string $token, array $postData, string $phoneNumber): array
{
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://notify.eskiz.uz/api/message/sms/send",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query($postData),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer $token",
"Content-Type: application/x-www-form-urlencoded"
],
]);
$response = curl_exec($curl);
$httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
Log::error("Eskiz SMS cURL Error: " . $err, [
'phone' => $phoneNumber,
'http_code' => $httpCode
]);
return ['http_code' => $httpCode];
}
Log::debug("Eskiz SMS Response", [
'phone' => $phoneNumber,
'http_code' => $httpCode,
'response' => $response
]);
return ['http_code' => $httpCode, 'body' => $response];
}
private function normalizePhoneNumber(string $phoneNumber): string
{
$phoneNumber = preg_replace('/[^0-9]/', '', $phoneNumber);
if (strlen($phoneNumber) === 9 && in_array(substr($phoneNumber, 0, 2), [
'33', '77', '88', '90', '91', '93', '94', '95', '97', '98', '99'
])) {
$phoneNumber = '998' . $phoneNumber;
} elseif (str_starts_with($phoneNumber, '+998')) {
$phoneNumber = substr($phoneNumber, 1);
}
return $phoneNumber;
}
private function getTokenFromDb(): ?string
{
$record = EskizToken::latest()->first();
if (!$record) return null;
// Token muddati tugagan bo'lsa
if ($record->expires_at && now()->greaterThan($record->expires_at)) {
return null;
}
return $record->token;
}
private function refreshToken(): ?string
{
$email = config('services.eskiz.email');
$password = config('services.eskiz.password');
$postData = ['email' => $email, 'password' => $password];
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://notify.eskiz.uz/api/auth/login",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query($postData),
CURLOPT_HTTPHEADER => ["Content-Type: application/x-www-form-urlencoded"],
]);
$response = curl_exec($curl);
$httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
Log::error("Eskiz Auth Error: " . $err, ['http_code' => $httpCode]);
return null;
}
$data = json_decode($response, true);
if (isset($data['message'], $data['data']['token']) && $data['message'] === 'token_generated') {
$token = $data['data']['token'];
$expiresAt = now()->addHour(); // Token 1 soatga amal qiladi
// Tokenni bazaga saqlash (update yoki create)
EskizToken::updateOrCreate(
['id' => 1], // har doim 1-row ishlatiladi
[
'token' => $token,
'expires_at' => $expiresAt
]
);
return $token;
}
Log::error("Eskiz Auth Response Invalid", ['response' => $response]);
return null;
}
}

25
app/Models/EskizToken.php Normal file
View File

@@ -0,0 +1,25 @@
<?php
namespace App\Models;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class EskizToken extends Model
{
use HasFactory;
protected $fillable = ['token', 'expires_at'];
protected $dates = ['expires_at'];
/**
* Token amal qiladimi?
*/
public function isValid(): bool
{
return $this->expires_at && Carbon::now()->lt($this->expires_at);
}
}

View File

@@ -27,6 +27,6 @@ class AppServiceProvider extends ServiceProvider
public function boot() public function boot()
{ {
Schema::defaultStringLength(191); Schema::defaultStringLength(191);
URL::forceScheme('http'); URL::forceScheme('https');
} }
} }

View File

@@ -0,0 +1,29 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('eskiz_tokens', function (Blueprint $table) {
$table->id();
$table->text('token');
$table->timestamp('expires_at');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('eskiz_tokens');
}
};

View File

@@ -23,6 +23,42 @@ services:
condition: service_healthy condition: service_healthy
restart: unless-stopped restart: unless-stopped
horizon:
build:
context: .
dockerfile: Dockerfile
args:
WWWGROUP: '${WWWGROUP:-1000}'
container_name: ${DOCKER_CONTAINER_NAME}_horizon
command: php artisan horizon
volumes:
- .:/var/www/html
networks:
- devituz
depends_on:
mysql:
condition: service_healthy
redis:
condition: service_healthy
restart: unless-stopped
scheduler:
build:
context: .
dockerfile: Dockerfile
container_name: ${DOCKER_CONTAINER_NAME}_scheduler
command: sh -c "while [ true ]; do php artisan schedule:run --verbose --no-interaction; sleep 60; done"
volumes:
- .:/var/www/html
depends_on:
mysql:
condition: service_healthy
redis:
condition: service_healthy
networks:
- devituz
restart: unless-stopped
nginx: nginx:
image: nginx:1.25-alpine image: nginx:1.25-alpine
container_name: ${DOCKER_CONTAINER_NAME}_nginx container_name: ${DOCKER_CONTAINER_NAME}_nginx

View File

@@ -71,6 +71,7 @@ Route::group(['middleware' => ['auth:sanctum']], static function () {
Route::post('logout', [ApiController::class, 'logout']); Route::post('logout', [ApiController::class, 'logout']);
}); });
/* Non Authenticated Routes */ /* Non Authenticated Routes */
Route::get('user-exists', [ApiController::class, 'userExists']); Route::get('user-exists', [ApiController::class, 'userExists']);
Route::get('get-currencies', [ApiController::class, 'getCurrencies']); Route::get('get-currencies', [ApiController::class, 'getCurrencies']);