1: <?php
2:
3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13:
14: namespace Izberg;
15:
16: require_once __DIR__."/Html2Text/Html2Text.php";
17:
18: class Izberg
19: {
20: 21: 22:
23: const PRODUCTION_API_URL = 'https://api.iceberg.technology/v1/';
24:
25: 26: 27:
28: const SANDBOX_API_URL = 'https://api.sandbox.iceberg.technology/v1/';
29:
30: 31: 32:
33: const SINGLE_SIGN_ON_URL = 'user/sso/';
34:
35: 36: 37:
38: const DEFAULT_CURRENCY = 'EUR';
39:
40: 41: 42:
43: const DEFAULT_SHIPPING_COUNTRY = 'FR';
44:
45: 46: 47:
48: const DEFAULT_LOCALE = 'fr';
49:
50:
51: 52: 53: 54: 55:
56: protected static $_singleton;
57:
58: 59: 60: 61: 62:
63: protected static $_helper;
64:
65:
66: 67: 68: 69:
70: protected static $_api_url;
71:
72: 73: 74: 75: 76:
77: private $_appnamespace;
78:
79: 80: 81: 82: 83:
84: private $_enable_log;
85:
86: 87: 88: 89: 90:
91: private $_apisecret;
92:
93: 94: 95: 96: 97:
98: private $_enable_signed_request;
99:
100: 101: 102: 103: 104:
105: private $_access_token;
106:
107:
108: 109: 110: 111: 112:
113: private $_email;
114:
115: 116: 117: 118: 119:
120: private $_username;
121:
122: 123: 124: 125: 126:
127: private $_timestamp;
128:
129:
130: 131: 132: 133: 134:
135: private $_nonce;
136:
137:
138: 139: 140: 141: 142:
143: private $_anonymous;
144:
145: 146: 147: 148: 149:
150: private $_first_name;
151:
152: 153: 154: 155: 156:
157: private $_last_name;
158:
159: 160: 161: 162: 163:
164: private $_shipping_country;
165:
166: 167: 168: 169: 170:
171: private $_locale;
172:
173: 174: 175: 176: 177:
178: private $_currency;
179:
180: 181: 182: 183: 184:
185: private $_single_sign_on_response;
186:
187: 188: 189: 190: 191:
192: private $_debug;
193:
194: public function getDebug()
195: {
196: return $this->_debug;
197: }
198:
199:
200: 201: 202: 203: 204:
205: public function getApiSecret() {
206: return $this->_apisecret;
207: }
208:
209: 210: 211: 212: 213:
214: public function getAccessToken() {
215: return $this->_access_token;
216: }
217:
218: 219: 220: 221: 222:
223: public function getAppNamespace() {
224: return $this->_appnamespace;
225: }
226:
227: 228: 229: 230: 231:
232: public function getEmail() {
233: return $this->_email;
234: }
235:
236: 237: 238: 239: 240:
241: public function getFirstName() {
242: return $this->_first_name;
243: }
244:
245: 246: 247: 248: 249:
250: public function getLastName() {
251: return $this->_last_name;
252: }
253:
254: 255: 256: 257: 258:
259: public function getUsername() {
260: return $this->_username;
261: }
262:
263: 264: 265: 266: 267:
268: public function getCurrency() {
269: return $this->_currency;
270: }
271:
272: 273: 274: 275: 276:
277: public function getShippingCountry() {
278: return $this->_shipping_country;
279: }
280:
281: public static function getApiUrl()
282: {
283: return self::$_api_url;
284: }
285:
286:
287: 288: 289: 290: 291:
292: public function getTimestamp() {
293: return $this->_timestamp;
294: }
295:
296: 297: 298: 299: 300:
301: public function getNonce() {
302: return $this->_nonce;
303: }
304:
305: 306: 307: 308: 309:
310: public function getLocale() {
311: return $this->_locale;
312: }
313:
314: 315: 316: 317: 318:
319: public function enableLog() {
320: $this->_enable_log = true;
321: return $this->_enable_log;
322: }
323:
324: 325: 326: 327: 328:
329: public function enableSignature() {
330: $this->_enable_signed_request = true;
331: return $this->_enable_signed_request;
332: }
333:
334: 335: 336: 337: 338:
339: public function getMessageAuth($email, $first_name, $last_name)
340: {
341: $this->setTimestamp(time());
342: $to_compose = array($email, $first_name, $last_name, $this->getTimestamp());
343: if (is_null($this->getApiSecret())) {
344: throw new Exception\GenericException("To use SSO you have to set the api_secret");
345: }
346: $message_auth = hash_hmac('sha1', implode(";", $to_compose), $this->getApiSecret());
347: return $message_auth;
348: }
349:
350:
351: 352: 353: 354: 355:
356: public function getRequestSignature($body)
357: {
358: $this->setNonce(time());
359: $nonce = $this->getNonce();
360: $to_compose = array($body, $nonce);
361: if (is_null($this->getApiSecret()) && $_enable_signed_request) {
362: throw new Exception\GenericException("To sign your requests, you need a api secret");
363: }
364: return hash_hmac('sha1', implode(":", $to_compose), $this->getApiSecret());
365: }
366:
367: 368: 369: 370: 371:
372: public function setUsername($username)
373: {
374: $this->_username = $username;
375: }
376:
377: 378: 379: 380: 381:
382: public function setAccessToken($access_token)
383: {
384: $this->_access_token = $access_token;
385: }
386:
387: 388: 389: 390: 391: 392:
393: public function setApiSecret($apiSecret)
394: {
395: $this->_apisecret = $apiSecret;
396: }
397:
398: 399: 400: 401: 402: 403:
404: public function setAppNamespace($namespace)
405: {
406: $this->_appnamespace = $namespace;
407: }
408:
409: 410: 411: 412: 413: 414:
415: public function setEmail($email)
416: {
417: $this->_email = $email;
418: }
419:
420: 421: 422: 423: 424:
425: public function getUser()
426: {
427: return $this->get("user");
428: }
429:
430: 431: 432: 433: 434:
435: public function setUser($params)
436: {
437: $this->_single_sign_on_response = $this->_getSingleSignOnResponse($params);
438: $this->current_user = $this->getUser();
439: $this->setAccessToken($this->_single_sign_on_response->access_token);
440: $this->setUsername($this->_single_sign_on_response->username);
441: if ($this->_single_sign_on_response->username != "Anonymous") {
442: $this->_anonymous = false;
443: } else {
444: $this->_anonymous = true;
445: }
446:
447: return $this->_single_sign_on_response;
448: }
449:
450: 451: 452: 453: 454: 455:
456: public function setFirstName($firstname)
457: {
458: $this->_first_name = $firstname;
459: }
460:
461: 462: 463: 464: 465: 466:
467: public function setLastName($lastname)
468: {
469: $this->_last_name = $lastname;
470: }
471:
472: 473: 474: 475: 476: 477:
478: public function setCurrency($currency)
479: {
480: $this->_currency = $currency;
481: }
482:
483: 484: 485: 486: 487: 488:
489: public function setShippingCountry($shippingCountry)
490: {
491: $this->_shipping_country = $shippingCountry;
492: }
493:
494:
495: 496: 497: 498: 499: 500:
501: public function setTimestamp($timestamp)
502: {
503: $this->_timestamp = $timestamp;
504: }
505:
506: 507: 508: 509: 510: 511:
512: public function setNonce($nonce)
513: {
514: $this->_nonce = $nonce;
515: }
516:
517: 518: 519: 520: 521: 522:
523: public function setDebug($debug)
524: {
525: $this->_debug = $debug;
526: }
527:
528:
529: 530: 531: 532: 533: 534:
535: public function __construct($config)
536: {
537: $this->_debug = false;
538: $this->_enable_log = false;
539: $this->_enable_signed_request = false;
540: if (true === is_array($config)) {
541: $this->helper = new Helper();
542:
543: self::$_api_url = (isset($config['sandbox']) && $config['sandbox'] === true) ? self::SANDBOX_API_URL : self::PRODUCTION_API_URL;
544: self::$_api_url = (isset($config['apiUrl']))? $config['apiUrl']: self::$_api_url;
545:
546: $this->_locale = isset($config["locale"]) ? $config["locale"] : self::DEFAULT_LOCALE;
547:
548: if (isset($config['accessToken'])) {
549: $this->setAccessToken($config['accessToken']);
550: $this->setUsername($config['username']);
551: }
552:
553: $this->_anonymous = (isset($config["anonymous"]) && $config["anonymous"] == true) ? true : false;
554:
555: if (isset($config['apiSecret']))
556: $this->setApiSecret($config['apiSecret']);
557: if (isset($config['appNamespace']))
558: $this->setAppNamespace($config['appNamespace']);
559: (isset($config['currency'])) ? $this->setCurrency($config['currency']) : $this->setCurrency(self::DEFAULT_CURRENCY);
560: (isset($config['shippingCountry'])) ? $this->setShippingCountry($config['shippingCountry']) : $this->setShippingCountry(self::DEFAULT_SHIPPING_COUNTRY);
561:
562:
563: self::setInstance($this);
564: Resource::setIzberg($this);
565:
566: } else {
567: throw new Exception\GenericException("Error: __construct() - Configuration data is missing.");
568: }
569: }
570:
571: public function sso($config)
572: {
573:
574: if (isset($config['apiSecret']))
575: $this->setApiSecret($config['apiSecret']);
576: if (isset($config['appNamespace'])) $this->setAppNamespace($config['appNamespace']);
577: $this->setEmail( isset($config['email']) ? $config['email'] : "");
578: $this->setFirstName( isset($config['firstName']) ? $config['firstName'] : "");
579: $this->setLastName( isset($config['lastName']) ? $config['lastName'] : "");
580:
581:
582: return $this->setUser(array(
583: "email" => isset($config['email']) ? $config['email'] : "",
584: "first_name" => isset($config['firstName']) ? $config['firstName'] : "",
585: "last_name" => isset($config['lastName']) ? $config['lastName'] : "",
586: "from_session_id" => isset($config['from_session_id']) ? $config['from_session_id'] : null,
587: ));
588: }
589:
590: 591: 592: 593: 594:
595: public static function getInstance()
596: {
597: if (self::$_singleton) {
598: return self::$_singleton;
599: } else {
600: throw new Exception\GenericException("You should create a first validated Izberg instance");
601: }
602: }
603:
604: 605: 606: 607: 608: 609: 610:
611: public static function setInstance(Izberg $izberg)
612: {
613: self::$_singleton = $izberg;
614: }
615:
616: 617: 618: 619:
620: public function getHelper()
621: {
622: return $this->helper;
623: }
624:
625: 626: 627: 628: 629: 630: 631:
632: public function log($message, $level="error", $path = null)
633: {
634:
635: if (false === $this->_enable_log)
636: return ;
637:
638: if (false === is_dir($path)) {
639: $path = __DIR__."/log/";
640: if (!is_dir($path)) mkdir($path);
641: } else if (substr($path, -1) != '/')
642: $path .= '/';
643:
644: file_put_contents($path."log-".$level."-".date("m-d").".txt", date("H:i:s | ").$message."\n", FILE_APPEND);
645: }
646:
647: 648: 649: 650:
651: public function getInlineUserToken()
652: {
653: if ($this->_anonymous) {
654: $h = 'IcebergAccessToken anonymous:'. $this->getAppNamespace() . ":" . $this->getAccessToken();
655: } else {
656: $h = 'IcebergAccessToken '. $this->getUsername() . ":" . $this->getAccessToken();
657: }
658: return "Authorization: " . $h;
659: }
660:
661: 662: 663: 664:
665: public function getCurrentApplication()
666: {
667: return $this->get("application", null, null, "Accept: application/json", "application/mine");
668: }
669:
670: 671: 672: 673: 674: 675: 676: 677: 678:
679: public function Call($path, $method = 'GET', $params = null, $accept_type = 'Accept: application/json', $content_type = 'Content-Type: application/json; charset=UTF-8', $output_file = null)
680: {
681: if (!is_null($params) && is_array($params) && $content_type == "Content-Type: application/json; charset=UTF-8" && ('GET' !== $method))
682: {
683: $paramString = json_encode($params);
684: }
685: else if (!is_null($params) && is_array($params) && !empty($params)) {
686: $paramString = '?' . http_build_query($params);
687: } else {
688: $paramString = null;
689: }
690:
691: $apiCall = self::$_api_url . $path . (('GET' === $method) ? $paramString : null);
692:
693: $headers = array(
694: $content_type,
695: $accept_type,
696: "Content-Language: " . $this->getLocale(),
697: $this->getInlineUserToken()
698: );
699:
700: $ch = curl_init();
701:
702: if ($this->getDebug()) {
703: curl_setopt($ch, CURLOPT_VERBOSE, true);
704: }
705:
706: $body = ltrim(ltrim($paramString, '&'), '?');
707: if ('POST' === $method)
708: {
709: curl_setopt($ch, CURLOPT_POST, count($params));
710: if (ltrim(ltrim($paramString, '&'), '?') != "") {
711: curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
712: }
713: curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
714: } else if ('DELETE' === $method) {
715: curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
716: } else if ('PUT' === $method) {
717: curl_setopt($ch, CURLOPT_POST, count($params));
718: curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
719: curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
720: }
721:
722: if ($output_file) {
723: $fp = fopen ($output_file, 'w+');
724: curl_setopt($ch, CURLOPT_FILE, $fp);
725: } else {
726: curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
727: }
728:
729:
730: $signature = $this->getRequestSignature($body);
731: $nonce = $this->getNonce();
732: array_push($headers, "Application-Signature: $signature");
733: array_push($headers, "Application-Nonce: $nonce");
734:
735: curl_setopt($ch, CURLOPT_URL, $apiCall);
736: curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
737: curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
738: curl_setopt($ch, CURLOPT_HEADER, 0);
739: curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE);
740: curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
741:
742:
743:
744:
745: $data = $this->curlExec($ch);
746:
747: $this->log($method." | ".$apiCall, "call");
748: if (!is_null($paramString)) {
749: $this->log("PARAMS | ".ltrim(ltrim($paramString, '&'), '?'), "call", __DIR__."/log");
750: }
751: $this->log("DATE | ".$data, "call");
752:
753: if (false === $data) {
754: throw new Exception\GenericException("Error: Call() - cURL error: " . curl_error($ch));
755: }
756: $http_code = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
757: if ($http_code >= 400) {
758:
759: throw new Exception\GenericException ("We got an response with code " . $http_code . " and response " . $data . " from url: " .$apiCall . " and headers : " . json_encode($headers) . " and params: " . $paramString );
760: }
761: curl_close($ch);
762: return ($accept_type == 'Accept: application/json' || $accept_type == 'Content-Type: application/json') ? json_decode($data) : (($accept_type == 'Accept: application/xml') ? simplexml_load_string($data) : $data);
763: }
764:
765: 766: 767: 768: 769:
770: protected function _getSingleSignOnResponse($params = null)
771: {
772:
773: if(is_null($params)) {
774: $params = array(
775: "email" => $this->getEmail(),
776: "first_name" => $this->getFirstName(),
777: "last_name" => $this->getLastName()
778: );
779: }
780:
781: $params["message_auth"] = $this->getMessageAuth($params["email"], $params["first_name"], $params["last_name"]);
782: $params["application"] = $this->getAppNamespace();
783: $params["timestamp"] = $this->getTimeStamp();
784:
785: $apiCall = self::$_api_url . self::SINGLE_SIGN_ON_URL . "?" . http_build_query($params);
786:
787: $headers = array(
788: 'Accept: application/json',
789: 'Authorization: '. $this->getMessageAuth($params["email"],$params["first_name"],$params["last_name"])
790: );
791:
792: $ch = curl_init();
793: curl_setopt($ch, CURLOPT_URL, $apiCall);
794: curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
795: curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 300);
796: curl_setopt($ch, CURLOPT_HEADER, 0);
797: curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
798: curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
799:
800:
801:
802:
803: $jsonData = $this->curlExec($ch);
804:
805:
806: $httpcode = $this->curlGetInfo($ch, CURLINFO_HTTP_CODE);
807:
808: if (false === $jsonData) {
809: throw new Exception\GenericException("Error: _getSingleSignOnResponse() - cURL error: " . curl_error($ch));
810: }
811: $http_code = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
812:
813: curl_close($ch);
814: $jsonResponse = json_decode($jsonData);
815:
816: if (preg_match("/2\d{2}/", $httpcode) == 0) {
817: $message = "Error: from Iceberg API - error: " . print_r($jsonResponse,true) . " with params: " . json_encode($params);
818: switch ($httpcode){
819: case '400':
820: throw new Exception\BadRequestException($message);
821: break;
822: case '401':
823: throw new Exception\UnauthorizedException($message);
824: break;
825: case '403':
826: throw new Exception\ForbiddenException($message);
827: break;
828: case '404':
829: throw new Exception\NotFoundException($message);
830: break;
831: case '405':
832: throw new Exception\MethodNotAllowedException($message);
833: break;
834: case '500':
835: throw new Exception\InternalErrorException($message);
836: break;
837: default:
838: throw new Exception\GenericException($message);
839: break;
840: }
841: }
842: return $jsonResponse;
843: }
844:
845:
846: protected function curlExec($ch)
847: {
848: return curl_exec($ch);
849: }
850:
851: protected function curlGetInfo($ch, $name)
852: {
853: return curl_getinfo($ch, $name);
854: }
855:
856:
857: 858: 859: 860: 861: 862:
863: public function testIzbergToken()
864: {
865: try
866: {
867: $result = $this->Call('user/me/');
868: }
869: catch (Exception\GenericException $e)
870: {
871: $result = false;
872: }
873: if (isset($result->id) && $result->id == 0)
874: $result = false;
875: return ($result);
876: }
877:
878: 879: 880: 881: 882: 883:
884: public static function convertHtml($html)
885: {
886: $converter = new \Html2Text\Html2Text($html);
887: return $converter->getText();
888: }
889:
890: 891: 892: 893: 894: 895:
896: public function get($resource, $id = null, $params = null, $accept_type = "Accept: application/json", $endpoint = null)
897: {
898: if (strtolower($resource) == "cart" && !$id)
899: $id = "mine";
900: if (strtolower($resource) == "user" && !$id)
901: $id = "me";
902: if (strtolower($resource) == "country" && !$params)
903: $params = array("code" => "FR");
904: if (strtolower($resource) == "return" && !$params)
905: $resource = "ReturnRequest";
906:
907: $resource = "Izberg\Resource\\".ucfirst($resource);
908:
909: $object = new $resource();
910: if (!$endpoint)
911: $endpoint = $object->getName();
912: if ($id)
913: $response = $this->Call($object->getPrefix() . $endpoint."/".$id."/", 'GET', $params, $accept_type);
914: else
915: $response = $this->Call($object->getPrefix() . $endpoint."/", 'GET', $params, $accept_type);
916: if ($accept_type != "Accept: application/json") return $response;
917: $object->hydrate($response);
918: return $object;
919: }
920:
921:
922: 923: 924: 925: 926: 927:
928: public function get_list_response($resource, $params = null, $accept_type = "Accept: application/json", $url = null)
929: {
930: $resource = "Izberg\Resource\\" . ucfirst($resource);
931: $object = new $resource();
932:
933: if (method_exists($resource, "get_list") && !$url) {
934: return $object->get_list($params, $accept_type);
935: } else {
936:
937: if (is_null($url)) {
938: $url = $object->getPrefix() . $object->getName()."/";
939: }
940: return $this->Call($url, 'GET', $params, $accept_type);
941: }
942: }
943:
944: 945: 946: 947: 948: 949:
950: public function get_list($resource, $params = null, $accept_type = "Accept: application/json", $url = null)
951: {
952: $list = $this->get_list_response($resource, $params, $accept_type, $url);
953: $object_list = array();
954: $resource = "Izberg\Resource\\".ucfirst($resource);
955: foreach ($list->objects as $object)
956: {
957: $obj = new $resource();
958: $obj->hydrate($object);
959: $object_list[] = $obj;
960: }
961: return $object_list;
962: }
963:
964: 965: 966: 967: 968: 969:
970: public function get_list_meta($resource, $params = null, $accept_type = "Accept: application/json")
971: {
972: $result = $this->get_list_response($resource, $params, $accept_type);
973: return $result->meta;
974: }
975:
976: 977: 978: 979: 980: 981:
982: public function create($resource, $params = null, $accept_type = "Content-Type: application/json")
983: {
984: $resource = "Izberg\Resource\\".ucfirst($resource);
985: if ($this->getDebug())
986: $params['debug'] = 'true';
987: $object = new $resource();
988: $response = $this->Call($object->getPrefix() . $object->getName()."/", 'POST', $params, $accept_type);
989: $object->hydrate($response);
990: return $object;
991: }
992:
993: 994: 995: 996: 997: 998:
999: public function update($resource = null, $id = null, $params = null, $accept_type = "Content-Type: application/json")
1000: {
1001: if (!$id || !$resource)
1002: throw new Exception\GenericException(__METHOD__." needs a valid ID and a valid Resource Name");
1003: $resource = "Izberg\Resource\\".$resource;
1004: $object = new $resource();
1005: $name = $object->getName();
1006: $response = $this->Call($object->getPrefix() . $name . "/" . $id . "/", 'PUT', $params, $accept_type);
1007: $object->hydrate($response);
1008: return $object;
1009: }
1010:
1011:
1012: 1013: 1014: 1015: 1016: 1017:
1018: public function get_schema($resource, $params = null, $accept_type = 'Accept: application/json')
1019: {
1020: $resource = "Izberg\Resource\\".$resource;
1021: $object = new $resource();
1022: return $this->Call($object->getPrefix() . $object->getName() ."/schema/", 'GET', $params, $accept_type);
1023: }
1024:
1025: public function isAnonymous() {
1026: return $this->_anonymous;
1027: }
1028: }
1029: