Highstreet - Version 1.7.0

Version Notes

-

Download this release

Release Info

Developer Highstreet
Extension Highstreet
Version 1.7.0
Comparing to
See all releases


Code changes from version 1.5.0 to 1.7.0

Files changed (25) hide show
  1. app/code/local/Highstreet/Hsapi/Helper/Config/Account.php +408 -0
  2. app/code/local/Highstreet/Hsapi/Helper/Config/Api.php +30 -18
  3. app/code/local/Highstreet/Hsapi/Helper/Config/Cart.php +862 -0
  4. app/code/local/Highstreet/Hsapi/Helper/Config/Checkout.php +785 -0
  5. app/code/local/Highstreet/Hsapi/Helper/Config/Default.php +168 -0
  6. app/code/local/Highstreet/Hsapi/Helper/Config/Redirect.php +110 -0
  7. app/code/local/Highstreet/Hsapi/Model/Attributes.php +15 -9
  8. app/code/local/Highstreet/Hsapi/Model/CartObserver.php +14 -0
  9. app/code/local/Highstreet/Hsapi/Model/CheckoutV2.php +4 -4
  10. app/code/local/Highstreet/Hsapi/Model/Observer.php +11 -2
  11. app/code/local/Highstreet/Hsapi/Model/Products.php +140 -46
  12. app/code/local/Highstreet/Hsapi/Model/Session.php +40 -0
  13. app/code/local/Highstreet/Hsapi/Model/System/Config/Stores.php +19 -0
  14. app/code/local/Highstreet/Hsapi/controllers/AccountController.php +282 -0
  15. app/code/local/Highstreet/Hsapi/controllers/CartController.php +107 -0
  16. app/code/local/Highstreet/Hsapi/controllers/CheckoutController.php +45 -10
  17. app/code/local/Highstreet/Hsapi/controllers/CheckoutV3Controller.php +536 -0
  18. app/code/local/Highstreet/Hsapi/controllers/IndexController.php +19 -3
  19. app/code/local/Highstreet/Hsapi/controllers/RedirectController.php +37 -0
  20. app/code/local/Highstreet/Hsapi/etc/config.xml +55 -15
  21. app/code/local/Highstreet/Hsapi/etc/system.xml +74 -0
  22. app/code/local/Highstreet/SmartAppBanner/etc/config.xml +1 -1
  23. app/design/frontend/base/default/template/highstreet/native_smart_app_banner.phtml +1 -1
  24. app/design/frontend/base/default/template/highstreet/smart_app_banner.phtml +5 -15
  25. package.xml +5 -5
app/code/local/Highstreet/Hsapi/Helper/Config/Account.php ADDED
@@ -0,0 +1,408 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Highstreet_HSAPI_module
5
+ *
6
+ * @package Highstreet_Hsapi
7
+ * @author Radovan Dodic (radovan.dodic@atessoft.rs) ~ AtesSoft
8
+ * @copyright Copyright (c) 2016 Highstreet
9
+ */
10
+ class Highstreet_Hsapi_Helper_Config_Account extends Highstreet_Hsapi_Helper_Config_Default {
11
+
12
+ const INVALID = "invalid";
13
+ const MISSING = "missing";
14
+ const DUPLICATE = "duplicate";
15
+
16
+ public function getAddressStreetLines() {
17
+ return Mage::getStoreConfig('customer/address/street_lines');
18
+ }
19
+
20
+ public function getNewAccountEmailTemplate() {
21
+ return Mage::getStoreConfig('highstreet_hsapi/api/new_account_email_template');
22
+ }
23
+
24
+ public function getNewAccountEmailTemplateIsEnabled() {
25
+ return Mage::getStoreConfig('highstreet_hsapi/api/new_account_email_enabled');
26
+ }
27
+
28
+ /**
29
+ * Populate array with address data
30
+ *
31
+ * @param object $address
32
+ * @return array
33
+ */
34
+ public function getAddressData($address) {
35
+ $street = $address->getStreet();
36
+ // extract house number from first line of address
37
+ if (!isset($street[1]) && preg_match('/^([^\d]*[^\d\s]) *(\d.*)$/', $street[0], $pregResult)) {
38
+ $street[0] = trim($pregResult[1]);
39
+ $street[1] = $pregResult[2];
40
+ }
41
+ return array(
42
+ 'id' => $address->getAddressId(),
43
+ 'first_name' => $address->getFirstname(),
44
+ 'last_name' => $address->getLastname(),
45
+ 'company' => $address->getCompany(),
46
+ 'street' => (is_array($street)) ? $street[0] : $street,
47
+ 'house_number' => (isset($street[1]) && !empty($street[1])) ? $street[1] : null,
48
+ 'addition' => (isset($street[2])) ? $street[2] : null,
49
+ 'postal_code' => $address->getPostcode(),
50
+ 'city' => $address->getCity(),
51
+ 'state' => $address->getRegion(),
52
+ 'country_id' => $address->getCountryId(),
53
+ 'telephone' => (string) $address->getTelephone() . ' ',
54
+ );
55
+ }
56
+
57
+ /**
58
+ * Find CustomerId from session hash
59
+ *
60
+ * @param string $authCode
61
+ * @return bool/string
62
+ */
63
+ public function _getCustomerId($authCode) {
64
+ // instantiate session model first
65
+ $session = Mage::getSingleton('core/session');
66
+ // close session. the session model does not provide a method for this
67
+ session_write_close();
68
+ unset($_SESSION);
69
+ // open new session
70
+ $session->setSessionId($authCode);
71
+ $session->init('checkout', 'frontend');
72
+
73
+ return (isset($_SESSION['core']['visitor_data']['customer_id'])) ? $_SESSION['core']['visitor_data']['customer_id'] : false;
74
+ }
75
+
76
+ /**
77
+ * Get CustomerId from session
78
+ *
79
+ * @return bool/string
80
+ */
81
+ public function _getCId() {
82
+ return Mage::helper('customer')->getCustomer()->getId();
83
+ }
84
+
85
+ /**
86
+ * Get is user logged in, Magento requires valid "frontend" cookie
87
+ *
88
+ * @return bool/string
89
+ */
90
+ public function isLoggedIn() {
91
+ return Mage::helper('customer')->isLoggedIn();
92
+ }
93
+
94
+ /**
95
+ * Send corresponding email template
96
+ *
97
+ * @param object Mage_Customer_Model_Customer
98
+ */
99
+ public function _sendEmailTemplate($customer) {
100
+ if ($this->getNewAccountEmailTemplateIsEnabled()) {
101
+ // send custom email
102
+ $template = $this->getNewAccountEmailTemplate();
103
+ $store_id = Mage::app()->getStore()->getStoreId();
104
+ $emailTemplate = Mage::getModel('core/email_template')->loadByCode($template);
105
+
106
+ //variables passed to template
107
+ $emailTemplateVariables = array(
108
+ 'customer.email' => $customer->getEmail(),
109
+ );
110
+
111
+ $processedTemplate = $emailTemplate->getProcessedTemplate($emailTemplateVariables);
112
+ $emailTemplate->setSenderName(Mage::getStoreConfig('trans_email/ident_general/name'));
113
+ $emailTemplate->setSenderEmail(Mage::getStoreConfig('trans_email/ident_general/email'));
114
+
115
+ // subject is set inside email template
116
+ //$emailTemplate->setTemplateSubject("subject");
117
+
118
+ $emailTemplate->send($customer->getEmail(), $customer->getName(), $emailTemplateVariables);
119
+ } else {
120
+ // send default email
121
+ $customer->sendNewAccountEmail('registered', '', Mage::app()->getStore()->getId());
122
+ }
123
+ return $this;
124
+ }
125
+
126
+ /**
127
+ * checks required fields
128
+ *
129
+ * @param array $params
130
+ * @return bool
131
+ */
132
+ public function _checkCreateAccountFields($params) {
133
+
134
+ $fields = array('email', 'first_name', 'last_name', 'password');
135
+ foreach ($fields as $field) {
136
+ if (!isset($params[$field])) {
137
+ $this->_fieldError(self::MISSING, $field);
138
+ return false;
139
+ } elseif (isset($params[$field]) && empty($params[$field])) {
140
+ $this->_fieldError(self::INVALID, $field);
141
+ return false;
142
+ } elseif ($field == 'email') {
143
+ if (!Zend_Validate::is($params[$field], 'EmailAddress')) {
144
+ $this->_fieldError(self::INVALID, $field, '"' . $params[$field] . '" is not a valid email');
145
+ return false;
146
+ }
147
+ $customer = Mage::getModel('customer/customer');
148
+ $customer->setWebsiteId(Mage::app()->getWebsite()->getId());
149
+ $customer->loadByEmail($params[$field]);
150
+ if ($customer->getId()) {
151
+ $this->_fieldError(self::DUPLICATE, $field, 'This customer email already exists');
152
+ return false;
153
+ }
154
+ }
155
+ }
156
+ return true;
157
+ }
158
+
159
+ public function checkIsEmailValid($email) {
160
+ return Zend_Validate::is($email, 'EmailAddress');
161
+ }
162
+
163
+ /**
164
+ * checks required fields for Update action
165
+ *
166
+ * @param array $params
167
+ * @return bool
168
+ */
169
+ public function _checkUpdateAccountFields($params) {
170
+
171
+ foreach ($params as $key => $field) {
172
+ if ($key == 'address')
173
+ continue;
174
+ if (empty($params[$key])) {
175
+ $this->_fieldError(self::INVALID, $key);
176
+ return false;
177
+ }
178
+ if ($key == 'email' && !empty($params[$key])) {
179
+ if (!$this->checkIsEmailValid($params[$key])) {
180
+ $this->_fieldError(self::INVALID, 'email', '"' . $params[$key] . '" is not a valid email');
181
+ return false;
182
+ }
183
+ }
184
+ }
185
+ if (isset($params['address']) && !$this->_checkUpdateAccountAddressFields($params))
186
+ return false;
187
+ return true;
188
+ }
189
+
190
+ /**
191
+ * checks required address fields for Update action
192
+ *
193
+ * @param array $params
194
+ * @return bool
195
+ */
196
+ public function _checkUpdateAccountAddressFields($params) {
197
+
198
+ $fields = array(
199
+ 'id',
200
+ 'first_name',
201
+ 'last_name',
202
+ 'company',
203
+ 'street',
204
+ 'house_number',
205
+ 'addition',
206
+ 'postal_code',
207
+ 'city',
208
+ 'state',
209
+ 'country_id',
210
+ 'telephone',
211
+ );
212
+ $missingFields = array(
213
+ 'first_name',
214
+ 'last_name',
215
+ 'street',
216
+ 'city',
217
+ 'country_id',
218
+ 'telephone',
219
+ );
220
+
221
+ foreach ($missingFields as $field) {
222
+ if (!isset($params['address'][$field])) {
223
+ $this->_fieldError(self::MISSING, $field, 'Fill in address ' . $field);
224
+ return false;
225
+ }
226
+ }
227
+ foreach ($fields as $field) {
228
+ if (isset($params['address'][$field]) && empty($params['address'][$field])) {
229
+ $this->_fieldError(self::INVALID, $field, 'Fill in address ' . $field);
230
+ return false;
231
+ }
232
+ }
233
+ return true;
234
+ }
235
+
236
+ /**
237
+ * sets body to json error
238
+ *
239
+ * @param string $code
240
+ * @param string $field
241
+ */
242
+ public function _fieldError($code, $field, $message = false) {
243
+ $msg = ($message) ? $message : 'Fill in a ' . $field;
244
+ $this->_JSONencodeAndRespond(array("code" => $code, "field" => $field, 'message' => $msg));
245
+ return;
246
+ }
247
+
248
+ /**
249
+ * prepare customer data for json
250
+ *
251
+ * @param object $customer
252
+ * @return array
253
+ */
254
+ public function getCustomerData($customer) {
255
+ $quote = $this->getCustomerQuote($customer->getEntityId());
256
+ return array(
257
+ 'id' => $customer->getEntityId(),
258
+ 'email' => $customer->getEmail(),
259
+ 'handle' => $customer->getEmail(),
260
+ 'first_name' => $customer->getFirstname(),
261
+ 'last_name' => $customer->getLastname(),
262
+ 'cart_id' => $quote->getId()
263
+ );
264
+ }
265
+
266
+ /**
267
+ * retreives customer last quote
268
+ *
269
+ * @param string $customerId
270
+ * @return object Mage_Sales_Model_Resource_Quote_Collection
271
+ */
272
+ public function getCustomerQuote($customerId) {
273
+
274
+ /** @var Mage_Sales_Model_Resource_Quote_Collection $quoteCollection */
275
+ // Get cart_id
276
+ // reason for this approach: Magento CE1.7 and EE1.12 have bug where user can have 2 or more active quote's
277
+ // by this method we are retreiving latest active one
278
+ // there is no offical reported bug about this, but here its mentioned http://magento.stackexchange.com/questions/29621/how-can-registered-customer-have-two-active-quotes
279
+
280
+ $quoteCollection = Mage::getModel('sales/quote')->getCollection()
281
+ ->addFieldToFilter('customer_id', $customerId)
282
+ ->addOrder('updated_at');
283
+ $quote = $quoteCollection->getFirstItem();
284
+ return $quote;
285
+ }
286
+
287
+ /**
288
+ * fix for Magento street lines
289
+ * if address is in 1 line all params are combined
290
+ * if address is in 2 lines, house num, and addition are combined
291
+ * any other case will go unmodified
292
+ *
293
+ * @param array $params
294
+ * @return array $params
295
+ */
296
+ public function fixStreetLines($params) {
297
+ $tempParams = $params;
298
+ $tempParams['address']['street'] = (isset($tempParams['address']['street'])) ? $tempParams['address']['street'] : '';
299
+ $tempParams['address']['house_number'] = (isset($tempParams['address']['house_number'])) ? $tempParams['address']['house_number'] : '';
300
+ $tempParams['address']['addition'] = (isset($tempParams['address']['addition'])) ? $tempParams['address']['addition'] : '';
301
+ switch ($this->getAddressStreetLines()) {
302
+ case 1:
303
+ $params['address']['street'] = $tempParams['address']['street'] .
304
+ ' ' . $tempParams['address']['house_number'] .
305
+ ' ' . $tempParams['address']['addition'];
306
+ $params['address']['house_number'] = '';
307
+ $params['address']['addition'] = '';
308
+ break;
309
+ case 2:
310
+ $params['address']['house_number'] = $tempParams['address']['house_number'] .
311
+ ' ' . $tempParams['address']['addition'];
312
+ $params['address']['addition'] = '';
313
+ break;
314
+ }
315
+ return $params;
316
+ }
317
+
318
+ public function login($requestObject) {
319
+ $session = Mage::getSingleton('customer/session');
320
+
321
+ $success = false;
322
+ $message = "";
323
+
324
+ $loginArray = $requestObject->getParam('login');
325
+
326
+ $email = $loginArray["username"];
327
+ $password = $loginArray["password"];
328
+ $this->log($loginArray, 'User login');
329
+ if ($session->isLoggedIn()) {
330
+ $success = false;
331
+ $message = "hsapi.loginAction.success.already";
332
+ } else {
333
+ try {
334
+ if ($session->login($email, $password)) {
335
+ $success = true;
336
+ $message = "hsapi.loginAction.success";
337
+ }
338
+ } catch (Mage_Core_Exception $e) {
339
+ switch ($e->getCode()) {
340
+ case Mage_Customer_Model_Customer::EXCEPTION_EMAIL_NOT_CONFIRMED: { // E-mail not confirmed
341
+ $success = false;
342
+ $message = "hsapi.loginAction.error.activate";
343
+ break;
344
+ }
345
+ case Mage_Customer_Model_Customer::EXCEPTION_INVALID_EMAIL_OR_PASSWORD: { // E-mail or password wrong
346
+ $success = false;
347
+ $message = "hsapi.loginAction.error";
348
+ break;
349
+ }
350
+ default: {
351
+ $success = false;
352
+ $message = "hsapi.loginAction.error.fatal";
353
+ break;
354
+ }
355
+ }
356
+ } catch (Exception $e) {
357
+ $success = false;
358
+ $message = "hsapi.loginAction.error.fatal";
359
+ }
360
+ }
361
+
362
+ $response = array();
363
+ $response["success"] = $success;
364
+ $response["message"] = $message;
365
+ $responseCode = ($success) ? "200 OK" : "400 Bad Request";
366
+ $this->log($response, $responseCode);
367
+ $this->_JSONencodeAndRespond($response, $responseCode);
368
+ return;
369
+ }
370
+
371
+ public function logout() {
372
+ Mage::getSingleton('customer/session')->logout();
373
+ $this->_JSONencodeAndRespond(array("OK"), "200 OK");
374
+ return;
375
+ }
376
+
377
+ /**
378
+ * @return Mage_Customer_Model_Session
379
+ */
380
+ public function getCustomer() {
381
+ return Mage::getSingleton('customer/session')->getCustomer();
382
+ }
383
+
384
+ /**
385
+ * @param string $string
386
+ * @return string
387
+ */
388
+ public function escapeString($string) {
389
+ return Mage::helper('core')->htmlEscape($string);
390
+ }
391
+
392
+ /**
393
+ * @param string $string
394
+ * @return string base64encode
395
+ */
396
+ public function b64e($string) {
397
+ return base64_encode($string);
398
+ }
399
+
400
+ /**
401
+ * @param string $string
402
+ * @return string base64decode
403
+ */
404
+ public function b64d($string) {
405
+ return base64_decode($string);
406
+ }
407
+
408
+ }
app/code/local/Highstreet/Hsapi/Helper/Config/Api.php CHANGED
@@ -8,10 +8,9 @@
8
  */
9
 
10
  class Highstreet_Hsapi_Helper_Config_Api extends Mage_Core_Helper_Abstract {
11
- const MIDDLEWARE_URL_SCHEME = "http://";
12
- const MIDDLEWARE_URL_ENVIRONMENT_STAGING = "api-dev";
13
  const MIDDLEWARE_URL_ENVIRONMENT_PRODUCTION = "api";
14
- const MIDDLEWARE_URL_HOST_PATH = "highstreetapp.com/hs-api/1.4";
15
  const CHECKOUT_URL_FALLBACK = "checkout/cart";
16
 
17
  public function alwaysAddSimpleProductsToCart() {
@@ -25,7 +24,8 @@ class Highstreet_Hsapi_Helper_Config_Api extends Mage_Core_Helper_Abstract {
25
  }
26
 
27
  public function storeIdentifier() {
28
- $store_id = Mage::getStoreConfig('highstreet_hsapi/api/store_id');
 
29
  return ($store_id === NULL) ? "" : $store_id;
30
  }
31
 
@@ -64,22 +64,12 @@ class Highstreet_Hsapi_Helper_Config_Api extends Mage_Core_Helper_Abstract {
64
  }
65
 
66
  public function middlewareUrl() {
67
- if ($this->storeIdentifier() == "") {
 
68
  return NULL;
69
  }
70
 
71
- $url = self::MIDDLEWARE_URL_SCHEME . $this->storeIdentifier();
72
-
73
- if ($this->environment() === 'staging') {
74
- $url .= '.' . self::MIDDLEWARE_URL_ENVIRONMENT_STAGING;
75
- } else {
76
- $url .= '.' . self::MIDDLEWARE_URL_ENVIRONMENT_PRODUCTION;
77
- }
78
-
79
- $url .= '.' . self::MIDDLEWARE_URL_HOST_PATH;
80
-
81
-
82
- return $url;
83
  }
84
 
85
  public function shouldShowNativeSmartbanner() {
@@ -99,4 +89,26 @@ class Highstreet_Hsapi_Helper_Config_Api extends Mage_Core_Helper_Abstract {
99
 
100
  return ($data === NULL) ? array() : $data;
101
  }
102
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  */
9
 
10
  class Highstreet_Hsapi_Helper_Config_Api extends Mage_Core_Helper_Abstract {
11
+ const MIDDLEWARE_URL_SCHEME = "https://";
12
+ const MIDDLEWARE_URL_HOST_PATH = "api.highstreetapp.com/";
13
  const MIDDLEWARE_URL_ENVIRONMENT_PRODUCTION = "api";
 
14
  const CHECKOUT_URL_FALLBACK = "checkout/cart";
15
 
16
  public function alwaysAddSimpleProductsToCart() {
24
  }
25
 
26
  public function storeIdentifier() {
27
+ $realStoreId = Mage::helper('highstreet_hsapi/config_default')->getStoreId();
28
+ $store_id = Mage::getStoreConfig('highstreet_hsapi/api/store_id', $realStoreId);
29
  return ($store_id === NULL) ? "" : $store_id;
30
  }
31
 
64
  }
65
 
66
  public function middlewareUrl() {
67
+ $hostAndUri = $this->middlewareHostAndUri();
68
+ if(!$hostAndUri) {
69
  return NULL;
70
  }
71
 
72
+ return self::MIDDLEWARE_URL_SCHEME . $hostAndUri;
 
 
 
 
 
 
 
 
 
 
 
73
  }
74
 
75
  public function shouldShowNativeSmartbanner() {
89
 
90
  return ($data === NULL) ? array() : $data;
91
  }
92
+
93
+ public function storeOverride() {
94
+ return Mage::getStoreConfig('highstreet_hsapi/api/checkout_override_storeview');
95
+ }
96
+
97
+ public function middlewareHostAndUri() {
98
+ if ($this->storeIdentifier() == "") {
99
+ return NULL;
100
+ }
101
+
102
+ $url = $this->storeIdentifier();
103
+
104
+ if ($this->environment() === 'staging') {
105
+ $url .= '-staging';
106
+ }
107
+
108
+ $url .= '.' . self::MIDDLEWARE_URL_HOST_PATH;
109
+
110
+
111
+ return $url;
112
+ }
113
+
114
+ }
app/code/local/Highstreet/Hsapi/Helper/Config/Cart.php ADDED
@@ -0,0 +1,862 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Highstreet_HSAPI_module
5
+ *
6
+ * @package Highstreet_Hsapi
7
+ * @author Radovan Dodic (radovan.dodic@atessoft.rs) ~ AtesSoft
8
+ * @copyright Copyright (c) 2016 Highstreet
9
+ */
10
+ class Highstreet_Hsapi_Helper_Config_Cart extends Highstreet_Hsapi_Helper_Config_Account {
11
+
12
+ // Magento <= 1.7 does not have this const set up in Mage_Checkout_Helper_Cart
13
+ const COUPON_CODE_MAX_LENGTH = 255;
14
+
15
+ /**
16
+ * Constructor class
17
+ */
18
+ public function __construct() {
19
+ parent::__construct();
20
+ }
21
+
22
+ /**
23
+ * Retrieve shopping cart model object
24
+ *
25
+ * @return Mage_Checkout_Model_Cart
26
+ */
27
+ public function _getCart() {
28
+ return Mage::getSingleton('checkout/cart');
29
+ }
30
+
31
+ /**
32
+ * Get Cart helper object
33
+ *
34
+ * @return Mage_Checkout_Helper_Cart
35
+ */
36
+ public function _getCartHelper() {
37
+ return Mage::helper('checkout/cart');
38
+ }
39
+
40
+ /**
41
+ * Get checkout session model instance
42
+ *
43
+ * @return Mage_Checkout_Model_Session
44
+ */
45
+ public function _getSession() {
46
+ return Mage::getSingleton('checkout/session');
47
+ }
48
+
49
+ /**
50
+ * Get current active quote instance
51
+ *
52
+ * @return Mage_Sales_Model_Quote
53
+ */
54
+ public function _getQuote() {
55
+ return $this->_getCart()->getQuote();
56
+ }
57
+
58
+ /**
59
+ * @return Mage_Checkout_Model_Type_Onepage
60
+ */
61
+ public function _getOnepage() {
62
+ return Mage::getSingleton('checkout/type_onepage');
63
+ }
64
+
65
+ /**
66
+ * Initialize cart
67
+ */
68
+ public function cartInit() {
69
+ $this->_getCart()->init();
70
+ return;
71
+ }
72
+
73
+ /**
74
+ * Initialize product instance from request data
75
+ *
76
+ * @param string $pid
77
+ * @return Mage_Catalog_Model_Product || false
78
+ */
79
+ public function _initProduct($pid) {
80
+ if ($pid) {
81
+ $product = Mage::getModel('catalog/product')
82
+ ->setStoreId(Mage::app()->getStore()->getId())
83
+ ->load($pid);
84
+ if ($product->getId()) {
85
+ return $product;
86
+ }
87
+ }
88
+ return false;
89
+ }
90
+
91
+ /**
92
+ * returns etag
93
+ *
94
+ * @return string
95
+ */
96
+ public function getCartEtag() {
97
+ if (!Mage::getSingleton('core/session')->getCartEtag())
98
+ $this->updateCartEtag();
99
+ return Mage::getSingleton('core/session')->getCartEtag();
100
+ }
101
+
102
+ /**
103
+ * generate etag and save to session
104
+ */
105
+ public function updateCartEtag() {
106
+ $hash = bin2hex(mcrypt_create_iv(22, MCRYPT_DEV_URANDOM));
107
+ Mage::getSingleton('core/session')->setCartEtag($hash);
108
+ return;
109
+ }
110
+
111
+ /**
112
+ * Add or Update product to Cart object (Quote)
113
+ *
114
+ * @param array $productData
115
+ * @param array $errors
116
+ * @return array $errors
117
+ */
118
+ public function addOrUpdateProductToCart($productData, $errors) {
119
+
120
+ $params = array(
121
+ 'qty' => $productData['quantity'],
122
+ 'product' => $productData['product_id'],
123
+ 'related_product' => '',
124
+ );
125
+ $cart = $this->_getCart();
126
+ if (!isset($productData['product_id']))
127
+ return $errors;
128
+
129
+ // init product by product_id
130
+ $product = $this->_initProduct($productData['product_id']);
131
+ // if product can not be loaded add to errors and exit function
132
+ if (!$product) {
133
+ $errors[] = $this->getErrorArrayForProduct($productData, "product_unavailable");
134
+ return $errors;
135
+ }
136
+
137
+
138
+ // only simple, simple from configurable are checked for qty, if needed qty is corrected
139
+ // bundles are only check if requested configuration can be added or not
140
+ $productAvailibiltyAndQty = $this->getProductAvailabilityAndQty($product, $productData);
141
+ if ($productAvailibiltyAndQty['qty'] == -1) { // out of stock, add error & exit function
142
+ $errors[] = $this->getErrorArrayForProduct($productData, "quantity_changed", $productAvailibiltyAndQty['error']);
143
+ return $errors;
144
+ } elseif ($productAvailibiltyAndQty['qty'] < $productData['quantity']) { // requested qty not available, add error & change qty
145
+ $errors[] = $this->getErrorArrayForProduct($productData, "quantity_changed", $productAvailibiltyAndQty['error']);
146
+ $params['qty'] = $productAvailibiltyAndQty['qty'];
147
+ }
148
+
149
+
150
+ if (isset($productData['configuration'])) { // add configurable options
151
+ $attributes = $this->getArrayOfAttributesForConfigProduct($productData['configuration']['attributes']);
152
+ $params['super_attribute'] = $attributes;
153
+ } else if (isset($productData['bundle_configuration'])) { // add bundle options
154
+ $bundle = array();
155
+ $bundleQty = array();
156
+ foreach ($productData['bundle_configuration'] as $b) {
157
+ $bundle[$b['option']] = $b['selection'];
158
+ $bundleQty[$b['option']] = $b['quantity'];
159
+ }
160
+ $params['bundle_option'] = $bundle;
161
+ $params['bundle_option_qty'] = $bundleQty;
162
+ }
163
+
164
+ // add or update product
165
+ try {
166
+ // set quote to be ready for changes
167
+ $cart->getQuote()->setTotalsCollectedFlag(false);
168
+ // check if quote item id is set and product exists in quote (update)
169
+ if (isset($productData['id']) && $item = $this->_getQuote()->getItemById($productData['id'])) {
170
+ // check if product has specific configuration, if its configurable or bundle
171
+ if ($product->getTypeId() == Mage_Catalog_Model_Product_Type::TYPE_CONFIGURABLE) {
172
+ // get simple ids
173
+ $oldSimpleId = $item->getOptionByCode('simple_product')->getProductId();
174
+ $newSimpleId = $productData['configuration']['child_product_id'];
175
+
176
+ // if there is no change in child product, update qty only
177
+ if ($oldSimpleId != $newSimpleId) {
178
+ // update buy_request data
179
+ if ($buyRequest = $item->getProduct()->getCustomOption('info_buyRequest')) {
180
+ $buyRequestArr = unserialize($buyRequest->getValue());
181
+ unset($buyRequestArr['super_attribute']);
182
+ foreach ($params['super_attribute'] as $key => $value) {
183
+ $buyRequestArr['super_attribute'][$key] = $value;
184
+ }
185
+ $buyRequest->setValue(serialize($buyRequestArr))->save();
186
+ }
187
+
188
+ // update simple product, id of new simple product
189
+ $item->getOptionByCode('simple_product')->setValue($newSimpleId)->setProductId($newSimpleId)->save();
190
+
191
+ // update attributes
192
+ if ($attr = $item->getOptionByCode('attributes')) {
193
+ foreach ($params['super_attribute'] as $key => $value) {
194
+ $attrArr[$key] = $value;
195
+ }
196
+ if (count($attrArr))
197
+ $attr->setValue(serialize($attrArr))->save();
198
+ }
199
+
200
+ // update product qty by old and new simple id
201
+ $item->getOptionByCode('product_qty_' . $oldSimpleId)->setProductId($newSimpleId)->setCode('product_qty_' . $newSimpleId)->setValue($params['qty'])->save();
202
+ } else {
203
+ $item->setQty($params['qty'])->save();
204
+ }
205
+ } else {
206
+ // simple product or bundle product or any other product
207
+ $item->setQty($params['qty'])->save();
208
+ }
209
+ $this->updateCartEtag();
210
+ } else {
211
+ // if adding new product, add temp_id to $params
212
+ if (isset($productData['temp_id']))
213
+ $params['temp_id'] = $productData['temp_id'];
214
+ $cart->addProduct($product, $params);
215
+ }
216
+ } catch (Exception $e) {
217
+ $this->logException($e, 'Adding / updating product to cart - Exception');
218
+ $this->log(array(
219
+ "Exception message" => $e->getMessage(),
220
+ "Product data" => $productData), 'Adding / updating product to cart');
221
+ }
222
+ return $errors;
223
+ }
224
+
225
+ /**
226
+ * checks product availability and qty
227
+ *
228
+ * @param Mage_Catalog_Model_Product
229
+ * @param array $productData
230
+ * @return array
231
+ */
232
+ public function getProductAvailabilityAndQty($product, $productData) {
233
+ if ($product->getTypeId() == Mage_Catalog_Model_Product_Type::TYPE_SIMPLE) {
234
+ return $this->getSimpleProductAvailabilityAndQty($product, $productData['quantity']);
235
+ } elseif ($product->getTypeId() == Mage_Catalog_Model_Product_Type::TYPE_CONFIGURABLE) {
236
+ $simple_product = $this->_initProduct($productData['configuration']['child_product_id']);
237
+ if ($simple_product) {
238
+ return $this->getSimpleProductAvailabilityAndQty($simple_product, $productData['quantity']);
239
+ }
240
+ } elseif ($product->getTypeId() == Mage_Catalog_Model_Product_Type::TYPE_BUNDLE) {
241
+ return $this->getBundleProductAvailabilityAndQty($product, $productData['quantity']);
242
+ }
243
+
244
+ // any other product return same input data
245
+ return array(
246
+ 'error' => '',
247
+ 'qty' => $productData['quantity']
248
+ );
249
+ }
250
+
251
+ /**
252
+ * checks salability of child products inside bundle product
253
+ *
254
+ * @param Mage_Catalog_Model_Product
255
+ * @param string $qty
256
+ * @return array
257
+ */
258
+ public function getBundleProductAvailabilityAndQty($product, $qty) {
259
+ $returnArray = array(
260
+ 'error' => '',
261
+ 'qty' => $qty
262
+ );
263
+ $childrenIds = $product->getTypeInstance(true)->getChildrenIds($product->getId(), true);
264
+ $childBundleCollection = Mage::getModel('catalog/product')->getCollection()->addFieldToFilter('entity_id', $childrenIds);
265
+ foreach ($childBundleCollection as $childBundle) {
266
+ if (!$childBundle->isSalable()) {
267
+ $returnArray['error'] = "Product is not in stock";
268
+ $returnArray['qty'] = -1;
269
+ }
270
+ }
271
+ return $returnArray;
272
+ }
273
+
274
+ /**
275
+ * checks inventory of simple product
276
+ * checks if there is enough qty of product to be added
277
+ *
278
+ * @param Mage_Catalog_Model_Product
279
+ * @param string $qty
280
+ * @return array
281
+ */
282
+ public function getSimpleProductAvailabilityAndQty($simple_product, $qty) {
283
+ $returnArray = array(
284
+ 'error' => '',
285
+ 'qty' => $qty
286
+ );
287
+ $itemInventory = Mage::getModel('cataloginventory/stock_item')->loadByProduct($simple_product);
288
+ $availableQuantity = $itemInventory->getQty() - $itemInventory->getMinQty();
289
+ $isInStock = (bool) $itemInventory->getIsInStock();
290
+ $isStockManaged = $itemInventory->getManageStock();
291
+ $backordersAllowed = $itemInventory->getBackorders();
292
+ if ($isStockManaged) {
293
+ if (!$isInStock) {
294
+ $returnArray['error'] = "Product is not in stock";
295
+ $returnArray['qty'] = -1;
296
+ } elseif (!$backordersAllowed && $qty > $availableQuantity) {
297
+ $returnArray['error'] = "Requested quantity is not available";
298
+ $returnArray['qty'] = $availableQuantity;
299
+ }
300
+ }
301
+ return $returnArray;
302
+ }
303
+
304
+ /**
305
+ * error array
306
+ *
307
+ * @param array $productData
308
+ * @param string $errorCode
309
+ * @return array
310
+ */
311
+ public function getErrorArrayForProduct($productData = array(), $errorCode = "product_unavailable", $message = "Product is unavailable") {
312
+ return array(
313
+ "type" => "cart_item_error",
314
+ "code" => $errorCode,
315
+ "id" => (isset($productData['id'])) ? $productData['id'] : null,
316
+ "temp_id" => isset($productData['temp_id']) ? $productData['temp_id'] : null,
317
+ "message" => $message
318
+ );
319
+ }
320
+
321
+ /**
322
+ * Converts config values to attribute ID's
323
+ *
324
+ * @param array $attributes
325
+ * @return array $attArray
326
+ */
327
+ public function getArrayOfAttributesForConfigProduct($attributes) {
328
+ $attArray = array();
329
+ foreach ($attributes as $name => $value) {
330
+ $attribute = $this->getAttributeByCode($name);
331
+ $attributeId = $attribute['attribute_id'];
332
+ $attributeValueId = $value;
333
+ $attArray[$attributeId] = $attributeValueId;
334
+ }
335
+ return $attArray;
336
+ }
337
+
338
+ /**
339
+ * returns attribute filtered by frontend name
340
+ *
341
+ * @param string $name
342
+ * @return array $a
343
+ */
344
+ public function getAttributeByName($name) {
345
+ $attr = Mage::getModel('eav/entity_attribute')->getCollection()->addFieldToFilter('frontend_label', $name);
346
+ $a = $attr->getData();
347
+ return $a[0];
348
+ }
349
+
350
+ /**
351
+ * returns attribute filtered by attribute code
352
+ *
353
+ * @param string $name
354
+ * @return array $a
355
+ */
356
+ public function getAttributeByCode($name) {
357
+ $attr = Mage::getModel('eav/entity_attribute')->getCollection()->addFieldToFilter('attribute_code', $name);
358
+ $a = $attr->getData();
359
+ return $a[0];
360
+ }
361
+
362
+ /**
363
+ * returns id of specific attribute value
364
+ *
365
+ * @param string $attributeCode
366
+ * @param string $value
367
+ * @return string
368
+ */
369
+ public function getAttributeValueId($attributeCode, $value) {
370
+ $attribute = Mage::getModel('eav/config')->getAttribute(Mage_Catalog_Model_Product::ENTITY, $attributeCode);
371
+ return $attribute->getSource()->getOptionId($value);
372
+ }
373
+
374
+ /**
375
+ * Empty customer's shopping cart
376
+ */
377
+ public function _emptyShoppingCart() {
378
+ foreach ($this->_getQuote()->getAllItems() as $item) {
379
+ $this->_getQuote()->removeItem($item->getId());
380
+ }
381
+ }
382
+
383
+ /**
384
+ * remove items from cart that are not specified in JSON body
385
+ */
386
+ public function _removeUspecifiedItemsFromCart($params) {
387
+ // quote item ID array setup
388
+ $q_ids = array();
389
+ if (isset($params['items'])) {
390
+ foreach ($params['items'] as $pItem) {
391
+ if (isset($pItem['id']))
392
+ $q_ids[] = $pItem['id'];
393
+ }
394
+ }
395
+ $this->log($q_ids, 'Item IDs NOT to be removed from cart');
396
+ foreach ($this->_getQuote()->getAllVisibleItems() as $item) {
397
+ // remove by quote id
398
+ if (!in_array($item->getId(), $q_ids)) {
399
+ $this->log('Removing Item ID: ' . $item->getId(), '_removeUspecifiedItemsFromCart');
400
+ $this->_getQuote()->removeItem($item->getId())->save();
401
+ }
402
+ }
403
+ }
404
+
405
+ /**
406
+ * Add Coupon code to cart object
407
+ *
408
+ * @param string $code
409
+ * @param array $errors
410
+ * @return array $errors
411
+ */
412
+ public function addCouponCode($code, $errors) {
413
+ if (isset($code[0]['code']) && isset($code[0]['id'])) {
414
+ $couponCode = $code[0]['code'];
415
+
416
+ // generate error array if needed
417
+ $error = array(
418
+ "type" => "coupon_code_error",
419
+ "code" => "invalid_coupon",
420
+ "id" => $code[0]['id'],
421
+ "temp_id" => isset($code[0]['temp_id']) ? $code[0]['temp_id'] : 0,
422
+ "message" => "Invalid coupon code"
423
+ );
424
+ try {
425
+ $codeLength = strlen($couponCode);
426
+ $isCodeLengthValid = $codeLength && $codeLength <= Mage_Checkout_Helper_Cart::COUPON_CODE_MAX_LENGTH;
427
+
428
+ $this->_getQuote()->getShippingAddress()->setCollectShippingRates(true);
429
+ $this->_getQuote()->setCouponCode($isCodeLengthValid ? $couponCode : '');
430
+ if ($codeLength) {
431
+ if (!$isCodeLengthValid || $couponCode != $this->_getQuote()->getCouponCode()) {
432
+ $errors[] = $error;
433
+ } else {
434
+ $this->updateCartEtag();
435
+ }
436
+ }
437
+ } catch (Exception $e) {
438
+ $this->log(array(
439
+ "Exception message" => $e->getMessage(),
440
+ "Coupon code" => $code), 'Adding / updating coupon code');
441
+ $errors[] = $error;
442
+ }
443
+ }
444
+ return $errors;
445
+ }
446
+
447
+ /**
448
+ * Add ETag to header of response
449
+ */
450
+ public function _setETagJSONencodeAndRespondonse($data, $responseCode, $numeric = true) {
451
+ $this->_response->setHeader('ETag', $this->getCartEtag());
452
+ $this->_JSONencodeAndRespond($data, $responseCode, $numeric);
453
+ }
454
+
455
+ /**
456
+ * Returns products from Cart
457
+ * If Configurable - finds relation attributes, and values
458
+ * if Bundle - finds child products, bundle option ID, option selection ID, as well as QTY of non visible cart product
459
+ *
460
+ * @param array $params
461
+ * @return array $productItems
462
+ */
463
+ public function getProductsFromCart($params = array()) {
464
+ $productItems = array();
465
+ $quote = $this->_getQuote();
466
+ $items = $quote->getAllVisibleItems();
467
+
468
+ foreach ($items as $item) {
469
+ $tempArray = array(
470
+ 'id' => $item->getItemId(), // product id in quote table
471
+ 'temp_id' => $this->getProductTempId($item->getItemId(), $item->getProductId()),
472
+ 'product_id' => $item->getProductId(),
473
+ 'prices' => array(
474
+ 'original' => $this->getItemPrice('original', $item),
475
+ 'effective' => $this->getItemPrice('effective', $item),
476
+ 'effective_tax_free' => $this->getItemPrice('effective_tax_free', $item),
477
+ 'total' => $this->getItemPrice('total', $item),
478
+ ),
479
+ 'quantity' => $item->getQty()
480
+ );
481
+
482
+ if ($item->getProductType() == Mage_Catalog_Model_Product_Type::TYPE_CONFIGURABLE) {
483
+ $configurableAttributeCollection = $item->getProduct()->getTypeInstance()->getConfigurableAttributes();
484
+ $relationAttributes = array();
485
+ foreach ($configurableAttributeCollection as $attribute) {
486
+ $relationAttributes[] = $attribute->getProductAttribute()->getAttributeCode();
487
+ }
488
+
489
+ $simpleId = $item->getOptionByCode('simple_product')->getProduct()->getId();
490
+ $simple = Mage::getModel('catalog/product')->load($simpleId);
491
+ $confArray = array();
492
+ foreach ($relationAttributes as $att) {
493
+ $confArray[$att] = $simple->getResource()->getAttribute($att)->getFrontend()->getValue($simple);
494
+ }
495
+
496
+ $tempArray['configuration'] = array(
497
+ "child_product_id" => $simpleId
498
+ );
499
+ } else if ($item->getProductType() == Mage_Catalog_Model_Product_Type::TYPE_BUNDLE) {
500
+ $bundled_product = new Mage_Catalog_Model_Product();
501
+ $bundled_product->load($item->getProductId());
502
+
503
+ $selectionCollection = $bundled_product->getTypeInstance(true)->getSelectionsCollection(
504
+ $bundled_product->getTypeInstance(true)->getOptionsIds($bundled_product), $bundled_product
505
+ );
506
+
507
+ $bundled_items = array();
508
+ foreach ($selectionCollection as $option) {
509
+ $bundled_items[$option->product_id] = array($option->option_id, $option->selection_id);
510
+ }
511
+
512
+ foreach ($item->getChildren() as $child) {
513
+ $product_id = $child->getData('product_id');
514
+ if (isset($bundled_items[$product_id])) {
515
+ $bundleItemsArray[] = array(
516
+ "option" => $bundled_items[$product_id][0],
517
+ "selection" => $bundled_items[$product_id][1],
518
+ "quantity" => $this->getQtyForChildItemInCart($product_id, $item->getItemId(), $this->_getQuote()),
519
+ );
520
+ }
521
+ }
522
+ $tempArray['bundle_configuration'] = $bundleItemsArray;
523
+ }
524
+
525
+ $productItems[] = $tempArray;
526
+ }
527
+ return $productItems;
528
+ }
529
+
530
+ /**
531
+ * get Quote Item price
532
+ *
533
+ * @param string $priceType
534
+ * @param Mage_Sales_Model_Quote_Item $_item
535
+ * @return float
536
+ */
537
+ public function getItemPrice($priceType, $_item) {
538
+ $checkoutHelper = Mage::helper('checkout');
539
+ $taxHelper = Mage::helper('tax');
540
+ switch ($priceType) {
541
+ case 'original':
542
+ return $this->getOriginalProductPrice($_item);
543
+ break;
544
+ case 'effective':
545
+ return $checkoutHelper->getPriceInclTax($_item);
546
+ break;
547
+ case 'effective_tax_free':
548
+ return $_item->getCalculationPrice();
549
+ break;
550
+ case 'total':
551
+ // read setting from admin to show same as on desktop cart
552
+ if ($taxHelper->displayCartPriceExclTax()) {
553
+ return $_item->getRowTotal();
554
+ } else {
555
+ return $checkoutHelper->getSubtotalInclTax($_item);
556
+ };
557
+ break;
558
+ }
559
+ }
560
+
561
+ /**
562
+ * get product price from simple product
563
+ * this does not apply to bundle products
564
+ *
565
+ * @param Mage_Sales_Model_Quote_Item $item
566
+ * @return float
567
+ */
568
+ public function getOriginalProductPrice($_item) {
569
+ $productId = $_item->getProduct()->getId();
570
+ // get simple product from configurable
571
+ if ($_item->getProduct()->getTypeId() == Mage_Catalog_Model_Product_Type::TYPE_CONFIGURABLE && $simpleFromConfig = $_item->getOptionByCode('simple_product')->getProduct())
572
+ $productId = $simpleFromConfig->getId();
573
+ // reload product to get price without rules
574
+ $product = Mage::getModel('catalog/product')->load($productId);
575
+
576
+ if ($product->getTypeId() == 'bundle') {
577
+ return Mage::getModel('bundle/product_price')->getTotalPrices($product,'min',1);
578
+ }
579
+
580
+ return $product->getPrice();
581
+ }
582
+
583
+ /**
584
+ * get temp_id saved into Mage_Sales_Model_Quote_Item info_buyRequest
585
+ *
586
+ * @param int $item_id
587
+ * @param int $productId
588
+ * @return string
589
+ */
590
+ public function getProductTempId($item_id, $productId) {
591
+ $temp_id = null;
592
+ try {
593
+ // using direct sql query to get custom Quote Item Options
594
+ // options are not available using getModel imidietely after saving quote
595
+ $itemOptionTableName = $this->getCoreResource()->getTableName('sales/quote_item_option');
596
+ $sql = "SELECT `value` FROM `" . $itemOptionTableName . "` WHERE `item_id` = :item_id AND `product_id` = :product_id";
597
+ // bind data to prevent SQL injections
598
+ $dataBind = array(
599
+ 'item_id' => $item_id,
600
+ 'product_id' => $productId
601
+ );
602
+ $buyRequest = $this->directDBRead()->fetchOne($sql, $dataBind);
603
+ } catch (Exception $e) {
604
+ $this->logException($e, 'getProductTempId');
605
+ return null;
606
+ }
607
+ if (isset($buyRequest)) {
608
+ $buyRequestArr = unserialize($buyRequest);
609
+ if (isset($buyRequestArr['temp_id'])) {
610
+ $temp_id = $buyRequestArr['temp_id'];
611
+ };
612
+ };
613
+ return $temp_id;
614
+ }
615
+
616
+ /**
617
+ * get coupon code from quote
618
+ *
619
+ * @return string
620
+ */
621
+ public function getCouponCodes() {
622
+ $couponCodes = array();
623
+ if ($cc = $this->_getQuote()->getCouponCode()) {
624
+ $coupon = Mage::getModel('salesrule/coupon')->load($cc, 'code');
625
+ $couponCodes[] = array(
626
+ 'id' => $coupon->getRuleId(),
627
+ 'code' => $cc,
628
+ 'temp_id' => "0"
629
+ );
630
+ }
631
+ return $couponCodes;
632
+ }
633
+
634
+ /**
635
+ * Get tax from quote
636
+ *
637
+ * @return bool true || false
638
+ */
639
+ public function isTaxIncluded() {
640
+ $totals = $this->_getQuote()->getTotals();
641
+ return (isset($totals['tax']) && $totals['tax']->getValue()) ? true : false;
642
+ }
643
+
644
+ /**
645
+ * Get totals from quote
646
+ *
647
+ * @return array
648
+ */
649
+ public function getTotals() {
650
+ $totals = $this->_getQuote()->getTotals();
651
+
652
+ // get tax calculation based on shipping address
653
+ $shipAddress = $this->_getQuote()->getShippingAddress();
654
+ $shippingFromTotals = (isset($totals['shipping']) && $totals['shipping']->getValue()) ? $totals['shipping']->getValue() : 0;
655
+ $shipping = ($shipAddress) ? $this->getShippingPrice($shipAddress) : $shippingFromTotals;
656
+ return array(
657
+ 'discount' => (isset($totals['discount']) && $totals['discount']->getValue()) ? $totals['discount']->getValue() : 0,
658
+ 'sub_total' => (isset($totals['subtotal']) && $totals['subtotal']->getValue()) ? $totals['subtotal']->getValue() : 0,
659
+ 'tax' => (isset($totals['tax']) && $totals['tax']->getValue()) ? $totals['tax']->getValue() : 0,
660
+ 'shipping' => ($shipping == 0 && !$shipAddress->getShippingMethod()) ? null : $shipping,
661
+ 'grand_total' => (isset($totals['grand_total']) && $totals['grand_total']->getValue()) ? $totals['grand_total']->getValue() : 0,
662
+ );
663
+ }
664
+
665
+ /**
666
+ * Function finds difference between product id's in input json, and added products in cart, error products are excluded
667
+ *
668
+ * @param array $params
669
+ * @param array $errors
670
+ * @return array
671
+ */
672
+ public function getMessages($params, $errors) {
673
+ $messages = array();
674
+ if (isset($params['items'])) {
675
+ $itemsIds = array();
676
+ foreach ($params['items'] as $item) {
677
+ array_push($itemsIds, $item['product_id']);
678
+ }
679
+ $countItems = count($params['items']);
680
+ $countCartItems = count($this->_getQuote()->getAllVisibleItems());
681
+ $countErrrorItems = 0;
682
+ foreach ($errors as $e) {
683
+ if ($e['type'] == 'cart_item_error')
684
+ $countErrrorItems++;
685
+ foreach ($itemsIds as $key => $value) {
686
+ if ($value == $e['id'])
687
+ unset($itemsIds[$key]);
688
+ }
689
+ }
690
+ $itemsInCartIds = array();
691
+ if ($countItems - $countErrrorItems < $countCartItems) {
692
+ $items = $this->_getQuote()->getAllVisibleItems();
693
+ foreach ($items as $item) {
694
+ array_push($itemsInCartIds, $item->getProductId());
695
+ }
696
+ $arrayDiff = array_merge(array_diff($itemsIds, $itemsInCartIds), array_diff($itemsInCartIds, $itemsIds));
697
+ if (count($arrayDiff)) {
698
+ foreach ($arrayDiff as $arrayDiffID) {
699
+ $productName = Mage::getModel('catalog/product')->load($arrayDiffID)->getName();
700
+ $messages[] = array(
701
+ "type" => "cart_message",
702
+ "code" => "bonus_product",
703
+ "message" => "Product '" . $productName . "' added to cart"
704
+ );
705
+ }
706
+ }
707
+ }
708
+ }
709
+ return $messages;
710
+ }
711
+
712
+ /**
713
+ * get Qty for child Item in cart / quote
714
+ * filter by product ID, Quote object, and parent Quote Item ID
715
+ *
716
+ * @param integer $productId
717
+ * @param integer $quoteItemId
718
+ * @param Mage_Sales_Model_Quote $quote
719
+ * @return integer
720
+ */
721
+ public function getQtyForChildItemInCart($product_id, $quoteParentItemId, $quote) {
722
+ $salesQuoteItem = Mage::getModel('sales/quote_item')->getCollection()
723
+ ->setQuote($quote)
724
+ ->addFieldToFilter('quote_id', $this->_getQuote()->getId())
725
+ ->addFieldToFilter('product_id', $product_id)
726
+ ->addFieldToFilter('parent_item_id', $quoteParentItemId)
727
+ ->getFirstItem();
728
+ return $salesQuoteItem->getQty();
729
+ }
730
+
731
+ /**
732
+ * save cart, update session - for create and update Cart
733
+ */
734
+ public function saveCartAndQuote() {
735
+ $this->_getSession()->setCartWasUpdated(true);
736
+ $this->_getCart()->save();
737
+ $this->_getQuote()->setTotalsCollectedFlag(false);
738
+ $this->_getQuote()->collectTotals();
739
+ $this->_getQuote()->save();
740
+ }
741
+
742
+ /**
743
+ * get Coupon max length size
744
+ */
745
+ public function getCouponMaxLenght() {
746
+ return self::COUPON_CODE_MAX_LENGTH;
747
+ }
748
+
749
+ /**
750
+ * assign quote item id to error
751
+ * used in case where new item has QTY error but quote item id is not known at time of error was generated
752
+ */
753
+ public function assignQuoteItemIdToErrors($data, $params) {
754
+ foreach ($data['_errors'] as $key => $error) {
755
+ if ($error['id'] == null) {
756
+ foreach ($data['items'] as $item) {
757
+ if ($item['temp_id'] == $error['temp_id'])
758
+ $data['_errors'][$key]['id'] = $item['id'];
759
+ }
760
+ }
761
+ }
762
+ return $data;
763
+ }
764
+
765
+ /**
766
+ * clear unspecified items in input JSON from cart
767
+ * add products to quote
768
+ * add coupon code to quote
769
+ *
770
+ * @param array $params
771
+ * @param array $_errors
772
+ * @return array $_errors
773
+ */
774
+ public function addProductsAndCouponsToQuote($params = array(), $_errors = array()) {
775
+ // remove items from cart that are not specified in input JSON
776
+ $this->_removeUspecifiedItemsFromCart($params);
777
+
778
+ // add procucts to cart
779
+ if (isset($params['items'])) {
780
+ foreach ($params['items'] as $item) {
781
+ $_errors = $this->addOrUpdateProductToCart($item, $_errors);
782
+ }
783
+ }
784
+
785
+ // add coupon codes
786
+ if (isset($params['coupon_codes'])) {
787
+ $_errors = $this->addCouponCode($params['coupon_codes'], $_errors);
788
+ }
789
+
790
+ return $_errors;
791
+ }
792
+
793
+ /**
794
+ * Check if we need to display shipping include tax
795
+ *
796
+ * @return bool
797
+ */
798
+ public function displayShippingIncludeTax() {
799
+ return Mage::getSingleton('tax/config')->displayCartShippingInclTax();
800
+ }
801
+
802
+ /**
803
+ * Get shipping price from shipping address
804
+ *
805
+ * @param Mage_Sales_Model_Quote_Address $shipping
806
+ * @return float
807
+ */
808
+ public function getShippingPrice($shipAddress) {
809
+ if ($this->displayShippingIncludeTax() && $shipAddress->getShippingInclTax()) {
810
+ return $shipAddress->getShippingInclTax();
811
+ } elseif ($shipAddress->getShippingAmount()) {
812
+ return $shipAddress->getShippingAmount();
813
+ }
814
+ return 0;
815
+ }
816
+
817
+ /**
818
+ * Checks item messages added by Mage_CatalogInventory_Model_Observer::checkQuoteItemQty()
819
+ * if item has same message as added by observer, error is added to error array, and item is removed from cart
820
+ *
821
+ * @param array $errors
822
+ * @return array $errors
823
+ */
824
+ public function checkAndUpdateCartInventory($errors) {
825
+ // get original message text via cataloginventory helper in case it is translated or changed
826
+ $outOfStockMessage = Mage::helper('cataloginventory')->__('This product is currently out of stock.');
827
+ foreach ($this->_getQuote()->getAllVisibleItems() as $item) {
828
+ $itemMessage = $item->getMessage();
829
+ if ($itemMessage == $outOfStockMessage) {
830
+ $errors[] = $this->getErrorArrayForProduct(array('id' => $item->getId()));
831
+ $this->log('Removing Item ID: ' . $item->getId(), 'checkAndUpdateCartInventory');
832
+ $this->_getQuote()->removeItem($item->getId())->save();
833
+ } elseif ($itemMessage) { // if any other message is set, qty not available
834
+ // for all products except bundle we can check available qty
835
+ if ($item->getProduct()->getTypeId() != Mage_Catalog_Model_Product_Type::TYPE_BUNDLE) {
836
+ $productAvailableQty = $this->getProductAvailableQtyByQuoteItem($item);
837
+ $errors[] = $this->getErrorArrayForProduct(array('id' => $item->getId()), 'quantity_changed', "Requested quantity is not available");
838
+ $this->log('QTY Changed Item ID: ' . $item->getId() . ', old' . $item->getQty() . '/new' . $productAvailableQty, 'checkAndUpdateCartInventory');
839
+ $item->setQty($productAvailableQty)->save();
840
+ } else {
841
+ // for budle product we display error and keep product in cart
842
+ $errors[] = $this->getErrorArrayForProduct(array('id' => $item->getId()), 'product_unavailable', "Requested quantity is not available");
843
+ $this->log('QTY Unavailable Item ID: ' . $item->getId() . ', qty' . $item->getQty(), 'checkAndUpdateCartInventory');
844
+ }
845
+ }
846
+ }
847
+ return $errors;
848
+ }
849
+
850
+ public function getProductAvailableQtyByQuoteItem($item) {
851
+ if ($item->getProduct()->getTypeId() == Mage_Catalog_Model_Product_Type::TYPE_CONFIGURABLE) {
852
+ $simple_id = $item->getOptionByCode('simple_product')->getProduct()->getId();
853
+ $simple_product = $this->_initProduct($simple_id);
854
+ return Mage::getModel('cataloginventory/stock_item')->loadByProduct($simple_product)->getQty();
855
+ } else {
856
+ $product = $this->_initProduct($item->getProduct()->getId());
857
+ return Mage::getModel('cataloginventory/stock_item')->loadByProduct($simple_product)->getQty();
858
+ }
859
+ return $item->getQty();
860
+ }
861
+
862
+ }
app/code/local/Highstreet/Hsapi/Helper/Config/Checkout.php ADDED
@@ -0,0 +1,785 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Highstreet_HSAPI_module
5
+ *
6
+ * @package Highstreet_Hsapi
7
+ * @author Radovan Dodic (radovan.dodic@atessoft.rs) ~ AtesSoft
8
+ * @copyright Copyright (c) 2016 Highstreet
9
+ */
10
+ class Highstreet_Hsapi_Helper_Config_Checkout extends Highstreet_Hsapi_Helper_Config_Account {
11
+
12
+ /**
13
+ * Constructor class
14
+ */
15
+ public function __construct() {
16
+ parent::__construct();
17
+ }
18
+
19
+ /**
20
+ * Returns onepage object model
21
+ *
22
+ * @return object Mage_Checkout_Type_Onepage
23
+ */
24
+ public function _getOnepage() {
25
+ return Mage::getSingleton('checkout/type_onepage');
26
+ }
27
+
28
+ /**
29
+ * @return Mage_Customer_Model_Session
30
+ */
31
+ public function _getSession() {
32
+ return Mage::getSingleton('customer/session');
33
+ }
34
+
35
+ /**
36
+ * @return Mage_Checkout_Model_Session
37
+ */
38
+ public function _getCheckoutSession() {
39
+ return Mage::getSingleton('checkout/session');
40
+ }
41
+
42
+ /**
43
+ * @return Highstreet_Hsapi_Helper_Config_Cart
44
+ */
45
+ public function _getCartHelper() {
46
+ return Mage::helper('highstreet_hsapi/config_cart');
47
+ }
48
+
49
+ /**
50
+ * Set billing address to quote object
51
+ *
52
+ * @param array $address
53
+ */
54
+ public function setBillingAddressToQuote($address) {
55
+
56
+ $data = array(
57
+ "address_id" => (isset($address['id']) && is_numeric($address['id'])) ? $address['id'] : null,
58
+ "city" => $address['city'],
59
+ "company" => @$address['company'],
60
+ "firstname" => $address['first_name'],
61
+ "lastname" => $address['last_name'],
62
+ "email" => @$address['email'],
63
+ "country_id" => $address['country_id'],
64
+ "region_id" => @$address['state'],
65
+ "postcode" => @$address['postal_code'],
66
+ "street" => array($address['street'], @$address['house_number'], @$address['addition']),
67
+ "telephone" => (string) trim($address['telephone']),
68
+ );
69
+
70
+ $result = $this->_getOnepage()->saveBilling($data, $data['address_id']);
71
+ if (isset($result['error'])) {
72
+ $this->log($result, 'Save Billing address');
73
+ }
74
+
75
+ if ($address['use_same_for_shipping']) {
76
+ $this->setShippingAddressToQuote($data, true);
77
+ }
78
+ return;
79
+ }
80
+
81
+ /**
82
+ * Set shipping address to quote object
83
+ *
84
+ * @param object $quote
85
+ * @param array $address
86
+ * @param bool $as_billing
87
+ * @return object $quote
88
+ */
89
+ public function setShippingAddressToQuote($address, $as_billing = false) {
90
+ if ($as_billing) {
91
+ $data = $address;
92
+ $data["same_as_billing"] = 1;
93
+ } else {
94
+ $data = $this->getAddressDataFromArray($address);
95
+ $data["same_as_billing"] = 0;
96
+ $data["address_id"] = (is_numeric($data["address_id"])) ? $address['id'] : null;
97
+ }
98
+ if (isset($data))
99
+ $data['telephone'] = trim($data['telephone']);
100
+ $result = $this->_getOnepage()->saveShipping($data, $data['address_id']);
101
+ if (isset($result['error'])) {
102
+ $this->log($result, 'Save Shipping address');
103
+ }
104
+ return;
105
+ }
106
+
107
+ /**
108
+ * Populate array with errors
109
+ *
110
+ * @param array $data
111
+ * @param array $invalidFields
112
+ * @param array $missingFields
113
+ * @return array
114
+ */
115
+ public function checkMissingInvalidFields($data, $invalidFields = array(), $missingFields = array()) {
116
+ $nonRequiredFields = array('coupon_codes', 'tax_included');
117
+ $errorArray = array();
118
+ foreach (array_keys($data, null) as $key) {
119
+ if (in_array($key, $nonRequiredFields))
120
+ continue;
121
+ $errorArray[] = array(
122
+ "code" => "missing",
123
+ "field" => $key,
124
+ );
125
+ }
126
+ foreach ($invalidFields as $invalidField) {
127
+ $errorArray[] = array(
128
+ "code" => "invalid",
129
+ "field" => $invalidField,
130
+ );
131
+ }
132
+
133
+ // already defined missing fields by controller
134
+ foreach ($missingFields as $missingField) {
135
+ $errorArray[] = array(
136
+ "code" => "missing",
137
+ "field" => $missingField,
138
+ );
139
+ }
140
+
141
+ $data['_errors'] = $errorArray;
142
+ return $data;
143
+ }
144
+
145
+ /**
146
+ * Populate array with address data
147
+ *
148
+ * @param array $address
149
+ * @return array
150
+ */
151
+ public function getAddressDataFromArray($address) {
152
+ return array(
153
+ "address_id" => @$address['id'],
154
+ "city" => @$address['city'],
155
+ "company" => @$address['company'],
156
+ "firstname" => @$address['first_name'],
157
+ "lastname" => @$address['last_name'],
158
+ "country_id" => @$address['country_id'],
159
+ "region_id" => @$address['state'],
160
+ "postcode" => @$address['postal_code'],
161
+ "street" => array(@$address['street'], @$address['house_number'], @$address['addition']),
162
+ "telephone" => (string) trim(@$address['telephone']) . ' '
163
+ );
164
+ }
165
+
166
+ /**
167
+ * check required default magento address fields
168
+ *
169
+ * @param array $address
170
+ * @param bool $asBool
171
+ * @param array $missing
172
+ * @param string $type
173
+ * @return array
174
+ */
175
+ public function checkRequiredAddressFields($address, $asBool = false, $missing = array(), $type = 'billing_address') {
176
+ $requiredFields = array('first_name', 'last_name', 'street', 'city', 'country_id', 'telephone');
177
+ foreach ($requiredFields as $rfield) {
178
+ if (!isset($address[$rfield]) || $address[$rfield] == null || $address[$rfield] == 'null') {
179
+ if ($asBool)
180
+ return false;
181
+ $missing[] = $type;
182
+ return $missing;
183
+ //$missing[] = $type . '_' . $rfield;
184
+ }
185
+ }
186
+ if ($asBool)
187
+ return true;
188
+ return $missing;
189
+ }
190
+
191
+ /**
192
+ * check is shipping method valid
193
+ *
194
+ * @param string $shipping_method
195
+ * @return bool true | false
196
+ */
197
+ public function checkIsShippingMethodValid($shipping_method) {
198
+ $shipping_method = $this->escapeString($shipping_method);
199
+ // if address is set, use different method of retrieving shipping methods
200
+ $cart = Mage::getSingleton('checkout/cart');
201
+ $address = $cart->getQuote()->getShippingAddress();
202
+ if ($address->getQuoteId() && $address->getFirstname()) {
203
+ $methods = $this->getShippingMethods($address, $cart->getQuote(), true);
204
+ } else {
205
+ $methods = $this->getAllShippingMethods(true);
206
+ }
207
+ foreach ($methods as $sh_method) {
208
+ if (isset($sh_method['code'])) {
209
+ if ($shipping_method == $sh_method['code'])
210
+ return true;
211
+ }
212
+ }
213
+ return false;
214
+ }
215
+
216
+ /**
217
+ * returns array for JSON with ALL shipping methods
218
+ *
219
+ * @return array
220
+ */
221
+ public function getAllShippingMethods($asArray = false) {
222
+ $jsonMethods = array();
223
+ $methods = array();
224
+ $activeCarriers = Mage::getSingleton('shipping/config')->getActiveCarriers();
225
+ $activeMethods = array();
226
+ foreach ($activeCarriers as $carrierCode => $carrierModel) {
227
+ $m = array(
228
+ 'type' => 'option',
229
+ 'title' => Mage::getStoreConfig('carriers/' . $carrierCode . '/title'),
230
+ 'code' => $carrierCode,
231
+ 'price' => 0, //hardcoded for now
232
+ );
233
+ if ($carrierMethods = $carrierModel->getAllowedMethods()) {
234
+ $options = array();
235
+ foreach ($carrierMethods as $methodCode => $method) {
236
+ $code = $carrierCode . '_' . $methodCode;
237
+ $o = array(
238
+ 'type' => 'option',
239
+ 'title' => Mage::getStoreConfig('carriers/' . $carrierCode . '/title'),
240
+ 'code' => $code,
241
+ 'price' => 0, //hardcoded for now
242
+ );
243
+ $options[] = $o;
244
+ if ($asArray)
245
+ $activeMethods[] = $o;
246
+ }
247
+
248
+ $m['options'] = $options;
249
+ //$carrierTitle = Mage::getStoreConfig('carriers/' . $carrierCode . '/title');
250
+ }
251
+ $methods[] = $m;
252
+ }
253
+ if ($asArray)
254
+ return $activeMethods;
255
+ $jsonMethods['shipping_methods'] = $methods;
256
+ return $jsonMethods;
257
+ }
258
+
259
+ /**
260
+ * returns array for JSON with shipping methods dependent on address
261
+ *
262
+ * @return array
263
+ */
264
+ public function getShippingMethods($address, $quote, $asArray = false) {
265
+ $sp = array();
266
+ $quote = $this->_getOnepage()->getQuote();
267
+ $quote->getShippingAddress()->collectShippingRates();
268
+ foreach ($quote->getShippingAddress()->getGroupedAllShippingRates() as $rates) {
269
+ foreach ($rates as $rate) {
270
+ if ($rate instanceof Mage_Shipping_Model_Rate_Result_Error) {
271
+ $errors[$rate->getCarrierTitle()] = 1;
272
+ } else {
273
+ if ($address->getFreeShipping()) {
274
+ $price = 0;
275
+ } else {
276
+ $price = $rate->getPrice();
277
+ }
278
+ if ($price) {
279
+ // get shipping method default price with tax helper, with or without tax
280
+ if ($this->_getCartHelper()->displayShippingIncludeTax()) {
281
+ $price = $quote->getStore()->convertPrice(Mage::helper('tax')->getShippingPrice($price, null, $address));
282
+ } else {
283
+ $price = $quote->getStore()->convertPrice(Mage::helper('tax')->getShippingPrice($price, null, $address, true));
284
+ }
285
+ }
286
+
287
+ $sp[$rate->getCarrier()][] = array(
288
+ 'label' => $rate->getMethodTitle(),
289
+ 'carrier' => $rate->getCarrierTitle(),
290
+ 'code' => $rate->getCarrier() . '_' . $rate->getMethod(),
291
+ 'price' => $price,
292
+ );
293
+ }
294
+ }
295
+ }
296
+ $options = array();
297
+ // creating array ready for json output
298
+ foreach ($sp as $key => $mt) {
299
+ $carrierCode = $key;
300
+ foreach ($mt as $mt_key => $method) {
301
+ $code = $method['code'];
302
+ $optionsArray = array(
303
+ 'type' => 'option',
304
+ 'title' => $method['label'],
305
+ 'code' => $method['code'],
306
+ 'price' => $method['price'],
307
+ );
308
+ if (strstr($carrierCode, "postnl") !== false && $this->isPostNLDeliveryDaysEnabled()) {
309
+ $postNLOptionsRaw = $this->getPostNlDeliveryOptions($address);
310
+ if (count($postNLOptionsRaw)) {
311
+ $optionsArray['options'] = $this->convertPostNLResponseToSubOptions($postNLOptionsRaw, $method['price']);
312
+ }
313
+ }
314
+ $options[] = $optionsArray;
315
+ }
316
+ }
317
+ if ($asArray)
318
+ return $options;
319
+ $jsonMethods['shipping_methods'] = $options;
320
+ return $jsonMethods;
321
+ }
322
+
323
+ /**
324
+ * get PostNl delivery day
325
+ *
326
+ * @return bool
327
+ */
328
+ public function isPostNLDeliveryDaysEnabled() {
329
+ return (bool) Mage::getStoreConfig('postnl/delivery_options/enable_delivery_days');
330
+ }
331
+
332
+ /**
333
+ * convert raw data from postNl extension to custom format
334
+ *
335
+ * @param object $postNLOptionsRaw
336
+ * @param float $price
337
+ * @return array
338
+ */
339
+ public function convertPostNLResponseToSubOptions($postNLOptionsRaw, $price = 0) {
340
+ $response = array();
341
+ foreach ($postNLOptionsRaw as $postNLDate) {
342
+ foreach ($postNLDate->Timeframes->TimeframeTimeFrame as $timeframes) {
343
+ //var_dump($option->Timeframes->TimeframeTimeFrame);die;
344
+ $timeFrom = date('H:i', strtotime($timeframes->From));
345
+ $timeTo = date('H:i', strtotime($timeframes->To));
346
+ $response[] = array(
347
+ 'type' => 'option',
348
+ 'title' => 'Op ' . $postNLDate->Date . ' tussen ' . $timeFrom . ' to ' . $timeTo,
349
+ 'code' => $this->getPostNLCode($timeframes->Options->string[0], $postNLDate->Date, $timeframes->From, $timeframes->To),
350
+ 'price' => $price,
351
+ 'default' => false
352
+ );
353
+ }
354
+ }
355
+ return $response;
356
+ }
357
+
358
+ /**
359
+ * convert postnl array to base64 string
360
+ *
361
+ * @return string base64_encode
362
+ */
363
+ public function getPostNLCode($delivery, $date, $from, $to, $costIncl = 0, $costExcl = 0) {
364
+ // taxconfig code taken from DeliveryOptionsController
365
+ $taxConfig = Mage::getSingleton('tax/config');
366
+ $costs = ($taxConfig->shippingPriceIncludesTax()) ? $costIncl : $costExcl;
367
+
368
+ // array structure is same as function saveDeliveryOption from PostNl DeliveryOptionsController expects
369
+ $jsonArray = array(
370
+ 'type' => $this->translatePostNL($delivery),
371
+ 'date' => $date,
372
+ 'from' => $from,
373
+ 'to' => $to,
374
+ 'costs' => $costs
375
+ );
376
+ return $this->b64e(json_encode($jsonArray));
377
+ }
378
+
379
+ /**
380
+ * manual translate for PostNL delivery type
381
+ *
382
+ * @param string $string
383
+ * @return string
384
+ */
385
+ public function translatePostNL($string) {
386
+ return ($string == "Evening") ? "Avond" : "Overdag";
387
+ }
388
+
389
+ /**
390
+ * returns array for JSON with ALL active payment methods filtered by country set by shipping address
391
+ *
392
+ * @return array
393
+ */
394
+ public function getAllPaymentMethods($asArray = false) {
395
+ $jsonMethods = array();
396
+ $methods = array();
397
+ $model = new Mage_Checkout_Block_Onepage_Payment_Methods();
398
+ $quote = Mage::getSingleton('checkout/cart')->getQuote();
399
+ $selectedPaymentMethod;
400
+ try {
401
+ $selectedPaymentMethod = $quote->getPayment()->getData('method');
402
+ } catch (Exception $e) {
403
+ $this->logException($e, 'Get payment method');
404
+ $this->_JSONencodeAndRespond(array("title" => "Error", "content" => $e->getMessage()));
405
+ return;
406
+ }
407
+ foreach ($model->getMethods() as $method) {
408
+ $methodTitle = $method->getTitle();
409
+ $methodCode = $method->getCode();
410
+ if ($methodCode == "paypal_express") { // PayPal. Has logo and strange label text, override
411
+ $methodTitle = "PayPal";
412
+ }
413
+ $m = array(
414
+ 'type' => 'option',
415
+ 'title' => $method->getTitle(),
416
+ 'code' => $methodCode,
417
+ 'price' => $this->getPaymentMethodPrice($methodCode),
418
+ // Payment fee (price) is not available for standard payment methods, for extensions this need to be individualy coded
419
+ 'image' => null,
420
+ 'options' => $this->_getSuboptionsForPaymentMethod($methodCode),
421
+ );
422
+ $methods[] = $m;
423
+ }
424
+ if ($asArray)
425
+ return $methods;
426
+ $jsonMethods['payment_methods'] = $methods;
427
+
428
+ return $jsonMethods;
429
+ }
430
+
431
+ /**
432
+ * get payment price, used for external payment methods (buckaroo, Adyen...)
433
+ *
434
+ * @param string $methodCode
435
+ * @return float
436
+ */
437
+ public function getPaymentMethodPrice($methodCode) {
438
+ if (strstr($methodCode, 'buckaroo'))
439
+ return $this->getBuckarooFee($methodCode);
440
+ if ($methodCode == "adyen_ideal")
441
+ return $this->getAdyenIdealFee($methodCode);
442
+ return 0;
443
+ }
444
+
445
+ /**
446
+ * returns JSON with ALL active payment methods, methods that are enabled in backend
447
+ *
448
+ * @return string JSON
449
+ */
450
+ public function getAllActivePaymentMethods($asArray = false) {
451
+ $jsonMethods = array();
452
+ $methods = array();
453
+ $allActivePaymentMethods = Mage::getModel('payment/config')->getActiveMethods();
454
+ foreach ($allActivePaymentMethods as $paymentCode => $paymentModel) {
455
+ $paymentTitle = Mage::getStoreConfig('payment/' . $paymentCode . '/title');
456
+ if (strstr($paymentCode, 'buckaroo') && !strstr($paymentCode, 'lightbox') && !Mage::getStoreConfig('buckaroo/' . $paymentCode . '/active'))
457
+ continue;
458
+ $m = array(
459
+ 'type' => 'option',
460
+ 'title' => $paymentTitle,
461
+ 'code' => $paymentCode,
462
+ 'price' => (strstr($paymentCode, 'buckaroo')) ? $this->getBuckarooFee($paymentCode) : 0,
463
+ // Payment fee (price) is not available for standard payment methods, for extensions this need to be individualy coded
464
+ 'image' => null,
465
+ 'options' => ($ideal_options = $this->_getSuboptionsForPaymentMethod($paymentCode)) ? $ideal_options : array(),
466
+ );
467
+ $methods[] = $m;
468
+ }
469
+ if ($asArray)
470
+ return $methods;
471
+ $jsonMethods['payment_methods'] = $methods;
472
+ $this->_JSONencodeAndRespond($jsonMethods, "200 OK");
473
+ return;
474
+ }
475
+
476
+ /**
477
+ * returns JSON with ALL enabled shipping countries
478
+ *
479
+ * @return string JSON
480
+ */
481
+ public function getShippingCountries() {
482
+ $countryArray = array();
483
+
484
+ $countryList = Mage::getModel('directory/country')->getResourceCollection()
485
+ ->loadByStore()
486
+ ->toOptionArray(true);
487
+ foreach ($countryList as $country) {
488
+ if ($country['label'] == '' || $country['value'] == '')
489
+ continue;
490
+ $c = array(
491
+ "name" => $country['label'],
492
+ "code" => $country['value'],
493
+ "states" => $this->getStatesOfCountry($country['value']),
494
+ );
495
+ $countryArray[] = $c;
496
+ }
497
+ $this->_JSONencodeAndRespond($countryArray, "200 OK");
498
+ return;
499
+ }
500
+
501
+ /**
502
+ * returns array with all states
503
+ *
504
+ * @param string ISO country code, example "NL", "US"
505
+ * @return array
506
+ */
507
+ public function getStatesOfCountry($country) {
508
+ $stateArray = Mage::getModel('directory/country')->load($country)->getRegions();
509
+ $st = array();
510
+ if (count($stateArray) > 0) {
511
+ foreach ($stateArray as $state) {
512
+ $st[] = array(
513
+ 'code' => $state->getData('code'),
514
+ 'name' => $state->getData('name')
515
+ );
516
+ }
517
+ return $st;
518
+ } else {
519
+ return array();
520
+ }
521
+ }
522
+
523
+ /**
524
+ * returns subsoptions
525
+ * used for Buckaroo method IDEAL, and Adyen IDEAL
526
+ *
527
+ * @param string $method
528
+ * @return array/bool
529
+ */
530
+ public function _getSuboptionsForPaymentMethod($method) {
531
+ $options = array();
532
+ if ($method === "buckaroo3extended_ideal") {
533
+ $options = $this->getBuckarooPaymentSuboptions($method);
534
+ } elseif ($method === "adyen_ideal") {
535
+ $options = $this->getAdyenPaymentSuboptions($method);
536
+ }
537
+ return $options;
538
+ }
539
+
540
+ /**
541
+ * returns Adyen Ideal payment suboptions
542
+ *
543
+ * @param string $method
544
+ * @return array
545
+ */
546
+ public function getAdyenPaymentSuboptions($method) {
547
+ $options = array();
548
+ $issuerList = $this->getAdyenIssuers();
549
+ $price = $this->getAdyenIdealFee($method);
550
+ foreach ($issuerList as $issuer => $issuerDetails) {
551
+ // logo code from Adyen_Payment_Block_Form_Ideal
552
+ $_bankFile = strtoupper(str_replace(" ", '', $issuerDetails['label']));
553
+ $logo = Mage::getDesign()->getSkinUrl("images/adyen/$_bankFile.png");
554
+ $option = array();
555
+ $option["type"] = "option";
556
+ $option["price"] = $price;
557
+ $option["title"] = $issuerDetails['label'];
558
+ $option["code"] = $issuer;
559
+ $option["image"] = $logo;
560
+ $options[] = $option;
561
+ }
562
+ return $options;
563
+ }
564
+
565
+ /**
566
+ * gets JSON issuer list for ideal, and converts it to specific array
567
+ *
568
+ * @return array
569
+ */
570
+ public function getAdyenIssuers() {
571
+ $storeId = $this->getStoreId();
572
+ $json_issuers = Mage::getStoreConfig("payment/adyen_ideal/issuers", $storeId);
573
+ // code partially taken from Adyen_Payment_Model_Adyen_Ideal
574
+ $issuerData = json_decode($json_issuers, true);
575
+ $issuers = array();
576
+ if (!$issuerData) {
577
+ return $issuers;
578
+ }
579
+ foreach ($issuerData as $issuer) {
580
+ $issuers[(string) $issuer['issuerId'] . " "] = array(
581
+ 'label' => $issuer['name']
582
+ );
583
+ }
584
+ ksort($issuers);
585
+ return $issuers;
586
+ }
587
+
588
+ /**
589
+ * returns Buckaroo Ideal payment suboptions
590
+ *
591
+ * @param string $method
592
+ * @return array
593
+ */
594
+ public function getBuckarooPaymentSuboptions($method) {
595
+ $options = array();
596
+
597
+ $session = Mage::getSingleton('checkout/session');
598
+ $sessionValue = $session->getData('buckaroo3extended_ideal_BPE_Issuer');
599
+ $buckarooIdealModel = new TIG_Buckaroo3Extended_Block_PaymentMethods_Ideal_Checkout_Form();
600
+ $issuerList = $buckarooIdealModel->getIssuerList();
601
+
602
+ $price = $this->getBuckarooFee($method);
603
+ foreach ($issuerList as $issuer => $issuerDetails) {
604
+ $option = array();
605
+ $optionChecked = false;
606
+ if (!empty($sessionValue) && array_key_exists($sessionValue, $issuerList)) {
607
+ if ($issuer == $sessionValue) {
608
+ $optionChecked = true;
609
+ }
610
+ }
611
+ $option["type"] = "option";
612
+ $option["price"] = $price;
613
+ $option["title"] = $issuerDetails['name'];
614
+ $option["code"] = $issuer;
615
+ $option["image"] = $issuerDetails['logo'];
616
+ $options[] = $option;
617
+ }
618
+ return $options;
619
+ }
620
+
621
+ /**
622
+ * returns Buckaroo payment fee from magento configuration
623
+ *
624
+ * @param string $method
625
+ * @return string $price
626
+ */
627
+ public function getBuckarooFee($method) {
628
+ $xpath = 'buckaroo/' . $method . '/payment_fee';
629
+ $price = (Mage::getStoreConfig($xpath)) ? Mage::getStoreConfig($xpath) : 0;
630
+ return $price;
631
+ }
632
+
633
+ /**
634
+ * returns Adyen payment fee from magento configuration
635
+ *
636
+ * @param string $method
637
+ * @return string $price
638
+ */
639
+ public function getAdyenIdealFee($method) {
640
+ $xpath = 'payment/' . $method . '/fee';
641
+ $price = (Mage::getStoreConfig($xpath)) ? Mage::getStoreConfig($xpath) : 0;
642
+ return $price;
643
+ }
644
+
645
+ /**
646
+ * returns true if shipping address is same as billing
647
+ *
648
+ * @param Mage_Sales_Model_Quote_Address $shipping
649
+ * @return bool
650
+ */
651
+ public function sameAsBilling($shipping) {
652
+ return (bool) $shipping->getData('same_as_billing');
653
+ }
654
+
655
+ /**
656
+ * returns shiping method in specific array
657
+ *
658
+ * @param string $method
659
+ * @return array
660
+ */
661
+ public function getShippingMethod($method) {
662
+ if (is_array($method) && isset($method['code'])) {
663
+ $method = $method['code'];
664
+ }
665
+ if ($method) {
666
+ return array(
667
+ 'code' => $method,
668
+ 'options' => ($this->_getSession()->getPostNLData()) ? array('code' => $this->_getSession()->getPostNLData()) : null,
669
+ );
670
+ }
671
+ return null;
672
+ }
673
+
674
+ /**
675
+ * returns payment method in specific array
676
+ *
677
+ * @param string $method
678
+ * @return array
679
+ */
680
+ public function getPaymentMethod($method) {
681
+ if (strstr($method, 'buckaroo3extended')) {
682
+ return $this->getPaymentMethodBuckarooArray($method);
683
+ } elseif (strstr($method, 'adyen_ideal')) {
684
+ return $this->getPaymentMethodAdyenArray($method);
685
+ } else {
686
+ return array(
687
+ 'code' => $method,
688
+ 'options' => null,
689
+ );
690
+ }
691
+ }
692
+
693
+ /**
694
+ * returns options array for Buckaroo method
695
+ *
696
+ * @param string $method
697
+ * @return array
698
+ */
699
+ public function getPaymentMethodBuckarooArray($method) {
700
+ $issuer = $this->_getCheckoutSession()->getData('additionalFields');
701
+ $code = (isset($issuer) && isset($issuer['Issuer'])) ? $issuer['Issuer'] : null;
702
+ return array(
703
+ 'code' => $method,
704
+ 'options' => array(array(
705
+ 'code' => $code
706
+ ))
707
+ );
708
+ }
709
+
710
+ /**
711
+ * returns options array for Adyen_Ideal method
712
+ *
713
+ * @param string $method
714
+ * @return array
715
+ */
716
+ public function getPaymentMethodAdyenArray($method) {
717
+ $quote = $this->_getCartHelper()->_getQuote();
718
+ $code = ($quote->getPayment()->getPoNumber()) ? $quote->getPayment()->getPoNumber() : null;
719
+ return array(
720
+ 'code' => $method,
721
+ 'options' => array(array(
722
+ 'code' => (string) $code . " "
723
+ ))
724
+ );
725
+ }
726
+
727
+ public function getPostNlDeliveryOptions($address) {
728
+ $street = $address->getStreet();
729
+ $postNlOptions = array();
730
+ $cif = Mage::getModel('postnl_deliveryoptions/cif');
731
+ if ($cif) {
732
+ $housenumber = "1"; //default to 1.
733
+ if (Mage::getStoreConfig('customer/address/street_lines') == 1) {
734
+ $st = (is_array($street)) ? $street[0] : $street;
735
+ preg_match('([0-9]+)', $st, $matches);
736
+ if (isset($matches) && count($matches) > 0) {
737
+ $housenumber = $matches[0];
738
+ }
739
+ } else {
740
+ if (is_array($street) && isset($street[1]))
741
+ $housenumber = $street[1];
742
+ }
743
+ $postNlOptions = $cif->setStoreId(Mage::app()->getStore()->getId())
744
+ ->getDeliveryTimeframes(array(
745
+ 'postcode' => str_replace(" ", "", $address->getPostcode()), // Postcode
746
+ 'housenumber' => $housenumber,
747
+ 'deliveryDate' => date('d-m-Y', strtotime('+ 1 day')), // Set delivery day to tomorrow
748
+ 'country' => $address->getCountryId(),
749
+ ));
750
+ }
751
+ return $postNlOptions;
752
+ }
753
+
754
+ /**
755
+ * check is payment method valid
756
+ *
757
+ * @param string $payment_method
758
+ * @return bool
759
+ */
760
+ public function checkIsPaymentMethodValid($payment_method) {
761
+ $payment_method = $this->escapeString($payment_method);
762
+ $validPaymentMethods = $this->getAllPaymentMethods(true);
763
+ foreach ($validPaymentMethods as $method) {
764
+ if ($method['code'] == $payment_method)
765
+ return true;
766
+ }
767
+ return false;
768
+ }
769
+
770
+ /**
771
+ * add additional options to payment array, before its saved
772
+ *
773
+ * @param array $payment
774
+ * @param array $params
775
+ * @return array $payment
776
+ */
777
+ public function addAdditionalPaymentOptions($payment, $params) {
778
+ if ($params['payment_method']['code'] == "adyen_ideal" && isset($params['payment_method']['options']['code'])) {
779
+ $trimmed = trim((string) $params['payment_method']['options']['code']);
780
+ $payment["adyen_ideal_type"] = (string) $trimmed;
781
+ }
782
+ return $payment;
783
+ }
784
+
785
+ }
app/code/local/Highstreet/Hsapi/Helper/Config/Default.php ADDED
@@ -0,0 +1,168 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Highstreet_HSAPI_module
5
+ *
6
+ * @package Highstreet_Hsapi
7
+ * @author Radovan Dodic (radovan.dodic@atessoft.rs) ~ AtesSoft
8
+ * @copyright Copyright (c) 2016 Highstreet
9
+ */
10
+ class Highstreet_Hsapi_Helper_Config_Default extends Mage_Core_Helper_Abstract {
11
+
12
+ const LOG_FILE = "Highstreet_HSAPI.log";
13
+
14
+ /**
15
+ * Constructor class
16
+ */
17
+ public function __construct() {
18
+ $this->_response = Mage::app()->getResponse();
19
+ }
20
+
21
+ /**
22
+ * JSON validation
23
+ *
24
+ * @param string $json
25
+ * @return boolen
26
+ */
27
+ public function isJSONValid($json) {
28
+ $json_array = $this->_JSONtoArray($json);
29
+ return (is_array($json_array)) ? true : false;
30
+ }
31
+
32
+ /**
33
+ * Sets the proper headers
34
+ */
35
+ public function _setHeader($responseCode) {
36
+ Mage::getSingleton('core/session')->setLastStoreCode(Mage::app()->getStore()->getCode());
37
+ header_remove('Pragma'); // removes 'no-cache' header
38
+ $this->_response->setHeader('Content-Type', 'application/json', true);
39
+ if ($responseCode) {
40
+ $this->_response->setHeader('HTTP/1.1', $responseCode);
41
+ }
42
+ }
43
+
44
+ /**
45
+ * Sets headers and body with proper JSON encoding
46
+ */
47
+ public function _JSONencodeAndRespond($data, $responseCode = "400 Bad Request", $numeric_check = true) {
48
+ $this->_setHeader($responseCode);
49
+ if ($numeric_check === FALSE || version_compare(PHP_VERSION, '5.3.3', '<')) {
50
+ $this->_response->setBody(json_encode($data));
51
+ } else {
52
+ $this->_response->setBody(json_encode($data, JSON_NUMERIC_CHECK));
53
+ }
54
+ }
55
+
56
+ /**
57
+ * checks request
58
+ *
59
+ *
60
+ * @param object $request
61
+ * @param array $methods
62
+ * @return bool
63
+ */
64
+ public function _checkIsRequestValid($request, $methods = array('POST', 'GET', 'PUT', 'PATCH'), $checkBody = true) {
65
+ if (!in_array($_SERVER['REQUEST_METHOD'], $methods)) {
66
+ $this->_JSONencodeAndRespond(array("title" => "Error", "content" => "Wrong method"));
67
+ return false;
68
+ } elseif (strtolower($request->getHeader('Content-Type')) != 'application/json' && $_SERVER['REQUEST_METHOD'] == 'POST') {
69
+ $this->_JSONencodeAndRespond(array("title" => "Error", "content" => "Wrong media type"));
70
+ return false;
71
+ } elseif (!$this->isJSONValid($request->getRawBody()) && $_SERVER['REQUEST_METHOD'] == 'POST' && $checkBody) {
72
+ $this->_JSONencodeAndRespond(array("title" => "Error", "content" => "No JSON body"));
73
+ return false;
74
+ }
75
+ return true;
76
+ }
77
+
78
+ /**
79
+ * converts raw json to array
80
+ *
81
+ * @param string $raw_json
82
+ * @return array
83
+ */
84
+ public function _JSONtoArray($raw_json) {
85
+ try {
86
+ $this->log($raw_json, 'JSON to decode');
87
+ $decodedJson = Mage::helper('core')->jsonDecode($raw_json);
88
+ } catch (Exception $e) {
89
+ $this->log($e->getMessage(), 'JSON decoding failed');
90
+ return;
91
+ }
92
+ return $decodedJson;
93
+ }
94
+
95
+ /**
96
+ * HSAPI logger
97
+ *
98
+ * @param array | string $data
99
+ * @param string $message
100
+ * @param int $level
101
+ *
102
+ * Log levels:
103
+ * 0 - Emergency: system is unusable
104
+ * 1 - Alert: action must be taken immediately
105
+ * 2 - Critical: critical conditions
106
+ * 3 - Error: error conditions
107
+ * 4 - Warning: warning conditions
108
+ * 5 - Notice: normal but significant condition
109
+ * 6 - Informational: informational messages
110
+ * 7 - Debug: debug messages
111
+ */
112
+ public function log($data = array(), $message = '', $level = Zend_Log::DEBUG) {
113
+ if ($this->isLogEnabled()) {
114
+ $dataToLog = array('message' => $message, 'data' => $data);
115
+ Mage::log($dataToLog, $level, self::LOG_FILE, true);
116
+ }
117
+ return;
118
+ }
119
+
120
+ /**
121
+ * Exception logger
122
+ *
123
+ * @param Exception $e
124
+ * @param string $message
125
+ */
126
+ public function logException(Exception $e, $message = '') {
127
+ $logData = array(
128
+ "Exception message" => $e->getMessage(),
129
+ "Exception" => "\n" . $e->__toString());
130
+ $this->log($logData, $message);
131
+ return;
132
+ }
133
+
134
+ /**
135
+ * returns setting from admin
136
+ */
137
+ public function isLogEnabled() {
138
+ return Mage::getStoreConfig('highstreet_hsapi/developer/log_enabled');
139
+ }
140
+
141
+ /**
142
+ * Use for direct DB access in read mode
143
+ *
144
+ * @return Mage_Core_Model_Resource getConnection
145
+ */
146
+ public function directDBRead() {
147
+ return $this->getCoreResource()->getConnection('core_read');
148
+ }
149
+
150
+ /**
151
+ * Use for direct resource functions
152
+ *
153
+ * @return Mage_Core_Model_Resource
154
+ */
155
+ public function getCoreResource() {
156
+ return Mage::getSingleton('core/resource');
157
+ }
158
+
159
+ /**
160
+ * Get current store id
161
+ *
162
+ * @return int
163
+ */
164
+ public function getStoreId() {
165
+ return Mage::app()->getStore()->getStoreId();
166
+ }
167
+
168
+ }
app/code/local/Highstreet/Hsapi/Helper/Config/Redirect.php ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Highstreet_HSAPI_module
5
+ *
6
+ * @package Highstreet_Hsapi
7
+ * @author Radovan Dodic (radovan.dodic@atessoft.rs) ~ AtesSoft
8
+ * @copyright Copyright (c) 2016 Highstreet
9
+ */
10
+ class Highstreet_Hsapi_Helper_Config_Redirect extends Highstreet_Hsapi_Helper_Config_Checkout {
11
+
12
+ const BASE_PATH = "/hsapi/";
13
+
14
+ /**
15
+ * Constructor class
16
+ */
17
+ public function __construct() {
18
+ parent::__construct();
19
+ }
20
+
21
+ /**
22
+ * Get decoded path and set session cookies
23
+ *
24
+ * @param object $requestObject
25
+ * @param bool $redirectType (true = external, false = redirect)
26
+ * @return string
27
+ */
28
+ public function getPathAndSetCookies($requestObject, $redirectType = true) {
29
+ $pathHash = $requestObject->getParam('path', false);
30
+ $sessionHash = $requestObject->getParam('session', false);
31
+ $tid = $requestObject->getParam('tid', false);
32
+
33
+ if (!$pathHash && !$redirectType) {
34
+ $this->_fieldError(Highstreet_Hsapi_Helper_Config_Account::MISSING, 'path');
35
+ return false;
36
+ }
37
+ if (!$tid && $redirectType) {
38
+ $this->_fieldError(Highstreet_Hsapi_Helper_Config_Account::MISSING, 'tid');
39
+ return false;
40
+ }
41
+
42
+ if (!$sessionHash) {
43
+ $this->_fieldError(Highstreet_Hsapi_Helper_Config_Account::MISSING, 'session');
44
+ return false;
45
+ }
46
+
47
+ try {
48
+ $decodedSessionJson = base64_decode($sessionHash);
49
+ $paramsSession = $this->_JSONtoArray($decodedSessionJson);
50
+ if (!$redirectType)
51
+ $decodedPath = base64_decode($pathHash);
52
+ } catch (Exception $e) {
53
+ $this->logException($e, 'Redirect base64decode');
54
+ $this->_JSONencodeAndRespond(array("title" => "Error", "content" => $e->getMessage()), "200 OK");
55
+ return false;
56
+ }
57
+
58
+ if (!isset($paramsSession['frontend'])) {
59
+ $this->_fieldError(Highstreet_Hsapi_Helper_Config_Account::MISSING, 'frontend');
60
+ return false;
61
+ }
62
+ if (!$redirectType && !isset($decodedPath)) {
63
+ $this->_fieldError(Highstreet_Hsapi_Helper_Config_Account::MISSING, 'path');
64
+ return false;
65
+ }
66
+
67
+ $sessionCookie = $paramsSession['frontend'];
68
+
69
+ // try to init session by cookie/session ID
70
+ try {
71
+ $session = Mage::getSingleton('core/session');
72
+ // close session. the session model does not provide a method for this
73
+ session_write_close();
74
+ unset($_SESSION);
75
+ // open new session
76
+ $session->setSessionId($sessionCookie);
77
+ $session->init('frontend', 'checkout');
78
+ } catch (Exception $e) {
79
+ $this->logException($e, 'Set Cookie Action - setting session cookie');
80
+ $this->_JSONencodeAndRespond(array("title" => "Error", "content" => $e->getMessage()), "200 OK");
81
+ return false;
82
+ }
83
+
84
+ // if this is external checkout, we set TID to session, and get checkout url from config or helper
85
+ if ($redirectType) {
86
+ $session->setHsTid($tid);
87
+ $decodedPath = $this->getRedirectUrl();
88
+ }
89
+
90
+ // set cookie
91
+ $cookie = Mage::getSingleton('core/cookie');
92
+ $cookie->set('frontend', $sessionCookie, time() + 3600, '/');
93
+ if (isset($paramsSession['frontend_cid']))
94
+ $cookie->set('frontend_cid', $paramsSession['frontend_cid'], time() + 3600, '/');
95
+
96
+ return $decodedPath;
97
+ }
98
+
99
+ /**
100
+ * get redirect url for external checkout
101
+ *
102
+ * @return string
103
+ */
104
+ public function getRedirectUrl() {
105
+ // get redirect url from API helper
106
+ $urlFromHelper = Mage::helper('highstreet_hsapi/config_api')->checkoutRedirectUrl();
107
+ return ($urlFromHelper) ? $urlFromHelper : Mage::helper('checkout/url')->getCheckoutUrl();
108
+ }
109
+
110
+ }
app/code/local/Highstreet/Hsapi/Model/Attributes.php CHANGED
@@ -50,20 +50,23 @@ class Highstreet_Hsapi_Model_Attributes extends Mage_Core_Model_Abstract
50
  * @param null $code
51
  * @return bool
52
  */
53
- public function getAttribute($code=null)
54
  {
55
  if(null != $code){
56
- if (array_key_exists($code, $this->_cachedAttributes)) {
57
  return $this->_cachedAttributes[$code];
58
  } else {
59
- $attribute = $this->_extractResponse($this->_getAttribute($code));
60
 
61
  $response = array();
62
  if (array_key_exists('attributes', $attribute) && count($attribute['attributes']) > 0) {
63
  $response = $attribute['attributes'][0];
64
  }
65
 
66
- $this->_cachedAttributes[$code] = $response;
 
 
 
67
  return $response;
68
  }
69
  }
@@ -107,9 +110,10 @@ class Highstreet_Hsapi_Model_Attributes extends Mage_Core_Model_Abstract
107
  /**
108
  * Extract the correct formatted array from the attribute data
109
  * @param $attributes
 
110
  * @return array|bool
111
  */
112
- private function _extractResponse($attributes)
113
  {
114
 
115
  if(!is_array($attributes)){
@@ -127,10 +131,12 @@ class Highstreet_Hsapi_Model_Attributes extends Mage_Core_Model_Abstract
127
  $result['type'] = $attribute['frontend_input'];
128
 
129
  //Get the optionValues for this attribute
130
- $result['options'] = $this->_getAttributeOptionValues(
131
- $attribute['attribute_id'],
132
- $attribute['default_value']
133
- );
 
 
134
 
135
  //we need to push to respond as a json array without index
136
  array_push($response['attributes'], $result);
50
  * @param null $code
51
  * @return bool
52
  */
53
+ public function getAttribute($code=null,$includeOptions=true)
54
  {
55
  if(null != $code){
56
+ if (array_key_exists($code, $this->_cachedAttributes) && $includeOptions) {
57
  return $this->_cachedAttributes[$code];
58
  } else {
59
+ $attribute = $this->_extractResponse($this->_getAttribute($code), $includeOptions);
60
 
61
  $response = array();
62
  if (array_key_exists('attributes', $attribute) && count($attribute['attributes']) > 0) {
63
  $response = $attribute['attributes'][0];
64
  }
65
 
66
+ if ($includeOptions) {
67
+ $this->_cachedAttributes[$code] = $response;
68
+ }
69
+
70
  return $response;
71
  }
72
  }
110
  /**
111
  * Extract the correct formatted array from the attribute data
112
  * @param $attributes
113
+ * @param $includeOptions
114
  * @return array|bool
115
  */
116
+ private function _extractResponse($attributes, $includeOptions=true)
117
  {
118
 
119
  if(!is_array($attributes)){
131
  $result['type'] = $attribute['frontend_input'];
132
 
133
  //Get the optionValues for this attribute
134
+ if ($includeOptions) {
135
+ $result['options'] = $this->_getAttributeOptionValues(
136
+ $attribute['attribute_id'],
137
+ $attribute['default_value']
138
+ );
139
+ }
140
 
141
  //we need to push to respond as a json array without index
142
  array_push($response['attributes'], $result);
app/code/local/Highstreet/Hsapi/Model/CartObserver.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Highstreet_Hsapi_Model_CartObserver {
4
+
5
+ /**
6
+ * Listener for the cart or quote changes
7
+ * updates session etag with hashed timestamp
8
+ */
9
+ public function cartEtagUpdate($observer) {
10
+ Mage::helper('highstreet_hsapi/config_cart')->updateCartEtag();
11
+ return;
12
+ }
13
+
14
+ }
app/code/local/Highstreet/Hsapi/Model/CheckoutV2.php CHANGED
@@ -230,8 +230,8 @@ class Highstreet_Hsapi_Model_CheckoutV2 extends Mage_Core_Model_Abstract
230
  } else {
231
  $response["error"] = -1;
232
  }
233
-
234
- if ($billingAddressData["firstname"] !== null) {
235
  $billingAddressResponse = array();
236
 
237
  $billingAddressResponse["email"] = $billingAddressData["email"];
@@ -343,7 +343,7 @@ class Highstreet_Hsapi_Model_CheckoutV2 extends Mage_Core_Model_Abstract
343
  $actualQuantity = $requestedQuantity; //actual qty is what we are going to add
344
 
345
  //adjust actual quantity if we are requesting more than in stock
346
- $availableQuantity = $itemInventory->getQty();
347
  $isInStock = $itemInventory->getIsInStock();
348
  $isStockManaged = $itemInventory->getManageStock();
349
  $backordersAllowed = $itemInventory->getBackorders();
@@ -747,7 +747,7 @@ class Highstreet_Hsapi_Model_CheckoutV2 extends Mage_Core_Model_Abstract
747
  $responseRate["carrierTitle"] = $rate->getCarrierTitle();
748
  $responseRate["carrierCode"] = $rate->getCode();
749
 
750
- $responseRate["method"] = $rate->getMethod();
751
  $responseRate["methodTitle"] = $rate->getMethodTitle();
752
  $responseRate["methodDescription"] = $rate->getMethodDescription();
753
  $responseRate["price"] = $price;
230
  } else {
231
  $response["error"] = -1;
232
  }
233
+
234
+ if (isset($billingAddressData["firstname"]) && $billingAddressData["firstname"] !== null) {
235
  $billingAddressResponse = array();
236
 
237
  $billingAddressResponse["email"] = $billingAddressData["email"];
343
  $actualQuantity = $requestedQuantity; //actual qty is what we are going to add
344
 
345
  //adjust actual quantity if we are requesting more than in stock
346
+ $availableQuantity = $itemInventory->getQty() - $itemInventory->getMinQty();
347
  $isInStock = $itemInventory->getIsInStock();
348
  $isStockManaged = $itemInventory->getManageStock();
349
  $backordersAllowed = $itemInventory->getBackorders();
747
  $responseRate["carrierTitle"] = $rate->getCarrierTitle();
748
  $responseRate["carrierCode"] = $rate->getCode();
749
 
750
+ $responseRate["method"] = $rate->getMethod() . " ";
751
  $responseRate["methodTitle"] = $rate->getMethodTitle();
752
  $responseRate["methodDescription"] = $rate->getMethodDescription();
753
  $responseRate["price"] = $price;
app/code/local/Highstreet/Hsapi/Model/Observer.php CHANGED
@@ -2,7 +2,7 @@
2
 
3
  class Highstreet_Hsapi_Model_Observer {
4
 
5
- private $_orderCommentTest = "Order made via the Highstreet app. Data identifier: ";
6
 
7
  /**
8
  * Listener for the sales_quote_merge_before event
@@ -48,15 +48,24 @@ class Highstreet_Hsapi_Model_Observer {
48
  $order->addStatusHistoryComment($this->_orderCommentTest . Mage::getSingleton('checkout/session')->getHsTid())
49
  ->setIsVisibleOnFront(false)
50
  ->setIsCustomerNotified(false);
 
 
 
 
 
 
 
51
  $order->save();
52
  } catch (Exception $e) {}
 
 
53
  }
54
  }
55
 
56
  /**
57
  * Private function used to communicate orders to the Highstreet middleware
58
  */
59
- private function _communicateOrderEvent($order, $status = '') {
60
  if ($order->getQuoteId() > 0) {
61
 
62
  // Check if this order identifies as a HS order trough the earlier added comment
2
 
3
  class Highstreet_Hsapi_Model_Observer {
4
 
5
+ protected $_orderCommentTest = "Order made via the Highstreet app. Data identifier: ";
6
 
7
  /**
8
  * Listener for the sales_quote_merge_before event
48
  $order->addStatusHistoryComment($this->_orderCommentTest . Mage::getSingleton('checkout/session')->getHsTid())
49
  ->setIsVisibleOnFront(false)
50
  ->setIsCustomerNotified(false);
51
+
52
+ //Override the store (if filled in)
53
+ $configHelper = Mage::helper('highstreet_hsapi/config_api');
54
+ $storeId = $configHelper->storeOverride();
55
+ if($storeId && $storeId != -1) {
56
+ $order->setStoreId($storeId);
57
+ }
58
  $order->save();
59
  } catch (Exception $e) {}
60
+
61
+
62
  }
63
  }
64
 
65
  /**
66
  * Private function used to communicate orders to the Highstreet middleware
67
  */
68
+ protected function _communicateOrderEvent($order, $status = '') {
69
  if ($order->getQuoteId() > 0) {
70
 
71
  // Check if this order identifies as a HS order trough the earlier added comment
app/code/local/Highstreet/Hsapi/Model/Products.php CHANGED
@@ -11,9 +11,13 @@ class Highstreet_Hsapi_Model_Products extends Mage_Core_Model_Abstract
11
  const MEDIA_PATH = '/media/';
12
  const PRODUCTS_MEDIA_PATH = '/media/catalog/product';
13
  const NO_IMAGE_PATH = 'no_selection';
14
- const RANGE_FALLBACK_RANGE = 100;
 
15
  const SPECIAL_PRICE_FROM_DATE_FALLBACK = "1970-01-01 00:00:00";
16
 
 
 
 
17
  protected $_attributesModel = null;
18
 
19
  public function __construct() {
@@ -137,16 +141,18 @@ class Highstreet_Hsapi_Model_Products extends Mage_Core_Model_Abstract
137
 
138
  $collection = $catalogSearchModelCollection;
139
  } else {
140
-
141
  // initialize
142
  $collection = Mage::getModel('catalog/product')->getCollection();
143
- $collection->addStoreFilter();
144
- Mage::getSingleton('catalog/product_status')->addVisibleFilterToCollection($collection);
145
- Mage::getSingleton('catalog/product_visibility')->addVisibleInCatalogFilterToCollection($collection);
146
-
147
  }
148
 
149
 
 
 
 
 
 
 
 
150
 
151
 
152
  $categoryNotSet = false;
@@ -158,7 +164,7 @@ class Highstreet_Hsapi_Model_Products extends Mage_Core_Model_Abstract
158
 
159
  $category = Mage::getModel('catalog/category')->load($categoryId);
160
  if ($category->getId() === NULL) {
161
- return array();
162
  }
163
 
164
  // apply search
@@ -174,6 +180,10 @@ class Highstreet_Hsapi_Model_Products extends Mage_Core_Model_Abstract
174
  $range = array(0, self::RANGE_FALLBACK_RANGE);
175
  }
176
 
 
 
 
 
177
  $collection->getSelect()->limit($range[1], $range[0]);
178
 
179
  $attributesArray = array();
@@ -193,39 +203,12 @@ class Highstreet_Hsapi_Model_Products extends Mage_Core_Model_Abstract
193
 
194
  //apply filters
195
  if(!empty($filters)) {
196
- foreach ($filters as $filter) {
197
- if (array_key_exists('attribute', $filter)) {
198
- foreach ($filter as $operator => $condition) {
199
- if ($operator != 'attribute') {
200
- $collection->addAttributeToFilter(array(array('attribute' => $filter['attribute'], $operator => $condition)));
201
- }
202
- }
203
- }
204
- }
205
  }
206
 
207
  // Add 'out of stock' filter, if preffered
208
  if (!Mage::getStoreConfig('cataloginventory/options/show_out_of_stock')) {
209
  Mage::getSingleton('cataloginventory/stock')->addInStockFilterToCollection($collection);
210
-
211
- // Make a better fix for this. At this time this seems impossible, this doesn't work:
212
- // $collection->addAttributeToFilter('is_salable', array('eq' => 1));
213
- // Mage::getSingleton('cataloginventory/stock')->addInStockFilterToCollection($collection); // Another popular suggestion around the web, crashes the app for now and 'under water' does the same as l:276
214
- // For now we look trough all the configurable products in the collection of a certain category and filter out the unneded products
215
- $collectionConfigurable = Mage::getResourceModel('catalog/product_collection')->addAttributeToFilter('type_id', array('eq' => 'configurable'));
216
- $collectionConfigurable->addCategoryFilter($category);
217
-
218
- $outOfStockConfis = array();
219
- foreach ($collectionConfigurable as $_configurableproduct) {
220
- $product = Mage::getModel('catalog/product')->load($_configurableproduct->getId());
221
- if (!$product->getData('is_salable')) {
222
- $outOfStockConfis[] = $product->getId();
223
- }
224
- }
225
-
226
- if (count($outOfStockConfis) > 0) {
227
- $collection->addAttributeToFilter('entity_id', array('nin' => $outOfStockConfis));
228
- }
229
  }
230
 
231
  // Apply type filter, we only want Simple and Configurable and Bundle products in our API
@@ -241,7 +224,7 @@ class Highstreet_Hsapi_Model_Products extends Mage_Core_Model_Abstract
241
  * Format result array
242
  */
243
  $products = array('products' => array());
244
-
245
  // If range requests no products to be returned, return no products. The limit() doesn't take 0 for an answer
246
  if ($range[1] > 0) {
247
  if (!$hideAttributes) {
@@ -319,6 +302,10 @@ class Highstreet_Hsapi_Model_Products extends Mage_Core_Model_Abstract
319
  $range = array(0, self::RANGE_FALLBACK_RANGE);
320
  }
321
 
 
 
 
 
322
  $collection->getSelect()->limit($range[1], $range[0]);
323
 
324
  // Add 'out of stock' filter, if preffered
@@ -543,11 +530,8 @@ class Highstreet_Hsapi_Model_Products extends Mage_Core_Model_Abstract
543
  $result = $controller->createBlock($filterBlockName)->setLayer($layer)->setAttributeModel($attribute)->init();
544
  $options = array();
545
  foreach($result->getItems() as $option) {
546
- $title = str_replace('<span class="price">', "", $option->getLabel());
547
- $title = str_replace('</span>', "", $title);
548
-
549
- $count = $option->getData('count');
550
- array_push($options, array('value' => $option->getValue(), 'title' => $title, 'product_count' => $count));
551
  }
552
 
553
  if (count($options) > 1) {
@@ -571,6 +555,27 @@ class Highstreet_Hsapi_Model_Products extends Mage_Core_Model_Abstract
571
  */
572
  /***********************************/
573
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
574
  /**
575
  * Gets count for given collection. Function made for easier subclassing
576
  *
@@ -649,6 +654,76 @@ class Highstreet_Hsapi_Model_Products extends Mage_Core_Model_Abstract
649
  return $collection;
650
  }
651
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
652
  /**
653
  *
654
  * Gets attributes of a given product object.
@@ -872,11 +947,17 @@ class Highstreet_Hsapi_Model_Products extends Mage_Core_Model_Abstract
872
 
873
  // SEE: Mage_Catalog_Block_Product_View_Attributes
874
  $controller = Mage::app()->getLayout();
875
- $block = $controller->createBlock('Mage_Catalog_Block_Product_View_Attributes');
876
 
877
  // Same function as used by the layouting
878
- foreach ($block->getAdditionalData() as $attribute) {
879
- $html .= "<p><strong>".$attribute['label'].":</strong></br>".$attribute['value']."</p>";
 
 
 
 
 
 
880
  }
881
 
882
  $additionalAttributeData['inline_value'] = $html;
@@ -888,6 +969,17 @@ class Highstreet_Hsapi_Model_Products extends Mage_Core_Model_Abstract
888
  continue;
889
  }
890
 
 
 
 
 
 
 
 
 
 
 
 
891
  $attributeObject = $resProduct->getResource()->getAttribute($attribute);
892
 
893
  if ($attributeObject !== false) {
@@ -1078,9 +1170,11 @@ class Highstreet_Hsapi_Model_Products extends Mage_Core_Model_Abstract
1078
 
1079
  $output = array();
1080
 
1081
- foreach (Mage::getModel('catalog/product')->load($productId)->getMediaGalleryImages()->getItems() as $key => $value) {
 
1082
  $imageData = $value->getData();
1083
 
 
1084
  if ($this->_shouldExcludeImageFromMediaGallery($imageData["file"], $resProduct)) {
1085
  continue;
1086
  }
@@ -1129,4 +1223,4 @@ class Highstreet_Hsapi_Model_Products extends Mage_Core_Model_Abstract
1129
  "special_from_date", "special_to_date", "special_price", "is_salable");
1130
  }
1131
 
1132
- }
11
  const MEDIA_PATH = '/media/';
12
  const PRODUCTS_MEDIA_PATH = '/media/catalog/product';
13
  const NO_IMAGE_PATH = 'no_selection';
14
+ const RANGE_FALLBACK_RANGE = 20;
15
+ const RANGE_LIMIT = 100;
16
  const SPECIAL_PRICE_FROM_DATE_FALLBACK = "1970-01-01 00:00:00";
17
 
18
+ const PRODUCTS_ERROR_NOT_FOUND = 404;
19
+ const PRODUCTS_ERROR_OUT_OF_RANGE = 403;
20
+
21
  protected $_attributesModel = null;
22
 
23
  public function __construct() {
141
 
142
  $collection = $catalogSearchModelCollection;
143
  } else {
 
144
  // initialize
145
  $collection = Mage::getModel('catalog/product')->getCollection();
 
 
 
 
146
  }
147
 
148
 
149
+ $collection->addStoreFilter()
150
+ ->addMinimalPrice()
151
+ ->addFinalPrice()
152
+ ->addTaxPercents()
153
+ ->distinct(true);
154
+ Mage::getSingleton('catalog/product_status')->addVisibleFilterToCollection($collection);
155
+ Mage::getSingleton('catalog/product_visibility')->addVisibleInCatalogFilterToCollection($collection);
156
 
157
 
158
  $categoryNotSet = false;
164
 
165
  $category = Mage::getModel('catalog/category')->load($categoryId);
166
  if ($category->getId() === NULL) {
167
+ return self::PRODUCTS_ERROR_NOT_FOUND;
168
  }
169
 
170
  // apply search
180
  $range = array(0, self::RANGE_FALLBACK_RANGE);
181
  }
182
 
183
+ if ($range[1] > self::RANGE_LIMIT) {
184
+ return self::PRODUCTS_ERROR_OUT_OF_RANGE;
185
+ }
186
+
187
  $collection->getSelect()->limit($range[1], $range[0]);
188
 
189
  $attributesArray = array();
203
 
204
  //apply filters
205
  if(!empty($filters)) {
206
+ $collection = $this->_filterProductsCollection($collection, $filters);
 
 
 
 
 
 
 
 
207
  }
208
 
209
  // Add 'out of stock' filter, if preffered
210
  if (!Mage::getStoreConfig('cataloginventory/options/show_out_of_stock')) {
211
  Mage::getSingleton('cataloginventory/stock')->addInStockFilterToCollection($collection);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
212
  }
213
 
214
  // Apply type filter, we only want Simple and Configurable and Bundle products in our API
224
  * Format result array
225
  */
226
  $products = array('products' => array());
227
+
228
  // If range requests no products to be returned, return no products. The limit() doesn't take 0 for an answer
229
  if ($range[1] > 0) {
230
  if (!$hideAttributes) {
302
  $range = array(0, self::RANGE_FALLBACK_RANGE);
303
  }
304
 
305
+ if ($range[1] > self::RANGE_LIMIT) {
306
+ return self::PRODUCTS_ERROR_OUT_OF_RANGE;
307
+ }
308
+
309
  $collection->getSelect()->limit($range[1], $range[0]);
310
 
311
  // Add 'out of stock' filter, if preffered
530
  $result = $controller->createBlock($filterBlockName)->setLayer($layer)->setAttributeModel($attribute)->init();
531
  $options = array();
532
  foreach($result->getItems() as $option) {
533
+
534
+ array_push($options, $this->getFilterOptionValue($option));
 
 
 
535
  }
536
 
537
  if (count($options) > 1) {
555
  */
556
  /***********************************/
557
 
558
+ /**
559
+ * Returns a formatted object for a given option item
560
+ * Made for subclassing
561
+ *
562
+ * @param $optionItem
563
+ * @return array
564
+ *
565
+ */
566
+ protected function getFilterOptionValue($optionItem = null) {
567
+ if (!$optionItem) {
568
+ return;
569
+ }
570
+
571
+ $title = str_replace('<span class="price">', "", $optionItem->getLabel());
572
+ $title = str_replace('</span>', "", $title);
573
+
574
+ $count = $optionItem->getData('count');
575
+
576
+ return array('value' => $optionItem->getValue(), 'title' => $title, 'product_count' => $count);
577
+ }
578
+
579
  /**
580
  * Gets count for given collection. Function made for easier subclassing
581
  *
654
  return $collection;
655
  }
656
 
657
+ /**
658
+ * Joins tables for attributes and adds given filters
659
+ * Inspired by the applyFilterToCollection function by the class Mage_Catalog_Model_Resource_Layer_Filter_Attribute
660
+ *
661
+ * @param mixed Collection
662
+ * @param array filters
663
+ * @return mixed Collection
664
+ *
665
+ */
666
+ protected function _filterProductsCollection ($collection = null, $filters = array()) {
667
+ if ($collection === null || count($filters) <= 0) {
668
+ return $collection;
669
+ }
670
+
671
+ foreach ($filters as $filter) {
672
+ if (array_key_exists('attribute', $filter)) {
673
+
674
+ if ($filter['attribute'] === 'price') {
675
+ foreach ($filter as $operator => $value) {
676
+ if ($operator != 'attribute') {
677
+ $collection->addAttributeToFilter(array(array('attribute' => $filter['attribute'], $operator => $value)));
678
+ }
679
+ }
680
+ } else {
681
+ $attributeObject = $this->_attributesModel->getAttribute($filter['attribute'], false);
682
+ $resource = Mage::getSingleton('core/resource');
683
+ $connection = $resource->getConnection('default_read');
684
+ $tableAlias = $filter['attribute'] . '_idx';
685
+ $conditions = array(
686
+ "{$tableAlias}.entity_id = e.entity_id",
687
+ $connection->quoteInto("{$tableAlias}.attribute_id = ?", $attributeObject['id']),
688
+ $connection->quoteInto("{$tableAlias}.store_id = ?", $collection->getStoreId())
689
+ );
690
+
691
+ foreach ($filter as $operator => $filterValue) {
692
+ if ($operator != 'attribute') {
693
+ if (is_array($filterValue)) {
694
+
695
+ $whereString = "(";
696
+ $i = 0;
697
+ foreach ($filterValue as $value) {
698
+ if ($i >= count($filterValue)-1) {
699
+ $whereString .= $connection->quoteInto("{$tableAlias}.value = ?", $value);
700
+ } else {
701
+ $whereString .= $connection->quoteInto("{$tableAlias}.value = ?", $value) . ' OR ';
702
+ }
703
+ $i++;
704
+ }
705
+ $whereString .= ")";
706
+
707
+ array_push($conditions, $whereString);
708
+
709
+ } else {
710
+ array_push($conditions, $connection->quoteInto("{$tableAlias}.value = ?", $filterValue));
711
+ }
712
+ }
713
+ }
714
+
715
+ $collection->getSelect()->join(
716
+ array($tableAlias => $resource->getTableName('catalog/product_index_eav')),
717
+ implode(' AND ', $conditions),
718
+ array()
719
+ );
720
+ }
721
+ }
722
+ }
723
+
724
+ return $collection;
725
+ }
726
+
727
  /**
728
  *
729
  * Gets attributes of a given product object.
947
 
948
  // SEE: Mage_Catalog_Block_Product_View_Attributes
949
  $controller = Mage::app()->getLayout();
950
+ $block = $controller->createBlock('catalog/product_view_attributes');
951
 
952
  // Same function as used by the layouting
953
+ foreach ($block->getAdditionalData() as $attributeData) {
954
+
955
+ if ($attributeData['value'] === Mage::helper('catalog')->__('No') || $attributeData['value'] === Mage::helper('catalog')->__('N/A')) {
956
+ continue;
957
+ }
958
+
959
+
960
+ $html .= "<p><strong>".$attributeData['label'].":</strong></br>".$attributeData['value']."</p>";
961
  }
962
 
963
  $additionalAttributeData['inline_value'] = $html;
969
  continue;
970
  }
971
 
972
+ if ($attribute === "tax_price") {
973
+ $additionalAttributeData = array();
974
+ $additionalAttributeData['title'] = "Price with tax";
975
+ $additionalAttributeData['code'] = "tax_price";
976
+ $additionalAttributeData['type'] = "number";
977
+ $additionalAttributeData['inline_value'] = Mage::helper('tax')->getPrice($resProduct, $resProduct->getFinalPrice(), true);
978
+ $response[] = $additionalAttributeData;
979
+
980
+ continue;
981
+ }
982
+
983
  $attributeObject = $resProduct->getResource()->getAttribute($attribute);
984
 
985
  if ($attributeObject !== false) {
1170
 
1171
  $output = array();
1172
 
1173
+ $resProduct = Mage::getModel('catalog/product')->load($productId);
1174
+ foreach ($resProduct->getMediaGalleryImages()->getItems() as $key => $value) {
1175
  $imageData = $value->getData();
1176
 
1177
+ $resProduct = false;
1178
  if ($this->_shouldExcludeImageFromMediaGallery($imageData["file"], $resProduct)) {
1179
  continue;
1180
  }
1223
  "special_from_date", "special_to_date", "special_price", "is_salable");
1224
  }
1225
 
1226
+ }
app/code/local/Highstreet/Hsapi/Model/Session.php ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Highstreet_Hsapi_Model_Session extends Mage_Core_Model_Session {
4
+
5
+ protected $_externalRoute = array("hsapi/checkoutV3/external", "hsapi/redirect/setCookie");
6
+
7
+ /**
8
+ * Configure and start session
9
+ *
10
+ * @param string $sessionName
11
+ * @return Mage_Core_Model_Session_Abstract_Varien
12
+ */
13
+ public function start($sessionName = null) {
14
+ if ($this->strstr_array($this->_externalRoute, $_SERVER['REQUEST_URI']) !== false) {
15
+ $_SESSION['frontend'] = array();
16
+ }
17
+ parent::start($sessionName);
18
+ return $this;
19
+ }
20
+
21
+ /**
22
+ * strstr function for searching arrays
23
+ *
24
+ * @param array || string $routes
25
+ * @param string $url
26
+ * @return strstr
27
+ */
28
+ public function strstr_array($routes, $url) {
29
+ if (!is_array($routes)) {
30
+ return strstr($url, $routes);
31
+ }
32
+ foreach ($routes as $route) {
33
+ if (strstr($url, $route)) {
34
+ return $route;
35
+ }
36
+ }
37
+ return false;
38
+ }
39
+
40
+ }
app/code/local/Highstreet/Hsapi/Model/System/Config/Stores.php ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Highstreet_Hsapi_Model_System_Config_Stores {
4
+ public function toOptionArray()
5
+ {
6
+ $stores = Mage::app()->getStores();
7
+ $options = array();
8
+ $options[] = array('value' => "-1",'label' => Mage::helper('highstreet_hsapi')->__('Current store'));
9
+ foreach ($stores as $store) {
10
+ $storeCode = $store->getCode();
11
+ $storeId = $store->getId();
12
+
13
+ $options[] = array('value' => $storeId,'label' => $storeCode);
14
+
15
+ }
16
+
17
+ return $options;
18
+ }
19
+ }
app/code/local/Highstreet/Hsapi/controllers/AccountController.php ADDED
@@ -0,0 +1,282 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Highstreet_HSAPI_module
5
+ *
6
+ * @package Highstreet_Hsapi
7
+ * @author Radovan Dodic (radovan.dodic@atessoft.rs) ~ AtesSoft
8
+ * @copyright Copyright (c) 2016 Highstreet
9
+ */
10
+ class Highstreet_Hsapi_AccountController extends Mage_Core_Controller_Front_Action {
11
+
12
+ /**
13
+ * @return Highstreet_Hsapi_Helper_Config_Account
14
+ */
15
+ private function _getHelper() {
16
+ return Mage::helper('highstreet_hsapi/config_account');
17
+ }
18
+
19
+ /**
20
+ * @return Highstreet_Hsapi_Helper_Config_Checkout
21
+ */
22
+ private function _getCheckoutHelper() {
23
+ return Mage::helper('highstreet_hsapi/config_checkout');
24
+ }
25
+
26
+ /**
27
+ * detects request method
28
+ * method: POST, GET, PATCH
29
+ * body: JSON
30
+ * Content-Type: application/json
31
+ */
32
+ public function indexAction() {
33
+ $method = $_SERVER['REQUEST_METHOD'];
34
+ if (!$this->_getHelper()->_checkIsRequestValid($this->getRequest())) {
35
+ return;
36
+ } else {
37
+ switch ($method) {
38
+ case 'POST':
39
+ $this->createUser();
40
+ break;
41
+ case 'GET':
42
+ $this->viewUser();
43
+ break;
44
+ case 'PATCH':
45
+ $this->updateUser();
46
+ break;
47
+ }
48
+ }
49
+ return;
50
+ }
51
+
52
+ /**
53
+ * Create Magento user
54
+ * method: POST
55
+ * body: JSON
56
+ * Content-Type: application/json
57
+ */
58
+ protected function createUser() {
59
+ $request = $this->getRequest();
60
+ $params = $this->_getHelper()->_JSONtoArray($request->getRawBody());
61
+ if (!$this->_getHelper()->_checkCreateAccountFields($params))
62
+ return;
63
+
64
+ // create Magento user
65
+ $websiteId = Mage::app()->getWebsite()->getId();
66
+ $store = Mage::app()->getStore();
67
+ $email = (isset($params['email'])) ? $params['email'] : $params['handle'];
68
+
69
+ $customer = Mage::getModel("customer/customer");
70
+ $customer->setWebsiteId($websiteId)
71
+ ->setStore($store)
72
+ ->setFirstname($params['first_name'])
73
+ ->setLastname($params['last_name'])
74
+ ->setEmail($email)
75
+ ->setPassword($params['password']);
76
+ try {
77
+ $customer->save();
78
+ $customer->setConfirmation(null);
79
+ $customer->save();
80
+ $this->_getHelper()->_sendEmailTemplate($customer);
81
+ } catch (Exception $e) {
82
+ $this->_getHelper()->logException($e, 'Customer save');
83
+ $this->_getHelper()->_JSONencodeAndRespond(array("title" => "Error", "content" => $e->getMessage()));
84
+ return;
85
+ }
86
+ $this->_getHelper()->_JSONencodeAndRespond(array(
87
+ "id" => $customer->getId(),
88
+ "email" => $customer->getEmail(),
89
+ 'handle' => $customer->getEmail(),
90
+ 'first_name' => $customer->getFirstname(),
91
+ 'last_name' => $customer->getLastname(),
92
+ ), "201 Created");
93
+ return;
94
+ }
95
+
96
+ /**
97
+ * checks Authorization from header
98
+ * not used function !!!
99
+ * @return bool/string
100
+ */
101
+ protected function _checkAuth() {
102
+ if ($this->getRequest()->getHeader('Authorization') == '') {
103
+ $this->_getHelper()->_JSONencodeAndRespond(array("title" => "Error", "content" => "No authorization code"), "401 Unauthorized");
104
+ return false;
105
+ }
106
+ return substr($this->getRequest()->getHeader('Authorization'), 7);
107
+ }
108
+
109
+ /**
110
+ * View Magento user data
111
+ * Validate user from session token and session files
112
+ * method: GET
113
+ * body: JSON
114
+ * Content-Type: application/json
115
+ * Authorization: frontend cookie, Magento authorization
116
+ */
117
+ protected function viewUser() {
118
+ if ($this->_getHelper()->isLoggedIn()) {
119
+ if ($customerId = $this->_getHelper()->_getCId()) {
120
+ $customer = Mage::getModel('customer/customer')->load($customerId);
121
+
122
+ $data = $this->_getHelper()->getCustomerData($customer);
123
+
124
+ // get Primary Billing Address
125
+ $primaryBillingAddress = $customer->getPrimaryBillingAddress();
126
+ if ($primaryBillingAddress) {
127
+ $address = $this->_getHelper()->getAddressData($primaryBillingAddress);
128
+ } else {
129
+ $addresses = $customer->getAddresses();
130
+ if (count($addresses)) {
131
+ foreach ($addresses as $adr) {
132
+ $address = $this->_getHelper()->getAddressData($adr);
133
+ // only first address
134
+ continue;
135
+ }
136
+ }
137
+ }
138
+ $data['address'] = (isset($address)) ? $address : array();
139
+ } else {
140
+ $this->_getHelper()->_JSONencodeAndRespond(array("title" => "Error", "content" => "Session expired"), "401 Unauthorized");
141
+ return;
142
+ }
143
+ } else {
144
+ $this->_getHelper()->_JSONencodeAndRespond(array("title" => "Error", "content" => "Session expired"), "401 Unauthorized");
145
+ return;
146
+ }
147
+ $this->_getHelper()->_JSONencodeAndRespond($data, "200 OK");
148
+ return;
149
+ }
150
+
151
+ /**
152
+ * Update Magento user data
153
+ * Validate user from session token and session files
154
+ * method: PATCH
155
+ * body: JSON
156
+ * Content-Type: application/json
157
+ * Authorization: Bearer valid_token
158
+ */
159
+ protected function updateUser() {
160
+ if ($this->_getHelper()->isLoggedIn()) {
161
+ if ($customerId = $this->_getHelper()->_getCId()) {
162
+ $customer = Mage::getModel('customer/customer')->load($customerId);
163
+ $params = $this->_getHelper()->_JSONtoArray($this->getRequest()->getRawBody());
164
+
165
+ if (!$this->_getHelper()->_checkUpdateAccountFields($params))
166
+ return;
167
+
168
+ $userChanged = false;
169
+ // update user FirstName if needed
170
+ if (isset($params['first_name'])) {
171
+ $customer->setFirstname($params['first_name']);
172
+ $userChanged = true;
173
+ }
174
+ // update user LasttName if needed
175
+ if (isset($params['last_name'])) {
176
+ $customer->setLastname($params['last_name']);
177
+ $userChanged = true;
178
+ }
179
+ // update user email if needed
180
+ if (isset($params['email'])) {
181
+ $customer->setEmail($params['email']);
182
+ $userChanged = true;
183
+ }
184
+
185
+ // update customer if any field is changed - less SQL resources in use
186
+ if ($userChanged) {
187
+ try {
188
+ $customer->save();
189
+ } catch (Exception $e) {
190
+ $this->_getHelper()->logException($e, 'Customer update');
191
+ $this->_getHelper()->_JSONencodeAndRespond(array("title" => "Error", "content" => $e->getMessage()));
192
+ return;
193
+ }
194
+ }
195
+
196
+ // update or add address if address is set in json
197
+ if (isset($params['address'])) {
198
+ if (isset($params['address']['id']) && is_numeric($params['address']['id'])) {
199
+ $address = Mage::getModel('customer/address')->load($params['address']['id']);
200
+ if ($address->getId()) {
201
+ $address->setId($params['address']['id']);
202
+ } else {
203
+ // if address ID is invalidor missing, we assume user is adding new default address
204
+ $address->setIsDefaultBilling('1')
205
+ ->setIsDefaultShipping('1');
206
+ }
207
+ } else {
208
+ // if address ID is invalidor missing, we assume user is adding new default address
209
+ $address = Mage::getModel("customer/address");
210
+ $address->setIsDefaultBilling('1')
211
+ ->setIsDefaultShipping('1');
212
+ }
213
+ // fix for diffrent street line settings
214
+ $params = $this->_getHelper()->fixStreetLines($params);
215
+
216
+ $address->setCustomerId($customerId)
217
+ ->setFirstname($params['address']['first_name'])
218
+ ->setLastname($params['address']['last_name'])
219
+ ->setCountryId($params['address']['country_id'])
220
+ ->setCity($params['address']['city'])
221
+ ->setTelephone($params['address']['telephone'])
222
+ ->setStreet(
223
+ array(
224
+ '0' => $params['address']['street'],
225
+ '1' => $params['address']['house_number'],
226
+ '2' => $params['address']['addition']
227
+ ))
228
+ ->setSaveInAddressBook('1');
229
+ if (isset($params['address']['postal_code']))
230
+ $address->setPostcode($params['address']['postal_code']);
231
+ if (isset($params['address']['state']))
232
+ $address->setRegion($params['address']['state']);
233
+ if (isset($params['address']['company']))
234
+ $address->setCompany($params['address']['company']);
235
+
236
+ try {
237
+ $address->save();
238
+ } catch (Exception $e) {
239
+ $this->_getHelper()->logException($e, 'Address add / update');
240
+ $this->_getHelper()->_JSONencodeAndRespond(array("title" => "Error", "content" => $e->getMessage()));
241
+ return;
242
+ }
243
+ } // endif address is set
244
+ } else {
245
+ $this->_getHelper()->_JSONencodeAndRespond(array("title" => "Error", "content" => "Session expired"), "401 Unauthorized");
246
+ return;
247
+ }
248
+ } else {
249
+ $this->_getHelper()->_JSONencodeAndRespond(array("title" => "Error", "content" => "Session expired"), "401 Unauthorized");
250
+ return;
251
+ }
252
+
253
+ $data = $this->_getHelper()->getCustomerData($customer);
254
+ if (isset($params['address'])) {
255
+ $addressArray = $this->_getHelper()->getAddressData($address);
256
+ $data['address'] = $addressArray;
257
+ }
258
+ $this->_getHelper()->_JSONencodeAndRespond($data, "200 OK");
259
+ return;
260
+ }
261
+
262
+ /**
263
+ * Logout action
264
+ */
265
+ public function logoutAction() {
266
+ $this->_getHelper()->logout();
267
+ return;
268
+ }
269
+
270
+ /**
271
+ * Logs the user in. Gets the parameters "email" and "password" from POST
272
+ *
273
+ * @author Tim Wachter
274
+ *
275
+ */
276
+ public function loginAction() {
277
+ $requestObject = Mage::app()->getRequest();
278
+ $this->_getHelper()->login($requestObject);
279
+ return;
280
+ }
281
+
282
+ }
app/code/local/Highstreet/Hsapi/controllers/CartController.php ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Highstreet_HSAPI_module
5
+ *
6
+ * @package Highstreet_Hsapi
7
+ * @author Radovan Dodic (radovan.dodic@atessoft.rs) ~ AtesSoft
8
+ * @copyright Copyright (c) 2016 Highstreet
9
+ */
10
+ class Highstreet_Hsapi_CartController extends Mage_Core_Controller_Front_Action {
11
+
12
+ /**
13
+ * @return Highstreet_Hsapi_Helper_Config_Cart
14
+ */
15
+ private function _getHelper() {
16
+ return Mage::helper('highstreet_hsapi/config_cart');
17
+ }
18
+
19
+ /**
20
+ * detects request method
21
+ * method: POST, GET, PATCH
22
+ * body: JSON
23
+ * Content-Type: application/json
24
+ */
25
+ public function indexAction() {
26
+ $method = $_SERVER['REQUEST_METHOD'];
27
+ $request = $this->getRequest();
28
+ $_errors = array();
29
+ if (!$this->_getHelper()->_checkIsRequestValid($request)) {
30
+ return;
31
+ } else {
32
+ // initialize cart before everything
33
+ if ($method == "POST")
34
+ $this->_getHelper()->cartInit();
35
+ $response = "200 OK";
36
+ if ($method == 'PUT') {
37
+ $etag = $request->getHeader('If-Match');
38
+ if ($etag && $etag != $this->_getHelper()->getCartEtag()) {
39
+ $response = "412 Precondition failed";
40
+ }
41
+ }
42
+
43
+ // check if there are some unavailable products, and remove them
44
+ if ($method == "GET")
45
+ $_errors = $this->_getHelper()->checkAndUpdateCartInventory($_errors);
46
+
47
+ $params = $this->_getHelper()->_JSONtoArray($request->getRawBody());
48
+
49
+ // add products and coupon codes if method is POST or PUT
50
+ if (count($params) && ($method == "POST" || $method == "PUT"))
51
+ $_errors = $this->_getHelper()->addProductsAndCouponsToQuote($params);
52
+
53
+ $this->_getHelper()->saveCartAndQuote();
54
+
55
+ // get data for JSON
56
+ $data = $this->_getData($_errors, $params, $method);
57
+
58
+ // assign quote item id to errors
59
+ $data = $this->_getHelper()->assignQuoteItemIdToErrors($data, $params);
60
+
61
+ // log data if logger is enabled
62
+ if ($this->_getHelper()->isLogEnabled()) {
63
+ $this->_getHelper()->log(array(
64
+ "Response code" => $response,
65
+ "Method" => $method,
66
+ "Data" => $data), 'Cart indexAction');
67
+ }
68
+
69
+ $this->_getHelper()->_JSONencodeAndRespond($data, $response);
70
+ }
71
+ return;
72
+ }
73
+
74
+ /**
75
+ * get $data array
76
+ *
77
+ * @param array $_errors
78
+ * @param array $params
79
+ * @param string $method
80
+ * @return array
81
+ */
82
+ protected function _getData($_errors = array(), $params = array(), $method) {
83
+ $request = $this->getRequest();
84
+ $cart = $this->_getHelper()->_getCart();
85
+ $data = array();
86
+
87
+ // add etag to body
88
+ $data['cart_etag'] = Mage::helper('highstreet_hsapi/config_cart')->getCartEtag();
89
+ // add quote id
90
+ $data['id'] = $this->_getHelper()->_getQuote()->getId();
91
+ // add errors
92
+ $data['_errors'] = $_errors;
93
+ // add messages
94
+ $data['_messages'] = array();
95
+ // add products
96
+ $data['items'] = $this->_getHelper()->getProductsFromCart($params);
97
+ // add coupon code
98
+ $data['coupon_codes'] = $this->_getHelper()->getCouponCodes();
99
+ // add tax included
100
+ $data['tax_included'] = $this->_getHelper()->isTaxIncluded();
101
+ // add totals
102
+ $data['totals'] = $this->_getHelper()->getTotals();
103
+
104
+ return $data;
105
+ }
106
+
107
+ }
app/code/local/Highstreet/Hsapi/controllers/CheckoutController.php CHANGED
@@ -151,9 +151,18 @@ class Highstreet_Hsapi_CheckoutController extends Mage_Core_Controller_Front_Act
151
  if (isset($data['use_for_shipping']) && $data['use_for_shipping'] == 1) {
152
  $result['goto_section'] = 'shipping_method';
153
 
 
 
 
 
 
 
 
 
 
154
  $result['update_section'] = array(
155
  'name' => 'shipping-method',
156
- 'data' => $this->_getShippingMethods()
157
  );
158
 
159
  $result['allow_sections'] = array('shipping');
@@ -180,11 +189,20 @@ class Highstreet_Hsapi_CheckoutController extends Mage_Core_Controller_Front_Act
180
 
181
  $result = $this->getOnepage()->saveShipping($data, $customerAddressId);
182
 
 
 
 
 
 
 
 
 
 
183
  if (!isset($result['error'])) {
184
  $result['goto_section'] = 'shipping_method';
185
  $result['update_section'] = array(
186
  'name' => 'shipping-method',
187
- 'data' => $this->_getShippingMethods()
188
  );
189
  }
190
  $this->_JSONencodeAndRespond($result);
@@ -207,13 +225,20 @@ class Highstreet_Hsapi_CheckoutController extends Mage_Core_Controller_Front_Act
207
  $this->getOnepage()->getQuote()->collectTotals();
208
  $this->getResponse()->setBody(Mage::helper('core')->jsonEncode($result));
209
 
210
- $paymentMethods = array();
211
 
 
 
 
 
 
 
 
 
212
 
213
- $result['goto_section'] = 'payment';
214
  $result['update_section'] = array(
215
  'name' => 'payment-method',
216
- 'data' => $this->_getPaymentMethods()
217
  );
218
  }
219
  $this->getOnepage()->getQuote()->collectTotals()->save();
@@ -401,7 +426,7 @@ class Highstreet_Hsapi_CheckoutController extends Mage_Core_Controller_Front_Act
401
  return Mage::getSingleton('checkout/type_onepage');
402
  }
403
 
404
- private function _getShippingMethods() {
405
  $shippingMethods = array();
406
 
407
  $shouldGetPostNL = true;
@@ -412,10 +437,18 @@ class Highstreet_Hsapi_CheckoutController extends Mage_Core_Controller_Front_Act
412
 
413
  $cif = Mage::getModel('postnl_deliveryoptions/cif');
414
  if ($cif) {
 
 
 
 
 
 
 
 
415
  $postNlOptions = $cif->setStoreId(Mage::app()->getStore()->getId())
416
  ->getDeliveryTimeframes(array(
417
  'postcode' => str_replace(" ", "", $shippingAddressData["postcode"]), // Postcode
418
- 'housenumber' => ereg_replace("[^0-9]", "", $shippingAddressData["street"]), // Extract housenumber from street field
419
  'deliveryDate' => date('d-m-Y', strtotime('+ 1 day')), // Set delivery day to tomorrow
420
  ));
421
  } else {
@@ -447,6 +480,7 @@ class Highstreet_Hsapi_CheckoutController extends Mage_Core_Controller_Front_Act
447
  $shippingMethod['price'] = $_rate->getData('price');
448
  $shippingMethod['code'] = $_rate->getCode();
449
  $shippingMethod['checked'] = $checked;
 
450
 
451
  if (strstr($_rate->getCode(), "postnl") !== false) {
452
  $shippingMethod['sub_options'] = $postNlOptions;
@@ -460,7 +494,7 @@ class Highstreet_Hsapi_CheckoutController extends Mage_Core_Controller_Front_Act
460
  return $shippingMethods;
461
  }
462
 
463
- private function _getPaymentMethods() {
464
  $paymentMethods = array();
465
 
466
  $model = new Mage_Checkout_Block_Onepage_Payment_Methods();
@@ -491,6 +525,7 @@ class Highstreet_Hsapi_CheckoutController extends Mage_Core_Controller_Front_Act
491
  $object["checked"] = $checked;
492
 
493
  $object["sub_options"] = $this->_getSuboptionsForPaymentMethod($method);
 
494
 
495
  $paymentMethods[] = $object;
496
  }
@@ -624,11 +659,11 @@ class Highstreet_Hsapi_CheckoutController extends Mage_Core_Controller_Front_Act
624
  protected function _JSONencodeAndRespond($data, $numericCheck = true) {
625
  //set response body
626
  $this->_setHeader();
627
- if ($numeric_check === FALSE || version_compare(PHP_VERSION, '5.3.3', '<')) {
628
  $this->getResponse()->setBody(json_encode($data));
629
  } else {
630
  $this->getResponse()->setBody(json_encode($data, JSON_NUMERIC_CHECK));
631
  }
632
 
633
  }
634
- }
151
  if (isset($data['use_for_shipping']) && $data['use_for_shipping'] == 1) {
152
  $result['goto_section'] = 'shipping_method';
153
 
154
+ $shippingMethods = $this->_getShippingMethods();
155
+
156
+ Mage::dispatchEvent(
157
+ 'highstreet_hsapi_checkout_shipping_methods_after',
158
+ array(
159
+ 'shipping_methods' => &$shippingMethods,
160
+ 'quote' => $this->getOnepage()->getQuote(),
161
+ 'request' => $this->getRequest()));
162
+
163
  $result['update_section'] = array(
164
  'name' => 'shipping-method',
165
+ 'data' => $shippingMethods
166
  );
167
 
168
  $result['allow_sections'] = array('shipping');
189
 
190
  $result = $this->getOnepage()->saveShipping($data, $customerAddressId);
191
 
192
+ $shippingMethods = $this->_getShippingMethods();
193
+
194
+ Mage::dispatchEvent(
195
+ 'highstreet_hsapi_checkout_shipping_methods_after',
196
+ array(
197
+ 'shipping_methods' => &$shippingMethods,
198
+ 'quote' => $this->getOnepage()->getQuote(),
199
+ 'request' => $this->getRequest()));
200
+
201
  if (!isset($result['error'])) {
202
  $result['goto_section'] = 'shipping_method';
203
  $result['update_section'] = array(
204
  'name' => 'shipping-method',
205
+ 'data' => $shippingMethods
206
  );
207
  }
208
  $this->_JSONencodeAndRespond($result);
225
  $this->getOnepage()->getQuote()->collectTotals();
226
  $this->getResponse()->setBody(Mage::helper('core')->jsonEncode($result));
227
 
228
+ $result['goto_section'] = 'payment';
229
 
230
+ $paymentMethods = $this->_getPaymentMethods();
231
+
232
+ Mage::dispatchEvent(
233
+ 'highstreet_hsapi_checkout_payment_methods_after',
234
+ array(
235
+ 'payment_methods' => &$paymentMethods,
236
+ 'quote' => $this->getOnepage()->getQuote(),
237
+ 'request' => $this->getRequest()));
238
 
 
239
  $result['update_section'] = array(
240
  'name' => 'payment-method',
241
+ 'data' => $paymentMethods
242
  );
243
  }
244
  $this->getOnepage()->getQuote()->collectTotals()->save();
426
  return Mage::getSingleton('checkout/type_onepage');
427
  }
428
 
429
+ protected function _getShippingMethods() {
430
  $shippingMethods = array();
431
 
432
  $shouldGetPostNL = true;
437
 
438
  $cif = Mage::getModel('postnl_deliveryoptions/cif');
439
  if ($cif) {
440
+
441
+ preg_match('([0-9]+)', $shippingAddressData["street"], $matches);
442
+ $housenumber = 1; //default to 1.
443
+ if(isset($matches) && count($matches) > 0) {
444
+ $housenumber = $matches[0];
445
+ }
446
+
447
+
448
  $postNlOptions = $cif->setStoreId(Mage::app()->getStore()->getId())
449
  ->getDeliveryTimeframes(array(
450
  'postcode' => str_replace(" ", "", $shippingAddressData["postcode"]), // Postcode
451
+ 'housenumber' => $housenumber,
452
  'deliveryDate' => date('d-m-Y', strtotime('+ 1 day')), // Set delivery day to tomorrow
453
  ));
454
  } else {
480
  $shippingMethod['price'] = $_rate->getData('price');
481
  $shippingMethod['code'] = $_rate->getCode();
482
  $shippingMethod['checked'] = $checked;
483
+ $shippingMethod['custom_fields'] = array();
484
 
485
  if (strstr($_rate->getCode(), "postnl") !== false) {
486
  $shippingMethod['sub_options'] = $postNlOptions;
494
  return $shippingMethods;
495
  }
496
 
497
+ protected function _getPaymentMethods() {
498
  $paymentMethods = array();
499
 
500
  $model = new Mage_Checkout_Block_Onepage_Payment_Methods();
525
  $object["checked"] = $checked;
526
 
527
  $object["sub_options"] = $this->_getSuboptionsForPaymentMethod($method);
528
+ $object["custom_fields"] = array();
529
 
530
  $paymentMethods[] = $object;
531
  }
659
  protected function _JSONencodeAndRespond($data, $numericCheck = true) {
660
  //set response body
661
  $this->_setHeader();
662
+ if ($numericCheck === FALSE || version_compare(PHP_VERSION, '5.3.3', '<')) {
663
  $this->getResponse()->setBody(json_encode($data));
664
  } else {
665
  $this->getResponse()->setBody(json_encode($data, JSON_NUMERIC_CHECK));
666
  }
667
 
668
  }
669
+ }
app/code/local/Highstreet/Hsapi/controllers/CheckoutV3Controller.php ADDED
@@ -0,0 +1,536 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Highstreet_HSAPI_module
5
+ *
6
+ * @package Highstreet_Hsapi
7
+ * @author Radovan Dodic (radovan.dodic@atessoft.rs) ~ AtesSoft
8
+ * @copyright Copyright (c) 2016 Highstreet
9
+ */
10
+ class Highstreet_Hsapi_CheckoutV3Controller extends Mage_Core_Controller_Front_Action {
11
+
12
+ /**
13
+ * detects request method
14
+ * method: POST, GET, PATCH
15
+ * body: JSON
16
+ * Content-Type: application/json
17
+ */
18
+ public function indexAction() {
19
+ $method = $_SERVER['REQUEST_METHOD'];
20
+ if (!$this->_getHelper()->_checkIsRequestValid($this->getRequest())) {
21
+ return;
22
+ } else {
23
+ switch ($method) {
24
+ case 'POST':
25
+ $this->createCheckoutSession();
26
+ break;
27
+ case 'GET':
28
+ $this->viewCheckoutSession();
29
+ break;
30
+ case 'PATCH':
31
+ $this->updateCheckoutSession();
32
+ break;
33
+ }
34
+ }
35
+ return;
36
+ }
37
+
38
+ /**
39
+ * returns shipping methods
40
+ * method: GET
41
+ * body: JSON
42
+ * Content-Type: application/json
43
+ */
44
+ public function shipping_methodsAction() {
45
+ $method = $_SERVER['REQUEST_METHOD'];
46
+ if (!$this->_getHelper()->_checkIsRequestValid($this->getRequest())) {
47
+ return;
48
+ }
49
+ $cart = Mage::getSingleton('checkout/cart');
50
+ $address = $cart->getQuote()->getShippingAddress();
51
+ if ($address->getQuoteId() && $address->getFirstname()) {
52
+ $jsonMethods = $this->_getHelper()->getShippingMethods($address, $cart->getQuote());
53
+ } else {
54
+ $jsonMethods = $this->_getHelper()->getAllShippingMethods();
55
+ }
56
+
57
+ Mage::dispatchEvent(
58
+ 'highstreet_hsapi_checkoutV3_shipping_methods_after', array(
59
+ 'shipping_methods' => &$jsonMethods,
60
+ 'quote' => $this->_getOnepage()->getQuote(),
61
+ 'request' => $this->getRequest()
62
+ )
63
+ );
64
+ $this->_getHelper()->_JSONencodeAndRespond($jsonMethods, "200 OK");
65
+ return;
66
+ }
67
+
68
+ /**
69
+ * returns payment methods
70
+ * method: GET
71
+ * body: JSON
72
+ * Content-Type: application/json
73
+ */
74
+ public function payment_methodsAction() {
75
+ $method = $_SERVER['REQUEST_METHOD'];
76
+ if (!$this->_getHelper()->_checkIsRequestValid($this->getRequest())) {
77
+ return;
78
+ }
79
+ $paymentMethods = $this->_getHelper()->getAllPaymentMethods();
80
+
81
+ Mage::dispatchEvent(
82
+ 'highstreet_hsapi_checkoutV3_payment_methods_after', array(
83
+ 'payment_methods' => &$paymentMethods,
84
+ 'quote' => $this->_getOnepage()->getQuote(),
85
+ 'request' => $this->getRequest()
86
+ )
87
+ );
88
+
89
+ $this->_getHelper()->_JSONencodeAndRespond($paymentMethods, "200 OK");
90
+ return;
91
+ }
92
+
93
+ /**
94
+ * returns shipping countries
95
+ * method: GET
96
+ * body: JSON
97
+ * Content-Type: application/json
98
+ */
99
+ public function shipping_countriesAction() {
100
+ $method = $_SERVER['REQUEST_METHOD'];
101
+ if (!$this->_getHelper()->_checkIsRequestValid($this->getRequest())) {
102
+ return;
103
+ }
104
+
105
+ $this->_getHelper()->getShippingCountries();
106
+ return;
107
+ }
108
+
109
+ /**
110
+ * @return Highstreet_Hsapi_Helper_Config_Checkout
111
+ */
112
+ private function _getHelper() {
113
+ return Mage::helper('highstreet_hsapi/config_checkout');
114
+ }
115
+
116
+ /**
117
+ * @return Highstreet_Hsapi_Helper_Config_Redirect
118
+ */
119
+ private function _getRedirectHelper() {
120
+ return Mage::helper('highstreet_hsapi/config_redirect');
121
+ }
122
+
123
+ /**
124
+ * @return Highstreet_Hsapi_Helper_Config_Cart
125
+ */
126
+ private function _getCartHelper() {
127
+ return Mage::helper('highstreet_hsapi/config_cart');
128
+ }
129
+
130
+ /**
131
+ * @return Mage_Checkout_Model_Type_Onepage
132
+ */
133
+ protected function _getOnepage() {
134
+ return Mage::getSingleton('checkout/type_onepage');
135
+ }
136
+
137
+ /**
138
+ * @return Mage_Customer_Model_Session
139
+ */
140
+ protected function _getSession() {
141
+ return Mage::getSingleton('customer/session');
142
+ }
143
+
144
+ /**
145
+ * @return Mage_Checkout_Model_Session
146
+ */
147
+ protected function _getCheckoutSession() {
148
+ return Mage::getSingleton('checkout/session');
149
+ }
150
+
151
+ /**
152
+ * Update checkout session
153
+ * method: PATCH
154
+ * Content-Type: application/json
155
+ */
156
+ protected function updateCheckoutSession() {
157
+ $request = $this->getRequest();
158
+ if (!$this->_getHelper()->_checkIsRequestValid($request, array('PATCH'))) {
159
+ return;
160
+ }
161
+ $quote = $this->_getOnepage()->getQuote();
162
+ $params = $this->_getHelper()->_JSONtoArray($request->getRawBody());
163
+ $invalidFields = array();
164
+ $missingFields = array();
165
+
166
+ //email is set only if customer is not logged in... Logged customers already have email preset.
167
+ if (isset($params['email']) && !$this->_getHelper()->isLoggedIn()) {
168
+ try {
169
+ $email = $params['email'];
170
+ if ($this->_getHelper()->checkIsEmailValid($email)) {
171
+ $quote->setCustomerEmail($params['email']);
172
+ $quote->save();
173
+ } else {
174
+ $invalidFields[] = 'email';
175
+ }
176
+ } catch (Exception $e) {
177
+ $this->_getHelper()->logException($e, 'Unable to set Email address');
178
+ $error = $this->__('Unable to set Email address');
179
+ }
180
+ if (isset($error)) {
181
+ $this->_getHelper()->_JSONencodeAndRespond(array("title" => "Error", "content" => $error), "200 OK");
182
+ return;
183
+ }
184
+ }
185
+
186
+ // set billing address
187
+ if (isset($params['addresses']['billing_address']) && $params['addresses']['billing_address'] != null && $params['addresses']['billing_address'] != 'null') {
188
+ if (!$quote->getCustomerEmail()) {
189
+ $error = $this->__('Please fill in email address');
190
+ $this->_getHelper()->_JSONencodeAndRespond(array("title" => "Error", "content" => $error), "200 OK");
191
+ return;
192
+ }
193
+ try {
194
+ $billingAddress = $params['addresses']['billing_address'];
195
+ $billingAddress['use_same_for_shipping'] = (isset($params['addresses']['shipping_address']) && $params['addresses']['shipping_address'] != null && $params['addresses']['shipping_address'] != 'null') ? 0 : 1;
196
+ if ($quote->getCustomerEmail())
197
+ $billingAddress['email'] = $quote->getCustomerEmail();
198
+ if ($this->_getHelper()->checkRequiredAddressFields($billingAddress, true)) {
199
+ $this->_getHelper()->setBillingAddressToQuote($billingAddress);
200
+ // save with billing info
201
+ $quote->save();
202
+ // save again after calculating new totals
203
+ $quote->collectTotals();
204
+ if ($billingAddress['use_same_for_shipping'])
205
+ $quote->getShippingAddress()->setCollectShippingRates(true);
206
+ $quote->save();
207
+ } else {
208
+ $invalidFields[] = "billing_address";
209
+ if ($params['addresses']['shipping_address'] == null || $params['addresses']['shipping_address'] == 'null')
210
+ $invalidFields[] = "shipping_address";
211
+ }
212
+ } catch (Exception $e) {
213
+ $this->_getHelper()->logException($e, 'Unable to set Billing address');
214
+ $error = $this->__('Unable to set Billing address');
215
+ }
216
+ if (isset($error)) {
217
+ $this->_getHelper()->_JSONencodeAndRespond(array("title" => "Error", "content" => $error), "200 OK");
218
+ return;
219
+ }
220
+ }
221
+ // set shipping address
222
+ if (isset($params['addresses']['shipping_address']) && $params['addresses']['shipping_address'] != null && $params['addresses']['shipping_address'] != 'null') {
223
+ if (!$quote->getCustomerEmail()) {
224
+ $error = $this->__('Please fill in email address');
225
+ $this->_getHelper()->_JSONencodeAndRespond(array("title" => "Error", "content" => $error), "200 OK");
226
+ return;
227
+ }
228
+ try {
229
+ $shipping_address = $params['addresses']['shipping_address'];
230
+ if ($quote->getCustomerEmail())
231
+ $shipping_address['email'] = $quote->getCustomerEmail();
232
+ if ($this->_getHelper()->checkRequiredAddressFields($shipping_address, true)) {
233
+ $this->_getHelper()->setShippingAddressToQuote($shipping_address);
234
+ // save
235
+ $quote->save();
236
+ // save again after calculating new totals
237
+ $quote->collectTotals();
238
+ $quote->getShippingAddress()->setCollectShippingRates(true)->save();
239
+ } else {
240
+ $invalidFields[] = "shipping_address";
241
+ }
242
+ } catch (Exception $e) {
243
+ $this->_getHelper()->logException($e, 'Unable to set Billing address');
244
+ $error = $this->__('Unable to set Shipping address');
245
+ }
246
+ if (isset($error)) {
247
+ $this->_getHelper()->_JSONencodeAndRespond(array("title" => "Error", "content" => $error), "200 OK");
248
+ return;
249
+ }
250
+ }
251
+
252
+
253
+ // set shipping method - depends on shipping address
254
+ if (isset($params['shipping_method']) && $params['shipping_method'] != 'null' && $params['shipping_method'] != null) {
255
+ try {
256
+ $this->_getSession()->setPostNLData(null);
257
+ $shippingMethod = $params['shipping_method'];
258
+ if ($this->_getHelper()->checkIsShippingMethodValid($shippingMethod['code'])) {
259
+ if ($quote->getShippingAddress()->getSameAsBilling() || $quote->getShippingAddress()->getFirstname()) {
260
+ // check if shipping is postNl and it has suboption set
261
+ if (strstr($shippingMethod['code'], "postnl") && isset($shippingMethod['options']['code'])) {
262
+ $postNLdata = json_decode($this->_getHelper()->b64d($shippingMethod['options']['code']), true);
263
+ $this->_getSession()->setPostNLData($shippingMethod['options']['code']);
264
+ // code taken from PostNl controller DeliveryOptionsController
265
+ /** @var TIG_PostNL_Model_DeliveryOptions_Service $service */
266
+ $service = Mage::getModel('postnl_deliveryoptions/service');
267
+ $service->saveDeliveryOption($postNLdata);
268
+ }
269
+ $quote->getShippingAddress()->setCollectShippingRates(true)->collectShippingRates();
270
+ $result = $this->_getOnepage()->saveShippingMethod($shippingMethod['code']);
271
+ if ($result) {
272
+ $error = $this->__('Unable to set shipping method');
273
+ }
274
+ $quote->collectTotals()->save();
275
+ } else {
276
+ $error = $this->__('Unable to set shipping method without shipping address');
277
+ }
278
+ } else {
279
+ $invalidFields[] = 'shipping_method';
280
+ }
281
+ } catch (Exception $e) {
282
+ $this->_getHelper()->logException($e, 'Unable to set shipping method');
283
+ $error = $this->__('Unable to set shipping method');
284
+ }
285
+ if (isset($error)) {
286
+ $this->_getHelper()->_JSONencodeAndRespond(array("title" => "Error", "content" => $error), "200 OK");
287
+ return;
288
+ }
289
+ }
290
+
291
+ // set coupon codes
292
+ if (isset($params['coupon_codes']) && $params['coupon_codes'] != 'null' && $params['coupon_codes'] != null) {
293
+ try {
294
+ if (isset($params['coupon_codes'][0]['code']) && strlen($params['coupon_codes'][0]['code']) <= $this->_getCartHelper()->getCouponMaxLenght()) {
295
+ $couponCode = $params['coupon_codes'][0]['code'];
296
+ $oCoupon = Mage::getModel('salesrule/coupon')->load($couponCode, 'code');
297
+ if ($oCoupon->getRuleId()) {
298
+ $quote = Mage::getSingleton('checkout/cart')->getQuote();
299
+ $quote->getShippingAddress()->setCollectShippingRates(true);
300
+ $quote->setCouponCode($couponCode)
301
+ ->collectTotals()
302
+ ->save();
303
+ }
304
+ if ($couponCode != $quote->getCouponCode()) {
305
+ $invalidFields[] = 'coupon_codes';
306
+ } else {
307
+ $this->_getCartHelper()->updateCartEtag();
308
+ }
309
+ } else {
310
+ $invalidFields[] = 'coupon_codes';
311
+ }
312
+ } catch (Exception $e) {
313
+ $this->_getHelper()->logException($e, 'Unable to set coupon code');
314
+ $error = $this->__('Unable to set coupon code');
315
+ }
316
+ if (isset($error)) {
317
+ $this->_getHelper()->_JSONencodeAndRespond(array("title" => "Error", "content" => $error), "200 OK");
318
+ return;
319
+ }
320
+ } elseif (isset($params['coupon_codes']) && $params['coupon_codes'] == null) {
321
+ // remove coupon codes
322
+ $quote = Mage::getSingleton('checkout/cart')->getQuote();
323
+ $quote->getShippingAddress()->setCollectShippingRates(true);
324
+ $quote->setCouponCode(null)
325
+ ->collectTotals()
326
+ ->save();
327
+ }
328
+
329
+ // set payment method
330
+ if (isset($params['payment_method']) && isset($params['payment_method']['code'])) {
331
+ try {
332
+ $payment = array('method' => $params['payment_method']['code']);
333
+ if ($this->_getHelper()->checkIsPaymentMethodValid($payment['method'])) {
334
+ // add additionl payment options, example: Adyen_Ideal bank id
335
+ $payment = $this->_getHelper()->addAdditionalPaymentOptions($payment, $params);
336
+ $result = $this->_getOnepage()->savePayment($payment);
337
+ if (isset($result['error'])) {
338
+ Mage::log($result);
339
+ $invalidFields[] = 'payment_method';
340
+ } else {
341
+ // set options to session like Buckaroo BPE Issuer
342
+ if (isset($params['payment_method']['options']['code'])) {
343
+ $this->_getCheckoutSession()->setData('additionalFields', array('Issuer' => $params['payment_method']['options']['code']));
344
+ }
345
+ }
346
+ } else {
347
+ $invalidFields[] = 'payment_method';
348
+ }
349
+ } catch (Exception $e) {
350
+ $this->_getHelper()->logException($e, 'Unable to set Payment Method');
351
+ $error = $this->__('Unable to set Payment Method.');
352
+ }
353
+ if (isset($error)) {
354
+ $this->_getHelper()->_JSONencodeAndRespond(array("title" => "Error", "content" => $error), "200 OK");
355
+ return;
356
+ }
357
+ }
358
+
359
+
360
+ $this->viewCheckoutSession('200 OK', $invalidFields, $missingFields);
361
+ return;
362
+ }
363
+
364
+ /**
365
+ * View checkout session
366
+ * method: GET
367
+ * Content-Type: application/json
368
+ * @param string $response
369
+ * @param array $invalidFields
370
+ * @param array $missingFields
371
+ * @return json
372
+ */
373
+ protected function viewCheckoutSession($response = "200 OK", $invalidFields = array(), $missingFields = array()) {
374
+ // call function saveCartAndQuote() just in case so collectTotals is calculated
375
+ $this->_getCartHelper()->saveCartAndQuote();
376
+ $request = $this->getRequest();
377
+ $quote = $this->_getOnepage()->getQuote();
378
+ $billAddress = $quote->getBillingAddress();
379
+ $shipAddress = $quote->getShippingAddress();
380
+ //populate fields
381
+ $data = array(
382
+ "id" => $quote->getId(),
383
+ "cart_etag" => Mage::helper('highstreet_hsapi/config_cart')->getCartEtag(),
384
+ "email" => $quote->getCustomerEmail(),
385
+ "payment_method" => ($quote->getPayment()->getMethod()) ? $this->_getHelper()->getPaymentMethod($quote->getPayment()->getMethod()) : null,
386
+ "shipping_method" => ($shipAddress->getQuoteId() && $shipAddress->getShippingMethod()) ? $this->_getHelper()->getShippingMethod($shipAddress->getShippingMethod()) : null,
387
+ "coupon_codes" => ($quote->getCouponCode()) ? array(array('code' => $quote->getCouponCode())) : array(),
388
+ "addresses" => array(
389
+ "billing_address" => ($billAddress->getQuoteId() && $billAddress->getFirstname()) ? $this->_getHelper()->getAddressData($billAddress) : null,
390
+ "shipping_address" => ($shipAddress->getQuoteId() && !$shipAddress->getSameAsBilling() && $shipAddress->getFirstname()) ? $this->_getHelper()->getAddressData($shipAddress) : null,
391
+ ),
392
+ "tax_included" => ($shipAddress && $shipAddress->getTaxAmount()) ? true : false,
393
+ "totals" => $this->_getCartHelper()->getTotals(),
394
+ );
395
+
396
+ // shipping and billing address missing errors
397
+ if ($data['addresses']['billing_address'] == null)
398
+ $missingFields[] = "billing_address";
399
+ if (($data['addresses']['billing_address'] == null && $data['addresses']['shipping_address'] == null) || ($data['addresses']['shipping_address'] == null && !$shipAddress->getSameAsBilling()) || ($data['addresses']['shipping_address'] == null && in_array('shipping_address', $invalidFields)))
400
+ $missingFields[] = "shipping_address";
401
+ // check missing fields
402
+ $data = $this->_getHelper()->checkMissingInvalidFields($data, $invalidFields, $missingFields);
403
+ // log data if logging is enabled
404
+ if ($this->_getHelper()->isLogEnabled()) {
405
+ $this->_getHelper()->log(array(
406
+ "Response code" => $response,
407
+ "Data array for JSON" => $data), 'View checkout session');
408
+ }
409
+ $this->_getHelper()->_JSONencodeAndRespond($data, $response);
410
+ return;
411
+ }
412
+
413
+ /**
414
+ * Create checkout session
415
+ * method: POST
416
+ * Content-Type: application/json
417
+ */
418
+ protected function createCheckoutSession() {
419
+ //Can be used to determine elsewhere in the code whether this is a checkout session initiated in the app.
420
+ Mage::getSingleton('checkout/session')->setIsHighstreetCheckoutSession(true);
421
+
422
+
423
+ $request = $this->getRequest();
424
+ if (!$this->_getHelper()->_checkIsRequestValid($request, array('POST'))) {
425
+ return;
426
+ }
427
+ $quote = $this->_getOnepage()->getQuote();
428
+ $params = $this->_getHelper()->_JSONtoArray($request->getRawBody());
429
+ if (!isset($params['cart_id']) || $params['cart_id'] != $quote->getId()) {
430
+ $this->_getHelper()->_JSONencodeAndRespond(array("title" => "Error", "content" => "cart_id is not set or do not match"));
431
+ return;
432
+ }
433
+ if (!isset($params['cart_etag']) || $params['cart_etag'] != Mage::helper('highstreet_hsapi/config_cart')->getCartEtag()) {
434
+ $this->_getHelper()->_JSONencodeAndRespond(array("title" => "Error", "content" => "cart_etag is not set or do not match"), "409"); // or 412 ???
435
+ return;
436
+ }
437
+
438
+ $customer = $this->_getSession()->getCustomer();
439
+ $isLoggedIn = $this->_getSession()->isLoggedIn();
440
+
441
+ if ($isLoggedIn) {
442
+ $defaultBillingAddressId = $customer->getDefaultBilling();
443
+ $defaultShippingAddressId = $customer->getDefaultShipping();
444
+ if ($defaultBillingAddressId > 0) {
445
+ $billingAddress = $customer->getAddressById($defaultBillingAddressId);
446
+ // if email is not pre set, do it now
447
+ if (!$this->_getOnepage()->getQuote()->getCustomerEmail() && $customer->getEmail())
448
+ $this->_getOnepage()->getQuote()->setCustomerEmail($customer->getEmail());
449
+ $this->_getOnepage()->getQuote()->getBillingAddress()->importCustomerAddress($billingAddress);
450
+ $this->_getOnepage()->getQuote()->getShippingAddress()->importCustomerAddress($billingAddress);
451
+ $this->_getOnepage()->getQuote()->getShippingAddress()->setSameAsBilling(1);
452
+ $this->_getOnepage()->getQuote()->getShippingAddress()->setCollectShippingRates(true);
453
+ if ($defaultBillingAddressId != $defaultShippingAddressId) {
454
+ $shippingAddress = $customer->getAddressById($defaultShippingAddressId);
455
+ $this->_getOnepage()->getQuote()->getShippingAddress()->importCustomerAddress($shippingAddress);
456
+ $this->_getOnepage()->getQuote()->getShippingAddress()->setSameAsBilling(0);
457
+ $this->_getOnepage()->getQuote()->getShippingAddress()->setCollectShippingRates(true);
458
+ }
459
+ }
460
+ $quote->setCheckoutMethod('customer')->save();
461
+ } else {
462
+ $quote->setCheckoutMethod('guest')->save();
463
+ }
464
+ $this->viewCheckoutSession();
465
+ return;
466
+ }
467
+
468
+ /**
469
+ * Finalize checkout and create order
470
+ * method: POST
471
+ * body: tid
472
+ * Content-Type: application/json
473
+ */
474
+ public function finalizeAction() {
475
+ $request = $this->getRequest();
476
+ if (!$this->_getHelper()->_checkIsRequestValid($request, array('POST'))) {
477
+ return;
478
+ }
479
+ $params = $this->_getHelper()->_JSONtoArray($request->getRawBody());
480
+ if (!isset($params['tid'])) {
481
+ $this->_getHelper()->_fieldError(Highstreet_Hsapi_Helper_Config_Account::MISSING, 'tid');
482
+ return;
483
+ }
484
+ Mage::getSingleton('checkout/session')->setHsTid($params['tid']);
485
+ $result = array();
486
+ $quote = $this->_getOnepage()->getQuote();
487
+ try {
488
+ $quote->collectTotals()->save();
489
+ $this->_getOnepage()->saveOrder();
490
+ $redirectUrl = $this->_getOnepage()->getCheckout()->getRedirectUrl();
491
+ } catch (Mage_Payment_Model_Info_Exception $e) {
492
+ $this->_getHelper()->logException($e, 'Finalize Checkout - Mage_Payment_Model_Info_Exception');
493
+ // if payment method is not valid return ViewCheckout session with all missing fields
494
+ $this->viewCheckoutSession('400 Bad Request');
495
+ return;
496
+ } catch (Mage_Core_Exception $e) {
497
+ $this->_getHelper()->logException($e, 'Finalize Checkout - Mage_Core_Exception');
498
+ Mage::helper('checkout')->sendPaymentFailedEmail($this->_getOnepage()->getQuote(), $e->getMessage());
499
+ // if payment method via Core functions is not valid return ViewCheckout session with all missing fields
500
+ $this->viewCheckoutSession('400 Bad Request');
501
+ return;
502
+ } catch (Exception $e) {
503
+ // if anything goes wrong, return proccessing error, log exception
504
+ $this->_getHelper()->logException($e, 'Finalize Checkout');
505
+ Mage::helper('checkout')->sendPaymentFailedEmail($this->_getOnepage()->getQuote(), $e->getMessage());
506
+ $this->_getHelper()->_JSONencodeAndRespond($this->__('There was an error processing your order. Please contact us or try again later.'), "400 Bad Request");
507
+ return;
508
+ }
509
+ $this->_getOnepage()->getQuote()->save();
510
+ $result['hpp'] = null;
511
+ if (isset($redirectUrl)) {
512
+ $result['hpp'] = $redirectUrl;
513
+ }
514
+
515
+ $this->_getHelper()->_JSONencodeAndRespond($result, "200 Ok");
516
+ }
517
+
518
+ /**
519
+ * External checkout
520
+ * method: POST
521
+ * POST_param: tid
522
+ * POST_param: session
523
+ * Content-Type: application/json
524
+ */
525
+ public function externalAction() {
526
+ $requestObject = Mage::app()->getRequest();
527
+
528
+ // get decoded path from reuest and set cookies
529
+ $decodedPath = $this->_getRedirectHelper()->getPathAndSetCookies($requestObject);
530
+
531
+ // redirect to web checkout
532
+ if ($decodedPath)
533
+ Mage::app()->getResponse()->setRedirect($redirectUrl)->sendResponse();
534
+ }
535
+
536
+ }
app/code/local/Highstreet/Hsapi/controllers/IndexController.php CHANGED
@@ -104,6 +104,7 @@ class Highstreet_Hsapi_IndexController extends Mage_Core_Controller_Front_Action
104
  "magento_version" => (string)Mage::getVersion(),
105
  "environment" => $configApi->environment(),
106
  "storefront" => Mage::app()->getStore()->getCode(),
 
107
  "attributes_sort_order_setting_raw" => $configApi->attributesSortOrderRaw()));
108
  }
109
 
@@ -301,9 +302,12 @@ class Highstreet_Hsapi_IndexController extends Mage_Core_Controller_Front_Action
301
  $requestObject->getParam('include_configuration_details'),
302
  $requestObject->getParam('include_media_gallery'));
303
 
304
- if ($products == null) {
305
  $this->_respondWith404();
306
  return false;
 
 
 
307
  }
308
 
309
  $this->_JSONencodeAndRespond($products);
@@ -321,6 +325,11 @@ class Highstreet_Hsapi_IndexController extends Mage_Core_Controller_Front_Action
321
  $productObjects = array();
322
  if ($requestObject->getParam('ids') != "") {
323
  $idsArray = explode(',', $requestObject->getParam('ids'));
 
 
 
 
 
324
 
325
  foreach ($idsArray as $value) {
326
  $productObject = Mage::getModel('catalog/product')->load($value);
@@ -369,8 +378,8 @@ class Highstreet_Hsapi_IndexController extends Mage_Core_Controller_Front_Action
369
  $requestObject->getParam('include_configuration_details'),
370
  $requestObject->getParam('include_media_gallery'));
371
 
372
- if ($response == null) {
373
- $this->_respondWith404();
374
  return false;
375
  }
376
 
@@ -468,6 +477,13 @@ class Highstreet_Hsapi_IndexController extends Mage_Core_Controller_Front_Action
468
  return;
469
  }
470
 
 
 
 
 
 
 
 
471
  protected function _respondWith404()
472
  {
473
  $this->getResponse()->setHeader('HTTP/1.1','404 Not Found');
104
  "magento_version" => (string)Mage::getVersion(),
105
  "environment" => $configApi->environment(),
106
  "storefront" => Mage::app()->getStore()->getCode(),
107
+ "street_line_number" => Mage::getStoreConfig('customer/address/street_lines'),
108
  "attributes_sort_order_setting_raw" => $configApi->attributesSortOrderRaw()));
109
  }
110
 
302
  $requestObject->getParam('include_configuration_details'),
303
  $requestObject->getParam('include_media_gallery'));
304
 
305
+ if ($products == $model::PRODUCTS_ERROR_NOT_FOUND) {
306
  $this->_respondWith404();
307
  return false;
308
+ } else if ($products == $model::PRODUCTS_ERROR_OUT_OF_RANGE) {
309
+ $this->_respondWith403();
310
+ return false;
311
  }
312
 
313
  $this->_JSONencodeAndRespond($products);
325
  $productObjects = array();
326
  if ($requestObject->getParam('ids') != "") {
327
  $idsArray = explode(',', $requestObject->getParam('ids'));
328
+ if (count($idsArray) >= Highstreet_Hsapi_Model_Products::RANGE_LIMIT) {
329
+ $this->_JSONencodeAndRespond(array());
330
+ return false;
331
+ }
332
+
333
 
334
  foreach ($idsArray as $value) {
335
  $productObject = Mage::getModel('catalog/product')->load($value);
378
  $requestObject->getParam('include_configuration_details'),
379
  $requestObject->getParam('include_media_gallery'));
380
 
381
+ if ($response == $productsModel::PRODUCTS_ERROR_OUT_OF_RANGE) {
382
+ $this->_respondWith403();
383
  return false;
384
  }
385
 
477
  return;
478
  }
479
 
480
+ protected function _respondWith403()
481
+ {
482
+ $this->getResponse()->setHeader('HTTP/1.1','403 Forbidden');
483
+ $this->getResponse()->sendHeaders();
484
+ return;
485
+ }
486
+
487
  protected function _respondWith404()
488
  {
489
  $this->getResponse()->setHeader('HTTP/1.1','404 Not Found');
app/code/local/Highstreet/Hsapi/controllers/RedirectController.php ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Highstreet_HSAPI_module
5
+ *
6
+ * @package Highstreet_Hsapi
7
+ * @author Radovan Dodic (radovan.dodic@atessoft.rs) ~ AtesSoft
8
+ * @copyright Copyright (c) 2016 Highstreet
9
+ */
10
+ class Highstreet_Hsapi_RedirectController extends Mage_Core_Controller_Front_Action {
11
+
12
+ /**
13
+ * @return Highstreet_Hsapi_Helper_Config_Redirect
14
+ */
15
+ private function _getHelper() {
16
+ return Mage::helper('highstreet_hsapi/config_redirect');
17
+ }
18
+
19
+ /**
20
+ * External checkout
21
+ * method: POST
22
+ * POST_param: tid
23
+ * POST_param: session
24
+ * Content-Type: application/json
25
+ */
26
+ public function setCookieAction() {
27
+ $requestObject = Mage::app()->getRequest();
28
+
29
+ // get decoded path from reuest and set cookies
30
+ $decodedPath = $this->_getHelper()->getPathAndSetCookies($requestObject, false);
31
+
32
+ // redirect to web checkout
33
+ if ($decodedPath)
34
+ Mage::app()->getResponse()->setRedirect($decodedPath)->sendResponse();
35
+ }
36
+
37
+ }
app/code/local/Highstreet/Hsapi/etc/config.xml CHANGED
@@ -1,17 +1,17 @@
1
  <?xml version="1.0"?>
2
  <!--
3
  /**
4
- * Highstreet_HSAPI_module
5
- *
6
- * @package Highstreet_Hsapi
7
- * @author Tim Wachter (tim@touchwonders.com) ~ Touchwonders
8
- * @copyright Copyright (c) 2015 Touchwonders b.v. (http://www.touchwonders.com/)
9
- */
10
- -->
11
  <config>
12
  <modules>
13
  <Highstreet_Hsapi>
14
- <version>1.5.0</version>
15
  </Highstreet_Hsapi>
16
  </modules>
17
  <frontend>
@@ -30,6 +30,11 @@
30
  <highstreet_hsapi>
31
  <class>Highstreet_Hsapi_Model</class>
32
  </highstreet_hsapi>
 
 
 
 
 
33
  </models>
34
  <helpers>
35
  <highstreet_hsapi>
@@ -48,8 +53,8 @@
48
  </setup>
49
  </highstreet_hsapi_setup>
50
  </resources>
51
- <events>
52
- <sales_quote_merge_before>
53
  <observers>
54
  <highstreet_hsapi_merge_quote>
55
  <type>singleton</type>
@@ -57,8 +62,8 @@
57
  <method>mergeQuote</method>
58
  </highstreet_hsapi_merge_quote>
59
  </observers>
60
- </sales_quote_merge_before>
61
- <sales_order_place_after>
62
  <observers>
63
  <highstreet_hsapi_sales_order_place_after>
64
  <class>Highstreet_Hsapi_Model_Observer</class>
@@ -77,12 +82,47 @@
77
  <sales_order_invoice_cancel>
78
  <observers>
79
  <highstreet_hsapi_sales_order_invoice_cancel>
80
- <class>Highstreet_Hsapi_Model_Observer</class>
81
- <method>salesOrderInvoiceCancel</method>
82
  </highstreet_hsapi_sales_order_invoice_cancel>
83
  </observers>
84
  </sales_order_invoice_cancel>
85
- </events>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
86
  </global>
87
  <adminhtml>
88
  <acl>
1
  <?xml version="1.0"?>
2
  <!--
3
  /**
4
+ * Highstreet_HSAPI_module
5
+ *
6
+ * @package Highstreet_Hsapi
7
+ * @author Tim Wachter (tim@touchwonders.com) ~ Touchwonders
8
+ * @copyright Copyright (c) 2015 Touchwonders b.v. (http://www.touchwonders.com/)
9
+ */
10
+ -->
11
  <config>
12
  <modules>
13
  <Highstreet_Hsapi>
14
+ <version>1.7.0</version>
15
  </Highstreet_Hsapi>
16
  </modules>
17
  <frontend>
30
  <highstreet_hsapi>
31
  <class>Highstreet_Hsapi_Model</class>
32
  </highstreet_hsapi>
33
+ <core>
34
+ <rewrite>
35
+ <session>Highstreet_Hsapi_Model_Session</session>
36
+ </rewrite>
37
+ </core>
38
  </models>
39
  <helpers>
40
  <highstreet_hsapi>
53
  </setup>
54
  </highstreet_hsapi_setup>
55
  </resources>
56
+ <events>
57
+ <sales_quote_merge_before>
58
  <observers>
59
  <highstreet_hsapi_merge_quote>
60
  <type>singleton</type>
62
  <method>mergeQuote</method>
63
  </highstreet_hsapi_merge_quote>
64
  </observers>
65
+ </sales_quote_merge_before>
66
+ <sales_order_place_after>
67
  <observers>
68
  <highstreet_hsapi_sales_order_place_after>
69
  <class>Highstreet_Hsapi_Model_Observer</class>
82
  <sales_order_invoice_cancel>
83
  <observers>
84
  <highstreet_hsapi_sales_order_invoice_cancel>
85
+ <class>Highstreet_Hsapi_Model_Observer</class>
86
+ <method>salesOrderInvoiceCancel</method>
87
  </highstreet_hsapi_sales_order_invoice_cancel>
88
  </observers>
89
  </sales_order_invoice_cancel>
90
+ <!-- eTag update to quote and session object -->
91
+ <checkout_cart_product_add_after>
92
+ <observers>
93
+ <highstreet_hsapi_cart_etag_update>
94
+ <type>singleton</type>
95
+ <class>Highstreet_Hsapi_Model_CartObserver</class>
96
+ <method>cartEtagUpdate</method>
97
+ </highstreet_hsapi_cart_etag_update>
98
+ </observers>
99
+ </checkout_cart_product_add_after>
100
+ <sales_quote_remove_item>
101
+ <observers>
102
+ <highstreet_hsapi_cart_etag_update_remove>
103
+ <type>singleton</type>
104
+ <class>Highstreet_Hsapi_Model_CartObserver</class>
105
+ <method>cartEtagUpdate</method>
106
+ </highstreet_hsapi_cart_etag_update_remove>
107
+ </observers>
108
+ </sales_quote_remove_item>
109
+ <checkout_cart_update_items_after>
110
+ <observers>
111
+ <highstreet_hsapi_cart_etag_update_update>
112
+ <type>singleton</type>
113
+ <class>Highstreet_Hsapi_Model_CartObserver</class>
114
+ <method>cartEtagUpdate</method>
115
+ </highstreet_hsapi_cart_etag_update_update>
116
+ </observers>
117
+ </checkout_cart_update_items_after>
118
+ </events>
119
+ <default>
120
+ <highstreet_hsapi>
121
+ <api>
122
+ <checkout_override_storeview><![CDATA[-1]]></checkout_override_storeview>
123
+ </api>
124
+ </highstreet_hsapi>
125
+ </default>
126
  </global>
127
  <adminhtml>
128
  <acl>
app/code/local/Highstreet/Hsapi/etc/system.xml CHANGED
@@ -19,6 +19,14 @@
19
  <show_in_website>1</show_in_website>
20
  <show_in_store>1</show_in_store>
21
  <fields>
 
 
 
 
 
 
 
 
22
  <store_id>
23
  <label>Store identifier</label>
24
  <comment>Your store identifier, e.g. your brand name</comment>
@@ -114,6 +122,15 @@
114
  <show_in_website>1</show_in_website>
115
  <show_in_store>1</show_in_store>
116
  </checkout_redirect_url>
 
 
 
 
 
 
 
 
 
117
  <attribute_sort_order translate="label">
118
  <label>Adjust the product sort order</label>
119
  <comment>JSON encoded string, formatted as {"attribute_name":"sortorder",...}</comment>
@@ -123,8 +140,65 @@
123
  <show_in_website>1</show_in_website>
124
  <show_in_store>1</show_in_store>
125
  </attribute_sort_order>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
126
  </fields>
127
  </api>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
  </groups>
129
  </highstreet_hsapi>
130
  </sections>
19
  <show_in_website>1</show_in_website>
20
  <show_in_store>1</show_in_store>
21
  <fields>
22
+ <heading_general>
23
+ <label><![CDATA[General Configuration]]></label>
24
+ <frontend_model>adminhtml/system_config_form_field_heading</frontend_model>
25
+ <sort_order>1</sort_order>
26
+ <show_in_default>1</show_in_default>
27
+ <show_in_website>1</show_in_website>
28
+ <show_in_store>1</show_in_store>
29
+ </heading_general>
30
  <store_id>
31
  <label>Store identifier</label>
32
  <comment>Your store identifier, e.g. your brand name</comment>
122
  <show_in_website>1</show_in_website>
123
  <show_in_store>1</show_in_store>
124
  </checkout_redirect_url>
125
+ <checkout_override_storeview translate="label">
126
+ <label>The store view in which the orders should be saved</label>
127
+ <frontend_type>select</frontend_type>
128
+ <source_model>highstreet_hsapi/system_config_stores</source_model>
129
+ <sort_order>92</sort_order>
130
+ <show_in_default>1</show_in_default>
131
+ <show_in_website>1</show_in_website>
132
+ <show_in_store>1</show_in_store>
133
+ </checkout_override_storeview>
134
  <attribute_sort_order translate="label">
135
  <label>Adjust the product sort order</label>
136
  <comment>JSON encoded string, formatted as {"attribute_name":"sortorder",...}</comment>
140
  <show_in_website>1</show_in_website>
141
  <show_in_store>1</show_in_store>
142
  </attribute_sort_order>
143
+ <heading_account>
144
+ <label><![CDATA[New Account Configuration]]></label>
145
+ <frontend_model>adminhtml/system_config_form_field_heading</frontend_model>
146
+ <sort_order>110</sort_order>
147
+ <show_in_default>1</show_in_default>
148
+ <show_in_website>1</show_in_website>
149
+ <show_in_store>1</show_in_store>
150
+ </heading_account>
151
+ <new_account_email_enabled>
152
+ <label>Send Custom New Account Email Template</label>
153
+ <show_in_default>1</show_in_default>
154
+ <show_in_website>1</show_in_website>
155
+ <show_in_store>1</show_in_store>
156
+ <sort_order>119</sort_order>
157
+ <frontend_type>select</frontend_type>
158
+ <source_model>adminhtml/system_config_source_yesno</source_model>
159
+ </new_account_email_enabled>
160
+ <new_account_email_template>
161
+ <label>New Account Email Template</label>
162
+ <show_in_default>1</show_in_default>
163
+ <show_in_website>1</show_in_website>
164
+ <show_in_store>1</show_in_store>
165
+ <sort_order>120</sort_order>
166
+ <frontend_type>select</frontend_type>
167
+ <source_model>adminhtml/system_config_source_email_template</source_model>
168
+ <depends>
169
+ <new_account_email_enabled>1</new_account_email_enabled>
170
+ </depends>
171
+ </new_account_email_template>
172
  </fields>
173
  </api>
174
+ <developer>
175
+ <label>Developer</label>
176
+ <frontend_type>text</frontend_type>
177
+ <sort_order>50</sort_order>
178
+ <show_in_default>1</show_in_default>
179
+ <show_in_website>1</show_in_website>
180
+ <show_in_store>1</show_in_store>
181
+ <fields>
182
+ <heading_general>
183
+ <label><![CDATA[Developer Options]]></label>
184
+ <frontend_model>adminhtml/system_config_form_field_heading</frontend_model>
185
+ <sort_order>1</sort_order>
186
+ <show_in_default>1</show_in_default>
187
+ <show_in_website>1</show_in_website>
188
+ <show_in_store>1</show_in_store>
189
+ </heading_general>
190
+ <log_enabled translate="label">
191
+ <label>Enable Logging</label>
192
+ <comment>var/log/Highstreet_HSAPI.log</comment>
193
+ <frontend_type>select</frontend_type>
194
+ <source_model>adminhtml/system_config_source_yesno</source_model>
195
+ <sort_order>30</sort_order>
196
+ <show_in_default>1</show_in_default>
197
+ <show_in_website>1</show_in_website>
198
+ <show_in_store>1</show_in_store>
199
+ </log_enabled>
200
+ </fields>
201
+ </developer>
202
  </groups>
203
  </highstreet_hsapi>
204
  </sections>
app/code/local/Highstreet/SmartAppBanner/etc/config.xml CHANGED
@@ -5,7 +5,7 @@
5
  <modules>
6
 
7
  <Highstreet_SmartAppBanner>
8
- <version>1.5.0</version>
9
  </Highstreet_SmartAppBanner>
10
 
11
  </modules>
5
  <modules>
6
 
7
  <Highstreet_SmartAppBanner>
8
+ <version>1.7.0</version>
9
  </Highstreet_SmartAppBanner>
10
 
11
  </modules>
app/design/frontend/base/default/template/highstreet/native_smart_app_banner.phtml CHANGED
@@ -11,7 +11,7 @@ if ($configHelper->nativeSmartbannerAppUrl() != "") {
11
  $product = Mage::registry('current_product');
12
  $deeplink .= $product ? "products/" . $product->getId() . "/" : "";
13
 
14
- $searchQuery = Mage::app()->getRequest()->getParam('q');
15
  $deeplink .= $searchQuery ? "search/" . $searchQuery . "/" : "";
16
 
17
  if ($deeplink === $configHelper->nativeSmartbannerAppUrl() . "://") {
11
  $product = Mage::registry('current_product');
12
  $deeplink .= $product ? "products/" . $product->getId() . "/" : "";
13
 
14
+ $searchQuery = $this->escapeHtml(Mage::app()->getRequest()->getParam('q'));
15
  $deeplink .= $searchQuery ? "search/" . $searchQuery . "/" : "";
16
 
17
  if ($deeplink === $configHelper->nativeSmartbannerAppUrl() . "://") {
app/design/frontend/base/default/template/highstreet/smart_app_banner.phtml CHANGED
@@ -36,7 +36,7 @@ $content->category = $category ? $category->getId() : null;
36
  $product = Mage::registry('current_product');
37
  $content->product = $product ? $product->getId() : null;
38
 
39
- $content->query = Mage::app()->getRequest()->getParam('q', null);
40
 
41
  $content->version = Mage::getConfig()->getModuleConfig('Highstreet_SmartAppBanner')->version;
42
 
@@ -54,23 +54,13 @@ if ( in_array($config->module, $module_blacklist)
54
 
55
  // Construct javascript url
56
  $configHelper = Mage::helper('highstreet_hsapi/config_api');
57
- $middleWareUrl = $configHelper->middlewareUrl();
58
- $base_url = $middleWareUrl . "/smartbanner";
59
- $key = md5('dY63*23xOi'. $configHelper->storeIdentifier());
60
- $params = base64_url_encode(encrypt(json_encode($config), $key));
61
 
62
  $js_url = "${base_url}/${params}";
63
 
64
- /**
65
- * Returns an encrypted & utf8-encoded string
66
- */
67
- function encrypt($pure_string, $encryption_key) {
68
- $iv_size = mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_ECB);
69
- $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
70
- $encrypted_string = mcrypt_encrypt(MCRYPT_BLOWFISH, $encryption_key, utf8_encode($pure_string), MCRYPT_MODE_ECB, $iv);
71
- return trim($encrypted_string);
72
- }
73
-
74
  /**
75
  * Returns base64 url-safe encoded string
76
  */
36
  $product = Mage::registry('current_product');
37
  $content->product = $product ? $product->getId() : null;
38
 
39
+ $content->query = $this->escapeHtml(Mage::app()->getRequest()->getParam('q'));
40
 
41
  $content->version = Mage::getConfig()->getModuleConfig('Highstreet_SmartAppBanner')->version;
42
 
54
 
55
  // Construct javascript url
56
  $configHelper = Mage::helper('highstreet_hsapi/config_api');
57
+ $middleWareUrl = '//' . $configHelper->middlewareHostAndUri();
58
+ $base_url = $middleWareUrl . "smartbanner";
59
+ $base_url = str_replace(Highstreet_Hsapi_Helper_Config_Api::MIDDLEWARE_URL_SCHEME, "//", $base_url); // Replace the scheme with '//' so that the browser always decides if this resource needs to be loaded over SSL or not
60
+ $params = base64_url_encode(json_encode($config));
61
 
62
  $js_url = "${base_url}/${params}";
63
 
 
 
 
 
 
 
 
 
 
 
64
  /**
65
  * Returns base64 url-safe encoded string
66
  */
package.xml CHANGED
@@ -1,7 +1,7 @@
1
  <?xml version="1.0"?>
2
  <package>
3
  <name>Highstreet</name>
4
- <version>1.5.0</version>
5
  <stability>stable</stability>
6
  <license>-</license>
7
  <channel>community</channel>
@@ -10,10 +10,10 @@
10
  <description>The extension provides read-only access to the catalog (products and categories) by providing a simple API for the app. &#xD;
11
  This extension does not modify or affect the Magento installation, nor does it change any catalog data.</description>
12
  <notes>-</notes>
13
- <authors><author><name>Christian Apers</name><user>Christian</user><email>Christian@Touchwonders.com</email></author><author><name>Tim Wachter</name><user>Tim</user><email>Tim@Touchwonders.com</email></author></authors>
14
- <date>2015-11-11</date>
15
- <time>16:02:28</time>
16
- <contents><target name="magelocal"><dir name="Highstreet"><dir name="Hsapi"><dir name="Helper"><dir name="Config"><file name="Api.php" hash="2d36d4018cb9f106794a7a895dcc3e0b"/></dir><file name="Data.php" hash="c0a9f7313e8487f10c40be73dbe34e2a"/><file name="Encryption.php" hash="c381845a0bb22e460abac657ac96cd8b"/></dir><dir name="Model"><file name="Attributes.php" hash="419fad545dc34dafd246fdfb4a139e3d"/><file name="Categories.php" hash="46b5f643165efdcaaab8a77f51b126f8"/><file name="CheckoutV2.php" hash="b9a85c8729d3cac5f34f67dd3dabda89"/><file name="Observer.php" hash="66c91124a567aba176a12c42de870af0"/><file name="Products.php" hash="3bb719c0731ba4570cad5184b4efc4f2"/><file name="SearchSuggestions.php" hash="7bed3efaf5af19276a4fbb7d6aade83f"/><dir name="System"><dir name="Config"><file name="Environment.php" hash="5ef07d78cf67bc47be1a64e54ff643ec"/></dir></dir></dir><dir name="controllers"><file name="CheckoutController.php" hash="e6bf261b14ba1db83e21b2bfb9ae4dee"/><file name="IndexController.php" hash="56d2269c92984d46c9fa9575647088a2"/></dir><dir name="etc"><file name="config.xml" hash="c31bd4f4f8a0c3e11ca556b74e797099"/><file name="system.xml" hash="531fc9ffcf8cb09ee7d6457ef7cbaa4b"/></dir></dir><dir name="SmartAppBanner"><dir name="etc"><file name="config.xml" hash="6abce99709952c93f3af13afdca03a79"/></dir></dir></dir></target><target name="mageetc"><dir name="modules"><file name="Highstreet_Api.xml" hash="97e586a7ef0a274d4f0fda883c646f31"/><file name="Highstreet_SmartAppBanner.xml" hash="c739b442a3907d7d7414d0abfd0efaf2"/></dir></target><target name="magedesign"><dir name="frontend"><dir name="base"><dir name="default"><dir name="layout"><dir name="highstreet"><file name="smart_app_banner.xml" hash="ed1ad1737439e221c8ca1e0daa0ccd1b"/></dir></dir><dir name="template"><dir name="highstreet"><file name="native_smart_app_banner.phtml" hash="e7c16669aa29fcfbc18ea5275b80341a"/><file name="smart_app_banner.phtml" hash="0faa81b53d95a57583e1c1747ac9cd96"/></dir></dir></dir></dir></dir></target></contents>
17
  <compatible/>
18
  <dependencies><required><php><min>5.0.0.0</min><max>6.0.0.0</max></php></required></dependencies>
19
  </package>
1
  <?xml version="1.0"?>
2
  <package>
3
  <name>Highstreet</name>
4
+ <version>1.7.0</version>
5
  <stability>stable</stability>
6
  <license>-</license>
7
  <channel>community</channel>
10
  <description>The extension provides read-only access to the catalog (products and categories) by providing a simple API for the app. &#xD;
11
  This extension does not modify or affect the Magento installation, nor does it change any catalog data.</description>
12
  <notes>-</notes>
13
+ <authors><author><name>Highstreet</name><user>highstreet</user><email>support@highstreetapp.com</email></author></authors>
14
+ <date>2017-02-16</date>
15
+ <time>14:47:43</time>
16
+ <contents><target name="magelocal"><dir name="Highstreet"><dir name="Hsapi"><dir name="Helper"><dir name="Config"><file name="Account.php" hash="fa740f2a49c830f48919c1823faa5022"/><file name="Api.php" hash="50d19b77b4958e58644a9304ab21a786"/><file name="Cart.php" hash="a4211d5986579352de27c7e31fcae739"/><file name="Checkout.php" hash="310edf6ea6b9fa81c92dfcc6664c2f94"/><file name="Default.php" hash="4b6cbde6ed60ac786ebc32d1997992b2"/><file name="Redirect.php" hash="b0b754f3821bdf2078e9d0ca393af850"/></dir><file name="Data.php" hash="c0a9f7313e8487f10c40be73dbe34e2a"/><file name="Encryption.php" hash="c381845a0bb22e460abac657ac96cd8b"/></dir><dir name="Model"><file name="Attributes.php" hash="3c4f6880fd8352b1343bc2c5abc6a19b"/><file name="CartObserver.php" hash="2eb6be637f5c78f49d8ed618e4e52337"/><file name="Categories.php" hash="46b5f643165efdcaaab8a77f51b126f8"/><file name="CheckoutV2.php" hash="9c9741f632ac60567d26fd8ac7d9d899"/><file name="Observer.php" hash="b272c33ca23a30375c31f4f37be7e962"/><file name="Products.php" hash="9e43f10be77a646b1bf2986a1cb9180a"/><file name="SearchSuggestions.php" hash="7bed3efaf5af19276a4fbb7d6aade83f"/><file name="Session.php" hash="a21f63dc8b6a78492378022d63c8190b"/><dir name="System"><dir name="Config"><file name="Environment.php" hash="5ef07d78cf67bc47be1a64e54ff643ec"/><file name="Stores.php" hash="0812f9942274c0a8f605391f697c0265"/></dir></dir></dir><dir name="controllers"><file name="AccountController.php" hash="ef2e81bdf9139a25560773aea58789a5"/><file name="CartController.php" hash="44c1694e920c3c47c9c8a216c3c3270e"/><file name="CheckoutController.php" hash="2363b1d849ba2347ae59c1e9498cb74c"/><file name="CheckoutV3Controller.php" hash="550813c044d594ee9b9095bc5e97bccf"/><file name="IndexController.php" hash="135da479e6146d6868e60469e745450d"/><file name="RedirectController.php" hash="c058db20643ea870f7da552f2888faf7"/></dir><dir name="etc"><file name="config.xml" hash="b5c48be2cfe4a0a9c9d54559ca6dba8d"/><file name="system.xml" hash="a33c1ef938f3e453d677f8adc45ce8d1"/></dir></dir><dir name="SmartAppBanner"><dir name="etc"><file name="config.xml" hash="83360f37f88541b874382d832fd0a76b"/></dir></dir></dir></target><target name="mageetc"><dir name="modules"><file name="Highstreet_Api.xml" hash="97e586a7ef0a274d4f0fda883c646f31"/><file name="Highstreet_SmartAppBanner.xml" hash="c739b442a3907d7d7414d0abfd0efaf2"/></dir></target><target name="magedesign"><dir name="frontend"><dir name="base"><dir name="default"><dir name="layout"><dir name="highstreet"><file name="smart_app_banner.xml" hash="ed1ad1737439e221c8ca1e0daa0ccd1b"/></dir></dir><dir name="template"><dir name="highstreet"><file name="native_smart_app_banner.phtml" hash="fb9a44823ca7e074ed924b1b6706a0d3"/><file name="smart_app_banner.phtml" hash="dc5f8cf40b4c42a05095744c96afe816"/></dir></dir></dir></dir></dir></target></contents>
17
  <compatible/>
18
  <dependencies><required><php><min>5.0.0.0</min><max>6.0.0.0</max></php></required></dependencies>
19
  </package>