Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 84
0.00% covered (danger)
0.00%
0 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
OrangeCiSmsProvider
0.00% covered (danger)
0.00%
0 / 84
0.00% covered (danger)
0.00%
0 / 3
182
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
20
 getAccessToken
0.00% covered (danger)
0.00%
0 / 22
0.00% covered (danger)
0.00%
0 / 1
12
 send
0.00% covered (danger)
0.00%
0 / 53
0.00% covered (danger)
0.00%
0 / 1
42
1<?php
2
3namespace App\Services\Communication\Providers\Sms\Country\CI;
4
5use App\Services\Communication\Contracts\SmsProviderInterface;
6
7class OrangeCiSmsProvider implements SmsProviderInterface
8{
9    private string $apiUrl;
10    private string $clientId;
11    private string $clientSecret;
12    private string $senderId;
13    private string $senderName;  // ← déclaration explicite (fix deprecation PHP 8.2)
14
15    public function __construct(array $credentials)
16    {
17        $this->apiUrl       = rtrim($credentials['api_url'] ?? 'https://api.orange.com/smsmessaging/v1', '/');
18        $this->clientId     = trim($credentials['client_id']     ?? '');
19        $this->clientSecret = trim($credentials['client_secret'] ?? '');
20        $this->senderId     = trim($credentials['sender_id']     ?? $credentials['sender'] ?? '');
21        $this->senderName   = trim($credentials['sender_name']   ?? '');
22
23        if (empty($this->clientId) || empty($this->clientSecret)) {
24            throw new \RuntimeException('OrangeCiSmsProvider: client_id et client_secret sont obligatoires');
25        }
26        if (empty($this->senderId)) {
27            throw new \RuntimeException('OrangeCiSmsProvider: sender_id est obligatoire');
28        }
29    }
30
31    // ================================================================
32    // TOKEN
33    // ================================================================
34
35    private function getAccessToken(): string
36    {
37        $basicAuth = base64_encode($this->clientId . ':' . $this->clientSecret);
38        $client    = \Config\Services::curlrequest();
39
40        $response = $client->post('https://api.orange.com/oauth/v3/token', [
41            'headers' => [
42                'Authorization' => 'Basic ' . $basicAuth,
43                'Content-Type'  => 'application/x-www-form-urlencoded',
44                'Accept'        => 'application/json',
45            ],
46            'form_params' => ['grant_type' => 'client_credentials'],
47            'http_errors' => false,
48            'timeout'     => 30,
49        ]);
50
51        $status = $response->getStatusCode();
52        $body   = $response->getBody();
53        $parsed = json_decode($body, true);
54
55        if ($status !== 200 || empty($parsed['access_token'])) {
56            log_message('error', "[OrangeCiSmsProvider::getAccessToken] HTTP {$status} | body={$body}");
57            throw new \RuntimeException(
58                "[OrangeCiSmsProvider::getAccessToken] HTTP {$status} — {$body}"
59            );
60        }
61
62        log_message('debug', '[OrangeCiSmsProvider::getAccessToken] Token obtenu OK');
63
64        return $parsed['access_token'];
65    }
66
67    // ================================================================
68    // SEND
69    // ================================================================
70
71 public function send(string $to, string $message): bool
72{
73    $token = $this->getAccessToken();
74
75    // ── 1. Formatage du destinataire ─────────────────────────
76    $digits = ltrim(preg_replace('/[^0-9+]/', '', $to), '+');
77    if (str_starts_with($digits, '225')) {
78        $digits = substr($digits, 3);
79    }
80    $toClean = '+225' . ltrim($digits, '0');
81
82    // ── 2. senderAddress depuis credentials (sender_id) ──────
83    $rawSender = preg_replace('/[^0-9+]/', '', $this->senderId);
84    if (empty($rawSender)) {
85        throw new \RuntimeException('OrangeCiSmsProvider: sender_id est obligatoire');
86    }
87    if (!str_starts_with($rawSender, '+')) {
88        $rawSender = '+' . $rawSender;
89    }
90    $countrySender = 'tel:' . $rawSender;
91    $senderForUrl  = rawurlencode($countrySender);
92    $url           = $this->apiUrl . '/outbound/' . $senderForUrl . '/requests';
93
94    // ── 3. Construction du body ───────────────────────────────
95    $requestBody = [
96        'address'                => 'tel:' . $toClean,
97        'senderAddress'          => $countrySender,
98        'outboundSMSTextMessage' => ['message' => $message],
99    ];
100
101    if (!empty($this->senderName)) {
102        $requestBody['senderName'] = $this->senderName;
103    }
104
105    log_message('debug', '[OrangeCiSmsProvider::send] url='  . $url);
106    log_message('debug', '[OrangeCiSmsProvider::send] body=' . json_encode($requestBody));
107
108    // ── 4. Envoi ──────────────────────────────────────────────
109    $client   = \Config\Services::curlrequest();
110    $response = $client->post($url, [
111        'headers' => [
112            'Authorization' => 'Bearer ' . $token,
113            'Content-Type'  => 'application/json',
114            'Accept'        => 'application/json',
115        ],
116        'json'        => ['outboundSMSMessageRequest' => $requestBody],
117        'http_errors' => false,
118        'timeout'     => 30,
119    ]);
120
121    $status       = $response->getStatusCode();
122    $responseBody = $response->getBody();
123
124    // ── 5. Succès ─────────────────────────────────────────────
125    if ($status === 201) {
126        $parsed      = json_decode($responseBody, true);
127        $resourceUrl = $parsed['outboundSMSMessageRequest']['resourceURL'] ?? 'N/A';
128        log_message('info', '[OrangeCiSmsProvider::send] SUCCESS'
129            . ' | to='       . $toClean
130            . ' | sender='   . $countrySender
131            . ' | resource=' . $resourceUrl
132        );
133        return true;
134    }
135
136    // ── 6. Échec ──────────────────────────────────────────────
137    log_message('error', '[OrangeCiSmsProvider::send] FAIL'
138        . ' | status=' . $status
139        . ' | to='     . $toClean
140        . ' | sender=' . $countrySender
141        . ' | body='   . $responseBody
142    );
143
144    throw new \RuntimeException(
145        "[OrangeCiSmsProvider::send] HTTP {$status} — to={$toClean} sender={$countrySender} — {$responseBody}"
146    );
147}
148}