* http://gsxwsut.apple.com/apidocs/html/WSReference.html?user=asp * @license * This program is free software. It comes without any warranty, to * the extent permitted by applicable law. You can redistribute it * and/or modify it under the terms of the Do What The Fuck You Want * To Public License, Version 2, as published by Sam Hocevar. See * http://sam.zoy.org/wtfpl/COPYING for more details. */ class GsxLib { private $client; private $region; private $session_id; private $environment; const timeout = 30; function __construct($account, $username, $password, $environment = '', $region = 'emea', $tz = 'CEST') { if (!class_exists('SoapClient')) { exit('Looks like your PHP lacks SOAP support'); } if (!preg_match('/\d+/', $account)) { exit('Invalid Sold-To: ' . $account); } $regions = array('am', 'emea', 'apac', 'la'); if (!in_array($region, $regions)) { exit('Region '.$region.' should be one of: ' . implode(', ', $regions)); } $envirs = array('ut', 'it'); if (!empty($environment)) { if (!in_array($environment, $envirs)) { exit('Environment '.$environment. ' should be one of: ' . implode(', ', $envirs)); } } $wsdl = 'https://gsxws2%s.apple.com/wsdl/%sAsp/gsx-%sAsp.wsdl'; if( $environment == 'ut' ) { $wsdl = 'https://gsxws2%s.apple.com/gsx-ws/services/%s/asp?wsdl'; } $wsdl = sprintf( $wsdl, $environment, $region, $region ); $this->client = new SoapClient($wsdl, array('exceptions' => TRUE, 'trace' => 1)); if (!$this->client) { exit('Failed to create SOAP client.'); } $a = array( 'AuthenticateRequest' => array( 'userId' => $username, 'password' => $password, 'serviceAccountNo' => $account, 'languageCode' => 'en', 'userTimeZone' => $tz, ) ); if (@$_SESSION['_gsxlib_session_timeout'] > time()) { return $this->session_id = $_SESSION['_gsxlib_session_id']; } try { $this->session_id = $this->client ->Authenticate($a) ->AuthenticateResponse ->userSessionId; } catch (SoapFault $e) { if( empty( $environment )) { $environment = 'production'; } exit('Authentication with GSX failed. Does this account have access to '.$environment."?\n"); } // there's a session going, put the credentials in there if (session_id()) { $_SESSION['_gsxlib_session_id'] = $this->session_id; $_SESSION['_gsxlib_session_timeout'] = time()+(60*self::timeout); } } /** * Get current GSX status of repair * @param mixed $dispatchId */ public function repairStatus( $dispatchId ) { $toCheck = array(); if (!is_array($dispatchId)) { $dispatchId = array($dispatchId); } foreach ($dispatchId as $id) { if (self::looksLike($id, 'dispatchId')) { $toCheck[] = $id; } } if (empty($toCheck)) { exit('No valid dispatch IDs given'); } $req = array('RepairStatus' => array( 'repairConfirmationNumbers' => $toCheck )); return $this->request($req)->repairStatus; } public function bulkReturnProforma() { } public function repairLookup( $query ) { $fields = array( 'repairConfirmationNumber' => '', 'customerEmailAddress' => '', 'customerFirstName' => '', 'customerLastName' => '', 'fromDate' => '', 'toDate' => '', 'incompleteRepair' => 'N', 'pendingShipment' => 'N', 'purchaseOrderNumber' => '', 'repairNumber' => '', 'repairStatus' => '', 'repairType' => 'CA', 'serialNumber' => '', 'shipToCode' => '', 'soldToReferenceNumber' => '', 'technicianFirstName' => '', 'technicianLastName' => '', 'unreceivedModules' => 'N', ); $like = self::looksLike( $query ); switch( $like ) { case 'dispatchId': $query = array( 'repairConfirmationNumber' => $query ); break; case 'serialNumber': $query = array( 'serialNumber' => $query ); break; } $query = array_merge( $fields, $query ); $req = array( 'RepairLookupRequest' => array( 'userSession' => array( 'userSessionId' => $this->session_id ), 'lookupRequestData' => $query ) ); $response = $this->client->RepairLookup( $req )->RepairLookupResponse; return $response->lookupResponseData; } /** * List parts pending for return * Default to Open Carry-Ins * @param mixed $repairData * @return mixed */ public function partsPendingReturn($repairData = null) { $fields = array( 'repairType' => 'CA', // default to Carry In repairs 'repairStatus' => 'Open', // and current ones 'purchaseOrderNumber' => '', 'sroNumber' => '', 'repairConfirmationNumber' => '', 'serialNumber' => '', 'shipToCode' => '', 'customerFirstName' => '', 'customerLastName' => '', 'customerEmailAddress' => '', 'createdFromDate' => '', 'createdToDate' => '', ); if (!is_array($repairData)) { $repairData = array(); } if (!empty($repairData)) { foreach ($fields as $k => $v) { if (array_key_exists($k, $repairData)) { $fields[$k] = $repairData[$f]; } } } $req = array('PartsPendingReturn' => array('repairData' => $fields)); return $this->request($req)->partsPendingResponse; } public function compTiaCodes() { $result = $this->request(array('ComptiaCodeLookup' => array())); return $result->comptiaInfo; } /** * Return details for given dispatch ID * @param string $dispatchId * @return object lookupResponseData */ public function repairDetails($dispatchId) { $dispatchId = trim($dispatchId); if (!self::looksLike($dispatchId, 'dispatchId')) { exit('Invalid dispatch ID: ' . $dispatchId); } $req = array('RepairDetails' => array('dispatchId' => $dispatchId)); return $this->request($req)->lookupResponseData; } /** * Get PDF label for part return * @param string $returnOrder order number * @param string $partNumber code of part being returned */ public function returnLabel($returnOrder, $partNumber) { if (!self::looksLike($returnOrder, 'returnOrder')) { exit('Invalid order number: ' . $returnOrder); } if (!self::looksLike($partNumber, 'partNumber')) { exit('Invalid part number: ' . $partNumber); } $req = array('ReturnLabel' => array( 'returnOrderNumber' => $returnOrder, 'partNumber' => $partNumber )); return $this->request($req)->returnLabelData; } /** * a shortcut for looking up part information * @param mixed $string * @return [bool|string] */ public function partsLookup($string = null) { $string = trim($string); $what = self::looksLike($string); if (!$what) { exit('Invalid search term for part lookup: ' . $string); } $req = array('PartsLookup' => array( 'lookupRequestData' => array($what => $string) )); return $this->request($req)->parts; } /** * A shortcut for checking warranty status of device */ public function warrantyStatus( $serialNumber ) { if( !$this->isValidSerialNumber( $serialNumber )) { exit('Invalid serial number: ' . $serialNumber); } $req = array('WarrantyStatus' => array( 'unitDetail' => array('serialNumber' => $serialNumber) )); return $this->request($req)->warrantyDetailInfo; } public function productModel( $serialNumber ) { if( !$this->isValidSerialNumber( $serialNumber )) { exit('Invalid serial number: ' . $serialNumber); } $req = array( 'FetchProductModelRequest' => array( 'userSession' => array( 'userSessionId' => $this->session_id ), 'productModelRequest' => array( 'serialNumber' => $serialNumber ) )); $response = $this->client->FetchProductModel( $req )->FetchProductModelResponse; return $response->productModelResponse; } public function onsiteDispatchDetail( $query ) { if( !self::looksLike( $query, 'dispatchId' )) { exit( "Invalid dispatch ID: $query" ); } $req = array( 'OnsiteDispatchDetailRequest' => array( 'userSession' => array( 'userSessionId' => $this->session_id ), 'lookupRequestData' => array( 'dispatchId' => $query, 'dispatchSentFromDate' => '', 'dispatchSentToDate' => '' ) )); $response = $this->client->OnsiteDispatchDetail( $req )->OnsiteDispatchDetailResponse; return $response->onsiteDispatchDetails; } public function isValidSerialNumber( $serialNumber ) { $serialNumber = trim( $serialNumber ); // SNs should never start with an S, but they're often coded into barcodes // and since an "old- ormat" SN + S would still qualify as a "new format" SN, // we strip it here and not in self::looksLike $serialNumber = ltrim($serialNumber, 'sS'); return self::looksLike( $serialNumber, 'serialNumber' ); } /** * return the GSX user session ID * I still keep the property private since it should not be modified * outside the constructor * @return string GSX session ID */ public function getSessionId() { return $this->session_id; } /** * Do the actual SOAP request */ private function request( $req ) { $result = FALSE; // split the request name and data list( $r, $p ) = each( $req ); // add session info $p['userSession'] = array( 'userSessionId' => $this->session_id ); $request = array( $r.'Request' => $p ); print_r( $request ); try { $result = $this->client->$r( $request ); $resp = "{$r}Response"; return $result->$resp; } catch (SoapFault $e) { print( $this->client->__getLastRequest() ); trigger_error( $e->getMessage() ); } return $result; } /** * Try to "categorise" a string * About identifying serial numbers - before 2010, Apple had a logical * serial number format, with structure, that you could id quite reliably. * unfortunately, it's no longer the case * @param string $string string to check */ static function looksLike( $string, $what = null ) { $result = false; $rex = array( 'partNumber' => '/^([a-z]{1,2})?\d{3}\-\d{4}$/i', 'serialNumber' => '/^[a-z0-9]{11,12}$/i', 'eeeCode' => '/^[a-z0-9]{3,4}$/i', 'returnOrder' => '/^7\d{9}$/', 'repairNumber' => '/^\d{12}$/', 'dispatchId' => '/^G\d{9}$/i', ); foreach ($rex as $k => $v) { if (preg_match($v, $string)) { $result = $k; } } return ($what) ? ($result == $what) : $result; } } ?>