feat: Implement SMS sending functionality with KavehNegar and Rangineh providers

- Add SendSmsMessage class for encapsulating SMS message data.
- Create KavehNegarProvider and RanginehProvider classes implementing SmsProviderInterface for sending SMS.
- Implement SmsLogRepository and SmsTemplateRepository for managing SMS logs and templates.
- Develop SendSmsHandler for handling SMS sending messages.
- Create SmsService to manage SMS dispatching and logging.
- Add UserProfileController for managing user profiles with CRUD operations.
- Implement UserProfile entity and repository for user profile data management.
- Update symfony.lock and bootstrap.php for project dependencies and environment setup.
This commit is contained in:
hamed
2026-06-09 22:00:34 +03:30
commit de1a78a235
222 changed files with 36388 additions and 0 deletions
+58
View File
@@ -0,0 +1,58 @@
<?php
namespace App\Sms\Provider;
use Symfony\Contracts\HttpClient\HttpClientInterface;
class KavehNegarProvider implements SmsProviderInterface
{
private const BASE = 'https://api.kavenegar.com/v1';
public function __construct(
private readonly HttpClientInterface $httpClient,
private readonly string $apiKey,
private readonly string $sender,
) {}
public function getName(): string { return 'kavenegar'; }
public function send(string $mobile, string $message): bool
{
try {
$resp = $this->httpClient->request('POST',
self::BASE . '/' . $this->apiKey . '/sms/send.json', [
'body' => http_build_query([
'receptor' => $mobile,
'message' => $message,
'sender' => $this->sender,
]),
'timeout' => 10,
]
);
$data = $resp->toArray();
return ($data['return']['status'] ?? 0) === 200;
} catch (\Throwable) {
return false;
}
}
public function sendTemplate(string $mobile, string $templateCode, array $vars): bool
{
try {
$params = ['receptor' => $mobile, 'template' => $templateCode];
foreach (array_values($vars) as $i => $v) {
$params['token' . ($i > 0 ? $i + 1 : '')] = $v;
}
$resp = $this->httpClient->request('POST',
self::BASE . '/' . $this->apiKey . '/verify/lookup.json', [
'body' => http_build_query($params),
'timeout' => 10,
]
);
$data = $resp->toArray();
return ($data['return']['status'] ?? 0) === 200;
} catch (\Throwable) {
return false;
}
}
}
+50
View File
@@ -0,0 +1,50 @@
<?php
namespace App\Sms\Provider;
use Symfony\Contracts\HttpClient\HttpClientInterface;
class RanginehProvider implements SmsProviderInterface
{
private const BASE = 'https://rest.payamresan.com/api/v1';
public function __construct(
private readonly HttpClientInterface $httpClient,
private readonly string $apiKey,
private readonly string $sender,
) {}
public function getName(): string { return 'rangineh'; }
public function send(string $mobile, string $message): bool
{
try {
$resp = $this->httpClient->request('POST', self::BASE . '/send', [
'json' => ['from' => $this->sender, 'to' => [$mobile], 'text' => $message],
'headers' => ['ApiKey' => $this->apiKey],
'timeout' => 10,
]);
return $resp->getStatusCode() === 200;
} catch (\Throwable) {
return false;
}
}
public function sendTemplate(string $mobile, string $templateCode, array $vars): bool
{
try {
$resp = $this->httpClient->request('POST', self::BASE . '/send/verify', [
'json' => [
'mobile' => $mobile,
'template' => $templateCode,
'params' => $vars,
],
'headers' => ['ApiKey' => $this->apiKey],
'timeout' => 10,
]);
return $resp->getStatusCode() === 200;
} catch (\Throwable) {
return false;
}
}
}
+14
View File
@@ -0,0 +1,14 @@
<?php
namespace App\Sms\Provider;
interface SmsProviderInterface
{
public function getName(): string;
/** @return bool true on success */
public function send(string $mobile, string $message): bool;
/** Send via approved template (pattern send) */
public function sendTemplate(string $mobile, string $templateCode, array $vars): bool;
}