diff --git a/modules/payment_form/src/Plugin/Payment/Type/PaymentForm.php b/modules/payment_form/src/Plugin/Payment/Type/PaymentForm.php index 1031cb8..0e8680d 100644 --- a/modules/payment_form/src/Plugin/Payment/Type/PaymentForm.php +++ b/modules/payment_form/src/Plugin/Payment/Type/PaymentForm.php @@ -10,12 +10,11 @@ use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\Core\Session\AccountInterface; use Drupal\Core\StringTranslation\TranslationInterface; +use Drupal\Core\Url; use Drupal\payment\Plugin\Payment\Type\PaymentTypeBase; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\HttpFoundation\RedirectResponse; -use Symfony\Component\HttpKernel\Event\FilterResponseEvent; -use Symfony\Component\HttpKernel\KernelEvents; /** * The payment form field payment type. @@ -93,12 +92,8 @@ class PaymentForm extends PaymentTypeBase implements ContainerFactoryPluginInter /** * {@inheritdoc} */ - protected function doResumeContext() { - $response = new RedirectResponse($this->getDestinationUrl()); - $listener = function(FilterResponseEvent $event) use ($response) { - $event->setResponse($response); - }; - $this->eventDispatcher->addListener(KernelEvents::RESPONSE, $listener, 999); + protected function doGetResumeContextResponse() { + return new RedirectResponse(Url::fromUri($this->getDestinationUrl())); } /** diff --git a/modules/payment_form/tests/src/Unit/Plugin/Payment/Type/PaymentFormUnitTest.php b/modules/payment_form/tests/src/Unit/Plugin/Payment/Type/PaymentFormUnitTest.php index 5e772c7..22d54c3 100644 --- a/modules/payment_form/tests/src/Unit/Plugin/Payment/Type/PaymentFormUnitTest.php +++ b/modules/payment_form/tests/src/Unit/Plugin/Payment/Type/PaymentFormUnitTest.php @@ -199,30 +199,18 @@ class PaymentFormUnitTest extends UnitTestCase { } /** - * @covers ::doResumeContext + * @covers ::doGetResumeContextResponse * @depends testGetDestinationUrl */ - public function testDoResumeContext() { - $url = 'http://example.com'; + public function testDoGetResumeContextResponse() { + $url = 'http://example.com/' . $this->randomMachineName(); $this->paymentType->setDestinationUrl($url); - $kernel = $this->getMock('\Symfony\Component\HttpKernel\HttpKernelInterface'); - $request = $this->getMockBuilder('\Symfony\Component\HttpFoundation\Request') - ->disableOriginalConstructor() - ->getMock(); - $request_type = $this->randomMachineName(); - $response = $this->getMockBuilder('\Symfony\Component\HttpFoundation\Response') - ->disableOriginalConstructor() - ->getMock(); - $event = new FilterResponseEvent($kernel, $request, $request_type, $response); - - $this->eventDispatcher->expects($this->once()) - ->method('addListener') - ->with(KernelEvents::RESPONSE, new PaymentFormUnitTestDoResumeContextCallableConstraint($event, $url), 999); + $response = $this->paymentType->getResumeContextResponse(); - - $this->paymentType->resumeContext(); + $this->assertInstanceOf('\Drupal\payment\ResponseInterface', $response); + $this->assertSame($url, $response->getRedirectUrl()->toString()); } /** @@ -236,56 +224,3 @@ class PaymentFormUnitTest extends UnitTestCase { } } - -/** - * Provides a constraint for the doResumeContext() callable. - */ -class PaymentFormUnitTestDoResumeContextCallableConstraint extends \PHPUnit_Framework_Constraint { - - /** - * The event to listen to. - * - * @var \Symfony\Component\HttpKernel\Event\FilterResponseEvent - */ - protected $event; - - /** - * The redirect URL. - * - * @var string - */ - protected $url; - - /** - * Constructs a new class instance. - * - * @param \Symfony\Component\HttpKernel\Event\FilterResponseEvent $event - * @param string $url - */ - public function __construct(FilterResponseEvent $event, $url) { - $this->event = $event; - $this->url = $url; - } - - /** - * {@inheritdoc} - */ - public function matches($other) { - if (is_callable($other)) { - $other($this->event); - $response = $this->event->getResponse(); - if ($response instanceof RedirectResponse) { - return $response->getTargetUrl() == $this->url; - } - } - return FALSE; - } - - /** - * {@inheritdoc} - */ - public function toString() { - return 'returns a RedirectResponse through a KernelEvents::RESPONSE event listener'; - } - -} \ No newline at end of file diff --git a/modules/payment_reference/payment_reference.module b/modules/payment_reference/payment_reference.module index 21aa625..9522139 100644 --- a/modules/payment_reference/payment_reference.module +++ b/modules/payment_reference/payment_reference.module @@ -14,19 +14,6 @@ use Drupal\field\FieldConfigInterface; use Drupal\field\FieldStorageConfigInterface; use Drupal\payment\Entity\PaymentInterface; use Drupal\payment_reference\PaymentReference; -use Symfony\Cmf\Component\Routing\RouteObjectInterface; - -/** - * Implements hook_page_alter(). - */ -function payment_reference_page_alter(&$page) { - if (\Drupal::request()->attributes->get(RouteObjectInterface::ROUTE_NAME) == 'payment_reference.pay') { - $skip_regions = array_diff(Element::children($page), array('content')); - foreach ($skip_regions as $skip_region) { - $page[$skip_region]['#access'] = FALSE; - } - } -} /** * Implements hook_entity_extra_field_info(). diff --git a/modules/payment_reference/payment_reference.routing.yml b/modules/payment_reference/payment_reference.routing.yml index 6773cc6..0945401 100644 --- a/modules/payment_reference/payment_reference.routing.yml +++ b/modules/payment_reference/payment_reference.routing.yml @@ -1,10 +1,3 @@ -payment_reference.pay: - path: '/payment_reference/pay/{storage_key}' - defaults: - _controller: '\Drupal\payment_reference\Controller\Pay::execute' - requirements: - _custom_access: '\Drupal\payment_reference\Controller\Pay::access' - _csrf_token: 'TRUE' payment_reference.resume_context: path: '/payment_reference/resume/{payment}' defaults: diff --git a/modules/payment_reference/src/Controller/Pay.php b/modules/payment_reference/src/Controller/Pay.php deleted file mode 100644 index 46a1c5d..0000000 --- a/modules/payment_reference/src/Controller/Pay.php +++ /dev/null @@ -1,77 +0,0 @@ -keyValueFactory = $key_value_factory; - } - - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container) { - return new static($container->get('keyvalue.expirable')); - } - - /** - * Executes a payment. - * - * @param string $storage_key - * The storage key with which the payment can be loaded. - * - * @return mixed[] - * A render array. - */ - public function execute($storage_key) { - $storage = $this->keyValueFactory->get('payment.payment_type.payment_reference'); - /** @var \Drupal\payment\Entity\PaymentInterface $payment */ - $payment = $storage->get($storage_key); - $storage->delete($storage_key); - $payment->execute(); - $payment->getPaymentType()->resumeContext(); - return array(); - } - - /** - * Checks if the user has access to make a payment. - * - * @param string $storage_key - * The storage key with which the payment can be loaded. - * - * @return \Drupal\Core\Access\AccessResultInterface - * The access result. - */ - public function access($storage_key) { - return AccessResult::allowedIf($this->keyValueFactory->get('payment.payment_type.payment_reference')->has($storage_key)); - } - -} diff --git a/modules/payment_reference/src/Element/PaymentReferenceBase.php b/modules/payment_reference/src/Element/PaymentReferenceBase.php index cf2ddb5..f7d5caf 100644 --- a/modules/payment_reference/src/Element/PaymentReferenceBase.php +++ b/modules/payment_reference/src/Element/PaymentReferenceBase.php @@ -417,9 +417,7 @@ abstract class PaymentReferenceBase extends FormElement implements FormElementIn 'class' => array('button', 'payment_reference-complete-payment-link'), 'target' => '_blank', ), - '#url' => new Url('payment_reference.pay', array( - 'storage_key' => $this->getTemporaryPaymentStorageKey($element, $form_state), - )), + '#url' => $payment_method->getPayment()->urlInfo('complete'), '#title' => $this->t('Complete payment'), '#type' => 'link', ); @@ -456,23 +454,18 @@ abstract class PaymentReferenceBase extends FormElement implements FormElementIn $payment = $payment_method_selector->getPayment(); $payment_method = $payment_method_selector->getPaymentMethod(); $payment->setPaymentMethod($payment_method); - if ($payment_method->isPaymentExecutionInterruptive()) { - $this->setTemporaryPayment($root_element, $form_state, $payment); + + $result = $payment->execute(); + if (!$result->hasCompleted()) { if (!$this->requestStack->getCurrentRequest()->isXmlHttpRequest()) { - $link = $this->linkGenerator->generate($this->t('Complete payment (opens in a new window).'), new Url('payment_reference.pay', array( - 'storage_key' => $this->getTemporaryPaymentStorageKey($root_element, $form_state), - ), array( - 'attributes' => array( - 'target' => '_blank', - ), - ))); + $url = $payment->urlInfo('complete'); + $url->setOption('attributes', [ + 'target' => '_blank', + ]); + $link = $this->linkGenerator->generate($this->t('Complete payment (opens in a new window).'), $url); drupal_set_message($link); } } - else { - $payment->save(); - $payment->execute(); - } $form_state->setRebuild(); } @@ -487,7 +480,7 @@ abstract class PaymentReferenceBase extends FormElement implements FormElementIn $response = new AjaxResponse(); $response->addCommand(new ReplaceCommand('#' . $root_element['container']['#id'], drupal_render($root_element['container']))); - if ($this->getPaymentMethodSelector($root_element, $form_state)->getPaymentMethod()->isPaymentExecutionInterruptive()) { + if (!$this->getPaymentMethodSelector($root_element, $form_state)->getPaymentMethod()->getPaymentExecutionResult()->hasCompleted()) { $link = $this->buildCompletePaymentLink($root_element, $form_state); $response->addCommand(new OpenModalDialogCommand($this->t('Complete payment'), drupal_render($link))); } @@ -563,58 +556,6 @@ abstract class PaymentReferenceBase extends FormElement implements FormElementIn } /** - * Gets the element temporary payment storage key. - * - * @param array $element - * @param \Drupal\Core\Form\FormStateInterface $form_state - * - * @return string - */ - protected function getTemporaryPaymentStorageKey(array $element, FormStateInterface $form_state) { - $key = 'payment_reference.element.payment_reference.temporary_storage_key.' . $element['#name']; - if (!$form_state->has($key)) { - $form_state->set($key, $this->random->name(128)); - } - - return $form_state->get($key); - } - - /** - * Stores the payment temporarily. - * - * @param array $element - * @param \Drupal\Core\Form\FormStateInterface $form_state - * @param \Drupal\payment\Entity\PaymentInterface $payment - */ - protected function setTemporaryPayment(array $element, FormStateInterface $form_state, PaymentInterface $payment) { - $this->getTemporaryPaymentStorage()->setWithExpire($this->getTemporaryPaymentStorageKey($element, $form_state), $payment, static::KEY_VALUE_TTL); - } - - /** - * Checks if a temporary payment exists. - * - * @param array $element - * @param \Drupal\Core\Form\FormStateInterface $form_state - * - * @return bool - */ - protected function hasTemporaryPayment(array $element, FormStateInterface $form_state) { - return $this->getTemporaryPaymentStorage()->has($this->getTemporaryPaymentStorageKey($element, $form_state)); - } - - /** - * Retrieves the temporarily stored payment. - * - * @param array $element - * @param \Drupal\Core\Form\FormStateInterface $form_state - * - * @return \Drupal\payment\Entity\PaymentInterface - */ - protected function getTemporaryPayment(array $element, FormStateInterface $form_state) { - return $this->getTemporaryPaymentStorage()->get($this->getTemporaryPaymentStorageKey($element, $form_state)); - } - - /** * Gets the payment queue. * * @return \Drupal\payment\QueueInterface @@ -622,13 +563,6 @@ abstract class PaymentReferenceBase extends FormElement implements FormElementIn abstract protected function getPaymentQueue(); /** - * Gets the temporary payment storage. - * - * @return \Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface - */ - abstract protected function getTemporaryPaymentStorage(); - - /** * {@inheritdoc} */ public static function valueCallback(&$element, $input, FormStateInterface $form_state) { diff --git a/modules/payment_reference/src/Plugin/Payment/Type/PaymentReference.php b/modules/payment_reference/src/Plugin/Payment/Type/PaymentReference.php index 5947555..064b1ce 100644 --- a/modules/payment_reference/src/Plugin/Payment/Type/PaymentReference.php +++ b/modules/payment_reference/src/Plugin/Payment/Type/PaymentReference.php @@ -11,12 +11,11 @@ use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\Core\Routing\UrlGeneratorInterface; use Drupal\Core\Session\AccountInterface; use Drupal\Core\StringTranslation\TranslationInterface; +use Drupal\Core\Url; use Drupal\payment\Plugin\Payment\Type\PaymentTypeBase; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\HttpFoundation\RedirectResponse; -use Symfony\Component\HttpKernel\Event\FilterResponseEvent; -use Symfony\Component\HttpKernel\KernelEvents; /** * The payment reference field payment type. @@ -98,22 +97,12 @@ class PaymentReference extends PaymentTypeBase implements ContainerFactoryPlugin /** * {@inheritdoc} */ - protected function doResumeContext() { - // If the payment method does not interrupt the payment type context, the - // payer is still in the original context and we do not need to redirect - // them back. - if ($this->getPayment()->getPaymentMethod()->isPaymentExecutionInterruptive()) { - $url = $this->urlGenerator->generateFromRoute('payment_reference.resume_context', array( - 'payment' => $this->getPayment()->id(), - ), array( - 'absolute' => TRUE, - )); - $response = new RedirectResponse($url); - $listener = function(FilterResponseEvent $event) use ($response) { - $event->setResponse($response); - }; - $this->eventDispatcher->addListener(KernelEvents::RESPONSE, $listener, 999); - } + protected function doGetResumeContextResponse() { + return new RedirectResponse(new Url('payment_reference.resume_context', array( + 'payment' => $this->getPayment()->id(), + ), array( + 'absolute' => TRUE, + ))); } /** diff --git a/modules/payment_reference/src/Tests/Element/PaymentReferenceWebTest.php b/modules/payment_reference/src/Tests/Element/PaymentReferenceWebTest.php index c8f2515..89fc05c 100644 --- a/modules/payment_reference/src/Tests/Element/PaymentReferenceWebTest.php +++ b/modules/payment_reference/src/Tests/Element/PaymentReferenceWebTest.php @@ -89,7 +89,7 @@ class PaymentReferenceWebTest extends WebTestBase { // Test with a non-interruptive payment method. $text_field_value = $this->randomMachineName(); $this->drupalPostForm($path, array( - 'payment_reference[container][payment_form][payment_method][container][select][container][payment_method_id]' => 'payment_test_uninterruptive', + 'payment_reference[container][payment_form][payment_method][container][select][container][payment_method_id]' => 'payment_test_no_response', 'payment_reference[container][payment_form][quxfoobar][0][value]' => $text_field_value, ), t('Choose payment method')); $this->drupalPostForm(NULL, array(), t('Pay')); @@ -106,7 +106,7 @@ class PaymentReferenceWebTest extends WebTestBase { // @todo Once Behat is supported, test the behavior of opening a new window // and going back to the original form. $this->drupalPostForm($path, array( - 'payment_reference[container][payment_form][payment_method][container][select][container][payment_method_id]' => 'payment_test_interruptive', + 'payment_reference[container][payment_form][payment_method][container][select][container][payment_method_id]' => 'payment_test_response', 'payment_reference[container][payment_form][quxfoobar][0][value]' => $text_field_value, ), t('Choose payment method')); $this->drupalPostForm(NULL, array(), t('Pay')); diff --git a/modules/payment_reference/tests/src/Unit/Controller/PayUnitTest.php b/modules/payment_reference/tests/src/Unit/Controller/PayUnitTest.php deleted file mode 100644 index 3529981..0000000 --- a/modules/payment_reference/tests/src/Unit/Controller/PayUnitTest.php +++ /dev/null @@ -1,127 +0,0 @@ -keyValueFactory = $this->getMock('\Drupal\Core\KeyValueStore\KeyValueFactoryInterface'); - - $this->controller = new Pay($this->keyValueFactory); - } - - /** - * @covers ::create - */ - function testCreate() { - $container = $this->getMock('\Symfony\Component\DependencyInjection\ContainerInterface'); - $map = array( - array('keyvalue.expirable', ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, $this->keyValueFactory), - ); - $container->expects($this->any()) - ->method('get') - ->will($this->returnValueMap($map)); - - $form = Pay::create($container); - $this->assertInstanceOf('\Drupal\payment_reference\Controller\Pay', $form); - } - - /** - * @covers ::execute - */ - public function testExecute() { - $payment_type = $this->getMock('\Drupal\payment\Plugin\Payment\Type\PaymentTypeInterface'); - $payment_type->expects($this->once()) - ->method('resumeContext'); - - $payment = $this->getMockBuilder('\Drupal\payment\Entity\Payment') - ->disableOriginalConstructor() - ->getMock(); - $payment->expects($this->once()) - ->method('execute'); - $payment->expects($this->once()) - ->method('getPaymentType') - ->willReturn($payment_type); - - $storage_key = $this->randomMachineName(); - - $storage = $this->getMock('\Drupal\Core\KeyValueStore\KeyValueStoreInterface'); - $storage->expects($this->once()) - ->method('get') - ->with($storage_key) - ->willReturn($payment); - - $this->keyValueFactory->expects($this->once()) - ->method('get') - ->with('payment.payment_type.payment_reference') - ->willReturn($storage); - - $this->controller->execute($storage_key); - } - - /** - * @covers ::access - * - * @dataProvider providerTestAccess - */ - public function testAccess($expected, $payment_exists) { - $storage_key = $this->randomMachineName(); - - $storage = $this->getMock('\Drupal\Core\KeyValueStore\KeyValueStoreInterface'); - $storage->expects($this->once()) - ->method('has') - ->with($storage_key) - ->willReturn($payment_exists); - - $this->keyValueFactory->expects($this->once()) - ->method('get') - ->with('payment.payment_type.payment_reference') - ->willReturn($storage); - - $this->assertSame($expected, $this->controller->access($storage_key)->isAllowed()); - } - - /** - * Provides data to testAccess(). - */ - public function providerTestAccess() { - return array( - array(TRUE, TRUE), - array(FALSE, FALSE), - ); - } - -} diff --git a/modules/payment_reference/tests/src/Unit/Element/PaymentReferenceBaseUnitTest.php b/modules/payment_reference/tests/src/Unit/Element/PaymentReferenceBaseUnitTest.php index 59f319b..2f54129 100644 --- a/modules/payment_reference/tests/src/Unit/Element/PaymentReferenceBaseUnitTest.php +++ b/modules/payment_reference/tests/src/Unit/Element/PaymentReferenceBaseUnitTest.php @@ -323,7 +323,7 @@ class PaymentReferenceBaseUnitTest extends UnitTestCase { * * @dataProvider providerTestPay */ - public function testPay($is_payment_execution_interruptive, $is_xml_http_request) { + public function testPay($has_payment_execution_completed, $is_xml_http_request) { $configuration = array(); $plugin_id = $this->randomMachineName(); $this->pluginDefinition = array(); @@ -361,11 +361,11 @@ class PaymentReferenceBaseUnitTest extends UnitTestCase { $request = $this->getMockBuilder('\Symfony\Component\HttpFoundation\Request') ->disableOriginalConstructor() ->getMock(); - $request->expects($is_payment_execution_interruptive ? $this->atLeastOnce() : $this->never()) + $request->expects($has_payment_execution_completed ? $this->never() : $this->atLeastOnce()) ->method('isXmlHttpRequest') ->willReturn($is_xml_http_request); - $this->requestStack->expects($is_payment_execution_interruptive ? $this->atLeastOnce() : $this->never()) + $this->requestStack->expects($has_payment_execution_completed ? $this->never() : $this->atLeastOnce()) ->method('getCurrentRequest') ->willReturn($request); diff --git a/modules/payment_reference/tests/src/Unit/Plugin/Payment/Type/PaymentReferenceUnitTest.php b/modules/payment_reference/tests/src/Unit/Plugin/Payment/Type/PaymentReferenceUnitTest.php index c9d99f6..4361bcf 100644 --- a/modules/payment_reference/tests/src/Unit/Plugin/Payment/Type/PaymentReferenceUnitTest.php +++ b/modules/payment_reference/tests/src/Unit/Plugin/Payment/Type/PaymentReferenceUnitTest.php @@ -232,103 +232,12 @@ class PaymentReferenceUnitTest extends UnitTestCase { } /** - * @covers ::doResumeContext + * @covers ::doGetResumeContextResponse */ - public function testDoResumeContextWithoutInterruption() { - $payment_method = $this->getMock('\Drupal\payment\Plugin\Payment\Method\PaymentMethodInterface'); + public function testDoGetResumeContextResponse() { + $response = $this->paymentType->getResumeContextResponse(); - $this->payment->expects($this->atLeastOnce()) - ->method('getPaymentMethod') - ->will($this->returnValue($payment_method)); - - // Nothing should happen when the payment method is not interruptive. - $this->paymentType->resumeContext(); - } - - /** - * @covers ::doResumeContext - */ - public function testDoResumeContextWithInterruption() { - $url = 'http://example.com'; - - $payment_method = $this->getMock('\Drupal\payment\Plugin\Payment\Method\PaymentMethodInterface'); - $payment_method->expects($this->atLeastOnce()) - ->method('isPaymentExecutionInterruptive') - ->will($this->returnValue(TRUE)); - - $this->payment->expects($this->atLeastOnce()) - ->method('getPaymentMethod') - ->will($this->returnValue($payment_method)); - - $kernel = $this->getMock('\Symfony\Component\HttpKernel\HttpKernelInterface'); - $request = $this->getMockBuilder('\Symfony\Component\HttpFoundation\Request') - ->disableOriginalConstructor() - ->getMock(); - $request_type = $this->randomMachineName(); - $response = $this->getMockBuilder('\Symfony\Component\HttpFoundation\Response') - ->disableOriginalConstructor() - ->getMock(); - $event = new FilterResponseEvent($kernel, $request, $request_type, $response); - - $this->eventDispatcher->expects($this->once()) - ->method('addListener') - ->with(KernelEvents::RESPONSE, new PaymentReferenceUnitTestDoResumeContextCallableConstraint($event, $url), 999); - - - $this->paymentType->resumeContext(); - } - -} - -/** - * Provides a constraint for the doResumeContext() callable. - */ -class PaymentReferenceUnitTestDoResumeContextCallableConstraint extends \PHPUnit_Framework_Constraint { - - /** - * The event to listen to. - * - * @var \Symfony\Component\HttpKernel\Event\FilterResponseEvent - */ - protected $event; - - /** - * The redirect URL. - * - * @var string - */ - protected $url; - - /** - * Constructs a new class instance. - * - * @param \Symfony\Component\HttpKernel\Event\FilterResponseEvent $event - * @param string $url - */ - public function __construct(FilterResponseEvent $event, $url) { - $this->event = $event; - $this->url = $url; - } - - /** - * {@inheritdoc} - */ - public function matches($other) { - if (is_callable($other)) { - $other($this->event); - $response = $this->event->getResponse(); - if ($response instanceof RedirectResponse) { - return $response->getTargetUrl() == $this->url; - } - } - return FALSE; - } - - /** - * {@inheritdoc} - */ - public function toString() { - return 'returns a RedirectResponse through a KernelEvents::RESPONSE event listener'; + $this->assertInstanceOf('\Drupal\payment\ResponseInterface', $response); } } diff --git a/modules/payment_test/src/Plugin/Payment/Method/PaymentTestInterruptive.php b/modules/payment_test/src/Plugin/Payment/Method/PaymentTestInterruptive.php deleted file mode 100644 index a6df2da..0000000 --- a/modules/payment_test/src/Plugin/Payment/Method/PaymentTestInterruptive.php +++ /dev/null @@ -1,109 +0,0 @@ -defaultConfiguration(); - parent::__construct($configuration, $plugin_id, $plugin_definition, $module_handler, $event_dispatcher, $token); - $this->paymentStatusManager = $payment_status_manager; - } - - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { - return new static($configuration, $plugin_id, $plugin_definition, $container->get('module_handler'), $container->get('event_dispatcher'), $container->get('token'), $container->get('plugin.manager.payment.status')); - } - - /** - * {@inheritdoc} - */ - protected function getSupportedCurrencies() { - return TRUE; - } - - /** - * {@inheritdoc} - */ - protected function doExecutePayment() { - $this->getPayment()->setPaymentStatus($this->paymentStatusManager->createInstance('payment_success')); - $this->getPayment()->save(); - } - - /** - * {@inheritdoc} - */ - protected function doCapturePayment() { - } - - /** - * {@inheritdoc} - */ - protected function doCapturePaymentAccess(AccountInterface $account) { - return FALSE; - } - - /** - * {@inheritdoc} - */ - protected function doRefundPayment() { - } - - /** - * {@inheritdoc} - */ - protected function doRefundPaymentAccess(AccountInterface $account) { - return FALSE; - } - -} diff --git a/modules/payment_test/src/Plugin/Payment/Method/PaymentTestNoResponse.php b/modules/payment_test/src/Plugin/Payment/Method/PaymentTestNoResponse.php new file mode 100644 index 0000000..582f68b --- /dev/null +++ b/modules/payment_test/src/Plugin/Payment/Method/PaymentTestNoResponse.php @@ -0,0 +1,65 @@ +getPayment()->setPaymentStatus($this->paymentStatusManager->createInstance('payment_success')); + $this->getPayment()->save(); + } + + /** + * {@inheritdoc} + */ + protected function doCapturePayment() { + } + + /** + * {@inheritdoc} + */ + protected function doCapturePaymentAccess(AccountInterface $account) { + return FALSE; + } + + /** + * {@inheritdoc} + */ + protected function doRefundPayment() { + } + + /** + * {@inheritdoc} + */ + protected function doRefundPaymentAccess(AccountInterface $account) { + return FALSE; + } + +} diff --git a/modules/payment_test/src/Plugin/Payment/Method/PaymentTestResponse.php b/modules/payment_test/src/Plugin/Payment/Method/PaymentTestResponse.php new file mode 100644 index 0000000..c8dc25a --- /dev/null +++ b/modules/payment_test/src/Plugin/Payment/Method/PaymentTestResponse.php @@ -0,0 +1,32 @@ +getPaymentMethod()->getPaymentExecutionResult(); - if ($result->getCompletionResponse()->getResponse() instanceof Response) { - return $result->getCompletionResponse(); - } - else { - return new RedirectResponse($result->getCompletionResponse()->getRedirectUrl()->toString()); - } + return $payment->getPaymentMethod()->getPaymentExecutionResult()->getCompletionResponse()->getResponse(); } } diff --git a/src/Entity/Payment.php b/src/Entity/Payment.php index 78a1fcb..867af70 100644 --- a/src/Entity/Payment.php +++ b/src/Entity/Payment.php @@ -55,7 +55,7 @@ use Drupal\user\UserInterface; * "update-status-form" = "payment.payment.update_status", * "capture-form" = "payment.payment.capture", * "refund-form" = "payment.payment.refund", - * "resume" = "payment.payment.resume" + * "complete" = "payment.payment.complete" * } * ) */ @@ -328,15 +328,8 @@ class Payment extends ContentEntityBase implements PaymentInterface { * {@inheritdoc} */ public function execute() { - $manager = PaymentServiceWrapper::statusManager(); - // Execute the payment. - if ($this->getPaymentMethod()->executePaymentAccess(\Drupal::currentUser())) { - $this->setPaymentStatus($manager->createInstance('payment_pending')); - $this->getPaymentMethod()->executePayment($this); - } - else { - $this->setPaymentStatus($manager->createInstance('payment_failed')); - $this->getPaymentType()->resumeContext(); + if ($this->getPaymentMethod()) { + return $this->getPaymentMethod()->executePayment(); } } diff --git a/src/Entity/Payment/PaymentAccessControlHandler.php b/src/Entity/Payment/PaymentAccessControlHandler.php index 254be79..53efd7a 100644 --- a/src/Entity/Payment/PaymentAccessControlHandler.php +++ b/src/Entity/Payment/PaymentAccessControlHandler.php @@ -52,8 +52,8 @@ class PaymentAccessControlHandler extends EntityAccessControlHandler { } elseif ($operation == 'complete') { return AccessResult::allowedIf($payment->getOwnerId() == $account->id()) - ->andIf($payment->getPaymentMethod()) - ->andIf($payment->getPaymentMethod()->getPaymentExecutionStatus() === TRUE); + ->andIf(AccessResult::allowedIf($payment->getPaymentMethod())) + ->andIf(AccessResult::allowedIf($payment->getPaymentMethod()->getPaymentExecutionResult()->hasCompleted())); } return $this->checkAccessPermission($payment, $operation, $account); } diff --git a/src/Entity/PaymentInterface.php b/src/Entity/PaymentInterface.php index 10fe99f..e60520a 100644 --- a/src/Entity/PaymentInterface.php +++ b/src/Entity/PaymentInterface.php @@ -9,7 +9,6 @@ namespace Drupal\payment\Entity; use Drupal\Core\Entity\ContentEntityInterface; use Drupal\Core\Entity\EntityChangedInterface; -use Drupal\Core\Executable\ExecutableInterface; use Drupal\payment\Plugin\Payment\LineItem\PaymentLineItemInterface; use Drupal\payment\Plugin\Payment\Method\PaymentMethodInterface as PluginPaymentMethodInterface; use Drupal\payment\Plugin\Payment\Status\PaymentStatusInterface as PluginPaymentStatusInterface; @@ -18,7 +17,14 @@ use Drupal\user\EntityOwnerInterface; /** * Defines a payment entity type . */ -interface PaymentInterface extends ContentEntityInterface, EntityChangedInterface, EntityOwnerInterface, ExecutableInterface { +interface PaymentInterface extends ContentEntityInterface, EntityChangedInterface, EntityOwnerInterface { + + /** + * Executes the payment. + * + * @return \Drupal\payment\PaymentExecutionResultInterface + */ + public function execute(); /** * Gets the payment's type plugin. diff --git a/src/PaymentExecutionResult.php b/src/PaymentExecutionResult.php new file mode 100644 index 0000000..4a458f0 --- /dev/null +++ b/src/PaymentExecutionResult.php @@ -0,0 +1,46 @@ +response = $response; + } + + /** + * {@inheritdoc} + */ + public function hasCompleted() { + return is_null($this->response); + } + + /** + * {@inheritdoc} + */ + public function getCompletionResponse() { + return $this->response; + } + +} diff --git a/src/PaymentExecutionResultInterface.php b/src/PaymentExecutionResultInterface.php index 737aa1a..e91725a 100644 --- a/src/PaymentExecutionResultInterface.php +++ b/src/PaymentExecutionResultInterface.php @@ -17,13 +17,13 @@ namespace Drupal\payment; interface PaymentExecutionResultInterface { /** - * Checks whether the execution process is still in progress. + * Checks whether the execution process has completed. * * @return bool - * Whether the execution process is still in progress. When TRUE is + * Whether the execution process is has completed. When FALSE is * returned, self::getCompletionResponse() MUST return a response. */ - public function isInProgress(); + public function hasCompleted(); /** * Gets the response to complete payment execution. diff --git a/src/Plugin/Payment/Method/Basic.php b/src/Plugin/Payment/Method/Basic.php index 2089256..59b5323 100644 --- a/src/Plugin/Payment/Method/Basic.php +++ b/src/Plugin/Payment/Method/Basic.php @@ -6,14 +6,9 @@ namespace Drupal\payment\Plugin\Payment\Method; -use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\Core\Session\AccountInterface; -use Drupal\Core\Utility\Token; use Drupal\payment\Entity\PaymentInterface; -use Drupal\payment\Plugin\Payment\Status\PaymentStatusManagerInterface; -use Symfony\Component\DependencyInjection\ContainerInterface; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * A basic payment method that does not transfer money. @@ -107,13 +102,6 @@ class Basic extends PaymentMethodBase implements ContainerFactoryPluginInterface /** * {@inheritdoc} */ - public function isPaymentExecutionInterruptive() { - return FALSE; - } - - /** - * {@inheritdoc} - */ protected function doExecutePayment() { $this->getPayment()->setPaymentStatus($this->paymentStatusManager->createInstance($this->getExecuteStatusId())); $this->getPayment()->save(); @@ -122,13 +110,6 @@ class Basic extends PaymentMethodBase implements ContainerFactoryPluginInterface /** * {@inheritdoc} */ - public function getPaymentExecutionStatus() { - return $this->getPayment()->getPaymentStatus()->getPluginId() == $this->getExecuteStatusId(); - } - - /** - * {@inheritdoc} - */ public function doCapturePayment() { $this->getPayment()->setPaymentStatus($this->paymentStatusManager->createInstance($this->getCaptureStatusId())); $this->getPayment()->save(); diff --git a/src/Plugin/Payment/Method/PaymentMethodBase.php b/src/Plugin/Payment/Method/PaymentMethodBase.php index 714b3e9..9e2eb6e 100644 --- a/src/Plugin/Payment/Method/PaymentMethodBase.php +++ b/src/Plugin/Payment/Method/PaymentMethodBase.php @@ -18,6 +18,7 @@ use Drupal\payment\Event\PaymentExecuteAccess; use Drupal\payment\Event\PaymentPreCapture; use Drupal\payment\Event\PaymentPreExecute; use Drupal\payment\Event\PaymentPreRefund; +use Drupal\payment\PaymentExecutionResult; use Drupal\payment\Plugin\Payment\Status\PaymentStatusManagerInterface; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; @@ -205,14 +206,6 @@ abstract class PaymentMethodBase extends PluginBase implements ContainerFactoryP /** * {@inheritdoc} */ - public function isPaymentExecutionInterruptive() { - // To be on the safe side, we assume any payment method is interruptive. - return TRUE; - } - - /** - * {@inheritdoc} - */ public function executePaymentAccess(AccountInterface $account) { if (!$this->getPayment()) { throw new \LogicException('Trying to check access for a non-existing payment. A payment must be set trough self::setPayment() first.'); @@ -245,6 +238,7 @@ abstract class PaymentMethodBase extends PluginBase implements ContainerFactoryP $event = new PaymentPreExecute($this->getPayment()); $this->eventDispatcher->dispatch(PaymentEvents::PAYMENT_PRE_EXECUTE, $event); // @todo invoke Rules event. + $this->payment->setPaymentStatus($this->paymentStatusManager->createInstance('payment_pending')); $this->doExecutePayment(); return $this->getPaymentExecutionResult(); @@ -258,6 +252,13 @@ abstract class PaymentMethodBase extends PluginBase implements ContainerFactoryP /** * {@inheritdoc} */ + public function getPaymentExecutionResult() { + return new PaymentExecutionResult(); + } + + /** + * {@inheritdoc} + */ public function capturePaymentAccess(AccountInterface $account) { if (!$this->getPayment()) { throw new \LogicException('Trying to check access for a non-existing payment. A payment must be set trough self::setPayment() first.'); diff --git a/src/Plugin/Payment/Method/Unavailable.php b/src/Plugin/Payment/Method/Unavailable.php index 52b000d..2b5fa09 100644 --- a/src/Plugin/Payment/Method/Unavailable.php +++ b/src/Plugin/Payment/Method/Unavailable.php @@ -10,6 +10,7 @@ use Drupal\Component\Plugin\PluginBase; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Session\AccountInterface; use Drupal\payment\Entity\PaymentInterface; +use Drupal\payment\PaymentExecutionResult; /** * A payment method controller that essentially disables payment methods. @@ -72,8 +73,8 @@ class Unavailable extends PluginBase implements PaymentMethodInterface { /** * {@inheritdoc} */ - public function isPaymentExecutionInterruptive() { - return FALSE; + public function getPaymentExecutionResult() { + return new PaymentExecutionResult(); } /** diff --git a/src/Plugin/Payment/Type/PaymentTypeBase.php b/src/Plugin/Payment/Type/PaymentTypeBase.php index e18c275..f7cb9f3 100644 --- a/src/Plugin/Payment/Type/PaymentTypeBase.php +++ b/src/Plugin/Payment/Type/PaymentTypeBase.php @@ -107,16 +107,18 @@ abstract class PaymentTypeBase extends PluginBase implements ContainerFactoryPlu /** * {@inheritdoc} */ - function resumeContext() { + function getResumeContextResponse() { $event = new PaymentTypePreResumeContext($this->getPayment()); $this->eventDispatcher->dispatch(PaymentEvents::PAYMENT_TYPE_PRE_RESUME_CONTEXT, $event); // @todo Invoke Rules event. - $this->doResumeContext(); + return $this->doGetResumeContextResponse(); } /** * Performs the actual context resumption. + * + * @return \Drupal\payment\ResponseInterface */ - abstract protected function doResumeContext(); + abstract protected function doGetResumeContextResponse(); } diff --git a/src/Plugin/Payment/Type/PaymentTypeInterface.php b/src/Plugin/Payment/Type/PaymentTypeInterface.php index fca3d1a..bf7b626 100644 --- a/src/Plugin/Payment/Type/PaymentTypeInterface.php +++ b/src/Plugin/Payment/Type/PaymentTypeInterface.php @@ -49,22 +49,17 @@ interface PaymentTypeInterface extends PluginInspectionInterface, ConfigurablePl * * @return bool * - * @see self::resumeContext + * @see self::getResumeContextResponse */ public function resumeContextAccess(AccountInterface $account); /** * Resumes the payer's original workflow. * - * The returned response MUST contain all necessary information to return the - * payer to where they were when they started payment execution. Because this - * method does not always have to be called, it MUST NOT perform any other - * critical operations. - * * @return \Drupal\payment\ResponseInterface * * @see self::resumeContextAccess */ - public function resumeContext(); + public function getResumeContextResponse(); } diff --git a/src/Plugin/Payment/Type/Unavailable.php b/src/Plugin/Payment/Type/Unavailable.php index 513b943..13620af 100644 --- a/src/Plugin/Payment/Type/Unavailable.php +++ b/src/Plugin/Payment/Type/Unavailable.php @@ -58,7 +58,7 @@ class Unavailable extends PaymentTypeBase { /** * {@inheritdoc} */ - protected function doResumeContext() { + protected function doGetResumeContextResponse() { throw new NotFoundHttpException(); } diff --git a/src/Response.php b/src/Response.php index 1503f99..6d96950 100644 --- a/src/Response.php +++ b/src/Response.php @@ -8,6 +8,7 @@ namespace Drupal\payment; use Drupal\Core\Url; +use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Response as SymfonyResponse; /** @@ -20,7 +21,7 @@ class Response implements ResponseInterface { * * @var \Drupal\Core\Url */ - protected $redirectUrl; + protected $url; /** * The response. @@ -30,28 +31,28 @@ class Response implements ResponseInterface { protected $response; /** - * Constructs a new instance. + * Creates a new instance. * - * @param \Drupal\Core\Url $redirect_url + * @param \Drupal\Core\Url $url * @param \Symfony\Component\HttpFoundation\Response|null $response */ - public function __construct(Url $redirect_url, SymfonyResponse $response = NULL) { - $this->redirectUrl = $redirect_url; + public function __construct(Url $url, SymfonyResponse $response = NULL) { $this->response = $response; + $this->url = $url; } /** * {@inheritdoc} */ public function getRedirectUrl() { - return $this->redirectUrl; + return $this->url; } /** * {@inheritdoc} */ public function getResponse() { - return $this->response; + return $this->response ? $this->response : new RedirectResponse($this->getRedirectUrl()->toString()); } } diff --git a/src/ResponseInterface.php b/src/ResponseInterface.php index 27aab41..c9e9497 100644 --- a/src/ResponseInterface.php +++ b/src/ResponseInterface.php @@ -26,7 +26,7 @@ interface ResponseInterface { /** * Gets the response * - * @return \Symfony\Component\HttpFoundation\Response|null + * @return \Symfony\Component\HttpFoundation\Response */ public function getResponse(); diff --git a/tests/src/Unit/PaymentExecutionResultUnitTest.php b/tests/src/Unit/PaymentExecutionResultUnitTest.php new file mode 100644 index 0000000..1360986 --- /dev/null +++ b/tests/src/Unit/PaymentExecutionResultUnitTest.php @@ -0,0 +1,66 @@ +response = $this->getMock('\Drupal\payment\ResponseInterface'); + } + + /** + * @covers ::hasCompleted + * @covers ::__construct + */ + function testHasCompleted() { + $this->paymentExecutionResult = new PaymentExecutionResult(); + $this->assertFalse($this->paymentExecutionResult->hasCompleted()); + + $this->paymentExecutionResult = new PaymentExecutionResult($this->response); + $this->assertTrue($this->paymentExecutionResult->hasCompleted()); + } + + /** + * @covers ::getCompletionResponse + * @covers ::__construct + */ + function testGetCompletionResponse() { + $this->paymentExecutionResult = new PaymentExecutionResult(); + $this->assertNULL($this->paymentExecutionResult->getCompletionResponse()); + + $this->paymentExecutionResult = new PaymentExecutionResult($this->response); + $this->assertSame($this->response, $this->paymentExecutionResult->getCompletionResponse()); + } + +} diff --git a/tests/src/Unit/Plugin/Payment/Method/BasicUnitTest.php b/tests/src/Unit/Plugin/Payment/Method/BasicUnitTest.php index 69ded14..c5c1bcb 100644 --- a/tests/src/Unit/Plugin/Payment/Method/BasicUnitTest.php +++ b/tests/src/Unit/Plugin/Payment/Method/BasicUnitTest.php @@ -124,13 +124,6 @@ class BasicUnitTest extends PaymentMethodBaseUnitTestBase { } /** - * @covers ::isPaymentExecutionInterruptive - */ - public function testIsPaymentExecutionInterruptive() { - $this->assertInternalType('bool', $this->paymentMethod->isPaymentExecutionInterruptive()); - } - - /** * @covers ::doExecutePayment */ public function testDoExecutePayment() { diff --git a/tests/src/Unit/Plugin/Payment/Method/PaymentMethodBaseUnitTest.php b/tests/src/Unit/Plugin/Payment/Method/PaymentMethodBaseUnitTest.php index b0d34b9..e413504 100644 --- a/tests/src/Unit/Plugin/Payment/Method/PaymentMethodBaseUnitTest.php +++ b/tests/src/Unit/Plugin/Payment/Method/PaymentMethodBaseUnitTest.php @@ -193,13 +193,6 @@ class PaymentMethodBaseUnitTest extends PaymentMethodBaseUnitTestBase { } /** - * @covers ::isPaymentExecutionInterruptive - */ - public function testIsPaymentExecutionInterruptive() { - $this->assertInternalType('bool', $this->plugin->isPaymentExecutionInterruptive()); - } - - /** * @covers ::executePayment * * @expectedException \Exception diff --git a/tests/src/Unit/Plugin/Payment/Method/UnavailableUnitTest.php b/tests/src/Unit/Plugin/Payment/Method/UnavailableUnitTest.php index cacff46..277ff73 100644 --- a/tests/src/Unit/Plugin/Payment/Method/UnavailableUnitTest.php +++ b/tests/src/Unit/Plugin/Payment/Method/UnavailableUnitTest.php @@ -157,13 +157,6 @@ class UnavailableUnitTest extends UnitTestCase { } /** - * @covers ::isPaymentExecutionInterruptive - */ - public function testIsPaymentExecutionInterruptive() { - $this->assertInternalType('bool', $this->plugin->isPaymentExecutionInterruptive()); - } - - /** * @covers ::executePayment * @expectedException \RuntimeException */ diff --git a/tests/src/Unit/Plugin/Payment/Type/PaymentTypeBaseUnitTest.php b/tests/src/Unit/Plugin/Payment/Type/PaymentTypeBaseUnitTest.php index fd88b46..23482f1 100644 --- a/tests/src/Unit/Plugin/Payment/Type/PaymentTypeBaseUnitTest.php +++ b/tests/src/Unit/Plugin/Payment/Type/PaymentTypeBaseUnitTest.php @@ -106,20 +106,26 @@ class PaymentTypeBaseUnitTest extends UnitTestCase { } /** - * @covers ::resumeContext + * @covers ::getResumeContextResponse * * @depends testGetPayment */ - public function testResumeContext() { + public function testGetResumeContextResponse() { + $response = $this->getMock('\Drupal\payment\ResponseInterface'); + $payment = $this->getMockBuilder('\Drupal\payment\Entity\Payment') ->disableOriginalConstructor() ->getMock(); $this->paymentType->setPayment($payment); + $this->paymentType->expects($this->atLeastOnce()) + ->method('doGetResumeContextResponse') + ->willReturn($response); + $this->eventDispatcher->expects($this->once()) ->method('dispatch') ->with(PaymentEvents::PAYMENT_TYPE_PRE_RESUME_CONTEXT); - $this->paymentType->resumeContext(); + $this->assertSame($response, $this->paymentType->getResumeContextResponse()); } } diff --git a/tests/src/Unit/Plugin/Payment/Type/UnavailableUnitTest.php b/tests/src/Unit/Plugin/Payment/Type/UnavailableUnitTest.php index c2832c3..8a36301 100644 --- a/tests/src/Unit/Plugin/Payment/Type/UnavailableUnitTest.php +++ b/tests/src/Unit/Plugin/Payment/Type/UnavailableUnitTest.php @@ -91,13 +91,13 @@ class UnavailableUnitTest extends UnitTestCase { * @covers ::doResumeContext * @expectedException \Symfony\Component\HttpKernel\Exception\NotFoundHttpException */ - public function testDoResumeContext() { + public function testDoGetResumeContextResponse() { $payment = $this->getMockBuilder('\Drupal\payment\Entity\Payment') ->disableOriginalConstructor() ->getMock(); $this->paymentType->setPayment($payment); - $this->paymentType->resumeContext(); + $this->paymentType->getResumeContextResponse(); } /** diff --git a/tests/src/Unit/ResponseUnitTest.php b/tests/src/Unit/ResponseUnitTest.php index d6f0196..9496576 100644 --- a/tests/src/Unit/ResponseUnitTest.php +++ b/tests/src/Unit/ResponseUnitTest.php @@ -10,7 +10,6 @@ namespace Drupal\Tests\payment\Unit; use Drupal\Core\Url; use Drupal\payment\Response; use Drupal\Tests\UnitTestCase; -use Symfony\Component\DependencyInjection\ContainerInterface; /** * @coversDefaultClass \Drupal\payment\Response @@ -69,4 +68,14 @@ class ResponseUnitTest extends UnitTestCase { $this->assertSame($this->symfonyResponse, $this->response->getResponse()); } + /** + * @covers ::getResponse + * @covers ::__construct + */ + function testGetResponseWithAutomaticallyCreatedResponse() { + $this->response = new Response($this->redirectUrl); + + $this->assertInstanceOf('\Symfony\Component\HttpFoundation\RedirectResponse', $this->response->getResponse()); + } + }