PageRenderTime 63ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 1ms

/magento/app/code/core/Mage/GoogleCheckout/Model/Api/Xml/Callback.php

https://bitbucket.org/jit_bec/shopifine
PHP | 1104 lines | 767 code | 182 blank | 155 comment | 98 complexity | aa5795f89b4ee14c3591e79f88b217dd MD5 | raw file
Possible License(s): LGPL-3.0
  1. <?php
  2. /**
  3. * Magento
  4. *
  5. * NOTICE OF LICENSE
  6. *
  7. * This source file is subject to the Open Software License (OSL 3.0)
  8. * that is bundled with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://opensource.org/licenses/osl-3.0.php
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@magentocommerce.com so we can send you a copy immediately.
  14. *
  15. * DISCLAIMER
  16. *
  17. * Do not edit or add to this file if you wish to upgrade Magento to newer
  18. * versions in the future. If you wish to customize Magento for your
  19. * needs please refer to http://www.magentocommerce.com for more information.
  20. *
  21. * @category Mage
  22. * @package Mage_GoogleCheckout
  23. * @copyright Copyright (c) 2012 Magento Inc. (http://www.magentocommerce.com)
  24. * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
  25. */
  26. class Mage_GoogleCheckout_Model_Api_Xml_Callback extends Mage_GoogleCheckout_Model_Api_Xml_Abstract
  27. {
  28. protected $_cachedShippingInfo = array(); // Cache of possible shipping carrier-methods combinations per storeId
  29. /**
  30. * Process notification from google
  31. * @return Mage_GoogleCheckout_Model_Api_Xml_Callback
  32. */
  33. public function process()
  34. {
  35. // Retrieve the XML sent in the HTTP POST request to the ResponseHandler
  36. $xmlResponse = isset($GLOBALS['HTTP_RAW_POST_DATA']) ?
  37. $GLOBALS['HTTP_RAW_POST_DATA'] : file_get_contents("php://input");
  38. if (get_magic_quotes_gpc()) {
  39. $xmlResponse = stripslashes($xmlResponse);
  40. }
  41. $debugData = array('request' => $xmlResponse, 'dir' => 'in');
  42. if (empty($xmlResponse)) {
  43. $this->getApi()->debugData($debugData);
  44. return false;
  45. }
  46. list($root, $data) = $this->getGResponse()->GetParsedXML($xmlResponse);
  47. $this->getGResponse()->SetMerchantAuthentication($this->getMerchantId(), $this->getMerchantKey());
  48. $status = $this->getGResponse()->HttpAuthentication();
  49. if (!$status || empty($data[$root])) {
  50. exit;
  51. }
  52. $this->setRootName($root)->setRoot($data[$root]);
  53. $serialNumber = $this->getData('root/serial-number');
  54. $this->getGResponse()->setSerialNumber($serialNumber);
  55. /*
  56. * Prevent multiple notification processing
  57. */
  58. $notification = Mage::getModel('googlecheckout/notification')
  59. ->setSerialNumber($serialNumber)
  60. ->loadNotificationData();
  61. if ($notification->getStartedAt()) {
  62. if ($notification->isProcessed()) {
  63. $this->getGResponse()->SendAck();
  64. return;
  65. }
  66. if ($notification->isTimeout()) {
  67. $notification->updateProcess();
  68. } else {
  69. $this->getGResponse()->SendServerErrorStatus();
  70. return;
  71. }
  72. } else {
  73. $notification->startProcess();
  74. }
  75. $method = '_response' . uc_words($root, '', '-');
  76. if (method_exists($this, $method)) {
  77. ob_start();
  78. try {
  79. $this->$method();
  80. $notification->stopProcess();
  81. } catch (Exception $e) {
  82. $this->getGResponse()->log->logError($e->__toString());
  83. }
  84. $debugData['result'] = ob_get_flush();
  85. $this->getApi()->debugData($debugData);
  86. } else {
  87. $this->getGResponse()->SendBadRequestStatus("Invalid or not supported Message");
  88. }
  89. return $this;
  90. }
  91. /**
  92. * Load quote from request and make sure the proper payment method is set
  93. *
  94. * @return Mage_Sales_Model_Quote
  95. */
  96. protected function _loadQuote()
  97. {
  98. $quoteId = $this->getData('root/shopping-cart/merchant-private-data/quote-id/VALUE');
  99. $storeId = $this->getData('root/shopping-cart/merchant-private-data/store-id/VALUE');
  100. $quote = Mage::getModel('sales/quote')
  101. ->setStoreId($storeId)
  102. ->load($quoteId);
  103. if ($quote->isVirtual()) {
  104. $quote->getBillingAddress()->setPaymentMethod('googlecheckout');
  105. } else {
  106. $quote->getShippingAddress()->setPaymentMethod('googlecheckout');
  107. }
  108. return $quote;
  109. }
  110. protected function _getApiUrl()
  111. {
  112. return null;
  113. }
  114. protected function getGoogleOrderNumber()
  115. {
  116. return $this->getData('root/google-order-number/VALUE');
  117. }
  118. protected function _responseRequestReceived()
  119. {
  120. }
  121. protected function _responseError()
  122. {
  123. }
  124. protected function _responseDiagnosis()
  125. {
  126. }
  127. protected function _responseCheckoutRedirect()
  128. {
  129. }
  130. /**
  131. * Calculate available shipping amounts and taxes
  132. */
  133. protected function _responseMerchantCalculationCallback()
  134. {
  135. $merchantCalculations = new GoogleMerchantCalculations($this->getCurrency());
  136. $quote = $this->_loadQuote();
  137. $billingAddress = $quote->getBillingAddress();
  138. $address = $quote->getShippingAddress();
  139. $googleAddress = $this->getData('root/calculate/addresses/anonymous-address');
  140. $googleAddresses = array();
  141. if ( isset( $googleAddress['id'] ) ) {
  142. $googleAddresses[] = $googleAddress;
  143. } else {
  144. $googleAddresses = $googleAddress;
  145. }
  146. $methods = Mage::getStoreConfig('google/checkout_shipping_merchant/allowed_methods', $this->getStoreId());
  147. $methods = unserialize($methods);
  148. $limitCarrier = array();
  149. foreach ($methods['method'] as $method) {
  150. if ($method) {
  151. list($carrierCode, $methodCode) = explode('/', $method);
  152. $limitCarrier[$carrierCode] = $carrierCode;
  153. }
  154. }
  155. $limitCarrier = array_values($limitCarrier);
  156. foreach($googleAddresses as $googleAddress) {
  157. $addressId = $googleAddress['id'];
  158. $regionCode = $googleAddress['region']['VALUE'];
  159. $countryCode = $googleAddress['country-code']['VALUE'];
  160. $regionModel = Mage::getModel('directory/region')->loadByCode($regionCode, $countryCode);
  161. $regionId = $regionModel->getId();
  162. $address->setCountryId($countryCode)
  163. ->setRegion($regionCode)
  164. ->setRegionId($regionId)
  165. ->setCity($googleAddress['city']['VALUE'])
  166. ->setPostcode($googleAddress['postal-code']['VALUE'])
  167. ->setLimitCarrier($limitCarrier);
  168. $billingAddress->setCountryId($countryCode)
  169. ->setRegion($regionCode)
  170. ->setRegionId($regionId)
  171. ->setCity($googleAddress['city']['VALUE'])
  172. ->setPostcode($googleAddress['postal-code']['VALUE'])
  173. ->setLimitCarrier($limitCarrier);
  174. $billingAddress->collectTotals();
  175. $shippingTaxClass = $this->_getTaxClassForShipping($quote);
  176. $gRequestMethods = $this->getData('root/calculate/shipping/method');
  177. if ($gRequestMethods) {
  178. // Make stable format of $gRequestMethods for convenient usage
  179. if (array_key_exists('VALUE', $gRequestMethods)) {
  180. $gRequestMethods = array($gRequestMethods);
  181. }
  182. // Form list of mapping Google method names to applicable address rates
  183. $rates = array();
  184. $address->setCollectShippingRates(true)
  185. ->collectShippingRates();
  186. foreach ($address->getAllShippingRates() as $rate) {
  187. if ($rate instanceof Mage_Shipping_Model_Rate_Result_Error) {
  188. continue;
  189. }
  190. $methodName = sprintf('%s - %s', $rate->getCarrierTitle(), $rate->getMethodTitle());
  191. $rates[$methodName] = $rate;
  192. }
  193. foreach ($gRequestMethods as $method) {
  194. $result = new GoogleResult($addressId);
  195. $methodName = $method['name'];
  196. if (isset($rates[$methodName])) {
  197. $rate = $rates[$methodName];
  198. $address->setShippingMethod($rate->getCode())
  199. ->setLimitCarrier($rate->getCarrier())
  200. ->setCollectShippingRates(true)
  201. ->collectTotals();
  202. $shippingRate = $address->getBaseShippingAmount() - $address->getBaseShippingDiscountAmount();
  203. $result->SetShippingDetails($methodName, $shippingRate, 'true');
  204. if ($this->getData('root/calculate/tax/VALUE') == 'true') {
  205. $taxAmount = $address->getBaseTaxAmount();
  206. $taxAmount += $billingAddress->getBaseTaxAmount();
  207. $result->setTaxDetails($taxAmount);
  208. }
  209. } else {
  210. if ($shippingTaxClass &&
  211. $this->getData('root/calculate/tax/VALUE') == 'true') {
  212. $i = 1;
  213. $price = Mage::getStoreConfig(
  214. 'google/checkout_shipping_flatrate/price_'.$i,
  215. $quote->getStoreId()
  216. );
  217. $price = number_format($price, 2, '.','');
  218. $price = (float) Mage::helper('tax')->getShippingPrice($price, false, false);
  219. $address->setShippingMethod(null);
  220. $address->setCollectShippingRates(true)->collectTotals();
  221. $billingAddress->setCollectShippingRates(true)->collectTotals();
  222. $address->setBaseShippingAmount($price);
  223. $address->setShippingAmount(
  224. $this->_reCalculateToStoreCurrency($price, $quote)
  225. );
  226. $this->_applyShippingTaxClass($address, $shippingTaxClass);
  227. $taxAmount = $address->getBaseTaxAmount();
  228. $taxAmount += $billingAddress->getBaseTaxAmount();
  229. $result->SetShippingDetails(
  230. $methodName,
  231. $price - $address->getBaseShippingDiscountAmount(),
  232. 'true'
  233. );
  234. $result->setTaxDetails($taxAmount);
  235. $i++;
  236. } else {
  237. $result->SetShippingDetails($methodName, 0, 'false');
  238. }
  239. }
  240. $merchantCalculations->AddResult($result);
  241. }
  242. } else if ($this->getData('root/calculate/tax/VALUE') == 'true') {
  243. $address->setShippingMethod(null);
  244. $address->setCollectShippingRates(true)->collectTotals();
  245. $billingAddress->setCollectShippingRates(true)->collectTotals();
  246. if (!Mage::helper('googlecheckout')->isShippingCarrierActive($this->getStoreId())) {
  247. $this->_applyShippingTaxClass($address, $shippingTaxClass);
  248. }
  249. $taxAmount = $address->getBaseTaxAmount();
  250. $taxAmount += $billingAddress->getBaseTaxAmount();
  251. $result = new GoogleResult($addressId);
  252. $result->setTaxDetails($taxAmount);
  253. $merchantCalculations->addResult($result);
  254. }
  255. }
  256. $this->getGResponse()->ProcessMerchantCalculations($merchantCalculations);
  257. }
  258. /**
  259. * Apply shipping tax class
  260. *
  261. * @param Varien_Object $qAddress
  262. * @param mixed $shippingTaxClass
  263. */
  264. protected function _applyShippingTaxClass($qAddress, $shippingTaxClass)
  265. {
  266. if (!$shippingTaxClass) {
  267. return;
  268. }
  269. $quote = $qAddress->getQuote();
  270. $taxCalculationModel = Mage::getSingleton('tax/calculation');
  271. $request = $taxCalculationModel->getRateRequest($qAddress);
  272. $rate = $taxCalculationModel->getRate($request->setProductClassId($shippingTaxClass));
  273. if (!Mage::helper('tax')->shippingPriceIncludesTax()) {
  274. $shippingTax = $qAddress->getShippingAmount() * $rate/100;
  275. $shippingBaseTax= $qAddress->getBaseShippingAmount() * $rate/100;
  276. } else {
  277. $shippingTax = $qAddress->getShippingTaxAmount();
  278. $shippingBaseTax= $qAddress->getBaseShippingTaxAmount();
  279. }
  280. $shippingTax = $quote->getStore()->roundPrice($shippingTax);
  281. $shippingBaseTax= $quote->getStore()->roundPrice($shippingBaseTax);
  282. $qAddress->setTaxAmount($qAddress->getTaxAmount() + $shippingTax);
  283. $qAddress->setBaseTaxAmount($qAddress->getBaseTaxAmount() + $shippingBaseTax);
  284. }
  285. /**
  286. * Process new order creation notification from google.
  287. * Convert customer quote to order
  288. */
  289. protected function _responseNewOrderNotification()
  290. {
  291. $this->getGResponse()->SendAck();
  292. // LOOK FOR EXISTING ORDER TO AVOID DUPLICATES
  293. $orders = Mage::getModel('sales/order')->getCollection()
  294. ->addAttributeToFilter('ext_order_id', $this->getGoogleOrderNumber());
  295. if (count($orders)) {
  296. return;
  297. }
  298. // IMPORT GOOGLE ORDER DATA INTO QUOTE
  299. /* @var $quote Mage_Sales_Model_Quote */
  300. $quote = $this->_loadQuote();
  301. $quote->setIsActive(true)->reserveOrderId();
  302. Mage::dispatchEvent('googlecheckout_create_order_before', array('quote' => $quote));
  303. if ($quote->getErrorMessage()) {
  304. $this->getGRequest()->SendCancelOrder($this->getGoogleOrderNumber(),
  305. $this->__('Order creation error'),
  306. $quote->getErrorMessage()
  307. );
  308. return;
  309. }
  310. $storeId = $quote->getStoreId();
  311. Mage::app()->setCurrentStore(Mage::app()->getStore($storeId));
  312. if ($quote->getQuoteCurrencyCode() != $quote->getBaseCurrencyCode()) {
  313. Mage::app()->getStore()->setCurrentCurrencyCode($quote->getQuoteCurrencyCode());
  314. }
  315. $billing = $this->_importGoogleAddress($this->getData('root/buyer-billing-address'));
  316. $quote->setBillingAddress($billing);
  317. $shipping = $this->_importGoogleAddress($this->getData('root/buyer-shipping-address'));
  318. $quote->setShippingAddress($shipping);
  319. $this->_importGoogleTotals($quote->getShippingAddress());
  320. $quote->getPayment()->importData(array('method'=>'googlecheckout'));
  321. $taxMessage = $this->_applyCustomTax($quote->getShippingAddress());
  322. // CONVERT QUOTE TO ORDER
  323. $convertQuote = Mage::getSingleton('sales/convert_quote');
  324. /* @var $order Mage_Sales_Model_Order */
  325. $order = $convertQuote->toOrder($quote);
  326. if ($quote->isVirtual()) {
  327. $convertQuote->addressToOrder($quote->getBillingAddress(), $order);
  328. } else {
  329. $convertQuote->addressToOrder($quote->getShippingAddress(), $order);
  330. }
  331. $order->setExtOrderId($this->getGoogleOrderNumber());
  332. $order->setExtCustomerId($this->getData('root/buyer-id/VALUE'));
  333. if (!$order->getCustomerEmail()) {
  334. $order->setCustomerEmail($billing->getEmail())
  335. ->setCustomerPrefix($billing->getPrefix())
  336. ->setCustomerFirstname($billing->getFirstname())
  337. ->setCustomerMiddlename($billing->getMiddlename())
  338. ->setCustomerLastname($billing->getLastname())
  339. ->setCustomerSuffix($billing->getSuffix());
  340. }
  341. $order->setBillingAddress($convertQuote->addressToOrderAddress($quote->getBillingAddress()));
  342. if (!$quote->isVirtual()) {
  343. $order->setShippingAddress($convertQuote->addressToOrderAddress($quote->getShippingAddress()));
  344. }
  345. #$order->setPayment($convertQuote->paymentToOrderPayment($quote->getPayment()));
  346. foreach ($quote->getAllItems() as $item) {
  347. $orderItem = $convertQuote->itemToOrderItem($item);
  348. if ($item->getParentItem()) {
  349. $orderItem->setParentItem($order->getItemByQuoteItemId($item->getParentItem()->getId()));
  350. }
  351. $order->addItem($orderItem);
  352. }
  353. /*
  354. * Adding transaction for correct transaction information displaying on order view at back end.
  355. * It has no influence on api interaction logic.
  356. */
  357. $payment = Mage::getModel('sales/order_payment')
  358. ->setMethod('googlecheckout')
  359. ->setTransactionId($this->getGoogleOrderNumber())
  360. ->setIsTransactionClosed(false);
  361. $order->setPayment($payment);
  362. $payment->addTransaction(Mage_Sales_Model_Order_Payment_Transaction::TYPE_AUTH);
  363. $order->setCanShipPartiallyItem(false);
  364. $emailAllowed = ($this->getData('root/buyer-marketing-preferences/email-allowed/VALUE') === 'true');
  365. $emailStr = $emailAllowed ? $this->__('Yes') : $this->__('No');
  366. $message = $this->__('Google Order Number: %s', '<strong>' . $this->getGoogleOrderNumber() . '</strong><br />')
  367. . $this->__('Google Buyer ID: %s', '<strong>' . $this->getData('root/buyer-id/VALUE') . '</strong><br />')
  368. . $this->__('Is Buyer Willing to Receive Marketing Emails: %s', '<strong>' . $emailStr . '</strong>');
  369. if ($taxMessage) {
  370. $message .= $this->__('<br />Warning: <strong>%s</strong><br />', $taxMessage);
  371. }
  372. $order->addStatusToHistory($order->getStatus(), $message);
  373. $order->place();
  374. $order->save();
  375. $order->sendNewOrderEmail();
  376. Mage::dispatchEvent('googlecheckout_save_order_after', array('order' => $order));
  377. $quote->setIsActive(false)->save();
  378. if ($emailAllowed) {
  379. $customer = $quote->getCustomer();
  380. if ($customer && $customer->getId()) {
  381. $customer->setIsSubscribed(true);
  382. Mage::getModel('newsletter/subscriber')->subscribeCustomer($customer);
  383. } else {
  384. Mage::getModel('newsletter/subscriber')->subscribe($order->getCustomerEmail());
  385. }
  386. }
  387. Mage::dispatchEvent('checkout_submit_all_after', array('order' => $order, 'quote' => $quote));
  388. $this->getGRequest()->SendMerchantOrderNumber($order->getExtOrderId(), $order->getIncrementId());
  389. }
  390. /**
  391. * If tax value differs tax which is setted on magento,
  392. * apply Google tax and recollect quote
  393. *
  394. * @param Varien_Object $qAddress
  395. * @return string | false
  396. */
  397. protected function _applyCustomTax($qAddress)
  398. {
  399. $quote = $qAddress->getQuote();
  400. $qTaxAmount = $qAddress->getBaseTaxAmount();
  401. $newTaxAmount = $this->getData('root/order-adjustment/total-tax/VALUE');
  402. if ($qTaxAmount != $newTaxAmount) {
  403. $taxQuotient = (int) $qTaxAmount ? $newTaxAmount/$qTaxAmount : $newTaxAmount;
  404. $qAddress->setTaxAmount(
  405. $this->_reCalculateToStoreCurrency($newTaxAmount, $quote)
  406. );
  407. $qAddress->setBaseTaxAmount($newTaxAmount);
  408. $grandTotal = $qAddress->getBaseGrandTotal() - $qTaxAmount + $newTaxAmount;
  409. $qAddress->setGrandTotal(
  410. $this->_reCalculateToStoreCurrency($grandTotal, $quote)
  411. );
  412. $qAddress->setBaseGrandTotal($grandTotal);
  413. $subtotalInclTax = $qAddress->getSubtotalInclTax() - $qTaxAmount + $newTaxAmount;
  414. $qAddress->setSubtotalInclTax($subtotalInclTax);
  415. foreach ($quote->getAllVisibleItems() as $item) {
  416. if ($item->getParentItem()) {
  417. continue;
  418. }
  419. if ($item->getTaxAmount()) {
  420. $item->setTaxAmount($item->getTaxAmount()*$taxQuotient);
  421. $item->setBaseTaxAmount($item->getBaseTaxAmount()*$taxQuotient);
  422. $taxPercent = round(($item->getTaxAmount()/$item->getRowTotal())*100);
  423. $item->setTaxPercent($taxPercent);
  424. }
  425. }
  426. $grandTotal = $quote->getBaseGrandTotal() - $qTaxAmount + $newTaxAmount;
  427. $quote->setGrandTotal(
  428. $this->_reCalculateToStoreCurrency($grandTotal, $quote)
  429. );
  430. $quote->setBaseGrandTotal($grandTotal);
  431. $message = $this->__('The tax amount has been applied based on the information received from Google Checkout, because tax amount received from Google Checkout is different from the calculated tax amount');
  432. return $message;
  433. }
  434. return false;
  435. }
  436. /**
  437. * Import address data from google request to address object
  438. *
  439. * @param array | Varien_Object $gAddress
  440. * @param Varien_Object $qAddress
  441. * @return Varien_Object
  442. */
  443. protected function _importGoogleAddress($gAddress, Varien_Object $qAddress=null)
  444. {
  445. if (is_array($gAddress)) {
  446. $gAddress = new Varien_Object($gAddress);
  447. }
  448. if (!$qAddress) {
  449. $qAddress = Mage::getModel('sales/quote_address');
  450. }
  451. $nameArr = $gAddress->getData('structured-name');
  452. if ($nameArr) {
  453. $qAddress->setFirstname($nameArr['first-name']['VALUE'])
  454. ->setLastname($nameArr['last-name']['VALUE']);
  455. } else {
  456. $nameArr = explode(' ', $gAddress->getData('contact-name/VALUE'), 2);
  457. $qAddress->setFirstname($nameArr[0]);
  458. if (!empty($nameArr[1])) {
  459. $qAddress->setLastname($nameArr[1]);
  460. }
  461. }
  462. $region = Mage::getModel('directory/region')->loadByCode(
  463. $gAddress->getData('region/VALUE'),
  464. $gAddress->getData('country-code/VALUE')
  465. );
  466. $qAddress->setCompany($gAddress->getData('company-name/VALUE'))
  467. ->setEmail($gAddress->getData('email/VALUE'))
  468. ->setStreet(trim($gAddress->getData('address1/VALUE') . "\n" . $gAddress->getData('address2/VALUE')))
  469. ->setCity($gAddress->getData('city/VALUE'))
  470. ->setRegion($gAddress->getData('region/VALUE'))
  471. ->setRegionId($region->getId())
  472. ->setPostcode($gAddress->getData('postal-code/VALUE'))
  473. ->setCountryId($gAddress->getData('country-code/VALUE'))
  474. ->setTelephone($gAddress->getData('phone/VALUE'))
  475. ->setFax($gAddress->getData('fax/VALUE'));
  476. return $qAddress;
  477. }
  478. /**
  479. * Returns array of possible shipping methods combinations
  480. * Includes internal GoogleCheckout shipping methods, that can be created
  481. * after successful Google Checkout
  482. *
  483. * @return array
  484. */
  485. protected function _getShippingInfos($storeId = null)
  486. {
  487. $cacheKey = ($storeId === null) ? 'nofilter' : $storeId;
  488. if (!isset($this->_cachedShippingInfo[$cacheKey])) {
  489. /* @var $shipping Mage_Shipping_Model_Shipping */
  490. $shipping = Mage::getModel('shipping/shipping');
  491. $carriers = Mage::getStoreConfig('carriers', $storeId);
  492. $infos = array();
  493. foreach (array_keys($carriers) as $carrierCode) {
  494. $carrier = $shipping->getCarrierByCode($carrierCode);
  495. if (!$carrier) {
  496. continue;
  497. }
  498. if ($carrierCode == 'googlecheckout') {
  499. // Add info about internal google checkout methods
  500. $methods = array_merge($carrier->getAllowedMethods(), $carrier->getInternallyAllowedMethods());
  501. $carrierName = 'Google Checkout';
  502. } else {
  503. $methods = $carrier->getAllowedMethods();
  504. $carrierName = Mage::getStoreConfig('carriers/' . $carrierCode . '/title', $storeId);
  505. }
  506. foreach ($methods as $methodCode => $methodName) {
  507. $code = $carrierCode . '_' . $methodCode;
  508. $name = sprintf('%s - %s', $carrierName, $methodName);
  509. $infos[$code] = array(
  510. 'code' => $code,
  511. 'name' => $name, // Internal name for google checkout api - to distinguish it in google requests
  512. 'carrier' => $carrierCode,
  513. 'carrier_title' => $carrierName,
  514. 'method' => $methodCode,
  515. 'method_title' => $methodName
  516. );
  517. }
  518. }
  519. $this->_cachedShippingInfo[$cacheKey] = $infos;
  520. }
  521. return $this->_cachedShippingInfo[$cacheKey];
  522. }
  523. /**
  524. * Return shipping method code by shipping method name received from Google
  525. *
  526. * @param string $name
  527. * @param int|string|Mage_Core_Model_Store $storeId
  528. * @return string|false
  529. */
  530. protected function _getShippingMethodByName($name, $storeId = null)
  531. {
  532. $code = false;
  533. $infos = $this->_getShippingInfos($storeId);
  534. foreach ($infos as $info) {
  535. if ($info['name'] == $name) {
  536. $code = $info['code'];
  537. break;
  538. }
  539. }
  540. return $code;
  541. }
  542. /**
  543. * Creates rate by method code
  544. * Sets shipping rate's accurate description, titles and so on,
  545. * so it will get in order description properly
  546. *
  547. * @param string $code
  548. * @return Mage_Sales_Model_Quote_Address_Rate
  549. */
  550. protected function _createShippingRate($code, $storeId = null)
  551. {
  552. $rate = Mage::getModel('sales/quote_address_rate')
  553. ->setCode($code);
  554. $infos = $this->_getShippingInfos($storeId);
  555. if (isset($infos[$code])) {
  556. $info = $infos[$code];
  557. $rate->setCarrier($info['carrier'])
  558. ->setCarrierTitle($info['carrier_title'])
  559. ->setMethod($info['method'])
  560. ->setMethodTitle($info['method_title']);
  561. }
  562. return $rate;
  563. }
  564. /**
  565. * Import totals information from google request to quote address
  566. *
  567. * @param Varien_Object $qAddress
  568. */
  569. protected function _importGoogleTotals($qAddress)
  570. {
  571. $quote = $qAddress->getQuote();
  572. $qAddress->setTaxAmount(
  573. $this->_reCalculateToStoreCurrency($this->getData('root/order-adjustment/total-tax/VALUE'), $quote)
  574. );
  575. $qAddress->setBaseTaxAmount($this->getData('root/order-adjustment/total-tax/VALUE'));
  576. $method = null;
  577. $prefix = 'root/order-adjustment/shipping/';
  578. if (null !== ($shipping = $this->getData($prefix . 'carrier-calculated-shipping-adjustment'))) {
  579. $method = 'googlecheckout_carrier';
  580. } else if (null !== ($shipping = $this->getData($prefix . 'merchant-calculated-shipping-adjustment'))) {
  581. $method = 'googlecheckout_merchant';
  582. } else if (null !== ($shipping = $this->getData($prefix . 'flat-rate-shipping-adjustment'))) {
  583. $method = 'googlecheckout_flatrate';
  584. } else if (null !== ($shipping = $this->getData($prefix . 'pickup-shipping-adjustment'))) {
  585. $method = 'googlecheckout_pickup';
  586. }
  587. if ($method) {
  588. Mage::getSingleton('tax/config')->setShippingPriceIncludeTax(false);
  589. $rate = $this->_createShippingRate($method)
  590. ->setMethodTitle($shipping['shipping-name']['VALUE'])
  591. ->setPrice($shipping['shipping-cost']['VALUE']);
  592. $qAddress->addShippingRate($rate)
  593. ->setShippingMethod($method)
  594. ->setShippingDescription($shipping['shipping-name']['VALUE']);
  595. // We get from Google price with discounts applied via merchant calculations
  596. $qAddress->setShippingAmountForDiscount(0);
  597. /*if (!Mage::helper('tax')->shippingPriceIncludesTax($quote->getStore())) {
  598. $includingTax = Mage::helper('tax')->getShippingPrice(
  599. $excludingTax, true, $qAddress, $quote->getCustomerTaxClassId()
  600. );
  601. $shippingTax = $includingTax - $excludingTax;
  602. $qAddress->setShippingTaxAmount($this->_reCalculateToStoreCurrency($shippingTax, $quote))
  603. ->setBaseShippingTaxAmount($shippingTax)
  604. ->setShippingInclTax($includingTax)
  605. ->setBaseShippingInclTax($this->_reCalculateToStoreCurrency($includingTax, $quote));
  606. } else {
  607. if ($method == 'googlecheckout_carrier') {
  608. $qAddress->setShippingTaxAmount(0)
  609. ->setBaseShippingTaxAmount(0);
  610. }
  611. }*/
  612. } else {
  613. $qAddress->setShippingMethod(null);
  614. }
  615. $qAddress->setGrandTotal(
  616. $this->_reCalculateToStoreCurrency($this->getData('root/order-total/VALUE'), $quote)
  617. );
  618. $qAddress->setBaseGrandTotal($this->getData('root/order-total/VALUE'));
  619. }
  620. /**
  621. * Order getter
  622. *
  623. * @return Mage_Sales_Model_Order
  624. */
  625. public function getOrder()
  626. {
  627. if (!$this->hasData('order')) {
  628. $order = Mage::getModel('sales/order')
  629. ->loadByAttribute('ext_order_id', $this->getGoogleOrderNumber());
  630. if (!$order->getId()) {
  631. Mage::throwException('Invalid Order: ' . $this->getGoogleOrderNumber());
  632. }
  633. $this->setData('order', $order);
  634. }
  635. return $this->getData('order');
  636. }
  637. protected function _responseRiskInformationNotification()
  638. {
  639. $this->getGResponse()->SendAck();
  640. $order = $this->getOrder();
  641. $payment = $order->getPayment();
  642. $order
  643. ->setRemoteIp($this->getData('root/risk-information/ip-address/VALUE'));
  644. $payment
  645. ->setCcLast4($this->getData('root/risk-information/partial-cc-number/VALUE'))
  646. ->setCcAvsStatus($this->getData('root/risk-information/avs-response/VALUE'))
  647. ->setCcCidStatus($this->getData('root/risk-information/cvn-response/VALUE'));
  648. $msg = $this->__('Google Risk Information:');
  649. $msg .= '<br />' . $this->__('IP Address: %s', '<strong>' . $order->getRemoteIp() . '</strong>');
  650. $msg .= '<br />' . $this->__('CC Partial: xxxx-%s', '<strong>' . $payment->getCcLast4() . '</strong>');
  651. $msg .= '<br />' . $this->__('AVS Status: %s', '<strong>' . $payment->getCcAvsStatus() . '</strong>');
  652. $msg .= '<br />' . $this->__('CID Status: %s', '<strong>' . $payment->getCcCidStatus() . '</strong>');
  653. $msg .= '<br />' . $this->__('Eligible for Protection: %s', '<strong>' . ($this->getData('root/risk-information/eligible-for-protection/VALUE')=='true' ? 'Yes' : 'No') . '</strong>');
  654. $msg .= '<br />' . $this->__('Buyer Account Age: %s days', '<strong>' . $this->getData('root/risk-information/buyer-account-age/VALUE') . '</strong>');
  655. $order->addStatusToHistory($order->getStatus(), $msg);
  656. $order->save();
  657. }
  658. /**
  659. * Process authorization notification
  660. */
  661. protected function _responseAuthorizationAmountNotification()
  662. {
  663. $this->getGResponse()->SendAck();
  664. $order = $this->getOrder();
  665. $payment = $order->getPayment();
  666. $payment->setAmountAuthorized($this->getData('root/authorization-amount/VALUE'));
  667. $expDate = $this->getData('root/authorization-expiration-date/VALUE');
  668. $expDate = new Zend_Date($expDate);
  669. $msg = $this->__('Google Authorization:');
  670. $msg .= '<br />' . $this->__('Amount: %s', '<strong>' . $this->_formatAmount($payment->getAmountAuthorized()) . '</strong>');
  671. $msg .= '<br />' . $this->__('Expiration: %s', '<strong>' . $expDate->toString() . '</strong>');
  672. $order->addStatusToHistory($order->getStatus(), $msg);
  673. $order->setPaymentAuthorizationAmount($payment->getAmountAuthorized());
  674. $timestamp = Mage::getModel('core/date')->gmtTimestamp(
  675. $this->getData('root/authorization-expiration-date/VALUE')
  676. );
  677. $order->setPaymentAuthorizationExpiration(
  678. $timestamp ? $timestamp : Mage::getModel('core/date')->gmtTimestamp()
  679. );
  680. $order->save();
  681. }
  682. /**
  683. * Process charge notification
  684. *
  685. */
  686. protected function _responseChargeAmountNotification()
  687. {
  688. $this->getGResponse()->SendAck();
  689. $order = $this->getOrder();
  690. $payment = $order->getPayment();
  691. $latestCharged = $this->getData('root/latest-charge-amount/VALUE');
  692. $totalCharged = $this->getData('root/total-charge-amount/VALUE');
  693. $payment->setAmountCharged($totalCharged);
  694. $order->setIsInProcess(true);
  695. $msg = $this->__('Google Charge:');
  696. $msg .= '<br />' . $this->__('Latest Charge: %s', '<strong>' . $this->_formatAmount($latestCharged) . '</strong>');
  697. $msg .= '<br />' . $this->__('Total Charged: %s', '<strong>' . $this->_formatAmount($totalCharged) . '</strong>');
  698. if (!$order->hasInvoices() && abs($order->getBaseGrandTotal() - $latestCharged) < .0001) {
  699. $invoice = $this->_createInvoice();
  700. $msg .= '<br />' . $this->__('Invoice Auto-Created: %s', '<strong>' . $invoice->getIncrementId() . '</strong>');
  701. }
  702. $this->_addChildTransaction(Mage_Sales_Model_Order_Payment_Transaction::TYPE_CAPTURE);
  703. $open = Mage_Sales_Model_Order_Invoice::STATE_OPEN;
  704. foreach ($order->getInvoiceCollection() as $orderInvoice) {
  705. if ($orderInvoice->getState() == $open && $orderInvoice->getBaseGrandTotal() == $latestCharged) {
  706. $orderInvoice->setState(Mage_Sales_Model_Order_Invoice::STATE_PAID)
  707. ->setTransactionId($this->getGoogleOrderNumber())
  708. ->save();
  709. break;
  710. }
  711. }
  712. $order->addStatusToHistory($order->getStatus(), $msg);
  713. $order->save();
  714. }
  715. protected function _createInvoice()
  716. {
  717. $order = $this->getOrder();
  718. $invoice = $order->prepareInvoice()
  719. ->setTransactionId($this->getGoogleOrderNumber())
  720. ->addComment(Mage::helper('googlecheckout')->__('Auto-generated from GoogleCheckout Charge'))
  721. ->register()
  722. ->pay();
  723. $transactionSave = Mage::getModel('core/resource_transaction')
  724. ->addObject($invoice)
  725. ->addObject($invoice->getOrder());
  726. $transactionSave->save();
  727. return $invoice;
  728. }
  729. protected function _createShipment()
  730. {
  731. $order = $this->getOrder();
  732. $shipment = $order->prepareShipment();
  733. if ($shipment) {
  734. $shipment->register();
  735. $order->setIsInProcess(true);
  736. $transactionSave = Mage::getModel('core/resource_transaction')
  737. ->addObject($shipment)
  738. ->addObject($shipment->getOrder())
  739. ->save();
  740. }
  741. return $shipment;
  742. }
  743. /**
  744. * Process chargeback notification
  745. */
  746. protected function _responseChargebackAmountNotification()
  747. {
  748. $this->getGResponse()->SendAck();
  749. $latestChargeback = $this->getData('root/latest-chargeback-amount/VALUE');
  750. $totalChargeback = $this->getData('root/total-chargeback-amount/VALUE');
  751. $order = $this->getOrder();
  752. if ($order->getBaseGrandTotal() == $totalChargeback) {
  753. $creditmemo = Mage::getModel('sales/service_order', $order)
  754. ->prepareCreditmemo()
  755. ->setPaymentRefundDisallowed(true)
  756. ->setAutomaticallyCreated(true)
  757. ->register();
  758. $creditmemo->addComment($this->__('Credit memo has been created automatically'));
  759. $creditmemo->save();
  760. }
  761. $msg = $this->__('Google Chargeback:');
  762. $msg .= '<br />' . $this->__('Latest Chargeback: %s', '<strong>' . $this->_formatAmount($latestChargeback) . '</strong>');
  763. $msg .= '<br />' . $this->__('Total Chargeback: %s', '<strong>' . $this->_formatAmount($totalChargeback) . '</strong>');
  764. $this->_addChildTransaction(Mage_Sales_Model_Order_Payment_Transaction::TYPE_REFUND,
  765. Mage_Sales_Model_Order_Payment_Transaction::TYPE_CAPTURE);
  766. $order->addStatusToHistory($order->getStatus(), $msg);
  767. $order->save();
  768. }
  769. /**
  770. * Process refund notification
  771. */
  772. protected function _responseRefundAmountNotification()
  773. {
  774. $this->getGResponse()->SendAck();
  775. $latestRefunded = $this->getData('root/latest-refund-amount/VALUE');
  776. $totalRefunded = $this->getData('root/total-refund-amount/VALUE');
  777. $order = $this->getOrder();
  778. $amountRefundLeft = $order->getBaseGrandTotal() - $order->getBaseTotalRefunded()
  779. - $order->getBaseAdjustmentNegative();
  780. if (abs($amountRefundLeft) < .0001) {
  781. return;
  782. }
  783. if ($amountRefundLeft < $latestRefunded) {
  784. $latestRefunded = $amountRefundLeft;
  785. $totalRefunded = $order->getBaseGrandTotal();
  786. }
  787. if ($order->getBaseTotalRefunded() > 0) {
  788. $adjustment = array('adjustment_positive' => $latestRefunded);
  789. } else {
  790. $adjustment = array('adjustment_negative' => $order->getBaseGrandTotal() - $latestRefunded);
  791. }
  792. $creditmemo = Mage::getModel('sales/service_order', $order)
  793. ->prepareCreditmemo($adjustment)
  794. ->setPaymentRefundDisallowed(true)
  795. ->setAutomaticallyCreated(true)
  796. ->register()
  797. ->addComment($this->__('Credit memo has been created automatically'))
  798. ->save();
  799. $msg = $this->__('Google Refund:');
  800. $msg .= '<br />' . $this->__('Latest Refund: %s', '<strong>' . $this->_formatAmount($latestRefunded) . '</strong>');
  801. $msg .= '<br />' . $this->__('Total Refunded: %s', '<strong>' . $this->_formatAmount($totalRefunded) . '</strong>');
  802. $this->_addChildTransaction(Mage_Sales_Model_Order_Payment_Transaction::TYPE_REFUND,
  803. Mage_Sales_Model_Order_Payment_Transaction::TYPE_CAPTURE);
  804. $order->addStatusToHistory($order->getStatus(), $msg);
  805. $order->save();
  806. }
  807. protected function _responseOrderStateChangeNotification()
  808. {
  809. $this->getGResponse()->SendAck();
  810. $prevFinancial = $this->getData('root/previous-financial-order-state/VALUE');
  811. $newFinancial = $this->getData('root/new-financial-order-state/VALUE');
  812. $prevFulfillment = $this->getData('root/previous-fulfillment-order-state/VALUE');
  813. $newFulfillment = $this->getData('root/new-fulfillment-order-state/VALUE');
  814. $msg = $this->__('Google Order Status Change:');
  815. if ($prevFinancial!=$newFinancial) {
  816. $msg .= "<br />" . $this->__('Financial: %s -> %s', '<strong>' . $prevFinancial . '</strong>', '<strong>' . $newFinancial . '</strong>');
  817. }
  818. if ($prevFulfillment!=$newFulfillment) {
  819. $msg .= "<br />" . $this->__('Fulfillment: %s -> %s', '<strong>' . $prevFulfillment . '</strong>', '<strong>' . $newFulfillment . '</strong>');
  820. }
  821. $this->getOrder()
  822. ->addStatusToHistory($this->getOrder()->getStatus(), $msg)
  823. ->save();
  824. $method = '_orderStateChangeFinancial' . uc_words(strtolower($newFinancial), '', '_');
  825. if (method_exists($this, $method)) {
  826. $this->$method();
  827. }
  828. $method = '_orderStateChangeFulfillment' . uc_words(strtolower($newFulfillment), '', '_');
  829. if (method_exists($this, $method)) {
  830. $this->$method();
  831. }
  832. }
  833. /**
  834. * Add transaction to payment with defined type
  835. *
  836. * @param string $typeTarget
  837. * @param string $typeParent
  838. * @return Mage_GoogleCheckout_Model_Api_Xml_Callback
  839. */
  840. protected function _addChildTransaction(
  841. $typeTarget,
  842. $typeParent = Mage_Sales_Model_Order_Payment_Transaction::TYPE_AUTH)
  843. {
  844. $payment = $this->getOrder()->getPayment();
  845. $googleOrderId = $this->getGoogleOrderNumber();
  846. $parentTransactionId = $googleOrderId;
  847. if ($typeParent != Mage_Sales_Model_Order_Payment_Transaction::TYPE_AUTH) {
  848. $parentTransactionId .= '-' . $typeParent;
  849. } else {
  850. $payment->setIsTransactionClosed(false);
  851. }
  852. $parentTransaction = $payment->getTransaction($parentTransactionId);
  853. if ($parentTransaction) {
  854. $payment->setParentTransactionId($parentTransactionId)
  855. ->setTransactionId($googleOrderId . '-' . $typeTarget)
  856. ->addTransaction($typeTarget);
  857. if ($this->getOrder()->getTotalDue() < .0001) {
  858. $parentTransaction->setIsClosed(true)
  859. ->save();
  860. }
  861. }
  862. return $this;
  863. }
  864. protected function _orderStateChangeFinancialReviewing()
  865. {
  866. }
  867. protected function _orderStateChangeFinancialChargeable()
  868. {
  869. #$this->getGRequest()->SendProcessOrder($this->getGoogleOrderNumber());
  870. #$this->getGRequest()->SendChargeOrder($this->getGoogleOrderNumber(), '');
  871. }
  872. protected function _orderStateChangeFinancialCharging()
  873. {
  874. }
  875. protected function _orderStateChangeFinancialCharged()
  876. {
  877. }
  878. protected function _orderStateChangeFinancialPaymentDeclined()
  879. {
  880. }
  881. protected function _orderStateChangeFinancialCancelled()
  882. {
  883. $this->getOrder()->setBeingCanceledFromGoogleApi(true)->cancel()->save();
  884. }
  885. protected function _orderStateChangeFinancialCancelledByGoogle()
  886. {
  887. $this
  888. ->getOrder()
  889. ->setBeingCanceledFromGoogleApi(true)
  890. ->cancel()
  891. ->save();
  892. $this
  893. ->getGRequest()
  894. ->SendBuyerMessage($this->getGoogleOrderNumber(), "Sorry, your order is cancelled by Google", true);
  895. }
  896. protected function _orderStateChangeFulfillmentNew()
  897. {
  898. }
  899. protected function _orderStateChangeFulfillmentProcessing()
  900. {
  901. }
  902. protected function _orderStateChangeFulfillmentDelivered()
  903. {
  904. $shipment = $this->_createShipment();
  905. if (!is_null($shipment))
  906. $shipment->save();
  907. }
  908. protected function _orderStateChangeFulfillmentWillNotDeliver()
  909. {
  910. }
  911. /**
  912. * Format amount to be displayed
  913. *
  914. * @param mixed $amount
  915. * @return string
  916. */
  917. protected function _formatAmount($amount)
  918. {
  919. // format currency in currency format, but don't enclose it into <span>
  920. return Mage::helper('core')->currency($amount, true, false);
  921. }
  922. }