From f3d220fcfc9a8f209e140bff0dba2847d709c665 Mon Sep 17 00:00:00 2001 From: Nanda <29716662+nandadubey@users.noreply.github.com> Date: Wed, 21 Mar 2018 09:36:26 -0700 Subject: [PATCH] Dev 3.5.3 (#36) * Initial commit for modal checkout flow support - Beta feature --- Makefile | 2 +- build/affirm_tar_to_connect_config.php | 8 +- .../community/Affirm/Affirm/Helper/Data.php | 111 ++++++ .../Affirm/Affirm/Model/Order/Observer.php | 152 +++++++-- .../community/Affirm/Affirm/Model/Payment.php | 152 ++++++++- .../Model/Source/PaymentCheckoutFlow.php | 39 +++ .../Affirm/controllers/PaymentController.php | 302 +++++++++++++++- .../community/Affirm/Affirm/etc/config.xml | 38 ++- .../community/Affirm/Affirm/etc/system.xml | 18 +- .../base/default/layout/affirm/affirm.xml | 84 ++++- .../affirm/checkout/review/button.phtml | 73 ++++ .../affirm/affirm/checkout/review/opc.phtml | 321 ++++++++++++++++++ extension/package.xml | 6 +- .../base/default/js/affirm/checkout.js | 136 ++++++++ 14 files changed, 1379 insertions(+), 63 deletions(-) create mode 100644 extension/app/code/community/Affirm/Affirm/Model/Source/PaymentCheckoutFlow.php create mode 100644 extension/app/design/frontend/base/default/template/affirm/affirm/checkout/review/button.phtml create mode 100644 extension/app/design/frontend/base/default/template/affirm/affirm/checkout/review/opc.phtml create mode 100644 extension/skin/frontend/base/default/js/affirm/checkout.js diff --git a/Makefile b/Makefile index e952446..0196f91 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,7 @@ dependencies: package: validate_version mkdir -p ./var/ - cd ./extension && tar -cvf ../var/Affirm_Affirm-3.5.2.tgz * + cd ./extension && tar -cvf ../var/Affirm_Affirm-3.5.3.tgz * cd ./build && ./magento-tar-to-connect.phar affirm_tar_to_connect_config.php clean: diff --git a/build/affirm_tar_to_connect_config.php b/build/affirm_tar_to_connect_config.php index a98d192..2c5e85a 100644 --- a/build/affirm_tar_to_connect_config.php +++ b/build/affirm_tar_to_connect_config.php @@ -1,15 +1,15 @@ realpath('../var/'), -'archive_files' => 'Affirm_Affirm-3.5.2.tgz', +'archive_files' => 'Affirm_Affirm-3.5.3.tgz', 'extension_name' => 'Affirm_Magento', 'skip_version_compare' => true, -'extension_version' => '3.5.2', -'archive_connect' => 'Affirm_Affirm-3.5.2.tgz', +'extension_version' => '3.5.3', +'archive_connect' => 'Affirm_Affirm-3.5.3.tgz', 'path_output' => realpath('../var/'), 'stability' => 'stable', -'license' => 'MIT', +'license' => 'BSD-3-Clause', 'channel' => 'community', 'summary' => 'Affirm provides instant, flexible financing for your customers at checkout', 'description' => 'Affirm provides flexible financing for your customers at checkout to increase sales and conversion. Your customers will be instantly approved for a loan with Affirm during checkout and will use Affirm as their form of payment. They can then pay off their balance in easy monthly payments with a low interest rate. Affirm assumes all credit risk and will settle full payment with you right away. You will need an Affirm account to use this extension. For more information and to sign up for an Affirm account, please visit https://www.affirm.com/merchants/', diff --git a/extension/app/code/community/Affirm/Affirm/Helper/Data.php b/extension/app/code/community/Affirm/Affirm/Helper/Data.php index e46104f..2535267 100644 --- a/extension/app/code/community/Affirm/Affirm/Helper/Data.php +++ b/extension/app/code/community/Affirm/Affirm/Helper/Data.php @@ -56,6 +56,11 @@ class Affirm_Affirm_Helper_Data extends Mage_Core_Helper_Abstract */ const PAYMENT_AFFIRM_DISABLE_BACK_ORDERED_ITEMS = 'payment/affirm/disable_for_backordered_items'; + /** + * Checkout Flow Type + */ + const PAYMENT_AFFIRM_CHECKOUT_FLOW_TYPE = 'payment/affirm/checkout_flow_type'; + /** * Disabled module * @@ -260,6 +265,36 @@ public function isPreOrder($store = null) return Mage::getStoreConfig(self::PAYMENT_AFFIRM_PRE_ORDER, $store); } + /** + * Checkout flow type + * + * @param Mage_Core_Model_Store $store + * @return string + */ + public function getCheckoutFlowType($store = null) + { + if($store == null) { + $store = Mage::app()->getStore()->getStoreId(); + } + return Mage::getStoreConfig(self::PAYMENT_AFFIRM_CHECKOUT_FLOW_TYPE, $store); + } + + /** + * Is Checkout flow type Modal + * + * @param Mage_Core_Model_Store $store + * @return bool + */ + public function isCheckoutFlowTypeModal($store = null) + { + $configCheckoutType = Mage::helper('affirm')->getCheckoutFlowType(); + if ($configCheckoutType == Affirm_Affirm_Model_Payment::CHECKOUT_FLOW_MODAL) { + return true; + } else { + return false; + } + } + /** * Get affirm js url * @@ -279,6 +314,24 @@ public function getAffirmJsUrl() return 'https://' . $prefix . '' . $domain . '/js/v2/affirm.js'; } + /** + * Get affirm js text + * + * @return string + */ + public function getAffirmJs() + { + $affirmJs = ''; + return $affirmJs; + } + /** * Is xhr request * @@ -484,6 +537,64 @@ public function getAffirmAssetsUrl() return 'https://' . $prefix . '.' . $domain . '/' . $assetPath ; } + /** + * Get template for button in order review page if Affirm method was selected and checkout flow type is modal + * + * @param string $name template name + * @param string $block buttons block name + * @return string + */ + public function getReviewButtonTemplate($name, $block) + { + $quote = Mage::getSingleton('checkout/session')->getQuote(); + if ($quote) { + $payment = $quote->getPayment(); + if ($payment && ($payment->getMethod() == Affirm_Affirm_Model_Payment::METHOD_CODE) && $this->isCheckoutFlowTypeModal()) { + return $name; + } + } + + if ($blockObject = Mage::getSingleton('core/layout')->getBlock($block)) { + return $blockObject->getTemplate(); + } + + return ''; + } + + /** + * Get Affirm modal checkout js + * + * @return string + */ + public function getAffirmCheckoutJsScript() + { + if (Mage::helper('affirm')->isCheckoutFlowTypeModal()) { + return 'js/affirm/checkout.js'; + } + return ''; + } + + /** + * Returns a checkout object instance + * + * @return Mage_Checkout_Model_Type_Onepage + */ + public function _getCheckout() + { + return Mage::getSingleton('checkout/type_onepage'); + } + + /** + * Get OPC save order URL + * + * @return string + */ + public function getOPCCheckoutUrl() + { + $paramHttps = (Mage::app()->getStore()->isCurrentlySecure()) ? array('_forced_secure' => true) : array(); + return Mage::getUrl('checkout/onepage/saveOrder/form_key/' . Mage::getSingleton('core/session')->getFormKey(), $paramHttps); + } + public function getAllGroups() { $customerGroups = Mage::getResourceModel('customer/group_collection') diff --git a/extension/app/code/community/Affirm/Affirm/Model/Order/Observer.php b/extension/app/code/community/Affirm/Affirm/Model/Order/Observer.php index 95898c0..a8711cb 100644 --- a/extension/app/code/community/Affirm/Affirm/Model/Order/Observer.php +++ b/extension/app/code/community/Affirm/Affirm/Model/Order/Observer.php @@ -85,45 +85,43 @@ protected function _callToPreOrderActionAndExit($order, $quote) } /** - * Pre order + * Pre order to be executed only for redirect checkout flow type * * @param Varien_Event_Observer $observer */ public function preOrder($observer) { - $order = $observer->getEvent()->getOrder(); - $quote = $observer->getEvent()->getQuote(); - $methodInst = $order->getPayment()->getMethodInstance(); - if (Mage::helper('affirm')->getAffirmTokenCode()) { - $methodInst->setAffirmCheckoutToken(Mage::helper('affirm')->getAffirmTokenCode()); - } - - if ($this->_isCreateOrderAfterConf($methodInst)) { - if (!Mage::helper('affirm')->getAffirmTokenCode()) { - #ok record the current controller that we are using... - $request = Mage::app()->getRequest(); - $orderRequest = array('action' => $request->getActionName(), - 'controller' => $request->getControllerName(), - 'module' => $request->getModuleName(), - 'params' => $request->getParams(), - 'method' => $request->getMethod(), - 'xhr' => $request->isXmlHttpRequest(), - 'POST' => Mage::app()->getRequest()->getPost(), //need post for some cross site issues - 'quote_id' => $quote->getId() - ); - - $orderRequest['routing_info'] = array( - 'requested_route' => $request->getRequestedRouteName(), - 'requested_controller' => $request->getRequestedControllerName(), - 'requested_action' => $request->getRequestedActionName() - ); - - Mage::helper('affirm')->getCheckoutSession()->setAffirmOrderRequest(serialize($orderRequest)); - - $this->_callToPreOrderActionAndExit($order, $quote); + if (!Mage::helper('affirm')->isCheckoutFlowTypeModal()) { + $order = $observer->getEvent()->getOrder(); + $quote = $observer->getEvent()->getQuote(); + $methodInst = $order->getPayment()->getMethodInstance(); + if (Mage::helper('affirm')->getAffirmTokenCode()) { + $methodInst->setAffirmCheckoutToken(Mage::helper('affirm')->getAffirmTokenCode()); + } + if ($this->_isCreateOrderAfterConf($methodInst)) { + if (!Mage::helper('affirm')->getAffirmTokenCode()) { + #ok record the current controller that we are using... + $request = Mage::app()->getRequest(); + $orderRequest = array('action' => $request->getActionName(), + 'controller' => $request->getControllerName(), + 'module' => $request->getModuleName(), + 'params' => $request->getParams(), + 'method' => $request->getMethod(), + 'xhr' => $request->isXmlHttpRequest(), + 'POST' => Mage::app()->getRequest()->getPost(), //need post for some cross site issues + 'quote_id' => $quote->getId() + ); + $orderRequest['routing_info'] = array( + 'requested_route' => $request->getRequestedRouteName(), + 'requested_controller' => $request->getRequestedControllerName(), + 'requested_action' => $request->getRequestedActionName() + ); + Mage::helper('affirm')->getCheckoutSession()->setAffirmOrderRequest(serialize($orderRequest)); + $this->_callToPreOrderActionAndExit($order, $quote); + } + } elseif ($this->_isCreateOrderBeforeConf($methodInst)) { + Mage::helper('affirm')->getCheckoutSession()->setAffirmOrderRequest(null); } - } elseif ($this->_isCreateOrderBeforeConf($methodInst)) { - Mage::helper('affirm')->getCheckoutSession()->setAffirmOrderRequest(null); } } @@ -142,4 +140,92 @@ public function reactivateQuote($observer) $quote->save(); } } + + /** + * Modal checkout Before save order + * + * @param Varien_Event_Observer $observer + * @void + */ + public function preDispatchSaveOrderAction(Varien_Event_Observer $observer) + { + if (Mage::helper('affirm')->isCheckoutFlowTypeModal()) { + /* @var $controller Mage_Core_Controller_Front_Action */ + $controller = $observer->getEvent()->getControllerAction(); + $payment = Mage::helper('affirm')->getCheckoutSession()->getQuote()->getPayment(); + $paymentMethod = $payment->getMethod(); + if($paymentMethod){ + $methodInst = $payment->getMethodInstance(); + } else { + $dataSavePayment = $controller->getRequest()->getPost('payment', array()); + try { + Mage::getSingleton('checkout/type_onepage')->savePayment($dataSavePayment); + $payment = Mage::helper('affirm')->getCheckoutSession()->getQuote()->getPayment(); + $paymentMethod = $payment->getMethod(); + $methodInst = $payment->getMethodInstance(); + } catch (Exception $e) { + $message = $e->getMessage(); + $controller->setFlag('', Mage_Core_Controller_Front_Action::FLAG_NO_DISPATCH, true); + $response = array('error' => -1, 'message' => $message); + $controller->getResponse()->setBody(Mage::helper('core')->jsonEncode($response)); + $controller->getRequest()->setDispatched(true); + return; + } + } + if (!Mage::helper('affirm')->getAffirmTokenCode()) { + $requiredAgreements = Mage::helper('checkout')->getRequiredAgreementIds(); + if ($requiredAgreements && $controller->getRequest()->getPost('agreement', array())) { + $postedAgreements = array_keys($controller->getRequest()->getPost('agreement', array())); + $diff = array_diff($requiredAgreements, $postedAgreements); + if ($diff) { + $result['success'] = false; + $result['error'] = true; + $result['error_messages'] = 'Please agree to all the terms and conditions before placing the order.'; + $controller->setFlag('', Mage_Core_Controller_Front_Action::FLAG_NO_DISPATCH, true); + $controller->getResponse()->setBody( + Mage::helper('core')->jsonEncode($result) + ); + $controller->getRequest()->setDispatched(true); + return; + } + } + + $data = $controller->getRequest()->getPost('payment', array()); + if ($data) { + $data['checks'] = Mage_Payment_Model_Method_Abstract::CHECK_USE_CHECKOUT + | Mage_Payment_Model_Method_Abstract::CHECK_USE_FOR_COUNTRY + | Mage_Payment_Model_Method_Abstract::CHECK_USE_FOR_CURRENCY + | Mage_Payment_Model_Method_Abstract::CHECK_ORDER_TOTAL_MIN_MAX + | Mage_Payment_Model_Method_Abstract::CHECK_ZERO_TOTAL; + Mage::helper('affirm')->getCheckoutSession()->getQuote()->getPayment()->importData($data); + } + + if ($controller->getRequest()->getPost('newsletter')) { + Mage::getSingleton('checkout/session') + ->setNewsletterSubsribed(true) + ->setNewsletterEmail( + Mage::helper('affirm')->getCheckoutSession()->getQuote()->getCustomerEmail() + ); + } + #ok record the current controller that we are using... + $request = Mage::app()->getRequest(); + $post = (Mage::app()->getRequest()->getPost()) ? Mage::app()->getRequest()->getPost() : null; + $orderRequest = array('action' => $request->getActionName(), + 'controller' => $request->getControllerName(), + 'module' => $request->getModuleName(), + 'params' => $request->getParams(), + 'method' => $request->getMethod(), + 'xhr' => $request->isXmlHttpRequest(), + 'POST' => $post, //need post for some cross site issues + 'quote_id' => Mage::helper('affirm')->getCheckoutSession()->getQuote()->getId() + ); + Mage::helper('affirm')->getCheckoutSession()->setAffirmOrderRequest(serialize($orderRequest)); + $controller->setFlag('', Mage_Core_Controller_Front_Action::FLAG_NO_DISPATCH, true); + $controller->getRequest()->setDispatched(true); + return; + } else { + $methodInst->setAffirmCheckoutToken(Mage::helper('affirm')->getAffirmTokenCode()); + } + } + } } diff --git a/extension/app/code/community/Affirm/Affirm/Model/Payment.php b/extension/app/code/community/Affirm/Affirm/Model/Payment.php index b647bc7..a6b8b1b 100644 --- a/extension/app/code/community/Affirm/Affirm/Model/Payment.php +++ b/extension/app/code/community/Affirm/Affirm/Model/Payment.php @@ -29,6 +29,8 @@ class Affirm_Affirm_Model_Payment extends Mage_Payment_Model_Method_Abstract const CHECKOUT_XHR_AUTO = 'auto'; const CHECKOUT_XHR = 'xhr'; const CHECKOUT_REDIRECT = 'redirect'; + const CHECKOUT_FLOW_REDIRECT = 'redirect'; + const CHECKOUT_FLOW_MODAL = 'modal'; /**#@-*/ /** @@ -170,7 +172,6 @@ public function getBaseApiUrl() protected function _apiRequest($method, $path, $data = null, $storeId = null, $resourcePath = self::API_CHARGES_PATH) { $url = trim($this->getBaseApiUrl(), '/') . $resourcePath . $path; - Mage::log($url); $client = new Zend_Http_Client($url); if ($method == Zend_Http_Client::POST && $data) { $json = json_encode($data); @@ -627,6 +628,155 @@ public function getCheckoutObject($order) return $checkout; } + /** + * Get checkout object from quote + * + * @param Mage_Sales_Model_Quote $quote + * @return array + */ + public function getCheckoutQuoteObject($quote) + { + $shippingAddress = $quote->getShippingAddress(); + $shipping = null; + if ($shippingAddress) { + $shipping = array( + 'name' => array('full' => $shippingAddress->getName()), + 'phone_number' => $shippingAddress->getTelephone(), + 'phone_number_alternative' => $shippingAddress->getAltTelephone(), + 'address' => array( + 'line1' => $shippingAddress->getStreet(1), + 'line2' => $shippingAddress->getStreet(2), + 'city' => $shippingAddress->getCity(), + 'state' => $shippingAddress->getRegion(), + 'country' => $shippingAddress->getCountryModel()->getIso2Code(), + 'zipcode' => $shippingAddress->getPostcode(), + )); + } + + $billingAddress = $quote->getBillingAddress(); + $billing = array( + 'name' => array('full' => $billingAddress->getName()), + 'email' => $quote->getCustomerEmail(), + 'phone_number' => $billingAddress->getTelephone(), + 'phone_number_alternative' => $billingAddress->getAltTelephone(), + 'address' => array( + 'line1' => $billingAddress->getStreet(1), + 'line2' => $billingAddress->getStreet(2), + 'city' => $billingAddress->getCity(), + 'state' => $billingAddress->getRegion(), + 'country' => $billingAddress->getCountryModel()->getIso2Code(), + 'zipcode' => $billingAddress->getPostcode(), + )); + + $items = array(); + $productIds = array(); + $productItemsMFP = array(); + $categoryItemsIds = array(); + foreach ($quote->getAllVisibleItems() as $orderItem) { + $productIds[] = $orderItem->getProductId(); + } + $products = Mage::getModel('catalog/product')->getCollection() + ->addAttributeToSelect( + array('affirm_product_mfp', 'affirm_product_mfp_type', 'affirm_product_mfp_priority') + ) + ->addAttributeToFilter('entity_id', array('in' => $productIds)); + $productItems = $products->getItems(); + foreach ($quote->getAllVisibleItems() as $orderItem) { + $product = $productItems[$orderItem->getProductId()]; + if (Mage::helper('affirm')->isPreOrder() && $orderItem->getParentItem() && + ($orderItem->getParentItem()->getProductType() == Mage_Catalog_Model_Product_Type::TYPE_CONFIGURABLE) + ) { + continue; + } + $items[] = array( + 'sku' => $orderItem->getSku(), + 'display_name' => $orderItem->getName(), + 'item_url' => $product->getProductUrl(), + 'item_image_url' => $product->getImageUrl(), + 'qty' => intval($orderItem->getQtyOrdered()), + 'unit_price' => Mage::helper('affirm/util')->formatCents($orderItem->getPrice()) + ); + + $start_date = $product->getAffirmProductMfpStartDate(); + $end_date = $product->getAffirmProductMfpEndDate(); + if(empty($start_date) || empty($end_date)) { + $mfpValue = $product->getAffirmProductMfp(); + } else { + if(Mage::app()->getLocale()->isStoreDateInInterval(null, $start_date, $end_date)) { + $mfpValue = $product->getAffirmProductMfp(); + } else { + $mfpValue = ""; + } + } + + $productItemsMFP[] = array( + 'value' => $mfpValue, + 'type' => $product->getAffirmProductMfpType(), + 'priority' => $product->getAffirmProductMfpPriority() ? + $product->getAffirmProductMfpPriority() : 0 + ); + + $categoryIds = $product->getCategoryIds(); + if (!empty($categoryIds)) { + $categoryItemsIds = array_merge($categoryItemsIds, $categoryIds); + } + } + + $checkout = array( + 'checkout_id' => $quote->getId(), + 'currency' => $quote->getQuoteCurrencyCode(), + 'shipping_amount' => Mage::helper('affirm/util')->formatCents($quote->getShippingAddress()->getShippingAmount()), + 'shipping_type' => $quote->getShippingAddress()->getShippingMethod(), + 'tax_amount' => Mage::helper('affirm/util')->formatCents($quote->getShippingAddress()->getTaxAmount()), + 'merchant' => array( + 'public_api_key' => Mage::helper('affirm')->getApiKey(), + 'user_confirmation_url' => Mage::getUrl('affirm/payment/confirm', array('_secure' => true)), + 'user_cancel_url' => Mage::helper('checkout/url')->getCheckoutUrl(), + 'user_confirmation_url_action' => 'POST', + 'charge_declined_url' => Mage::helper('checkout/url')->getCheckoutUrl() + ), + 'config' => array('required_billing_fields' => 'name,address,email'), + 'items' => $items, + 'billing' => $billing + ); + // By convention, Affirm expects positive value for discount amount. Magento provides negative. + $discountAmtAffirm = (-1) * $quote->getDiscountAmount(); + if ($discountAmtAffirm > 0.001) { + $discountCode = $this->_getDiscountCode($quote); + $checkout['discounts'] = array( + $discountCode => array( + 'discount_amount' => Mage::helper('affirm/util')->formatCents($discountAmtAffirm) + ) + ); + } + + if ($shipping) { + $checkout['shipping'] = $shipping; + } + $checkout['total'] = Mage::helper('affirm/util')->formatCents($quote->getGrandTotal()); + if (method_exists('Mage', 'getEdition')){ + $platform_edition = Mage::getEdition(); + } + $platform_version = Mage::getVersion(); + $platform_version_edition = isset($platform_edition) ? $platform_version.' '.$platform_edition : $platform_version; + $checkout['metadata'] = array( + 'shipping_type' => $quote->getShippingAddress()->getShippingDescription(), + 'platform_type' => 'Magento', + 'platform_version' => $platform_version_edition, + 'platform_affirm' => Mage::helper('affirm')->getExtensionVersion(), + 'mode' => 'modal' + ); + $affirmMFPValue = Mage::helper('affirm/mfp')->getAffirmMFPValue($productItemsMFP, $categoryItemsIds, $quote->getBaseGrandTotal()); + if ($affirmMFPValue) { + $checkout['financing_program'] = $affirmMFPValue; + } + + $checkoutObject = new Varien_Object($checkout); + Mage::dispatchEvent('affirm_get_checkout_object_after', array('checkout_object' => $checkoutObject)); + $checkout = $checkoutObject->getData(); + return $checkout; + } + /** * Get discount code * diff --git a/extension/app/code/community/Affirm/Affirm/Model/Source/PaymentCheckoutFlow.php b/extension/app/code/community/Affirm/Affirm/Model/Source/PaymentCheckoutFlow.php new file mode 100644 index 0000000..4359f15 --- /dev/null +++ b/extension/app/code/community/Affirm/Affirm/Model/Source/PaymentCheckoutFlow.php @@ -0,0 +1,39 @@ + Affirm_Affirm_Model_Payment::CHECKOUT_FLOW_REDIRECT, + 'label' => Mage::helper('affirm')->__('Checkout flow using Redirect') + ), + array( + 'value' => Affirm_Affirm_Model_Payment::CHECKOUT_FLOW_MODAL, + 'label' => Mage::helper('affirm')->__('Checkout flow using Modal (Beta)') + ) + ); + } +} + diff --git a/extension/app/code/community/Affirm/Affirm/controllers/PaymentController.php b/extension/app/code/community/Affirm/Affirm/controllers/PaymentController.php index bddc2fb..6e4be63 100644 --- a/extension/app/code/community/Affirm/Affirm/controllers/PaymentController.php +++ b/extension/app/code/community/Affirm/Affirm/controllers/PaymentController.php @@ -54,12 +54,10 @@ public function renderPreOrderAction() ->setOrder($order)->toHtml(); $serializedRequest = $checkoutSession->getAffirmOrderRequest(); $proxyRequest = unserialize($serializedRequest); - //only reserve this order id $modQuote = Mage::getModel('sales/quote')->load($quote->getId()); $modQuote->setReservedOrderId($order->getIncrementId()); $modQuote->save(); - if (Mage::helper('affirm')->isXhrRequest($proxyRequest)) { $checkoutSession->setPreOrderRender($string); $result = array('redirect' => Mage::getUrl('affirm/payment/redirectPreOrder', @@ -96,16 +94,48 @@ protected function _isPlaceOrderAfterConf($serializedRequest, $checkoutToken) */ public function confirmAction() { - $serializedRequest = Mage::helper('affirm')->getCheckoutSession()->getAffirmOrderRequest(); $checkoutToken = $this->getRequest()->getParam('checkout_token'); - - if ($this->_isPlaceOrderAfterConf($serializedRequest, $checkoutToken)) { - $this->_processConfWithSaveOrder($checkoutToken, $serializedRequest); + $checkoutSession = Mage::helper('affirm')->getCheckoutSession(); + if (!$checkoutToken) { + $checkoutSession->addError($this->__('Confirm has no checkout token.')); + $this->getResponse()->setRedirect(Mage::getUrl('checkout/cart'))->sendResponse(); + return; + } + $serializedRequest = $checkoutSession->getAffirmOrderRequest(); + if (Mage::helper('affirm')->isCheckoutFlowTypeModal()) { + $this->_processConfWithSaveOrderModalCheckout($checkoutToken, $serializedRequest); } else { - $this->_processConfWithoutSaveOrder($checkoutToken); + + if ($this->_isPlaceOrderAfterConf($serializedRequest, $checkoutToken)) { + $this->_processConfWithSaveOrder($checkoutToken, $serializedRequest); + } else { + $this->_processConfWithoutSaveOrder($checkoutToken); + } } } + /** + * Process conf with save order for modal checkout + * + * @param string $checkoutToken + * @param string $serializedRequest + */ + protected function _processConfWithSaveOrderModalCheckout($checkoutToken, $serializedRequest) + { + $checkoutSession = Mage::helper('affirm')->getCheckoutSession(); + if ($checkoutSession->getLastAffirmSuccess() == $checkoutToken) { + $checkoutSession->addSuccess($this->__('This order was already completed.')); + //Go directly to success page if this is already successful + $this->_redirect('checkout/onepage/success'); + return; + } + + $proxyRequest = unserialize($serializedRequest); + $this->getRequest()->setPost($proxyRequest['POST']); + Mage::register('affirm_token_code', $checkoutToken); + $this->_forward($proxyRequest['action'], $proxyRequest['controller'], $proxyRequest['module'], $proxyRequest['params']); + } + /** * Process conf with save order * @@ -180,4 +210,262 @@ public function setPaymentFlagAndCheckoutAction() Mage::helper('affirm')->getCheckoutSession()->setAffirmPaymentFlag(true); $this->_redirectUrl(Mage::helper('checkout/url')->getCheckoutUrl()); } + + /** + * Get checkout object from quote + * + * @return array + */ + public function processCheckoutQuoteObjectAction() + { + $post = $this->getRequest()->getPost(); + $checkoutSession = Mage::helper('affirm')->getCheckoutSession(); + $checkoutSession->setCartWasUpdated(false); + if($post) { + $billingData = $this->getRequest()->getPost('billing', array()); + $result = $this->createAccountWhenCheckout($billingData); + if ($result['success']) { + $updatedSection = $this->getRequest()->getPost('updated_section', null); + switch ($updatedSection) { + case "billing": + $this->_saveBilling(); + break; + case "shipping": + $this->_saveShipping(); + break; + case "shipping_method": + $this->_saveShippingMethod(); + break; + case "payment_method": + $this->_savePaymentMethod(); + break; + default: + $this->_saveBilling(); + $this->_saveShippingMethod(); + $this->_savePaymentMethod(); + break; + } + } else { + $result['success'] = false; + $this->getResponse()->setBody(Mage::helper('core')->jsonEncode($result)); + return; + } + } + $this->getOnepage()->getQuote()->setTotalsCollectedFlag(false); + $quote = $checkoutSession->getQuote(); + $shippingAddress = $quote->getShippingAddress(); + $shipping = null; + if ($shippingAddress) { + $shipping = array( + 'name' => array('full' => $shippingAddress->getName()), + 'phone_number' => $shippingAddress->getTelephone(), + 'phone_number_alternative' => $shippingAddress->getAltTelephone(), + 'address' => array( + 'line1' => $shippingAddress->getStreet(1), + 'line2' => $shippingAddress->getStreet(2), + 'city' => $shippingAddress->getCity(), + 'state' => $shippingAddress->getRegion(), + 'country' => $shippingAddress->getCountryModel()->getIso2Code(), + 'zipcode' => $shippingAddress->getPostcode(), + )); + } + $billingAddress = $quote->getBillingAddress(); + $billing = array( + 'name' => array('full' => $billingAddress->getName()), + 'email' => $quote->getCustomerEmail(), + 'phone_number' => $billingAddress->getTelephone(), + 'phone_number_alternative' => $billingAddress->getAltTelephone(), + 'address' => array( + 'line1' => $billingAddress->getStreet(1), + 'line2' => $billingAddress->getStreet(2), + 'city' => $billingAddress->getCity(), + 'state' => $billingAddress->getRegion(), + 'country' => $billingAddress->getCountryModel()->getIso2Code(), + 'zipcode' => $billingAddress->getPostcode(), + )); + + $items = array(); + $productIds = array(); + $productItemsMFP = array(); + $categoryItemsIds = array(); + foreach ($quote->getAllVisibleItems() as $orderItem) { + $productIds[] = $orderItem->getProductId(); + } + $products = Mage::getModel('catalog/product')->getCollection() + ->addAttributeToSelect( + array('affirm_product_mfp', 'affirm_product_mfp_type', 'affirm_product_mfp_priority') + ) + ->addAttributeToFilter('entity_id', array('in' => $productIds)); + $productItems = $products->getItems(); + foreach ($quote->getAllVisibleItems() as $orderItem) { + $product = $productItems[$orderItem->getProductId()]; + if (Mage::helper('affirm')->isPreOrder() && $orderItem->getParentItem() && + ($orderItem->getParentItem()->getProductType() == Mage_Catalog_Model_Product_Type::TYPE_CONFIGURABLE) + ) { + continue; + } + $items[] = array( + 'sku' => $orderItem->getSku(), + 'display_name' => $orderItem->getName(), + 'item_url' => $product->getProductUrl(), + 'item_image_url' => $product->getImageUrl(), + 'qty' => intval($orderItem->getQtyOrdered()), + 'unit_price' => Mage::helper('affirm/util')->formatCents($orderItem->getPrice()) + ); + + $start_date = $product->getAffirmProductMfpStartDate(); + $end_date = $product->getAffirmProductMfpEndDate(); + if(empty($start_date) || empty($end_date)) { + $mfpValue = $product->getAffirmProductMfp(); + } else { + if(Mage::app()->getLocale()->isStoreDateInInterval(null, $start_date, $end_date)) { + $mfpValue = $product->getAffirmProductMfp(); + } else { + $mfpValue = ""; + } + } + + $productItemsMFP[] = array( + 'value' => $mfpValue, + 'type' => $product->getAffirmProductMfpType(), + 'priority' => $product->getAffirmProductMfpPriority() ? + $product->getAffirmProductMfpPriority() : 0 + ); + + $categoryIds = $product->getCategoryIds(); + if (!empty($categoryIds)) { + $categoryItemsIds = array_merge($categoryItemsIds, $categoryIds); + } + } + $checkout = array( + 'checkout_id' => $quote->getId(), + 'currency' => $quote->getQuoteCurrencyCode(), + 'shipping_amount' => Mage::helper('affirm/util')->formatCents($quote->getShippingAddress()->getShippingAmount()), + 'shipping_type' => $quote->getShippingAddress()->getShippingMethod(), + 'tax_amount' => Mage::helper('affirm/util')->formatCents($quote->getShippingAddress()->getTaxAmount()), + 'merchant' => array( + 'public_api_key' => Mage::helper('affirm')->getApiKey(), + 'user_confirmation_url' => Mage::getUrl('affirm/payment/confirm', array('_secure' => true)), + 'user_cancel_url' => Mage::helper('checkout/url')->getCheckoutUrl(), + 'user_confirmation_url_action' => 'POST', + 'charge_declined_url' => Mage::helper('checkout/url')->getCheckoutUrl() + ), + 'config' => array('required_billing_fields' => 'name,address,email'), + 'items' => $items, + 'billing' => $billing + ); + // By convention, Affirm expects positive value for discount amount. Magento provides negative. + $discountAmtAffirm = (-1) * $quote->getDiscountAmount(); + if ($discountAmtAffirm > 0.001) { + $discountCode = $this->_getDiscountCode($quote); + $checkout['discounts'] = array( + $discountCode => array( + 'discount_amount' => Mage::helper('affirm/util')->formatCents($discountAmtAffirm) + ) + ); + } + + if ($shipping) { + $checkout['shipping'] = $shipping; + } + $checkout['total'] = Mage::helper('affirm/util')->formatCents($quote->getGrandTotal()); + if (method_exists('Mage', 'getEdition')){ + $platform_edition = Mage::getEdition(); + } + $platform_version = Mage::getVersion(); + $platform_version_edition = isset($platform_edition) ? $platform_version.' '.$platform_edition : $platform_version; + $checkout['metadata'] = array( + 'shipping_type' => $quote->getShippingAddress()->getShippingDescription(), + 'platform_type' => 'Magento', + 'platform_version' => $platform_version_edition, + 'platform_affirm' => Mage::helper('affirm')->getExtensionVersion(), + 'mode' => 'modal' + ); + $affirmMFPValue = Mage::helper('affirm/mfp')->getAffirmMFPValue($productItemsMFP, $categoryItemsIds, $quote->getBaseGrandTotal()); + if ($affirmMFPValue) { + $checkout['financing_program'] = $affirmMFPValue; + } + + $checkoutObject = new Varien_Object($checkout); + Mage::dispatchEvent('affirm_get_checkout_object_after', array('checkout_object' => $checkoutObject)); + $checkout = $checkoutObject->getData(); + $result = array( + 'success' => true, + 'error' => false, + 'checkout' => $checkout + ); + $this->getResponse()->setBody(Mage::helper('core')->jsonEncode($result)); + } + + protected function _saveBilling(){ + $billing = $this->getRequest()->getPost('billing', array()); + $this->saveMethodAction(); + $this->saveBillingAction(); + $usingShippingCase = isset($billing['use_for_shipping']) ? (int)$billing['use_for_shipping'] : 0; + + if (!$usingShippingCase) + $this->saveShippingAction(); + } + + protected function _saveShipping(){ + $this->saveShippingAction(); + } + + protected function _saveShippingMethod(){ + $this->saveShippingMethodAction(); + } + + protected function _savePaymentMethod(){ + $this->savePaymentAction(); + } + + /** + * Create accont when customer checkout + * + * @return array + */ + public function createAccountWhenCheckout($billingData) + { + $result = array( + 'success' => true, + 'messages' => array(), + ); + if (!$this->getOnepage()->getCustomerSession()->isLoggedIn()) { + if (isset($billingData['create_account'])) { + $this->getOnepage()->saveCheckoutMethod(Mage_Checkout_Model_Type_Onepage::METHOD_REGISTER); + } else { + $this->getOnepage()->saveCheckoutMethod(Mage_Checkout_Model_Type_Onepage::METHOD_GUEST); + } + } + + if (!$this->getOnepage()->getQuote()->getCustomerId() && + Mage_Checkout_Model_Type_Onepage::METHOD_REGISTER == $this->getOnepage()->getQuote()->getCheckoutMethod() + ) { + if ($this->_customerEmailExists($billingData['email'], Mage::app()->getWebsite()->getId())) { + $result['success'] = false; + $result['messages'][] = $this->__('There is already a customer registered using this email address. Please login using this email address or enter a different email address to register your account.'); + } + } + return $result; + } + + /** + * @reference Mage_Checkout_OnepageController + * @param string $email + * @param int $websiteId + * @return false|Mage_Customer_Model_Customer + */ + protected function _customerEmailExists($email, $websiteId = null) + { + $customer = Mage::getModel('customer/customer'); + if ($websiteId) { + $customer->setWebsiteId($websiteId); + } + $customer->loadByEmail($email); + if ($customer->getId()) { + return $customer; + } + + return false; + } } diff --git a/extension/app/code/community/Affirm/Affirm/etc/config.xml b/extension/app/code/community/Affirm/Affirm/etc/config.xml index 0d63843..e26f82d 100644 --- a/extension/app/code/community/Affirm/Affirm/etc/config.xml +++ b/extension/app/code/community/Affirm/Affirm/etc/config.xml @@ -2,7 +2,7 @@ - 3.5.2 + 3.5.3 @@ -85,6 +85,42 @@ + + + + singleton + affirm/order_observer + preDispatchSaveOrderAction + + + + + + + singleton + affirm/order_observer + preDispatchSaveOrderAction + + + + + + + singleton + affirm/order_observer + preDispatchSaveOrderAction + + + + + + + singleton + affirm/order_observer + preDispatchSaveOrderAction + + + diff --git a/extension/app/code/community/Affirm/Affirm/etc/system.xml b/extension/app/code/community/Affirm/Affirm/etc/system.xml index e027be6..c0006b4 100644 --- a/extension/app/code/community/Affirm/Affirm/etc/system.xml +++ b/extension/app/code/community/Affirm/Affirm/etc/system.xml @@ -174,19 +174,31 @@ 1 + + + + + select + affirm/source_paymentCheckoutFlow + 230 + 1 + 1 + 0 + Modal checkout flow is currently in beta. After enabling this, if you encounter any issues with the checkout flow, reach out to Affirm. + - + select affirm/source_paymentCheckoutXhr - 230 + 235 1 1 0 - Enable for an improved order management experience. Disable if there are + Only applicable with Redirect checkout flow type. Enable for an improved order management experience. Disable if there are compatibility issues. diff --git a/extension/app/design/frontend/base/default/layout/affirm/affirm.xml b/extension/app/design/frontend/base/default/layout/affirm/affirm.xml index 5aeb98f..bd3f0b4 100644 --- a/extension/app/design/frontend/base/default/layout/affirm/affirm.xml +++ b/extension/app/design/frontend/base/default/layout/affirm/affirm.xml @@ -6,9 +6,13 @@ skin_js js/affirm/promos.js + + + + + - @@ -18,22 +22,22 @@ + name="checkout.cart.methods.affirm.top" + template="affirm/promo/checkout_button.phtml" + after="-" /> + name="checkout.cart.methods.affirm.top" + template="affirm/promo/checkout_button.phtml" + after="checkout.cart.methods.onepage.bottom" /> + template="affirm/promo/aslowas/checkout.phtml" before="-" /> - + @@ -43,7 +47,7 @@ + template="affirm/promo/aslowas/product.phtml" after="-" /> @@ -68,4 +72,64 @@ + + + + skin_js + + + + + + + + + + + + + + + + + + + skin_js + + + + + + + + + + + skin_js + + + + + + + + + + + skin_js + + + + + + + + + + + skin_js + + + + diff --git a/extension/app/design/frontend/base/default/template/affirm/affirm/checkout/review/button.phtml b/extension/app/design/frontend/base/default/template/affirm/affirm/checkout/review/button.phtml new file mode 100644 index 0000000..13bba0d --- /dev/null +++ b/extension/app/design/frontend/base/default/template/affirm/affirm/checkout/review/button.phtml @@ -0,0 +1,73 @@ + + +getQuote(); +$paymentMethod = $quote->getPayment()->getMethodInstance(); +?> + \ No newline at end of file diff --git a/extension/app/design/frontend/base/default/template/affirm/affirm/checkout/review/opc.phtml b/extension/app/design/frontend/base/default/template/affirm/affirm/checkout/review/opc.phtml new file mode 100644 index 0000000..3f7d701 --- /dev/null +++ b/extension/app/design/frontend/base/default/template/affirm/affirm/checkout/review/opc.phtml @@ -0,0 +1,321 @@ +isCheckoutFlowTypeModal()) { + ?> + + \ No newline at end of file diff --git a/extension/package.xml b/extension/package.xml index 2a6e9a8..53b1725 100644 --- a/extension/package.xml +++ b/extension/package.xml @@ -1,16 +1,16 @@ Affirm_Affirm - 3.5.2 + 3.5.3 stable - OSL-3.0 + BSD-3-Clause community Affirm functions as an alternative payment method and offers easy financing. Affirm functions as an alternative payment method and offers easy financing. Payment restrictions via rules and admin notification and other bug fixes Affirmaffirmmts@affirm.com - 2018-03-08 + 2018-03-20 diff --git a/extension/skin/frontend/base/default/js/affirm/checkout.js b/extension/skin/frontend/base/default/js/affirm/checkout.js new file mode 100644 index 0000000..c9e54f0 --- /dev/null +++ b/extension/skin/frontend/base/default/js/affirm/checkout.js @@ -0,0 +1,136 @@ +function isAffirmMethod() +{ + var result = false; + + if ($$('input:checked[type=radio][name=payment[method]][value^=affirm]').length) { + + // This is for other checkout modules + result = true; + + } else if (null != $('iwd_opc_payment_method_select')) { + + // This is for IWD Checkout suite + result = $('iwd_opc_payment_method_select').value.match(/affirm/); + } + return result; +} + +document.observe('dom:loaded', function () { + + // This is for Amasty One Step Checkout + if ($('amscheckout-onepage')) { + + completeCheckout = completeCheckout.wrap( + function (parentMethod) { + + if (isAffirmMethod()) { + callAmastyCheckoutForAffirm(); + } else { + return parentMethod(); + } + + } + ); + } + + if ($('one-step-checkout-form')) { + if (typeof oscPlaceOrder == 'function') { // This is for Magestore_Onestepcheckout + oscPlaceOrder = oscPlaceOrder.wrap( + function (parentMethod, elem) { + if (isAffirmMethod()) { + callMageStoreOneStepCheckoutForAffirm(); + } else { + return parentMethod(elem); + } + + } + ); + } + + if(MagecheckoutSecuredCheckoutForm) { + if (typeof MagecheckoutSecuredCheckoutForm.prototype.placeOrderProcess == 'function') { + MagecheckoutSecuredCheckoutForm.prototype.placeOrderProcess = MagecheckoutSecuredCheckoutForm.prototype.placeOrderProcess.wrap( + function (parentMethod) { + if (isAffirmMethod()) { + callMagecheckoutSecuredCheckoutForAffirm(); + } else { + return parentMethod(); + } + } + ); + } + } + } + + if($('iwd_opc_place_order_button')){ + if (typeof OnePage.prototype.saveOrder == 'function') { // This is for IWD OPC + OnePage.prototype.saveOrder = OnePage.prototype.saveOrder.wrap( + function (parentMethod) { + if (isAffirmMethod()) { + callIWDOneStepCheckoutForAffirm(); + } else { + return parentMethod(); + } + + } + ); + } + } + + // This is for Firecheckout + // And Venedor Theme 1.6.3 + if ($('firecheckout-form') || $('onepagecheckout_orderform')) { + if (typeof checkout.save == 'function') { // This is for TM Firecheckout + checkout.save = checkout.save.wrap( + function (parentMethod) { + if (isAffirmMethod()) { + var createAccount = $('billing:register_account'); + billing.setCreateAccount(createAccount ? createAccount.checked : 1); // create account if checkbox is missing + callTMFireCheckoutForAffirm(); + } else { + return parentMethod(); + } + + } + ); + } + } + + // This is for One Step Checkout by AheadWorks + if ($('aw-onestepcheckout-general-form')) { + if (typeof AWOnestepcheckoutForm.prototype._sendPlaceOrderRequest == 'function') { + AWOnestepcheckoutForm.prototype._sendPlaceOrderRequest = AWOnestepcheckoutForm.prototype._sendPlaceOrderRequest.wrap( + function (parentMethod) { + if (isAffirmMethod()) { + var params = Form.serialize($('aw-onestepcheckout-general-form'), true); + if (AWOnestepcheckoutCore.validateParams(params)) { + callAWOneStepCheckoutForAffirm(); + AWOnestepcheckoutForm.prototype.hideOverlay(); + AWOnestepcheckoutForm.prototype.hidePleaseWaitNotice(); + AWOnestepcheckoutForm.prototype.enablePlaceOrderButton(); + } + } else { + return parentMethod(); + } + } + ); + } + } +}); + +window.addEventListener('load', function() { + if ($('onestep_form')) { + if (typeof window.OneStep.Views.Init.prototype.updateOrder == 'function') { + window.OneStep.Views.Init.prototype.updateOrder = window.OneStep.Views.Init.prototype.updateOrder.wrap( + function (parentMethod) { + if (isAffirmMethod()) { + callMageWorldCheckoutForAffirm(); + } else { + return parentMethod(); + } + + } + ) + } + } +}); \ No newline at end of file