Files
boursecu_server/app/Services/SmsBilling/SuperAdminSmsClient.php
T
2026-08-05 18:34:07 +03:30

211 lines
6.1 KiB
PHP

<?php
namespace App\Services\SmsBilling;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
/**
* SuperAdmin SMS wallet client (status / consume).
*
* Preferred: SUPERADMIN_SMS_BASE_URL + SUPERADMIN_SMS_SHARED_SECRET (+ optional DOMAIN)
* Fallback: SUPERADMIN_REMOTE_TOKEN
*/
class SuperAdminSmsClient
{
public function enabled(): bool
{
$base = trim((string) config('services.superadmin.sms_base_url', ''));
if ($base === '') {
return false;
}
return $this->sharedSecret() !== '' || $this->bearerToken() !== '';
}
/**
* @return array{
* allowed: bool,
* blocked: bool,
* reason: ?string,
* balanceRials: int,
* pricePerPartRials: int,
* estimatedCostRials: int,
* balanceLevel: string
* }|null
*/
public function status(int $parts = 1, bool $isAlert = false): ?array
{
if (! $this->enabled()) {
return null;
}
try {
$path = '/api/remote/sms/status';
$response = Http::withHeaders($this->authHeaders('GET', $path))
->acceptJson()
->timeout(4)
->get($this->url('status'), [
'parts' => max(1, $parts),
'isAlert' => $isAlert ? '1' : '0',
]);
if (! $response->successful()) {
Log::warning('SuperAdmin SMS status failed', [
'status' => $response->status(),
'body' => $response->body(),
]);
return null;
}
$data = $response->json();
if (! is_array($data)) {
return null;
}
return [
'allowed' => (bool) ($data['allowed'] ?? false),
'blocked' => (bool) ($data['blocked'] ?? false),
'reason' => isset($data['reason']) ? (string) $data['reason'] : null,
'balanceRials' => (int) ($data['balanceRials'] ?? 0),
'pricePerPartRials' => (int) ($data['pricePerPartRials'] ?? 0),
'estimatedCostRials' => (int) ($data['estimatedCostRials'] ?? 0),
'balanceLevel' => (string) ($data['balanceLevel'] ?? 'unknown'),
];
} catch (\Throwable $e) {
Log::warning('SuperAdmin SMS status exception', [
'error' => $e->getMessage(),
]);
return null;
}
}
/**
* @return array{ok: bool, balanceRials?: int, costRials?: int}|null
*/
public function consume(
string $receiver,
string $body,
int $parts,
?string $provider = null,
?string $source = null,
bool $isAlert = false
): ?array {
if (! $this->enabled()) {
return null;
}
try {
$path = '/api/remote/sms/consume';
$payload = [
'receiver' => $receiver,
'body' => mb_substr($body, 0, 1000),
'parts' => max(1, $parts),
'provider' => $provider,
'source' => $source,
'isAlert' => $isAlert,
];
$response = Http::withHeaders($this->authHeaders('POST', $path))
->acceptJson()
->timeout(6)
->post($this->url('consume'), $payload);
if (! $response->successful()) {
Log::error('SuperAdmin SMS consume failed', [
'status' => $response->status(),
'body' => $response->body(),
]);
return null;
}
$data = $response->json();
if (! is_array($data) || ! ($data['ok'] ?? false)) {
return null;
}
return [
'ok' => true,
'balanceRials' => (int) ($data['balanceRials'] ?? 0),
'costRials' => (int) ($data['costRials'] ?? 0),
];
} catch (\Throwable $e) {
Log::error('SuperAdmin SMS consume exception', [
'error' => $e->getMessage(),
]);
return null;
}
}
/**
* @return array<string, string>
*/
private function authHeaders(string $method, string $canonicalPath): array
{
$secret = $this->sharedSecret();
if ($secret !== '') {
$domain = $this->domain();
$timestamp = (string) time();
$payload = $timestamp.'.'.strtoupper($method).'.'.$canonicalPath.'.'.$domain;
$signature = hash_hmac('sha256', $payload, $secret);
return [
'X-Abshode-Domain' => $domain,
'X-Abshode-Timestamp' => $timestamp,
'X-Abshode-Signature' => $signature,
];
}
$token = $this->bearerToken();
if ($token !== '') {
return [
'Authorization' => 'Bearer '.$token,
];
}
return [];
}
private function sharedSecret(): string
{
return trim((string) config('services.superadmin.sms_shared_secret', ''));
}
private function bearerToken(): string
{
return trim((string) config('services.superadmin.remote_token', ''));
}
private function domain(): string
{
$override = trim((string) config('services.superadmin.sms_domain', ''));
if ($override !== '') {
return $this->normalizeHost($override);
}
$fromApp = parse_url((string) config('app.url'), PHP_URL_HOST);
return $this->normalizeHost(is_string($fromApp) ? $fromApp : '');
}
private function normalizeHost(string $value): string
{
$host = strtolower(trim($value));
$host = preg_replace('#^https?://#', '', $host) ?? $host;
$host = explode('/', $host)[0] ?? $host;
$host = explode(':', $host)[0] ?? $host;
return $host;
}
private function url(string $path): string
{
$base = rtrim((string) config('services.superadmin.sms_base_url', ''), '/');
return $base.'/'.ltrim($path, '/');
}
}