From 4da38a75a435eba44aa76573cf12876f30cbaadd Mon Sep 17 00:00:00 2001 From: Kevin Kong Date: Fri, 12 Mar 2021 15:37:33 -0800 Subject: [PATCH] add inline checkout (#58) * add inline checkout * Update inline-checkout.js --- Api/InlineCheckoutInterface.php | 37 ++++ Model/InlineCheckout.php | 197 ++++++++++++++++++ Model/Ui/ConfigProvider.php | 3 +- composer.json | 2 +- etc/adminhtml/system.xml | 5 + etc/di.xml | 1 + etc/module.xml | 2 +- etc/webapi.xml | 6 + .../frontend/web/js/action/inline-checkout.js | 79 +++++++ .../payment/method-renderer/affirm_gateway.js | 56 ++++- view/frontend/web/template/payment/form.html | 7 +- 11 files changed, 387 insertions(+), 8 deletions(-) create mode 100644 Api/InlineCheckoutInterface.php create mode 100644 Model/InlineCheckout.php create mode 100644 view/frontend/web/js/action/inline-checkout.js diff --git a/Api/InlineCheckoutInterface.php b/Api/InlineCheckoutInterface.php new file mode 100644 index 00000000..45234b99 --- /dev/null +++ b/Api/InlineCheckoutInterface.php @@ -0,0 +1,37 @@ +session = $checkoutSession; + $this->quote = $checkoutSession->getQuote(); + $this->urlBuilder = $urlInterface; + $this->moduleResource = $moduleResource; + $this->productMetadata = $productMetadata; + $this->util = $util; + $this->quoteValidator = $quoteValidator; + } + + public function initInline(){ + $quote = $this->quote; + $quote->collectTotals(); + + if(!$quote->getReservedOrderId()) { + $quote->reserveOrderId(); + } + + try{ + $this->quoteValidator->validateBeforeSubmit($quote); + } catch (LocalizedException $e) { + return json_encode(array( + 'merchant' => array( + 'user_confirmation_url' => $this->urlBuilder + ->getUrl('affirm/payment/confirm', ['_secure' => true]), + 'user_cancel_url' => $this->urlBuilder + ->getUrl('affirm/payment/cancel', ['_secure' => true]), + 'user_confirmation_url_action' => 'POST', + ), + 'order_id' => $quote->getReservedOrderId(), + 'total' => $this->util->formatToCents($quote->getGrandTotal()) + )); + + }catch (\Exception $e) { + + } + + $checkoutObject = array( + 'merchant' => array( + 'user_confirmation_url' => $this->urlBuilder + ->getUrl('affirm/payment/confirm', ['_secure' => true]), + 'user_cancel_url' => $this->urlBuilder + ->getUrl('affirm/payment/cancel', ['_secure' => true]), + 'user_confirmation_url_action' => 'POST', + ), + 'order_id' => $quote->getReservedOrderId(), + 'shipping_amount' => $this->util->formatToCents($quote->getShippingAddress()->getShippingAmount()), + 'total' => $this->util->formatToCents($quote->getGrandTotal()), + 'tax_amount' => $this->util->formatToCents($quote->getShippingAddress()->getTaxAmount()), + 'metadata' => array( + 'platform_type' => $this->productMetadata->getName() . ' 2', + 'platform_version' => $this->productMetadata->getVersion() . ' ' . $this->productMetadata->getEdition(), + 'platform_affirm' => $this->moduleResource->getDbVersion('Astound_Affirm'), + 'mode' => 'inline' + ) + ); + + if($items = $this->formatItems($quote->getAllVisibleItems())) { + $checkoutObject['items'] = $items; + } + + if ($shippingAddress = $this->formatAddress($quote->getShippingAddress())) { + $checkoutObject['shipping'] = $shippingAddress; + } + + if ($billingAddress = $this->formatAddress($quote->getBillingAddress())) { + $checkoutObject['billing'] = $billingAddress; + } + + $discountAmount = $this->quote->getBaseSubtotal() - $this->quote->getBaseSubtotalWithDiscount(); + if ($discountAmount > 0.001) { + $checkoutObject['discounts']['discount'] = [ + 'discount_amount' => Util::formatToCents($discountAmount) + ]; + } + + if ($this->productMetadata->getEdition() == 'Enterprise') { + $giftWrapperItemsManager = $this->objectManager->create('Astound\Affirm\Api\GiftWrapManagerInterface'); + $wrapped = $giftWrapperItemsManager->getWrapItems(); + if ($wrapped) { + $checkoutObject['wrapped_items'] = $wrapped; + } + $giftCards = $this->quote->getGiftCards(); + if ($giftCards) { + $giftCards = json_decode($giftCards); + foreach ($giftCards as $giftCard) { + $giftCardDiscountDescription = sprintf(__('Gift Card (%s)'), $giftCard[self::ID]); + $checkoutObject['discounts'][$giftCardDiscountDescription] = [ + 'discount_amount' => Util::formatToCents($giftCard[self::AMOUNT]) + ]; + } + } + } + + return json_encode($checkoutObject); + } + + private function formatAddress($address){ + $formattedAddress = false; + if($address->getCity()) { + $street = $address->getStreet(); + $formattedAddress = array( + 'name' => array( + 'first' => $address->getFirstName(), + 'last' => $address->getLastName(), + ), + 'address' => array( + 'line1' => $street[0] ? $street[0] : '', + 'line2' => $street[1] ? $street[1] : '', + 'city' => $address->getCity(), + 'state' => $address->getRegion(), + 'zipcode' => $address->getPostcode(), + 'country' => $address->getCountryId() + ), + 'phone_number' => $address->getTelephone() ? $address->getTelephone() : '', + 'email' => $address->getEmail() ? $address->getEmail() : '' + ); + } + + return $formattedAddress; + } + + private function formatItems($items) { + $formattedItems = array(); + foreach ( (object)$items as $item) { + if( is_object($item)){ + $product = $item->getProduct(); + + $formattedItems[] = array( + 'display_name' => $item->getName(), + 'sku' => $item->getSku(), + 'unit_price' => $item->getPrice(), + 'qty' => $item->getQty(), + 'item_image_url' => $product->getData('thumbnail'), + 'item_url' => $product->getUrlModel()->getUrl($product) + ); + } + } + return $formattedItems; + } +} diff --git a/Model/Ui/ConfigProvider.php b/Model/Ui/ConfigProvider.php index 0a55cec6..05432586 100644 --- a/Model/Ui/ConfigProvider.php +++ b/Model/Ui/ConfigProvider.php @@ -123,7 +123,8 @@ public function getConfig() 'afterAffirmConf' => $this->config->getValue('after_affirm_conf'), 'logoSrc' => $this->config->getValue('icon'), 'info' => $this->config->getValue('info'), - 'visibleType' => $this->config->getValue('control') ? true: false + 'visibleType' => $this->config->getValue('control') ? true: false, + 'edu' => $this->config->getValue('edu') ? true : false ] ] ]; diff --git a/composer.json b/composer.json index e3f8fe98..5f403ed6 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "affirm/magento2", "description": "Affirm's extension for the Magento 2 https://www.affirm.com/", "type": "magento2-module", - "version": "3.0.8", + "version": "3.0.10", "license": [ "BSD-3-Clause" ], diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml index c0bb8116..2c16f737 100644 --- a/etc/adminhtml/system.xml +++ b/etc/adminhtml/system.xml @@ -124,6 +124,11 @@ Astound\Affirm\Model\Adminhtml\Source\PaymentCheckoutFlow + + + Magento\Config\Model\Config\Source\Yesno + Enable/Disable Inline checkout value props on the checkout page when Affirm is selected as a payment method + diff --git a/etc/di.xml b/etc/di.xml index 989d7d14..98914689 100644 --- a/etc/di.xml +++ b/etc/di.xml @@ -14,6 +14,7 @@ + diff --git a/etc/module.xml b/etc/module.xml index 89e5a0ac..689578d7 100644 --- a/etc/module.xml +++ b/etc/module.xml @@ -10,7 +10,7 @@ */ --> - + diff --git a/etc/webapi.xml b/etc/webapi.xml index c6c1002f..e67399c0 100644 --- a/etc/webapi.xml +++ b/etc/webapi.xml @@ -25,4 +25,10 @@ + + + + + + diff --git a/view/frontend/web/js/action/inline-checkout.js b/view/frontend/web/js/action/inline-checkout.js new file mode 100644 index 00000000..18716063 --- /dev/null +++ b/view/frontend/web/js/action/inline-checkout.js @@ -0,0 +1,79 @@ +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +/*jshint browser:true jquery:true*/ +/*global alert*/ +define([ + 'jquery', + 'mage/storage', + 'Magento_Checkout/js/model/url-builder', +], function ($,storage, urlBuilder) { + 'use strict'; + var configData = window.checkoutConfig.payment['affirm_gateway']; + var checkoutObject + var initAffirmInline = true + return { + inlineCheckout: function(){ + let serviceUrl = urlBuilder.createUrl('/affirm/checkout/inline', {}), result; + storage.get( + serviceUrl + ).done( + function(response) { + if(!(checkoutObject == response)) { + affirm.ui.ready(function() { + affirm.checkout(JSON.parse(response)) + affirm.checkout.inline({ + merchant: { + inline_container: "affirm-inline-checkout" + }, + }); + }) + initAffirmInline = false + } else { + affirm.checkout.inline({ + container: "affirm-inline-checkout", + data: JSON.parse(response), + }); + } + checkoutObject = response + } + ).fail( + function (response) { + console.log(response) + } + ) + }, + + updateInlineCheckout : function(){ + var _self = this; + let serviceUrl = urlBuilder.createUrl('/affirm/checkout/inline', {}), result; + storage.get( + serviceUrl + ).done( + function(response) { + setTimeout(function(){ + $('.action-apply').click(function(){ + _self.updateInlineCheckout() + }) + $('.action-cancel').click(function(){ + _self.updateInlineCheckout() + }) + $('.action-update').click(function(){ + _self.updateInlineCheckout() + }) + }, + 3000); + affirm.checkout.inline({ + container: "affirm-inline-checkout", + data: JSON.parse(response), + }); + } + ).fail( + function (response) { + console.log(response) + } + ) + } + } +}) diff --git a/view/frontend/web/js/view/payment/method-renderer/affirm_gateway.js b/view/frontend/web/js/view/payment/method-renderer/affirm_gateway.js index 94270019..9c0fc029 100644 --- a/view/frontend/web/js/view/payment/method-renderer/affirm_gateway.js +++ b/view/frontend/web/js/view/payment/method-renderer/affirm_gateway.js @@ -28,11 +28,12 @@ define( 'Magento_Checkout/js/action/set-payment-information', 'Astound_Affirm/js/action/prepare-affirm-checkout', 'Astound_Affirm/js/action/send-to-affirm-checkout', - 'Astound_Affirm/js/action/verify-affirm' + 'Astound_Affirm/js/action/verify-affirm', + 'Astound_Affirm/js/action/inline-checkout' ], function ($, Component, quote, additionalValidators, urlBuilder, errorProcessor, Messages, setPaymentAction, - initChekoutAction, sendToAffirmCheckout, verifyAffirmAction) { + initChekoutAction, sendToAffirmCheckout, verifyAffirmAction, inlineCheckout) { 'use strict'; @@ -85,6 +86,57 @@ define( return window.checkoutConfig.payment['affirm_gateway'].visibleType; }, + /** + * Show Affirm Checkout Education Modal + * + * @returns {*} + */ + getEdu: function() { + if (!window.checkoutConfig.payment['affirm_gateway'].edu) { + return "You will be redirected to Affirm to securely complete your purchase. Just fill out a few pieces of basic information and get a real-time decision. Checking your eligibility won\'t affect your credit score." + } + }, + + /** + * Show Affirm Checkout Education Modal + * + * @returns {*} + */ + getEduHTML: function() { + if (window.checkoutConfig.payment['affirm_gateway'].edu) { + let timestamp = 1 + $('form').change(function(e){ + if (timestamp == 1 || e.timeStamp > timestamp+500) { + timestamp = e.timeStamp + inlineCheckout.inlineCheckout() + } + }) + + $('.action-apply').click(function(){ + inlineCheckout.updateInlineCheckout() + }) + + $('.action-cancel').click(function(){ + inlineCheckout.updateInlineCheckout() + }) + + inlineCheckout.inlineCheckout() + } + }, + + /** + * Change place order button text + * + * @returns {*} + */ + getAffirmTitle: function() { + if (!window.checkoutConfig.payment['affirm_gateway'].edu) { + return "Continue with Affirm" + } else { + return "Place Order" + } + }, + /** * Continue to Affirm redirect logic */ diff --git a/view/frontend/web/template/payment/form.html b/view/frontend/web/template/payment/form.html index 310ff480..1043e760 100644 --- a/view/frontend/web/template/payment/form.html +++ b/view/frontend/web/template/payment/form.html @@ -38,7 +38,8 @@
-

+

+

@@ -56,9 +57,9 @@ type="submit" data-bind=" click: continueInAffirm, - attr: {title: $t('Continue with Affirm')} + attr: {title: getAffirmTitle()} "> - +