├── .gitignore ├── Api ├── GuestPPPPaymentInformationManagementInterface.php └── PPPPaymentInformationManagementInterface.php ├── Block ├── Adminhtml │ └── System │ │ └── Config │ │ └── ThirdPartyInfo.php ├── Config │ └── System │ │ └── Config │ │ └── Form │ │ └── Comment.php ├── Onepage │ └── Success.php └── PaymentInfo.php ├── Controller ├── Checkout │ └── Cancel.php ├── Order │ └── Create.php └── Webhooks │ └── Index.php ├── Helper └── Data.php ├── Model ├── Api.php ├── ConfigProvider.php ├── MethodList.php ├── Payment.php ├── PaymentInformationManagement.php ├── PaymentInformationManagement │ ├── GuestPPPPaymentInformationManagement.php │ └── PPPPaymentInformationManagement.php ├── System │ └── Config │ │ └── Source │ │ ├── Mode.php │ │ └── ThirdPartyModuls.php └── Webhook │ └── Event.php ├── Observer ├── ResetObserver.php ├── TpmiObserver.php └── ValidateObserver.php ├── Plugin ├── Config │ └── Structure │ │ └── Element │ │ └── Group.php ├── Payment │ └── Model │ │ └── MethodListPlugin.php └── Sales │ └── Model │ └── Order │ └── PaymentPlugin.php ├── README.md ├── RELEASE_NOTES.txt ├── Setup └── InstallSchema.php ├── composer.json ├── etc ├── adminhtml │ ├── events.xml │ └── system.xml ├── config.xml ├── csp_whitelist.xml ├── di.xml ├── extension_attributes.xml ├── frontend │ ├── di.xml │ └── routes.xml ├── module.xml ├── payment.xml └── webapi.xml ├── i18n └── de_DE.csv ├── registration.php └── view ├── adminhtml └── templates │ └── paypalplus │ └── info │ ├── default.phtml │ └── pdf │ └── default.phtml └── frontend ├── layout ├── checkout_index_index.xml └── checkout_onepage_success.xml ├── templates └── paypalplus │ ├── info │ └── default.phtml │ └── success.phtml └── web ├── css └── iways-paypalplus.css ├── js ├── action │ └── patch-ppp-payment.js └── view │ └── payment │ ├── method-renderer.js │ └── method-renderer │ └── payment.js ├── requirejs-config.js └── template └── payment.html /.gitignore: -------------------------------------------------------------------------------- 1 | /.project 2 | -------------------------------------------------------------------------------- /Api/GuestPPPPaymentInformationManagementInterface.php: -------------------------------------------------------------------------------- 1 | 15 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License 3.0 16 | * @link https://www.i-ways.net 17 | */ 18 | namespace Iways\PayPalPlus\Api; 19 | 20 | /** 21 | * Interface for managing guest payment information 22 | * 23 | * @api 24 | */ 25 | interface GuestPPPPaymentInformationManagementInterface 26 | { 27 | /** 28 | * Set payment information for a specified cart. 29 | * 30 | * @param string $cartId 31 | * @param string $email 32 | * @param \Magento\Quote\Api\Data\PaymentInterface $paymentMethod 33 | * @param \Magento\Quote\Api\Data\AddressInterface|null $billingAddress 34 | * 35 | * @throws \Magento\Framework\Exception\CouldNotSaveException 36 | * 37 | * @return int Order ID. 38 | */ 39 | public function savePaymentInformation( 40 | $cartId, 41 | $email, 42 | \Magento\Quote\Api\Data\PaymentInterface $paymentMethod, 43 | \Magento\Quote\Api\Data\AddressInterface $billingAddress = null 44 | ); 45 | 46 | /** 47 | * Get payment information 48 | * 49 | * @param string $cartId 50 | * 51 | * @return \Magento\Checkout\Api\Data\PaymentDetailsInterface 52 | */ 53 | public function getPaymentInformation($cartId); 54 | } 55 | -------------------------------------------------------------------------------- /Api/PPPPaymentInformationManagementInterface.php: -------------------------------------------------------------------------------- 1 | 15 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License 3.0 16 | * @link https://www.i-ways.net 17 | */ 18 | namespace Iways\PayPalPlus\Api; 19 | 20 | /** 21 | * Interface for managing quote payment information 22 | * 23 | * @api 24 | */ 25 | interface PPPPaymentInformationManagementInterface 26 | { 27 | /** 28 | * Set payment information for a specified cart. 29 | * 30 | * @param int $cartId 31 | * @param \Magento\Quote\Api\Data\PaymentInterface $paymentMethod 32 | * @param \Magento\Quote\Api\Data\AddressInterface|null $billingAddress 33 | * 34 | * @throws \Magento\Framework\Exception\CouldNotSaveException 35 | * 36 | * @return int Order ID. 37 | */ 38 | public function savePaymentInformation( 39 | $cartId, 40 | \Magento\Quote\Api\Data\PaymentInterface $paymentMethod, 41 | \Magento\Quote\Api\Data\AddressInterface $billingAddress = null 42 | ); 43 | 44 | /** 45 | * Get payment information 46 | * 47 | * @param int $cartId 48 | * 49 | * @return \Magento\Checkout\Api\Data\PaymentDetailsInterface 50 | */ 51 | public function getPaymentInformation($cartId); 52 | } 53 | -------------------------------------------------------------------------------- /Block/Adminhtml/System/Config/ThirdPartyInfo.php: -------------------------------------------------------------------------------- 1 | 15 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License 3.0 16 | * @link https://www.i-ways.net 17 | */ 18 | 19 | /** 20 | * Config form fieldset renderer 21 | */ 22 | namespace Iways\PayPalPlus\Block\Adminhtml\System\Config; 23 | 24 | class ThirdPartyInfo extends \Magento\Config\Block\System\Config\Form\Fieldset 25 | { 26 | /** 27 | * Payment method config 28 | * 29 | * @var \Magento\Payment\Model\Config 30 | */ 31 | protected $paymentConfig; 32 | 33 | public function __construct( 34 | \Magento\Payment\Model\Config $paymentConfig, 35 | \Magento\Backend\Block\Context $context, 36 | \Magento\Backend\Model\Auth\Session $authSession, 37 | \Magento\Framework\View\Helper\Js $jsHelper, 38 | array $data = [] 39 | ) { 40 | $this->paymentConfig = $paymentConfig; 41 | parent::__construct($context, $authSession, $jsHelper, $data); 42 | } 43 | 44 | /** 45 | * Render fieldset html 46 | * 47 | * @param \Magento\Framework\Data\Form\Element\AbstractElement $element 48 | * 49 | * @return string 50 | */ 51 | public function render(\Magento\Framework\Data\Form\Element\AbstractElement $element) 52 | { 53 | $this->setElement($element); 54 | $html = $this->_getHeaderHtml($element); 55 | $dummyField = $element->getElements()[0]; 56 | 57 | $thirdPartyModuls = $this->_scopeConfig->getValue('payment/iways_paypalplus_payment/third_party_moduls'); 58 | $thirdPartyMethods = explode(',', $thirdPartyModuls); 59 | foreach ($this->paymentConfig->getActiveMethods() as $paymentMethod) { 60 | if (in_array($paymentMethod->getCode(), $thirdPartyMethods)) { 61 | $thirdPartyMethod = $paymentMethod->getCode(); 62 | $textSetting = 'payment/iways_paypalplus_section/third_party_modul_info/text_' . $thirdPartyMethod; 63 | $text = $this->_scopeConfig->getValue($textSetting); 64 | $field = clone $dummyField; 65 | $field->setData('name', str_replace('dummy', $thirdPartyMethod, $field->getName())); 66 | $field->setData('label', $paymentMethod->getTitle()); 67 | $field->setData('value', $text); 68 | $fieldConfig = $field->getData('field_config'); 69 | $fieldConfig['id'] = 'text_' . $thirdPartyMethod; 70 | $fieldConfig['label'] = $paymentMethod->getTitle(); 71 | $fieldConfig['config_path'] = $textSetting; 72 | $field->setData('field_config', $fieldConfig); 73 | $field->setData('html_id', str_replace('dummy', $thirdPartyMethod, $field->getData('html_id'))); 74 | $html .= $field->toHtml(); 75 | } 76 | } 77 | $html .= $this->_getFooterHtml($element); 78 | 79 | return $html; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Block/Config/System/Config/Form/Comment.php: -------------------------------------------------------------------------------- 1 | 15 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License 3.0 16 | * @link https://www.i-ways.net 17 | */ 18 | namespace Iways\PayPalPlus\Block\Config\System\Config\Form; 19 | 20 | class Comment extends \Magento\Config\Block\System\Config\Form\Field 21 | { 22 | /** 23 | * Retrieve HTML markup for given form element 24 | * 25 | * @param \Magento\Framework\Data\Form\Element\AbstractElement $element 26 | * 27 | * @return string 28 | */ 29 | public function render(\Magento\Framework\Data\Form\Element\AbstractElement $element) 30 | { 31 | return $this->_decorateRowHtml($element, __($element->getComment())); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Block/Onepage/Success.php: -------------------------------------------------------------------------------- 1 | 15 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License 3.0 16 | * @link https://www.i-ways.net 17 | */ 18 | namespace Iways\PayPalPlus\Block\Onepage; 19 | 20 | use Iways\PayPalPlus\Model\Payment; 21 | use Magento\Sales\Model\Order; 22 | 23 | /** 24 | * One page checkout success page 25 | */ 26 | class Success extends \Magento\Framework\View\Element\Template 27 | { 28 | /** 29 | * Store name config path 30 | */ 31 | const STORE_NAME_PATH = 'general/store_information/name'; 32 | 33 | /** 34 | * Checkout session 35 | * 36 | * @var \Magento\Checkout\Model\Session 37 | */ 38 | protected $checkoutSession; 39 | 40 | /** 41 | * Order 42 | * 43 | * @var Order 44 | */ 45 | protected $order; 46 | 47 | /** 48 | * Construct 49 | * 50 | * @param \Magento\Framework\View\Element\Template\Context $context 51 | * @param \Magento\Checkout\Model\Session $checkoutSession 52 | * @param [] $data 53 | */ 54 | public function __construct( 55 | \Magento\Framework\View\Element\Template\Context $context, 56 | \Magento\Checkout\Model\Session $checkoutSession, 57 | array $data = [] 58 | ) { 59 | parent::__construct($context, $data); 60 | $this->checkoutSession = $checkoutSession; 61 | $this->order = $this->checkoutSession->getLastRealOrder(); 62 | } 63 | 64 | /** 65 | * Check if last order is PayPalPlus 66 | * 67 | * @return bool 68 | */ 69 | public function isPPP() 70 | { 71 | if ($this->order->getPayment()->getMethodInstance()->getCode() == Payment::CODE) { 72 | return true; 73 | } 74 | return false; 75 | } 76 | 77 | /** 78 | * Checks if order is PayPal PLUS and PuI 79 | * 80 | * @return bool 81 | */ 82 | public function isPUI() 83 | { 84 | return ( 85 | $this->isPPP() 86 | && ( 87 | $this->order->getPayment()->getData('ppp_instruction_type') 88 | == Payment::PPP_INSTRUCTION_TYPE 89 | ) 90 | ) ? true : false; 91 | } 92 | 93 | /** 94 | * Checks if order is PayPal PLUS and has payment instructions 95 | * 96 | * @return bool 97 | */ 98 | public function hasPaymentInstruction() 99 | { 100 | return ($this->isPPP() && $this->order->getPayment()->getData('ppp_instruction_type')) ? true : false; 101 | } 102 | 103 | /** 104 | * Wrapper for $payment->getData($key) 105 | * 106 | * @param string $key 107 | * 108 | * @return array|mixed|null 109 | */ 110 | public function getAdditionalInformation($key) 111 | { 112 | return $this->order->getPayment()->getData($key); 113 | } 114 | 115 | /** 116 | * Get store name from config 117 | * 118 | * @return string|null 119 | */ 120 | public function getStoreName() 121 | { 122 | return $this->_scopeConfig->getValue(self::STORE_NAME_PATH); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /Block/PaymentInfo.php: -------------------------------------------------------------------------------- 1 | 15 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License 3.0 16 | * @link https://www.i-ways.net 17 | */ 18 | namespace Iways\PayPalPlus\Block; 19 | 20 | use Iways\PayPalPlus\Model\Payment; 21 | 22 | /** 23 | * Class Info 24 | */ 25 | class PaymentInfo extends \Magento\Payment\Block\Info 26 | { 27 | /** 28 | * Default template file 29 | * 30 | * @var string 31 | */ 32 | protected $_template = 'paypalplus/info/default.phtml'; // phpcs:ignore PSR2.Classes.PropertyDeclaration 33 | 34 | /** 35 | * Render as PDF 36 | * 37 | * @return string 38 | */ 39 | public function toPdf() 40 | { 41 | $this->setTemplate('paypalplus/info/pdf/default.phtml'); 42 | return $this->toHtml(); 43 | } 44 | 45 | /** 46 | * Prepare information specific to current payment method 47 | * 48 | * @param null $transport 49 | * 50 | * @return \Magento\Framework\DataObject 51 | * 52 | * @throws \Magento\Framework\Exception\LocalizedException 53 | */ 54 | protected function _prepareSpecificInformation($transport = null) // phpcs:ignore PSR2.Methods.MethodDeclaration 55 | { 56 | $transport = parent::_prepareSpecificInformation($transport); 57 | $payment = $this->getInfo(); 58 | $info = []; 59 | 60 | if (!$this->getIsSecureMode()) { 61 | $info[(string)__('Transaction ID')] = $this->getInfo()->getLastTransId(); 62 | } 63 | if ($this->isPUI()) { 64 | $info[(string)__('Account holder')] = $payment->getData('ppp_account_holder_name'); 65 | $info[(string)__('Bank')] = $payment->getData('ppp_bank_name'); 66 | $info[(string)__('IBAN')] = $payment->getData('ppp_international_bank_account_number'); 67 | $info[(string)__('BIC')] = $payment->getData('ppp_bank_identifier_code'); 68 | $info[(string)__('Reference number')] = $payment->getData('ppp_reference_number'); 69 | $info[(string)__('Payment due date')] = $payment->getData('ppp_payment_due_date'); 70 | } 71 | 72 | return $transport->addData($info); 73 | } 74 | 75 | /** 76 | * Checks if PayPal PLUS payment is PUI 77 | * 78 | * @return bool 79 | * 80 | * @throws \Magento\Framework\Exception\LocalizedException 81 | */ 82 | public function isPUI() 83 | { 84 | return ( 85 | $this->getInfo()->getData('ppp_instruction_type') == Payment::PPP_INSTRUCTION_TYPE 86 | ) ? true : false; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /Controller/Checkout/Cancel.php: -------------------------------------------------------------------------------- 1 | 15 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License 3.0 16 | * @link https://www.i-ways.net 17 | */ 18 | 19 | namespace Iways\PayPalPlus\Controller\Checkout; 20 | 21 | /** 22 | * PayPalPlus checkout controller 23 | * 24 | * @author robert 25 | */ 26 | class Cancel extends \Magento\Framework\App\Action\Action 27 | { 28 | /** 29 | * Execute 30 | */ 31 | public function execute() 32 | { 33 | $this->_redirect( 34 | 'checkout', 35 | [ 36 | '_query' => $this->_request->getParams(), 37 | '_fragment' => 'payment' 38 | ] 39 | ); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Controller/Order/Create.php: -------------------------------------------------------------------------------- 1 | 15 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License 3.0 16 | * @link https://www.i-ways.net 17 | */ 18 | 19 | namespace Iways\PayPalPlus\Controller\Order; 20 | 21 | use Magento\Customer\Model\Session; 22 | use Magento\Framework\DataObject; 23 | use Magento\Quote\Model\QuoteIdMaskFactory; 24 | use Magento\Sales\Model\Order\Email\Sender\OrderSender; 25 | use Magento\Sales\Model\OrderFactory; 26 | 27 | /** 28 | * PayPalPlus checkout controller 29 | * 30 | * @author robert 31 | */ 32 | class Create extends \Magento\Framework\App\Action\Action 33 | { 34 | const MAX_SEND_MAIL_VERSION = '2.2.6'; 35 | /** 36 | * Protected $logger 37 | * 38 | * @var \Psr\Log\LoggerInterface 39 | */ 40 | protected $logger; 41 | 42 | /** 43 | * Protected $checkoutSession 44 | * 45 | * @var \Magento\Checkout\Model\Session 46 | */ 47 | protected $checkoutSession; 48 | 49 | /** 50 | * Protected $checkoutHelper 51 | * 52 | * @var \Magento\Checkout\Helper\Data 53 | */ 54 | protected $checkoutHelper; 55 | 56 | /** 57 | * Protected $customerSession 58 | * 59 | * @var Session 60 | */ 61 | protected $customerSession; 62 | 63 | /** 64 | * Protected $cartManagement 65 | * 66 | * @var \Magento\Quote\Api\CartManagementInterface 67 | */ 68 | protected $cartManagement; 69 | 70 | /** 71 | * Protected $guestCartManagement 72 | * 73 | * @var \Magento\Quote\Api\GuestCartManagementInterface 74 | */ 75 | protected $guestCartManagement; 76 | 77 | /** 78 | * Protected $quoteIdMaskFactory 79 | * 80 | * @var QuoteIdMaskFactory 81 | */ 82 | protected $quoteIdMaskFactory; 83 | 84 | /** 85 | * Protected $orderFactory 86 | * 87 | * @var OrderFactory 88 | */ 89 | protected $orderFactory; 90 | 91 | /** 92 | * Protected $orderSender 93 | * 94 | * @var OrderSender 95 | */ 96 | protected $orderSender; 97 | 98 | /** 99 | * Protected $historyFactory 100 | * 101 | * @var \Magento\Sales\Model\Order\Status\HistoryFactory 102 | */ 103 | protected $historyFactory; 104 | 105 | /** 106 | * Protected $productMetadata 107 | * 108 | * @var \Magento\Framework\App\ProductMetadataInterface 109 | */ 110 | protected $productMetadata; 111 | 112 | public function __construct( 113 | \Magento\Framework\App\Action\Context $context, 114 | \Psr\Log\LoggerInterface $logger, 115 | \Magento\Checkout\Model\Session $checkoutSession, 116 | \Magento\Checkout\Helper\Data $checkoutHelper, 117 | \Magento\Quote\Api\CartManagementInterface $cartManagement, 118 | \Magento\Quote\Api\GuestCartManagementInterface $guestCartManagement, 119 | QuoteIdMaskFactory $quoteIdMaskFactory, 120 | OrderSender $orderSender, 121 | OrderFactory $orderFactory, 122 | \Magento\Sales\Model\Order\Status\HistoryFactory $historyFactory, 123 | Session $customerSession, 124 | \Magento\Framework\App\ProductMetadataInterface $productMetadata 125 | ) { 126 | $this->logger = $logger; 127 | $this->checkoutSession = $checkoutSession; 128 | $this->checkoutHelper = $checkoutHelper; 129 | $this->cartManagement = $cartManagement; 130 | $this->guestCartManagement = $guestCartManagement; 131 | $this->customerSession = $customerSession; 132 | $this->quoteIdMaskFactory = $quoteIdMaskFactory; 133 | $this->orderSender = $orderSender; 134 | $this->orderFactory = $orderFactory; 135 | $this->historyFactory = $historyFactory; 136 | $this->productMetadata = $productMetadata; 137 | parent::__construct($context); 138 | } 139 | 140 | /** 141 | * Execute 142 | * 143 | * @return void 144 | */ 145 | public function execute() 146 | { 147 | try { 148 | /* this seems to help with loss of checkout session 149 | after finalizing payment and returning back from PP server*/ 150 | $this->getRequest()->setParams(['ajax' => 1]); 151 | 152 | $cartId = $this->checkoutSession->getQuoteId(); 153 | $result = new DataObject(); 154 | if ($this->customerSession->isLoggedIn()) { 155 | $orderId = $this->cartManagement->placeOrder($cartId); 156 | } else { 157 | $quoteIdMask = $this->quoteIdMaskFactory->create()->load($cartId, 'quote_id'); 158 | $orderId = $this->guestCartManagement->placeOrder($quoteIdMask->getMaskedId()); 159 | } 160 | 161 | if ($orderId) { 162 | $order = $this->orderFactory->create()->load($orderId); 163 | if ($order->getCanSendNewEmailFlag() 164 | && version_compare($this->productMetadata->getVersion(), self::MAX_SEND_MAIL_VERSION, '<') 165 | ) { 166 | try { 167 | $this->orderSender->send($order); 168 | } catch (\Exception $e) { 169 | $this->logger->critical($e); 170 | } 171 | } 172 | try { 173 | // IWD_Opc Order Comment 174 | if ($this->customerSession->getOrderComment()) { 175 | if ($order->getData('entity_id')) { 176 | $status = $order->getData('status'); 177 | $history = $this->historyFactory->create(); 178 | // set comment history data 179 | $history->setData('comment', strip_tags($this->customerSession->getOrderComment())); 180 | $history->setData('parent_id', $orderId); 181 | $history->setData('is_visible_on_front', 1); 182 | $history->setData('is_customer_notified', 0); 183 | $history->setData('entity_name', 'order'); 184 | $history->setData('status', $status); 185 | $history->save(); 186 | $this->customerSession->setOrderComment(null); 187 | } 188 | } 189 | } catch (\Exception $e) { 190 | $this->logger->log($e); 191 | } 192 | } 193 | $result->setData('success', true); 194 | $result->setData('error', false); 195 | 196 | $this->_eventManager->dispatch( 197 | 'checkout_controller_onepage_saveOrder', 198 | [ 199 | 'result' => $result, 200 | 'action' => $this 201 | ] 202 | ); 203 | $this->_redirect('checkout/onepage/success'); 204 | } catch (\Exception $e) { 205 | $this->messageManager->addError($e->getMessage()); 206 | $this->_redirect('checkout/cart'); 207 | } 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /Controller/Webhooks/Index.php: -------------------------------------------------------------------------------- 1 | 15 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License 3.0 16 | * @link https://www.i-ways.net 17 | */ 18 | 19 | namespace Iways\PayPalPlus\Controller\Webhooks; 20 | 21 | use Magento\Framework\App\CsrfAwareActionInterface; 22 | use Magento\Framework\App\Request\InvalidRequestException; 23 | use Magento\Framework\Exception\LocalizedException; 24 | 25 | /** 26 | * Unified IPN controller for all supported PayPal methods 27 | */ 28 | class Index extends \Magento\Framework\App\Action\Action implements CsrfAwareActionInterface 29 | { 30 | /** 31 | * Protected $_logger 32 | * 33 | * @var \Psr\Log\LoggerInterface 34 | */ 35 | protected $_logger; // phpcs:ignore PSR2.Classes.PropertyDeclaration 36 | 37 | /** 38 | * Protected $_webhookEventFactory 39 | * 40 | * @var \Iways\PayPalPlus\Model\Webhook\EventFactory 41 | */ 42 | protected $_webhookEventFactory; // phpcs:ignore PSR2.Classes.PropertyDeclaration 43 | 44 | /** 45 | * Protected $_apiFactory 46 | * 47 | * @var \Iways\PayPalPlus\Model\ApiFactory 48 | */ 49 | protected $_apiFactory; // phpcs:ignore PSR2.Classes.PropertyDeclaration 50 | 51 | /** 52 | * Protected $_driver 53 | * 54 | * @var \Magento\Framework\Filesystem\DriverInterface 55 | */ 56 | protected $_driver; // phpcs:ignore PSR2.Classes.PropertyDeclaration 57 | 58 | /** 59 | * Function validateForCsrf 60 | * 61 | * @param \Magento\Framework\App\RequestInterface $request 62 | * 63 | * @return bool|null 64 | */ 65 | public function validateForCsrf(\Magento\Framework\App\RequestInterface $request): 66 | ?bool 67 | { 68 | return true; 69 | } 70 | 71 | /** 72 | * Function createCsrfValidationException 73 | * 74 | * @param \Magento\Framework\App\RequestInterface $request 75 | * 76 | * @return InvalidRequestException|null 77 | */ 78 | public function createCsrfValidationException(\Magento\Framework\App\RequestInterface $request): 79 | ?InvalidRequestException 80 | { 81 | return null; 82 | } 83 | 84 | /** 85 | * Class constructor 86 | * 87 | * @param \Magento\Framework\App\Action\Context $context 88 | * @param \Iways\PayPalPlus\Model\Webhook\EventFactory $webhookEventFactory 89 | * @param \Iways\PayPalPlus\Model\ApiFactory $apiFactory 90 | * @param \Psr\Log\LoggerInterface $logger 91 | * @param \Magento\Framework\Filesystem\DriverInterface $driver 92 | */ 93 | public function __construct( 94 | \Magento\Framework\App\Action\Context $context, 95 | \Iways\PayPalPlus\Model\Webhook\EventFactory $webhookEventFactory, 96 | \Iways\PayPalPlus\Model\ApiFactory $apiFactory, 97 | \Psr\Log\LoggerInterface $logger, 98 | \Magento\Framework\Filesystem\Driver\File $driver 99 | ) { 100 | $this->_logger = $logger; 101 | $this->_webhookEventFactory = $webhookEventFactory; 102 | $this->_apiFactory = $apiFactory; 103 | $this->_driver = $driver; 104 | parent::__construct($context); 105 | } 106 | 107 | /** 108 | * Instantiate Event model and pass Webhook request to it 109 | * 110 | * @return void 111 | * 112 | * @SuppressWarnings(PHPMD.ExitExpression) 113 | */ 114 | public function execute() 115 | { 116 | if (!$this->getRequest()->isPost()) { 117 | return; 118 | } 119 | 120 | try { 121 | $data = $this->_driver->fileGetContents('php://input'); // file_get_contents('php://input'); 122 | $webhookEvent = $this->_apiFactory->create()->validateWebhook($data); 123 | if (!$webhookEvent) { 124 | throw new LocalizedException(__('Event not found.')); 125 | } 126 | $this->_webhookEventFactory->create()->processWebhookRequest($webhookEvent); 127 | } catch (\Exception $e) { 128 | $this->_logger->critical($e); 129 | $this->getResponse()->setStatusHeader(503, '1.1', 'Service Unavailable')->sendResponse(); 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /Helper/Data.php: -------------------------------------------------------------------------------- 1 | 15 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License 3.0 16 | * @link https://www.i-ways.net 17 | */ 18 | 19 | namespace Iways\PayPalPlus\Helper; 20 | 21 | use Magento\Framework\App\Cache\TypeListInterface; 22 | use Magento\Framework\View\LayoutFactory; 23 | use Magento\Store\Model\ScopeInterface; 24 | 25 | /** 26 | * Iways\PayPalPlus\Helper\Data 27 | * 28 | * @author Robert Hillebrand 29 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License 3.0 30 | * @link https://www.i-ways.net 31 | */ 32 | class Data extends \Magento\Payment\Helper\Data 33 | { 34 | /** 35 | * Protected $generic 36 | * 37 | * @var \Magento\Framework\Session\Generic 38 | */ 39 | protected $generic; 40 | 41 | /** 42 | * Protected $request 43 | * 44 | * @var \Magento\Framework\App\Request\Http 45 | */ 46 | protected $request; 47 | 48 | /** 49 | * Protected $storeManager 50 | * 51 | * @var \Magento\Store\Model\StoreManagerInterface 52 | */ 53 | protected $storeManager; 54 | 55 | /** 56 | * Protected $payPalPlusApiFactory 57 | * 58 | * @var \Iways\PayPalPlus\Model\ApiFactory 59 | */ 60 | protected $payPalPlusApiFactory; 61 | 62 | /** 63 | * Protected $messageManager 64 | * 65 | * @var \Magento\Framework\Message\ManagerInterface 66 | */ 67 | protected $messageManager; 68 | 69 | /** 70 | * Protected $configResource 71 | * 72 | * @var \Magento\Config\Model\ResourceModel\Config 73 | */ 74 | protected $configResource; 75 | 76 | /** 77 | * Protected $cacheTypeList 78 | * 79 | * @var TypeListInterface 80 | */ 81 | protected $cacheTypeList; 82 | 83 | /** 84 | * Protected $productMetaData 85 | * 86 | * @var \Magento\Framework\App\ProductMetadata 87 | */ 88 | protected $productMetaData; 89 | 90 | /** 91 | * Data constructor 92 | * 93 | * @param \Magento\Framework\App\Helper\Context $context 94 | * @param LayoutFactory $layoutFactory 95 | * @param \Magento\Payment\Model\Method\Factory $paymentMethodFactory 96 | * @param \Magento\Store\Model\App\Emulation $appEmulation 97 | * @param \Magento\Payment\Model\Config $paymentConfig 98 | * @param \Magento\Framework\App\Config\Initial $initialConfig 99 | * @param \Magento\Framework\Session\Generic $generic 100 | * @param \Magento\Framework\App\Request\Http $request 101 | * @param \Magento\Store\Model\StoreManagerInterface $storeManager 102 | * @param \Iways\PayPalPlus\Model\ApiFactory $payPalPlusApiFactory 103 | * @param \Magento\Framework\Message\ManagerInterface $messageManager 104 | * @param \Magento\Config\Model\ResourceModel\Config $configResource 105 | * @param \Magento\Framework\App\ProductMetadata $productMetaData 106 | * @param TypeListInterface $cacheTypeList 107 | */ 108 | public function __construct( 109 | \Magento\Framework\App\Helper\Context $context, 110 | LayoutFactory $layoutFactory, 111 | \Magento\Payment\Model\Method\Factory $paymentMethodFactory, 112 | \Magento\Store\Model\App\Emulation $appEmulation, 113 | \Magento\Payment\Model\Config $paymentConfig, 114 | \Magento\Framework\App\Config\Initial $initialConfig, 115 | \Magento\Framework\Session\Generic $generic, 116 | \Magento\Framework\App\Request\Http $request, 117 | \Magento\Store\Model\StoreManagerInterface $storeManager, 118 | \Iways\PayPalPlus\Model\ApiFactory $payPalPlusApiFactory, 119 | \Magento\Framework\Message\ManagerInterface $messageManager, 120 | \Magento\Config\Model\ResourceModel\Config $configResource, 121 | \Magento\Framework\App\ProductMetadata $productMetaData, 122 | TypeListInterface $cacheTypeList 123 | ) { 124 | parent::__construct( 125 | $context, 126 | $layoutFactory, 127 | $paymentMethodFactory, 128 | $appEmulation, 129 | $paymentConfig, 130 | $initialConfig 131 | ); 132 | $this->generic = $generic; 133 | $this->request = $request; 134 | $this->storeManager = $storeManager; 135 | $this->payPalPlusApiFactory = $payPalPlusApiFactory; 136 | $this->messageManager = $messageManager; 137 | $this->configResource = $configResource; 138 | $this->cacheTypeList = $cacheTypeList; 139 | $this->productMetaData = $productMetaData; 140 | } 141 | 142 | /** 143 | * Show Exception if debug mode. 144 | * 145 | * @param \Exception $e 146 | */ 147 | public function handleException(\Exception $e) 148 | { 149 | if ($this->scopeConfig->getValue( 150 | 'iways_paypalplus/dev/debug', 151 | \Magento\Store\Model\ScopeInterface::SCOPE_STORE 152 | ) 153 | ) { 154 | $this->messageManager->addWarning($e->getData()); 155 | } 156 | } 157 | 158 | /** 159 | * Build webhook listener url 160 | * 161 | * @return string 162 | */ 163 | public function getWebhooksUrl() 164 | { 165 | return str_replace( 166 | 'http://', 167 | 'https://', 168 | $this->_getUrl( 169 | 'paypalplus/webhooks/index/', 170 | [ 171 | '_forced_secure' => true, 172 | '_nosid' => true, 173 | ] 174 | ) 175 | ); 176 | } 177 | 178 | /** 179 | * Get url wrapper for security urls and form key 180 | * 181 | * @param $url 182 | * @param array $params 183 | * @param bool|true $formKey 184 | * 185 | * @return string 186 | */ 187 | public function getUrl($url, $params = [], $formKey = true) 188 | { 189 | $isSecure = $this->request->isSecure(); 190 | if ($isSecure) { 191 | $params['_forced_secure'] = true; 192 | } else { 193 | $params['_secure'] = true; 194 | } 195 | if ($formKey) { 196 | $params['form_key'] = $this->generic->getFormKey(); 197 | } 198 | return $this->_getUrl($url, $params); 199 | } 200 | 201 | /** 202 | * Get deafult country id for different supported checkouts 203 | * 204 | * @return mixed 205 | */ 206 | public function getDefaultCountryId() 207 | { 208 | return $this->scopeConfig->getValue( 209 | 'payment/account/merchant_country', 210 | \Magento\Store\Model\ScopeInterface::SCOPE_STORE 211 | ); 212 | } 213 | 214 | /** 215 | * Save Store Config 216 | * 217 | * @param $key 218 | * @param $value 219 | * @param null $storeId 220 | * 221 | * @return bool 222 | * 223 | * @throws \Magento\Framework\Exception\NoSuchEntityException 224 | */ 225 | public function saveStoreConfig($key, $value, $storeId = null) 226 | { 227 | if (!$storeId) { 228 | $storeId = $this->storeManager->getStore()->getId(); 229 | } 230 | $this->configResource->saveConfig( 231 | $key, 232 | $value, 233 | 'stores', 234 | $storeId 235 | ); 236 | $this->cacheTypeList->cleanType('config'); 237 | return true; 238 | } 239 | 240 | /** 241 | * Reset web profile id 242 | * 243 | * @return boolean 244 | */ 245 | public function resetWebProfileId() 246 | { 247 | foreach ($this->storeManager->getStores() as $store) { 248 | $this->configResource->saveConfig( 249 | 'iways_paypalplus/dev/web_profile_id', 250 | false, 251 | 'stores', 252 | $store->getId() 253 | ); 254 | } 255 | $this->cacheTypeList->cleanType('config'); 256 | return true; 257 | } 258 | 259 | /** 260 | * Request payment experience from PayPal for current quote. 261 | * 262 | * @return string 263 | */ 264 | public function getPaymentExperience() 265 | { 266 | if ($this->scopeConfig->getValue( 267 | 'payment/iways_paypalplus_payment/active', 268 | \Magento\Store\Model\ScopeInterface::SCOPE_STORE 269 | ) 270 | ) { 271 | return $this->payPalPlusApiFactory->create()->getPaymentExperience(); 272 | } 273 | return false; 274 | } 275 | 276 | public function getPaymentThirdPartyModuls() 277 | { 278 | return $this->scopeConfig->getValue( 279 | 'payment/iways_paypalplus_payment/third_party_moduls', 280 | ScopeInterface::SCOPE_STORE 281 | ); 282 | } 283 | 284 | /** 285 | * Convert due date 286 | * 287 | * @param $date 288 | * 289 | * @return string 290 | */ 291 | public function convertDueDate($date) 292 | { 293 | $dateArray = explode('-', $date); 294 | $dateArray = array_reverse($dateArray); 295 | return implode('.', $dateArray); 296 | } 297 | } 298 | -------------------------------------------------------------------------------- /Model/Api.php: -------------------------------------------------------------------------------- 1 | 15 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License 3.0 16 | * @link https://www.i-ways.net 17 | */ 18 | 19 | namespace Iways\PayPalPlus\Model; 20 | 21 | use Magento\Framework\App\Filesystem\DirectoryList; 22 | use Magento\Framework\Encryption\EncryptorInterface; 23 | use Magento\Quote\Model\Quote; 24 | use PayPal\Api\Refund; 25 | use PayPal\Rest\ApiContext; 26 | use PayPal\Auth\OAuthTokenCredential; 27 | use PayPal\Api\Address; 28 | use PayPal\Api\WebProfile; 29 | use PayPal\Api\Presentation; 30 | use PayPal\Api\Payment as PayPalPayment; 31 | use PayPal\Api\Sale as PayPalSale; 32 | use PayPal\Api\Amount; 33 | use PayPal\Api\Details; 34 | use PayPal\Api\InputFields; 35 | use PayPal\Api\Item; 36 | use PayPal\Api\ItemList; 37 | use PayPal\Api\Payer; 38 | use PayPal\Api\RedirectUrls; 39 | use PayPal\Api\Transaction; 40 | use PayPal\Api\PayerInfo; 41 | use PayPal\Api\ShippingAddress; 42 | use PayPal\Api\PatchRequest; 43 | use PayPal\Api\Patch; 44 | use PayPal\Api\PaymentExecution; 45 | use PayPal\Exception\PayPalConnectionException; 46 | 47 | /** 48 | * Iways PayPal Rest Api wrapper 49 | * 50 | * @author robert 51 | */ 52 | class Api 53 | { 54 | /** 55 | * Webhook url already exists error code 56 | */ 57 | const WEBHOOK_URL_ALREADY_EXISTS = 'WEBHOOK_URL_ALREADY_EXISTS'; 58 | 59 | const PATCH_ADD = 'add'; 60 | const PATCH_REPLACE = 'replace'; 61 | 62 | const VALIDATION_ERROR = 'VALIDATION_ERROR'; 63 | 64 | /** 65 | * Protected $_apiContext 66 | * 67 | * @var null|ApiContext 68 | */ 69 | protected $_apiContext = null; // phpcs:ignore PSR2.Classes.PropertyDeclaration 70 | 71 | /** 72 | * Protected $_mode 73 | * 74 | * @var mixed|null 75 | */ 76 | protected $_mode = null; // phpcs:ignore PSR2.Classes.PropertyDeclaration 77 | 78 | /** 79 | * Protected $scopeConfig 80 | * 81 | * @var \Magento\Framework\App\Config\ScopeConfigInterface 82 | */ 83 | protected $scopeConfig; 84 | 85 | /** 86 | * Protected $registry 87 | * 88 | * @var \Magento\Framework\Registry 89 | */ 90 | protected $registry; 91 | 92 | /** 93 | * Protected $customerSession 94 | * 95 | * @var \Magento\Customer\Model\Session 96 | */ 97 | protected $customerSession; 98 | 99 | /** 100 | * Protected $payPalPlusHelper 101 | * 102 | * @var \Iways\PayPalPlus\Helper\Data 103 | */ 104 | protected $payPalPlusHelper; 105 | 106 | /** 107 | * Protected $logger 108 | * 109 | * @var \Psr\Log\LoggerInterface 110 | */ 111 | protected $logger; 112 | 113 | /** 114 | * Protected $storeManager 115 | * 116 | * @var \Magento\Store\Model\StoreManagerInterface 117 | */ 118 | protected $storeManager; 119 | 120 | /** 121 | * Protected $payPalPlusWebhookEventFactory 122 | * 123 | * @var \Iways\PayPalPlus\Model\Webhook\EventFactory 124 | */ 125 | protected $payPalPlusWebhookEventFactory; 126 | 127 | /** 128 | * Protected $checkoutSession 129 | * 130 | * @var \Magento\Checkout\Model\Session 131 | */ 132 | protected $checkoutSession; 133 | 134 | /** 135 | * Protected $backendSession 136 | * 137 | * @var \Magento\Backend\Model\Session 138 | */ 139 | protected $backendSession; 140 | 141 | /** 142 | * Protected $directoryList 143 | * 144 | * @var \Magento\Framework\App\Filesystem\DirectoryList 145 | */ 146 | protected $directoryList; 147 | 148 | /** 149 | * Protected $messageManage 150 | * 151 | * @var \Magento\Framework\Message\ManagerInterface 152 | */ 153 | protected $messageManager; 154 | 155 | /** 156 | * Protected $encryptor 157 | * 158 | * @var EncryptorInterface 159 | */ 160 | protected $encryptor; 161 | 162 | /** 163 | * Protected $assetRepo 164 | * 165 | * @var \Magento\Framework\View\Asset\Repository 166 | */ 167 | protected $assetRepo; 168 | 169 | /** 170 | * Protected $urlBuilder 171 | * 172 | * @var \Magento\Framework\UrlInterface 173 | */ 174 | protected $urlBuilder; 175 | 176 | /** 177 | * Prepare PayPal REST SDK ApiContent 178 | * 179 | * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig 180 | * @param \Magento\Framework\Registry $registry 181 | * @param \Magento\Customer\Model\Session $customerSession 182 | * @param \Iways\PayPalPlus\Helper\Data $payPalPlusHelper 183 | * @param \Psr\Log\LoggerInterface $logger 184 | * @param \Magento\Store\Model\StoreManagerInterface $storeManager 185 | * @param Webhook\EventFactory $payPalPlusWebhookEventFactory 186 | * @param \Magento\Checkout\Model\Session $session 187 | * @param \Magento\Backend\Model\Session $backendSession 188 | * @param DirectoryList $directoryList 189 | * @param \Magento\Framework\Message\ManagerInterface $messageManager 190 | * @param EncryptorInterface $encryptor 191 | * @param \Magento\Framework\View\Asset\Repository $assetRepo 192 | * @param \Magento\Framework\UrlInterface $urlBuilder 193 | */ 194 | public function __construct( 195 | \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, 196 | \Magento\Framework\Registry $registry, 197 | \Magento\Customer\Model\Session $customerSession, 198 | \Iways\PayPalPlus\Helper\Data $payPalPlusHelper, 199 | \Psr\Log\LoggerInterface $logger, 200 | \Magento\Store\Model\StoreManagerInterface $storeManager, 201 | \Iways\PayPalPlus\Model\Webhook\EventFactory $payPalPlusWebhookEventFactory, 202 | \Magento\Checkout\Model\Session $checkoutSession, 203 | \Magento\Backend\Model\Session $backendSession, 204 | \Magento\Framework\App\Filesystem\DirectoryList $directoryList, 205 | \Magento\Framework\Message\ManagerInterface $messageManager, 206 | EncryptorInterface $encryptor, 207 | \Magento\Framework\View\Asset\Repository $assetRepo, 208 | \Magento\Framework\UrlInterface $urlBuilder 209 | ) { 210 | $this->scopeConfig = $scopeConfig; 211 | $this->registry = $registry; 212 | $this->customerSession = $customerSession; 213 | $this->payPalPlusHelper = $payPalPlusHelper; 214 | $this->logger = $logger; 215 | $this->storeManager = $storeManager; 216 | $this->payPalPlusWebhookEventFactory = $payPalPlusWebhookEventFactory; 217 | $this->checkoutSession = $checkoutSession; 218 | $this->backendSession = $backendSession; 219 | $this->directoryList = $directoryList; 220 | $this->messageManager = $messageManager; 221 | $this->encryptor = $encryptor; 222 | $this->assetRepo = $assetRepo; 223 | $this->urlBuilder = $urlBuilder; 224 | $this->setApiContext(null); 225 | } 226 | 227 | /** 228 | * Function setApiContext 229 | * 230 | * @param null $website 231 | * 232 | * @return $this 233 | * 234 | * @throws \Magento\Framework\Exception\FileSystemException 235 | */ 236 | public function setApiContext($website = null) 237 | { 238 | $this->_apiContext = new ApiContext( 239 | new OAuthTokenCredential( 240 | $this->scopeConfig->getValue( 241 | 'iways_paypalplus/api/client_id', 242 | \Magento\Store\Model\ScopeInterface::SCOPE_STORE, 243 | $website 244 | ), 245 | $this->scopeConfig->getValue( 246 | 'iways_paypalplus/api/client_secret', 247 | \Magento\Store\Model\ScopeInterface::SCOPE_STORE, 248 | $website 249 | ) 250 | ) 251 | ); 252 | 253 | $this->_mode = $this->scopeConfig->getValue( 254 | 'iways_paypalplus/api/mode', 255 | \Magento\Store\Model\ScopeInterface::SCOPE_STORE, 256 | $website 257 | ); 258 | 259 | $this->_apiContext->setConfig( 260 | [ 261 | 'http.ConnectionTimeOut' => 30, 262 | 'http.Retry' => 1, 263 | 'cache.enabled' => $this->scopeConfig->getValue( 264 | 'iways_paypalplus/dev/token_cache', 265 | \Magento\Store\Model\ScopeInterface::SCOPE_STORE, 266 | $website 267 | ), 268 | 'mode' => $this->_mode, 269 | 'log.LogEnabled' => $this->scopeConfig->getValue( 270 | 'iways_paypalplus/dev/debug', 271 | \Magento\Store\Model\ScopeInterface::SCOPE_STORE, 272 | $website 273 | ), 274 | 'log.FileName' => $this->directoryList->getPath(DirectoryList::LOG) . '/PayPal.log', 275 | 'log.LogLevel' => 'INFO' 276 | ] 277 | ); 278 | $this->_apiContext->addRequestHeader('PayPal-Partner-Attribution-Id', 'Magento_Cart_PayPalPlusMagento2'); 279 | return $this; 280 | } 281 | 282 | /** 283 | * Get ApprovalLink for curretn Quote 284 | * 285 | * @return bool|mixed|string|null 286 | * 287 | * @throws \Magento\Framework\Exception\LocalizedException 288 | */ 289 | public function getPaymentExperience() 290 | { 291 | $paymentExperience = $this->registry->registry('payment_experience'); 292 | if ($paymentExperience === null) { 293 | $webProfile = $this->buildWebProfile(); 294 | if ($webProfile) { 295 | $payment = $this->createPayment($webProfile, $this->getQuote()); 296 | $paymentExperience = $payment ? $payment->getApprovalLink() : false; 297 | } else { 298 | $paymentExperience = false; 299 | } 300 | $this->registry->register('payment_experience', $paymentExperience); 301 | } 302 | return $paymentExperience; 303 | } 304 | 305 | /** 306 | * Get a payment 307 | * 308 | * @param string $paymentId 309 | * 310 | * @return \PayPal\Api\Payment 311 | */ 312 | public function getPayment($paymentId) 313 | { 314 | return PayPalPayment::get($paymentId, $this->_apiContext); 315 | } 316 | 317 | /** 318 | * Get a sale 319 | * 320 | * @param string $paymentId 321 | * 322 | * @return \PayPal\Api\Payment 323 | */ 324 | public function getSale($paymentId) 325 | { 326 | return PayPalSale::get($paymentId, $this->_apiContext); 327 | } 328 | 329 | /** 330 | * Create payment for current quote 331 | * 332 | * @param WebProfile $webProfile 333 | * @param \Magento\Quote\Model\Quote $quote 334 | * 335 | * @return boolean|PayPalPayment 336 | */ 337 | public function createPayment($webProfile, $quote, $taxFailure = false) 338 | { 339 | /** 340 | * Skip if grand total is zero 341 | */ 342 | if ($quote->getBaseGrandTotal() == "0.0000") { 343 | return false; 344 | } 345 | 346 | $quote->setTotalsCollectedFlag(false)->collectTotals()->save(); 347 | 348 | $payer = $this->buildPayer($quote); 349 | 350 | $itemList = $this->buildItemList($quote, $taxFailure); 351 | 352 | $amount = $this->buildAmount($quote); 353 | 354 | $transaction = new Transaction(); 355 | $transaction->setAmount($amount); 356 | $transaction->setItemList($itemList); 357 | 358 | $redirectUrls = new RedirectUrls(); 359 | $redirectUrls->setReturnUrl($this->urlBuilder->getUrl('paypalplus/order/create')) 360 | ->setCancelUrl($this->urlBuilder->getUrl('paypalplus/checkout/cancel')); 361 | 362 | $payment = new PayPalPayment(); 363 | $payment->setIntent("sale") 364 | ->setExperienceProfileId($webProfile->getId()) 365 | ->setPayer($payer) 366 | ->setRedirectUrls($redirectUrls) 367 | ->setTransactions([$transaction]); 368 | 369 | try { 370 | $response = $payment->create($this->_apiContext); 371 | $this->customerSession->setPayPalPaymentId($response->getId()); 372 | $this->customerSession->setPayPalPaymentPatched(null); 373 | } catch (PayPalConnectionException $ex) { 374 | if (!$taxFailure) { 375 | return $this->createPayment($webProfile, $quote, true); 376 | } 377 | $this->payPalPlusHelper->handleException($ex); 378 | return false; 379 | } catch (\Exception $e) { 380 | $this->logger->critical($e); 381 | return false; 382 | } 383 | return $response; 384 | } 385 | 386 | /** 387 | * Function patchPayment 388 | * 389 | * @param $quote 390 | * 391 | * @return bool 392 | * 393 | * @throws \Exception 394 | */ 395 | public function patchPayment($quote) 396 | { 397 | if ($this->customerSession->getPayPalPaymentId()) { 398 | $payment = PayPalPayment::get($this->customerSession->getPayPalPaymentId(), $this->_apiContext); 399 | $patchRequest = new PatchRequest(); 400 | 401 | $quote->setTotalsCollectedFlag(false)->collectTotals()->save(); 402 | 403 | if (!$quote->isVirtual()) { 404 | $shippingAddress = $this->buildShippingAddress($quote); 405 | $addressPatch = new Patch(); 406 | $addressPatch->setOp(self::PATCH_ADD); 407 | $addressPatch->setPath('/transactions/0/item_list/shipping_address'); 408 | $addressPatch->setValue($shippingAddress); 409 | $patchRequest->addPatch($addressPatch); 410 | } 411 | 412 | $payerInfo = $this->buildBillingAddress($quote); 413 | $payerInfoPatch = new Patch(); 414 | $payerInfoPatch->setOp(self::PATCH_ADD); 415 | $payerInfoPatch->setPath('/potential_payer_info/billing_address'); 416 | $payerInfoPatch->setValue($payerInfo); 417 | $patchRequest->addPatch($payerInfoPatch); 418 | 419 | $itemList = $this->buildItemList($quote); 420 | $itemListPatch = new Patch(); 421 | $itemListPatch->setOp('replace'); 422 | $itemListPatch->setPath('/transactions/0/item_list'); 423 | $itemListPatch->setValue($itemList); 424 | $patchRequest->addPatch($itemListPatch); 425 | 426 | $amount = $this->buildAmount($quote); 427 | $amountPatch = new Patch(); 428 | $amountPatch->setOp('replace'); 429 | $amountPatch->setPath('/transactions/0/amount'); 430 | $amountPatch->setValue($amount); 431 | $patchRequest->addPatch($amountPatch); 432 | 433 | try { 434 | $response = $payment->update($patchRequest, $this->_apiContext); 435 | return $response; 436 | } catch (PayPalConnectionException $ex) { 437 | $message = json_decode($ex->getData()); 438 | if (isset($message->name) 439 | && isset($message->details) 440 | && $message->name == self::VALIDATION_ERROR 441 | ) { 442 | $validationMessage = __('Your address is invalid. Please check following errors: '); 443 | foreach ($message->details as $detail) { 444 | if (isset($detail->field) && isset($detail->issue)) { 445 | $validationMessage .= 446 | __( 447 | 'Field: "%1" - %2. ', 448 | [ 449 | $detail->field, 450 | $detail->issue 451 | ] 452 | ); 453 | } 454 | } 455 | $this->logger->critical($validationMessage); // throw new \Exception($validationMessage); 456 | } 457 | } 458 | } 459 | return false; 460 | } 461 | 462 | /** 463 | * Patches invoice number to PayPal transaction 464 | * (Magento order increment id) 465 | * 466 | * @param string $paymentId 467 | * @param string $invoiceNumber 468 | * 469 | * @return bool 470 | */ 471 | public function patchInvoiceNumber($paymentId, $invoiceNumber) 472 | { 473 | $payment = PayPalPayment::get($paymentId, $this->_apiContext); 474 | 475 | $patchRequest = new PatchRequest(); 476 | 477 | $invoiceNumberPatch = new Patch(); 478 | $invoiceNumberPatch->setOp('add'); 479 | $invoiceNumberPatch->setPath('/transactions/0/invoice_number'); 480 | $invoiceNumberPatch->setValue($invoiceNumber); 481 | $patchRequest->addPatch($invoiceNumberPatch); 482 | 483 | $response = $payment->update($patchRequest, $this->_apiContext); 484 | 485 | return $response; 486 | } 487 | 488 | /** 489 | * Execute an existing payment 490 | * 491 | * @param string $paymentId 492 | * @param string $payerId 493 | * 494 | * @return boolean|\PayPal\Api\Payment 495 | */ 496 | public function executePayment($paymentId, $payerId) 497 | { 498 | try { 499 | $payment = $this->getPayment($paymentId); 500 | $paymentExecution = new PaymentExecution(); 501 | $paymentExecution->setPayerId($payerId); 502 | return $payment->execute($paymentExecution, $this->_apiContext); 503 | } catch (PayPalConnectionException $ex) { 504 | $this->payPalPlusHelper->handleException($ex); 505 | return false; 506 | } catch (\Exception $e) { 507 | $this->logger->critical($e); 508 | return false; 509 | } 510 | } 511 | 512 | /** 513 | * Refund Payment 514 | * 515 | * @param $paymentId 516 | * @param $amount 517 | * 518 | * @return Refund 519 | * 520 | * @throws \Magento\Framework\Exception\NoSuchEntityException 521 | */ 522 | public function refundPayment($paymentId, $amount) 523 | { 524 | $transactions = []; 525 | if (!$transactions = $this->getPayment($paymentId)->getTransactions()) { 526 | $saleJson = $this->getSale($paymentId); 527 | $transactions = $this->getPayment($saleJson->parent_payment)->getTransactions(); 528 | } 529 | $relatedResources = $transactions[0]->getRelatedResources(); 530 | $sale = $relatedResources[0]->getSale(); 531 | $refund = new \PayPal\Api\Refund(); 532 | 533 | $ppAmount = new Amount(); 534 | $ppAmount->setCurrency($this->storeManager->getStore()->getCurrentCurrencyCode())->setTotal($amount); 535 | $refund->setAmount($ppAmount); 536 | 537 | return $sale->refund($refund, $this->_apiContext); 538 | } 539 | 540 | /** 541 | * Get a list of all registrated webhooks for $this->_apiContext 542 | * 543 | * @return bool|\PayPal\Api\WebhookList 544 | */ 545 | public function getWebhooks() 546 | { 547 | $webhooks = new \PayPal\Api\Webhook(); 548 | try { 549 | return $webhooks->getAll($this->_apiContext); 550 | } catch (PayPalConnectionException $ex) { 551 | $this->payPalPlusHelper->handleException($ex); 552 | return false; 553 | } catch (\Exception $e) { 554 | $this->logger->critical($e); 555 | return false; 556 | } 557 | } 558 | 559 | /** 560 | * Retrive an webhook event 561 | * 562 | * @param $webhookEventId 563 | * 564 | * @return bool|\PayPal\Api\WebhookEvent 565 | */ 566 | public function getWebhookEvent($webhookEventId) 567 | { 568 | try { 569 | $webhookEvent = new \PayPal\Api\WebhookEvent(); 570 | return $webhookEvent->get($webhookEventId, $this->_apiContext); 571 | } catch (PayPalConnectionException $ex) { 572 | $this->payPalPlusHelper->handleException($ex); 573 | return false; 574 | } catch (\Exception $e) { 575 | $this->logger->critical($e); 576 | return false; 577 | } 578 | } 579 | 580 | /** 581 | * Get a list of all available event types 582 | * 583 | * @return bool|\PayPal\Api\WebhookEventTypeList 584 | */ 585 | public function getWebhooksEventTypes() 586 | { 587 | $webhookEventType = new \PayPal\Api\WebhookEventType(); 588 | try { 589 | return $webhookEventType->availableEventTypes($this->_apiContext); 590 | } catch (PayPalConnectionException $ex) { 591 | $this->payPalPlusHelper->handleException($ex); 592 | return false; 593 | } catch (\Exception $e) { 594 | $this->logger->critical($e); 595 | return false; 596 | } 597 | return false; 598 | } 599 | 600 | /** 601 | * Creates a webhook 602 | * 603 | * @return bool|\PayPal\Api\Webhook 604 | */ 605 | public function createWebhook() 606 | { 607 | $webhook = new \PayPal\Api\Webhook(); 608 | $webhook->setUrl($this->payPalPlusHelper->getWebhooksUrl()); 609 | $webhookEventTypes = []; 610 | foreach ($this->payPalPlusWebhookEventFactory->create()->getSupportedWebhookEvents() as $webhookEvent) { 611 | $webhookEventType = new \PayPal\Api\WebhookEventType(); 612 | $webhookEventType->setName($webhookEvent); 613 | $webhookEventTypes[] = $webhookEventType; 614 | } 615 | $webhook->setEventTypes($webhookEventTypes); 616 | try { 617 | $webhookData = $webhook->create($this->_apiContext); 618 | $this->saveWebhookId($webhookData->getId()); 619 | return $webhookData; 620 | } catch (PayPalConnectionException $ex) { 621 | if ($ex->getData()) { 622 | $data = json_decode($ex->getData(), true); 623 | if (isset($data['name']) && $data['name'] == self::WEBHOOK_URL_ALREADY_EXISTS) { 624 | return true; 625 | } 626 | } 627 | $this->payPalPlusHelper->handleException($ex); 628 | return false; 629 | } catch (\Exception $e) { 630 | $this->logger->critical($e); 631 | return false; 632 | } 633 | return false; 634 | } 635 | 636 | /** 637 | * Delete webhook with webhookId for PayPal APP $this->_apiContext 638 | * 639 | * @param int $webhookId 640 | * 641 | * @return bool 642 | */ 643 | public function deleteWebhook($webhookId) 644 | { 645 | $webhook = new \PayPal\Api\Webhook(); 646 | $webhook->setId($webhookId); 647 | try { 648 | return $webhook->delete($this->_apiContext); 649 | } catch (PayPalConnectionException $ex) { 650 | $this->payPalPlusHelper->handleException($ex); 651 | return false; 652 | } catch (\Exception $e) { 653 | $this->logger->critical($e); 654 | return false; 655 | } 656 | return false; 657 | } 658 | 659 | /** 660 | * Validate WebhookEvent 661 | * 662 | * @param $rawBody Raw request string 663 | * 664 | * @return bool|\PayPal\Api\WebhookEvent 665 | */ 666 | public function validateWebhook($rawBody) 667 | { 668 | try { 669 | $webhookEvent = new \PayPal\Api\WebhookEvent(); 670 | return $webhookEvent->validateAndGetReceivedEvent($rawBody, $this->_apiContext); 671 | } catch (\Exception $ex) { 672 | $this->logger->critical($ex); 673 | return false; 674 | } 675 | } 676 | 677 | /** 678 | * Build ShippingAddress from quote 679 | * 680 | * @param \Magento\Quote\Model\Quote $quote 681 | * 682 | * @return ShippingAddress 683 | */ 684 | protected function buildShippingAddress($quote) 685 | { 686 | $address = $quote->getShippingAddress(); 687 | $addressCheckerArray = [ 688 | 'setRecipientName' => $this->buildFullName($address), 689 | 'setLine1' => implode(' ', $address->getStreet()), 690 | 'setCity' => $address->getCity(), 691 | 'setCountryCode' => $address->getCountryId(), 692 | 'setPostalCode' => $address->getPostcode(), 693 | 'setState' => $address->getRegion(), 694 | ]; 695 | $allowedEmpty = ['setPhone', 'setState']; 696 | $shippingAddress = new ShippingAddress(); 697 | foreach ($addressCheckerArray as $setter => $value) { 698 | if (empty($value) && !in_array($setter, $allowedEmpty)) { 699 | return false; 700 | } 701 | $shippingAddress->{$setter}($value); 702 | } 703 | 704 | return $shippingAddress; 705 | } 706 | 707 | /** 708 | * Build BillingAddress from quote 709 | * 710 | * @param \Magento\Quote\Model\Quote $quote 711 | * 712 | * @return ShippingAddress|boolean 713 | */ 714 | protected function buildBillingAddress($quote) 715 | { 716 | $address = $quote->getBillingAddress(); 717 | $addressCheckerArray = [ 718 | 'setLine1' => implode(' ', $address->getStreet()), 719 | 'setCity' => $address->getCity(), 720 | 'setCountryCode' => $address->getCountryId(), 721 | 'setPostalCode' => $address->getPostcode(), 722 | 'setState' => $address->getRegion(), 723 | ]; 724 | $allowedEmpty = ['setPhone', 'setState']; 725 | $billingAddress = new Address(); 726 | foreach ($addressCheckerArray as $setter => $value) { 727 | if (empty($value) && !in_array($setter, $allowedEmpty)) { 728 | return false; 729 | } 730 | $billingAddress->{$setter}($value); 731 | } 732 | 733 | return $billingAddress; 734 | } 735 | 736 | /** 737 | * Build Payer for payment 738 | * 739 | * @param $quote 740 | * 741 | * @return Payer 742 | */ 743 | protected function buildPayer($quote) 744 | { 745 | $payer = new Payer(); 746 | $payer->setPaymentMethod("paypal"); 747 | return $payer; 748 | } 749 | 750 | /** 751 | * Build PayerInfo for Payer 752 | * 753 | * @param $quote 754 | * 755 | * @return PayerInfo 756 | */ 757 | protected function buildPayerInfo($quote) 758 | { 759 | $payerInfo = new PayerInfo(); 760 | $address = $quote->getBillingAddress(); 761 | if ($address->getFirstname()) { 762 | $payerInfo->setFirstName($address->getFirstname()); 763 | } 764 | if ($address->getMiddlename()) { 765 | $payerInfo->setMiddleName($address->getMiddlename()); 766 | } 767 | if ($address->getLastname()) { 768 | $payerInfo->setLastName($address->getLastname()); 769 | } 770 | 771 | $billingAddress = $this->buildBillingAddress($quote); 772 | if ($billingAddress) { 773 | $payerInfo->setBillingAddress($billingAddress); 774 | } 775 | return $payerInfo; 776 | } 777 | 778 | /** 779 | * Get fullname from address 780 | * 781 | * @param \Magento\Quote\Model\Quote\Address $address 782 | * 783 | * @return type 784 | */ 785 | protected function buildFullName($address) 786 | { 787 | $name = []; 788 | if ($address->getFirstname()) { 789 | $name[] = $address->getFirstname(); 790 | } 791 | if ($address->getMiddlename()) { 792 | $name[] = $address->getMiddlename(); 793 | } 794 | if ($address->getLastname()) { 795 | $name[] = $address->getLastname(); 796 | } 797 | return implode(' ', $name); 798 | } 799 | 800 | /** 801 | * Build Item List 802 | * 803 | * @param $quote 804 | * @param bool $taxFailure 805 | * 806 | * @return ItemList 807 | */ 808 | protected function buildItemList($quote, $taxFailure = false) 809 | { 810 | $itemArray = []; 811 | $itemList = new ItemList(); 812 | $currencyCode = $quote->getBaseCurrencyCode(); 813 | 814 | if (!$taxFailure) { 815 | foreach ($quote->getAllVisibleItems() as $quoteItem) { 816 | $item = new Item(); 817 | if ($quoteItem->getQty() > 1) { 818 | $item->setName($quoteItem->getName() . ' x' . $quoteItem->getQty()); 819 | } else { 820 | $item->setName($quoteItem->getName()); 821 | } 822 | $item 823 | ->setSku($quoteItem->getSku()) 824 | ->setCurrency($currencyCode) 825 | ->setQuantity(1) 826 | ->setPrice($quoteItem->getBaseRowTotal()); 827 | 828 | $itemArray[] = $item; 829 | } 830 | 831 | $itemList->setItems($itemArray); 832 | } 833 | return $itemList; 834 | } 835 | 836 | /** 837 | * Build Amount 838 | * 839 | * @param Quote $quote 840 | * 841 | * @return Amount 842 | */ 843 | protected function buildAmount($quote) 844 | { 845 | $details = new Details(); 846 | $baseShippingAmount = $quote->getShippingAddress()->getBaseShippingAmount(); 847 | $shippingCost = ($quote->getShippingAddress()->getFreeShipping() 848 | ? 0 849 | : ($baseShippingAmount ?: $quote->getShippingAddress()->getShippingAmount())); 850 | 851 | $details->setShipping($shippingCost) 852 | ->setTax( 853 | $quote->getShippingAddress()->getBaseTaxAmount() 854 | + $quote->getShippingAddress()->getBaseHiddenTaxAmount() 855 | + $quote->getBillingAddress()->getBaseTaxAmount() 856 | + $quote->getBillingAddress()->getBaseHiddenTaxAmount() 857 | ) 858 | ->setSubtotal( 859 | $quote->getBaseSubtotal() 860 | ); 861 | 862 | if ($quote->isVirtual()) { 863 | if ($quote->getBillingAddress()->getDiscountAmount()) { 864 | $details->setShippingDiscount( 865 | -( 866 | $quote->getBillingAddress()->getDiscountAmount() 867 | + $quote->getBillingAddress()->getBaseDiscountTaxCompensationAmount() 868 | ) 869 | ); 870 | } 871 | } else { 872 | if ($quote->getShippingAddress()->getDiscountAmount()) { 873 | $details->setShippingDiscount( 874 | -( 875 | $quote->getShippingAddress()->getDiscountAmount() 876 | + $quote->getShippingAddress()->getBaseDiscountTaxCompensationAmount() 877 | ) 878 | ); 879 | } 880 | } 881 | 882 | $total = $quote->getBaseGrandTotal(); 883 | if ((float)$quote->getShippingAddress()->getBaseShippingAmount() == 0 884 | && (float)$quote->getShippingAddress()->getBaseShippingInclTax() >= 0 885 | ) { 886 | $total = (float)$total - (float)$quote->getShippingAddress()->getBaseShippingInclTax(); 887 | } 888 | 889 | $amount = new Amount(); 890 | $amount->setCurrency($quote->getBaseCurrencyCode()) 891 | ->setDetails($details) 892 | ->setTotal($total); 893 | 894 | return $amount; 895 | } 896 | 897 | /** 898 | * Build WebProfil 899 | * 900 | * @return bool|\PayPal\Api\CreateProfileResponse|WebProfile 901 | * 902 | * @throws \Magento\Framework\Exception\LocalizedException 903 | */ 904 | protected function buildWebProfile() 905 | { 906 | $webProfile = new WebProfile(); 907 | if ($this->scopeConfig->getValue( 908 | 'iways_paypalplus/dev/web_profile_id', 909 | \Magento\Store\Model\ScopeInterface::SCOPE_STORE 910 | ) 911 | ) { 912 | $webProfile->setId( 913 | $this->scopeConfig->getValue( 914 | 'iways_paypalplus/dev/web_profile_id', 915 | \Magento\Store\Model\ScopeInterface::SCOPE_STORE 916 | ) 917 | ); 918 | return $webProfile; 919 | } 920 | try { 921 | $webProfile->setName('magento_' . microtime()); 922 | $webProfile->setPresentation($this->buildWebProfilePresentation()); 923 | $inputFields = new InputFields(); 924 | $inputFields->setAddressOverride(1); 925 | $webProfile->setInputFields($inputFields); 926 | $response = $webProfile->create($this->_apiContext); 927 | $this->saveWebProfileId($response->getId()); 928 | return $response; 929 | } catch (PayPalConnectionException $ex) { 930 | $this->payPalPlusHelper->handleException($ex); 931 | } 932 | return false; 933 | } 934 | 935 | /** 936 | * Build Web Profile Presentation 937 | * 938 | * @return Presentation 939 | * 940 | * @throws \Magento\Framework\Exception\LocalizedException 941 | */ 942 | protected function buildWebProfilePresentation() 943 | { 944 | $presentation = new Presentation(); 945 | $presentation->setBrandName($this->storeManager->getWebsite()->getName()); 946 | $presentation->setLogoImage($this->getHeaderImage()); 947 | $presentation->setLocaleCode( 948 | substr( 949 | $this->scopeConfig->getValue('general/locale/code', \Magento\Store\Model\ScopeInterface::SCOPE_STORE), 950 | -2 951 | ) 952 | ); 953 | return $presentation; 954 | } 955 | 956 | /** 957 | * Get Header Logo for Web experience 958 | * 959 | * @return string 960 | */ 961 | protected function getHeaderImage() 962 | { 963 | if ($this->scopeConfig->getValue( 964 | 'iways_paypalplus/api/hdrimg', 965 | \Magento\Store\Model\ScopeInterface::SCOPE_WEBSITE 966 | ) 967 | ) { 968 | return $this->scopeConfig->getValue( 969 | 'iways_paypalplus/api/hdrimg', 970 | \Magento\Store\Model\ScopeInterface::SCOPE_WEBSITE 971 | ); 972 | } 973 | $folderName = \Magento\Config\Model\Config\Backend\Image\Logo::UPLOAD_DIR; 974 | $storeLogoPath = $this->scopeConfig->getValue( 975 | 'design/header/logo_src', 976 | \Magento\Store\Model\ScopeInterface::SCOPE_STORE 977 | ); 978 | if ($storeLogoPath) { 979 | $path = $folderName . '/' . $storeLogoPath; 980 | return $this->urlBuilder->getBaseUrl(['_type' => \Magento\Framework\UrlInterface::URL_TYPE_MEDIA]) . $path; 981 | } 982 | return $this->assetRepo->getUrlWithParams('images/logo.svg', ['_secure' => true]); 983 | } 984 | 985 | /** 986 | * Reset web profile id 987 | * 988 | * @return boolean 989 | */ 990 | public function resetWebProfileId() 991 | { 992 | return $this->payPalPlusHelper->resetWebProfileId(); 993 | } 994 | 995 | /** 996 | * Save WebProfileId 997 | * 998 | * @param $id 999 | * 1000 | * @return bool 1001 | * 1002 | * @throws \Magento\Framework\Exception\NoSuchEntityException 1003 | */ 1004 | protected function saveWebProfileId($id) 1005 | { 1006 | return $this->payPalPlusHelper->saveStoreConfig('iways_paypalplus/dev/web_profile_id', $id); 1007 | } 1008 | 1009 | /** 1010 | * Save WebhookId 1011 | * 1012 | * @param $id 1013 | * 1014 | * @return bool 1015 | * 1016 | * @throws \Magento\Framework\Exception\NoSuchEntityException 1017 | */ 1018 | protected function saveWebhookId($id) 1019 | { 1020 | return $this->payPalPlusHelper->saveStoreConfig('iways_paypalplus/dev/webhook_id', $id); 1021 | } 1022 | 1023 | /** 1024 | * Get current quote 1025 | * 1026 | * @return \Magento\Quote\Model\Quote 1027 | */ 1028 | protected function getQuote() 1029 | { 1030 | return $this->checkoutSession->getQuote(); 1031 | } 1032 | 1033 | /** 1034 | * Check if PayPal credentails are valid for given configuration 1035 | * Uses WebProfile::get_list() 1036 | * 1037 | * @param $website 1038 | * 1039 | * @return bool 1040 | * 1041 | * @throws \Magento\Framework\Exception\FileSystemException 1042 | */ 1043 | public function testCredentials($website) 1044 | { 1045 | try { 1046 | $this->setApiContext($website); 1047 | WebProfile::get_list($this->_apiContext); 1048 | return true; 1049 | } catch (PayPalConnectionException $ex) { 1050 | $this->messageManager->addError( 1051 | __('Provided credentials not valid.') 1052 | ); 1053 | return false; 1054 | } catch (\Exception $e) { 1055 | $this->logger->critical($e); 1056 | return false; 1057 | } 1058 | } 1059 | } 1060 | -------------------------------------------------------------------------------- /Model/ConfigProvider.php: -------------------------------------------------------------------------------- 1 | 15 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License 3.0 16 | * @link https://www.i-ways.net 17 | */ 18 | 19 | namespace Iways\PayPalPlus\Model; 20 | 21 | use Magento\Checkout\Model\ConfigProviderInterface; 22 | use Magento\Framework\Escaper; 23 | use Magento\Framework\View\Asset\Repository; 24 | use Magento\Payment\Helper\Data as PaymentHelper; 25 | use Psr\Log\LoggerInterface; 26 | 27 | class ConfigProvider implements ConfigProviderInterface 28 | { 29 | /** 30 | * Protected $methodCode 31 | * 32 | * @var string[] 33 | */ 34 | protected $methodCode = Payment::CODE; 35 | 36 | /** 37 | * Protected $method 38 | * 39 | * @var Checkmo 40 | */ 41 | protected $method; 42 | 43 | /** 44 | * Protected $escaper 45 | * 46 | * @var Escaper 47 | */ 48 | protected $escaper; 49 | 50 | /** 51 | * Protected $payPalPlusHelper 52 | * 53 | * @var \Iways\PayPalPlus\Helper\Data 54 | */ 55 | protected $payPalPlusHelper; 56 | 57 | /** 58 | * Protected $checkoutSession 59 | * 60 | * @var \Magento\Checkout\Model\Session 61 | */ 62 | protected $checkoutSession; 63 | 64 | /** 65 | * Protected $scopeConfig 66 | * 67 | * @var \Magento\Framework\App\Config\ScopeConfigInterface 68 | */ 69 | protected $scopeConfig; 70 | 71 | /** 72 | * Protected $paymentConfig 73 | * 74 | * @var \Magento\Payment\Model\Config 75 | */ 76 | protected $paymentConfig; 77 | 78 | /** 79 | * Url Builder 80 | * 81 | * @var \Magento\Framework\UrlInterface 82 | */ 83 | protected $urlBuilder; 84 | 85 | /** 86 | * Protected $methodList 87 | * 88 | * @var MethodList 89 | */ 90 | protected $methodList; 91 | 92 | /** 93 | * Protected $logger 94 | * 95 | * @var \Psr\Log\LoggerInterface 96 | */ 97 | protected $logger; 98 | 99 | /** 100 | * Protected $assetRepo 101 | * 102 | * @var \Magento\Framework\View\Asset\Repository 103 | */ 104 | protected $assetRepo; 105 | 106 | /** 107 | * Protected $request 108 | * 109 | * @var \Magento\Framework\App\Request\Http 110 | */ 111 | protected $request; 112 | 113 | /** 114 | * ConfigProvider constructor 115 | * 116 | * @param PaymentHelper $paymentHelper 117 | * @param Escaper $escaper 118 | * @param \Iways\PayPalPlus\Helper\Data $payPalPlusHelper 119 | * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig 120 | * @param \Magento\Checkout\Model\Session $checkoutSession 121 | * @param \Magento\Payment\Model\Config $paymentConfig 122 | * @param MethodList $methodList 123 | * @param \Magento\Framework\UrlInterface $urlBuilder 124 | * @param LoggerInterface $logger 125 | * @param Magento\Framework\View\Asset\Repository $assetRepo 126 | * 127 | * @throws \Magento\Framework\Exception\LocalizedException 128 | */ 129 | public function __construct( 130 | PaymentHelper $paymentHelper, 131 | Escaper $escaper, 132 | \Iways\PayPalPlus\Helper\Data $payPalPlusHelper, 133 | \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, 134 | \Magento\Checkout\Model\Session $checkoutSession, 135 | \Magento\Payment\Model\Config $paymentConfig, 136 | MethodList $methodList, 137 | \Magento\Framework\UrlInterface $urlBuilder, 138 | LoggerInterface $logger, 139 | Repository $assetRepo, 140 | \Magento\Framework\App\Request\Http $request 141 | ) { 142 | $this->escaper = $escaper; 143 | $this->method = $paymentHelper->getMethodInstance($this->methodCode); 144 | $this->payPalPlusHelper = $payPalPlusHelper; 145 | $this->scopeConfig = $scopeConfig; 146 | $this->checkoutSession = $checkoutSession; 147 | $this->paymentConfig = $paymentConfig; 148 | $this->methodList = $methodList; 149 | $this->urlBuilder = $urlBuilder; 150 | $this->logger = $logger; 151 | $this->assetRepo = $assetRepo; 152 | $this->request = $request; 153 | } 154 | 155 | /** 156 | * {@inheritdoc} 157 | */ 158 | public function getConfig() 159 | { 160 | if ($this->request->getFullActionName() == 'checkout_cart_index') { 161 | return []; 162 | } 163 | 164 | $showPuiOnSandbox = $this->scopeConfig->getValue( 165 | 'iways_paypalplus/dev/pui_sandbox', 166 | \Magento\Store\Model\ScopeInterface::SCOPE_STORE 167 | ) ? true : false; 168 | 169 | $showLoadingIndicator = $this->scopeConfig->getValue( 170 | 'payment/iways_paypalplus_payment/show_loading_indicator', 171 | \Magento\Store\Model\ScopeInterface::SCOPE_STORE 172 | ) ? true : false; 173 | 174 | $mode = $this->scopeConfig->getValue( 175 | 'iways_paypalplus/api/mode', 176 | \Magento\Store\Model\ScopeInterface::SCOPE_STORE 177 | ); 178 | 179 | $language = $this->scopeConfig->getValue( 180 | 'general/locale/code', 181 | \Magento\Store\Model\ScopeInterface::SCOPE_STORE 182 | ); 183 | 184 | return $this->method->isAvailable() ? [ 185 | 'payment' => [ 186 | 'iways_paypalplus_payment' => [ 187 | 'paymentExperience' => $this->payPalPlusHelper->getPaymentExperience(), 188 | 'showPuiOnSandbox' => $showPuiOnSandbox, 189 | 'showLoadingIndicator' => $showLoadingIndicator, 190 | 'mode' => $mode, 191 | 'country' => $this->getCountry(), 192 | 'language' => $language, 193 | 'thirdPartyPaymentMethods' => $this->getThirdPartyMethods() 194 | ], 195 | ], 196 | ] : []; 197 | } 198 | 199 | protected function getCountry() 200 | { 201 | $billingAddress = $this->checkoutSession->getQuote()->getBillingAddress(); 202 | if ($billingAddress->getCountryId()) { 203 | return $billingAddress->getCountryId(); 204 | } 205 | 206 | $shippingAddress = $this->checkoutSession->getQuote()->getShippingAddress(); 207 | if ($shippingAddress->getCountryId()) { 208 | return $shippingAddress->getCountryId(); 209 | } 210 | 211 | return $this->scopeConfig->getValue( 212 | 'paypal/general/merchant_country', 213 | \Magento\Store\Model\ScopeInterface::SCOPE_STORE 214 | ); 215 | } 216 | 217 | protected function getThirdPartyMethods() 218 | { 219 | $this->methodList->setCheckPPP(true); 220 | $paymentMethods = $this->methodList->getAvailableMethods($this->checkoutSession->getQuote()); 221 | $this->methodList->setCheckPPP(false); 222 | $allowedPPPMethods = explode( 223 | ',', 224 | $this->scopeConfig->getValue( 225 | 'payment/iways_paypalplus_payment/third_party_moduls', 226 | \Magento\Store\Model\ScopeInterface::SCOPE_STORE 227 | ) 228 | ); 229 | $methods = []; 230 | foreach ($paymentMethods as $paymentMethod) { 231 | if (strpos($paymentMethod->getCode(), 'paypal') === false 232 | && in_array($paymentMethod->getCode(), $allowedPPPMethods) 233 | ) { 234 | if ($methodImage = $this->scopeConfig->getValue( 235 | 'payment/iways_paypalplus_section/third_party_modul_info_image_' . $paymentMethod->getCode(), 236 | \Magento\Store\Model\ScopeInterface::SCOPE_STORE 237 | ) 238 | ) { 239 | if (substr($methodImage, 0, 4) != 'http') { 240 | $methodImage = $this->assetRepo->getUrl($methodImage); 241 | } 242 | } 243 | 244 | $method = [ 245 | 'redirectUrl' => $this->urlBuilder->getUrl('checkout', ['_secure' => true]), 246 | 'methodName' => $paymentMethod->getTitle(), 247 | 'imageUrl' => $methodImage, 248 | 'description' => $this->scopeConfig->getValue( 249 | 'payment/iways_paypalplus_section/third_party_modul_info_text_' . $paymentMethod->getCode(), 250 | \Magento\Store\Model\ScopeInterface::SCOPE_STORE 251 | ), 252 | ]; 253 | $methods[$paymentMethod->getCode()] = $method; 254 | } 255 | } 256 | return $methods; 257 | } 258 | } 259 | -------------------------------------------------------------------------------- /Model/MethodList.php: -------------------------------------------------------------------------------- 1 | 15 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License 3.0 16 | * @link https://www.i-ways.net 17 | */ 18 | 19 | namespace Iways\PayPalPlus\Model; 20 | 21 | class MethodList extends \Magento\Payment\Model\MethodList 22 | { 23 | protected $checkPPP; 24 | 25 | public function setCheckPPP($checkPPP) 26 | { 27 | $this->checkPPP = $checkPPP; 28 | } 29 | 30 | public function getCheckPPP() 31 | { 32 | return $this->checkPPP; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Model/Payment.php: -------------------------------------------------------------------------------- 1 | 15 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License 3.0 16 | * @link https://www.i-ways.net 17 | */ 18 | namespace Iways\PayPalPlus\Model; 19 | 20 | use Iways\PayPalPlus\Block\PaymentInfo; 21 | use Magento\Framework\DataObject; 22 | use Magento\Framework\Exception\LocalizedException; 23 | 24 | /** 25 | * Class Payment model 26 | */ 27 | class Payment extends \Magento\Payment\Model\Method\AbstractMethod 28 | { 29 | const PPP_STATUS_APPROVED = 'approved'; 30 | const CODE = 'iways_paypalplus_payment'; 31 | const PPP_PENDING = 'pending'; 32 | 33 | const PPP_INSTRUCTION_TYPE = 'PAY_UPON_INVOICE'; 34 | 35 | protected $_code = self::CODE; // phpcs:ignore PSR2.Classes.PropertyDeclaration 36 | 37 | /** 38 | * Payment Method features 39 | * 40 | * @var bool 41 | */ 42 | protected $_isGateway = true; // phpcs:ignore PSR2.Classes.PropertyDeclaration 43 | protected $_canOrder = false; // phpcs:ignore PSR2.Classes.PropertyDeclaration 44 | protected $_canAuthorize = true; // phpcs:ignore PSR2.Classes.PropertyDeclaration 45 | protected $_canCapture = true; // phpcs:ignore PSR2.Classes.PropertyDeclaration 46 | protected $_canCapturePartial = false; // phpcs:ignore PSR2.Classes.PropertyDeclaration 47 | protected $_canCaptureOnce = false; // phpcs:ignore PSR2.Classes.PropertyDeclaration 48 | protected $_canRefund = true; // phpcs:ignore PSR2.Classes.PropertyDeclaration 49 | protected $_canRefundInvoicePartial = true; // phpcs:ignore PSR2.Classes.PropertyDeclaration 50 | protected $_canUseCheckout = true; // phpcs:ignore PSR2.Classes.PropertyDeclaration 51 | 52 | /** 53 | * Protected $_infoBlockType 54 | * 55 | * @var string 56 | */ 57 | protected $_infoBlockType = PaymentInfo::class; // phpcs:ignore PSR2.Classes.PropertyDeclaration 58 | 59 | /** 60 | * Protected $request 61 | * 62 | * @var \Magento\Framework\App\Request\Http 63 | */ 64 | protected $request; 65 | 66 | /** 67 | * Protected $scopeConfig 68 | * 69 | * @var \Magento\Framework\App\Config\ScopeConfigInterface 70 | */ 71 | protected $scopeConfig; 72 | 73 | /** 74 | * Protected $payPalPlusApiFactory 75 | * 76 | * @var \Iways\PayPalPlus\Model\ApiFactory 77 | */ 78 | protected $payPalPlusApiFactory; 79 | 80 | /** 81 | * Protected $logger 82 | * 83 | * @var \Psr\Log\LoggerInterface 84 | */ 85 | protected $logger; 86 | 87 | /** 88 | * Protected $customerSession 89 | * 90 | * @var \Magento\Customer\Model\Session 91 | */ 92 | protected $customerSession; 93 | 94 | /** 95 | * Protected $payPalPlusHelpe 96 | * 97 | * @var \Iways\PayPalPlus\Helper\Data 98 | */ 99 | protected $payPalPlusHelper; 100 | 101 | /** 102 | * Protected $salesOrderPaymentTransactionFactory 103 | * 104 | * @var \Magento\Sales\Model\Order\Payment\TransactionFactory 105 | */ 106 | protected $salesOrderPaymentTransactionFactory; 107 | 108 | /** 109 | * Protected $ppLogger 110 | * 111 | * @var \Psr\Log\LoggerInterface 112 | */ 113 | protected $ppLogger; 114 | 115 | /** 116 | * Payment constructor 117 | * 118 | * @param \Magento\Framework\App\Request\Http $request 119 | * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig 120 | * @param ApiFactory $payPalPlusApiFactory 121 | * @param \Magento\Customer\Model\Session $customerSession 122 | * @param \Iways\PayPalPlus\Helper\Data $payPalPlusHelper 123 | * @param \Magento\Sales\Model\Order\Payment\TransactionFactory $salesOrderPaymentTransactionFactory 124 | * @param \Magento\Framework\Model\Context $context 125 | * @param \Magento\Framework\Registry $registry 126 | * @param \Magento\Framework\Api\ExtensionAttributesFactory $extensionFactory 127 | * @param \Magento\Framework\Api\AttributeValueFactory $customAttributeFactory 128 | * @param \Magento\Payment\Helper\Data $paymentData 129 | * @param \Magento\Payment\Model\Method\Logger $logger 130 | * @param \Magento\Framework\Model\ResourceModel\AbstractResource|null $resource 131 | * @param \Magento\Framework\Data\Collection\AbstractDb|null $resourceCollection 132 | * @param array $data 133 | */ 134 | public function __construct( 135 | \Magento\Framework\App\Request\Http $request, 136 | \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, 137 | ApiFactory $payPalPlusApiFactory, 138 | \Magento\Customer\Model\Session $customerSession, 139 | \Iways\PayPalPlus\Helper\Data $payPalPlusHelper, 140 | \Magento\Sales\Model\Order\Payment\TransactionFactory $salesOrderPaymentTransactionFactory, 141 | \Magento\Framework\Model\Context $context, 142 | \Magento\Framework\Registry $registry, 143 | \Magento\Framework\Api\ExtensionAttributesFactory $extensionFactory, 144 | \Magento\Framework\Api\AttributeValueFactory $customAttributeFactory, 145 | \Magento\Payment\Helper\Data $paymentData, 146 | \Magento\Payment\Model\Method\Logger $logger, 147 | \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, 148 | \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, 149 | array $data = [] 150 | ) { 151 | $this->request = $request; 152 | $this->scopeConfig = $scopeConfig; 153 | $this->payPalPlusApiFactory = $payPalPlusApiFactory; 154 | $this->customerSession = $customerSession; 155 | $this->payPalPlusHelper = $payPalPlusHelper; 156 | $this->salesOrderPaymentTransactionFactory = $salesOrderPaymentTransactionFactory; 157 | parent::__construct( 158 | $context, 159 | $registry, 160 | $extensionFactory, 161 | $customAttributeFactory, 162 | $paymentData, 163 | $scopeConfig, 164 | $logger, 165 | $resource, 166 | $resourceCollection, 167 | $data 168 | ); 169 | $this->ppLogger = $context->getLogger(); 170 | } 171 | 172 | /** 173 | * Authorize payment method 174 | * 175 | * @param \Magento\Payment\Model\InfoInterface $payment 176 | * @param float $amount 177 | * 178 | * @throws \Exception Payment could not be executed 179 | * 180 | * @return \Iways\PayPalPlus\Model\Payment 181 | */ 182 | public function authorize(\Magento\Payment\Model\InfoInterface $payment, $amount) 183 | { 184 | $paymentId = $this->request->getParam('paymentId'); 185 | $payerId = $this->request->getParam('PayerID'); 186 | try { 187 | if ($this->scopeConfig->getValue( 188 | 'payment/iways_paypalplus_payment/transfer_reserved_order_id', 189 | \Magento\Store\Model\ScopeInterface::SCOPE_STORE 190 | ) 191 | ) { 192 | $this->payPalPlusApiFactory->create()->patchInvoiceNumber( 193 | $paymentId, 194 | $payment->getOrder()->getIncrementId() 195 | ); 196 | } 197 | } catch (\Exception $e) { 198 | $this->ppLogger->critical($e); 199 | } 200 | 201 | $ppPayment = $this->payPalPlusApiFactory->create()->executePayment( 202 | $paymentId, 203 | $payerId 204 | ); 205 | 206 | $this->customerSession->setPayPalPaymentId(null); 207 | $this->customerSession->setPayPalPaymentPatched(null); 208 | 209 | if (!$ppPayment) { 210 | throw new LocalizedException( 211 | __('Payment could not be executed.') 212 | ); 213 | } 214 | 215 | if ($paymentInstructions = $ppPayment->getPaymentInstruction()) { 216 | $payment->setData('ppp_reference_number', $paymentInstructions->getReferenceNumber()); 217 | $payment->setData('ppp_instruction_type', $paymentInstructions->getInstructionType()); 218 | $payment->setData( 219 | 'ppp_payment_due_date', 220 | $this->payPalPlusHelper->convertDueDate($paymentInstructions->getPaymentDueDate()) 221 | ); 222 | $payment->setData('ppp_note', $paymentInstructions->getNote()); 223 | 224 | $bankInsctructions = $paymentInstructions->getRecipientBankingInstruction(); 225 | $payment->setData('ppp_bank_name', $bankInsctructions->getBankName()); 226 | $payment->setData('ppp_account_holder_name', $bankInsctructions->getAccountHolderName()); 227 | $payment->setData( 228 | 'ppp_international_bank_account_number', 229 | $bankInsctructions->getInternationalBankAccountNumber() 230 | ); 231 | $payment->setData('ppp_bank_identifier_code', $bankInsctructions->getBankIdentifierCode()); 232 | $payment->setData('ppp_routing_number', $bankInsctructions->getRoutingNumber()); 233 | 234 | $ppAmount = $paymentInstructions->getAmount(); 235 | $payment->setData('ppp_amount', $ppAmount->getValue()); 236 | $payment->setData('ppp_currency', $ppAmount->getCurrency()); 237 | } 238 | 239 | $transactionId = null; 240 | try { 241 | $transactions = $ppPayment->getTransactions(); 242 | 243 | if ($transactions && isset($transactions[0])) { 244 | $resource = $transactions[0]->getRelatedResources(); 245 | if ($resource && isset($resource[0])) { 246 | $sale = $resource[0]->getSale(); 247 | $transactionId = $sale->getId(); 248 | if ($sale->getState() == self::PPP_PENDING) { 249 | $payment->setIsTransactionPending(true); 250 | } 251 | } 252 | } 253 | } catch (\Exception $e) { 254 | $transactionId = $ppPayment->getId(); 255 | } 256 | $payment->setTransactionId($transactionId)->setLastTransId($transactionId); 257 | 258 | if ($ppPayment->getState() == self::PPP_STATUS_APPROVED) { 259 | $payment->setIsTransactionApproved(true); 260 | } 261 | 262 | $payment->setStatus(self::STATUS_APPROVED) 263 | ->setIsTransactionClosed(false) 264 | ->setAmount($amount) 265 | ->setShouldCloseParentTransaction(false); 266 | if ($payment->isCaptureFinal($amount)) { 267 | $payment->setShouldCloseParentTransaction(true); 268 | } 269 | 270 | return $this; 271 | } 272 | 273 | /** 274 | * Refund specified amount for payment 275 | * 276 | * @param \Magento\Payment\Model\InfoInterface $payment 277 | * @param float $amount 278 | * 279 | * @return $this|\Magento\Payment\Model\Method\AbstractMethod 280 | * 281 | * @throws \Magento\Framework\Exception\NoSuchEntityException 282 | */ 283 | public function refund(\Magento\Payment\Model\InfoInterface $payment, $amount) 284 | { 285 | $ppRefund = $this->payPalPlusApiFactory->create()->refundPayment( 286 | $this->_getParentTransactionId($payment), 287 | $amount 288 | ); 289 | $payment->setTransactionId($ppRefund->getId())->setTransactionClosed(1); 290 | return $this; 291 | } 292 | 293 | /** 294 | * Parent transaction id getter 295 | * 296 | * @param \Magento\Framework\DataObject $payment 297 | * 298 | * @return string 299 | */ 300 | protected function _getParentTransactionId(DataObject $payment) // phpcs:ignore PSR2.Methods.MethodDeclaration 301 | { 302 | $transaction = $this->salesOrderPaymentTransactionFactory->create()->load($payment->getLastTransId(), 'txn_id'); 303 | if ($transaction && $transaction->getParentTxnId()) { 304 | return $transaction->getParentTxnId(); 305 | } 306 | return $payment->getLastTransId(); 307 | } 308 | } 309 | -------------------------------------------------------------------------------- /Model/PaymentInformationManagement.php: -------------------------------------------------------------------------------- 1 | 15 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License 3.0 16 | * @link https://www.i-ways.net 17 | */ 18 | 19 | namespace Iways\PayPalPlus\Model; 20 | 21 | class PaymentInformationManagement 22 | { 23 | /** 24 | * Protected $payPalPlusApiFactory 25 | * 26 | * @var ApiFactory 27 | */ 28 | protected $payPalPlusApiFactory; 29 | 30 | /** 31 | * Protected $quoteManagement 32 | * 33 | * @var \Magento\Quote\Api\CartRepositoryInterface 34 | */ 35 | protected $quoteManagement; 36 | 37 | /** 38 | * Protected $customerSession 39 | * 40 | * @var \Magento\Customer\Model\Session 41 | */ 42 | protected $customerSession; 43 | 44 | public function __construct( 45 | \Iways\PayPalPlus\Model\ApiFactory $payPalPlusApiFactory, 46 | \Magento\Quote\Api\CartRepositoryInterface $quoteRepository, 47 | \Magento\Customer\Model\Session $customerSession 48 | ) { 49 | $this->payPalPlusApiFactory = $payPalPlusApiFactory; 50 | $this->quoteManagement = $quoteRepository; 51 | $this->customerSession = $customerSession; 52 | } 53 | 54 | public function patchPayment($cartId) 55 | { 56 | $quote = $this->quoteManagement->getActive($cartId); 57 | return $this->payPalPlusApiFactory->create()->patchPayment($quote); 58 | } 59 | 60 | public function handleComment($paymentMethod) 61 | { 62 | $this->customerSession->setOrderComment(null); 63 | $additionalData = $paymentMethod->getAdditionalData(); 64 | if (isset($additionalData['comments']) && !empty($additionalData['comments'])) { 65 | $this->customerSession->setOrderComment($additionalData['comments']); 66 | } 67 | return $paymentMethod; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Model/PaymentInformationManagement/GuestPPPPaymentInformationManagement.php: -------------------------------------------------------------------------------- 1 | 15 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License 3.0 16 | * @link https://www.i-ways.net 17 | */ 18 | 19 | namespace Iways\PayPalPlus\Model\PaymentInformationManagement; 20 | 21 | use Iways\PayPalPlus\Api\GuestPPPPaymentInformationManagementInterface as ClassInterface; 22 | use Iways\PayPalPlus\Model\PaymentInformationManagement; 23 | use Magento\Framework\Exception\CouldNotSaveException; 24 | use Magento\Quote\Api\CartRepositoryInterface; 25 | 26 | class GuestPPPPaymentInformationManagement extends PaymentInformationManagement implements ClassInterface 27 | { 28 | /** 29 | * Protected $billingAddressManagement 30 | * 31 | * @var \Magento\Quote\Api\GuestBillingAddressManagementInterface 32 | */ 33 | protected $billingAddressManagement; 34 | 35 | /** 36 | * Protected $paymentMethodManagement 37 | * 38 | * @var \Magento\Quote\Api\GuestPaymentMethodManagementInterface 39 | */ 40 | protected $paymentMethodManagement; 41 | 42 | /** 43 | * Protected $cartManagement 44 | * 45 | * @var \Magento\Quote\Api\GuestCartManagementInterface 46 | */ 47 | protected $cartManagement; 48 | 49 | /** 50 | * Protected $paymentInformationManagement 51 | * 52 | * @var \Magento\Checkout\Api\PaymentInformationManagementInterface 53 | */ 54 | protected $paymentInformationManagement; 55 | 56 | /** 57 | * Protected $quoteIdMaskFactory 58 | * 59 | * @var \Magento\Quote\Model\QuoteIdMaskFactory 60 | */ 61 | protected $quoteIdMaskFactory; 62 | 63 | /** 64 | * Protected $cartRepository 65 | * 66 | * @var CartRepositoryInterface 67 | */ 68 | protected $cartRepository; 69 | 70 | /** 71 | * GuestPPPPaymentInformationManagement constructor 72 | * 73 | * @param \Iways\PayPalPlus\Model\ApiFactory $payPalPlusApiFactory 74 | * @param CartRepositoryInterface $quoteRepository 75 | * @param \Magento\Customer\Model\Session $customerSession 76 | * @param \Magento\Quote\Api\GuestBillingAddressManagementInterface $billingAddressManagement 77 | * @param \Magento\Quote\Api\GuestPaymentMethodManagementInterface $paymentMethodManagement 78 | * @param \Magento\Quote\Api\GuestCartManagementInterface $cartManagement 79 | * @param \Magento\Checkout\Api\PaymentInformationManagementInterface $paymentInformationManagement 80 | * @param \Magento\Quote\Model\QuoteIdMaskFactory $quoteIdMaskFactory 81 | */ 82 | public function __construct( 83 | \Iways\PayPalPlus\Model\ApiFactory $payPalPlusApiFactory, 84 | CartRepositoryInterface $quoteRepository, 85 | \Magento\Customer\Model\Session $customerSession, 86 | \Magento\Quote\Api\GuestBillingAddressManagementInterface $billingAddressManagement, 87 | \Magento\Quote\Api\GuestPaymentMethodManagementInterface $paymentMethodManagement, 88 | \Magento\Quote\Api\GuestCartManagementInterface $cartManagement, 89 | \Magento\Checkout\Api\PaymentInformationManagementInterface $paymentInformationManagement, 90 | \Magento\Quote\Model\QuoteIdMaskFactory $quoteIdMaskFactory 91 | ) { 92 | $this->billingAddressManagement = $billingAddressManagement; 93 | $this->paymentMethodManagement = $paymentMethodManagement; 94 | $this->cartManagement = $cartManagement; 95 | $this->paymentInformationManagement = $paymentInformationManagement; 96 | $this->quoteIdMaskFactory = $quoteIdMaskFactory; 97 | $this->cartRepository = $quoteRepository; 98 | parent::__construct($payPalPlusApiFactory, $quoteRepository, $customerSession); 99 | } 100 | 101 | /** 102 | * {@inheritDoc} 103 | */ 104 | public function savePaymentInformation( 105 | $cartId, 106 | $email, 107 | \Magento\Quote\Api\Data\PaymentInterface $paymentMethod, 108 | \Magento\Quote\Api\Data\AddressInterface $billingAddress = null 109 | ) { 110 | $quoteIdMask = $this->quoteIdMaskFactory->create()->load($cartId, 'masked_id'); 111 | if ($billingAddress) { 112 | $billingAddress->setEmail($email); 113 | $this->billingAddressManagement->assign($cartId, $billingAddress); 114 | } else { 115 | $this->cartRepository->getActive($quoteIdMask->getQuoteId())->getBillingAddress()->setEmail($email); 116 | } 117 | $paymentMethod = $this->handleComment($paymentMethod); 118 | $this->paymentMethodManagement->set($cartId, $paymentMethod); 119 | 120 | try { 121 | $this->patchPayment($quoteIdMask->getQuoteId()); 122 | } catch (\Exception $e) { 123 | throw new CouldNotSaveException( 124 | __($e->getMessage()), 125 | $e 126 | ); 127 | } 128 | return true; 129 | } 130 | 131 | /** 132 | * {@inheritDoc} 133 | */ 134 | public function getPaymentInformation($cartId) 135 | { 136 | $quoteIdMask = $this->quoteIdMaskFactory->create()->load($cartId, 'masked_id'); 137 | return $this->paymentInformationManagement->getPaymentInformation($quoteIdMask->getQuoteId()); 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /Model/PaymentInformationManagement/PPPPaymentInformationManagement.php: -------------------------------------------------------------------------------- 1 | 15 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License 3.0 16 | * @link https://www.i-ways.net 17 | */ 18 | 19 | namespace Iways\PayPalPlus\Model\PaymentInformationManagement; 20 | 21 | use Iways\PayPalPlus\Api\PPPPaymentInformationManagementInterface as ClassInterface; 22 | use Iways\PayPalPlus\Model\PaymentInformationManagement; 23 | use Magento\Framework\Exception\CouldNotSaveException; 24 | 25 | class PPPPaymentInformationManagement extends PaymentInformationManagement implements ClassInterface 26 | { 27 | /** 28 | * Protected $billingAddressManagement 29 | * 30 | * @var \Magento\Quote\Api\BillingAddressManagementInterface 31 | */ 32 | protected $billingAddressManagement; 33 | 34 | /** 35 | * Protected $paymentMethodManagement 36 | * 37 | * @var \Magento\Quote\Api\PaymentMethodManagementInterface 38 | */ 39 | protected $paymentMethodManagement; 40 | 41 | /** 42 | * Protected $cartManagement 43 | * 44 | * @var \Magento\Quote\Api\CartManagementInterface 45 | */ 46 | protected $cartManagement; 47 | 48 | /** 49 | * Protected $paymentDetailsFactory 50 | * 51 | * @var PaymentDetailsFactory 52 | */ 53 | protected $paymentDetailsFactory; 54 | 55 | /** 56 | * Protected $cartTotalsRepository 57 | * 58 | * @var \Magento\Quote\Api\CartTotalRepositoryInterface 59 | */ 60 | protected $cartTotalsRepository; 61 | 62 | /** 63 | * PPPPaymentInformationManagement constructor 64 | * 65 | * @param \Iways\PayPalPlus\Model\ApiFactory $payPalPlusApiFactory 66 | * @param \Magento\Quote\Api\CartRepositoryInterface $quoteRepository 67 | * @param \Magento\Customer\Model\Session $customerSession 68 | * @param \Magento\Quote\Api\BillingAddressManagementInterface $billingAddressManagement 69 | * @param \Magento\Quote\Api\PaymentMethodManagementInterface $paymentMethodManagement 70 | * @param \Magento\Quote\Api\CartManagementInterface $cartManagement 71 | * @param \Magento\Checkout\Model\PaymentDetailsFactory $paymentDetailsFactory 72 | * @param \Magento\Quote\Api\CartTotalRepositoryInterface $cartTotalsRepository 73 | */ 74 | public function __construct( 75 | \Iways\PayPalPlus\Model\ApiFactory $payPalPlusApiFactory, 76 | \Magento\Quote\Api\CartRepositoryInterface $quoteRepository, 77 | \Magento\Customer\Model\Session $customerSession, 78 | \Magento\Quote\Api\BillingAddressManagementInterface $billingAddressManagement, 79 | \Magento\Quote\Api\PaymentMethodManagementInterface $paymentMethodManagement, 80 | \Magento\Quote\Api\CartManagementInterface $cartManagement, 81 | \Magento\Checkout\Model\PaymentDetailsFactory $paymentDetailsFactory, 82 | \Magento\Quote\Api\CartTotalRepositoryInterface $cartTotalsRepository 83 | ) { 84 | $this->billingAddressManagement = $billingAddressManagement; 85 | $this->paymentMethodManagement = $paymentMethodManagement; 86 | $this->cartManagement = $cartManagement; 87 | $this->paymentDetailsFactory = $paymentDetailsFactory; 88 | $this->cartTotalsRepository = $cartTotalsRepository; 89 | parent::__construct($payPalPlusApiFactory, $quoteRepository, $customerSession); 90 | } 91 | 92 | /** 93 | * {@inheritDoc} 94 | */ 95 | public function savePaymentInformation( 96 | $cartId, 97 | \Magento\Quote\Api\Data\PaymentInterface $paymentMethod, 98 | \Magento\Quote\Api\Data\AddressInterface $billingAddress = null 99 | ) { 100 | if ($billingAddress) { 101 | $this->billingAddressManagement->assign($cartId, $billingAddress); 102 | } 103 | $paymentMethod = $this->handleComment($paymentMethod); 104 | $this->paymentMethodManagement->set($cartId, $paymentMethod); 105 | 106 | try { 107 | $this->patchPayment($cartId); 108 | } catch (\Exception $e) { 109 | throw new CouldNotSaveException( 110 | __($e->getMessage()), 111 | $e 112 | ); 113 | } 114 | return true; 115 | } 116 | 117 | /** 118 | * {@inheritDoc} 119 | */ 120 | public function getPaymentInformation($cartId) 121 | { 122 | $paymentDetails = $this->paymentDetailsFactory->create(); 123 | $paymentDetails->setPaymentMethods($this->paymentMethodManagement->getList($cartId)); 124 | $paymentDetails->setTotals($this->cartTotalsRepository->get($cartId)); 125 | return $paymentDetails; 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /Model/System/Config/Source/Mode.php: -------------------------------------------------------------------------------- 1 | 15 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License 3.0 16 | * @link https://www.i-ways.net 17 | */ 18 | namespace Iways\PayPalPlus\Model\System\Config\Source; 19 | 20 | /** 21 | * PayPal Api Mode resource class 22 | * 23 | * @author robert 24 | */ 25 | class Mode implements \Magento\Framework\Option\ArrayInterface 26 | { 27 | /** 28 | * Live string 29 | */ 30 | const LIVE = 'live'; 31 | 32 | /** 33 | * Sandbox string 34 | */ 35 | const SANDBOX = 'sandbox'; 36 | 37 | /** 38 | * Options getter 39 | * 40 | * @return array 41 | */ 42 | public function toOptionArray() 43 | { 44 | return [ 45 | ['value' => self::LIVE, 'label' => __('Live')], 46 | ['value' => self::SANDBOX, 'label' => __('Sandbox')], 47 | ]; 48 | } 49 | 50 | /** 51 | * Get options in "key-value" format 52 | * 53 | * @return array 54 | */ 55 | public function toArray() 56 | { 57 | return [ 58 | self::SANDBOX => __('Sandbox'), 59 | self::LIVE => __('Live'), 60 | ]; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Model/System/Config/Source/ThirdPartyModuls.php: -------------------------------------------------------------------------------- 1 | 15 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License 3.0 16 | * @link https://www.i-ways.net 17 | */ 18 | namespace Iways\PayPalPlus\Model\System\Config\Source; 19 | 20 | use Magento\Framework\App\Config\ScopeConfigInterface; 21 | 22 | /** 23 | * PayPal Api Mode resource class 24 | * 25 | * @author robert 26 | */ 27 | class ThirdPartyModuls implements \Magento\Framework\Option\ArrayInterface 28 | { 29 | /** 30 | * Protected $_paymentConfig 31 | * 32 | * @var \Magento\Payment\Model\Config 33 | */ 34 | protected $_paymentConfig; // phpcs:ignore PSR2.Classes.PropertyDeclaration 35 | 36 | /** 37 | * Protected $_scopeConfig 38 | * 39 | * @var \Magento\Framework\App\Config\ScopeConfigInterface 40 | */ 41 | protected $_scopeConfig; // phpcs:ignore PSR2.Classes.PropertyDeclaration 42 | 43 | /** 44 | * Construct 45 | * 46 | * @param \Magento\Payment\Model\Config $paymentConfig 47 | * @param ScopeConfigInterface $scopeConfig 48 | */ 49 | public function __construct( 50 | \Magento\Payment\Model\Config $paymentConfig, 51 | ScopeConfigInterface $scopeConfig 52 | ) { 53 | $this->_paymentConfig = $paymentConfig; 54 | $this->_scopeConfig = $scopeConfig; 55 | } 56 | 57 | /** 58 | * Options getter 59 | * 60 | * @return array 61 | */ 62 | public function toOptionArray() 63 | { 64 | $payments = $this->_paymentConfig->getActiveMethods(); 65 | 66 | $methods = [['value' => '', 'label' => __('--Please Select--')]]; 67 | 68 | foreach ($payments as $paymentCode => $paymentModel) { 69 | if (strpos($paymentCode, 'paypal') !== false) { 70 | continue; 71 | } 72 | 73 | $paymentTitle = $this->_scopeConfig->getValue('payment/' . $paymentCode . '/title'); 74 | if (empty($paymentTitle)) { 75 | $paymentTitle = $paymentCode; 76 | } 77 | $methods[$paymentCode] = [ 78 | 'label' => $paymentTitle, 79 | 'value' => $paymentCode, 80 | ]; 81 | } 82 | return $methods; 83 | } 84 | 85 | /** 86 | * Get options in "key-value" format 87 | * 88 | * @return array 89 | */ 90 | public function toArray() 91 | { 92 | $payments = $this->_paymentConfig->getAllMethods(); 93 | 94 | $methods = []; 95 | 96 | foreach ($payments as $paymentCode => $paymentModel) { 97 | if ($paymentCode == 'iways_paypalplus_payment') { 98 | continue; 99 | } 100 | if (empty($paymentTitle)) { 101 | $paymentTitle = $paymentCode; 102 | } 103 | $paymentTitle = $this->_scopeConfig->getValue('payment/' . $paymentCode . '/title'); 104 | $methods[$paymentCode] = $paymentTitle; 105 | } 106 | return $methods; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /Model/Webhook/Event.php: -------------------------------------------------------------------------------- 1 | 15 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License 3.0 16 | * @link https://www.i-ways.net 17 | */ 18 | 19 | namespace Iways\PayPalPlus\Model\Webhook; 20 | 21 | /** 22 | * Iways PayPalPlus Event Handler 23 | * 24 | * @author robert 25 | */ 26 | class Event 27 | { 28 | /** 29 | * Payment sale completed event type code 30 | */ 31 | const PAYMENT_SALE_COMPLETED = 'PAYMENT.SALE.COMPLETED'; 32 | /** 33 | * Payment sale pending event type code 34 | */ 35 | const PAYMENT_SALE_PENDING = 'PAYMENT.SALE.PENDING'; 36 | /** 37 | * Payment sale refunded event type 38 | */ 39 | const PAYMENT_SALE_REFUNDED = 'PAYMENT.SALE.REFUNDED'; 40 | /** 41 | * Payment sale reversed event type code 42 | */ 43 | const PAYMENT_SALE_REVERSED = 'PAYMENT.SALE.REVERSED'; 44 | /** 45 | * Risk dispute created event type code 46 | */ 47 | const RISK_DISPUTE_CREATED = 'RISK.DISPUTE.CREATED'; 48 | 49 | /** 50 | * Store order instance 51 | * 52 | * @var \Magento\Sales\Model\Order 53 | */ 54 | protected $_order = null; // phpcs:ignore PSR2.Classes.PropertyDeclaration 55 | 56 | /** 57 | * Protected $salesOrderPaymentTransactionFactory 58 | * 59 | * @var \Magento\Sales\Model\Order\Payment\TransactionFactory 60 | */ 61 | protected $salesOrderPaymentTransactionFactory; 62 | 63 | /** 64 | * Protected $salesOrderFactory 65 | * 66 | * @var \Magento\Sales\Model\OrderFactory 67 | */ 68 | protected $salesOrderFactory; 69 | 70 | /** 71 | * Protected $logger 72 | * 73 | * @var \Psr\Log\LoggerInterface 74 | */ 75 | protected $logger; 76 | 77 | public function __construct( 78 | \Magento\Sales\Model\Order\Payment\TransactionFactory $salesOrderPaymentTransactionFactory, 79 | \Magento\Sales\Model\OrderFactory $salesOrderFactory, 80 | \Psr\Log\LoggerInterface $logger 81 | ) { 82 | $this->salesOrderPaymentTransactionFactory = $salesOrderPaymentTransactionFactory; 83 | $this->salesOrderFactory = $salesOrderFactory; 84 | $this->logger = $logger; 85 | } 86 | 87 | /** 88 | * Process the given $webhookEvent 89 | * 90 | * @param \PayPal\Api\WebhookEvent $webhookEvent 91 | * 92 | * @throws \Exception 93 | */ 94 | public function processWebhookRequest(\PayPal\Api\WebhookEvent $webhookEvent) 95 | { 96 | if ($webhookEvent->getEventType() !== null 97 | && in_array($webhookEvent->getEventType(), $this->getSupportedWebhookEvents()) 98 | ) { 99 | $this->getOrder($webhookEvent); 100 | $this->{$this->eventTypeToHandler($webhookEvent->getEventType())}($webhookEvent); 101 | } 102 | } 103 | 104 | /** 105 | * Get supported webhook events 106 | * 107 | * @return array 108 | */ 109 | public function getSupportedWebhookEvents() 110 | { 111 | return [ 112 | self::PAYMENT_SALE_COMPLETED, 113 | self::PAYMENT_SALE_PENDING, 114 | self::PAYMENT_SALE_REFUNDED, 115 | self::PAYMENT_SALE_REVERSED, 116 | self::RISK_DISPUTE_CREATED 117 | ]; 118 | } 119 | 120 | /** 121 | * Parse event type to handler function 122 | * 123 | * @param $eventType 124 | * 125 | * @return string 126 | */ 127 | protected function eventTypeToHandler($eventType) 128 | { 129 | $eventParts = explode('.', $eventType); 130 | foreach ($eventParts as $key => $eventPart) { 131 | if (!$key) { 132 | $eventParts[$key] = strtolower($eventPart); 133 | continue; 134 | } 135 | $eventParts[$key] = ucfirst(strtolower($eventPart)); 136 | } 137 | return implode('', $eventParts); 138 | } 139 | 140 | /** 141 | * Mark transaction as completed 142 | * 143 | * @param \PayPal\Api\WebhookEvent $webhookEvent 144 | * 145 | * @throws \Exception 146 | */ 147 | protected function paymentSaleCompleted(\PayPal\Api\WebhookEvent $webhookEvent) 148 | { 149 | $paymentResource = $webhookEvent->getResource(); 150 | $parentTransactionId = $paymentResource->parent_payment; 151 | $payment = $this->_order->getPayment(); 152 | 153 | $payment->setTransactionId($paymentResource->id) 154 | ->setCurrencyCode($paymentResource->amount->currency) 155 | ->setParentTransactionId($parentTransactionId) 156 | ->setIsTransactionClosed(true) 157 | ->registerCaptureNotification( 158 | $paymentResource->amount->total, 159 | true 160 | ); 161 | $this->_order->save(); 162 | 163 | // notify customer 164 | $invoice = $payment->getCreatedInvoice(); 165 | if ($invoice && !$this->_order->getEmailSent()) { 166 | $this->_order->queueNewOrderEmail() 167 | ->addStatusHistoryComment( 168 | __( 169 | 'Notified customer about invoice #%1.', 170 | $invoice->getIncrementId() 171 | ) 172 | )->setIsCustomerNotified(true)->save(); 173 | } 174 | } 175 | 176 | /** 177 | * Mark transaction as refunded 178 | * 179 | * @param \PayPal\Api\WebhookEvent $webhookEvent 180 | * 181 | * @throws \Exception 182 | */ 183 | protected function paymentSaleRefunded(\PayPal\Api\WebhookEvent $webhookEvent) 184 | { 185 | $paymentResource = $webhookEvent->getResource(); 186 | $parentTransactionId = $paymentResource->parent_payment; 187 | 188 | $payment = $this->_order->getPayment(); 189 | $amount = $paymentResource->amount->total; 190 | 191 | $transactionId = $paymentResource->id; 192 | 193 | $payment->setPreparedMessage('') 194 | ->setTransactionId($transactionId) 195 | ->setParentTransactionId($parentTransactionId) 196 | ->setIsTransactionClosed(1) 197 | ->registerRefundNotification($amount); 198 | 199 | $this->_order->save(); 200 | 201 | $creditmemo = $payment->getCreatedCreditmemo(); 202 | if ($creditmemo) { 203 | $creditmemo->sendEmail(); 204 | $this->_order 205 | ->addStatusHistoryComment( 206 | __( 207 | 'Notified customer about creditmemo #%1.', 208 | $creditmemo->getIncrementId() 209 | ) 210 | )->setIsCustomerNotified(true) 211 | ->save(); 212 | } 213 | } 214 | 215 | /** 216 | * Mark transaction as pending 217 | * 218 | * @param \PayPal\Api\WebhookEvent $webhookEvent 219 | * 220 | * @throws \Exception 221 | */ 222 | protected function paymentSalePending(\PayPal\Api\WebhookEvent $webhookEvent) 223 | { 224 | $paymentResource = $webhookEvent->getResource(); 225 | $this->_order->getPayment() 226 | ->setPreparedMessage($webhookEvent->getSummary()) 227 | ->setTransactionId($paymentResource->id) 228 | ->setIsTransactionClosed(0) 229 | ->registerPaymentReviewAction(\Magento\Sales\Model\Order\Payment::REVIEW_ACTION_UPDATE, false); 230 | $this->_order->save(); 231 | } 232 | 233 | /** 234 | * Mark transaction as reversed 235 | * 236 | * @param \PayPal\Api\WebhookEvent $webhookEvent 237 | * 238 | * @throws \Exception 239 | */ 240 | protected function paymentSaleReversed(\PayPal\Api\WebhookEvent $webhookEvent) 241 | { 242 | $this->_order->setStatus(\Magento\Paypal\Model\Info::ORDER_STATUS_REVERSED); 243 | $this->_order->save(); 244 | $this->_order 245 | ->addStatusHistoryComment( 246 | $webhookEvent->getSummary(), 247 | \Magento\Paypal\Model\Info::ORDER_STATUS_REVERSED 248 | )->setIsCustomerNotified(false) 249 | ->save(); 250 | } 251 | 252 | /** 253 | * Add risk dispute to order comment 254 | * 255 | * @param \PayPal\Api\WebhookEvent $webhookEvent 256 | */ 257 | protected function riskDisputeCreated(\PayPal\Api\WebhookEvent $webhookEvent) 258 | { 259 | //Add IPN comment about registered dispute 260 | $this->_order->addStatusHistoryComment($webhookEvent->getSummary())->setIsCustomerNotified(false)->save(); 261 | } 262 | 263 | /** 264 | * Load and validate order, instantiate proper configuration 265 | * 266 | * @return \Magento\Sales\Model\Order 267 | * 268 | * @throws \Exception 269 | */ 270 | protected function getOrder(\PayPal\Api\WebhookEvent $webhookEvent) 271 | { 272 | if (empty($this->_order)) { 273 | // get proper order 274 | $resource = $webhookEvent->getResource(); 275 | if (!$resource) { 276 | $this->logger->critical('Event resource not found.'); 277 | // throw new \Exception('Event resource not found.'); 278 | } 279 | 280 | $transactionId = $resource->id; 281 | 282 | $transaction = $this->salesOrderPaymentTransactionFactory->create()->load($transactionId, 'txn_id'); 283 | $this->_order = $this->salesOrderFactory->create()->load($transaction->getOrderId()); 284 | if (!$this->_order->getId()) { 285 | throw new \Magento\Framework\Exception\LocalizedException(__('Order not found.')); 286 | } 287 | } 288 | return $this->_order; 289 | } 290 | } 291 | -------------------------------------------------------------------------------- /Observer/ResetObserver.php: -------------------------------------------------------------------------------- 1 | 15 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License 3.0 16 | * @link https://www.i-ways.net 17 | */ 18 | namespace Iways\PayPalPlus\Observer; 19 | 20 | use Iways\PayPalPlus\Helper\Data; 21 | use Magento\Framework\Event\ObserverInterface; 22 | 23 | class ResetObserver implements ObserverInterface 24 | { 25 | /** 26 | * Protected $payPalPlusHelper 27 | * 28 | * @var Data 29 | */ 30 | protected $payPalPlusHelper; 31 | 32 | /** 33 | * ValidateObserver constructor 34 | * 35 | * @param Data $payPalPlusHelper 36 | */ 37 | public function __construct( 38 | Data $payPalPlusHelper 39 | ) { 40 | $this->payPalPlusHelper = $payPalPlusHelper; 41 | } 42 | 43 | /** 44 | * Log out user and redirect to new admin custom url 45 | * 46 | * @param \Magento\Framework\Event\Observer $observer 47 | * 48 | * @return void 49 | * 50 | * @SuppressWarnings(PHPMD.ExitExpression) 51 | * @SuppressWarnings(PHPMD.UnusedFormalParameter) 52 | */ 53 | public function execute(\Magento\Framework\Event\Observer $observer) 54 | { 55 | $this->payPalPlusHelper->resetWebProfileId(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Observer/TpmiObserver.php: -------------------------------------------------------------------------------- 1 | 15 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License 3.0 16 | * @link https://www.i-ways.net 17 | */ 18 | namespace Iways\PayPalPlus\Observer; 19 | 20 | use Magento\Framework\App\Config\Storage\WriterInterface; 21 | use Magento\Framework\App\RequestInterface; 22 | use Magento\Framework\Event\Observer; 23 | use Magento\Framework\Event\ObserverInterface; 24 | 25 | class TpmiObserver implements ObserverInterface 26 | { 27 | /** 28 | * Protected $request 29 | * 30 | * @var \Magento\Framework\App\RequestInterface 31 | */ 32 | protected $request; 33 | 34 | /** 35 | * Protected $writer 36 | * 37 | * @var \Magento\Framework\App\Config\Storage\WriterInterface 38 | */ 39 | protected $writer; 40 | 41 | public function __construct( 42 | RequestInterface $request, 43 | WriterInterface $writer 44 | ) { 45 | $this->request = $request; 46 | $this->writer = $writer; 47 | } 48 | 49 | public function execute(Observer $observer) 50 | { 51 | $groups = $this->request->getParam('groups'); 52 | 53 | if (isset($groups['iways_paypalplus_section']['groups']['third_party_modul_info'])) { 54 | $tpmiFields = $groups['iways_paypalplus_section']['groups']['third_party_modul_info']['fields']; 55 | foreach ($tpmiFields as $key => $value) { 56 | $this->writer->save('payment/iways_paypalplus_section/third_party_modul_info_' . $key, $value['value']); 57 | } 58 | } 59 | 60 | return $this; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Observer/ValidateObserver.php: -------------------------------------------------------------------------------- 1 | 15 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License 3.0 16 | * @link https://www.i-ways.net 17 | */ 18 | namespace Iways\PayPalPlus\Observer; 19 | 20 | use Magento\Framework\Event\ObserverInterface; 21 | 22 | class ValidateObserver implements ObserverInterface 23 | { 24 | /** 25 | * Backend data 26 | * 27 | * @var \Magento\Backend\Helper\Data 28 | */ 29 | protected $_backendData; // phpcs:ignore PSR2.Classes.PropertyDeclaration 30 | 31 | /** 32 | * Core registry 33 | * 34 | * @var \Magento\Framework\Registry 35 | */ 36 | protected $_coreRegistry; // phpcs:ignore PSR2.Classes.PropertyDeclaration 37 | 38 | /** 39 | * Protected $_authSession 40 | * 41 | * @var \Magento\Backend\Model\Auth\Session 42 | */ 43 | protected $_authSession; // phpcs:ignore PSR2.Classes.PropertyDeclaration 44 | 45 | /** 46 | * Protected $_response 47 | * 48 | * @var \Magento\Framework\App\ResponseInterface 49 | */ 50 | protected $_response; // phpcs:ignore PSR2.Classes.PropertyDeclaration 51 | 52 | /** 53 | * Protected $payPalPlusApiFactory 54 | * 55 | * @var \Iways\PayPalPlus\Model\ApiFactory 56 | */ 57 | protected $payPalPlusApiFactory; 58 | 59 | /** 60 | * Protected $storeManager 61 | * 62 | * @var \Magento\Store\Model\StoreManagerInterface 63 | */ 64 | protected $storeManager; 65 | 66 | /** 67 | * Protected $messageManager 68 | * 69 | * @var \Magento\Framework\Message\ManagerInterface 70 | */ 71 | protected $messageManager; 72 | 73 | /** 74 | * ValidateObserver constructor 75 | * 76 | * @param \Magento\Backend\Helper\Data $backendData 77 | * @param \Magento\Framework\Registry $coreRegistry 78 | * @param \Magento\Backend\Model\Auth\Session $authSession 79 | * @param \Magento\Framework\App\ResponseInterface $response 80 | * @param \Iways\PayPalPlus\Model\ApiFactory $payPalPlusApiFactory 81 | * @param \Magento\Store\Model\StoreManagerInterface $storeManager 82 | * @param \Magento\Framework\Message\ManagerInterface $messageManager 83 | */ 84 | public function __construct( 85 | \Magento\Backend\Helper\Data $backendData, 86 | \Magento\Framework\Registry $coreRegistry, 87 | \Magento\Backend\Model\Auth\Session $authSession, 88 | \Magento\Framework\App\ResponseInterface $response, 89 | \Iways\PayPalPlus\Model\ApiFactory $payPalPlusApiFactory, 90 | \Magento\Store\Model\StoreManagerInterface $storeManager, 91 | \Magento\Framework\Message\ManagerInterface $messageManager 92 | ) { 93 | $this->_backendData = $backendData; 94 | $this->_coreRegistry = $coreRegistry; 95 | $this->_authSession = $authSession; 96 | $this->_response = $response; 97 | $this->payPalPlusApiFactory = $payPalPlusApiFactory; 98 | $this->storeManager = $storeManager; 99 | $this->messageManager = $messageManager; 100 | } 101 | 102 | /** 103 | * Log out user and redirect to new admin custom url 104 | * 105 | * @param \Magento\Framework\Event\Observer $observer 106 | * 107 | * @throws \Magento\Framework\Exception\FileSystemException 108 | * @throws \Magento\Framework\Exception\LocalizedException 109 | */ 110 | public function execute(\Magento\Framework\Event\Observer $observer) 111 | { 112 | $pppApi = $this->payPalPlusApiFactory->create(); 113 | $pppApi->testCredentials($this->getDefaultStoreId($observer)); 114 | $api = $pppApi->setApiContext($this->getDefaultStoreId($observer)); 115 | $webhook = $api->createWebhook(); 116 | if ($webhook === false) { 117 | $this->messageManager->addError( 118 | __('Webhook creation failed.') 119 | ); 120 | } 121 | } 122 | 123 | /** 124 | * Try to get default store id from observer 125 | * 126 | * @param \Magento\Framework\Event\Observer $observer 127 | * 128 | * @return object|null 129 | */ 130 | protected function getDefaultStoreId(\Magento\Framework\Event\Observer $observer) 131 | { 132 | $store = $observer->getStore(); 133 | 134 | if ($store) { 135 | return $store; 136 | } 137 | return null; 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /Plugin/Config/Structure/Element/Group.php: -------------------------------------------------------------------------------- 1 | 15 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License 3.0 16 | * @link https://www.i-ways.net 17 | */ 18 | 19 | namespace Iways\PayPalPlus\Plugin\Config\Structure\Element; 20 | 21 | use Iways\PayPalPlus\Helper\Data; 22 | use Magento\Config\Model\Config\Structure\Element\Group as OriginalGroup; 23 | use Magento\Payment\Model\Config as PaymentConfig; 24 | use Magento\Store\Model\ScopeInterface; 25 | 26 | class Group 27 | { 28 | const CONFIG_GROUP_ID = 'third_party_modul_info'; 29 | 30 | /** 31 | * Protected $helper 32 | * 33 | * @var \Iways\PayPalPlus\Helper\Data 34 | */ 35 | protected $helper; 36 | 37 | /** 38 | * Protected $paymentConfig 39 | * 40 | * @var \Magento\Payment\Model\Config 41 | */ 42 | protected $paymentConfig; 43 | 44 | public function __construct( 45 | Data $helper, 46 | PaymentConfig $paymentConfig 47 | ) { 48 | $this->helper = $helper; 49 | $this->paymentConfig = $paymentConfig; 50 | } 51 | 52 | /** 53 | * Adds dynamic configuration fields if third party methods are set as in use 54 | * 55 | * @param defaultSection $subject 56 | * @param callable $proceed 57 | * @param array $data 58 | * @param $scope 59 | * 60 | * @return mixed 61 | */ 62 | public function aroundSetData(OriginalGroup $subject, callable $proceed, array $data, $scope) 63 | { 64 | $fields = []; 65 | 66 | if ($data['id'] == self::CONFIG_GROUP_ID) { 67 | if ($thirdPartyModuls = $this->helper->getPaymentThirdPartyModuls()) { 68 | $activePaymentMethods = $this->paymentConfig->getActiveMethods(); 69 | $path = "payment/iways_paypalplus_section/third_party_modul_info"; 70 | foreach (explode(',', $thirdPartyModuls) as $key => $value) { 71 | if (isset($activePaymentMethods[$value])) { 72 | $paymentMethod = $activePaymentMethods[$value]; 73 | 74 | $id = 'text_' . $value; 75 | $fields[$id] = [ 76 | 'id' => $id, 77 | 'type' => 'text', 78 | 'label' => $paymentMethod->getTitle(), 79 | 'sortOrder' => $key * 10, 80 | 'showInDefault' => "1", 81 | 'showInWebsite' => "1", 82 | 'showInStore' => "1", 83 | 'path' => $path, 84 | 'config_path' => $path . '_' . $id, 85 | '_elementType' => "field" 86 | ]; 87 | 88 | $id = 'image_' . $value; 89 | $fields[$id] = [ 90 | 'id' => $id, 91 | 'type' => 'text', 92 | 'label' => __("Custom optional image for ") 93 | . '
"' . $paymentMethod->getTitle() . '"', 94 | 'sortOrder' => $key * 10 + 5, 95 | 'showInDefault' => "1", 96 | 'showInWebsite' => "1", 97 | 'showInStore' => "1", 98 | 'path' => $path, 99 | 'config_path' => $path . '_' . $id, 100 | '_elementType' => "field" 101 | ]; 102 | } 103 | } 104 | } 105 | 106 | if (!empty($fields)) { 107 | $data['children'] = $fields; 108 | } 109 | } 110 | 111 | return $proceed($data, $scope); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /Plugin/Payment/Model/MethodListPlugin.php: -------------------------------------------------------------------------------- 1 | 15 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License 3.0 16 | * @link https://www.i-ways.net 17 | */ 18 | 19 | namespace Iways\PayPalPlus\Plugin\Payment\Model; 20 | 21 | use Iways\PayPalPlus\Model\Payment; 22 | use Magento\Framework\App\Config\ScopeConfigInterface; 23 | use Magento\Payment\Model\MethodList; 24 | 25 | class MethodListPlugin 26 | { 27 | const AMAZON_PAYMENT = 'amazon_payment'; 28 | const CHECK_PPP_FUNCTION_NAME = 'getCheckPPP'; 29 | 30 | /** 31 | * Protected $scopeConfig 32 | * 33 | * @var \Magento\Framework\App\Config\ScopeConfigInterface 34 | */ 35 | protected $scopeConfig; 36 | 37 | /** 38 | * MethodListPlugin constructor 39 | * 40 | * @param ScopeConfigInterface $scopeConfig 41 | */ 42 | public function __construct( 43 | ScopeConfigInterface $scopeConfig 44 | ) { 45 | $this->scopeConfig = $scopeConfig; 46 | } 47 | 48 | /** 49 | * Function afterGetAvailableMethods 50 | * 51 | * @param MethodList $methodList 52 | * @param $result 53 | * 54 | * @return array 55 | */ 56 | public function afterGetAvailableMethods(MethodList $methodList, $result) 57 | { 58 | $checkPPP = false; 59 | if (method_exists($methodList, self::CHECK_PPP_FUNCTION_NAME)) { 60 | $checkPPP = $methodList->{self::CHECK_PPP_FUNCTION_NAME}(); 61 | } 62 | 63 | if (!$checkPPP) { 64 | $allowedPPPMethods = explode( 65 | ',', 66 | $this->scopeConfig->getValue( 67 | 'payment/iways_paypalplus_payment/third_party_moduls', 68 | \Magento\Store\Model\ScopeInterface::SCOPE_STORE 69 | ) 70 | ); 71 | $allowedMethods = []; 72 | 73 | foreach ($result as $method) { 74 | if ($method->getCode() == Payment::CODE 75 | || $method->getCode() == self::AMAZON_PAYMENT 76 | || !in_array($method->getCode(), $allowedPPPMethods) 77 | ) { 78 | $allowedMethods[] = $method; 79 | } 80 | } 81 | 82 | return $allowedMethods; 83 | } 84 | return $result; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /Plugin/Sales/Model/Order/PaymentPlugin.php: -------------------------------------------------------------------------------- 1 | 15 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License 3.0 16 | * @link https://www.i-ways.net 17 | */ 18 | 19 | namespace Iways\PayPalPlus\Plugin\Sales\Model\Order; 20 | 21 | use Magento\Framework\Model\AbstractExtensibleModel; 22 | use Magento\Sales\Api\Data\OrderPaymentExtensionFactory; 23 | use Magento\Sales\Model\Order\Payment; 24 | 25 | class PaymentPlugin 26 | { 27 | /** 28 | * Protected $orderPaymentExtensionFactory 29 | * 30 | * @var \Magento\Sales\Api\Data\OrderPaymentExtensionInterfaceFactory 31 | */ 32 | protected $orderPaymentExtensionFactory; 33 | 34 | /** 35 | * PaymentPlugin constructor 36 | * 37 | * @param \Magento\Sales\Api\Data\OrderPaymentExtensionInterfaceFactory $orderPaymentExtensionFactory 38 | */ 39 | public function __construct( 40 | \Magento\Sales\Api\Data\OrderPaymentExtensionInterfaceFactory $orderPaymentExtensionFactory 41 | ) { 42 | $this->orderPaymentExtensionFactory = $orderPaymentExtensionFactory; 43 | } 44 | 45 | /** 46 | * Add stock item information to the product's extension attributes 47 | * 48 | * @param Payment $payment 49 | * 50 | * @return \Magento\Catalog\Model\Product 51 | */ 52 | public function afterGetExtensionAttributes(Payment $payment) 53 | { 54 | $paymentExtension = $payment->getData(AbstractExtensibleModel::EXTENSION_ATTRIBUTES_KEY); 55 | if ($paymentExtension === null) { 56 | $paymentExtension = $this->orderPaymentExtensionFactory->create(); 57 | } 58 | $pppAttributes = [ 59 | 'ppp_reference_number', 60 | 'ppp_instruction_type', 61 | 'ppp_payment_due_date', 62 | 'ppp_note', 63 | 'ppp_bank_name', 64 | 'ppp_account_holder_name', 65 | 'ppp_international_bank_account_number', 66 | 'ppp_bank_identifier_code', 67 | 'ppp_routing_number', 68 | 'ppp_amount', 69 | 'ppp_currency' 70 | ]; 71 | 72 | foreach ($pppAttributes as $pppAttribute) { 73 | $paymentExtension->setData($pppAttribute, $payment->getData($pppAttribute)); 74 | } 75 | 76 | $payment->setExtensionAttributes($paymentExtension); 77 | return $paymentExtension; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Magento 2 PayPal PLUS 2 | 3 | PayPal PLUS is a solution where PayPal offers PayPal, Credit Card, Direct Debit (ELV) and Pay Upon Invoice (Kauf auf Rechnung) as individual payment options on the payment selection page. These payment methods cover around 80% customer demand in Germany. 4 | 5 | No matter how customer chooses to pay, it is always a single PayPal transaction for merchant, including all resulting advantages like Seller Protection and easy refund. 6 | 7 | Based on the payment method selected by the buyer, he will be presented with either the PayPal Login page or an input mask for bank / credit card details. Should PayPal PLUS service be unavailable, standard Magento payment methods will be displayed. 8 | 9 | ## Installation 10 | 11 | To install the Magento 2 PayPal PLUS extension please add our repository to your Magento _composer.json_. 12 | 13 | { 14 | "repositories": [ 15 | { 16 | "url": "https://github.com/i-ways/magento2-paypal-plus", 17 | "type": "vcs" 18 | } 19 | ] 20 | } 21 | 22 | After you added our repository you need to require our module. 23 | 24 | There are two possibilities: 25 | 26 | 1. Run the command _composer require iways/module-pay-pal-plus_ 27 | 2. Add it manually to your _composer.json_ 28 | 29 | "require": { 30 | "iways/module-pay-pal-plus": "~1.0.0" 31 | } 32 | 33 | ## Enable our module in Magento 34 | 35 | To enable our module via Magento 2 CLI go to your Magento root and run: 36 | 37 | bin/magento module:enable --clear-static-content Iways_PayPalPlus 38 | 39 | In order to initialize database updates please run following command afterwards: 40 | 41 | bin/magento setup:upgrade 42 | 43 | Depending on your Magento 2 installation setup, you may want to run following additional command: 44 | 45 | bin/magento setup:di:compile 46 | 47 | The Magento 2 PayPal PLUS module should now be installed and ready to use. 48 | 49 | ## Issues 50 | Please use our Servicedesk at: https://support.i-ways.net/hc/de 51 | -------------------------------------------------------------------------------- /RELEASE_NOTES.txt: -------------------------------------------------------------------------------- 1 | ==== 1.3.6 ==== 2 | workaround for checkout sessions loss 3 | 4 | ==== 1.3.5 ==== 5 | Version update + bugfix 6 | 7 | ==== 1.3.4 ==== 8 | Magento v2.4 9 | PHP v7.4.3 10 | 11 | ==== 1.3.3 ==== 12 | Removed Instalments Banners dependency 13 | 14 | ==== 1.3.2 ==== 15 | Better shipping cost calculation 16 | Documentation updated 17 | Instalments Banners dependency 18 | 19 | ==== 1.3.1 ==== 20 | Added PHP 7.3 compatibility 21 | 22 | ==== 1.3.0 ==== 23 | Reimplemented M2.3 combatibility 24 | Removed M2.0 M2.1 M2.2 compatibility 25 | Note: 26 | M2.0 M2.1 M2.2 will be supported with v1.2.X upgrades. 27 | 28 | ==== 1.2.4 ==== 29 | Removed 2.3 compatibility due to problems with PHP 7.0 and M2.2.6 and the CsrfValidation in M2.3 30 | 31 | ==== 1.2.3 ==== 32 | Renamed PayPal-Plus to PayPal PLUS 33 | 34 | ==== 1.2.2 ==== 35 | Renamed PayPal Plus to PayPal PLUS 36 | 37 | ==== 1.2.1 ==== 38 | Changed PaymentMethodTitle translation 39 | Removed processed by PayPal 40 | 41 | ==== 1.2.0 ==== 42 | Compatibility with 2.2.6 and 2.3 43 | Renamed third party modules config 44 | Added item list to patch 45 | 46 | ==== 1.1.8 ==== 47 | Fixed discount calculation on virtual quotes 48 | 49 | ==== 1.1.7 ==== 50 | Fixed minicart not empty after order success 51 | Fixed shipping calculation with free shipping 52 | Skip payment creation call if total is zero 53 | Removed unused file sections.xml 54 | 55 | ==== 1.1.6 ==== 56 | Hide PayPal Express in checkout if PPP is active 57 | 58 | ==== 1.1.5 ==== 59 | US address validation error handling. 60 | 61 | ==== 1.1.4 ==== 62 | Changed locale_code parsing 63 | Added workaround for broken shipping cost calculation in checkout 64 | Fixed magento/module-paypal requirement to support Magento 2.2.X 65 | 66 | ==== 1.1.3 ==== 67 | Added notice to configuration if merchant country is not set to Germany 68 | 69 | ==== 1.1.2 ==== 70 | Fix JS Script error on empty third party method array 71 | Fix for missing ShippingAddress on virtual orders 72 | 73 | ==== 1.1.1 ==== 74 | Added token cache to reduce API-Calls 75 | 76 | ==== 1.1.0 ==== 77 | Added third party payment modul support outside iframe 78 | 79 | ==== 1.0.5 ==== 80 | Compatibility with Amazon_Payment 81 | Compatibility with IWD_Opc 82 | Compatibility with Swissup_Firecheckout 83 | Compatibility with Magestore_OneStepCheckout 84 | 85 | ==== 1.0.4 ==== 86 | Fixed tax calculation with special discount settings 87 | 88 | ==== 1.0.3 ==== 89 | Fixed always failing credential validation on payment method config save. 90 | 91 | ==== 1.0.2 ==== 92 | Fixed Javascript errors with Magento 2.1 on payment method block. 93 | 94 | ==== 1.0.1 ==== 95 | Added PUI legal text 96 | 97 | ==== 1.0.0 ==== 98 | PayPal PLUS is a solution where PayPal offers PayPal, Credit Card and ELV (pay upon invoice will be added in a later phase) as individual payment options on the payment selection page. 99 | Based on the payment method selected by the buyer, he will at a later stage be presented with either the PayPal Login page or an input mask for bank / credit card details. 100 | -------------------------------------------------------------------------------- /Setup/InstallSchema.php: -------------------------------------------------------------------------------- 1 | 15 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License 3.0 16 | * @link https://www.i-ways.net 17 | */ 18 | 19 | namespace Iways\PayPalPlus\Setup; 20 | 21 | use Magento\Framework\Setup\InstallSchemaInterface; 22 | use Magento\Framework\Setup\ModuleContextInterface; 23 | use Magento\Framework\Setup\SchemaSetupInterface; 24 | 25 | /** 26 | * Setup install schema 27 | * 28 | * @codeCoverageIgnore 29 | */ 30 | class InstallSchema implements InstallSchemaInterface 31 | { 32 | /** 33 | * {@inheritdoc} 34 | * 35 | * @SuppressWarnings(PHPMD.ExcessiveMethodLength) 36 | */ 37 | public function install(SchemaSetupInterface $setup, ModuleContextInterface $context) 38 | { 39 | $installer = $setup; 40 | $installer->startSetup(); 41 | 42 | /** 43 | * Create table 'training_category_country' 44 | */ 45 | $table = $installer->getTable('sales_order_payment'); 46 | $connection = $installer->getConnection(); 47 | $connection->addColumn( 48 | $table, 49 | 'ppp_reference_number', 50 | [ 51 | 'type' => \Magento\Framework\DB\Ddl\Table::TYPE_TEXT, 52 | 'length' => 255, 53 | 'nullable' => true, 54 | 'default' => null, 55 | 'comment' => 'PayPal PLUS Reference Number' 56 | ] 57 | ); 58 | $connection->addColumn( 59 | $table, 60 | 'ppp_instruction_type', 61 | [ 62 | 'type' => \Magento\Framework\DB\Ddl\Table::TYPE_TEXT, 63 | 'length' => 255, 64 | 'nullable' => true, 65 | 'default' => null, 66 | 'comment' => 'PayPal PLUS Instruction Type' 67 | ] 68 | ); 69 | $connection->addColumn( 70 | $table, 71 | 'ppp_payment_due_date', 72 | [ 73 | 'type' => \Magento\Framework\DB\Ddl\Table::TYPE_TEXT, 74 | 'length' => 255, 75 | 'nullable' => true, 76 | 'default' => null, 77 | 'comment' => 'PayPal PLUS Payment Due Date' 78 | ] 79 | ); 80 | $connection->addColumn( 81 | $table, 82 | 'ppp_note', 83 | [ 84 | 'type' => \Magento\Framework\DB\Ddl\Table::TYPE_TEXT, 85 | 'length' => 255, 86 | 'nullable' => true, 87 | 'default' => null, 88 | 'comment' => 'PayPal PLUS Note' 89 | ] 90 | ); 91 | $connection->addColumn( 92 | $table, 93 | 'ppp_bank_name', 94 | [ 95 | 'type' => \Magento\Framework\DB\Ddl\Table::TYPE_TEXT, 96 | 'length' => 255, 97 | 'nullable' => true, 98 | 'default' => null, 99 | 'comment' => 'PayPal PLUS Bank Name' 100 | ] 101 | ); 102 | $connection->addColumn( 103 | $table, 104 | 'ppp_account_holder_name', 105 | [ 106 | 'type' => \Magento\Framework\DB\Ddl\Table::TYPE_TEXT, 107 | 'length' => 255, 108 | 'nullable' => true, 109 | 'default' => null, 110 | 'comment' => 'PayPal PLUS Holder Name' 111 | ] 112 | ); 113 | $connection->addColumn( 114 | $table, 115 | 'ppp_international_bank_account_number', 116 | [ 117 | 'type' => \Magento\Framework\DB\Ddl\Table::TYPE_TEXT, 118 | 'length' => 255, 119 | 'nullable' => true, 120 | 'default' => null, 121 | 'comment' => 'PayPal PLUS International Bank Account Number' 122 | ] 123 | ); 124 | $connection->addColumn( 125 | $table, 126 | 'ppp_bank_identifier_code', 127 | [ 128 | 'type' => \Magento\Framework\DB\Ddl\Table::TYPE_TEXT, 129 | 'length' => 255, 130 | 'nullable' => true, 131 | 'default' => null, 132 | 'comment' => 'PayPal PLUS Bank Identifier Code' 133 | ] 134 | ); 135 | $connection->addColumn( 136 | $table, 137 | 'ppp_routing_number', 138 | [ 139 | 'type' => \Magento\Framework\DB\Ddl\Table::TYPE_TEXT, 140 | 'length' => 255, 141 | 'nullable' => true, 142 | 'default' => null, 143 | 'comment' => 'PayPal PLUS Routing Number' 144 | ] 145 | ); 146 | $connection->addColumn( 147 | $table, 148 | 'ppp_amount', 149 | [ 150 | 'type' => \Magento\Framework\DB\Ddl\Table::TYPE_TEXT, 151 | 'length' => 255, 152 | 'nullable' => true, 153 | 'default' => null, 154 | 'comment' => 'PayPal PLUS Amount' 155 | ] 156 | ); 157 | $connection->addColumn( 158 | $table, 159 | 'ppp_currency', 160 | [ 161 | 'type' => \Magento\Framework\DB\Ddl\Table::TYPE_TEXT, 162 | 'length' => 255, 163 | 'nullable' => true, 164 | 'default' => null, 165 | 'comment' => 'PayPal PLUS Currency' 166 | ] 167 | ); 168 | 169 | $installer->endSetup(); 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "iways/module-pay-pal-plus", 3 | "description": "PayPal PLUS for Magento 2", 4 | "repositories": [ 5 | { 6 | "type": "composer", 7 | "url": "https://repo.magento.com" 8 | } 9 | ], 10 | "require": { 11 | "php": "~7.1.0|~7.2.0|~7.3.0|~7.4.0", 12 | "paypal/rest-api-sdk-php": "^1.6", 13 | "magento/module-paypal": "100.0.*|100.1.*|100.2.*|100.3.*|101.0.*" 14 | }, 15 | "authors": [ 16 | { 17 | "name": "Robert Hillebrand", 18 | "email": "hillebrand@i-ways.net", 19 | "homepage": "https://www.i-ways.net", 20 | "role": "Senior Software Developer" 21 | } 22 | ], 23 | "type": "magento2-module", 24 | "version": "1.3.6", 25 | "license": [ 26 | "OSL-3.0" 27 | ], 28 | "autoload": { 29 | "files": [ 30 | "registration.php" 31 | ], 32 | "psr-4": { 33 | "Iways\\PayPalPlus\\": "" 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /etc/adminhtml/events.xml: -------------------------------------------------------------------------------- 1 | 2 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /etc/adminhtml/system.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 18 | 19 |
20 | 27 | 28 | 35 | 36 | Merchant Country -> Germany)]]> 37 | Iways\PayPalPlus\Block\Config\System\Config\Form\Comment 38 | 39 | 40 |
41 |
42 | 49 | 50 | 51 | 1 52 | complex paypalplus-section 53 | Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Group 54 | 61 | 62 | 63 | Iways\PayPalPlus\Block\Config\System\Config\Form\Comment 64 | 65 | 70 | How to retrieve your PayPal API credentials.]]> 71 | 72 | 1 73 | Magento\Config\Block\System\Config\Form\Fieldset 74 | 81 | 82 | iways_paypalplus/api/client_id 83 | Magento\Config\Model\Config\Backend\Encrypted 84 | 85 | 92 | 93 | iways_paypalplus/api/client_secret 94 | Magento\Config\Model\Config\Backend\Encrypted 95 | 96 | 103 | 104 | Iways\PayPalPlus\Model\System\Config\Source\Mode 105 | iways_paypalplus/api/mode 106 | 107 | 114 | 115 | https is highly encouraged.]]> 116 | iways_paypalplus/api/hdrimg 117 | 118 | 119 | 126 | 127 | Magento\Config\Block\System\Config\Form\Fieldset 128 | 135 | 136 | Magento\Config\Model\Config\Source\Yesno 137 | payment/iways_paypalplus_payment/active 138 | 139 | 146 | 147 | Magento\Config\Model\Config\Source\Yesno 148 | payment/iways_paypalplus_payment/show_loading_indicator 149 | 150 | 157 | 158 | Magento\Config\Model\Config\Source\Yesno 159 | payment/iways_paypalplus_payment/transfer_reserved_order_id 160 | 161 | 168 | 169 | Magento\Payment\Model\Config\Source\Allspecificcountries 170 | payment/iways_paypalplus_payment/allowspecific 171 | 172 | 179 | 180 | Magento\Directory\Model\Config\Source\Country 181 | 1 182 | payment/iways_paypalplus_payment/specificcountry 183 | 184 | 191 | 192 | Iways\PayPalPlus\Model\System\Config\Source\ThirdPartyModuls 193 | 1 194 | payment/iways_paypalplus_payment/third_party_moduls 195 | 196 | 197 | 204 | 205 | Only selected and saved third party payments will be shown. 206 | 211 | 212 | 217 | 218 | Magento\Config\Block\System\Config\Form\Fieldset 219 | 226 | 227 | Magento\Config\Model\Config\Source\Yesno 228 | iways_paypalplus/dev/debug 229 | 230 | 237 | 238 | Magento\Config\Model\Config\Source\Yesno 239 | iways_paypalplus/dev/pui_sandbox 240 | 241 | 248 | 249 | Reduces API calls 250 | Magento\Config\Model\Config\Source\Yesno 251 | iways_paypalplus/dev/token_cache 252 | 253 | 260 | 261 | iways_paypalplus/dev/web_profile_id 262 | 263 | 270 | 271 | iways_paypalplus/dev/webhook_id 272 | 273 | 274 | 275 |
276 |
277 |
278 | -------------------------------------------------------------------------------- /etc/config.xml: -------------------------------------------------------------------------------- 1 | 2 | 20 | 22 | 23 | 24 | 25 | 26 | 27 | sandbox 28 | 29 | 30 | 31 | 32 | Iways\PayPalPlus\Model\Payment 33 | PayPal PLUS 34 | authorize 35 | 1 36 | 0 37 | processing 38 | 0 39 | -1 40 | paypal 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /etc/csp_whitelist.xml: -------------------------------------------------------------------------------- 1 | 2 | 23 | 25 | 26 | 27 | 28 | https://www.paypalobjects.com 30 | 31 | 32 | 33 | 34 | https://www.sandbox.paypal.com 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /etc/di.xml: -------------------------------------------------------------------------------- 1 | 2 | 20 | 22 | 24 | 26 | 27 | 29 | 30 | 31 | 33 | 34 | 35 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /etc/extension_attributes.xml: -------------------------------------------------------------------------------- 1 | 2 | 20 | 22 | 23 | 25 | 26 | 27 | 28 | 29 | 31 | 32 | 33 | 34 | 35 | 37 | 38 | 39 | 40 | 41 | 43 | 44 | 45 | 46 | 47 | 49 | 50 | 51 | 52 | 53 | 55 | 56 | 57 | 58 | 59 | 61 | 62 | 63 | 64 | 65 | 67 | 68 | 69 | 70 | 71 | 73 | 74 | 75 | 76 | 77 | 79 | 80 | 81 | 82 | 83 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /etc/frontend/di.xml: -------------------------------------------------------------------------------- 1 | 2 | 20 | 22 | 23 | 24 | 26 | Iways\PayPalPlus\Model\ConfigProvider 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /etc/frontend/routes.xml: -------------------------------------------------------------------------------- 1 | 2 | 20 | 22 | 23 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /etc/module.xml: -------------------------------------------------------------------------------- 1 | 2 | 20 | 22 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /etc/payment.xml: -------------------------------------------------------------------------------- 1 | 2 | 20 | 22 | 23 | 24 | 0 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /etc/webapi.xml: -------------------------------------------------------------------------------- 1 | 2 | 20 | 22 | 23 | 25 | 27 | 28 | 29 | 30 | 31 | 32 | 34 | 36 | 37 | 38 | 39 | 40 | %cart_id% 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /i18n/de_DE.csv: -------------------------------------------------------------------------------- 1 | "PayPal PLUS","PayPal PLUS" 2 | "PayPal PLUS Settings","PayPal PLUS-Einstellungen" 3 | "PayPal PLUS Payment Settings","PayPal PLUS-Einstellungen" 4 | "PayPal PLUS Third party methods info","PayPal PLUS Drittanbieterinformation" 5 | "Only selected and saved third party payments will be shown.","Nur ausgewählte und gespeicherte Zahlungsmethoden von Drittanbietern werden angezeigt." 6 | "PayPal PLUS Api Settings","PayPal PLUS API-Einstellungen" 7 | "Client ID","Klienten-ID" 8 | "Client Secret","Klienten-Schlüssel" 9 | "Mode","Modus" 10 | "Enabled","Aktiviert" 11 | "Payment from Applicable Countries","Ländereinstellung" 12 | "Payment from Specific Countries","Erlaubte Länder" 13 | "Allowed Third-Party Moduls","Erlaubte Zahlungsmethoden von Drittanbietern" 14 | "Only select payment methods which doesn't require extra input in checkout.","Bitte wählen Sie nur Zahlungsmethoden aus, die keine weiteren Eingaben im Checkout benötigen." 15 | "PayPal PLUS Development Settings","PayPal PLUS Entwicklereinstellungen" 16 | "Enable autoloader for PayPal SDK","PayPalSDK-Autoloader aktiv" 17 | "Disable if you have problems with other autoloaders.","Bei Problemen mit anderen PHP-Autoloader deaktivieren." 18 | "Debug","Debug" 19 | "How to retrieve your PayPal API credentials.","Wie Sie Ihre Zugangsdaten erhalten." 20 | "How to retrieve your PayPal API credentials.","Wie Sie Ihre Zugangsdaten erhalten." 21 | "Header Image URL","Kopfbereich-Bild-URL" 22 | "The image at the top left of the checkout page. Max size is 750x90-pixel. https is highly encouraged.","Das Bild oben links auf der Bezahlseite. Max. 750px × 90px. https wird dringend empfohlen." 23 | "Please select an other payment method.","Bitte wählen Sie eine andere Zahlungsmethode." 24 | "Provided credentials not valid.","Ihre Zugangsdaten können nicht validiert werden." 25 | "Account holder","Kontobesitzer" 26 | "Bank","Bank" 27 | "IBAN","IBAN" 28 | "BIC","BIC" 29 | "Transaction ID","Transaktionsnummer" 30 | "Reference number","Verwendungszweck" 31 | "Payment due date","Fälligkeitsdatum" 32 | "Account holder:","Kontobesitzer:" 33 | "Bank:","Bank:" 34 | "IBAN:","IBAN:" 35 | "BIC:","BIC:" 36 | "Reference number:","Verwendungszweck:" 37 | "Payment due date:","Fälligkeitsdatum:" 38 | "Pay upon Invoice","Kauf auf Rechnung" 39 | "Show PuI in Sandbox","Kauf auf Rechnung in der Sandbox anzeigen" 40 | "Please transfer the invoice amount to the given account within the payment deadline.","Bitte überweisen Sie den Rechnungsbetrag auf das angegebene Konto innerhalb der Zahlungsfrist." 41 | "Transfer order's ID as PayPal invoice number","Bestellnummer als PayPal Rechnungsnummer übertragen" 42 | "PayPal Plus","PayPal, Lastschrift, Kreditkarte, Kauf auf Rechnung" 43 | "PayPal PLUS","PayPal, Lastschrift, Kreditkarte, Kauf auf Rechnung" 44 | "Show loading indicator","Loading Indicator anzeigen" 45 | "Payment could not be executed.","Zahlung konnte nicht durchgeführt werden." 46 | "%1 has assigned the claim against you on basis of a factoring agreement to PayPal (Europe) S.à r.l. et Cie, S.C.A. You will only be released from your debt by paying to PayPal (Europe) S.à r.l. et Cie, S.C.A.","%1 hat die Forderung gegen Sie im Rahmen eines laufenden Factoringvertrages an die PayPal (Europe) S.àr.l. et Cie, S.C.A. abgetreten. Zahlungen mit schuldbefreiender Wirkung können nur an die PayPal (Europe) S.àr.l. et Cie, S.C.A. geleistet werden." 47 | "Cache access token","Zugriffstoken zwischenspeichern" 48 | "Reduces API calls","Reduziert die API-Aufrufe" 49 | "Only available for merchants located in Germany. Please switch your merchant country to Germany. (Merchant Location -> Merchant Country -> Germany)","Nur verfügbar für Händler mit Standort in Deutschland. Bitte stellen Sie den Händlerstandort auf Deutschland. Händlerstandort -> Händlerland -> Deutschland" 50 | "Add payment methods to PayPal-Plus frame","Zahlungsmethoden dem PayPal PLUS Frame hinzufügen" 51 | "Add payment methods to PayPal PLUS frame","Zahlungsmethoden dem PayPal PLUS Frame hinzufügen" 52 | "Custom optional image for ","Benutzerdefiniertes optionales Bild für " 53 | -------------------------------------------------------------------------------- /registration.php: -------------------------------------------------------------------------------- 1 | 11 | * @license http://opensource.org/licenses/osl-3.0.php Open Software License 3.0 12 | * @link https://www.i-ways.net 13 | */ 14 | 15 | \Magento\Framework\Component\ComponentRegistrar::register( 16 | \Magento\Framework\Component\ComponentRegistrar::MODULE, 17 | 'Iways_PayPalPlus', 18 | __DIR__ 19 | ); 20 | -------------------------------------------------------------------------------- /view/adminhtml/templates/paypalplus/info/default.phtml: -------------------------------------------------------------------------------- 1 | getSpecificInformation(); 21 | ?> 22 | escapeHtml(__($block->getMethod()->getTitle())); ?> 23 | isPUI()): ?> 24 |

escapeHtml(__('Pay upon Invoice')); ?>

25 | 26 | 27 | 28 | $value):?> 29 | 30 | 31 | 34 | 35 | 36 |
escapeHtml($label)?>: 32 | escapeHtml(implode("\n", $block->getValueAsArray($value, true))));?> 33 |
37 | 38 | 39 | getChildHtml()?> 40 | -------------------------------------------------------------------------------- /view/adminhtml/templates/paypalplus/info/pdf/default.phtml: -------------------------------------------------------------------------------- 1 | 21 | escapeHtml(__($block->getMethod()->getTitle())); ?>{{pdf_row_separator}} 22 | 23 | getSpecificInformation()): ?> 24 | isPUI()): ?> 25 | {{pdf_row_separator}} 26 | escapeHtml(__('Please transfer the invoice amount to the given account within the payment deadline.')); ?>{{pdf_row_separator}} 27 | {{pdf_row_separator}} 28 | 29 | $value): ?> 30 | escapeHtml($label) ?>: 31 | escapeHtml(implode(' ', $block->getValueAsArray($value))); ?> 32 | {{pdf_row_separator}} 33 | 34 | 35 | 36 | escapeHtml(implode('{{pdf_row_separator}}', $block->getChildPdfAsArray())); ?> -------------------------------------------------------------------------------- /view/frontend/layout/checkout_index_index.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | uiComponent 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | Iways_PayPalPlus/js/view/payment/method-renderer 37 | 38 | 39 | true 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /view/frontend/layout/checkout_onepage_success.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 19 | 20 | 21 | 22 | 23 | 24 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /view/frontend/templates/paypalplus/info/default.phtml: -------------------------------------------------------------------------------- 1 | getSpecificInformation(); 22 | $title = $block->escapeHtml(__($block->getMethod()->getTitle())); 23 | ?> 24 |
25 |
26 | 27 |
28 | isPui()) : ?> 29 |

30 | 31 | 32 | 33 | $value):?> 34 | 35 | 36 | 39 | 40 | 41 |
escapeHtml($label)?> 37 | escapeHtml(implode("\n", $block->getValueAsArray($value, true))));?> 38 |
42 |
43 | 44 |
45 | getChildHtml()?> 46 | -------------------------------------------------------------------------------- /view/frontend/templates/paypalplus/success.phtml: -------------------------------------------------------------------------------- 1 | 21 | isPUI()) : ?> 22 |
23 |

26 | 27 | 28 | 29 | 32 | 35 | 36 | 37 | 40 | 43 | 44 | 45 | 48 | 51 | 52 | 53 | 56 | 59 | 60 | 61 | 64 | 67 | 68 | 69 | 72 | 75 | 76 | 77 |
30 | 31 | 33 | escapeHtml($block->getAdditionalInformation('ppp_account_holder_name')); ?> 34 |
38 | 39 | 41 | escapeHtml($block->getAdditionalInformation('ppp_bank_name')); ?> 42 |
46 | 47 | 49 | escapeHtml($block->getAdditionalInformation('ppp_international_bank_account_number')); ?> 50 |
54 | 55 | 57 | escapeHtml($block->getAdditionalInformation('ppp_bank_identifier_code')); ?> 58 |
62 | 63 | 65 | escapeHtml($block->getAdditionalInformation('ppp_reference_number')); ?> 66 |
70 | 71 | 73 | escapeHtml($block->getAdditionalInformation('ppp_payment_due_date')); ?> 74 |
78 |

getStoreName() 80 | ); ?>

81 |
82 | 83 | isPPP()): ?> 84 | 94 | -------------------------------------------------------------------------------- /view/frontend/web/css/iways-paypalplus.css: -------------------------------------------------------------------------------- 1 | div.ppp-pui {padding-top: 20px;} 2 | div.ppp-pui table{margin-left: auto;margin-right: auto;margin-bottom:15px;} 3 | div.ppp-pui table tbody tr td{text-align:left;padding:0 5px;} 4 | div.ppp-pui table tbody tr td.ppp-pui-label{width:20%;} -------------------------------------------------------------------------------- /view/frontend/web/js/action/patch-ppp-payment.js: -------------------------------------------------------------------------------- 1 | /** 2 | * NOTICE OF LICENSE 3 | * 4 | * This source file is subject to the Open Software License (OSL 3.0) 5 | * that is bundled with this package in the file LICENSE.txt. 6 | * It is also available through the world-wide-web at this URL: 7 | * http://opensource.org/licenses/osl-3.0.php 8 | * 9 | * Author Robert Hillebrand - hillebrand@i-ways.de - i-ways sales solutions GmbH 10 | * Copyright i-ways sales solutions GmbH © 2015. All Rights Reserved. 11 | * License http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) 12 | */ 13 | define( 14 | [ 15 | 'Magento_Checkout/js/model/quote', 16 | 'Magento_Checkout/js/model/url-builder', 17 | 'mage/storage', 18 | 'Magento_Checkout/js/model/error-processor', 19 | 'Magento_Customer/js/model/customer', 20 | 'Magento_Checkout/js/action/get-totals', 21 | 'Magento_Checkout/js/model/full-screen-loader' 22 | ], 23 | function (quote, urlBuilder, storage, errorProcessor, customer, getTotalsAction, fullScreenLoader) { 24 | 'use strict'; 25 | 26 | return function (messageContainer, paymentData, ppp) { 27 | var serviceUrl, 28 | payload; 29 | 30 | try { 31 | // IWD_Opc Order Comment Support 32 | var orderCommentConfig = window.checkoutConfig.show_hide_custom_block; 33 | if (orderCommentConfig) { // true 34 | var order_comments = jQuery('[name="comment-code"]').val(); 35 | 36 | if (typeof(paymentData.additional_data) === 'undefined' 37 | || paymentData.additional_data === null 38 | ) { 39 | paymentData.additional_data = {comments:order_comments}; 40 | } else { 41 | paymentData.additional_data.comments = order_comments; 42 | } 43 | } 44 | } catch (e) { 45 | console.log(e); 46 | } 47 | 48 | /** 49 | * Checkout for guest and registered customer. 50 | */ 51 | if (!customer.isLoggedIn()) { 52 | serviceUrl = urlBuilder.createUrl( 53 | '/guest-carts/:cartId/set-ppp-payment-information', 54 | { 55 | cartId: quote.getQuoteId() 56 | } 57 | ); 58 | payload = { 59 | cartId: quote.getQuoteId(), 60 | email: quote.guestEmail, 61 | paymentMethod: paymentData, 62 | billingAddress: quote.billingAddress() 63 | }; 64 | } else { 65 | serviceUrl = urlBuilder.createUrl('/carts/mine/set-ppp-payment-information', {}); 66 | payload = { 67 | cartId: quote.getQuoteId(), 68 | paymentMethod: paymentData, 69 | billingAddress: quote.billingAddress() 70 | }; 71 | } 72 | 73 | fullScreenLoader.startLoader(); 74 | 75 | return storage.post( 76 | serviceUrl, 77 | JSON.stringify(payload) 78 | ).done( 79 | function () { 80 | ppp.doCheckout(); 81 | } 82 | ).fail( 83 | function (response) { 84 | errorProcessor.process(response); 85 | } 86 | ).always( 87 | function () { 88 | fullScreenLoader.stopLoader(); 89 | } 90 | ); 91 | }; 92 | } 93 | ); 94 | -------------------------------------------------------------------------------- /view/frontend/web/js/view/payment/method-renderer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * NOTICE OF LICENSE 3 | * 4 | * This source file is subject to the Open Software License (OSL 3.0) 5 | * that is bundled with this package in the file LICENSE.txt. 6 | * It is also available through the world-wide-web at this URL: 7 | * http://opensource.org/licenses/osl-3.0.php 8 | * 9 | * Author Robert Hillebrand - hillebrand@i-ways.de - i-ways sales solutions GmbH 10 | * Copyright i-ways sales solutions GmbH © 2015. All Rights Reserved. 11 | * License http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) 12 | */ 13 | define( 14 | [ 15 | 'uiComponent', 16 | 'Magento_Checkout/js/model/payment/renderer-list' 17 | ], 18 | function ( 19 | Component, 20 | rendererList 21 | ) { 22 | 'use strict'; 23 | rendererList.push( 24 | { 25 | type: 'iways_paypalplus_payment', 26 | component: 'Iways_PayPalPlus/js/view/payment/method-renderer/payment' 27 | } 28 | ); 29 | 30 | return Component.extend({}); 31 | } 32 | ); -------------------------------------------------------------------------------- /view/frontend/web/js/view/payment/method-renderer/payment.js: -------------------------------------------------------------------------------- 1 | /*browser:true*/ 2 | /*global define*/ 3 | /** 4 | * NOTICE OF LICENSE 5 | * 6 | * This source file is subject to the Open Software License (OSL 3.0) 7 | * that is bundled with this package in the file LICENSE.txt. 8 | * It is also available through the world-wide-web at this URL: 9 | * http://opensource.org/licenses/osl-3.0.php 10 | * 11 | * Author Robert Hillebrand - hillebrand@i-ways.de - i-ways sales solutions GmbH 12 | * Copyright i-ways sales solutions GmbH © 2015. All Rights Reserved. 13 | * License http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) 14 | */ 15 | define( 16 | [ 17 | 'ko', 18 | 'jquery', 19 | 'underscore', 20 | 'Magento_Checkout/js/view/payment/default', 21 | 'Magento_Checkout/js/model/quote', 22 | 'Magento_Checkout/js/model/payment-service', 23 | 'Iways_PayPalPlus/js/action/patch-ppp-payment', 24 | 'Magento_Checkout/js/model/payment/additional-validators', 25 | '//www.paypalobjects.com/webstatic/ppplus/ppplus.min.js' 26 | ], 27 | function (ko, $, _, Component, quote, paymentService, patchPPPPayment, additionalValidators) { 28 | var paypalplusConfig = window.checkoutConfig.payment.iways_paypalplus_payment; 29 | return Component.extend( 30 | { 31 | isPaymentMethodSelected: ko.observable(false), 32 | lastCall: false, 33 | ppp: false, 34 | continueCount: 0, 35 | selectedMethod: "iways_paypalplus_payment", 36 | isInitialized: false, 37 | lastHash: false, 38 | defaults: { 39 | template: 'Iways_PayPalPlus/payment', 40 | paymentExperience: paypalplusConfig.paymentExperience, 41 | mode: paypalplusConfig.mode, 42 | showPuiOnSandbox: paypalplusConfig.showPuiOnSandbox, 43 | showLoadingIndicator: paypalplusConfig.showLoadingIndicator, 44 | country: paypalplusConfig.country, 45 | language: paypalplusConfig.language, 46 | thirdPartyPaymentMethods: paypalplusConfig.thirdPartyPaymentMethods, 47 | }, 48 | 49 | /** 50 | * Function initVars 51 | * 52 | * @function 53 | */ 54 | initVars: function () { 55 | this.paymentExperience = paypalplusConfig ? paypalplusConfig.paymentExperience : ''; 56 | this.mode = paypalplusConfig ? paypalplusConfig.mode : ''; 57 | this.country = paypalplusConfig ? paypalplusConfig.country : ''; 58 | this.language = paypalplusConfig ? paypalplusConfig.language : ''; 59 | this.showPuiOnSandbox = paypalplusConfig ? paypalplusConfig.showPuiOnSandbox : ''; 60 | this.showLoadingIndicator = paypalplusConfig ? paypalplusConfig.showLoadingIndicator : ''; 61 | this.thirdPartyPaymentMethods = paypalplusConfig ? paypalplusConfig.thirdPartyPaymentMethods : []; 62 | this.paymentCodeMappings = {}; 63 | }, 64 | 65 | /** 66 | * Function canInitialise 67 | * 68 | * @returns {*|String} 69 | */ 70 | canInitialise: function () { 71 | return this.paymentExperience; 72 | }, 73 | 74 | /** 75 | * Function initObservable 76 | * 77 | * @override 78 | */ 79 | initObservable: function () { 80 | this._super(); 81 | this.initVars(); 82 | this.startIframeChecker(this); 83 | var self = this; 84 | quote.billingAddress.subscribe( 85 | function (newAddress) { 86 | try { 87 | if (self.canInitialise() && self.isInitialized && newAddress !== null && newAddress.countryId != self.country) { 88 | self.country = newAddress.countryId; 89 | self.isInitialized = false; 90 | self.initPayPalPlusFrame(); 91 | } 92 | } catch (e) { 93 | console.log(e); 94 | } 95 | }, 96 | this 97 | ); 98 | self.selectPaymentMethod(); 99 | self.isPPPMethod = ko.computed( 100 | function () { 101 | if (quote.paymentMethod() 102 | && (quote.paymentMethod().method == 'iways_paypalplus_payment' 103 | || typeof self.thirdPartyPaymentMethods[quote.paymentMethod().method] !== "undefined") 104 | ) { 105 | return quote.paymentMethod().method; 106 | } 107 | self.ppp.deselectPaymentMethod(); 108 | return null; 109 | } 110 | ); 111 | return this; 112 | }, 113 | initPayPalPlusFrame: function () { 114 | var self = this; 115 | if (self.canInitialise() && !self.isInitialized) { 116 | self.ppp = PAYPAL.apps.PPP( 117 | { 118 | approvalUrl: self.paymentExperience, 119 | placeholder: "ppplus", 120 | mode: self.mode, 121 | useraction: "commit", 122 | buttonLocation: "outside", 123 | showPuiOnSandbox: self.showPuiOnSandbox, 124 | showLoadingIndicator: self.showLoadingIndicator, 125 | country: self.getCountry(), 126 | language: self.language, 127 | preselection: "paypal", 128 | thirdPartyPaymentMethods: self.getThirdPartyPaymentMethods(), 129 | onLoad: function () { 130 | self.lastCall = 'enableContinue'; 131 | var billingAgreements = $('.checkout-agreement.field.choice.required input.required-entry[type="checkbox"]'); 132 | billingAgreements.on( 133 | 'click', 134 | function (e) { 135 | var clickedBillingAgreements = $(this); 136 | billingAgreements.each( 137 | function () { 138 | if ($(this).attr('name') == clickedBillingAgreements.attr('name')) { 139 | $(this).prop('checked', clickedBillingAgreements.prop('checked')); 140 | } 141 | } 142 | ); 143 | } 144 | ); 145 | }, 146 | onThirdPartyPaymentMethodSelected: function (data) { 147 | self.lastCall = 'onThirdPartyPaymentMethodSelected'; 148 | self.selectedMethod = self.paymentCodeMappings[data.thirdPartyPaymentMethod]; 149 | self.selectPaymentMethod(); 150 | }, 151 | enableContinue: function () { 152 | if (self.lastCall != 'onThirdPartyPaymentMethodSelected') { 153 | self.selectedMethod = 'iways_paypalplus_payment'; 154 | self.selectPaymentMethod(); 155 | } 156 | self.lastCall = 'enableContinue'; 157 | self.isPaymentMethodSelected = true; 158 | $("#place-ppp-order").removeAttr("disabled"); 159 | }, 160 | disableContinue: function () { 161 | self.isPaymentMethodSelected = false; 162 | $("#place-ppp-order").attr("disabled", "disabled"); 163 | } 164 | } 165 | ); 166 | self.isInitialized = true; 167 | } 168 | }, 169 | startIframeChecker: function (self) { 170 | var currentHash = window.location.hash; 171 | if (self.isInitialized && currentHash == "#payment" && self.lastHash != "#payment") { 172 | self.isInitialized = false; 173 | self.initPayPalPlusFrame(); 174 | } 175 | self.lastHash = currentHash; 176 | setTimeout(self.startIframeChecker, 1000, self); 177 | }, 178 | getThirdPartyPaymentMethods: function () { 179 | var self = this; 180 | var pppThirdPartyMethods = []; 181 | _.each( 182 | self.thirdPartyPaymentMethods, 183 | function (activeMethod, code) { 184 | try { 185 | self.paymentCodeMappings[self.thirdPartyPaymentMethods[code].methodName] = code; 186 | pppThirdPartyMethods.push(self.thirdPartyPaymentMethods[code]); 187 | } catch (e) { 188 | console.log(e); 189 | } 190 | } 191 | ); 192 | return pppThirdPartyMethods; 193 | }, 194 | /** 195 | * Get payment method data 196 | */ 197 | getData: function () { 198 | var self = this; 199 | return { 200 | "method": self.selectedMethod, 201 | "po_number": null, 202 | "additional_data": null 203 | }; 204 | }, 205 | placePPPOrder: function (data, event) { 206 | if (event) { 207 | event.preventDefault(); 208 | } 209 | var self = this; 210 | if (self.selectedMethod == "iways_paypalplus_payment") { 211 | if (this.validate() && additionalValidators.validate()) { 212 | patchPPPPayment(this.messageContainer, this.getData(), self.ppp); 213 | return true; 214 | } 215 | return false; 216 | } else { 217 | return this.placeOrder(data, event); 218 | } 219 | }, 220 | getCountry: function () { 221 | try { 222 | if (quote.billingAddress().countryId) { 223 | return quote.billingAddress().countryId; 224 | } 225 | } catch (e) { 226 | //console.log(e); 227 | } 228 | return this.country; 229 | }, 230 | } 231 | ); 232 | } 233 | ); -------------------------------------------------------------------------------- /view/frontend/web/requirejs-config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * NOTICE OF LICENSE 3 | * 4 | * This source file is subject to the Open Software License (OSL 3.0) 5 | * that is bundled with this package in the file LICENSE.txt. 6 | * It is also available through the world-wide-web at this URL: 7 | * http://opensource.org/licenses/osl-3.0.php 8 | * 9 | * Author Robert Hillebrand - hillebrand@i-ways.de - i-ways sales solutions GmbH 10 | * Copyright i-ways sales solutions GmbH © 2015. All Rights Reserved. 11 | * License http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) 12 | */ 13 | 14 | var config = { 15 | map: { 16 | '*': { 17 | paypalplus: '//www.paypalobjects.com/webstatic/ppplus/ppplus.min.js', 18 | } 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /view/frontend/web/template/payment.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 | 6 |
7 | 8 | 9 | 10 |
11 |
12 | 13 | 14 | 15 |
16 |
17 |
18 | 29 |
30 |
31 |
32 |
33 | --------------------------------------------------------------------------------