diff --git a/app/code/Magento/Payment/Block/Adminhtml/Transparent/Form.php b/app/code/Magento/Payment/Block/Adminhtml/Transparent/Form.php new file mode 100644 index 0000000000000000000000000000000000000000..60717bda74798f92b9f1318da44139a16df86039 --- /dev/null +++ b/app/code/Magento/Payment/Block/Adminhtml/Transparent/Form.php @@ -0,0 +1,27 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Payment\Block\Adminhtml\Transparent; + +class Form extends \Magento\Payment\Block\Transparent\Form +{ + /** + * On backend this block does not have any conditional checks + * + * @return bool + */ + protected function shouldRender() + { + return true; + } + + /** + * {inheritdoc} + */ + protected function initializeMethod() + { + return; + } +} diff --git a/app/code/Magento/Payment/Block/Form.php b/app/code/Magento/Payment/Block/Form.php index 762baad7f3741b485b73ac458eeed8a66434770d..f56d9ac3df0474b2d1f28bbbb16c35ea45510bc3 100644 --- a/app/code/Magento/Payment/Block/Form.php +++ b/app/code/Magento/Payment/Block/Form.php @@ -5,6 +5,8 @@ */ namespace Magento\Payment\Block; +use Magento\Payment\Model\MethodInterface; + /** * Payment method form base block */ @@ -13,14 +15,14 @@ class Form extends \Magento\Framework\View\Element\Template /** * Retrieve payment method model * - * @return \Magento\Payment\Model\MethodInterface + * @return MethodInterface * @throws \Magento\Framework\Exception\LocalizedException */ public function getMethod() { $method = $this->getData('method'); - if (!$method instanceof \Magento\Payment\Model\MethodInterface) { + if (!$method instanceof MethodInterface) { throw new \Magento\Framework\Exception\LocalizedException( __('We cannot retrieve the payment method model object.') ); @@ -28,6 +30,18 @@ class Form extends \Magento\Framework\View\Element\Template return $method; } + /** + * Sets payment method instance to form + * + * @param MethodInterface $method + * @return $this + */ + public function setMethod(MethodInterface $method) + { + $this->setData('method', $method); + return $this; + } + /** * Retrieve payment method code * diff --git a/app/code/Magento/Payment/Block/Transparent/Form.php b/app/code/Magento/Payment/Block/Transparent/Form.php new file mode 100644 index 0000000000000000000000000000000000000000..3237e6e21191af8c37ce16ff0085007d5b8d190a --- /dev/null +++ b/app/code/Magento/Payment/Block/Transparent/Form.php @@ -0,0 +1,191 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Payment\Block\Transparent; + +use Magento\Framework\Exception\LocalizedException; +use Magento\Payment\Model\Method\TransparentInterface; +use Magento\Checkout\Model\Session; +use Magento\Payment\Model\Config; +use Magento\Framework\View\Element\Template\Context; + +/** + * Transparent form block + * + * @author Magento Core Team <core@magentocommerce.com> + */ +class Form extends \Magento\Payment\Block\Form\Cc +{ + /** + * @var Session + */ + private $checkoutSession; + + /** + * @var string + */ + protected $_template = 'Magento_Payment::transparent/form.phtml'; + + /** + * @param Context $context + * @param Config $paymentConfig + * @param Session $checkoutSession + * @param array $data + */ + public function __construct( + Context $context, + Config $paymentConfig, + Session $checkoutSession, + array $data = [] + ) { + parent::__construct($context, $paymentConfig, $data); + $this->checkoutSession = $checkoutSession; + } + + /** + * {inheritdoc} + */ + protected function _toHtml() + { + if ($this->shouldRender()) { + return $this->processHtml(); + } + + return ''; + } + + /** + * Checks whether block should be rendered + * basing on TransparentInterface presence in checkout session + * + * @return bool + */ + protected function shouldRender() + { + $quote = $this->checkoutSession->getQuote(); + if ($quote) { + $payment = $quote->getPayment(); + return $payment && $payment->getMethodInstance() instanceof TransparentInterface; + } + + return false; + } + + /** + * Initializes method + * + * @return void + */ + protected function initializeMethod() + { + $this->setData( + 'method', + $this->checkoutSession + ->getQuote() + ->getPayment() + ->getMethodInstance() + ); + } + + /** + * Parent rendering wrapper + * + * @return string + */ + protected function processHtml() + { + $this->initializeMethod(); + return parent::_toHtml(); + } + + /** + * Get type of request + * + * @return bool + */ + public function isAjaxRequest() + { + return $this->getRequest()->getParam('isAjax'); + } + + /** + * Get delimiter for date + * + * @return string + */ + public function getDateDelim() + { + return $this->getMethodConfigData('date_delim'); + } + + /** + * Get map of cc_code, cc_num, cc_expdate for gateway + * Returns json formatted string + * + * @return string + */ + public function getCardFieldsMap() + { + $keys = ['cccvv', 'ccexpdate', 'ccnum']; + $ccfields = array_combine($keys, explode(',', $this->getMethodConfigData('ccfields'))); + return json_encode($ccfields); + } + + /** + * Retrieve place order url on front + * + * @return string + */ + public function getOrderUrl() + { + return $this->_urlBuilder->getUrl( + $this->getMethodConfigData('place_order_url'), + [ + '_secure' => $this->getRequest()->isSecure() + ] + ); + } + + /** + * Retrieve gateway url + * + * @return string + */ + public function getCgiUrl() + { + return (bool)$this->getMethodConfigData('sandbox_flag') + ? $this->getMethodConfigData('cgi_url_test_mode') + : $this->getMethodConfigData('cgi_url'); + } + + /** + * Retrieve config data value by field name + * + * @param string $fieldName + * @return mixed + */ + public function getMethodConfigData($fieldName) + { + return $this->getMethod()->getConfigInterface()->getConfigValue($fieldName); + } + + /** + * Returns transparent method service + * + * @return TransparentInterface + * @throws LocalizedException + */ + public function getMethod() + { + $method = parent::getMethod(); + + if (!$method instanceof TransparentInterface) { + throw new LocalizedException( + __('We cannot retrieve the transparent payment method model object.') + ); + } + return $method; + } +} diff --git a/app/code/Magento/Payment/Block/Transparent/Iframe.php b/app/code/Magento/Payment/Block/Transparent/Iframe.php new file mode 100644 index 0000000000000000000000000000000000000000..3070173f92b36c0bae20d66aadce8981b7c322af --- /dev/null +++ b/app/code/Magento/Payment/Block/Transparent/Iframe.php @@ -0,0 +1,51 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Payment\Block\Transparent; + +/** + * Iframe block for register specific params in layout + * + * @author Magento Core Team <core@magentocommerce.com> + */ +class Iframe extends \Magento\Framework\View\Element\Template +{ + /** + * Core registry + * + * @var \Magento\Framework\Registry + */ + protected $coreRegistry; + + /** + * Constructor + * + * @param \Magento\Framework\View\Element\Template\Context $context + * @param \Magento\Framework\Registry $registry + * @param array $data + */ + public function __construct( + \Magento\Framework\View\Element\Template\Context $context, + \Magento\Framework\Registry $registry, + array $data = [] + ) { + $this->coreRegistry = $registry; + parent::__construct($context, $data); + } + + /** + * Preparing layout + * + * @return $this + */ + protected function _prepareLayout() + { + if ($this->hasRegistryKey()) { + $params = $this->coreRegistry->registry($this->getRegistryKey()); + $this->setParams($params); + } + return parent::_prepareLayout(); + } +} diff --git a/app/code/Magento/Payment/Block/Transparent/Info.php b/app/code/Magento/Payment/Block/Transparent/Info.php new file mode 100644 index 0000000000000000000000000000000000000000..af21b0e1758a59f2a7c3e9adb1b5905de346cd89 --- /dev/null +++ b/app/code/Magento/Payment/Block/Transparent/Info.php @@ -0,0 +1,18 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Payment\Block\Transparent; + +/** + * Class Info. Payment Information block used for transparent redirect feature + * @package Magento\Payment\Block\Transparent + */ +class Info extends \Magento\Framework\View\Element\Template +{ + /** + * @var string + */ + protected $_template = 'Magento_Payment::transparent/info.phtml'; +} diff --git a/app/code/Magento/Payment/Model/Method/AbstractMethod.php b/app/code/Magento/Payment/Model/Method/AbstractMethod.php index e316982a1be7081f470bc8cb2493d19ea0debfea..5a3a5a8b46262889fcd8a574a8b2fffb2a26f61c 100644 --- a/app/code/Magento/Payment/Model/Method/AbstractMethod.php +++ b/app/code/Magento/Payment/Model/Method/AbstractMethod.php @@ -254,6 +254,20 @@ abstract class AbstractMethod extends \Magento\Framework\Model\AbstractExtensibl $this->_scopeConfig = $scopeConfig; $this->_eventManager = $context->getEventDispatcher(); $this->logger = $context->getLogger(); + $this->initializeData($data); + } + + /** + * Initializes injected data + * + * @param array $data + * @return void + */ + protected function initializeData($data = []) + { + if (!empty($data['formBlockType'])) { + $this->_formBlockType = $data['formBlockType']; + } } /** diff --git a/app/code/Magento/Payment/Model/Method/ConfigInterface.php b/app/code/Magento/Payment/Model/Method/ConfigInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..600f4fdb53a8c0eada317308eeda14a9a26907ee --- /dev/null +++ b/app/code/Magento/Payment/Model/Method/ConfigInterface.php @@ -0,0 +1,23 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Payment\Model\Method; + +/** + * Interface for payment methods config + * + * @author Magento Core Team <core@magentocommerce.com> + */ +interface ConfigInterface +{ + /** + * Config field getter + * The specified key can be either in camelCase or under_score format + * + * @param string $key + * @return mixed + */ + public function getConfigValue($key); +} diff --git a/app/code/Magento/Payment/Model/Method/Logger.php b/app/code/Magento/Payment/Model/Method/Logger.php new file mode 100644 index 0000000000000000000000000000000000000000..9c9e54e5e23636527f64eaff58997e6bb0de70c7 --- /dev/null +++ b/app/code/Magento/Payment/Model/Method/Logger.php @@ -0,0 +1,44 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Payment\Model\Method; + +use Psr\Log\LoggerInterface; + +/** + * Class Logger for payment related information (request, response, etc.) which is used for debug + * + * @author Magento Core Team <core@magentocommerce.com> + */ +class Logger +{ + /** + * @var LoggerInterface + */ + protected $logger; + + /** + * @param LoggerInterface $logger + */ + public function __construct(LoggerInterface $logger) + { + $this->logger = $logger; + } + + /** + * Logs payment related information used for debug + * + * @param mixed $logData + * @param ConfigInterface $config + * + * @return void + */ + public function debug($logData, ConfigInterface $config) + { + if ($config->getConfigValue('debug')) { + $this->logger->debug(var_export($logData, true)); + } + } +} diff --git a/app/code/Magento/Payment/Model/Method/Online/GatewayInterface.php b/app/code/Magento/Payment/Model/Method/Online/GatewayInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..4a99a7e8308758e368ad364463673f4828e2ca0e --- /dev/null +++ b/app/code/Magento/Payment/Model/Method/Online/GatewayInterface.php @@ -0,0 +1,29 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Payment\Model\Method\Online; + +use Magento\Framework\Object; +use Magento\Payment\Model\Method\ConfigInterface; + +/** + * Gateway interface for online payment methods + * + * @author Magento Core Team <core@magentocommerce.com> + */ +interface GatewayInterface +{ + /** + * Post request to gateway and return response + * + * @param Object $request + * @param ConfigInterface $config + * + * @return Object + * + * @throws \Exception + */ + public function postRequest(Object $request, ConfigInterface $config); +} diff --git a/app/code/Magento/Payment/Model/Method/TransparentInterface.php b/app/code/Magento/Payment/Model/Method/TransparentInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..eebb8594cc2ea9a0259edd508aef77bd6fa1c9b2 --- /dev/null +++ b/app/code/Magento/Payment/Model/Method/TransparentInterface.php @@ -0,0 +1,23 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Payment\Model\Method; + +use Magento\Payment\Model\MethodInterface; + +/** + * Interface TransparentInterface need to be implemented by Payment Method service + * which supports transparent redirect feature + * @package Magento\Payment\Model\Method + */ +interface TransparentInterface extends MethodInterface +{ + /** + * Returns payment method configured config + * + * @return ConfigInterface + */ + public function getConfigInterface(); +} diff --git a/app/code/Magento/Payment/Test/Unit/Block/Adminhtml/Transparent/FormTest.php b/app/code/Magento/Payment/Test/Unit/Block/Adminhtml/Transparent/FormTest.php new file mode 100644 index 0000000000000000000000000000000000000000..3281e616a649bd16ce9293069d052a965e380c5d --- /dev/null +++ b/app/code/Magento/Payment/Test/Unit/Block/Adminhtml/Transparent/FormTest.php @@ -0,0 +1,86 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Payment\Test\Unit\Block\Adminhtml\Transparent; + +use Magento\Framework\App\RequestInterface; +use Magento\Framework\Object; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Framework\UrlInterface; +use Magento\Payment\Model\Method\TransparentInterface; + +class FormTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var FormTesting | \PHPUnit_Framework_MockObject_MockObject + */ + private $form; + + /** + * @var TransparentInterface | \PHPUnit_Framework_MockObject_MockObject + */ + private $methodMock; + + /** + * @var \Magento\Checkout\Model\Session | \PHPUnit_Framework_MockObject_MockObject + */ + private $checkoutSessionMock; + + protected function setUp() + { + $objectManagerHelper = new ObjectManager($this); + + $this->requestMock = $this->getMockBuilder('\Magento\Framework\App\RequestInterface') + ->setMethods(['getParam']) + ->getMockForAbstractClass(); + + $this->urlBuilderMock = $this->getMockBuilder('\Magento\Framework\UrlInterface') + ->setMethods(['getUrl']) + ->getMockForAbstractClass(); + + $context = $objectManagerHelper->getObject('Magento\Framework\View\Element\Template\Context'); + + $this->methodMock = $this->getMockBuilder('Magento\Payment\Model\Method\TransparentInterface') + ->getMock(); + + $this->checkoutSessionMock = $this->getMockBuilder('Magento\Checkout\Model\Session') + ->setMethods([]) + ->disableOriginalConstructor() + ->getMock(); + + $paymentConfigMock = $this->getMockBuilder('Magento\Payment\Model\Config') + ->setMethods([]) + ->disableOriginalConstructor() + ->getMock(); + + $this->form = new FormTesting( + $context, + $paymentConfigMock, + $this->checkoutSessionMock + ); + } + + public function testToHtmlShouldRender() + { + $quoteMock = $this->getMockBuilder('Magento\Quote\Model\Quote') + ->disableOriginalConstructor() + ->getMock(); + $paymentMock = $this->getMockBuilder('Magento\Quote\Model\Quote\Payment') + ->disableOriginalConstructor() + ->getMock(); + + $this->checkoutSessionMock->expects($this->never()) + ->method('getQuote') + ->willReturn($quoteMock); + $quoteMock->expects($this->never()) + ->method('getPayment') + ->willReturn($paymentMock); + $paymentMock->expects($this->never()) + ->method('getMethodInstance') + ->willReturn($this->methodMock); + + $this->form->toHtml(); + } +} diff --git a/app/code/Magento/Payment/Test/Unit/Block/Adminhtml/Transparent/FormTesting.php b/app/code/Magento/Payment/Test/Unit/Block/Adminhtml/Transparent/FormTesting.php new file mode 100644 index 0000000000000000000000000000000000000000..e48bf8f6a57e7cea7576db24e565c4b1e2a2b0bf --- /dev/null +++ b/app/code/Magento/Payment/Test/Unit/Block/Adminhtml/Transparent/FormTesting.php @@ -0,0 +1,28 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Payment\Test\Unit\Block\Adminhtml\Transparent; + +use Magento\Payment\Block\Adminhtml\Transparent\Form; + +/** + * Class FormTesting extended test class, used to substitute calls to parent methods + * @package Magento\Payment\Test\Unit\Block\Adminhtml\Transparent + */ +class FormTesting extends Form +{ + /** + * Return values for processHtml() method + */ + const PROCESS_HTML_RESULT = 'parent_result'; + + /** + * {inheritdoc} + */ + protected function processHtml() + { + return self::PROCESS_HTML_RESULT; + } +} diff --git a/app/code/Magento/Payment/Test/Unit/Block/FormTest.php b/app/code/Magento/Payment/Test/Unit/Block/FormTest.php index 2db84edbdfd599c017242715d510240ce944a5f7..9f01df530fa1520c38e79312f2f309de3b4df336 100644 --- a/app/code/Magento/Payment/Test/Unit/Block/FormTest.php +++ b/app/code/Magento/Payment/Test/Unit/Block/FormTest.php @@ -119,4 +119,12 @@ class FormTest extends \PHPUnit_Framework_TestCase ] ]; } + + public function testSetMethod() + { + $methodInterfaceMock = $this->getMockBuilder('\Magento\Payment\Model\MethodInterface') + ->getMockForAbstractClass(); + + $this->assertSame($this->_object, $this->_object->setMethod($methodInterfaceMock)); + } } diff --git a/app/code/Magento/Payment/Test/Unit/Block/Transparent/FormTest.php b/app/code/Magento/Payment/Test/Unit/Block/Transparent/FormTest.php new file mode 100644 index 0000000000000000000000000000000000000000..b361cc1881eec8ae1c463dbf7b2ead8d1ce39a19 --- /dev/null +++ b/app/code/Magento/Payment/Test/Unit/Block/Transparent/FormTest.php @@ -0,0 +1,301 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Payment\Test\Unit\Block\Transparent; + +use Magento\Checkout\Model\Session; +use Magento\Framework\App\RequestInterface; +use Magento\Framework\Object; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Framework\UrlInterface; +use Magento\Payment\Model\Method\TransparentInterface; + +class FormTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var FormTesting | \PHPUnit_Framework_MockObject_MockObject + */ + private $form; + + /** + * @var RequestInterface | \PHPUnit_Framework_MockObject_MockObject + */ + private $requestMock; + + /** + * @var UrlInterface | \PHPUnit_Framework_MockObject_MockObject + */ + private $urlBuilderMock; + + /** + * @var TransparentInterface | \PHPUnit_Framework_MockObject_MockObject + */ + private $methodMock; + + /** + * @var Session | \PHPUnit_Framework_MockObject_MockObject + */ + private $checkoutSessionMock; + + protected function setUp() + { + $objectManagerHelper = new ObjectManager($this); + + $this->requestMock = $this->getMockBuilder('\Magento\Framework\App\RequestInterface') + ->setMethods(['getParam']) + ->getMockForAbstractClass(); + + $this->urlBuilderMock = $this->getMockBuilder('\Magento\Framework\UrlInterface') + ->setMethods(['getUrl']) + ->getMockForAbstractClass(); + + $context = $objectManagerHelper->getObject( + 'Magento\Framework\View\Element\Template\Context', + [ + 'request' => $this->requestMock, + 'urlBuilder' => $this->urlBuilderMock + ] + ); + + $this->methodMock = $this->getMockBuilder('Magento\Payment\Model\Method\TransparentInterface') + ->getMock(); + + $this->checkoutSessionMock = $this->getMockBuilder('Magento\Checkout\Model\Session') + ->setMethods([]) + ->disableOriginalConstructor() + ->getMock(); + + $paymentConfigMock = $this->getMockBuilder('Magento\Payment\Model\Config') + ->setMethods([]) + ->disableOriginalConstructor() + ->getMock(); + + $this->form = new FormTesting( + $context, + $paymentConfigMock, + $this->checkoutSessionMock + ); + } + + public function testIsAjaxRequest() + { + $this->requestMock->expects($this->exactly(2)) + ->method('getParam') + ->with('isAjax') + ->willReturnOnConsecutiveCalls(true, false); + + $this->assertTrue($this->form->isAjaxRequest()); + $this->assertFalse($this->form->isAjaxRequest()); + } + + /** + * @param string $fieldName + * @param mixed $fieldValue + * @param mixed $expected + * + * @dataProvider getMethodConfigDataDataProvider + */ + public function testGetMethodConfigData($fieldName, $fieldValue, $expected) + { + $this->initializeMethodWithConfigMock([[$fieldName, $fieldValue]]); + + $this->form->setMethod($this->methodMock); + + $this->assertEquals($expected, $this->form->getMethodConfigData($fieldName)); + } + + /** + * Initializes method mock with config mock + * + * @param array $configMap + */ + private function initializeMethodWithConfigMock(array $configMap = []) + { + $configInterface = $this->getMockBuilder('Magento\Payment\Model\Method\ConfigInterface') + ->getMock(); + + $configInterface->expects($this->any()) + ->method('getConfigValue') + ->willReturnMap($configMap); + + $this->methodMock->expects($this->any()) + ->method('getConfigInterface') + ->willReturn($configInterface); + } + + /** + * Data provider for testGetMethodConfigData + * + * @see testGetMethodConfigData + * + * @case #1 Set string value + * @case #2 Set boolean value + * + * @return array + */ + public function getMethodConfigDataDataProvider() + { + return [ + ['gateway_name', 'payment_gateway', 'payment_gateway'], + ['sandbox_flag', true, true], + ]; + } + + /** + * @dataProvider getCgiUrlDataProvider + * + * @param $sandboxFlag + * @param $cgiUrlTestMode + * @param $cgiUrl + * @param $expectedUrl + */ + public function testGetCgiUrl($sandboxFlag, $cgiUrlTestMode, $cgiUrl, $expectedUrl) + { + $this->initializeMethodWithConfigMock( + [ + ['sandbox_flag', $sandboxFlag], + ['cgi_url_test_mode', $cgiUrlTestMode], + ['cgi_url', $cgiUrl] + ] + ); + + $this->form->setMethod($this->methodMock); + + $this->assertEquals($expectedUrl, $this->form->getCgiUrl()); + } + + /** + * Data provider for testGetCgiUrl + * + * @see testGetCgiUrl + * + * @case #1 The sandboxFlag is 1 we expected cgi_url_test_mode_value + * @case #2 The sandboxFlag is 0 we expected cgi_url_value + * + * @return array + */ + public function getCgiUrlDataProvider() + { + return [ + [ + 1, + 'cgi_url_test_mode_value', + 'cgi_url_value', + 'cgi_url_test_mode_value' + ], + [ + 0, + 'cgi_url_test_mode_value', + 'cgi_url_value', + 'cgi_url_value' + ], + ]; + } + + public function testGetOrderUrl() + { + $orderUrlPattern = 'order_url'; + $builtOrderUrl = 'built_url'; + $this->initializeMethodWithConfigMock([['place_order_url', $orderUrlPattern]]); + + $this->urlBuilderMock->expects($this->once()) + ->method('getUrl') + ->with($orderUrlPattern) + ->willReturn($builtOrderUrl); + + $this->form->setMethod($this->methodMock); + + $this->assertEquals($builtOrderUrl, $this->form->getOrderUrl()); + } + + public function testGetDateDelim() + { + $dateDelimiter = '/'; + $this->initializeMethodWithConfigMock([['date_delim', $dateDelimiter]]); + + $this->form->setMethod($this->methodMock); + + $this->assertEquals($dateDelimiter, $this->form->getDateDelim()); + } + + public function testGetCardFieldsMap() + { + $ccfields = 'x_card_code,x_exp_date,x_card_num'; + $this->initializeMethodWithConfigMock([['ccfields', $ccfields]]); + + $this->form->setMethod($this->methodMock); + + $expected = json_encode(['cccvv' => 'x_card_code', 'ccexpdate' => 'x_exp_date', 'ccnum' => 'x_card_num']); + + $this->assertEquals($expected, $this->form->getCardFieldsMap()); + } + + public function testToHtmlShouldRender() + { + $quoteMock = $this->getMockBuilder('Magento\Quote\Model\Quote') + ->disableOriginalConstructor() + ->getMock(); + $paymentMock = $this->getMockBuilder('Magento\Quote\Model\Quote\Payment') + ->disableOriginalConstructor() + ->getMock(); + + $this->checkoutSessionMock->expects($this->once()) + ->method('getQuote') + ->willReturn($quoteMock); + $quoteMock->expects($this->once()) + ->method('getPayment') + ->willReturn($paymentMock); + $paymentMock->expects($this->once()) + ->method('getMethodInstance') + ->willReturn($this->methodMock); + + $this->form->toHtml(); + } + + public function testToHtmlShouldNotRenderEmptyQuote() + { + $this->checkoutSessionMock->expects($this->once()) + ->method('getQuote') + ->willReturn(null); + + $this->assertEmpty($this->form->toHtml()); + } + + public function testToHtmlShouldNotRenderEmptyPayment() + { + $quoteMock = $this->getMockBuilder('Magento\Quote\Model\Quote') + ->disableOriginalConstructor() + ->getMock(); + + $this->checkoutSessionMock->expects($this->once()) + ->method('getQuote') + ->willReturn($quoteMock); + $quoteMock->expects($this->once()) + ->method('getPayment') + ->willReturn(null); + + $this->assertEmpty($this->form->toHtml()); + } + + public function testGetMethodSuccess() + { + $this->form->setMethod($this->methodMock); + $this->assertSame($this->methodMock, $this->form->getMethod()); + } + + public function testGetMethodNotTransparentInterface() + { + $this->setExpectedException( + 'Magento\Framework\Exception\LocalizedException', + __('We cannot retrieve the transparent payment method model object.') + ); + + $methodMock = $this->getMockBuilder('Magento\Payment\Model\MethodInterface') + ->getMockForAbstractClass(); + + $this->form->setMethod($methodMock); + $this->form->getMethod(); + } +} diff --git a/app/code/Magento/Payment/Test/Unit/Block/Transparent/FormTesting.php b/app/code/Magento/Payment/Test/Unit/Block/Transparent/FormTesting.php new file mode 100644 index 0000000000000000000000000000000000000000..cdeb6a8b5fc76d2fe77e824c3b1b9b56f6582274 --- /dev/null +++ b/app/code/Magento/Payment/Test/Unit/Block/Transparent/FormTesting.php @@ -0,0 +1,28 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Payment\Test\Unit\Block\Transparent; + +use Magento\Payment\Block\Transparent\Form; + +/** + * Class FormTesting extended test class, used to substitute calls to parent methods + * @package Magento\Payment\Test\Unit\Block\Transparent + */ +class FormTesting extends Form +{ + /** + * Return values for processHtml() method + */ + const PROCESS_HTML_RESULT = 'parent_result'; + + /** + * {inheritdoc} + */ + protected function processHtml() + { + return self::PROCESS_HTML_RESULT; + } +} diff --git a/app/code/Magento/Payment/Test/Unit/Model/Method/LoggerTest.php b/app/code/Magento/Payment/Test/Unit/Model/Method/LoggerTest.php new file mode 100644 index 0000000000000000000000000000000000000000..75efef237b33a53a318e56ca50920d21f644418f --- /dev/null +++ b/app/code/Magento/Payment/Test/Unit/Model/Method/LoggerTest.php @@ -0,0 +1,56 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Payment\Test\Unit\Model\Method; + +use Magento\Payment\Model\Method\ConfigInterface; +use Magento\Payment\Model\Method\Logger; +use Psr\Log\LoggerInterface; + +class LoggerTest extends \PHPUnit_Framework_TestCase +{ + /** @var Logger | \PHPUnit_Framework_MockObject_MockObject */ + private $logger; + + /** @var LoggerInterface | \PHPUnit_Framework_MockObject_MockObject */ + private $loggerMock; + + /** @var ConfigInterface | \PHPUnit_Framework_MockObject_MockObject */ + private $configMock; + + protected function setUp() + { + $this->loggerMock = $this->getMockForAbstractClass('Psr\Log\LoggerInterface'); + $this->configMock = $this->getMockForAbstractClass('Magento\Payment\Model\Method\ConfigInterface'); + + $this->logger = new Logger($this->loggerMock); + } + + public function testDebugOn() + { + $this->configMock->expects($this->once()) + ->method('getConfigValue') + ->with('debug') + ->willReturn(true); + $this->loggerMock->expects($this->once()) + ->method('debug') + ->with("'test_value'"); + + $this->logger->debug('test_value', $this->configMock); + } + + public function testDebugOff() + { + $this->configMock->expects($this->once()) + ->method('getConfigValue') + ->with('debug') + ->willReturn(false); + $this->loggerMock->expects($this->never()) + ->method('debug'); + + $this->logger->debug('', $this->configMock); + } +} diff --git a/app/code/Magento/Payment/view/adminhtml/templates/transparent/form.phtml b/app/code/Magento/Payment/view/adminhtml/templates/transparent/form.phtml new file mode 100644 index 0000000000000000000000000000000000000000..e9632aa0f89155238a8a659b660687a2bc4ce743 --- /dev/null +++ b/app/code/Magento/Payment/view/adminhtml/templates/transparent/form.phtml @@ -0,0 +1,94 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +?> +<?php +// @codingStandardsIgnoreFile + +/** @var \Magento\Payment\Block\Transparent\Form $block*/ +$code = $block->getMethodCode(); +?> + +<!-- IFRAME for request to Payment Gateway --> +<iframe id="<?php echo $code ?>-transparent-iframe" data-container="<?php echo $code ?>-transparent-iframe" allowtransparency="true" frameborder="0" name="iframeTransparent" style="display:none;width:100%;background-color:transparent" src="<?php echo $block->getViewFileUrl('blank.html') ?>"></iframe> +<div id="payment_form_<?php echo $code ?>" + data-mage-init='{ + "transparent":{ + "controller":"<?php echo $block->getRequest()->getControllerName() ?>", + "gateway":"<?php echo $block->getMethodCode() ?>", + "dateDelim":"<?php echo $block->getDateDelim() ?>", + "cardFieldsMap":<?php echo $block->getCardFieldsMap() ?>, + "orderSaveUrl":"<?php echo $block->getOrderUrl() ?>", + "cgiUrl":"<?php echo $block->getCgiUrl() ?>", + "nativeAction":"<?php echo $block->getUrl('*/*/save', ['_secure' => $block->getRequest()->isSecure()]) ?>" + }, "validation":[]}' + style="display:none;"> + + <div class="field required type"> + <label for="<?php echo $code ?>_cc_type" class="label"><span><?php echo __('Credit Card Type') ?></span></label> + <div class="control"> + <select id="<?php echo $code ?>_cc_type" data-container="<?php echo $code ?>-cc-type" name="payment[cc_type]" data-validate='{required:true, "validate-cc-type-select":"#<?php echo $code ?>_cc_number"}'> + <option value=""><?php echo __('--Please Select--')?></option> + <?php $_ccType = $block->getInfoData('cc_type') ?> + <?php foreach ($block->getCcAvailableTypes() as $_typeCode => $_typeName): ?> + <option value="<?php echo $_typeCode ?>"<?php if ($_typeCode == $_ccType): ?> selected="selected"<?php endif ?>><?php echo $_typeName ?></option> + <?php endforeach ?> + </select> + </div> + </div> + + <div class="field required number"> + <label for="<?php echo $code ?>_cc_number" class="label"><span><?php echo __('Credit Card Number') ?></span></label> + <div class="control"> + <input type="number" id="<?php echo $code ?>_cc_number" data-container="<?php echo $code ?>-cc-number" name="payment[cc_number]" title="<?php echo __('Credit Card Number') ?>" class="input-text" value="" data-validate='{"required-number":true, "validate-cc-number":"#<?php echo $code ?>_cc_type", "validate-cc-type":"#<?php echo $code ?>_cc_type"}' autocomplete="off"/> + </div> + </div> + + <div class="field required date" id="<?php echo $code ?>_cc_type_exp_div"> + <label for="<?php echo $code ?>_expiration" class="label"><span><?php echo __('Expiration Date') ?></span></label> + <div class="control"> + <div class="fields group group-2"> + <div class="field no-label month"> + <div class="control"> + <select id="<?php echo $code ?>_expiration" name="payment[cc_exp_month]" data-container="<?php echo $code ?>-cc-month" class="month" data-validate='{required:true, "validate-cc-exp":"#<?php echo $code ?>_expiration_yr"}'> + <?php $_ccExpMonth = $block->getInfoData('cc_exp_month') ?> + <?php foreach ($block->getCcMonths() as $k => $v): ?> + <option value="<?php echo $k ? $k : '' ?>"<?php if ($k == $_ccExpMonth): ?> selected="selected"<?php endif ?>><?php echo $v ?></option> + <?php endforeach ?> + </select> + </div> + </div> + <div class="field no-label year"> + <div class="control"> + <?php $_ccExpYear = $block->getInfoData('cc_exp_year') ?> + <select id="<?php echo $code ?>_expiration_yr" name="payment[cc_exp_year]" class="year" data-container="<?php echo $code ?>-cc-year" data-validate='{required:true}'> + <?php foreach ($block->getCcYears() as $k => $v): ?> + <option value="<?php echo $k ? $k : '' ?>"<?php if ($k == $_ccExpYear): ?> selected="selected"<?php endif ?>><?php echo $v ?></option> + <?php endforeach ?> + </select> + </div> + </div> + </div> + </div> + </div> + <?php if ($block->hasVerification()): ?> + <div class="field required cvv" id="<?php echo $code ?>_cc_type_cvv_div"> + <label for="<?php echo $code ?>_cc_cid" class="label"><span><?php echo __('Card Verification Number') ?></span></label> + <div class="control"> + <input type="number" title="<?php echo __('Card Verification Number') ?>" data-container="<?php echo $code ?>-cc-cvv" class="input-text cvv" id="<?php echo $code ?>_cc_cid" name="payment[cc_cid]" value="" data-validate='{"required-number":true, "validate-cc-cvn":"#<?php echo $code ?>_cc_type"}' autocomplete="off"/> + </div> + </div> + <?php endif; ?> + <?php echo $block->getChildHtml() ?> +</div> + +<script> + /** + * Disable card server validation in admin + */ + require(["Magento_Sales/order/create/form"], function(){ + order.addExcludedPaymentMethod('<?php echo $code ?>'); + }); +</script> diff --git a/app/code/Magento/Payment/view/adminhtml/templates/transparent/iframe.phtml b/app/code/Magento/Payment/view/adminhtml/templates/transparent/iframe.phtml new file mode 100644 index 0000000000000000000000000000000000000000..943767e9b80556510bc3d2aa637da38fa9a5ea44 --- /dev/null +++ b/app/code/Magento/Payment/view/adminhtml/templates/transparent/iframe.phtml @@ -0,0 +1,41 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +// @codingStandardsIgnoreFile + +$params = $block->getParams(); + +?> +<html> +<head> +<script> +<?php if (isset($params['redirect'])): ?> + window.location="<?php echo $block->escapeUrl($params['redirect']) ?>"; +<?php elseif (isset($params['redirect_parent'])): ?> + window.top.location="<?php echo $block->escapeUrl($params['redirect_parent']) ?>"; +<?php elseif (isset($params['error_msg'])): ?> + window.top.alert(<?php echo $block->helper('Magento\Framework\Json\Helper\Data')->jsonEncode($params['error_msg']) ?>); +<?php elseif (isset($params['order_success'])): ?> + window.top.location = "<?php echo $params['order_success'] ?>"; +<?php else: ?> + var require = window.top.require; + require(['jquery'], function($) { + $('#edit_form').trigger('processStop'); + + $("input[name='payment[cc_number]']").prop('disabled', true); + $("select[name='payment[cc_type]']").prop('disabled', true); + $("select[name='payment[cc_exp_month]']").prop('disabled', true); + $("select[name='payment[cc_exp_year]']").prop('disabled', true); + $("input[name='payment[cc_cid]']").prop('disabled', true); + + $('#edit_form').trigger('realOrder'); + }); +<?php endif; ?> +</script> +</head> +<body> +</body> +</html> diff --git a/app/code/Magento/Payment/view/adminhtml/templates/transparent/info.phtml b/app/code/Magento/Payment/view/adminhtml/templates/transparent/info.phtml new file mode 100644 index 0000000000000000000000000000000000000000..badfd92d13a478cb0c409eccb4f9f3af9210e150 --- /dev/null +++ b/app/code/Magento/Payment/view/adminhtml/templates/transparent/info.phtml @@ -0,0 +1,13 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +/** + * @see \Magento\Payment\Block\Transparent\Info + */ +?> +<fieldset id="payment_form_<?php echo $block->getMethodCode() ?>" style="display:none" class="fieldset items redirect"> + <div><?php echo __('You\'ll be asked for your payment details before placing an order.') ?></div> +</fieldset> diff --git a/app/code/Magento/Payment/view/adminhtml/web/transparent.js b/app/code/Magento/Payment/view/adminhtml/web/transparent.js new file mode 100644 index 0000000000000000000000000000000000000000..1317880d7f062c08ebe1c1fee96be951796b1a53 --- /dev/null +++ b/app/code/Magento/Payment/view/adminhtml/web/transparent.js @@ -0,0 +1,165 @@ +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +define([ + "jquery", + "mage/template", + "jquery/ui" +], function($, mageTemplate){ + "use strict"; + + $.widget('mage.transparent', { + options: { + hiddenFormTmpl: + '<form target="<%= data.target %>" action="<%= data.action %>" method="POST" hidden enctype="application/x-www-form-urlencoded" class="no-display">' + + '<% _.each(data.inputs, function(val, key){ %>' + + '<input value="<%= val %>" name="<%= key %>" type="hidden">' + + '<% }); %>' + + '</form>', + cgiUrl: null, + orderSaveUrl: null, + controller: null, + gateway: null, + dateDelim: null, + cardFieldsMap: null + }, + + _create: function() { + var prepare = function (event, method) { + if (method === this.options.gateway) { + $('#edit_form') + .off('submitOrder') + .on('submitOrder', this._orderSave.bind(this)) + } + }; + this.hiddenFormTmpl = mageTemplate(this.options.hiddenFormTmpl); + jQuery('#edit_form').on('changePaymentMethod', prepare.bind(this)); + + jQuery('#edit_form').trigger( + 'changePaymentMethod', + [ + jQuery('#edit_form').find(':radio[name="payment[method]"]:checked').val() + ] + ); + }, + + /** + * handler for Place Order button to call gateway for credit card validation + * Save order and generate post data for gateway call + * @private + */ + _orderSave: function() { + var postData = "form_key="+FORM_KEY; + $.ajax({ + url: this.options.orderSaveUrl, + type: 'post', + context: this, + data: postData, + dataType: 'json', + success: function(response) { + if (response.success && response[this.options.gateway]) { + this._postPaymentToGateway(response); + } else { + this._processErrors(response); + } + } + }); + }, + + /** + * Post data to gateway for credit card validation + * @param response + * @private + */ + _postPaymentToGateway: function(response) { + var data, + tmpl, + iframe; + + data = this._preparePaymentData(response); + var iframeSelector = '[data-container="' + this.options.gateway + '-transparent-iframe"]'; + + tmpl = this.hiddenFormTmpl({ + data: { + target: $(iframeSelector).attr('name'), + action: this.options.cgiUrl, + inputs: data + } + }); + + iframe = $(iframeSelector) + .on('submit', function(event){ + event.stopPropagation(); + }); + $(tmpl).appendTo(iframe).submit(); + iframe.html(''); + }, + + /** + * Add credit card fields to post data for gateway + * + * @param response + * @private + */ + _preparePaymentData: function(response) { + var ccfields, + data, + preparedata; + + data = response[this.options.gateway].fields; + ccfields = this.options.cardFieldsMap; + + if (this.element.find('[data-container="' + this.options.gateway + '-cc-cvv"]').length) { + data[ccfields.cccvv] = this.element.find( + '[data-container="' + this.options.gateway + '-cc-cvv"]' + ).val(); + } + preparedata = this._prepareExpDate(); + data[ccfields.ccexpdate] = preparedata.month + this.options.dateDelim + preparedata.year; + data[ccfields.ccnum] = this.element.find( + '[data-container="' + this.options.gateway + '-cc-number"]' + ).val(); + return data; + }, + + /** + * Grab Month and Year into one + * @returns {object} + * @private + */ + _prepareExpDate: function() { + var year = this.element.find('[data-container="' + this.options.gateway + '-cc-year"]').val(), + month = parseInt( + this.element.find('[data-container="' + this.options.gateway + '-cc-month"]').val() + , 10 + ); + if (year.length > 2) { + year = year.substring(2); + } + if (month < 10) { + month = '0' + month; + } + return {month: month, year: year}; + }, + + /** + * Processing errors + * + * @param response + * @private + */ + _processErrors: function (response) { + var msg = response.error_messages; + if (typeof (msg) === 'object') { + alert(msg.join("\n")); + } + if (msg) { + alert(msg); + } + } + }); + + return $.mage.transparent; +}); diff --git a/app/code/Magento/Payment/view/frontend/layout/checkout_onepage_review.xml b/app/code/Magento/Payment/view/frontend/layout/checkout_onepage_review.xml new file mode 100644 index 0000000000000000000000000000000000000000..8bdab785e571d24a7092c52b5ac420fe697dc1d5 --- /dev/null +++ b/app/code/Magento/Payment/view/frontend/layout/checkout_onepage_review.xml @@ -0,0 +1,18 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../lib/internal/Magento/Framework/View/Layout/etc/page_configuration.xsd"> + <body> + <referenceContainer name="checkout.onepage.review.info.items.after"> + <block class="Magento\Payment\Block\Transparent\Form" name="payment.form.transparent"> + <action method="setTemplate"> + <argument name="template" xsi:type="string">Magento_Payment::transparent/form.phtml</argument> + </action> + </block> + </referenceContainer> + </body> +</page> diff --git a/app/code/Magento/Payment/view/frontend/templates/transparent/form.phtml b/app/code/Magento/Payment/view/frontend/templates/transparent/form.phtml new file mode 100644 index 0000000000000000000000000000000000000000..5a4e8c63822a4ba2097d266cfd99232c67378ba6 --- /dev/null +++ b/app/code/Magento/Payment/view/frontend/templates/transparent/form.phtml @@ -0,0 +1,86 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +// @codingStandardsIgnoreFile + +/** @var \Magento\Payment\Block\Transparent\Form $block */ +$code = $block->getMethodCode(); +?> + +<!-- IFRAME for request to Payment Gateway --> +<iframe width="0" height="0" id="<?php echo $code ?>-transparent-iframe" data-container="<?php echo $code ?>-transparent-iframe" allowtransparency="true" frameborder="0" name="iframeTransparent" style="display:none;width:100%;background-color:transparent" src="<?php echo $block->getViewFileUrl('blank.html') ?>"></iframe> +<form class="form" id="co-transparent-form" action="#" method="post" data-mage-init='{ + "transparent":{ + "controller":"<?php echo $block->getRequest()->getControllerName() ?>", + "gateway":"<?php echo $code ?>", + "orderSaveUrl":"<?php echo $block->getOrderUrl() ?>", + "cgiUrl":"<?php echo $block->getCgiUrl() ?>", + "dateDelim":"<?php echo $block->getDateDelim() ?>", + "cardFieldsMap":<?php echo $block->getCardFieldsMap() ?>, + "nativeAction":"<?php echo $block->getUrl('checkout/onepage/saveOrder', ['_secure' => $block->getRequest()->isSecure()]) ?>" + }, "validation":[]}'> + <fieldset class="fieldset ccard <?php echo $code ?>" id="payment_form_<?php echo $code ?>"> + <legend class="legend"><span><?php echo __('Credit Card Information') ?></span></legend><br /> + <div class="field required type"> + <label for="<?php echo $code ?>_cc_type" class="label"><span><?php echo __('Credit Card Type') ?></span></label> + <div class="control"> + <select id="<?php echo $code ?>_cc_type" data-container="<?php echo $code ?>-cc-type" name="payment[cc_type]" data-validate='{required:true, "validate-cc-type-select":"#<?php echo $code ?>_cc_number"}'> + <option value=""><?php echo __('--Please Select--')?></option> + <?php $_ccType = $block->getInfoData('cc_type') ?> + <?php foreach ($block->getCcAvailableTypes() as $_typeCode => $_typeName): ?> + <option value="<?php echo $_typeCode ?>"<?php if ($_typeCode == $_ccType): ?> selected="selected"<?php endif ?>><?php echo $_typeName ?></option> + <?php endforeach ?> + </select> + </div> + </div> + <div class="field required number"> + <label for="<?php echo $code ?>_cc_number" class="label"><span><?php echo __('Credit Card Number') ?></span></label> + <div class="control"> + <input type="number" id="<?php echo $code ?>_cc_number" data-container="<?php echo $code ?>-cc-number" name="payment[cc_number]" title="<?php echo __('Credit Card Number') ?>" class="input-text" value="" data-validate='{"required-number":true, "validate-cc-number":"#<?php echo $code ?>_cc_type", "validate-cc-type":"#<?php echo $code ?>_cc_type"}' autocomplete="off"/> + </div> + </div> + <div class="field required date" id="<?php echo $code ?>_cc_type_exp_div"> + <label for="<?php echo $code ?>_expiration" class="label"><span><?php echo __('Expiration Date') ?></span></label> + <div class="control"> + <div class="fields group group-2"> + <div class="field no-label month"> + <div class="control"> + <select id="<?php echo $code ?>_expiration" name="payment[cc_exp_month]" data-container="<?php echo $code ?>-cc-month" class="month" data-validate='{required:true, "validate-cc-exp":"#<?php echo $code ?>_expiration_yr"}'> + <?php $ccExpMonth = $block->getInfoData('cc_exp_month') ?> + <?php foreach ($block->getCcMonths() as $k => $v): ?> + <option value="<?php echo $k ? $k : '' ?>"<?php if ($k == $ccExpMonth): ?> selected="selected"<?php endif ?>><?php echo $v ?></option> + <?php endforeach ?> + </select> + </div> + </div> + <div class="field no-label year"> + <div class="control"> + <select id="<?php echo $code ?>_expiration_yr" name="payment[cc_exp_year]" class="year" data-container="<?php echo $code ?>-cc-year" data-validate='{required:true}'> + <?php $ccExpYear = $block->getInfoData('cc_exp_year') ?> + <?php foreach ($block->getCcYears() as $k => $v): ?> + <option value="<?php echo $k ? $k : '' ?>"<?php if ($k == $ccExpYear): ?> selected="selected"<?php endif ?>><?php echo $v ?></option> + <?php endforeach ?> + </select> + </div> + </div> + </div> + </div> + </div> + <?php if ($block->hasVerification()): ?> + <div class="field required cvv" id="<?php echo $code ?>_cc_type_cvv_div"> + <label for="<?php echo $code ?>_cc_cid" class="label"><span><?php echo __('Card Verification Number') ?></span></label> + <div class="control"> + <input type="number" title="<?php echo __('Card Verification Number') ?>" data-container="<?php echo $code ?>-cc-cvv" class="input-text cvv" id="<?php echo $code ?>_cc_cid" name="payment[cc_cid]" value="" data-validate='{"required-number":true, "validate-cc-cvn":"#<?php echo $code ?>_cc_type"}' autocomplete="off"/> + <?php $_content = '<img src=\"' . $block->getViewFileUrl('Magento_Checkout::cvv.png') . '\" alt=\"' . __('Card Verification Number Visual Reference') . '\" title=\"' . __('Card Verification Number Visual Reference') . '\" />'; ?> + <div class="note"> + <a href="#" id="<?php echo $code ?>-cvv-what-is-this" class="action cvv" title="<?php echo $block->escapeHtml(__('What is this?'));?>" data-mage-init='{"tooltip": {"content": "<?php echo $_content ?>"}}'><span><?php echo __('What is this?') ?></span></a> + </div> + </div> + </div> + <?php endif; ?> + <?php echo $block->getChildHtml() ?> +</fieldset> +</form> diff --git a/app/code/Magento/Payment/view/frontend/templates/transparent/iframe.phtml b/app/code/Magento/Payment/view/frontend/templates/transparent/iframe.phtml new file mode 100644 index 0000000000000000000000000000000000000000..d090920cb02945f4d00f34b66d6c65355e084007 --- /dev/null +++ b/app/code/Magento/Payment/view/frontend/templates/transparent/iframe.phtml @@ -0,0 +1,33 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +// @codingStandardsIgnoreFile + +/** @var \Magento\Payment\Block\Transparent\Iframe $block */ +$params = $block->getParams(); +?> +<html> + <head> + <script> + <?php if (isset($params['redirect'])): ?> + window.location="<?php echo $block->escapeUrl($params['redirect']) ?>"; + <?php elseif (isset($params['redirect_parent'])): ?> + window.top.location="<?php echo $block->escapeUrl($params['redirect_parent']) ?>"; + <?php elseif (isset($params['error_msg'])): ?> + window.top.alert(<?php echo $this->helper('Magento\Framework\Json\Helper\Data')->jsonEncode($params['error_msg']) ?>); + <?php elseif (isset($params['order_success'])): ?> + window.top.location = "<?php echo $params['order_success'] ?>"; + <?php else: ?> + var require = window.top.require; + require(['jquery'], function($) { + $('#opc-review').trigger('hideAjaxLoader'); + $('#opc-review').trigger('saveOrder'); + }); + <?php endif; ?> + </script> + </head> + <body></body> +</html> diff --git a/app/code/Magento/Payment/view/frontend/templates/transparent/info.phtml b/app/code/Magento/Payment/view/frontend/templates/transparent/info.phtml new file mode 100644 index 0000000000000000000000000000000000000000..badfd92d13a478cb0c409eccb4f9f3af9210e150 --- /dev/null +++ b/app/code/Magento/Payment/view/frontend/templates/transparent/info.phtml @@ -0,0 +1,13 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +/** + * @see \Magento\Payment\Block\Transparent\Info + */ +?> +<fieldset id="payment_form_<?php echo $block->getMethodCode() ?>" style="display:none" class="fieldset items redirect"> + <div><?php echo __('You\'ll be asked for your payment details before placing an order.') ?></div> +</fieldset> diff --git a/app/code/Magento/Payment/view/frontend/web/transparent.js b/app/code/Magento/Payment/view/frontend/web/transparent.js new file mode 100644 index 0000000000000000000000000000000000000000..570772d6d430c67d6dcd4ea10325fade6d3407b0 --- /dev/null +++ b/app/code/Magento/Payment/view/frontend/web/transparent.js @@ -0,0 +1,154 @@ +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +define([ + "jquery", + "mage/template", + "jquery/ui" +], function($, mageTemplate){ + "use strict"; + + $.widget('mage.transparent', { + options: { + placeOrderSelector: '[data-role="review-save"]', + paymentFormSelector: '#co-payment-form', + updateSelectorPrefix: '#checkout-', + updateSelectorSuffix: '-load', + hiddenFormTmpl: + '<form target="<%= data.target %>" action="<%= data.action %>" method="POST" hidden enctype="application/x-www-form-urlencoded" class="no-display">' + + '<% _.each(data.inputs, function(val, key){ %>' + + '<input value="<%= val %>" name="<%= key %>" type="hidden">' + + '<% }); %>' + + '</form>', + reviewAgreementForm: '#checkout-agreements', + cgiUrl: null, + orderSaveUrl: null, + controller: null, + gateway: null, + dateDelim: null, + cardFieldsMap: null + }, + + _create: function() { + this.hiddenFormTmpl = mageTemplate(this.options.hiddenFormTmpl); + $(this.options.placeOrderSelector) + .off('click') + .on('click', $.proxy(this._placeOrderHandler, this)); + }, + + /** + * handler for Place Order button to call gateway for credit card validation + * @return {Boolean} + * @private + */ + _placeOrderHandler: function() { + if (this.element.validation && this.element.validation('isValid')) { + this._orderSave(); + } + return false; + }, + + /** + * Save order and generate post data for gateway call + * @private + */ + _orderSave: function() { + var postData = $(this.options.paymentFormSelector).serialize(); + if ($(this.options.reviewAgreementForm).length) { + postData += '&' + $(this.options.reviewAgreementForm).serialize(); + } + postData += '&controller=' + this.options.controller; + $.ajax({ + url: this.options.orderSaveUrl, + type: 'post', + context: this, + data: postData, + dataType: 'json', + beforeSend: function() {this.element.trigger('showAjaxLoader');}, + success: function(response) { + var preparedData, + msg; + if (response.success && response[this.options.gateway]) { + preparedData = this._preparePaymentData( + response[this.options.gateway].fields, + this.options.cardFieldsMap + ); + this._postPaymentToGateway(preparedData); + } else { + msg = response.error_messages; + if (typeof (msg) === 'object') { + alert(msg.join("\n")); + } + if (msg) { + alert(msg); + } + } + } + }); + }, + + /** + * Post data to gateway for credit card validation + * @param data + * @private + */ + _postPaymentToGateway: function(data) { + var tmpl; + var iframeSelector = '[data-container="' + this.options.gateway + '-transparent-iframe"]'; + + tmpl = this.hiddenFormTmpl({ + data: { + target: $(iframeSelector).attr('name'), + action: this.options.cgiUrl, + inputs: data + } + }); + + $(tmpl).appendTo($(iframeSelector)).submit(); + }, + + /** + * Add credit card fields to post data for gateway + * @param data + * @param ccfields + * @private + */ + _preparePaymentData: function(data, ccfields) { + if (this.element.find('[data-container="' + this.options.gateway + '-cc-cvv"]').length) { + data[ccfields.cccvv] = this.element.find( + '[data-container="' + this.options.gateway + '-cc-cvv"]' + ).val(); + } + var preparedata = this._prepareExpDate(); + data[ccfields.ccexpdate] = preparedata.month + this.options.dateDelim + preparedata.year; + data[ccfields.ccnum] = this.element.find( + '[data-container="' + this.options.gateway + '-cc-number"]' + ).val(); + return data; + }, + + /** + * Grab Month and Year into one + * @returns {object} + * @private + */ + _prepareExpDate: function() { + var year = this.element.find('[data-container="' + this.options.gateway + '-cc-year"]').val(), + month = parseInt( + this.element.find('[data-container="' + this.options.gateway + '-cc-month"]').val(), + 10 + ); + if (year.length > 2) { + year = year.substring(2); + } + if (month < 10) { + month = '0' + month; + } + return {month: month, year: year}; + } + }); + + return $.mage.transparent; +}); diff --git a/app/code/Magento/Quote/Model/PaymentMethodManagement.php b/app/code/Magento/Quote/Model/PaymentMethodManagement.php index fa4add6be7b7ec66b9872c55d1f1f1983ea1c475..b3a512ae1b9330585b778670e4bd647a556e4210 100644 --- a/app/code/Magento/Quote/Model/PaymentMethodManagement.php +++ b/app/code/Magento/Quote/Model/PaymentMethodManagement.php @@ -7,10 +7,13 @@ namespace Magento\Quote\Model; use Magento\Framework\Exception\State\InvalidTransitionException; +/** + * Class PaymentMethodManagement + */ class PaymentMethodManagement implements \Magento\Quote\Api\PaymentMethodManagementInterface { /** - * @var \Magento\Quote\Model\QuoteRepository + * @var \Magento\Quote\Api\CartRepositoryInterface */ protected $quoteRepository; @@ -25,12 +28,14 @@ class PaymentMethodManagement implements \Magento\Quote\Api\PaymentMethodManagem protected $methodList; /** - * @param QuoteRepository $quoteRepository + * Constructor + * + * @param \Magento\Quote\Api\CartRepositoryInterface $quoteRepository * @param \Magento\Payment\Model\Checks\ZeroTotal $zeroTotalValidator * @param \Magento\Payment\Model\MethodList $methodList */ public function __construct( - \Magento\Quote\Model\QuoteRepository $quoteRepository, + \Magento\Quote\Api\CartRepositoryInterface $quoteRepository, \Magento\Payment\Model\Checks\ZeroTotal $zeroTotalValidator, \Magento\Payment\Model\MethodList $methodList ) { @@ -45,7 +50,7 @@ class PaymentMethodManagement implements \Magento\Quote\Api\PaymentMethodManagem public function set($cartId, \Magento\Quote\Api\Data\PaymentInterface $method) { /** @var \Magento\Quote\Model\Quote $quote */ - $quote = $this->quoteRepository->getActive($cartId); + $quote = $this->quoteRepository->get($cartId); $method->setChecks([ \Magento\Payment\Model\Method\AbstractMethod::CHECK_USE_CHECKOUT, @@ -87,7 +92,7 @@ class PaymentMethodManagement implements \Magento\Quote\Api\PaymentMethodManagem public function get($cartId) { /** @var \Magento\Quote\Model\Quote $quote */ - $quote = $this->quoteRepository->getActive($cartId); + $quote = $this->quoteRepository->get($cartId); $payment = $quote->getPayment(); if (!$payment->getId()) { return null; @@ -101,7 +106,7 @@ class PaymentMethodManagement implements \Magento\Quote\Api\PaymentMethodManagem public function getList($cartId) { /** @var \Magento\Quote\Model\Quote $quote */ - $quote = $this->quoteRepository->getActive($cartId); + $quote = $this->quoteRepository->get($cartId); return $this->methodList->getAvailableMethods($quote); } } diff --git a/app/code/Magento/Quote/Model/Quote.php b/app/code/Magento/Quote/Model/Quote.php index 88010642002f69ba6d36ff56c882fd2f96076ccd..377ba1d34f937857dc446288129b6cb389284e1d 100644 --- a/app/code/Magento/Quote/Model/Quote.php +++ b/app/code/Magento/Quote/Model/Quote.php @@ -11,6 +11,7 @@ namespace Magento\Quote\Model; use Magento\Customer\Api\Data\CustomerInterface; use Magento\Customer\Api\Data\GroupInterface; use Magento\Framework\Model\AbstractExtensibleModel; +use Magento\Quote\Api\Data\PaymentInterface; use Magento\Quote\Model\Quote\Address; use Magento\Sales\Model\Resource; use Magento\Sales\Model\Status; @@ -1857,10 +1858,12 @@ class Quote extends AbstractExtensibleModel implements \Magento\Quote\Api\Data\C } /** - * @param \Magento\Quote\Model\Quote\Payment $payment + * Adds a payment to quote + * + * @param PaymentInterface $payment * @return $this */ - public function addPayment(\Magento\Quote\Model\Quote\Payment $payment) + public function addPayment(PaymentInterface $payment) { $payment->setQuote($this); if (!$payment->getId()) { @@ -1870,10 +1873,12 @@ class Quote extends AbstractExtensibleModel implements \Magento\Quote\Api\Data\C } /** - * @param \Magento\Quote\Model\Quote\Payment $payment - * @return \Magento\Quote\Model\Quote\Payment + * Sets payment to current quote + * + * @param PaymentInterface $payment + * @return PaymentInterface */ - public function setPayment(\Magento\Quote\Model\Quote\Payment $payment) + public function setPayment(PaymentInterface $payment) { if (!$this->getIsMultiPayment() && ($old = $this->getPayment())) { $payment->setId($old->getId()); diff --git a/app/code/Magento/Quote/Model/Quote/Payment/ToOrderPayment.php b/app/code/Magento/Quote/Model/Quote/Payment/ToOrderPayment.php index 9e34d40fdb342ab5ede3cd784d687250c89c41fe..a65e71d224697d8d38abad335a3ba3354482a6c4 100644 --- a/app/code/Magento/Quote/Model/Quote/Payment/ToOrderPayment.php +++ b/app/code/Magento/Quote/Model/Quote/Payment/ToOrderPayment.php @@ -67,11 +67,9 @@ class ToOrderPayment '\Magento\Sales\Api\Data\OrderPaymentInterface' ); $orderPayment->setAdditionalInformation( - serialize( - array_merge( - $object->getAdditionalInformation(), - [Substitution::INFO_KEY_TITLE => $object->getMethodInstance()->getTitle()] - ) + array_merge( + $object->getAdditionalInformation(), + [Substitution::INFO_KEY_TITLE => $object->getMethodInstance()->getTitle()] ) ); // set directly on the model diff --git a/app/code/Magento/Quote/Test/Unit/Model/PaymentMethodManagementTest.php b/app/code/Magento/Quote/Test/Unit/Model/PaymentMethodManagementTest.php index 70dd9e5238ad2e7427574c95c5f39ed533dea5a4..78751a96441cc1cf17f18f01eb3d41fe2fc2f252 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/PaymentMethodManagementTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/PaymentMethodManagementTest.php @@ -34,12 +34,20 @@ class PaymentMethodManagementTest extends \PHPUnit_Framework_TestCase protected function setUp() { $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $this->quoteRepositoryMock = $this->getMock('\Magento\Quote\Model\QuoteRepository', [], [], '', false); - $this->methodListMock = $this->getMock('\Magento\Payment\Model\MethodList', [], [], '', false); - $this->zeroTotalMock = $this->getMock('\Magento\Payment\Model\Checks\ZeroTotal', [], [], '', false); + $this->quoteRepositoryMock = $this->getMockForAbstractClass( + 'Magento\Quote\Api\CartRepositoryInterface', + [], + '', + false, + true, + true, + [] + ); + $this->methodListMock = $this->getMock('Magento\Payment\Model\MethodList', [], [], '', false); + $this->zeroTotalMock = $this->getMock('Magento\Payment\Model\Checks\ZeroTotal', [], [], '', false); $this->model = $this->objectManager->getObject( - '\Magento\Quote\Model\PaymentMethodManagement', + 'Magento\Quote\Model\PaymentMethodManagement', [ 'quoteRepository' => $this->quoteRepositoryMock, 'methodList' => $this->methodListMock, @@ -51,13 +59,13 @@ class PaymentMethodManagementTest extends \PHPUnit_Framework_TestCase public function testGetPaymentIfPaymentMethodNotSet() { $cartId = 11; - $quoteMock = $this->getMock('\Magento\Quote\Model\Quote', [], [], '', false); - $paymentMock = $this->getMock('\Magento\Quote\Model\Quote\Payment', [], [], '', false); + $quoteMock = $this->getMock('Magento\Quote\Model\Quote', [], [], '', false); + $paymentMock = $this->getMock('Magento\Quote\Model\Quote\Payment', [], [], '', false); $quoteMock->expects($this->once())->method('getPayment')->will($this->returnValue($paymentMock)); $paymentMock->expects($this->once())->method('getId')->will($this->returnValue(null)); $this->quoteRepositoryMock->expects($this->once()) - ->method('getActive') + ->method('get') ->with($cartId) ->will($this->returnValue($quoteMock)); @@ -68,14 +76,14 @@ class PaymentMethodManagementTest extends \PHPUnit_Framework_TestCase { $cartId = 11; - $paymentMock = $this->getMock('\Magento\Quote\Model\Quote\Payment', [], [], '', false); + $paymentMock = $this->getMock('Magento\Quote\Model\Quote\Payment', [], [], '', false); $paymentMock->expects($this->once())->method('getId')->will($this->returnValue(1)); - $quoteMock = $this->getMock('\Magento\Quote\Model\Quote', [], [], '', false); + $quoteMock = $this->getMock('Magento\Quote\Model\Quote', [], [], '', false); $quoteMock->expects($this->once())->method('getPayment')->will($this->returnValue($paymentMock)); $this->quoteRepositoryMock->expects($this->once()) - ->method('getActive') + ->method('get') ->with($cartId) ->will($this->returnValue($quoteMock)); $this->assertEquals($paymentMock, $this->model->get($cartId)); @@ -84,13 +92,13 @@ class PaymentMethodManagementTest extends \PHPUnit_Framework_TestCase public function testGetList() { $cartId = 10; - $quoteMock = $this->getMock('\Magento\Quote\Model\Quote', [], [], '', false); + $quoteMock = $this->getMock('Magento\Quote\Model\Quote', [], [], '', false); $this->quoteRepositoryMock->expects($this->once()) - ->method('getActive') + ->method('get') ->with($cartId) ->will($this->returnValue($quoteMock)); - $paymentMethod = $this->getMock('\Magento\Quote\Api\Data\PaymentMethodInterface'); + $paymentMethod = $this->getMock('Magento\Quote\Api\Data\PaymentMethodInterface'); $this->methodListMock->expects($this->once()) ->method('getAvailableMethods') ->with($quoteMock) @@ -106,15 +114,15 @@ class PaymentMethodManagementTest extends \PHPUnit_Framework_TestCase $paymentMethod = 'checkmo'; $quoteMock = $this->getMock( - '\Magento\Quote\Model\Quote', + 'Magento\Quote\Model\Quote', ['setTotalsCollectedFlag', 'getPayment', 'isVirtual', 'getBillingAddress', 'collectTotals', 'save'], [], '', false ); - $this->quoteRepositoryMock->expects($this->once())->method('getActive')->with($cartId)->willReturn($quoteMock); + $this->quoteRepositoryMock->expects($this->once())->method('get')->with($cartId)->willReturn($quoteMock); - $methodMock = $this->getMock('\Magento\Quote\Model\Quote\Payment', ['setChecks', 'getData'], [], '', false); + $methodMock = $this->getMock('Magento\Quote\Model\Quote\Payment', ['setChecks', 'getData'], [], '', false); $methodMock->expects($this->once()) ->method('setChecks') ->with([ @@ -127,7 +135,7 @@ class PaymentMethodManagementTest extends \PHPUnit_Framework_TestCase $methodMock->expects($this->once())->method('getData')->willReturn($methodData); $paymentMock = $this->getMock( - '\Magento\Quote\Model\Quote\Payment', + 'Magento\Quote\Model\Quote\Payment', ['importData', 'getMethod', 'getMethodInstance', 'getId'], [], '', @@ -137,7 +145,7 @@ class PaymentMethodManagementTest extends \PHPUnit_Framework_TestCase $paymentMock->expects($this->once())->method('getMethod')->willReturn($paymentMethod); $billingAddressMock = $this->getMock( - '\Magento\Quote\Model\Quote\Address', + 'Magento\Quote\Model\Quote\Address', ['getCountryId', 'setPaymentMethod'], [], '', @@ -153,7 +161,7 @@ class PaymentMethodManagementTest extends \PHPUnit_Framework_TestCase $quoteMock->expects($this->exactly(2))->method('isVirtual')->willReturn(true); $quoteMock->expects($this->exactly(2))->method('getBillingAddress')->willReturn($billingAddressMock); - $methodInstance = $this->getMock('\Magento\Payment\Model\Checks\PaymentMethodChecksInterface'); + $methodInstance = $this->getMock('Magento\Payment\Model\Checks\PaymentMethodChecksInterface'); $paymentMock->expects($this->once())->method('getMethodInstance')->willReturn($methodInstance); $this->zeroTotalMock->expects($this->once()) @@ -179,15 +187,15 @@ class PaymentMethodManagementTest extends \PHPUnit_Framework_TestCase $methodData = ['method' => 'data']; $quoteMock = $this->getMock( - '\Magento\Quote\Model\Quote', + 'Magento\Quote\Model\Quote', ['getPayment', 'isVirtual', 'getBillingAddress'], [], '', false ); - $this->quoteRepositoryMock->expects($this->once())->method('getActive')->with($cartId)->willReturn($quoteMock); + $this->quoteRepositoryMock->expects($this->once())->method('get')->with($cartId)->willReturn($quoteMock); - $methodMock = $this->getMock('\Magento\Quote\Model\Quote\Payment', ['setChecks', 'getData'], [], '', false); + $methodMock = $this->getMock('Magento\Quote\Model\Quote\Payment', ['setChecks', 'getData'], [], '', false); $methodMock->expects($this->once()) ->method('setChecks') ->with([ @@ -199,10 +207,10 @@ class PaymentMethodManagementTest extends \PHPUnit_Framework_TestCase ->willReturnSelf(); $methodMock->expects($this->once())->method('getData')->willReturn($methodData); - $paymentMock = $this->getMock('\Magento\Quote\Model\Quote\Payment', ['importData', 'getMethod'], [], '', false); + $paymentMock = $this->getMock('Magento\Quote\Model\Quote\Payment', ['importData', 'getMethod'], [], '', false); $paymentMock->expects($this->once())->method('importData')->with($methodData)->willReturnSelf(); - $billingAddressMock = $this->getMock('\Magento\Quote\Model\Quote\Address', ['getCountryId'], [], '', false); + $billingAddressMock = $this->getMock('Magento\Quote\Model\Quote\Address', ['getCountryId'], [], '', false); $billingAddressMock->expects($this->once())->method('getCountryId')->willReturn(null); $quoteMock->expects($this->once())->method('getPayment')->willReturn($paymentMock); @@ -223,15 +231,15 @@ class PaymentMethodManagementTest extends \PHPUnit_Framework_TestCase $paymentMethod = 'checkmo'; $quoteMock = $this->getMock( - '\Magento\Quote\Model\Quote', + 'Magento\Quote\Model\Quote', ['getPayment', 'isVirtual', 'getBillingAddress'], [], '', false ); - $this->quoteRepositoryMock->expects($this->once())->method('getActive')->with($cartId)->willReturn($quoteMock); + $this->quoteRepositoryMock->expects($this->once())->method('get')->with($cartId)->willReturn($quoteMock); - $methodMock = $this->getMock('\Magento\Quote\Model\Quote\Payment', ['setChecks', 'getData'], [], '', false); + $methodMock = $this->getMock('Magento\Quote\Model\Quote\Payment', ['setChecks', 'getData'], [], '', false); $methodMock->expects($this->once()) ->method('setChecks') ->with([ @@ -244,7 +252,7 @@ class PaymentMethodManagementTest extends \PHPUnit_Framework_TestCase $methodMock->expects($this->once())->method('getData')->willReturn($methodData); $paymentMock = $this->getMock( - '\Magento\Quote\Model\Quote\Payment', + 'Magento\Quote\Model\Quote\Payment', ['importData', 'getMethod', 'getMethodInstance'], [], '', @@ -254,7 +262,7 @@ class PaymentMethodManagementTest extends \PHPUnit_Framework_TestCase $paymentMock->expects($this->once())->method('getMethod')->willReturn($paymentMethod); $billingAddressMock = $this->getMock( - '\Magento\Quote\Model\Quote\Address', + 'Magento\Quote\Model\Quote\Address', ['getCountryId', 'setPaymentMethod'], [], '', @@ -270,7 +278,7 @@ class PaymentMethodManagementTest extends \PHPUnit_Framework_TestCase $quoteMock->expects($this->exactly(2))->method('isVirtual')->willReturn(true); $quoteMock->expects($this->exactly(2))->method('getBillingAddress')->willReturn($billingAddressMock); - $methodInstance = $this->getMock('\Magento\Payment\Model\Checks\PaymentMethodChecksInterface'); + $methodInstance = $this->getMock('Magento\Payment\Model\Checks\PaymentMethodChecksInterface'); $paymentMock->expects($this->once())->method('getMethodInstance')->willReturn($methodInstance); $this->zeroTotalMock->expects($this->once()) @@ -288,15 +296,15 @@ class PaymentMethodManagementTest extends \PHPUnit_Framework_TestCase $paymentMethod = 'checkmo'; $quoteMock = $this->getMock( - '\Magento\Quote\Model\Quote', + 'Magento\Quote\Model\Quote', ['getPayment', 'isVirtual', 'getShippingAddress', 'setTotalsCollectedFlag', 'collectTotals', 'save'], [], '', false ); - $this->quoteRepositoryMock->expects($this->once())->method('getActive')->with($cartId)->willReturn($quoteMock); + $this->quoteRepositoryMock->expects($this->once())->method('get')->with($cartId)->willReturn($quoteMock); - $methodMock = $this->getMock('\Magento\Quote\Model\Quote\Payment', ['setChecks', 'getData'], [], '', false); + $methodMock = $this->getMock('Magento\Quote\Model\Quote\Payment', ['setChecks', 'getData'], [], '', false); $methodMock->expects($this->once()) ->method('setChecks') ->with([ @@ -309,7 +317,7 @@ class PaymentMethodManagementTest extends \PHPUnit_Framework_TestCase $methodMock->expects($this->once())->method('getData')->willReturn($methodData); $paymentMock = $this->getMock( - '\Magento\Quote\Model\Quote\Payment', + 'Magento\Quote\Model\Quote\Payment', ['importData', 'getMethod', 'getMethodInstance', 'getId'], [], '', @@ -319,7 +327,7 @@ class PaymentMethodManagementTest extends \PHPUnit_Framework_TestCase $paymentMock->expects($this->once())->method('getMethod')->willReturn($paymentMethod); $shippingAddressMock = $this->getMock( - '\Magento\Quote\Model\Quote\Address', + 'Magento\Quote\Model\Quote\Address', ['getCountryId', 'setPaymentMethod'], [], '', @@ -335,7 +343,7 @@ class PaymentMethodManagementTest extends \PHPUnit_Framework_TestCase $quoteMock->expects($this->exactly(2))->method('isVirtual')->willReturn(false); $quoteMock->expects($this->exactly(4))->method('getShippingAddress')->willReturn($shippingAddressMock); - $methodInstance = $this->getMock('\Magento\Payment\Model\Checks\PaymentMethodChecksInterface'); + $methodInstance = $this->getMock('Magento\Payment\Model\Checks\PaymentMethodChecksInterface'); $paymentMock->expects($this->once())->method('getMethodInstance')->willReturn($methodInstance); $this->zeroTotalMock->expects($this->once()) @@ -361,15 +369,15 @@ class PaymentMethodManagementTest extends \PHPUnit_Framework_TestCase $methodData = ['method' => 'data']; $quoteMock = $this->getMock( - '\Magento\Quote\Model\Quote', + 'Magento\Quote\Model\Quote', ['getPayment', 'isVirtual', 'getShippingAddress'], [], '', false ); - $this->quoteRepositoryMock->expects($this->once())->method('getActive')->with($cartId)->willReturn($quoteMock); + $this->quoteRepositoryMock->expects($this->once())->method('get')->with($cartId)->willReturn($quoteMock); - $methodMock = $this->getMock('\Magento\Quote\Model\Quote\Payment', ['setChecks', 'getData'], [], '', false); + $methodMock = $this->getMock('Magento\Quote\Model\Quote\Payment', ['setChecks', 'getData'], [], '', false); $methodMock->expects($this->once()) ->method('setChecks') ->with([ @@ -381,10 +389,10 @@ class PaymentMethodManagementTest extends \PHPUnit_Framework_TestCase ->willReturnSelf(); $methodMock->expects($this->once())->method('getData')->willReturn($methodData); - $paymentMock = $this->getMock('\Magento\Quote\Model\Quote\Payment', ['importData'], [], '', false); + $paymentMock = $this->getMock('Magento\Quote\Model\Quote\Payment', ['importData'], [], '', false); $paymentMock->expects($this->once())->method('importData')->with($methodData)->willReturnSelf(); - $shippingAddressMock = $this->getMock('\Magento\Quote\Model\Quote\Address', ['getCountryId'], [], '', false); + $shippingAddressMock = $this->getMock('Magento\Quote\Model\Quote\Address', ['getCountryId'], [], '', false); $shippingAddressMock->expects($this->once())->method('getCountryId')->willReturn(null); $quoteMock->expects($this->once())->method('getPayment')->willReturn($paymentMock); diff --git a/app/code/Magento/Quote/Test/Unit/Model/Quote/Payment/ToOrderPaymentTest.php b/app/code/Magento/Quote/Test/Unit/Model/Quote/Payment/ToOrderPaymentTest.php index b68aeb602acb6cfd7d7d8af9cf45b60595dcd6c6..aa6099192147fe2557b93e465f822f1b30753d57 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/Quote/Payment/ToOrderPaymentTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/Quote/Payment/ToOrderPaymentTest.php @@ -113,7 +113,7 @@ class ToOrderPaymentTest extends \PHPUnit_Framework_TestCase ); $orderPayment->expects($this->once()) ->method('setAdditionalInformation') - ->with(serialize(array_merge($additionalInfo, [Substitution::INFO_KEY_TITLE => $paymentMethodTitle]))) + ->with(array_merge($additionalInfo, [Substitution::INFO_KEY_TITLE => $paymentMethodTitle])) ->willReturnSelf(); $orderPayment->expects($this->once()) ->method('setCcNumber') diff --git a/app/code/Magento/Sales/view/adminhtml/web/order/create/scripts.js b/app/code/Magento/Sales/view/adminhtml/web/order/create/scripts.js index 029a93e92456b0468571bb146a49c3aff2cc7ad0..f936da5c2516adf5ef34db62f334d811daf07adb 100644 --- a/app/code/Magento/Sales/view/adminhtml/web/order/create/scripts.js +++ b/app/code/Magento/Sales/view/adminhtml/web/order/create/scripts.js @@ -32,6 +32,7 @@ AdminOrder.prototype = { this.productPriceBase = {}; this.collectElementsValue = true; this.isOnlyVirtualProduct = false; + this.excludedPaymentMethods = []; Event.observe(window, 'load', (function(){ this.dataArea = new OrderFormArea('data', $(this.getAreaId('data')), this); this.itemsArea = Object.extend(new OrderFormArea('items', $(this.getAreaId('items')), this), { @@ -74,6 +75,13 @@ AdminOrder.prototype = { this.areasLoaded(); this.itemsArea.onLoad(); }).bind(this)); + + jQuery('#edit_form') + .on('submitOrder', function(){ + jQuery(this).trigger('realOrder'); + }) + .on('realOrder', this._realSubmit.bind(this)); + }, areasLoaded: function(){ @@ -94,6 +102,10 @@ AdminOrder.prototype = { this.addresses = addresses; }, + addExcludedPaymentMethod : function(method){ + this.excludedPaymentMethods.push(method); + }, + setCustomerId : function(id){ this.customerId = id; this.loadArea('header', true); @@ -327,6 +339,7 @@ AdminOrder.prototype = { }, switchPaymentMethod : function(method){ + jQuery('#edit_form').trigger('changePaymentMethod', [method]); this.setPaymentMethod(method); var data = {}; data['order[payment_method]'] = method; @@ -354,6 +367,7 @@ AdminOrder.prototype = { } if ($('payment_form_'+method)){ + jQuery('#' + this.getAreaId('billing_method')).trigger('contentUpdated'); this.paymentMethod = method; var form = 'payment_form_'+method; [form + '_before', form, form + '_after'].each(function(el) { @@ -394,6 +408,9 @@ AdminOrder.prototype = { return false; } } + if (this.isPaymentValidationAvailable() == false) { + return false; + } var data = {}; var fields = $('payment_form_' + currentMethod).select('input', 'select'); for(var i=0;i<fields.length;i++){ @@ -1037,15 +1054,30 @@ AdminOrder.prototype = { if (!params.form_key) { params.form_key = FORM_KEY; } - var data = this.serializeData('order-billing_method'); - if (data) { - data.each(function(value) { - params[value[0]] = value[1]; - }); + + if (this.isPaymentValidationAvailable()) { + var data = this.serializeData('order-billing_method'); + if (data) { + data.each(function(value) { + params[value[0]] = value[1]; + }); + } + } else { + params['payment[method]'] = this.paymentMethod; } return params; }, + /** + * Prevent from sending credit card information to server for some payment methods + * + * @returns {boolean} + */ + isPaymentValidationAvailable : function(){ + return ((typeof this.paymentMethod) == 'undefined' + || this.excludedPaymentMethods.indexOf(this.paymentMethod) == -1); + }, + serializeData : function(container){ var fields = $(container).select('input', 'select', 'textarea'); var data = Form.serializeElements(fields, true); @@ -1068,7 +1100,11 @@ AdminOrder.prototype = { submit : function() { - // Temporary solution will be replaced after refactoring order functionality + jQuery('#edit_form').trigger('processStart'); + jQuery('#edit_form').trigger('submitOrder'); + }, + + _realSubmit: function () { var disableAndSave = function() { disableElements('save'); jQuery('#edit_form').on('invalid-form.validate', function() { @@ -1308,4 +1344,4 @@ ControlButton.prototype = { } }; -}); \ No newline at end of file +}); diff --git a/dev/tests/integration/testsuite/Magento/Payment/Block/Transparent/IframeTest.php b/dev/tests/integration/testsuite/Magento/Payment/Block/Transparent/IframeTest.php new file mode 100644 index 0000000000000000000000000000000000000000..c91ead38e49e959564d03b2f90785d4f5e710bdb --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Payment/Block/Transparent/IframeTest.php @@ -0,0 +1,40 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Payment\Block\Transparent; + +class IframeTest extends \PHPUnit_Framework_TestCase +{ + /** + * @magentoAppIsolation enabled + * @magentoAppArea frontend + */ + public function testToHtml() + { + $xssString = '</script><script>alert("XSS")</script>'; + + /** @var $block Iframe */ + $block = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( + 'Magento\Framework\View\LayoutInterface' + )->createBlock( + 'Magento\Payment\Block\Transparent\Iframe' + ); + + $block->setTemplate('transparent/iframe.phtml'); + $block->setData( + 'params', + [ + 'redirect' => $xssString, + 'redirect_parent' => $xssString, + 'error_msg' => $xssString + ] + ); + + $content = $block->toHtml(); + + $this->assertNotContains($xssString, $content, 'Params mast be escaped'); + $this->assertContains(htmlspecialchars($xssString), $content, 'Content must present'); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Test/Integrity/Magento/Payment/MethodsTest.php b/dev/tests/integration/testsuite/Magento/Test/Integrity/Magento/Payment/MethodsTest.php index 56dfca42c97498e1a5a08510b766b7b925cfeb71..a0073fdd17b0ff1c859e85ec12dc0bc992c6f74c 100644 --- a/dev/tests/integration/testsuite/Magento/Test/Integrity/Magento/Payment/MethodsTest.php +++ b/dev/tests/integration/testsuite/Magento/Test/Integrity/Magento/Payment/MethodsTest.php @@ -60,7 +60,7 @@ class MethodsTest extends \PHPUnit_Framework_TestCase /** @var $block \Magento\Framework\View\Element\Template */ $block = $blockFactory->createBlock($blockClass); $block->setArea('frontend'); - $this->assertFileExists($block->getTemplateFile(), $message); + $this->assertFileExists((string)$block->getTemplateFile(), $message); if ($model->canUseInternal()) { try { \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( @@ -69,7 +69,7 @@ class MethodsTest extends \PHPUnit_Framework_TestCase \Magento\Store\Model\Store::DEFAULT_STORE_ID ); $block->setArea('adminhtml'); - $this->assertFileExists($block->getTemplateFile(), $message); + $this->assertFileExists((string)$block->getTemplateFile(), $message); \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( 'Magento\Store\Model\StoreManagerInterface' )->getStore()->setId( diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/LayoutTest.php b/dev/tests/static/testsuite/Magento/Test/Legacy/LayoutTest.php index ae89d5ddb32941c6dace4b041fe459153a34a6cb..28353e7f953138cfa0dd3d91b04c17f70062e07a 100644 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/LayoutTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/LayoutTest.php @@ -316,7 +316,6 @@ class LayoutTest extends \PHPUnit_Framework_TestCase 'setListOrders', 'setMAPTemplate', 'setMethodFormTemplate', - 'setMethodInfo', 'setMyClass', 'setPageLayout', 'setPageTitle',