├── .gitignore ├── README.md ├── composer.json ├── composer.lock ├── examples ├── include.php ├── index.php └── webhook.php └── lib ├── Api.php ├── ApiException.php ├── Client.php ├── Response ├── Balance.php ├── DirectNumber.php ├── IncomingCallsStatistics.php ├── NumberLookup.php ├── PbxInfo.php ├── PbxInternal.php ├── PbxRecordRequest.php ├── PbxRecording.php ├── PbxRedirection.php ├── PbxStatistics.php ├── PbxStatus.php ├── Price.php ├── Redirection.php ├── RequestCallback.php ├── Response.php ├── Sip.php ├── SipCaller.php ├── SipRedirection.php ├── SipRedirectionStatus.php ├── SipStatus.php ├── Sms.php ├── SpeechRecognition.php ├── Statistics.php ├── Tariff.php ├── Timezone.php ├── WebrtcKey.php └── Zcrm.php └── Webhook ├── AbstractNotify.php ├── NotifyAnswer.php ├── NotifyEnd.php ├── NotifyInternal.php ├── NotifyIvr.php ├── NotifyOutEnd.php ├── NotifyOutStart.php ├── NotifyRecord.php ├── NotifyStart.php ├── Request.php └── WaitDtmf.php /.gitignore: -------------------------------------------------------------------------------- 1 | vendor/ 2 | composer.phar 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Zadarma API - User class 2 | An official PHP class for work with Zadarma API. 3 | 4 | Allows to work with all API methods (including VoIP, PBX, CallBack etc). 5 | 6 | ## Requirements: 7 | - PHP >= 5.5.0 8 | - cURL 9 | - TLS v1.2 10 | 11 | ## How to use? 12 | An official documentation on Zadarma API is [here](https://zadarma.com/support/api/). 13 | 14 | Keys for authorization are in [personal account](https://my.zadarma.com/api/). 15 | 16 | ## Installation 17 | ### Via Сomposer 18 | ```sh 19 | composer require "zadarma/user-api-v1" 20 | ``` 21 | or just add this line to your `composer.json` file: 22 | ```json 23 | "zadarma/user-api-v1" 24 | ``` 25 | 26 | ### Via Git 27 | ```sh 28 | git clone git@github.com:zadarma/user-api-v1.git 29 | ``` 30 | 31 | ### \Zadarma_API\Api call code example 32 | ```php 33 | getSipStatus('YOURSIP'); 38 | echo $result->sip.' status: '.($result->is_online ? 'online' : 'offline'); 39 | } catch (\Zadarma_API\ApiException $e) { 40 | echo 'Error: '.$e->getMessage(); 41 | } 42 | 43 | ``` 44 | All other examples you can see in the "[example file](https://github.com/zadarma/user-api-v1/tree/master/examples/index.php)". 45 | 46 | ### \Zadarma_API\Client call code example 47 | ```php 48 | 'YOURSIP', 54 | 'status' => 'on' 55 | ); 56 | 57 | $zd = new \Zadarma_API\Client(YOUR_KEY, YOUR_SECRET); 58 | /* 59 | $zd->call('METHOD', 'PARAMS_ARRAY', 'REQUEST_TYPE', 'FORMAT', 'IS_AUTH'); 60 | where: 61 | - METHOD - a method API, started from /v1/ and ended by '/'; 62 | - PARAMS_ARRAY - an array of parameters to a method; 63 | - REQUEST_TYPE: GET (default), POST, PUT, DELETE; 64 | - FORMAT: json (default), xml; 65 | - IS_AUTH: true (default), false - is method under authentication or not. 66 | */ 67 | $answer = $zd->call('/v1/sip/redirection/', $params, 'put'); 68 | 69 | $answerObject = json_decode($answer); 70 | 71 | if ($answerObject->status == 'success') { 72 | echo 'Redirection on your SIP "' . $answerObject->sip . " has been changed to " . $answerObject->current_status . "."; 73 | } else { 74 | $answerObject->message; 75 | } 76 | ``` 77 | 78 | All other examples you can see in the "[examples](https://github.com/zadarma/user-api-v1/tree/master/examples)" folder. 79 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "zadarma/user-api-v1", 3 | "description": "PHP class for Zadarma API", 4 | "keywords": [ 5 | "zadarma", 6 | "api", 7 | "sip", 8 | "pbx", 9 | "free calls" 10 | ], 11 | "type": "library", 12 | "license": "MIT", 13 | "homepage": "https://github.com/zadarma/user-api-v1", 14 | "authors": [ 15 | { 16 | "name": "Zadarma", 17 | "email": "github@zadarma.com" 18 | } 19 | ], 20 | "autoload": { 21 | "psr-4": { 22 | "Zadarma_API\\": "lib/" 23 | } 24 | }, 25 | "require": { 26 | "php": ">=5.5.0", 27 | "ext-curl": "*" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "1269935064f18102c95cac4638286466", 8 | "packages": [], 9 | "packages-dev": [], 10 | "aliases": [], 11 | "minimum-stability": "stable", 12 | "stability-flags": [], 13 | "prefer-stable": false, 14 | "prefer-lowest": false, 15 | "platform": { 16 | "php": ">=5.5.0", 17 | "ext-curl": "*" 18 | }, 19 | "platform-dev": [] 20 | } 21 | -------------------------------------------------------------------------------- /examples/include.php: -------------------------------------------------------------------------------- 1 | getBalance(); 20 | $result = $api->getPrice($destinationEmail, $sourceNumber); 21 | $result = $api->getTimezone(); 22 | $result = $api->getTariff(); 23 | 24 | 25 | // pbx methods 26 | $result = $api->getPbxInternal(); 27 | $result = $api->getPbxStatus($pbx); 28 | $result = $api->getPbxInfo($pbx); 29 | $result = $api->getPbxRecord($callId, null); 30 | $result = $api->getPbxRedirection($pbx); 31 | 32 | $result = $api->setPbxPhoneRedirection($pbx, $destinationNumber, false, true); 33 | $result = $api->setPbxVoicemailRedirection($pbx, $destinationEmail, true, Api::PBX_REDIRECTION_OWN_GREETING); 34 | $result = $api->setPbxRedirectionOff($pbx); 35 | 36 | 37 | // sip methods 38 | $result = $api->getSip(); 39 | $result = $api->getSipStatus($sip); 40 | $result = $api->getSipRedirection($sip); 41 | 42 | $result = $api->setSipCallerId($sip, $sourceNumber); 43 | $result = $api->setSipRedirectionNumber($sip, $destinationNumber); 44 | $result = $api->setSipRedirectionStatus($sip, false); 45 | 46 | 47 | // statistic methods 48 | $result = $api->getStatistics(); 49 | $result = $api->getPbxStatistics(); 50 | $result = $api->getDirectNumbers(); 51 | 52 | // zcrm methods 53 | $result = $api->zcrmRequest('/users' ); 54 | 55 | // other methods 56 | $result = $api->requestCallback($pbx, $destinationNumber); 57 | 58 | $result = $api->numberLookup($destinationNumber); 59 | $result = $api->numberLookupMultiple([$sourceNumber, $destinationNumber]); 60 | 61 | $result = $api->sendSms($destinationNumber, 'text', $sourceNumber); 62 | 63 | $result = $api->startSpeechRecognition($callId); 64 | $result = $api->getSpeechRecognitionResult($callId); 65 | $result = $api->getWebrtcKey($sip); 66 | -------------------------------------------------------------------------------- /examples/webhook.php: -------------------------------------------------------------------------------- 1 | 'xxxxxxxxxxxxxxxx', 26 | 1 => [ 27 | 'file' => 'xxxxxxxxxxxxxxxx', 28 | 1 => [ 29 | 'file' => 'xxxxxxxxxxxxxxxx', 30 | 1 => [ 31 | 'file' => 'xxxxxxxxxxxxxxxx', 32 | ], 33 | ], 34 | 2 => [ 35 | 'file' => 'xxxxxxxxxxxxxxxx', 36 | ], 37 | ], 38 | 2 => [ 39 | 'file' => 'xxxxxxxxxxxxxxxx', 40 | ], 41 | ]; 42 | 43 | /** 44 | * The system dictates the last 3 digits of the caller's number and exits. 45 | */ 46 | public static function example1() 47 | { 48 | /** @var NotifyStart $notify */ 49 | $notify = self::getEvent([AbstractNotify::EVENT_START, AbstractNotify::EVENT_IVR]); 50 | if (!$notify) { 51 | return; 52 | } 53 | $request = new Request(); 54 | if ($notify->event == AbstractNotify::EVENT_START) { 55 | $request->setIvrSayDigits(mb_substr($notify->caller_id, -3), 'en'); 56 | } else { 57 | $request->setHangup(); 58 | } 59 | $request->send(); 60 | } 61 | 62 | /** 63 | * Enter the date of birth through dtmf and the system will say how many days are left. 64 | */ 65 | public static function example2() 66 | { 67 | $request = new Request(); 68 | $notify = self::getEvent([AbstractNotify::EVENT_START, AbstractNotify::EVENT_IVR]); 69 | if ($notify->event == AbstractNotify::EVENT_START) { 70 | /** @var NotifyStart $notify */ 71 | $request 72 | ->setIvrPlay(self::INFO_FILE_ID) 73 | ->setWaitDtmf(7, 1, 4, self::DTMF_NAME_2, 'hangup') 74 | ->send(); 75 | } elseif ($notify->event == AbstractNotify::EVENT_IVR) { 76 | /** @var NotifyIvr $notify */ 77 | if (!$notify->wait_dtmf 78 | || $notify->wait_dtmf->name != self::DTMF_NAME_2 79 | || $notify->wait_dtmf->default_behaviour 80 | ) { 81 | $request 82 | ->setHangup() 83 | ->send(); 84 | } else { 85 | 86 | if (mb_strlen($notify->wait_dtmf->digits) == 4) { 87 | $month = mb_substr($notify->wait_dtmf->digits, 0, 2); 88 | $day = mb_substr($notify->wait_dtmf->digits, 2); 89 | if ($month > 0 && $month <= 12 && $day > 0 && $day <= date('t', mktime(0, 0, 0, $month))) { 90 | $birth = (new DateTime())->setDate(date('Y'), $month, $day); 91 | $diff = (new DateTime())->diff($birth)->format('%r%a'); 92 | if ($diff < 0) { 93 | $diff += (date('L') ? 366 : 365); 94 | } 95 | 96 | $request 97 | ->setIvrSayNumber($diff, 'en') 98 | ->send(); 99 | return; 100 | } 101 | } 102 | 103 | $request 104 | ->setIvrPlay(self::WRONG_INPUT_FILE_ID) 105 | ->setWaitDtmf(7, 1, 4, self::DTMF_NAME_2, 'hangup') 106 | ->send(); 107 | } 108 | } 109 | } 110 | 111 | /** 112 | * Implementation of multi-level menu. 113 | * Enter '1' or '2' to go to the next menu, '4' to return to the level above, '5' for hangup. 114 | */ 115 | public static function example3() 116 | { 117 | $request = new Request(); 118 | /** @var NotifyIvr $notify */ 119 | 120 | if ($notify = self::getEvent([AbstractNotify::EVENT_START])) { 121 | $request 122 | ->setIvrPlay(self::$menu['file']) 123 | ->setWaitDtmf(5, 1, 1, self::DTMF_NAME_3, 'hangup') 124 | ->send(); 125 | return; 126 | } 127 | 128 | $notify = self::getEvent([AbstractNotify::EVENT_IVR]); 129 | if (!empty($notify->wait_dtmf->digits) && mb_strpos($notify->wait_dtmf->name, self::DTMF_NAME_3) === 0) { 130 | $menu = mb_substr($notify->wait_dtmf->name, mb_strlen(self::DTMF_NAME_3)); 131 | 132 | switch ($notify->wait_dtmf->digits){ 133 | case 1: 134 | case 2: 135 | case 3: 136 | $menu .= $notify->wait_dtmf->digits; 137 | break; 138 | 139 | case 4: 140 | if ($menu) { 141 | $menu = mb_substr($menu, 0, mb_strlen($menu) - 1); 142 | } 143 | break; 144 | 145 | case 5: 146 | default: 147 | $request->setHangup()->send(); 148 | return; 149 | } 150 | 151 | list($menu, $file) = self::getMenuFile($menu); 152 | $request 153 | ->setIvrPlay($file) 154 | ->setWaitDtmf(5, 1, 1, self::DTMF_NAME_3.$menu, 'hangup') 155 | ->send(); 156 | } 157 | } 158 | 159 | private static function getEvent($allowedTypes) 160 | { 161 | if (self::$api === null) { 162 | self::$api = new Api(KEY, SECRET, true); 163 | } 164 | return self::$api->getWebhookEvent($allowedTypes); 165 | } 166 | 167 | private static function getMenuFile($menuDigits) 168 | { 169 | $menuDigits = (string)$menuDigits; 170 | $menuDigitsResult = ''; 171 | $menu = self::$menu; 172 | while ($menuDigits) { 173 | $currentDigit = mb_substr($menuDigits, 0, 1); 174 | $menuDigits = mb_substr($menuDigits, 1); 175 | if (isset($menu[$currentDigit])) { 176 | $menu = $menu[$currentDigit]; 177 | $menuDigitsResult .= $currentDigit; 178 | } else { 179 | break; 180 | } 181 | } 182 | return [$menuDigitsResult, $menu['file']]; 183 | } 184 | } 185 | 186 | WebhookExample::example1(); -------------------------------------------------------------------------------- /lib/Api.php: -------------------------------------------------------------------------------- 1 | request('info/balance'); 61 | return new Balance($data); 62 | } 63 | 64 | /** 65 | * Return call rate in the user's current price plan. 66 | * 67 | * @param string $number 68 | * @param null|string $callerId 69 | * @return Price 70 | * @throws ApiException 71 | */ 72 | public function getPrice($number, $callerId = null) 73 | { 74 | $params = ['number' => self::filterNumber($number)]; 75 | if ($callerId) { 76 | $params['caller_id'] = self::filterNumber($number); 77 | } 78 | $data = $this->request('info/price', $params); 79 | return new Price($data['info']); 80 | } 81 | 82 | /** 83 | * Return user's timezone. 84 | * 85 | * @return Timezone 86 | * @throws ApiException 87 | */ 88 | public function getTimezone() 89 | { 90 | $data = $this->request('info/timezone'); 91 | return new Timezone($data); 92 | } 93 | 94 | /** 95 | * Return information about the user's current price plan. 96 | * 97 | * @return Tariff 98 | * @throws ApiException 99 | */ 100 | public function getTariff() 101 | { 102 | $data = $this->request('tariff'); 103 | return new Tariff($data['info']); 104 | } 105 | 106 | /** 107 | * Request a callback. 108 | * @see https://zadarma.com/en/services/calls/callback/ 109 | * 110 | * @param string from Your phone/SIP number, the PBX extension number or the PBX scenario, 111 | * to which the CallBack is made. 112 | * @param string to The phone or SIP number that is being called. 113 | * @param null|string sip SIP user's number or the PBX extension number, 114 | * which is used to make the call. 115 | * @param null|string predicted If this flag is specified the request is predicted 116 | * (the system calls the “to” number, and only connects it to your SIP, or your phone number, 117 | * if the call is successful.); 118 | * @return RequestCallback 119 | * @throws ApiException 120 | */ 121 | public function requestCallback($from, $to, $sip = null, $predicted = null) 122 | { 123 | $params = [ 124 | 'from' => $from, 125 | 'to' => self::filterNumber($to), 126 | ]; 127 | $params = $params + self::filterParams([ 128 | 'sip' => is_null($sip) ? null : self::filterNumber($sip), 129 | 'predicted' => $predicted, 130 | ]); 131 | $data = $this->request('request/callback', $params); 132 | return new RequestCallback($data); 133 | } 134 | 135 | /** 136 | * Return the list of user's SIP-numbers. 137 | * 138 | * @return array 139 | * @throws ApiException 140 | */ 141 | public function getSip() 142 | { 143 | $data = $this->request('sip'); 144 | unset($data['status']); 145 | if (is_array($data['sips']) && $data['sips']) { 146 | foreach ($data['sips'] as &$sipData) { 147 | $sipData = new Sip($sipData); 148 | } 149 | } 150 | return $data; 151 | } 152 | 153 | /** 154 | * Return the user's SIP number online status. 155 | * 156 | * @param $sipId 157 | * @return SipStatus 158 | * @throws ApiException 159 | */ 160 | public function getSipStatus($sipId) 161 | { 162 | $data = $this->request('sip/' . self::filterNumber($sipId) . '/status'); 163 | return new SipStatus($data); 164 | } 165 | 166 | /** 167 | * Return the current call forwarding based on the user's SIP numbers. 168 | * 169 | * @param null|integer $sipId Selection of the specific SIP ID. 170 | * @return Redirection[] 171 | * @throws ApiException 172 | */ 173 | public function getSipRedirection($sipId = null) 174 | { 175 | $params = $sipId ? ['id' => self::filterNumber($sipId)] : []; 176 | $data = $this->request('sip/redirection', $params); 177 | return self::arrayToResultObj($data['info'], Response\Redirection::class); 178 | } 179 | 180 | /** 181 | * Return information about the user's phone numbers. 182 | * @return DirectNumber[] 183 | * @throws ApiException 184 | */ 185 | public function getDirectNumbers() 186 | { 187 | $data = $this->request('direct_numbers'); 188 | return self::arrayToResultObj($data['info'], DirectNumber::class); 189 | } 190 | 191 | /** 192 | * Return online status of the PBX extension number. 193 | * @return PbxInternal 194 | * @throws ApiException 195 | */ 196 | public function getPbxInternal() 197 | { 198 | $data = $this->request('pbx/internal'); 199 | return new PbxInternal($data); 200 | } 201 | 202 | /** 203 | * Return online status of the PBX extension number. 204 | * @param $pbxId 205 | * @return PbxStatus 206 | * @throws ApiException 207 | */ 208 | public function getPbxStatus($pbxId) 209 | { 210 | $data = $this->request('pbx/internal/' . self::filterNumber($pbxId) . '/status'); 211 | return new PbxStatus($data); 212 | } 213 | 214 | /** 215 | * Return information about the PBX extension number. 216 | * @param $pbxId 217 | * @return PbxInfo 218 | * @throws ApiException 219 | */ 220 | public function getPbxInfo($pbxId) 221 | { 222 | $data = $this->request('pbx/internal/' . self::filterNumber($pbxId) . '/info'); 223 | return new PbxInfo($data); 224 | } 225 | 226 | /** 227 | * Return call recording file request. 228 | * @param string|null $callId Unique call ID, it is specified in the name of the file with the call 229 | * recording (unique for every recording) 230 | * @param string|null $pbxCallId Permanent ID of the external call to the PBX 231 | * @param integer|null $lifetime The link's lifetime in seconds (minimum - 180, maximum - 5184000, default - 1800) 232 | * @return PbxRecordRequest 233 | * @throws ApiException 234 | */ 235 | public function getPbxRecord($callId, $pbxCallId, $lifetime = null) 236 | { 237 | $params = array_filter([ 238 | 'call_id' => $callId, 239 | 'pbx_call_id' => $pbxCallId, 240 | ]); 241 | if (!$params) { 242 | throw new ApiException('callId or pbxCallId required'); 243 | } 244 | if ($lifetime) { 245 | $params['lifetime'] = $lifetime; 246 | } 247 | $data = $this->request('pbx/record/request', $params); 248 | return new PbxRecordRequest($data); 249 | } 250 | 251 | /** 252 | * Return call forwarding parameters on the PBX extension number. 253 | * @param integer $pbxNumber PBX extension number 254 | * @return PbxRedirection 255 | * @throws ApiException 256 | */ 257 | public function getPbxRedirection($pbxNumber) 258 | { 259 | $data = $this->request('pbx/redirection', ['pbx_number' => self::filterNumber($pbxNumber)]); 260 | return new PbxRedirection($data); 261 | } 262 | 263 | /** 264 | * Return overall statistics. 265 | * Maximum period of getting statistics is - 1 month. If the limit in the request is exceeded, the time period 266 | * automatically decreases to 30 days. If the start date is not specified, the start of the current month will be 267 | * selected. If the end date is not specified, the current date and time will be selected. 268 | * 269 | * @param string|null $start The start date of the statistics display (format - y-m-d H:i:s) 270 | * @param string|null $end The end date of the statistics display (format - y-m-d H:i:s) 271 | * @param integer|null $sip Filter based on a specific SIP number 272 | * @param bool|null $costOnly Display only the amount of funds spent during a specific period 273 | * @param string|null $type Request type: overall (is not specified in the request), toll and ru495 274 | * @param integer|null $skip Number of lines to be skipped in the sample. The output begins from skip +1 line. 275 | * @param integer|null $limit The limit on the number of input lines 276 | * (the maximum value is 1000, the default value is 1000) 277 | * @return Statistics 278 | * @throws ApiException 279 | */ 280 | public function getStatistics( 281 | $start = null, 282 | $end = null, 283 | $sip = null, 284 | $costOnly = null, 285 | $type = null, 286 | $skip = null, 287 | $limit = null 288 | ) { 289 | $params = [ 290 | 'start' => $start, 291 | 'end' => $end, 292 | 'sip' => is_null($sip) ? null : self::filterNumber($sip), 293 | 'cost_only' => $costOnly, 294 | 'type' => $type, 295 | 'skip' => $skip, 296 | 'limit' => $limit, 297 | ]; 298 | $data = $this->request('statistics', self::filterParams($params)); 299 | return new Statistics($data); 300 | } 301 | 302 | /** 303 | * Return PBX statistics. 304 | * @see Api::getStatistics() For $start, $end, $skip, $limit parameters details. 305 | * 306 | * @param string|null $start 307 | * @param string|null $end 308 | * @param true|bool $newFormat Format of the statistics result. 309 | * @param integer|null $skip 310 | * @param integer|null $limit 311 | * @param string|null $callType IN_CALLS for incoming calls, OUT_CALLS for outgoing, null for both 312 | * @return PbxStatistics 313 | * @throws ApiException 314 | */ 315 | public function getPbxStatistics( 316 | $start = null, 317 | $end = null, 318 | $newFormat = true, 319 | $callType = null, 320 | $skip = null, 321 | $limit = null 322 | ) { 323 | $params = [ 324 | 'start' => $start, 325 | 'end' => $end, 326 | 'version' => $newFormat ? 2 : 1, 327 | 'skip' => $skip, 328 | 'limit' => $limit, 329 | 'call_type' => $callType, 330 | ]; 331 | $data = $this->request('statistics/pbx', self::filterParams($params)); 332 | return new PbxStatistics($data); 333 | } 334 | 335 | /** 336 | * Return CallBack widget statistics. 337 | * @see Api::getStatistics() For $start and $end parameters details. 338 | * 339 | * @param string|null $start 340 | * @param string|null $end 341 | * @param string|null $widget_id 342 | * @return PbxStatistics 343 | * @throws ApiException 344 | */ 345 | public function getCallbackWidgetStatistics($start = null, $end = null, $widget_id = null) 346 | { 347 | $params = [ 348 | 'start' => $start, 349 | 'end' => $end, 350 | 'widget_id' => $widget_id, 351 | ]; 352 | $data = $this->request('statistics/callback_widget', self::filterParams($params)); 353 | return new PbxStatistics($data); 354 | } 355 | 356 | /** 357 | * Return overall incoming calls statistics. 358 | * Maximum period of getting statistics is - 1 month. If the limit in the request is exceeded, the time period 359 | * automatically decreases to 30 days. If the start date is not specified, the start of the current month will be 360 | * selected. If the end date is not specified, the current date and time will be selected. 361 | * 362 | * @param string|null $start The start date of the statistics display (format - y-m-d H:i:s) 363 | * @param string|null $end The end date of the statistics display (format - y-m-d H:i:s) 364 | * @param integer|null $sip Filter based on a specific SIP number 365 | * @param integer|null $skip Number of lines to be skipped in the sample. The output begins from skip +1 line. 366 | * @param integer|null $limit The limit on the number of input lines 367 | * (the maximum value is 1000, the default value is 1000) 368 | * @return IncomingCallsStatistics 369 | * @throws ApiException 370 | */ 371 | public function getIncomingCallStatistics($start = null, $end = null, $sip = null, $skip = null, $limit = null) 372 | { 373 | $params = [ 374 | 'start' => $start, 375 | 'end' => $end, 376 | 'sip' => is_null($sip) ? null : self::filterNumber($sip), 377 | 'skip' => $skip, 378 | 'limit' => $limit, 379 | ]; 380 | $data = $this->request('statistics/incoming-calls', self::filterParams($params)); 381 | return new IncomingCallsStatistics($data); 382 | } 383 | 384 | /** 385 | * Changing of the CallerID. 386 | * @param integer id The SIP ID, which needs the CallerID to be changed; 387 | * @param string number The new (changed) phone number, in international format 388 | * (from the list of confirmed or purchased phone numbers). 389 | * @return SipCaller 390 | * @throws ApiException 391 | */ 392 | public function setSipCallerId($sipId, $number) 393 | { 394 | $params = [ 395 | 'id' => self::filterNumber($sipId), 396 | 'number' => self::filterNumber($number) 397 | ]; 398 | $data = $this->request('sip/callerid', $params, 'put'); 399 | return new SipCaller($data); 400 | } 401 | 402 | 403 | /** 404 | * Call forwarding switch on/off based on the SIP number. 405 | * @param integer $sipId 406 | * @param bool $statusOn True for 'on' and false for 'off' status. 407 | * @return SipRedirectionStatus 408 | * @throws ApiException 409 | */ 410 | public function setSipRedirectionStatus($sipId, $statusOn) 411 | { 412 | $params = [ 413 | 'id' => self::filterNumber($sipId), 414 | 'status' => $statusOn ? 'on' : 'off', 415 | ]; 416 | $data = $this->request('sip/redirection', $params, 'put'); 417 | return new SipRedirectionStatus($data); 418 | } 419 | 420 | /** 421 | * Changing of the call forwarding parameters. 422 | * @param integer $sipId 423 | * @param string $number phone number 424 | * @return SipRedirection 425 | * @throws ApiException 426 | */ 427 | public function setSipRedirectionNumber($sipId, $number) 428 | { 429 | $params = [ 430 | 'id' => self::filterNumber($sipId), 431 | 'type' => 'phone', 432 | 'number' => self::filterNumber($number), 433 | ]; 434 | $data = $this->request('sip/redirection', $params, 'put'); 435 | return new SipRedirection($data); 436 | } 437 | 438 | /** 439 | * Enabling of the call recording on the PBX extension number. 440 | * @param integer $sipId 441 | * @param string $status One of the values: "on" - switch on, "off" - switch off, "on_email" - enable the option to 442 | * send the recordings to the email address only, "off_email" - disable the option to send the recordings to the 443 | * email address only, "on_store" - enable the option to save the recordings to the cloud, "off_store" - disable the 444 | * option to save the recordings to the cloud. 445 | * @param string|null $email (optional) change the email address, where the call recordings will be sent. 446 | * You can specify up to 3 email addresses, separated by comma. 447 | * @param string|null $speechRecognition (optional) change the speech recognition settings: "all" - recognize all, 448 | * "optional" - recognize selectively in statistics, "off" - disable. 449 | * @return PbxRecording 450 | * @throws ApiException 451 | */ 452 | public function setPbxRecording($sipId, $status, $email = null, $speechRecognition = null) 453 | { 454 | if (!in_array($status, ['on', 'off', 'on_email', 'off_email', 'on_store', 'off_store'])) { 455 | throw new \BadFunctionCallException('Wrong status parameter'); 456 | } 457 | $params = [ 458 | 'id' => self::filterNumber($sipId), 459 | 'status' => $status 460 | ]; 461 | if ($email) { 462 | $params['email'] = $email; 463 | } 464 | if ($speechRecognition && !in_array($status, ['off', 'off_store'])) { 465 | if (!in_array($speechRecognition, ['all', 'optional', 'off'])) { 466 | throw new \BadFunctionCallException('Wrong speechRecognition parameter'); 467 | } 468 | $params['speech_recognition'] = $speechRecognition; 469 | } 470 | $data = $this->request('pbx/internal/recording', $params, 'put'); 471 | return new PbxRecording($data); 472 | } 473 | 474 | /** 475 | * Sending the SMS messages. 476 | * 477 | * @param string|array $to Phone number(s), where to send the SMS message (array of numbers can be specified); 478 | * @param string $message Message (standard text limit applies; the text will be separated into several SMS messages, 479 | * if the limit is exceeded); 480 | * @param string $callerId Phone number, from which the SMS messages is sent (can be sent only from list of user's 481 | * confirmed phone numbers). 482 | * @return Sms 483 | * @throws ApiException 484 | */ 485 | public function sendSms($to, $message, $callerId = null) 486 | { 487 | $to = array_map([self::class, 'filterNumber'], is_array($to) ? $to : [$to]); 488 | $params = [ 489 | 'number' => implode(',', $to), 490 | 'message' => $message, 491 | ]; 492 | if ($callerId) { 493 | $params['caller_id'] = $callerId; 494 | } 495 | $data = $this->request('sms/send', $params, 'post'); 496 | return new Sms($data); 497 | } 498 | 499 | /** 500 | * Number lookup for one phone number. 501 | * @param string $number Phone number. 502 | * @return NumberLookup 503 | * @throws ApiException 504 | */ 505 | public function numberLookup($number) 506 | { 507 | $data = $this->request('info/number_lookup', ['numbers' => self::filterNumber($number)], 'post'); 508 | return new NumberLookup($data['info']); 509 | } 510 | 511 | /** 512 | * Number lookup for multiple phone numbers. 513 | * @param string[] $numbers Phone number. 514 | * @throws ApiException 515 | */ 516 | public function numberLookupMultiple($numbers) 517 | { 518 | $numbers = array_filter(array_map('\Zadarma_API\Api::filterNumber', $numbers)); 519 | $this->request('info/number_lookup', ['numbers' => $numbers], 'post'); 520 | } 521 | 522 | /** 523 | * Turn off call forwarding parameters on the PBX extension number. 524 | * @param integer $pbxNumber PBX extension number. 525 | * @return PbxRedirection 526 | * @throws ApiException 527 | */ 528 | public function setPbxRedirectionOff($pbxNumber) 529 | { 530 | $params = [ 531 | 'pbx_number' => self::filterNumber($pbxNumber), 532 | 'status' => 'off', 533 | ]; 534 | $data = $this->request('pbx/redirection', $params, 'post'); 535 | return new PbxRedirection($data); 536 | } 537 | 538 | /** 539 | * Turn on and setup call forwarding to phone on the PBX extension number. 540 | * @param integer $pbxNumber PBX extension number. 541 | * @param string $destination Phone number. 542 | * @param bool $always Always forward calls or only if there is no answer. 543 | * @param bool $setCallerId Setting up your CallerID during the call forwarding. 544 | * @return PbxRedirection 545 | * @throws ApiException 546 | */ 547 | public function setPbxPhoneRedirection($pbxNumber, $destination, $always, $setCallerId) 548 | { 549 | $params = [ 550 | 'pbx_number' => self::filterNumber($pbxNumber), 551 | 'type' => 'phone', 552 | 'condition' => $always ? 'always' : 'noanswer', 553 | 'destination' => self::filterNumber($destination), 554 | 'set_caller_id' => $setCallerId ? 'on' : 'off', 555 | ]; 556 | 557 | $data = $this->request('pbx/redirection', $params, 'post'); 558 | return new PbxRedirection($data); 559 | } 560 | 561 | /** 562 | * Turn on and setup call forwarding to voicemail on the PBX extension number. 563 | * @param integer $pbxNumber PBX extension number. 564 | * @param string $destination Email address. 565 | * @param bool $always Always forward calls or only if there is no answer. 566 | * @param string $greeting Notifications about call forwarding, possible values: 567 | * Api::PBX_REDIRECTION_NO_GREETING, Api::PBX_REDIRECTION_STANDART_GREETING, Api::PBX_REDIRECTION_OWN_GREETING. 568 | * @param string $greetingFile Path to file with notification in mp3 format or wav below 5 MB. 569 | * Specified only when greeting = own. 570 | * @return PbxRedirection 571 | * @throws ApiException 572 | */ 573 | public function setPbxVoicemailRedirection($pbxNumber, $destination, $always, $greeting, $greetingFile = null) 574 | { 575 | if (!filter_var($destination, FILTER_VALIDATE_EMAIL)) { 576 | throw new \BadFunctionCallException('Wrong email parameter'); 577 | } 578 | $allowedRedirections = [ 579 | self::PBX_REDIRECTION_NO_GREETING, 580 | self::PBX_REDIRECTION_OWN_GREETING, 581 | self::PBX_REDIRECTION_STANDART_GREETING 582 | ]; 583 | if (!in_array($greeting, $allowedRedirections)) { 584 | throw new \BadFunctionCallException('Wrong voicemailGreeting parameter'); 585 | } 586 | $params = [ 587 | 'pbx_number' => self::filterNumber($pbxNumber), 588 | 'type' => 'voicemail', 589 | 'condition' => $always ? 'always' : 'noanswer', 590 | 'destination' => $destination, 591 | 'voicemail_greeting' => $greeting, 592 | ]; 593 | if ($greeting == self::PBX_REDIRECTION_OWN_GREETING) { 594 | if ( 595 | !$greetingFile 596 | || !file_exists($greetingFile) 597 | || !in_array(pathinfo($greetingFile, PATHINFO_EXTENSION), ['wav', 'mp3']) 598 | ) { 599 | throw new \BadFunctionCallException('Greeting file does not exist or has wrong extension.'); 600 | } 601 | $params['greeting_file'] = curl_file_create($greetingFile); 602 | } 603 | $data = $this->request('pbx/redirection', $params, 'post'); 604 | return new PbxRedirection($data); 605 | } 606 | 607 | /** 608 | * Start speech recognition. 609 | * @param string $callId Unique call ID, it is specified in the name of the file with the call 610 | * recording (unique for every recording) 611 | * @param null|string $lang recognition language (not required) 612 | * @return bool 613 | * @throws ApiException 614 | */ 615 | public function startSpeechRecognition($callId, $lang = null) 616 | { 617 | $params = [ 618 | 'call_id' => $callId, 619 | ]; 620 | if ($lang) { 621 | $params['lang'] = $lang; 622 | } 623 | $data = $this->request('speech_recognition', $params, 'put'); 624 | return $data['status'] == 'success'; 625 | } 626 | 627 | /** 628 | * Obtaining recognition results. 629 | * @param string $callId Unique call ID, it is specified in the name of the file with the call 630 | * recording (unique for every recording) 631 | * @param null|string $lang recognition language (not required) 632 | * @param bool $returnWords return words or phrases 633 | * @param bool $returnAlternatives return alternative results 634 | * @return SpeechRecognition 635 | * @throws ApiException 636 | */ 637 | public function getSpeechRecognitionResult($callId, $lang = null, $returnWords = false, $returnAlternatives = false) 638 | { 639 | $params = [ 640 | 'call_id' => $callId, 641 | 'return' => $returnWords ? 'words' : 'phrases', 642 | 'alternatives' => (int)$returnAlternatives, 643 | ]; 644 | if ($lang) { 645 | $params['lang'] = $lang; 646 | } 647 | $data = $this->request('speech_recognition', $params, 'get'); 648 | return new SpeechRecognition($data); 649 | } 650 | 651 | /** 652 | * Return notify object populated from postData, depending on 'event' field. 653 | * If cannot match event to object, return null. 654 | * Perform signature test, before populating data. 655 | * Throw SignatureException in case of signature test failure. 656 | * @param array|null $eventFilter array of allowed events. If not specified, return all events. 657 | * Example: [AbstractNotify::EVENT_START, AbstractNotify::EVENT_IVR] 658 | * @param array|null $postData Data for model populating. If null, $_POST values used. 659 | * @param null $signature 660 | * @return null|NotifyStart 661 | */ 662 | public function getWebhookEvent($eventFilter = null, $postData = null, $signature = null) 663 | { 664 | if ($postData === null) { 665 | $postData = $_POST; 666 | } 667 | if (empty($postData['event']) || ($eventFilter && !in_array($postData['event'], $eventFilter))) { 668 | return null; 669 | } 670 | 671 | if ($signature === null) { 672 | $headers = getallheaders(); 673 | if (empty($headers['Signature'])) { 674 | return null; 675 | } else { 676 | $signature = $headers['Signature']; 677 | } 678 | } 679 | 680 | switch ($postData['event']) { 681 | case AbstractNotify::EVENT_START: 682 | $notify = new NotifyStart($postData); 683 | break; 684 | 685 | case AbstractNotify::EVENT_IVR: 686 | $notify = new NotifyIvr($postData); 687 | break; 688 | 689 | case AbstractNotify::EVENT_INTERNAL: 690 | $notify = new NotifyInternal($postData); 691 | break; 692 | 693 | case AbstractNotify::EVENT_ANSWER: 694 | $notify = new NotifyAnswer($postData); 695 | break; 696 | 697 | case AbstractNotify::EVENT_END: 698 | $notify = new NotifyEnd($postData); 699 | break; 700 | 701 | case AbstractNotify::EVENT_OUT_START: 702 | $notify = new NotifyOutStart($postData); 703 | break; 704 | 705 | case AbstractNotify::EVENT_OUT_END: 706 | $notify = new NotifyOutEnd($postData); 707 | break; 708 | 709 | case AbstractNotify::EVENT_RECORD: 710 | $notify = new NotifyRecord($postData); 711 | break; 712 | 713 | default: 714 | return null; 715 | } 716 | 717 | if ($signature != $this->encodeSignature($notify->getSignatureString())) { 718 | return null; 719 | } 720 | 721 | return $notify; 722 | } 723 | 724 | /** 725 | * @param $method 726 | * @param array $params 727 | * @param string $requestType 728 | * @return Zcrm 729 | * @throws ApiException 730 | */ 731 | public function zcrmRequest($method, $params = [], $requestType = 'get') 732 | { 733 | $result = $this->call('/' . self::VERSION . '/zcrm' . $method, $params, $requestType); 734 | 735 | $result = json_decode($result, true); 736 | if ((!empty($result['status']) && $result['status'] == 'error') || $this->getHttpCode() >= 400) { 737 | throw new ApiException(isset($result['data']) ? $result['data'] : '', $this->getHttpCode()); 738 | } 739 | if ($result === null) { 740 | throw new ApiException('Wrong response', $this->getHttpCode()); 741 | } 742 | 743 | return new Zcrm($result); 744 | } 745 | 746 | /** 747 | * Get a key for a webrtc widget. 748 | * @param string $sipLogin SIP login or login of PBX extension number 749 | * @return WebrtcKey 750 | * @throws ApiException 751 | */ 752 | public function getWebrtcKey($sipLogin) 753 | { 754 | return new WebrtcKey($this->request('webrtc/get_key', ['sip' => $sipLogin])); 755 | } 756 | 757 | /** 758 | * Make request to api with error checking. 759 | * 760 | * @param $method 761 | * @param array $params 762 | * @param string $requestType 763 | * @return array 764 | * @throws ApiException 765 | */ 766 | public function request($method, $params = [], $requestType = 'get') 767 | { 768 | $result = $this->call('/' . self::VERSION . '/' . $method . '/', $params, $requestType); 769 | 770 | $result = json_decode($result, true); 771 | if ((!empty($result['status']) && $result['status'] == 'error') || $this->getHttpCode() >= 400) { 772 | throw new ApiException($result['message'], $this->getHttpCode()); 773 | } 774 | if ($result === null) { 775 | throw new ApiException('Wrong response', $this->getHttpCode()); 776 | } 777 | return $result; 778 | } 779 | 780 | /** 781 | * Filter from non-digit symbols. 782 | * 783 | * @param string $number 784 | * @return string 785 | * @throws ApiException 786 | */ 787 | protected static function filterNumber($number) 788 | { 789 | $number = preg_replace('/\D/', '', $number); 790 | if (!$number) { 791 | throw new ApiException('Wrong number format.'); 792 | } 793 | return $number; 794 | } 795 | 796 | /** 797 | * Remove null value items from params. 798 | * @param $params 799 | * @return mixed 800 | */ 801 | protected static function filterParams($params) 802 | { 803 | foreach ($params as $k => $v) { 804 | if (is_null($v)) { 805 | unset($params[$k]); 806 | } 807 | } 808 | return $params; 809 | } 810 | 811 | /** 812 | * Convert items of array to object of given class name. 813 | * 814 | * @param array $array 815 | * @param string $resultClassName 816 | * @return array 817 | */ 818 | protected static function arrayToResultObj($array, $resultClassName) 819 | { 820 | foreach ($array as &$item) { 821 | $item = new $resultClassName($item); 822 | } 823 | return $array; 824 | } 825 | } 826 | -------------------------------------------------------------------------------- /lib/ApiException.php: -------------------------------------------------------------------------------- 1 | responseBody = $message; 15 | $message = isset($message['error']) ? $message['error'] : ''; 16 | } 17 | 18 | parent::__construct($message, $code, $previous); 19 | } 20 | 21 | public function getResponseBody() { 22 | return $this->responseBody; 23 | } 24 | } -------------------------------------------------------------------------------- /lib/Client.php: -------------------------------------------------------------------------------- 1 | url = $isSandbox ? static::SANDBOX_URL : static::PROD_URL; 26 | $this->key = $key; 27 | $this->secret = $secret; 28 | } 29 | 30 | /** 31 | * @param $method - API method, including version number 32 | * @param array $params - Query params 33 | * @param string $requestType - (get|post|put|delete) 34 | * @param string $format - (json|xml) 35 | * 36 | * @return mixed 37 | * @throws Exception 38 | * 39 | */ 40 | public function call($method, $params = [], $requestType = 'get', $format = 'json') 41 | { 42 | if (!is_array($params)) { 43 | throw new ApiException('Query params must be an array.'); 44 | } 45 | 46 | $type = strtoupper($requestType); 47 | if (!in_array($type, ['GET', 'POST', 'PUT', 'DELETE'])) { 48 | $type = 'GET'; 49 | } 50 | $params['format'] = $format; 51 | 52 | $options = [ 53 | CURLOPT_URL => $this->url . $method, 54 | CURLOPT_CUSTOMREQUEST => $type, 55 | CURLOPT_CONNECTTIMEOUT => 10, 56 | CURLOPT_RETURNTRANSFER => true, 57 | CURLOPT_SSL_VERIFYPEER => false, 58 | CURLOPT_SSL_VERIFYHOST => false, 59 | CURLOPT_HEADERFUNCTION => [$this, 'parseHeaders'], 60 | CURLOPT_HTTPHEADER => $this->getAuthHeader($method, $params), 61 | ]; 62 | 63 | $ch = curl_init(); 64 | 65 | if ($type == 'GET') { 66 | $options[CURLOPT_URL] = $this->url . $method . '?' . $this->httpBuildQuery($params); 67 | } else { 68 | $options[CURLOPT_POST] = true; 69 | if (array_filter($params, 'is_object')) { 70 | $options[CURLOPT_POSTFIELDS] = $params; 71 | } else { 72 | $options[CURLOPT_POSTFIELDS] = $this->httpBuildQuery($params); 73 | } 74 | } 75 | 76 | curl_setopt_array($ch, $options); 77 | 78 | $response = curl_exec($ch); 79 | $error = curl_error($ch); 80 | 81 | $this->httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); 82 | 83 | curl_close($ch); 84 | 85 | if ($error) { 86 | throw new ApiException($error); 87 | } 88 | 89 | return $response; 90 | } 91 | 92 | /** 93 | * @return int 94 | */ 95 | public function getHttpCode() 96 | { 97 | return $this->httpCode; 98 | } 99 | 100 | /** 101 | * @return array 102 | */ 103 | public function getLimits() 104 | { 105 | return $this->limits; 106 | } 107 | 108 | /** 109 | * @param $signatureString 110 | * @return string 111 | */ 112 | public function encodeSignature($signatureString) 113 | { 114 | return base64_encode(hash_hmac('sha1', $signatureString, $this->secret)); 115 | } 116 | 117 | /** 118 | * @param $method 119 | * @param $params 120 | * 121 | * @return array 122 | */ 123 | private function getAuthHeader($method, $params) 124 | { 125 | $params = array_filter($params, function ($a) { 126 | return !is_object($a); 127 | }); 128 | ksort($params); 129 | $paramsString = $this->httpBuildQuery($params); 130 | $signature = $this->encodeSignature($method . $paramsString . md5($paramsString)); 131 | 132 | return ['Authorization: ' . $this->key . ':' . $signature]; 133 | } 134 | 135 | /** 136 | * @param $curl 137 | * @param $line 138 | * 139 | * @return int 140 | */ 141 | private function parseHeaders($curl, $line) 142 | { 143 | if (preg_match('/^X-RateLimit-([a-z]+):\s([0-9]+)/i', $line, $match)) { 144 | $this->limits[$match[1]] = (int)$match[2]; 145 | } 146 | 147 | return strlen($line); 148 | } 149 | 150 | /** 151 | * Build HTTP query 152 | * 153 | * @param array $params 154 | * 155 | * @return string 156 | */ 157 | private function httpBuildQuery($params = []) 158 | { 159 | return http_build_query($params, '', '&', PHP_QUERY_RFC1738); 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /lib/Response/Balance.php: -------------------------------------------------------------------------------- 1 | 20 | * callstart – the call start time;
21 | * clid – CallerID
22 | * destination – the call destination;
23 | * disposition – the call status:
24 | * 'answered' – conversation,
25 | * 'busy' – busy,
26 | * 'cancel' - cancelled,
27 | * 'no answer' - no answer,
28 | * 'failed' - failed,
29 | * 'no money' - no funds, the limit has been exceeded,
30 | * 'unallocated number' - the phone number does not exist,
31 | * 'no limit' - the limit has been exceeded,
32 | * 'no day limit' - the day limit has been exceeded,
33 | * 'line limit' - the line limit has been exceeded,
34 | * 'no money, no limit' - the limit has been exceeded;
35 | * seconds – the amount of seconds; 36 | * is_recorded – (true, false) recorded or no conversations; 37 | * pbx_call_id – permanent ID of the external call to the PBX (does not alter with the scenario changes, 38 | * voice menu, etc., it is displayed in the statistics and notifications); 39 | * @var array 40 | */ 41 | public $stats; 42 | } -------------------------------------------------------------------------------- /lib/Response/PbxStatus.php: -------------------------------------------------------------------------------- 1 | toArray() + ['sandbox' => null]); 10 | foreach ($values as $k => $v) { 11 | $this->$k = $v; 12 | } 13 | } 14 | 15 | public function toArray() { 16 | return get_object_vars($this); 17 | } 18 | 19 | } -------------------------------------------------------------------------------- /lib/Response/Sip.php: -------------------------------------------------------------------------------- 1 | 16 | * sip – SIP-number;
17 | * callstart – the call start time;
18 | * description – description of call destination;
19 | * disposition – the call status:
20 | * 'answered' – conversation,
21 | * 'busy' – busy,
22 | * 'cancel' - cancelled,
23 | * 'no answer' - no answer,
24 | * 'failed' - failed,
25 | * 'no money' - no funds, the limit has been exceeded,
26 | * 'unallocated number' - the phone number does not exist,
27 | * 'no limit' - the limit has been exceeded,
28 | * 'no day limit' - the day limit has been exceeded,
29 | * 'line limit' - the line limit has been exceeded,
30 | * 'no money, no limit' - the limit has been exceeded;
31 | * billseconds – the amount of seconds;
32 | * cost – the cost per minute of calls to this destination;
33 | * billcost – the cost of the paid minutes;
34 | * currency – the cost currency;
35 | * from – which number was used to make a call;
36 | * to – the phone number that was called.
37 | * @var array 38 | */ 39 | public $stats; 40 | } -------------------------------------------------------------------------------- /lib/Response/Tariff.php: -------------------------------------------------------------------------------- 1 | caller_id.$this->destination.$this->call_start; 26 | } 27 | } -------------------------------------------------------------------------------- /lib/Webhook/NotifyEnd.php: -------------------------------------------------------------------------------- 1 | caller_id.$this->called_did.$this->call_start; 42 | } 43 | } -------------------------------------------------------------------------------- /lib/Webhook/NotifyInternal.php: -------------------------------------------------------------------------------- 1 | caller_id.$this->called_did.$this->call_start; 26 | } 27 | } -------------------------------------------------------------------------------- /lib/Webhook/NotifyIvr.php: -------------------------------------------------------------------------------- 1 | wait_dtmf = !empty($this->wait_dtmf) ? new WaitDtmf($this->wait_dtmf) : null; 19 | } 20 | } -------------------------------------------------------------------------------- /lib/Webhook/NotifyOutEnd.php: -------------------------------------------------------------------------------- 1 | internal.$this->destination.$this->call_start; 42 | } 43 | } -------------------------------------------------------------------------------- /lib/Webhook/NotifyOutStart.php: -------------------------------------------------------------------------------- 1 | internal.$this->destination.$this->call_start; 23 | } 24 | } -------------------------------------------------------------------------------- /lib/Webhook/NotifyRecord.php: -------------------------------------------------------------------------------- 1 | pbx_call_id.$this->call_id_with_rec; 16 | } 17 | } -------------------------------------------------------------------------------- /lib/Webhook/NotifyStart.php: -------------------------------------------------------------------------------- 1 | caller_id.$this->called_did.$this->call_start; 23 | } 24 | } -------------------------------------------------------------------------------- /lib/Webhook/Request.php: -------------------------------------------------------------------------------- 1 | data['ivr_play'] = $id; 19 | return $this; 20 | } 21 | 22 | public function setIvrSayPopular($number, $language) 23 | { 24 | $this->data['ivr_saypopular'] = (int)$number; 25 | return $this->setLanguage($language); 26 | } 27 | 28 | public function setIvrSayDigits($number, $language) 29 | { 30 | $this->data['ivr_saydigits'] = (int)$number; 31 | return $this->setLanguage($language); 32 | } 33 | 34 | public function setIvrSayNumber($number, $language) 35 | { 36 | $this->data['ivr_saynumber'] = (int)$number; 37 | return $this->setLanguage($language); 38 | } 39 | 40 | public function setWaitDtmf($timeout, $attempts, $maxdigits, $name, $default) 41 | { 42 | $this->data['wait_dtmf'] = [ 43 | 'timeout' => (int)$timeout, 44 | 'attempts' => (int)$attempts, 45 | 'maxdigits' => (int)$maxdigits, 46 | 'name' => $name, 47 | 'default' => $default, 48 | ]; 49 | return $this; 50 | } 51 | 52 | public function setRedirect($redirect, $returnTimeout, array $extraOptions = []) 53 | { 54 | $this->data['redirect'] = $redirect; 55 | $this->data['return_timeout'] = (int)$returnTimeout; 56 | if (!empty($extraOptions['rewrite_forward_number'])) { 57 | $number = (string)$extraOptions['rewrite_forward_number']; 58 | if (!preg_match('/^(\+*\d{5,})$/', $number)) { 59 | throw new \BadMethodCallException("Wrong 'rewrite_forward_number' extra option value."); 60 | } 61 | $this->data['rewrite_forward_number'] = $number; 62 | } 63 | return $this; 64 | } 65 | 66 | public function setHangup() 67 | { 68 | $this->data['hangup'] = 1; 69 | return $this; 70 | } 71 | 72 | public function setCallerName($name) 73 | { 74 | $this->data['caller_name'] = $name; 75 | return $this; 76 | } 77 | 78 | /** 79 | * @param string $language one of values returned by getSupportedLanguages() method 80 | * @return $this 81 | */ 82 | public function setLanguage($language) 83 | { 84 | if (!in_array($language, self::getSupportedLanguages())) { 85 | throw new \BadMethodCallException('Wrong language.'); 86 | } 87 | $this->data['language'] = $language; 88 | return $this; 89 | } 90 | 91 | /** 92 | * Validate data prepared for send and return it or sends it and terminate script. 93 | * @param bool $return 94 | * @return string 95 | */ 96 | public function send($return = false) 97 | { 98 | $result = json_encode($this->data); 99 | if ($return) { 100 | return $result; 101 | } else { 102 | echo $result; 103 | exit; 104 | } 105 | } 106 | } -------------------------------------------------------------------------------- /lib/Webhook/WaitDtmf.php: -------------------------------------------------------------------------------- 1 |