diff --git a/modules/payment_form/src/Entity/Payment/PaymentForm.php b/modules/payment_form/src/Entity/Payment/PaymentForm.php
index 24fdbd7..48c9e48 100644
--- a/modules/payment_form/src/Entity/Payment/PaymentForm.php
+++ b/modules/payment_form/src/Entity/Payment/PaymentForm.php
@@ -12,6 +12,7 @@ use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\StringTranslation\TranslationInterface;
 use Drupal\payment\Plugin\Payment\MethodSelector\PaymentMethodSelectorManagerInterface;
+use Drupal\payment\ResponseInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -101,7 +102,10 @@ class PaymentForm extends ContentEntityForm {
     $payment_method_selector->submitConfigurationForm($form['payment_method'], $form_state);
     $payment->setPaymentMethod($payment_method_selector->getPaymentMethod());
     $payment->save();
-    $payment->execute();
+    $result = $payment->execute();
+    if (!$result->hasCompleted()) {
+      $form_state->setRedirectUrl($result->getCompletionResponse()->getRedirectUrl());
+    }
   }
 
   /**
diff --git a/modules/payment_form/src/Plugin/Payment/Type/PaymentForm.php b/modules/payment_form/src/Plugin/Payment/Type/PaymentForm.php
index 1031cb8..9e3a47b 100644
--- a/modules/payment_form/src/Plugin/Payment/Type/PaymentForm.php
+++ b/modules/payment_form/src/Plugin/Payment/Type/PaymentForm.php
@@ -10,12 +10,12 @@ 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 Drupal\payment\Response;
 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 +93,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 Response(Url::fromUri($this->getDestinationUrl()));
   }
 
   /**
diff --git a/modules/payment_form/tests/src/Unit/Entity/Payment/PaymentFormUnitTest.php b/modules/payment_form/tests/src/Unit/Entity/Payment/PaymentFormUnitTest.php
index 5392b1e..70f142a 100644
--- a/modules/payment_form/tests/src/Unit/Entity/Payment/PaymentFormUnitTest.php
+++ b/modules/payment_form/tests/src/Unit/Entity/Payment/PaymentFormUnitTest.php
@@ -8,6 +8,8 @@
 namespace Drupal\Tests\payment_form\Unit\Entity\Payment;
 
 use Drupal\Core\Form\FormState;
+use Drupal\Core\Url;
+use Drupal\payment\Response;
 use Drupal\payment_form\Entity\Payment\PaymentForm;
 use Drupal\Tests\UnitTestCase;
 use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -209,6 +211,14 @@ class PaymentFormUnitTest extends UnitTestCase {
    * @covers ::submitForm
    */
   public function testSubmitForm() {
+    $redirect_url = new Url($this->randomMachineName());
+    $response = new Response($redirect_url);
+
+    $result = $this->getMock('\Drupal\payment\PaymentExecutionResultInterface');
+    $result->expects($this->atLeastOnce())
+      ->method('getCompletionResponse')
+      ->willReturn($response);
+
     $form = array(
       'payment_method' => array(
         '#type' => $this->randomMachineName(),
@@ -219,23 +229,27 @@ class PaymentFormUnitTest extends UnitTestCase {
       ->method('get')
       ->with('payment_method_selector')
       ->willReturn($this->paymentMethodSelector);
+    $form_state->expects($this->atLeastOnce())
+      ->method('setRedirectUrl')
+      ->with($redirect_url);
 
     $payment_method = $this->getMock('\Drupal\payment\Plugin\Payment\Method\PaymentMethodInterface');
 
-    $this->paymentMethodSelector->expects($this->once())
+    $this->paymentMethodSelector->expects($this->atLeastOnce())
       ->method('getPaymentMethod')
       ->will($this->returnValue($payment_method));
-    $this->paymentMethodSelector->expects($this->once())
+    $this->paymentMethodSelector->expects($this->atLeastOnce())
       ->method('submitConfigurationForm')
       ->with($form['payment_method'], $form_state);
 
-    $this->payment->expects($this->once())
+    $this->payment->expects($this->atLeastOnce())
       ->method('setPaymentMethod')
       ->with($payment_method);
-    $this->payment->expects($this->once())
+    $this->payment->expects($this->atLeastOnce())
       ->method('save');
-    $this->payment->expects($this->once())
-      ->method('execute');
+    $this->payment->expects($this->atLeastOnce())
+      ->method('execute')
+      ->willReturn($result);
 
     $this->form->submitForm($form, $form_state);
   }
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..070fe24 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
@@ -8,12 +8,10 @@
 
 namespace Drupal\Tests\payment_form\Unit\Plugin\Payment\Type;
 
+use Drupal\Core\DependencyInjection\ContainerBuilder;
 use Drupal\payment_form\Plugin\Payment\Type\PaymentForm;
 use Drupal\Tests\UnitTestCase;
 use Symfony\Component\DependencyInjection\ContainerInterface;
-use Symfony\Component\HttpFoundation\RedirectResponse;
-use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
-use Symfony\Component\HttpKernel\KernelEvents;
 
 /**
  * @coversDefaultClass \Drupal\payment_form\Plugin\Payment\Type\PaymentForm
@@ -199,30 +197,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()->getUri());
   }
 
   /**
@@ -236,56 +222,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 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\payment_reference\Controller\Pay.
- */
-
-namespace Drupal\payment_reference\Controller;
-
-use Drupal\Core\Access\AccessResult;
-use Drupal\Core\Controller\ControllerBase;
-use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
-use Drupal\Core\KeyValueStore\KeyValueFactoryInterface;
-use Symfony\Component\DependencyInjection\ContainerInterface;
-use Symfony\Component\HttpFoundation\Request;
-
-/**
- * Handles the "pay" route.
- */
-class Pay extends ControllerBase implements ContainerInjectionInterface {
-
-  /**
-   * The key/value factory.
-   *
-   * @var \Drupal\Core\KeyValueStore\KeyValueFactoryInterface
-   */
-  protected $keyValueFactory;
-
-  /**
-   * Constructs a new instance.
-   *
-   * @param \Drupal\Core\KeyValueStore\KeyValueFactoryInterface $key_value_factory
-   */
-  public function __construct(KeyValueFactoryInterface $key_value_factory) {
-    $this->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/PaymentReference.php b/modules/payment_reference/src/Element/PaymentReference.php
index d5b168a..fabe586 100644
--- a/modules/payment_reference/src/Element/PaymentReference.php
+++ b/modules/payment_reference/src/Element/PaymentReference.php
@@ -33,13 +33,6 @@ class PaymentReference extends PaymentReferenceBase {
   protected $paymentQueue;
 
   /**
-   * The temporary payment storage.
-   *
-   * @var \Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface
-   */
-  protected $temporaryPaymentStorage;
-
-  /**
    * Creates a new instance.
    *
    * @param array $configuration
@@ -55,13 +48,11 @@ class PaymentReference extends PaymentReferenceBase {
    * @param \Drupal\Core\Utility\LinkGeneratorInterface $link_generator
    * @param \Drupal\payment\Plugin\Payment\MethodSelector\PaymentMethodSelectorManagerInterface $payment_method_selector_manager
    * @param \Drupal\Component\Utility\Random $random
-   * @param \Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface $temporary_payment_storage
    * @param \Drupal\payment\QueueInterface $payment_queue
    */
-  public function __construct($configuration, $plugin_id, $plugin_definition, RequestStack $request_stack, EntityStorageInterface $payment_storage, TranslationInterface $string_translation, DateFormatter $date_formatter, LinkGeneratorInterface $link_generator, PaymentMethodSelectorManagerInterface $payment_method_selector_manager, Random $random, KeyValueStoreExpirableInterface $temporary_payment_storage, QueueInterface $payment_queue) {
+  public function __construct($configuration, $plugin_id, $plugin_definition, RequestStack $request_stack, EntityStorageInterface $payment_storage, TranslationInterface $string_translation, DateFormatter $date_formatter, LinkGeneratorInterface $link_generator, PaymentMethodSelectorManagerInterface $payment_method_selector_manager, Random $random, QueueInterface $payment_queue) {
     parent::__construct($configuration, $plugin_id, $plugin_definition, $request_stack, $payment_storage, $string_translation, $date_formatter, $link_generator, $payment_method_selector_manager, $random);
     $this->paymentQueue = $payment_queue;
-    $this->temporaryPaymentStorage = $temporary_payment_storage;
   }
 
   /**
@@ -71,10 +62,7 @@ class PaymentReference extends PaymentReferenceBase {
     /** @var \Drupal\Core\Entity\EntityManagerInterface $entity_manager */
     $entity_manager = $container->get('entity.manager');
 
-    /** @var \Drupal\Core\KeyValueStore\KeyValueExpirableFactoryInterface $key_value_expirable */
-    $key_value_expirable = $container->get('keyvalue.expirable');
-
-    return new static($configuration, $plugin_id, $plugin_definition, $container->get('request_stack'), $entity_manager->getStorage('payment'), $container->get('string_translation'), $container->get('date.formatter'), $container->get('link_generator'), $container->get('plugin.manager.payment.method_selector'), new Random(), $key_value_expirable->get('payment.payment_type.payment_reference'), $container->get('payment_reference.queue'));
+    return new static($configuration, $plugin_id, $plugin_definition, $container->get('request_stack'), $entity_manager->getStorage('payment'), $container->get('string_translation'), $container->get('date.formatter'), $container->get('link_generator'), $container->get('plugin.manager.payment.method_selector'), new Random(), $container->get('payment_reference.queue'));
   }
 
   /**
@@ -84,11 +72,4 @@ class PaymentReference extends PaymentReferenceBase {
     return $this->paymentQueue;
   }
 
-  /**
-   * {@inheritdoc}
-   */
-  protected function getTemporaryPaymentStorage() {
-    return $this->temporaryPaymentStorage;
-  }
-
 }
diff --git a/modules/payment_reference/src/Element/PaymentReferenceBase.php b/modules/payment_reference/src/Element/PaymentReferenceBase.php
index cf2ddb5..a3a9fc1 100644
--- a/modules/payment_reference/src/Element/PaymentReferenceBase.php
+++ b/modules/payment_reference/src/Element/PaymentReferenceBase.php
@@ -245,12 +245,12 @@ abstract class PaymentReferenceBase extends FormElement implements FormElementIn
       '#type' => 'payment_line_items_display',
     );
     $build['payment_method'] = $payment_method_selector->buildConfigurationForm(array(), $form_state);
-    if ($this->hasTemporaryPayment($element, $form_state)) {
+    if (!$payment_method_selector->getPaymentMethod() || !$payment_method_selector->getPaymentMethod()->getPaymentExecutionResult()->hasCompleted()) {
       $this->disableChildren($build['payment_method']);
     }
     $this->getEntityFormDisplay($element, $form_state)->buildForm($payment_method_selector->getPayment(), $build, $form_state);
     $build['pay_link'] = $this->buildCompletePaymentLink($element, $form_state);
-    $build['pay_link']['#access'] = $this->hasTemporaryPayment($element, $form_state);
+    $build['pay_link']['#access'] = !$payment_method_selector->getPaymentMethod() || !$payment_method_selector->getPaymentMethod()->getPaymentExecutionResult()->hasCompleted();
     $build['pay_link']['#process'] = array(array(get_class($this), 'processMaxWeight'));
     $plugin_id = $this->getPluginId();
     $build['pay_button'] = array(
@@ -305,8 +305,11 @@ abstract class PaymentReferenceBase extends FormElement implements FormElementIn
    *   A render array.
    */
   protected function buildRefreshButton(array $element, FormStateInterface $form_state) {
+    $payment_method_selector = $this->getPaymentMethodSelector($element, $form_state);
     $class = array('payment_reference-refresh-button');
-    if (!$element['#default_value'] && $element['#available_payment_id'] && !$this->hasTemporaryPayment($element, $form_state)) {
+    if (!$element['#default_value']
+      && $element['#available_payment_id']
+      && (!$payment_method_selector->getPaymentMethod() || !$payment_method_selector->getPaymentMethod()->getPaymentExecutionResult()->hasCompleted())) {
       $class[] = 'payment-reference-hidden';
     }
     $plugin_id = $this->getPluginId();
@@ -417,9 +420,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,22 +457,15 @@ 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);
-      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',
-          ),
-        )));
-        drupal_set_message($link);
-      }
-    }
-    else {
-      $payment->save();
-      $payment->execute();
+
+    $result = $payment->execute();
+    if (!$result->hasCompleted() && !$this->requestStack->getCurrentRequest()->isXmlHttpRequest()) {
+      $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);
     }
     $form_state->setRebuild();
   }
@@ -487,7 +481,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 +557,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 +564,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..6d23d26 100644
--- a/modules/payment_reference/src/Plugin/Payment/Type/PaymentReference.php
+++ b/modules/payment_reference/src/Plugin/Payment/Type/PaymentReference.php
@@ -11,12 +11,12 @@ 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 Drupal\payment\Response;
 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 +98,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 Response(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 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\Tests\payment_reference\Unit\Controller\PayUnitTest.
- */
-
-namespace Drupal\Tests\payment_reference\Unit\Controller;
-
-use Drupal\payment_reference\Controller\Pay;
-use Drupal\Tests\UnitTestCase;
-use Symfony\Component\DependencyInjection\ContainerInterface;
-
-/**
- * @coversDefaultClass \Drupal\payment_reference\Controller\Pay
- *
- * @group Payment Reference Field
- */
-class PayUnitTest extends UnitTestCase {
-
-  /**
-   * The controller under test.
-   *
-   * @var \Drupal\payment_reference\Controller\Pay
-   */
-  protected $controller;
-
-  /**
-   * The key/value factory.
-   *
-   * @var \Drupal\Core\KeyValueStore\KeyValueFactoryInterface|\PHPUnit_Framework_MockObject_MockObject
-   */
-  protected $keyValueFactory;
-
-  /**
-   * {@inheritdoc}
-   *
-   * @covers ::__construct
-   */
-  protected function setUp() {
-    $this->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..02d7330 100644
--- a/modules/payment_reference/tests/src/Unit/Element/PaymentReferenceBaseUnitTest.php
+++ b/modules/payment_reference/tests/src/Unit/Element/PaymentReferenceBaseUnitTest.php
@@ -9,7 +9,8 @@ namespace Drupal\Tests\payment_reference\Unit\Element {
 
 use Drupal\Component\Utility\Random;
 use Drupal\Core\Form\FormState;
-use Drupal\payment_reference\Element\PaymentReferenceBase;
+  use Drupal\Core\Url;
+  use Drupal\payment_reference\Element\PaymentReferenceBase;
 use Drupal\Tests\UnitTestCase;
 
 /**
@@ -83,13 +84,6 @@ class PaymentReferenceBaseUnitTest extends UnitTestCase {
   protected $stringTranslation;
 
   /**
-   * The temporary payment storage.
-   *
-   * @var \Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface|\PHPUnit_Framework_MockObject_MockObject
-   */
-  protected $temporaryPaymentStorage;
-
-  /**
    * The url generator.
    *
    * @var \Drupal\Core\Routing\UrlGeneratorInterface|\PHPUnit_Framework_MockObject_MockObject
@@ -120,8 +114,6 @@ class PaymentReferenceBaseUnitTest extends UnitTestCase {
 
     $this->stringTranslation = $this->getStringTranslationStub();
 
-    $this->temporaryPaymentStorage = $this->getMock('\Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface');
-
     $this->urlGenerator = $this->getMock('\Drupal\Core\Routing\UrlGeneratorInterface');
 
     $configuration = array();
@@ -135,9 +127,6 @@ class PaymentReferenceBaseUnitTest extends UnitTestCase {
     $this->element->expects($this->any())
       ->method('getPaymentQueue')
       ->willReturn($this->paymentQueue);
-    $this->element->expects($this->any())
-      ->method('getTemporaryPaymentStorage')
-      ->willReturn($this->temporaryPaymentStorage);
   }
 
   /**
@@ -150,111 +139,6 @@ class PaymentReferenceBaseUnitTest extends UnitTestCase {
   }
 
   /**
-   * @covers ::getTemporaryPaymentStorageKey
-   */
-  public function testGetTemporaryPaymentStorageKey() {
-    $method = new \ReflectionMethod($this->element, 'getTemporaryPaymentStorageKey');
-    $method->setAccessible(TRUE);
-
-    $element = array(
-      '#name' => $this->randomMachineName(),
-    );
-    $form_state = new FormState();
-
-    $key = $method->invoke($this->element, $element, $form_state);
-    $this->assertSame($key, $method->invoke($this->element, $element, $form_state));
-  }
-
-  /**
-   * @covers ::hasTemporaryPayment
-   *
-   * @depends testGetTemporaryPaymentStorageKey
-   */
-  public function testHasTemporaryPayment() {
-    $has_method = new \ReflectionMethod($this->element, 'hasTemporaryPayment');
-    $has_method->setAccessible(TRUE);
-    $get_key_method = new \ReflectionMethod($this->element, 'getTemporaryPaymentStorageKey');
-    $get_key_method->setAccessible(TRUE);
-
-    $element = array(
-      '#name' => $this->randomMachineName(),
-    );
-    $form_state = new FormState();
-
-    $key = $get_key_method->invoke($this->element, $element, $form_state);
-
-    // Use a random string instead of a boolean so we can assert the value.
-    $has = $this->randomMachineName();
-
-    $this->temporaryPaymentStorage->expects($this->once())
-      ->method('has')
-      ->with($key)
-      ->willReturn($has);
-
-    $this->assertSame($has, $has_method->invoke($this->element, $element, $form_state));
-  }
-
-  /**
-   * @covers ::getTemporaryPayment
-   *
-   * @depends testGetTemporaryPaymentStorageKey
-   */
-  public function testGetTemporaryPayment() {
-    $has_method = new \ReflectionMethod($this->element, 'getTemporaryPayment');
-    $has_method->setAccessible(TRUE);
-    $get_key_method = new \ReflectionMethod($this->element, 'getTemporaryPaymentStorageKey');
-    $get_key_method->setAccessible(TRUE);
-
-    $element = array(
-      '#name' => $this->randomMachineName(),
-    );
-    $form_state = new FormState();
-
-    $key = $get_key_method->invoke($this->element, $element, $form_state);
-
-    $payment = $this->getMockBuilder('\Drupal\payment\Entity\Payment')
-      ->disableOriginalConstructor()
-      ->getMock();
-
-    $this->temporaryPaymentStorage->expects($this->once())
-      ->method('get')
-      ->with($key)
-      ->willReturn($payment);
-
-    $this->assertSame($payment, $has_method->invoke($this->element, $element, $form_state));
-  }
-
-  /**
-   * @covers ::SetTemporaryPayment
-   *
-   * @depends testGetTemporaryPaymentStorageKey
-   */
-  public function testSetTemporaryPayment() {
-    $set_method = new \ReflectionMethod($this->element, 'setTemporaryPayment');
-    $set_method->setAccessible(TRUE);
-    $get_method = new \ReflectionMethod($this->element, 'getTemporaryPaymentStorageKey');
-    $get_method->setAccessible(TRUE);
-
-    $element = array(
-      '#name' => $this->randomMachineName(),
-    );
-    $form_state = new FormState();
-
-    $key = $get_method->invoke($this->element, $element, $form_state);
-
-    $payment = $this->getMockBuilder('\Drupal\payment\Entity\Payment')
-      ->disableOriginalConstructor()
-      ->getMock();
-
-    $this->temporaryPaymentStorage->expects($this->once())
-      ->method('setWithExpire')
-      ->with($key, $payment, PaymentReferenceBase::KEY_VALUE_TTL)
-      ->willReturn($payment);
-
-    $set_method->invoke($this->element, $element, $form_state, $payment);
-  }
-
-  /**
    * @covers ::valueCallback
    */
   public function testValueCallbackWithDefaultValue() {
@@ -323,14 +207,14 @@ class PaymentReferenceBaseUnitTest extends UnitTestCase {
    *
    * @dataProvider providerTestPay
    */
-  public function testPay($is_payment_execution_interruptive, $is_xml_http_request) {
+  public function testPay($has_completed, $is_xml_http_request) {
     $configuration = array();
     $plugin_id = $this->randomMachineName();
     $this->pluginDefinition = array();
 
     $this->element = $this->getMockBuilder('\Drupal\payment_reference\Element\PaymentReferenceBase')
       ->setConstructorArgs(array($configuration, $plugin_id, $this->pluginDefinition, $this->requestStack, $this->paymentStorage, $this->stringTranslation, $this->dateFormatter, $this->linkGenerator, $this->paymentMethodSelectorManager, new Random()))
-      ->setMethods(array('getEntityFormDisplay', 'getPaymentMethodSelector', 'getTemporaryPaymentStorageKey', 'setTemporaryPayment'))
+      ->setMethods(array('getEntityFormDisplay', 'getPaymentMethodSelector'))
       ->getMockForAbstractClass();
 
     $form = array(
@@ -361,29 +245,36 @@ 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_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_completed ? $this->never() : $this->atLeastOnce())
       ->method('getCurrentRequest')
       ->willReturn($request);
 
+    $result = $this->getMock('\Drupal\payment\PaymentExecutionResultInterface');
+    $result->expects($this->atLeastOnce())
+      ->method('hasCompleted')
+      ->willReturn($has_completed);
+
     $payment_method = $this->getMock('\Drupal\payment\Plugin\Payment\Method\PaymentMethodInterface');
-    $payment_method->expects($this->atLeastOnce())
-      ->method('isPaymentExecutionInterruptive')
-      ->willReturn($is_payment_execution_interruptive);
+
+    $url = new Url($this->randomMachineName());
 
     $payment = $this->getMockBuilder('\Drupal\payment\Entity\Payment')
       ->disableOriginalConstructor()
       ->getMock();
-    $payment->expects($is_payment_execution_interruptive ? $this->never() : $this->once())
-      ->method('execute');
-    $payment->expects($is_payment_execution_interruptive ? $this->never() : $this->once())
-      ->method('save');
-    $payment->expects($this->once())
+    $payment->expects($this->atLeastOnce())
+      ->method('execute')
+      ->willReturn($result);
+    $payment->expects($this->atLeastOnce())
       ->method('setPaymentMethod')
       ->with($payment_method);
+    $payment->expects(!$has_completed && !$is_xml_http_request ? $this->atLeastOnce() : $this->never())
+      ->method('urlInfo')
+      ->with('complete')
+      ->willReturn($url);
 
     $payment_method_selector = $this->getMock('\Drupal\payment\Plugin\Payment\MethodSelector\PaymentMethodSelectorInterface');
     $payment_method_selector->expects($this->atLeastOnce())
@@ -407,9 +298,6 @@ class PaymentReferenceBaseUnitTest extends UnitTestCase {
     $this->element->expects($this->atLeastOnce())
       ->method('getPaymentMethodSelector')
       ->willReturn($payment_method_selector);
-    $this->element->expects($is_payment_execution_interruptive ? $this->once() : $this->never())
-      ->method('setTemporaryPayment')
-      ->with($form['foo']['bar'], $form_state, $payment);
 
     $this->element->pay($form, $form_state);
   }
@@ -430,18 +318,29 @@ class PaymentReferenceBaseUnitTest extends UnitTestCase {
    *
    * @dataProvider providerTestAjaxPay
    */
-  public function testAjaxPay($is_payment_execution_interruptive, $number_of_commands) {
+  public function testAjaxPay($has_completed, $number_of_commands) {
     $payment = $this->getMockBuilder('\Drupal\payment\Entity\Payment')
       ->disableOriginalConstructor()
       ->getMock();
     $payment->expects($this->atLeastOnce())
       ->method('createDuplicate')
       ->willReturnSelf();
+    $payment->expects($has_completed ? $this->never() : $this->atLeastOnce())
+      ->method('urlInfo')
+      ->with('complete');
+
+    $result = $this->getMock('\Drupal\payment\PaymentExecutionResultInterface');
+    $result->expects($this->atLeastOnce())
+      ->method('hasCompleted')
+      ->willReturn($has_completed);
 
     $payment_method = $this->getMock('\Drupal\payment\Plugin\Payment\Method\PaymentMethodInterface');
-    $payment_method->expects($this->once())
-      ->method('isPaymentExecutionInterruptive')
-      ->willReturn($is_payment_execution_interruptive);
+    $payment_method->expects($this->any())
+      ->method('getPayment')
+      ->willReturn($payment);
+    $payment_method->expects($this->atLeastOnce())
+      ->method('getPaymentExecutionResult')
+      ->willReturn($result);
 
     $payment_method_selector_plugin_id = $this->randomMachineName();
 
@@ -495,8 +394,8 @@ class PaymentReferenceBaseUnitTest extends UnitTestCase {
    */
   public function providerTestAjaxPay() {
     return array(
-      array(TRUE, 2),
-      array(FALSE, 1),
+      array(TRUE, 1),
+      array(FALSE, 2),
     );
   }
 
@@ -639,15 +538,12 @@ class PaymentReferenceBaseUnitTest extends UnitTestCase {
 
     $this->element = $this->getMockBuilder('\Drupal\payment_reference\Element\PaymentReferenceBase')
       ->setConstructorArgs(array($configuration, $plugin_id, $this->pluginDefinition, $this->requestStack, $this->paymentStorage, $this->stringTranslation, $this->dateFormatter, $this->linkGenerator, $this->paymentMethodSelectorManager, new Random()))
-      ->setMethods(array('getPaymentMethodSelector', 'getTemporaryPaymentStorageKey'))
+      ->setMethods(array('getPaymentMethodSelector'))
       ->getMockForAbstractClass();
     $this->element->expects($this->atLeastOnce())
       ->method('getPaymentMethodSelector')
       ->with($element, $form_state)
       ->willReturn($payment_method_selector);
-    $this->element->expects($this->never())
-      ->method('getTemporaryPaymentStorageKey')
-      ->with($element, $form_state);
 
 
     $method = new \ReflectionMethod($this->element, 'buildCompletePaymentLink');
@@ -670,7 +566,14 @@ class PaymentReferenceBaseUnitTest extends UnitTestCase {
     );
     $form_state = $this->getMock('\Drupal\Core\Form\FormStateInterface');
 
+    $payment = $this->getMockBuilder('\Drupal\payment\Entity\Payment')
+      ->disableOriginalConstructor()
+      ->getMock();
+
     $payment_method = $this->getMock('\Drupal\payment\Plugin\Payment\Method\PaymentMethodInterface');
+    $payment_method->expects($this->atLeastOnce())
+      ->method('getPayment')
+      ->willReturn($payment);
 
     $payment_method_selector = $this->getMock('\Drupal\payment\Plugin\Payment\MethodSelector\PaymentMethodSelectorInterface');
     $payment_method_selector->expects($this->atLeastOnce())
@@ -679,15 +582,12 @@ class PaymentReferenceBaseUnitTest extends UnitTestCase {
 
     $this->element = $this->getMockBuilder('\Drupal\payment_reference\Element\PaymentReferenceBase')
       ->setConstructorArgs(array($configuration, $plugin_id, $this->pluginDefinition, $this->requestStack, $this->paymentStorage, $this->stringTranslation, $this->dateFormatter, $this->linkGenerator, $this->paymentMethodSelectorManager, new Random()))
-      ->setMethods(array('getPaymentMethodSelector', 'getTemporaryPaymentStorageKey'))
+      ->setMethods(array('getPaymentMethodSelector'))
       ->getMockForAbstractClass();
     $this->element->expects($this->atLeastOnce())
       ->method('getPaymentMethodSelector')
       ->with($element, $form_state)
       ->willReturn($payment_method_selector);
-    $this->element->expects($this->atLeastOnce())
-      ->method('getTemporaryPaymentStorageKey')
-      ->with($element, $form_state);
 
 
     $method = new \ReflectionMethod($this->element, 'buildCompletePaymentLink');
@@ -798,11 +698,34 @@ class PaymentReferenceBaseUnitTest extends UnitTestCase {
    * @covers ::buildRefreshButton
    */
   public function testBuildRefreshButton() {
+    $limit_allowed_payment_method_ids = [$this->randomMachineName()];
+
+    $payment_method_selector_id = $this->randomMachineName();
+
+    $payment_method_selector = $this->getMock('\Drupal\payment\Plugin\Payment\MethodSelector\PaymentMethodSelectorInterface');
+
+    $this->paymentMethodSelectorManager->expects($this->atLeastOnce())
+      ->method('createInstance')
+      ->with($payment_method_selector_id)
+      ->willReturn($payment_method_selector);
+
+    $payment = $this->getMockBuilder('\Drupal\payment\Entity\Payment')
+      ->disableOriginalConstructor()
+      ->getMock();
+    $payment->expects($this->atLeastOnce())
+      ->method('createDuplicate')
+      ->willReturnSelf();
+
     $element = array(
       '#default_value' => mt_rand(),
       'container' => array(
         '#id' => $this->randomMachineName(),
       ),
+      '#limit_allowed_payment_method_ids' => $limit_allowed_payment_method_ids,
+      '#name' => $this->randomMachineName(),
+      '#payment_method_selector_id' => $payment_method_selector_id,
+      '#prototype_payment' => $payment,
+      '#required' => (bool) mt_rand(0, 1),
     );
     $form_state = $this->getMock('\Drupal\Core\Form\FormStateInterface');
 
@@ -850,7 +773,7 @@ class PaymentReferenceBaseUnitTest extends UnitTestCase {
 
     $this->element = $this->getMockBuilder('\Drupal\payment_reference\Element\PaymentReferenceBase')
       ->setConstructorArgs(array($configuration, $plugin_id, $this->pluginDefinition, $this->requestStack, $this->paymentStorage, $this->stringTranslation, $this->dateFormatter, $this->linkGenerator, $this->paymentMethodSelectorManager, new Random()))
-      ->setMethods(array('getEntityFormDisplay', 'getPaymentMethodSelector', 'hasTemporaryPayment'))
+      ->setMethods(array('getEntityFormDisplay', 'getPaymentMethodSelector'))
       ->getMockForAbstractClass();
     $this->element->expects($this->atLeastOnce())
       ->method('getEntityFormDisplay')
@@ -859,10 +782,6 @@ class PaymentReferenceBaseUnitTest extends UnitTestCase {
       ->method('getPaymentMethodSelector')
       ->with($element, $form_state)
       ->willReturn($payment_method_selector);
-    $this->element->expects($this->atLeastOnce())
-      ->method('hasTemporaryPayment')
-      ->with($element, $form_state)
-      ->willReturn(TRUE);
 
     $build = $method->invoke($this->element, $element, $form_state);
     $this->assertInternalType('array', $build);
diff --git a/modules/payment_reference/tests/src/Unit/Element/PaymentReferenceUnitTest.php b/modules/payment_reference/tests/src/Unit/Element/PaymentReferenceUnitTest.php
index 381b549..6dedf9f 100644
--- a/modules/payment_reference/tests/src/Unit/Element/PaymentReferenceUnitTest.php
+++ b/modules/payment_reference/tests/src/Unit/Element/PaymentReferenceUnitTest.php
@@ -76,13 +76,6 @@ class PaymentReferenceUnitTest extends UnitTestCase {
   protected $stringTranslation;
 
   /**
-   * The temporary payment storage.
-   *
-   * @var \Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface|\PHPUnit_Framework_MockObject_MockObject
-   */
-  protected $temporaryPaymentStorage;
-
-  /**
    * The url generator.
    *
    * @var \Drupal\Core\Routing\UrlGeneratorInterface|\PHPUnit_Framework_MockObject_MockObject
@@ -113,15 +106,13 @@ class PaymentReferenceUnitTest extends UnitTestCase {
 
     $this->stringTranslation = $this->getStringTranslationStub();
 
-    $this->temporaryPaymentStorage = $this->getMock('\Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface');
-
     $this->urlGenerator = $this->getMock('\Drupal\Core\Routing\UrlGeneratorInterface');
 
     $configuration = array();
     $plugin_id = $this->randomMachineName();
     $plugin_definition = array();
 
-    $this->element = new PaymentReference($configuration, $plugin_id, $plugin_definition, $this->requestStack, $this->paymentStorage, $this->stringTranslation, $this->dateFormatter, $this->linkGenerator, $this->paymentMethodSelectorManager, new Random(), $this->temporaryPaymentStorage, $this->paymentQueue);
+    $this->element = new PaymentReference($configuration, $plugin_id, $plugin_definition, $this->requestStack, $this->paymentStorage, $this->stringTranslation, $this->dateFormatter, $this->linkGenerator, $this->paymentMethodSelectorManager, new Random(), $this->paymentQueue);
   }
 
   /**
@@ -134,17 +125,10 @@ class PaymentReferenceUnitTest extends UnitTestCase {
       ->with('payment')
       ->willReturn($this->paymentStorage);
 
-    $key_value_expirable = $this->getMock('\Drupal\Core\KeyValueStore\KeyValueExpirableFactoryInterface');
-    $key_value_expirable->expects($this->once())
-      ->method('get')
-      ->with('payment.payment_type.payment_reference')
-      ->willReturn($this->temporaryPaymentStorage);
-
     $container = $this->getMock('\Symfony\Component\DependencyInjection\ContainerInterface');
     $map = array(
       array('date.formatter', ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, $this->dateFormatter),
       array('entity.manager', ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, $entity_manager),
-      array('keyvalue.expirable', ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, $key_value_expirable),
       array('link_generator', ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, $this->linkGenerator),
       array('payment_reference.queue', ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, $this->paymentQueue),
       array('plugin.manager.payment.method_selector', ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, $this->paymentMethodSelectorManager),
@@ -172,13 +156,4 @@ class PaymentReferenceUnitTest extends UnitTestCase {
     $this->assertSame($this->paymentQueue, $method->invoke($this->element));
   }
 
-  /**
-   * @covers ::getTemporaryPaymentStorage
-   */
-  public function testGetTemporaryPaymentStorage() {
-    $method = new \ReflectionMethod($this->element, 'getTemporaryPaymentStorage');
-    $method->setAccessible(TRUE);
-    $this->assertSame($this->temporaryPaymentStorage, $method->invoke($this->element));
-  }
-
 }
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..8de72d5 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
@@ -8,12 +8,10 @@
 
 namespace Drupal\Tests\payment_reference\Unit\Plugin\Payment\Type;
 
+use Drupal\Core\DependencyInjection\ContainerBuilder;
 use Drupal\payment_reference\Plugin\Payment\Type\PaymentReference;
 use Drupal\Tests\UnitTestCase;
 use Symfony\Component\DependencyInjection\ContainerInterface;
-use Symfony\Component\HttpFoundation\RedirectResponse;
-use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
-use Symfony\Component\HttpKernel\KernelEvents;
 
 /**
  * @coversDefaultClass \Drupal\payment_reference\Plugin\Payment\Type\PaymentReference
@@ -232,103 +230,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 @@
-<?php
-
-/**
- * Contains \Drupal\payment_test\Plugin\Payment\Method\PaymentTest.
- */
-
-namespace Drupal\payment_test\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\Plugin\Payment\Method\PaymentMethodBase;
-use Drupal\payment\Plugin\Payment\Status\PaymentStatusManagerInterface;
-use Symfony\Component\DependencyInjection\ContainerInterface;
-use Symfony\Component\EventDispatcher\EventDispatcherInterface;
-
-/**
- * A testing payment method.
- *
- * @PaymentMethod(
- *   id = "payment_test_interruptive",
- *   label = @Translation("Test method (interruptive)"),
- *   message_text = "Foo",
- *   message_text_format = "plain_text"
- * )
- */
-class PaymentTestInterruptive extends PaymentMethodBase implements ContainerFactoryPluginInterface {
-
-  /**
-   * The payment status manager.
-   *
-   * @var \Drupal\payment\Plugin\Payment\Status\PaymentStatusManagerInterface
-   */
-  protected $paymentStatusManager;
-
-  /**
-   * Constructs a new class instance.
-   *
-   * @param array $configuration
-   *   A configuration array containing information about the plugin instance.
-   * @param string $plugin_id
-   *   The plugin_id for the plugin instance.
-   * @param array $plugin_definition
-   *   The plugin implementation definition.
-   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
-   *   The module handler.
-   * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
-   *   The event dispatcher.
-   * @param \Drupal\Core\Utility\Token $token
-   *   The token API.
-   * @param \Drupal\payment\Plugin\Payment\Status\PaymentStatusManagerInterface
-   *   The payment status manager.
-   */
-  public function __construct(array $configuration, $plugin_id, array $plugin_definition, ModuleHandlerInterface $module_handler, EventDispatcherInterface $event_dispatcher, Token $token, PaymentStatusManagerInterface $payment_status_manager) {
-    $configuration += $this->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 @@
+<?php
+
+/**
+ * Contains \Drupal\payment_test\Plugin\Payment\Method\PaymentTest.
+ */
+
+namespace Drupal\payment_test\Plugin\Payment\Method;
+
+use Drupal\Core\Session\AccountInterface;
+use Drupal\payment\Plugin\Payment\Method\PaymentMethodBase;
+
+/**
+ * A testing payment method.
+ *
+ * @PaymentMethod(
+ *   id = "payment_test_no_response",
+ *   label = @Translation("Test method (execution does not return response)"),
+ *   message_text = "Foo",
+ *   message_text_format = "plain_text"
+ * )
+ */
+class PaymentTestNoResponse extends PaymentMethodBase {
+
+  /**
+   * {@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/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 @@
+<?php
+
+/**
+ * Contains \Drupal\payment_test\Plugin\Payment\Method\PaymentTest.
+ */
+
+namespace Drupal\payment_test\Plugin\Payment\Method;
+
+use Drupal\Core\Url;
+use Drupal\payment\PaymentExecutionResult;
+use Drupal\payment\Response;
+
+/**
+ * A testing payment method.
+ *
+ * @PaymentMethod(
+ *   id = "payment_test_response",
+ *   label = @Translation("Test method (execution returns response)"),
+ *   message_text = "Foo",
+ *   message_text_format = "plain_text"
+ * )
+ */
+class PaymentTestResponse extends PaymentTestNoResponse {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPaymentExecutionResult() {
+    return new PaymentExecutionResult(new Response(new Url('http://example.com')));
+  }
+
+}
diff --git a/modules/payment_test/src/Plugin/Payment/Method/PaymentTestUninterruptive.php b/modules/payment_test/src/Plugin/Payment/Method/PaymentTestUninterruptive.php
deleted file mode 100644
index 2d8448d..0000000
--- a/modules/payment_test/src/Plugin/Payment/Method/PaymentTestUninterruptive.php
+++ /dev/null
@@ -1,36 +0,0 @@
-<?php
-
-/**
- * Contains \Drupal\payment_test\Plugin\Payment\Method\PaymentTest.
- */
-
-namespace Drupal\payment_test\Plugin\Payment\Method;
-
-use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
-use Drupal\Core\Session\AccountInterface;
-use Drupal\Core\Utility\Token;
-use Drupal\payment\Plugin\Payment\Method\PaymentMethodBase;
-use Drupal\payment\Plugin\Payment\Status\PaymentStatusManagerInterface;
-use Symfony\Component\DependencyInjection\ContainerInterface;
-use Symfony\Component\EventDispatcher\EventDispatcherInterface;
-
-/**
- * A testing payment method.
- *
- * @PaymentMethod(
- *   id = "payment_test_uninterruptive",
- *   label = @Translation("Test method (uninterruptive)"),
- *   message_text = "Foo",
- *   message_text_format = "plain_text"
- * )
- */
-class PaymentTestUninterruptive extends PaymentTestInterruptive {
-
-  /**
-   * {@inheritdoc}
-   */
-  public function isPaymentExecutionInterruptive() {
-    return FALSE;
-  }
-
-}
diff --git a/modules/payment_test/src/Plugin/Payment/Type/PaymentTest.php b/modules/payment_test/src/Plugin/Payment/Type/PaymentTest.php
index cbcd31a..7fe29f5 100644
--- a/modules/payment_test/src/Plugin/Payment/Type/PaymentTest.php
+++ b/modules/payment_test/src/Plugin/Payment/Type/PaymentTest.php
@@ -36,7 +36,7 @@ class PaymentTest extends PaymentTypeBase {
   /**
    * {@inheritdoc
    */
-  public function doResumeContext() {
+  public function doGetResumeContextResponse() {
   }
 
 }
diff --git a/payment.routing.yml b/payment.routing.yml
index bc932ff..4d9fc0f 100644
--- a/payment.routing.yml
+++ b/payment.routing.yml
@@ -62,6 +62,14 @@ payment.payment.refund:
   options:
     _admin_route: TRUE
 
+payment.payment.complete:
+  path: '/payment/{payment}/complete'
+  defaults:
+    _controller: '\Drupal\payment\Controller\CompletePayment::execute'
+  requirements:
+    _entity_access: 'payment.complete'
+    _csrf_token: 'TRUE'
+
 payment.payment.delete:
   path: '/payment/{payment}/delete'
   defaults:
@@ -117,7 +125,7 @@ payment.payment_method_configuration.enable:
     _controller: '\Drupal\payment\Controller\PaymentMethod::enable'
   requirements:
     _entity_access: 'payment_method_configuration.enable'
-    _csrf_token: 'TRUE'
+    _te_token: 'TRUE'
 
 payment.payment_method_configuration.disable:
   path: '/admin/config/services/payment/method/configuration/{payment_method_configuration}/disable'
@@ -125,7 +133,6 @@ payment.payment_method_configuration.disable:
     _controller: '\Drupal\payment\Controller\PaymentMethod::disable'
   requirements:
     _entity_access: 'payment_method_configuration.disable'
-    _csrf_token: 'TRUE'
 
 payment.payment_method_configuration.delete:
   path: '/admin/config/services/payment/method/configuration/{payment_method_configuration}/delete'
diff --git a/src/Controller/Payment.php b/src/Controller/Payment.php
index c72d6b4..cdd14c1 100644
--- a/src/Controller/Payment.php
+++ b/src/Controller/Payment.php
@@ -11,6 +11,8 @@ use Drupal\Core\Controller\ControllerBase;
 use Drupal\Core\StringTranslation\TranslationInterface;
 use Drupal\payment\Entity\PaymentInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\HttpFoundation\RedirectResponse;
+use Symfony\Component\HttpFoundation\Response;
 
 /**
  * Returns responses for payment routes.
@@ -60,4 +62,15 @@ class Payment extends ControllerBase {
     ));
   }
 
+  /**
+   * Completes a payment.
+   *
+   * @param \Drupal\payment\Entity\PaymentInterface $payment
+   *
+   * @return \Symfony\Component\HttpFoundation\Response
+   */
+  public function complete(PaymentInterface $payment) {
+    return $payment->getPaymentMethod()->getPaymentExecutionResult()->getCompletionResponse()->getResponse();
+  }
+
 }
diff --git a/src/Entity/Payment.php b/src/Entity/Payment.php
index b81f779..867af70 100644
--- a/src/Entity/Payment.php
+++ b/src/Entity/Payment.php
@@ -54,7 +54,8 @@ use Drupal\user\UserInterface;
  *     "delete-form" = "payment.payment.delete",
  *     "update-status-form" = "payment.payment.update_status",
  *     "capture-form" = "payment.payment.capture",
- *     "refund-form" = "payment.payment.refund"
+ *     "refund-form" = "payment.payment.refund",
+ *     "complete" = "payment.payment.complete"
  *   }
  * )
  */
@@ -327,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 775228c..53efd7a 100644
--- a/src/Entity/Payment/PaymentAccessControlHandler.php
+++ b/src/Entity/Payment/PaymentAccessControlHandler.php
@@ -50,6 +50,11 @@ class PaymentAccessControlHandler extends EntityAccessControlHandler {
       }
       return AccessResult::forbidden();
     }
+    elseif ($operation == 'complete') {
+      return AccessResult::allowedIf($payment->getOwnerId() == $account->id())
+        ->andIf(AccessResult::allowedIf($payment->getPaymentMethod()))
+        ->andIf(AccessResult::allowedIf($payment->getPaymentMethod()->getPaymentExecutionResult()->hasCompleted()));
+    }
     return $this->checkAccessPermission($payment, $operation, $account);
   }
 
diff --git a/src/Entity/Payment/PaymentListBuilder.php b/src/Entity/Payment/PaymentListBuilder.php
index 219fde6..ac2eb8a 100644
--- a/src/Entity/Payment/PaymentListBuilder.php
+++ b/src/Entity/Payment/PaymentListBuilder.php
@@ -192,6 +192,12 @@ class PaymentListBuilder extends EntityListBuilder {
           'url' => $entity->urlInfo('refund-form'),
         );
     }
+    if ($entity->access('complete')) {
+      $operations['resume'] = array(
+        'title' => $this->t('Complete'),
+        'url' => $entity->urlInfo('complete'),
+      );
+    }
 
     return $operations;
   }
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 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\payment\PaymentExecutionResult.
+ */
+
+namespace Drupal\payment;
+
+/**
+ * Provides a payment execution result.
+ */
+class PaymentExecutionResult implements PaymentExecutionResultInterface {
+
+  /**
+   * The response.
+   *
+   * @var \Drupal\payment\ResponseInterface|null
+   */
+  protected $response;
+
+  /**
+   * Creates a new instance.
+   *
+   * @param \Drupal\payment\ResponseInterface|null $response
+   *   A response or NULL if execution has completed.
+   */
+  public function __construct(ResponseInterface $response = NULL) {
+    $this->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
new file mode 100644
index 0000000..e91725a
--- /dev/null
+++ b/src/PaymentExecutionResultInterface.php
@@ -0,0 +1,37 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\payment\PaymentExecutionResultInterface.
+ */
+
+namespace Drupal\payment;
+
+/**
+ * Defines a payment execution result.
+ *
+ * This interface has nothing to do with payment statuses. Instead, it provides
+ * information about a payment's execution workflow, during which the payment's
+ * status can, but does not have to be changed.
+ */
+interface PaymentExecutionResultInterface {
+
+  /**
+   * Checks whether the execution process has completed.
+   *
+   * @return bool
+   *   Whether the execution process is has completed. When FALSE is
+   *   returned, self::getCompletionResponse() MUST return a response.
+   */
+  public function hasCompleted();
+
+  /**
+   * Gets the response to complete payment execution.
+   *
+   * @return \Drupal\payment\ResponseInterface|null
+   *   A response (only if self::isInProgress() returns TRUE) or NULL if payment
+   *   execution cannot be completed (anymore).
+   */
+  public function getCompletionResponse();
+
+}
diff --git a/src/Plugin/Payment/Method/Basic.php b/src/Plugin/Payment/Method/Basic.php
index ef05cce..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.
@@ -39,44 +34,6 @@ use Symfony\Component\EventDispatcher\EventDispatcherInterface;
 class Basic extends PaymentMethodBase implements ContainerFactoryPluginInterface, PaymentMethodCapturePaymentInterface, PaymentMethodRefundPaymentInterface, PaymentMethodUpdatePaymentStatusInterface {
 
   /**
-   * The payment status manager.
-   *
-   * @var \Drupal\payment\Plugin\Payment\Status\PaymentStatusManagerInterface
-   */
-  protected $paymentStatusManager;
-
-  /**
-   * Constructs a new class instance.
-   *
-   * @param array $configuration
-   *   A configuration array containing information about the plugin instance.
-   * @param string $plugin_id
-   *   The plugin_id for the plugin instance.
-   * @param array $plugin_definition
-   *   The plugin implementation definition.
-   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
-   *   The module handler.
-   * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
-   *   The event dispatcher.
-   * @param \Drupal\Core\Utility\Token $token
-   *   The token API.
-   * @param \Drupal\payment\Plugin\Payment\Status\PaymentStatusManagerInterface
-   *   The payment status manager.
-   */
-  public function __construct(array $configuration, $plugin_id, array $plugin_definition, ModuleHandlerInterface $module_handler, EventDispatcherInterface $event_dispatcher, Token $token, PaymentStatusManagerInterface $payment_status_manager) {
-    $configuration += $this->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}
    */
   public function getSupportedCurrencies() {
@@ -145,17 +102,9 @@ 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();
-    $this->getPayment()->getPaymentType()->resumeContext();
   }
 
   /**
diff --git a/src/Plugin/Payment/Method/PaymentMethodBase.php b/src/Plugin/Payment/Method/PaymentMethodBase.php
index 0572b6d..9e2eb6e 100644
--- a/src/Plugin/Payment/Method/PaymentMethodBase.php
+++ b/src/Plugin/Payment/Method/PaymentMethodBase.php
@@ -18,6 +18,8 @@ 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;
 
@@ -54,6 +56,13 @@ abstract class PaymentMethodBase extends PluginBase implements ContainerFactoryP
   protected $payment;
 
   /**
+   * The payment status manager.
+   *
+   * @var \Drupal\payment\Plugin\Payment\Status\PaymentStatusManagerInterface
+   */
+  protected $paymentStatusManager;
+
+  /**
    * The token API.
    *
    * @var \Drupal\Core\Utility\Token
@@ -75,12 +84,15 @@ abstract class PaymentMethodBase extends PluginBase implements ContainerFactoryP
    *   The event dispatcher.
    * @param \Drupal\Core\Utility\Token $token
    *   The token API.
+   * @param \Drupal\payment\Plugin\Payment\Status\PaymentStatusManagerInterface
+   *   The payment status manager.
    */
-  public function __construct(array $configuration, $plugin_id, array $plugin_definition, ModuleHandlerInterface $module_handler, EventDispatcherInterface $event_dispatcher, Token $token) {
+  public function __construct(array $configuration, $plugin_id, array $plugin_definition, ModuleHandlerInterface $module_handler, EventDispatcherInterface $event_dispatcher, Token $token, PaymentStatusManagerInterface $payment_status_manager) {
     $configuration += $this->defaultConfiguration();
     parent::__construct($configuration, $plugin_id, $plugin_definition);
     $this->eventDispatcher = $event_dispatcher;
     $this->moduleHandler = $module_handler;
+    $this->paymentStatusManager = $payment_status_manager;
     $this->token = $token;
   }
 
@@ -88,7 +100,7 @@ abstract class PaymentMethodBase extends PluginBase implements ContainerFactoryP
    * {@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'));
+    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'));
   }
 
   /**
@@ -194,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.');
@@ -234,7 +238,10 @@ 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();
   }
 
   /**
@@ -245,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/PaymentMethodInterface.php b/src/Plugin/Payment/Method/PaymentMethodInterface.php
index 3e45743..52bb998 100644
--- a/src/Plugin/Payment/Method/PaymentMethodInterface.php
+++ b/src/Plugin/Payment/Method/PaymentMethodInterface.php
@@ -43,21 +43,23 @@ interface PaymentMethodInterface extends PluginInspectionInterface, Configurable
    * Executes the payment.
    *
    * When executing a payment, it may be authorized, or authorized and captured.
+   * After calling this method, more action may be required depending on the
+   * return value of self::getPaymentExecutionResult().
+   * This method MUST set the payment's status to "payment_pending" before it
+   * performs any payment-method-specific logic.
+   *
+   * @return \Drupal\payment\PaymentExecutionResultInterface
    *
    * @see self::executePaymentAccess
    */
   public function executePayment();
 
   /**
-   * Whether payment execution interrupts the payment type's context.
-   *
-   * If payment execution interrupts the context's workflow, this must return
-   * TRUE. An example of an interruption is when the payer must be redirected
-   * off-site.
+   * Gets the payment execution status.
    *
-   * @return bool
+   * @return \Drupal\payment\PaymentExecutionResultInterface
    */
-  public function isPaymentExecutionInterruptive();
+  public function getPaymentExecutionResult();
 
   /**
    * Gets the payment this payment method is for.
diff --git a/src/Plugin/Payment/Method/Unavailable.php b/src/Plugin/Payment/Method/Unavailable.php
index 7ec1fa2..3ae39ba 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 0aff7d7..bf7b626 100644
--- a/src/Plugin/Payment/Type/PaymentTypeInterface.php
+++ b/src/Plugin/Payment/Type/PaymentTypeInterface.php
@@ -43,21 +43,23 @@ interface PaymentTypeInterface extends PluginInspectionInterface, ConfigurablePl
   public function paymentDescription($language_code = NULL);
 
   /**
-   * Checks if the payment type context can be resumed..
+   * Checks if the payment type context can be resumed.
    *
    * @param \Drupal\Core\Session\AccountInterface $account
    *
    * @return bool
    *
-   * @see self::resumeContext
+   * @see self::getResumeContextResponse
    */
   public function resumeContextAccess(AccountInterface $account);
 
   /**
-   * Resumes the payment context's workflow.
+   * Resumes the payer's original workflow.
+   *
+   * @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
new file mode 100644
index 0000000..6d96950
--- /dev/null
+++ b/src/Response.php
@@ -0,0 +1,58 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\payment\Response.
+ */
+
+namespace Drupal\payment;
+
+use Drupal\Core\Url;
+use Symfony\Component\HttpFoundation\RedirectResponse;
+use Symfony\Component\HttpFoundation\Response as SymfonyResponse;
+
+/**
+ * Provides a payment response.
+ */
+class Response implements ResponseInterface {
+
+  /**
+   * The redirect URL.
+   *
+   * @var \Drupal\Core\Url
+   */
+  protected $url;
+
+  /**
+   * The response.
+   *
+   * @var \Symfony\Component\HttpFoundation\Response|null
+   */
+  protected $response;
+
+  /**
+   * Creates a new instance.
+   *
+   * @param \Drupal\Core\Url $url
+   * @param \Symfony\Component\HttpFoundation\Response|null $response
+   */
+  public function __construct(Url $url, SymfonyResponse $response = NULL) {
+    $this->response = $response;
+    $this->url = $url;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getRedirectUrl() {
+    return $this->url;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getResponse() {
+    return $this->response ? $this->response : new RedirectResponse($this->getRedirectUrl()->toString());
+  }
+
+}
diff --git a/src/ResponseInterface.php b/src/ResponseInterface.php
new file mode 100644
index 0000000..c9e9497
--- /dev/null
+++ b/src/ResponseInterface.php
@@ -0,0 +1,33 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\payment\ResponseInterface.
+ */
+
+namespace Drupal\payment;
+
+/**
+ * Defines a payment response.
+ *
+ * When returning a instance of a class that implements this interface,
+ * self::getRedirectUrl() MUST return a value and calling code MUST be able to
+ * perform a redirect using this value. All other functionality is optional.
+ */
+interface ResponseInterface {
+
+  /**
+   * Gets the URL the calling code may redirect to after payment execution.
+   *
+   * @return \Drupal\Core\Url
+   */
+  public function getRedirectUrl();
+
+  /**
+   * Gets the response
+   *
+   * @return \Symfony\Component\HttpFoundation\Response
+   */
+  public function getResponse();
+
+}
diff --git a/tests/src/Unit/Controller/PaymentUnitTest.php b/tests/src/Unit/Controller/PaymentUnitTest.php
index b7d856c..df1c35d 100644
--- a/tests/src/Unit/Controller/PaymentUnitTest.php
+++ b/tests/src/Unit/Controller/PaymentUnitTest.php
@@ -104,4 +104,37 @@ class PaymentUnitTest extends UnitTestCase {
     $this->assertSame($string, $this->controller->editTitle($payment));
   }
 
+  /**
+   * @covers ::complete
+   */
+  public function testComplete() {
+    $symfony_response = $this->getMockBuilder('\Symfony\Component\HttpFoundation\Response')
+      ->disableOriginalConstructor()
+      ->getMock();
+
+    $response = $this->getMock('\Drupal\payment\ResponseInterface');
+    $response->expects($this->atLeastOnce())
+      ->method('getResponse')
+      ->willReturn($symfony_response);
+
+    $result = $this->getMock('\Drupal\payment\PaymentExecutionResultInterface');
+    $result->expects($this->atLeastOnce())
+      ->method('getCompletionResponse')
+      ->willReturn($response);
+
+    $payment_method = $this->getMock('\Drupal\payment\Plugin\Payment\Method\PaymentMethodInterface');
+    $payment_method->expects($this->atLeastOnce())
+      ->method('getPaymentExecutionResult')
+      ->willReturn($result);
+
+    $payment = $this->getMockBuilder('\Drupal\payment\Entity\Payment')
+      ->disableOriginalConstructor()
+      ->getMock();
+    $payment->expects($this->atLeastOnce())
+      ->method('getPaymentMethod')
+      ->willReturn($payment_method);
+
+    $this->assertSame($symfony_response, $this->controller->complete($payment));
+  }
+
 }
diff --git a/tests/src/Unit/PaymentExecutionResultUnitTest.php b/tests/src/Unit/PaymentExecutionResultUnitTest.php
new file mode 100644
index 0000000..61cbf60
--- /dev/null
+++ b/tests/src/Unit/PaymentExecutionResultUnitTest.php
@@ -0,0 +1,66 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\payment\Unit\PaymentExecutionResultUnitTest.
+ */
+
+namespace Drupal\Tests\payment\Unit;
+
+use Drupal\Core\Url;
+use Drupal\payment\PaymentExecutionResult;
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * @coversDefaultClass \Drupal\payment\PaymentExecutionResult
+ *
+ * @group Payment
+ */
+class PaymentExecutionResultUnitTest extends UnitTestCase {
+
+  /**
+   * The response.
+   *
+   * @var \Drupal\payment\ResponseInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $response;
+
+  /**
+   * The payment execution result under test.
+   *
+   * @var \Drupal\payment\PaymentExecutionResult
+   */
+  protected $paymentExecutionResult;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setUp() {
+    $this->response = $this->getMock('\Drupal\payment\ResponseInterface');
+  }
+
+  /**
+   * @covers ::hasCompleted
+   * @covers ::__construct
+   */
+  function testHasCompleted() {
+    $this->paymentExecutionResult = new PaymentExecutionResult();
+    $this->assertTrue($this->paymentExecutionResult->hasCompleted());
+
+    $this->paymentExecutionResult = new PaymentExecutionResult($this->response);
+    $this->assertFalse($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 a16042d..7533b2d 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() {
@@ -141,19 +134,12 @@ class BasicUnitTest extends PaymentMethodBaseUnitTestBase {
       ->with($this->pluginDefinition['execute_status_id'])
       ->will($this->returnValue($payment_status));
 
-    $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('save');
     $payment->expects($this->once())
-      ->method('getPaymentType')
-      ->will($this->returnValue($payment_type));
-    $payment->expects($this->once())
       ->method('setPaymentStatus')
       ->with($payment_status);
 
diff --git a/tests/src/Unit/Plugin/Payment/Method/PaymentMethodBaseUnitTest.php b/tests/src/Unit/Plugin/Payment/Method/PaymentMethodBaseUnitTest.php
index b0d34b9..fa45b8b 100644
--- a/tests/src/Unit/Plugin/Payment/Method/PaymentMethodBaseUnitTest.php
+++ b/tests/src/Unit/Plugin/Payment/Method/PaymentMethodBaseUnitTest.php
@@ -50,7 +50,7 @@ class PaymentMethodBaseUnitTest extends PaymentMethodBaseUnitTestBase {
     $this->moduleHandler = $this->getMock('\Drupal\Core\Extension\ModuleHandlerInterface');
 
     $this->plugin = $this->getMockBuilder('\Drupal\payment\Plugin\Payment\Method\PaymentMethodBase')
-      ->setConstructorArgs(array(array(), '', $this->pluginDefinition, $this->moduleHandler, $this->eventDispatcher, $this->token))
+      ->setConstructorArgs(array(array(), '', $this->pluginDefinition, $this->moduleHandler, $this->eventDispatcher, $this->token, $this->paymentStatusManager))
       ->getMockForAbstractClass();
   }
 
@@ -62,6 +62,7 @@ class PaymentMethodBaseUnitTest extends PaymentMethodBaseUnitTestBase {
     $map = array(
       array('event_dispatcher', ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, $this->eventDispatcher),
       array('module_handler', ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, $this->moduleHandler),
+      array('plugin.manager.payment.status', ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, $this->paymentStatusManager),
       array('token', ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, $this->token),
     );
     $container->expects($this->any())
@@ -193,13 +194,6 @@ class PaymentMethodBaseUnitTest extends PaymentMethodBaseUnitTestBase {
   }
 
   /**
-   * @covers ::isPaymentExecutionInterruptive
-   */
-  public function testIsPaymentExecutionInterruptive() {
-    $this->assertInternalType('bool', $this->plugin->isPaymentExecutionInterruptive());
-  }
-
-  /**
    * @covers ::executePayment
    *
    * @expectedException \Exception
@@ -212,6 +206,13 @@ class PaymentMethodBaseUnitTest extends PaymentMethodBaseUnitTestBase {
    * @covers ::executePayment
    */
   public function testExecutePayment() {
+    $payment_status = $this->getMock('\Drupal\payment\Plugin\Payment\Status\PaymentStatusInterface');
+
+    $this->paymentStatusManager->expects($this->atLeastOnce())
+      ->method('createInstance')
+      ->with('payment_pending')
+      ->willReturn($payment_status);
+
     $payment = $this->getMockBuilder('\Drupal\payment\Entity\Payment')
       ->disableOriginalConstructor()
       ->getMock();
@@ -250,7 +251,7 @@ class PaymentMethodBaseUnitTest extends PaymentMethodBaseUnitTestBase {
     $this->pluginDefinition['active'] = $active;
     /** @var \Drupal\payment\Plugin\Payment\Method\PaymentMethodBase|\PHPUnit_Framework_MockObject_MockObject $payment_method */
     $payment_method = $this->getMockBuilder('\Drupal\payment\Plugin\Payment\Method\PaymentMethodBase')
-      ->setConstructorArgs(array(array(), '', $this->pluginDefinition, $this->moduleHandler, $this->eventDispatcher, $this->token))
+      ->setConstructorArgs(array(array(), '', $this->pluginDefinition, $this->moduleHandler, $this->eventDispatcher, $this->token, $this->paymentStatusManager))
       ->setMethods(array('executePaymentAccessCurrency', 'executePaymentAccessEvent', 'doExecutePaymentAccess'))
       ->getMockForAbstractClass();
     $payment_method->expects($this->any())
diff --git a/tests/src/Unit/Plugin/Payment/Method/PaymentMethodBaseUnitTestBase.php b/tests/src/Unit/Plugin/Payment/Method/PaymentMethodBaseUnitTestBase.php
index a7e051c..e78ae23 100644
--- a/tests/src/Unit/Plugin/Payment/Method/PaymentMethodBaseUnitTestBase.php
+++ b/tests/src/Unit/Plugin/Payment/Method/PaymentMethodBaseUnitTestBase.php
@@ -30,6 +30,13 @@ abstract class PaymentMethodBaseUnitTestBase extends UnitTestCase {
   protected $token;
 
   /**
+   * The payment status manager.
+   *
+   * @var \Drupal\payment\Plugin\Payment\Status\PaymentStatusManagerInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $paymentStatusManager;
+
+  /**
    * The definition of the payment method plugin under test.
    *
    * @var array
@@ -51,6 +58,8 @@ abstract class PaymentMethodBaseUnitTestBase extends UnitTestCase {
 
     $this->eventDispatcher = $this->getMock('\Symfony\Component\EventDispatcher\EventDispatcherInterface');
 
+    $this->paymentStatusManager = $this->getMock('\Drupal\payment\Plugin\Payment\Status\PaymentStatusManagerInterface');
+
     $this->token = $this->getMockBuilder('\Drupal\Core\Utility\Token')
       ->disableOriginalConstructor()
       ->getMock();
diff --git a/tests/src/Unit/Plugin/Payment/Method/UnavailableUnitTest.php b/tests/src/Unit/Plugin/Payment/Method/UnavailableUnitTest.php
index b61d18d..0911671 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
new file mode 100644
index 0000000..c6e4aae
--- /dev/null
+++ b/tests/src/Unit/ResponseUnitTest.php
@@ -0,0 +1,93 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\payment\Unit\ResponseUnitTest.
+ */
+
+namespace Drupal\Tests\payment\Unit;
+
+use Drupal\Core\DependencyInjection\ContainerBuilder;
+use Drupal\Core\Url;
+use Drupal\payment\Response;
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * @coversDefaultClass \Drupal\payment\Response
+ *
+ * @group Payment
+ */
+class ResponseUnitTest extends UnitTestCase {
+
+  /**
+   * The redirect URL.
+   *
+   * @var \Drupal\Core\Url
+   */
+  protected $redirectUrl;
+
+  /**
+   * The response.
+   *
+   * @var \Symfony\Component\HttpFoundation\Response|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $symfonyResponse;
+
+  /**
+   * The response under test.
+   *
+   * @var \Drupal\payment\Response
+   */
+  protected $response;
+
+  /**
+   * {@inheritdoc}
+   *
+   * @covers ::__construct
+   */
+  public function setUp() {
+    $this->redirectUrl = new Url($this->randomMachineName());
+
+    $this->symfonyResponse = $this->getMockBuilder('\Symfony\Component\HttpFoundation\Response')
+      ->disableOriginalConstructor()
+      ->getMock();
+
+    $this->response = new Response($this->redirectUrl, $this->symfonyResponse);
+  }
+
+  /**
+   * @covers ::getRedirectUrl
+   */
+  function testGetRedirectUrl() {
+    $this->assertSame($this->redirectUrl, $this->response->getRedirectUrl());
+  }
+
+  /**
+   * @covers ::getResponse
+   */
+  function testGetResponse() {
+    $this->assertSame($this->symfonyResponse, $this->response->getResponse());
+  }
+
+  /**
+   * @covers ::getResponse
+   * @covers ::__construct
+   */
+  function testGetResponseWithAutomaticallyCreatedResponse() {
+    $url_generator = $this->getMock('\Drupal\Core\Routing\UrlGeneratorInterface');
+    $url_generator->expects($this->atLeastOnce())
+      ->method('generateFromRoute')
+      ->with($this->redirectUrl->getRouteName())
+      ->willReturn($this->randomMachineName());
+
+    $container = new ContainerBuilder();
+    $container->set('url_generator', $url_generator);
+
+    \Drupal::setContainer($container);
+
+    $this->response = new Response($this->redirectUrl);
+
+    $this->assertInstanceOf('\Symfony\Component\HttpFoundation\RedirectResponse', $this->response->getResponse());
+  }
+
+}
