├── .gitignore ├── .travis.yml ├── LICENCE ├── README.md ├── autoload.php ├── composer.json ├── phpunit.xml ├── src └── IPPanel │ ├── Client.php │ ├── Errors │ ├── Error.php │ ├── HttpException.php │ └── ResponseCodes.php │ ├── HTTPClient.php │ └── Models │ ├── Base.php │ ├── InboxMessage.php │ ├── Message.php │ ├── Pattern.php │ ├── Recipient.php │ └── Response.php └── tests └── test.php /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .vscode/ 3 | vendor/ 4 | composer.lock 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | dist: xenial 3 | php: 4 | - 5.6 5 | - 7.0 6 | - 7.1 7 | - 7.2 8 | - 7.3 9 | - 7.4 10 | 11 | before_script: 12 | - composer install 13 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2022, IPPANEL 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | * Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above copyright 9 | notice, this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | * Neither the name of the nor the 12 | names of its contributors may be used to endorse or promote products 13 | derived from this software without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 19 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # IPPanel SMS php api SDK 2 | 3 | [![Build Status](https://travis-ci.org/ippanel/php-rest-sdk.svg?branch=master)](https://travis-ci.org/ippanel/php-rest-sdk) 4 | 5 | ## Installation 6 | 7 | use with composer: 8 | 9 | ```bash 10 | composer require ippanel/php-rest-sdk 11 | ``` 12 | 13 | if you don't want to use composer, you can download it directly : 14 | 15 | ```bash 16 | wget https://github.com/ippanel/php-rest-sdk/archive/master.zip 17 | ``` 18 | 19 | ## Examples 20 | 21 | For using sdk, you have to create a client instance that gives you available methods on API 22 | 23 | ```php 24 | require 'autoload.php'; 25 | 26 | // you api key that generated from panel 27 | $apiKey = "api-key"; 28 | 29 | $client = new \IPPanel\Client($apiKey); 30 | 31 | ... 32 | ``` 33 | 34 | ### Credit check 35 | 36 | ```php 37 | # return float64 type credit amount 38 | $credit = $client->getCredit(); 39 | 40 | ``` 41 | 42 | ### Send one to many 43 | 44 | For sending sms, obviously you need `originator` number, `recipients` and `message`. 45 | 46 | ```php 47 | $messageId = $client->send( 48 | "+9810001", // originator 49 | ["98912xxxxxxx"], // recipients 50 | "ippanel is awesome",// message 51 | "description" // is logged 52 | ); 53 | 54 | ``` 55 | 56 | If send is successful, a unique tracking code returned and you can track your message status with that. 57 | 58 | ### Get message summery 59 | 60 | ```php 61 | $messageId = "message-tracking-code"; 62 | 63 | $message = $client->get_message($messageId); 64 | 65 | echo $message->state; // get message status 66 | echo $message->cost; // get message cost 67 | echo $message->returnCost; // get message payback 68 | ``` 69 | 70 | ### Get message delivery statuses 71 | 72 | ```php 73 | $messageId = "message-tracking-code" 74 | 75 | list($statuses, $paginationInfo) = $client->fetchStatuses($messageId, 0, 10) 76 | 77 | // you can loop in messages statuses list 78 | foreach($statuses as status) { 79 | echo sprintf("Recipient: %s, Status: %s", $status->recipient, $status->status); 80 | } 81 | 82 | echo sprintf("Total: ", $paginationInfo->total); 83 | ``` 84 | 85 | ### Inbox fetch 86 | 87 | fetch inbox messages 88 | 89 | ```php 90 | list($messages, $paginationInfo) = $client->fetchInbox(0, 10); 91 | 92 | foreach($messages as $message) { 93 | echo sprintf("Received message %s from number %s in line %s", $message->message, $message->from, $message->to); 94 | } 95 | ``` 96 | 97 | ### Pattern create 98 | 99 | For sending messages with predefined pattern(e.g. verification codes, ...), you hav to create a pattern. a pattern at 100 | least have a parameter. 101 | 102 | ```php 103 | $patternVariables = [ 104 | "name" => "string", 105 | "code" => "integer", 106 | ]; 107 | 108 | $code = $client->createPattern("%name% is awesome, your code is %code%", "description", 109 | $patternVariables, '%', False); 110 | 111 | echo $code; 112 | ``` 113 | 114 | ### Send with pattern 115 | 116 | ```php 117 | $patternValues = [ 118 | "name" => "IPPANEL", 119 | ]; 120 | 121 | $messageId = $client->sendPattern( 122 | "t2cfmnyo0c", // pattern code 123 | "+9810001", // originator 124 | "98912xxxxxxx", // recipient 125 | $patternValues, // pattern values 126 | ); 127 | ``` 128 | 129 | ### Error checking 130 | 131 | ```php 132 | use IPPanel\Errors\Error; 133 | use IPPanel\Errors\HttpException; 134 | 135 | try{ 136 | $messageId = $client->send("9810001", ["98912xxxxx"], "ippanel is awesome"); 137 | } catch (Error $e) { // ippanel error 138 | var_dump($e->unwrap()); // get real content of error 139 | echo $e->getCode(); 140 | 141 | // error codes checking 142 | if ($e->code() == ResponseCodes::ErrUnprocessableEntity) { 143 | echo "Unprocessable entity"; 144 | } 145 | } catch (HttpException $e) { // http error 146 | var_dump($e->getMessage()); // get stringified error 147 | echo $e->getCode(); 148 | } 149 | ``` 150 | -------------------------------------------------------------------------------- /autoload.php: -------------------------------------------------------------------------------- 1 | =7.0", 17 | "ext-curl": "*" 18 | }, 19 | "require-dev": { 20 | "phpunit/phpunit": "^5" 21 | }, 22 | "autoload": { 23 | "psr-4": { 24 | "IPPanel\\": "src/IPPanel/" 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 13 | 14 | 15 | ./tests/ 16 | 17 | 18 | 19 | 20 | ./src/ 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/IPPanel/Client.php: -------------------------------------------------------------------------------- 1 | _httpClient = $httpClient; 54 | $this->_apiKey = $apiKey; 55 | 56 | $userAgent = sprintf("IPPanel/ApiClient/%s PHP/%s", self::CLIENT_VERSION, phpversion()); 57 | 58 | if (!$httpClient) { 59 | $this->_httpClient = new HTTPClient(self::ENDPOINT, self::DEFAULT_TIMEOUT, array( 60 | sprintf("apikey: %s", $this->_apiKey), 61 | sprintf("User-Agent: %s", $userAgent), 62 | )); 63 | } 64 | } 65 | 66 | /** 67 | * Get user credit 68 | * @return float 69 | * @throws Errors\HttpException 70 | * @throws Errors\Error 71 | * @throws Exception 72 | */ 73 | public function getCredit(): float 74 | { 75 | $res = $this->_httpClient->get("/sms/accounting/credit/show"); 76 | 77 | if (!isset($res->data->credit)) { 78 | throw new Exception("returned response not valid", 1); 79 | } 80 | 81 | return $res->data->credit; 82 | } 83 | 84 | /** 85 | * Send a message from originator to many recipients. 86 | * @param string $originator originator number 87 | * @param array $recipients recipients list 88 | * @param string $message message body 89 | * @param string $summary description 90 | * @return int message tracking code 91 | * @throws Errors\HttpException 92 | * @throws Errors\Error 93 | * @throws Exception 94 | */ 95 | public function send(string $originator, array $recipients, string $message, string $summary): int 96 | { 97 | $res = $this->_httpClient->post("/sms/send/webservice/single", array( 98 | "sender" => $originator, 99 | "recipient" => $recipients, 100 | "message" => $message, 101 | "description" => [ 102 | "summary" => $summary, 103 | "count_recipient" => "" . count($recipients) 104 | ], 105 | )); 106 | 107 | if (!isset($res->data->message_id)) { 108 | throw new Exception("returned response not valid", 1); 109 | } 110 | 111 | return $res->data->message_id; 112 | } 113 | 114 | /** 115 | * Get a message brief info 116 | * @param int $messageId message tracking code 117 | * @return Models\Message message tracking code 118 | * @throws Errors\HttpException 119 | * @throws Errors\Error 120 | * @throws Exception 121 | */ 122 | public function getMessage(int $messageId): Message 123 | { 124 | $res = $this->_httpClient->get("/sms/message/all", [ 125 | 'message_id' => $messageId, 126 | ]); 127 | 128 | if (!isset($res->data) || !is_array($res->data)) { 129 | throw new Exception("returned response not valid", 1); 130 | } 131 | 132 | $msg = new Message(); 133 | $msg->fromJSON($res->data[0]); 134 | 135 | return $msg; 136 | } 137 | 138 | /** 139 | * Fetch message recipients status 140 | * @param int $messageId message tracking code 141 | * @param int $page page number(start from 0) 142 | * @param int $limit fetch limit 143 | * @return Models\Recipient[] message tracking code 144 | * @throws Errors\HttpException 145 | * @throws Errors\Error 146 | * @throws Exception 147 | */ 148 | public function fetchStatuses(int $messageId, int $page = 0, int $limit = 10): array 149 | { 150 | $res = $this->_httpClient->get(sprintf("/sms/message/show-recipient/message-id/%s", $messageId), array( 151 | 'page' => $page, 152 | 'per_page' => $limit, 153 | )); 154 | 155 | if (!isset($res->data->deliveries) || !is_array($res->data->deliveries)) { 156 | throw new Exception("returned response not valid", 1); 157 | } 158 | 159 | $statuses = []; 160 | 161 | foreach ($res->data->deliveries as $r) { 162 | $status = new Recipient(); 163 | $status->fromJSON($r); 164 | $statuses[] = $status; 165 | } 166 | 167 | $paginationInfo = new PaginationInfo(); 168 | $paginationInfo->fromJSON($res->meta); 169 | 170 | return array($statuses, $paginationInfo); 171 | } 172 | 173 | /** 174 | * Fetch inbox messages 175 | * @param int $page page number(starts from 1) 176 | * @param int $limit fetch limit 177 | * @return Models\InboxMessage[] messages 178 | * @throws Errors\HttpException 179 | * @throws Errors\Error 180 | * @throws Exception 181 | */ 182 | public function fetchInbox(int $page = 1, int $limit = 10): array 183 | { 184 | $res = $this->_httpClient->get("/inbox", array( 185 | 'page' => $page, 186 | 'per_page' => $limit, 187 | )); 188 | 189 | if (!isset($res->data) || !is_array($res->data)) { 190 | throw new Exception("returned response not valid", 1); 191 | } 192 | 193 | $messages = []; 194 | 195 | foreach ($res->data as $r) { 196 | $msg = new InboxMessage(); 197 | $msg->fromJSON($r); 198 | $messages[] = $msg; 199 | } 200 | 201 | $paginationInfo = new PaginationInfo(); 202 | $paginationInfo->fromJSON($res->meta); 203 | 204 | return array($messages, $paginationInfo); 205 | } 206 | 207 | /** 208 | * Create a pattern 209 | * @param string $pattern pattern schema 210 | * @param string $description pattern description 211 | * @param array $variables pattern variable names and types 212 | * @param string $delimiter variable delimiter 213 | * @param bool $isShared determine that pattern shared or not 214 | * @return string pattern code 215 | * @throws Errors\Error 216 | * @throws Errors\HttpException 217 | */ 218 | public function createPattern( 219 | string $pattern, string $description, 220 | array $variables, string $delimiter = "%", bool $isShared = false): string 221 | { 222 | $params = array( 223 | 'message' => $pattern, 224 | 'delimiter' => $delimiter, 225 | 'description' => $description, 226 | 'variable' => [], 227 | 'is_share' => $isShared, 228 | ); 229 | 230 | foreach ($variables as $variableName => $type) { 231 | $params['variable'][] = ['name' => $variableName, 'type' => $type]; 232 | } 233 | 234 | $res = $this->_httpClient->post("/sms/pattern/normal/store", $params); 235 | 236 | if (!isset($res->data[0]->code)) { 237 | throw new Exception("returned response not valid", 1); 238 | } 239 | 240 | return $res->data[0]->code; 241 | } 242 | 243 | /** 244 | * Send message with pattern 245 | * @param string $patternCode pattern code 246 | * @param string $originator originator number 247 | * @param string $recipient recipient number 248 | * @param array $variables pattern values 249 | * @return int message code 250 | * @throws Errors\HttpException 251 | * @throws Errors\Error 252 | * @throws Exception 253 | */ 254 | public function sendPattern(string $patternCode, string $originator, string $recipient, array $variables): int 255 | { 256 | $res = $this->_httpClient->post("/sms/pattern/normal/send", array( 257 | "code" => $patternCode, 258 | "sender" => $originator, 259 | "recipient" => $recipient, 260 | "variable" => $variables, 261 | )); 262 | 263 | if (!isset($res->data->message_id)) { 264 | throw new Exception("returned response not valid", 1); 265 | } 266 | 267 | return $res->data->message_id; 268 | } 269 | } 270 | -------------------------------------------------------------------------------- /src/IPPanel/Errors/Error.php: -------------------------------------------------------------------------------- 1 | _message = $message; 26 | $this->_code = $code; 27 | 28 | parent::__construct(json_encode($message), $code); 29 | } 30 | 31 | /** 32 | * Unwrap error message as is 33 | * @return mixed 34 | */ 35 | public function unwrap() 36 | { 37 | return $this->_message; 38 | } 39 | 40 | /** 41 | * Get error code 42 | * @return string 43 | */ 44 | public function code() 45 | { 46 | return $this->_code; 47 | } 48 | 49 | /** 50 | * Parse API errors 51 | * @param $response mixed api response array 52 | * @return Error 53 | */ 54 | public static function parseErrors($response) 55 | { 56 | if (isset($response->errorMessage) && $response->errorMessage != "") { 57 | return new Error($response->errorMessage, $response->code); 58 | } 59 | 60 | return null; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/IPPanel/Errors/HttpException.php: -------------------------------------------------------------------------------- 1 | _baseURL = $baseURL; 46 | 47 | if ($timeout > 0) { 48 | $this->_timeout = $timeout; 49 | } 50 | 51 | $this->_headers = $headers; 52 | } 53 | 54 | /** 55 | * Make a based url with given uri and query parameters 56 | * @param string $uri uri 57 | * @param array $params query params 58 | * @return string 59 | */ 60 | public function getBasedURL($uri, $params = null) 61 | { 62 | if (!$uri && !$params) { 63 | throw new InvalidArgumentException("function needs at least one argument"); 64 | } 65 | 66 | $url = rtrim($this->_baseURL, '/'); 67 | 68 | $url .= '/' . ltrim($uri, '/'); 69 | 70 | if ($params) { 71 | $query = http_build_query($params); 72 | $url .= "?" . $query; 73 | } 74 | 75 | return $url; 76 | } 77 | 78 | /** 79 | * Make custom http request 80 | * @param string $method http method 81 | * @param string $url request url 82 | * @param mixed $data request data 83 | * @param array $params query parameters 84 | * @param array $headers http headers 85 | * @return Response parsed response 86 | * @throws Errors\HttpException 87 | * @throws Errors\Error 88 | */ 89 | public function request( 90 | $method = "GET", 91 | $url = "", 92 | $data = null, 93 | $params = null, 94 | $headers = null 95 | ) { 96 | $curl = curl_init(); 97 | 98 | if (!$headers || count($headers) < 1) { 99 | $headers = ['Accept: application/json', 'Content-Type: application/json']; 100 | } 101 | 102 | $headers = array_merge($headers, $this->_headers); 103 | 104 | curl_setopt($curl, CURLOPT_HTTPHEADER, $headers); 105 | curl_setopt($curl, CURLOPT_HEADER, false); 106 | curl_setopt($curl, CURLOPT_URL, $this->getBasedURL($url, $params)); 107 | curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); // no need in php 5.1.3+. 108 | curl_setopt($curl, CURLOPT_TIMEOUT, $this->_timeout); 109 | curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, $this->_timeout); 110 | 111 | switch ($method) { 112 | case 'GET': 113 | curl_setopt($curl, CURLOPT_HTTPGET, true); 114 | break; 115 | case 'POST': 116 | curl_setopt($curl, CURLOPT_POST, true); 117 | curl_setopt($curl, CURLOPT_POSTFIELDS, @json_encode($data)); 118 | break; 119 | default: 120 | curl_setopt($curl, CURLOPT_HTTPGET, true); 121 | } 122 | 123 | $response = curl_exec($curl); 124 | 125 | if ($response === false) { 126 | throw new Errors\HttpException(curl_error($curl)); 127 | } 128 | 129 | // get http status 130 | $status = (int) curl_getinfo($curl, CURLINFO_HTTP_CODE); 131 | 132 | curl_close($curl); 133 | 134 | // http status code is parsable or not 135 | if (!in_array($status, $this->_supportedStatusCodes)) { 136 | throw new Errors\HttpException("unexpected http error occurred", $status); 137 | } 138 | 139 | $arrayResponse = json_decode($response); 140 | 141 | if (!$arrayResponse) { 142 | throw new Error(sprintf("Invalid response: %s", $response)); 143 | } 144 | 145 | // marshal received response to base Response object 146 | $parsedResponse = new Response(); 147 | $parsedResponse->fromJSON($arrayResponse); 148 | 149 | $errors = Error::parseErrors($parsedResponse); 150 | if ($errors) { 151 | throw $errors; 152 | } 153 | 154 | return $parsedResponse; 155 | } 156 | 157 | /** 158 | * Make http GET request 159 | * @param string $url request url 160 | * @param array|Null $params query parameters 161 | * @param array|Null $headers http headers 162 | * @return Models\Response parsed response 163 | * @throws Errors\HttpException 164 | * @throws Errors\Error 165 | */ 166 | public function get($url, $params = Null, $headers = Null) 167 | { 168 | return $this->request("GET", $url, Null, $params, $headers); 169 | } 170 | 171 | /** 172 | * Make http POST request 173 | * @param string $url request url 174 | * @param mixed $data request body 175 | * @param array|Null $headers http headers 176 | * @return Models\Response parsed response 177 | * @throws Errors\HttpException 178 | * @throws Errors\Error 179 | */ 180 | public function post($url, $data, $headers = null) 181 | { 182 | return $this->request("POST", $url, $data, Null, $headers); 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /src/IPPanel/Models/Base.php: -------------------------------------------------------------------------------- 1 | $value) { 25 | $transformed[snakeToCamel($key)] = $value; 26 | } 27 | 28 | return $transformed; 29 | } 30 | 31 | /** 32 | * Base class for data models 33 | */ 34 | abstract class Base 35 | { 36 | /** 37 | * Load class properties from json object 38 | * @param array $data associated list of properties 39 | */ 40 | public function fromJSON($data) 41 | { 42 | foreach ($data as $key => $value) { 43 | $camelCased = snakeToCamel($key); 44 | 45 | if (property_exists($this, $camelCased)) { 46 | $this->$camelCased = $value; 47 | } 48 | } 49 | 50 | return $this; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/IPPanel/Models/InboxMessage.php: -------------------------------------------------------------------------------- 1 | sendPattern("your_pattern_code", "sender_number", "recipient_number", ['variable_name' => "1234"]); 13 | var_dump($pattern); 14 | } catch (Error $e) { 15 | var_dump($e->unwrap()); 16 | echo $e->getCode(); 17 | 18 | if ($e->code() == ResponseCodes::ErrUnprocessableEntity) { 19 | echo "Unprocessable entity"; 20 | } 21 | } catch (Exception $e) { 22 | var_dump($e->getMessage()); 23 | echo $e->getCode(); 24 | } --------------------------------------------------------------------------------