├── composer.json ├── samples └── example.php ├── src ├── RussianPostCODRecord.php ├── RussianPostTrackingRecord.php └── RussianPostAPI.php └── README.md /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "injapan/russianpost", 3 | "description": "Russian Post API", 4 | "require": { 5 | "php": ">=5.0.0" 6 | }, 7 | "autoload": { 8 | "psr-0": { 9 | "": "src/" 10 | } 11 | }, 12 | "minimum-stability": "stable" 13 | } -------------------------------------------------------------------------------- /samples/example.php: -------------------------------------------------------------------------------- 1 | getOperationHistory('42382396002056', 'RUS')); //Use 'ENG' for English 10 | 11 | //fetch COD payment info 12 | var_dump($client->getCODHistory('42382396002056', 'RUS')); 13 | } catch(RussianPostException $e) { 14 | die('Something went wrong: ' . $e->getMessage() . "\n"); 15 | } -------------------------------------------------------------------------------- /src/RussianPostCODRecord.php: -------------------------------------------------------------------------------- 1 | 5.0 с включенными модулями: 49 | - pcre 50 | - curl 51 | - SimpleXML 52 | 53 | 54 | Russian Post items tracking via PHP 55 | =================================== 56 | 57 | **Changelog** 58 | 59 | - *2015-04-22* COD payment information; composer compatibility 60 | - *2012-11-24* First release 61 | 62 | **Install** 63 | 64 | Add custom repo and package requirement to composer.json: 65 | 66 | ```javascript 67 | { 68 | "repositories": [ 69 | { 70 | "type": "vcs", 71 | "url": "https://github.com/InJapan/russianpost-tracking" 72 | } 73 | ], 74 | "require": { 75 | "injapan/russianpost": "dev-master" 76 | } 77 | } 78 | ``` 79 | 80 | Run composer install. 81 | 82 | **Access** 83 | 84 | Web service used by the library does not require authentication of the requests. 85 | However you're required to obtain authorization for it's usage by mailing freeformed request to fc@russianpost.ru. 86 | 87 | **Usage** 88 | 89 | See example.php 90 | 91 | **Dependencies** 92 | 93 | PHP >5.0 is required with following extensions enabled: 94 | - pcre 95 | - curl 96 | - SimpleXML 97 | -------------------------------------------------------------------------------- /src/RussianPostAPI.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | ************************************************************************ 7 | * You MUST request usage access for this API through request mailed to * 8 | * fc@russianpost.ru * 9 | ************************************************************************ 10 | * 11 | * This program is free software: you can redistribute it and/or modify 12 | * it under the terms of the GNU General Public License as published by 13 | * the Free Software Foundation, either version 3 of the License, or 14 | * (at your option) any later version. 15 | * 16 | * This program is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | * GNU General Public License for more details. 20 | * 21 | * You should have received a copy of the GNU General Public License 22 | * along with this program. If not, see . 23 | */ 24 | 25 | class RussianPostAPI { 26 | /** 27 | * SOAP service URL 28 | */ 29 | const SOAPEndpoint = 'http://voh.russianpost.ru:8080/niips-operationhistory-web/OperationHistory'; 30 | 31 | const SOAPUser = 'admin'; 32 | const SOAPPassword = 'adminadmin'; //LOL 33 | 34 | protected $proxyHost; 35 | protected $proxyPort; 36 | protected $proxyAuthUser; 37 | protected $proxyAuthPassword; 38 | 39 | /** 40 | * Constructor. Pass proxy config here. 41 | * @param string $proxyHost 42 | * @param string $proxyPort 43 | * @param string $proxyAuthUser 44 | * @param string $proxyAuthPassword 45 | */ 46 | public function __construct($proxyHost = "", $proxyPort = "", $proxyAuthUser = "", $proxyAuthPassword = "") { 47 | $russianpostRequiredExtensions = array('SimpleXML', 'curl', 'pcre'); 48 | foreach($russianpostRequiredExtensions as $russianpostExt) { 49 | if (!extension_loaded($russianpostExt)) { 50 | throw new RussianPostSystemException('Required extension ' . $russianpostExt . ' is missing', 100); 51 | } 52 | } 53 | 54 | $this->proxyHost = $proxyHost; 55 | $this->proxyPort = $proxyPort; 56 | $this->proxyAuthUser = $proxyAuthUser; 57 | $this->proxyAuthPassword = $proxyAuthPassword; 58 | } 59 | 60 | /** 61 | * Returns tracking data 62 | * @param string $trackingNumber tracking number 63 | * @param string $language language for output strings 64 | * @return array of RussianPostTrackingRecord 65 | */ 66 | public function getOperationHistory($trackingNumber, $language = 'RUS') { 67 | $trackingNumber = $this->checkTrackingNumber($trackingNumber); 68 | 69 | $message = << 71 | 72 | 73 | 74 | $trackingNumber 75 | 0 76 | $language 77 | 78 | 79 | 80 | EOD; 81 | 82 | $data = $this->makeRequest($message); 83 | $data = $this->parseResponse($data); 84 | 85 | $records = $data->OperationHistoryData->historyRecord; 86 | 87 | if (empty($records)) 88 | throw new RussianPostDataException("There is no tracking data in XML response", 101); 89 | 90 | $out = array(); 91 | foreach($records as $rec) { 92 | $outRecord = new RussianPostTrackingRecord(); 93 | $outRecord->operationType = (string) $rec->OperationParameters->OperType->Name; 94 | $outRecord->operationTypeId = (int) $rec->OperationParameters->OperType->Id; 95 | 96 | $outRecord->operationAttribute = (string) $rec->OperationParameters->OperAttr->Name; 97 | $outRecord->operationAttributeId = (int) $rec->OperationParameters->OperAttr->Id; 98 | 99 | $outRecord->operationPlacePostalCode = (string) $rec->AddressParameters->OperationAddress->Index; 100 | $outRecord->operationPlaceName = (string) $rec->AddressParameters->OperationAddress->Description; 101 | 102 | $outRecord->destinationPostalCode = (string) $rec->AddressParameters->DestinationAddress->Index; 103 | $outRecord->destinationAddress = (string) $rec->AddressParameters->DestinationAddress->Description; 104 | 105 | $outRecord->operationDate = (string) $rec->OperationParameters->OperDate; 106 | 107 | $outRecord->itemWeight = round(floatval($rec->ItemParameters->Mass) / 1000, 3); 108 | $outRecord->declaredValue = floatval($rec->FinanceParameters->Value); 109 | $outRecord->collectOnDeliveryPrice = floatval($rec->FinanceParameters->Payment); 110 | 111 | $out[] = $outRecord; 112 | } 113 | 114 | return $out; 115 | } 116 | 117 | /** 118 | * Returns cash-on-delivery payment data 119 | * @param string $trackingNumber tracking number 120 | * @param string $language language for output strings 121 | * @return array of RussianPostCODRecord 122 | */ 123 | public function getCODHistory($trackingNumber, $language = 'RUS') { 124 | $trackingNumber = $this->checkTrackingNumber($trackingNumber); 125 | 126 | $user = self::SOAPUser; 127 | $pass = self::SOAPPassword; 128 | 129 | $message = << 131 | 132 | 133 | $user 134 | $pass 135 | 136 | 137 | 138 | 139 | 140 | 141 | EOD; 142 | 143 | $data = $this->makeRequest($message); 144 | $data = $this->parseResponse($data); 145 | 146 | $records = $data->PostalOrderEventsForMaiOutput; 147 | 148 | if (empty($records)) 149 | throw new RussianPostDataException("There is no COD data in XML response", 102); 150 | 151 | $out = array(); 152 | foreach($records->children() as $rec) { 153 | $rec = $rec->attributes(); 154 | 155 | $outRecord = new RussianPostCODRecord(); 156 | 157 | $outRecord->paymentNumber = (int) $rec->Number; 158 | $outRecord->eventDate = (string) $rec->EventDateTime; 159 | $outRecord->eventTypeId = (int) $rec->EventType; 160 | $outRecord->eventName = (string) $rec->EventName; 161 | $outRecord->destinationPostalCode = (string) $rec->IndexTo; 162 | $outRecord->eventPostalCode = (string) $rec->IndexEvent; 163 | $outRecord->paymentAmount = round(intval($rec->SumPaymentForward) / 100, 2); 164 | $outRecord->destinationContryCode = (string) $rec->CountryToCode; 165 | $outRecord->eventCountryCode = (string) $rec->CountryEventCode; 166 | 167 | $out[] = $outRecord; 168 | } 169 | 170 | return $out; 171 | } 172 | 173 | protected function checkTrackingNumber($trackingNumber) { 174 | $trackingNumber = trim($trackingNumber); 175 | if (!preg_match('/^[0-9]{14}|[A-Z]{2}[0-9]{9}[A-Z]{2}$/', $trackingNumber)) { 176 | throw new RussianPostArgumentException('Incorrect format of tracking number: ' . $trackingNumber, 103); 177 | } 178 | 179 | return $trackingNumber; 180 | } 181 | 182 | protected function parseResponse($raw) { 183 | $xml = @simplexml_load_string($raw); 184 | 185 | if (!is_object($xml)) 186 | throw new RussianPostDataException("Failed to parse XML response", 104); 187 | 188 | $ns = $xml->getNamespaces(true); 189 | 190 | foreach($ns as $key => $dummy) { 191 | if (strpos($key, 'ns') === 0) { 192 | $nsKey = $key; 193 | break; 194 | } 195 | } 196 | 197 | if (empty($nsKey)) { 198 | throw new RussianPostDataException("Failed to detect correct namespace in XML response", 105); 199 | } 200 | 201 | if (!( 202 | $xml->children($ns['S'])->Body && 203 | $data = $xml->children($ns['S'])->Body->children($ns[$nsKey]) 204 | )) 205 | throw new RussianPostDataException("There is no tracking data in XML response", 106); 206 | 207 | return $data; 208 | } 209 | 210 | protected function makeRequest($message) { 211 | $channel = curl_init(self::SOAPEndpoint); 212 | 213 | curl_setopt_array($channel, array( 214 | CURLOPT_POST => true, 215 | CURLOPT_RETURNTRANSFER => true, 216 | CURLOPT_CONNECTTIMEOUT => 10, 217 | CURLOPT_TIMEOUT => 10, 218 | CURLOPT_POSTFIELDS => $message, 219 | CURLOPT_HTTPHEADER => array( 220 | 'Content-Type: text/xml; charset=utf-8', 221 | 'SOAPAction: ""', 222 | ), 223 | )); 224 | 225 | if (!empty($this->proxyHost) && !empty($this->proxyPort)) { 226 | curl_setopt($channel, CURLOPT_PROXY, $this->proxyHost . ':' . $this->proxyPort); 227 | } 228 | 229 | if (!empty($this->proxyAuthUser)) { 230 | curl_setopt($channel, CURLOPT_PROXYUSERPWD, $this->proxyAuthUser . ':' . $this->proxyAuthPassword); 231 | } 232 | 233 | $result = curl_exec($channel); 234 | if ($errorCode = curl_errno($channel)) { 235 | throw new RussianPostChannelException(curl_error($channel), $errorCode); 236 | } 237 | 238 | return $result; 239 | } 240 | } 241 | 242 | class RussianPostException extends Exception { } 243 | class RussianPostArgumentException extends RussianPostException { } 244 | class RussianPostSystemException extends RussianPostException { } 245 | class RussianPostChannelException extends RussianPostException { } 246 | class RussianPostDataException extends RussianPostException { } --------------------------------------------------------------------------------