diff --git a/payment/src/Entity/Payment.php b/payment/src/Entity/Payment.php
index 089e7a2..da1e25b 100644
--- a/payment/src/Entity/Payment.php
+++ b/payment/src/Entity/Payment.php
@@ -344,7 +344,7 @@ class Payment extends ContentEntityBase implements PaymentInterface {
    */
   public static function preCreate(EntityStorageInterface $storage, array &$values) {
     $values += array(
-      'ownerId' => (int) \Drupal::currentUser()->id(),
+      'owner' => (int) \Drupal::currentUser()->id(),
     );
   }
 
@@ -354,7 +354,9 @@ class Payment extends ContentEntityBase implements PaymentInterface {
   public static function postLoad(EntityStorageInterface $storage, array &$entities) {
     /** @var \Drupal\payment\Entity\PaymentInterface[] $entities */
     foreach ($entities as $payment) {
-      $payment->getPaymentMethod()->setPayment($payment);
+      if ($payment->getPaymentMethod()) {
+        $payment->getPaymentMethod()->setPayment($payment);
+      }
     }
     /** @var \Drupal\payment\Entity\Payment\PaymentStorageInterface $storage */
     $storage->loadLineItems($entities);
@@ -441,4 +443,18 @@ class Payment extends ContentEntityBase implements PaymentInterface {
     $this->setStatuses($cloned_statuses);
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function createDuplicate() {
+    // @todo Remove this when https://www.drupal.org/node/2326377 is fixed.
+    $duplicate = parent::createDuplicate();
+
+    $duplicate->entityKeys = array(
+      'bundle' => $this->entityKeys['bundle'],
+    );
+
+    return $duplicate;
+  }
+
 }
diff --git a/payment/src/Entity/Payment/PaymentStorage.php b/payment/src/Entity/Payment/PaymentStorage.php
index 484ba4c..fd47ce9 100644
--- a/payment/src/Entity/Payment/PaymentStorage.php
+++ b/payment/src/Entity/Payment/PaymentStorage.php
@@ -138,7 +138,7 @@ class PaymentStorage extends ContentEntityDatabaseStorage implements PaymentStor
   /**
    * {@inheritdoc}
    */
-  protected function mapToStorageRecord(ContentEntityInterface $entity, $table_key = 'base_table') {
+  protected function mapToStorageRecord(ContentEntityInterface $entity, $table_name = NULL) {
     /** @var \Drupal\payment\Entity\PaymentInterface $payment */
     $payment = $entity;
 
diff --git a/payment/src/Plugin/Payment/Method/Basic.php b/payment/src/Plugin/Payment/Method/Basic.php
index aec1b48..07e127e 100644
--- a/payment/src/Plugin/Payment/Method/Basic.php
+++ b/payment/src/Plugin/Payment/Method/Basic.php
@@ -142,6 +142,13 @@ class Basic extends PaymentMethodBase implements ContainerFactoryPluginInterface
   /**
    * {@inheritdoc}
    */
+  public function isPaymentExecutionInterruptive() {
+    return FALSE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   protected function doExecutePayment() {
     $this->getPayment()->setStatus($this->paymentStatusManager->createInstance($this->getExecuteStatusId()));
     $this->getPayment()->save();
diff --git a/payment/src/Plugin/Payment/Method/PaymentMethodBase.php b/payment/src/Plugin/Payment/Method/PaymentMethodBase.php
index a6c177b..8071384 100644
--- a/payment/src/Plugin/Payment/Method/PaymentMethodBase.php
+++ b/payment/src/Plugin/Payment/Method/PaymentMethodBase.php
@@ -178,6 +178,14 @@ abstract class PaymentMethodBase extends PluginBase implements AccessInterface,
   /**
    * {@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.');
diff --git a/payment/src/Plugin/Payment/Method/PaymentMethodInterface.php b/payment/src/Plugin/Payment/Method/PaymentMethodInterface.php
index fb4c0b9..3e45743 100644
--- a/payment/src/Plugin/Payment/Method/PaymentMethodInterface.php
+++ b/payment/src/Plugin/Payment/Method/PaymentMethodInterface.php
@@ -49,6 +49,17 @@ interface PaymentMethodInterface extends PluginInspectionInterface, Configurable
   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.
+   *
+   * @return bool
+   */
+  public function isPaymentExecutionInterruptive();
+
+  /**
    * Gets the payment this payment method is for.
    *
    * @return \Drupal\payment\Entity\PaymentInterface
diff --git a/payment/src/Plugin/Payment/Method/Unavailable.php b/payment/src/Plugin/Payment/Method/Unavailable.php
index 6804e78..7ec1fa2 100644
--- a/payment/src/Plugin/Payment/Method/Unavailable.php
+++ b/payment/src/Plugin/Payment/Method/Unavailable.php
@@ -72,6 +72,13 @@ class Unavailable extends PluginBase implements PaymentMethodInterface {
   /**
    * {@inheritdoc}
    */
+  public function isPaymentExecutionInterruptive() {
+    return FALSE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function executePaymentAccess(AccountInterface $account) {
     return FALSE;
   }
diff --git a/payment/src/Plugin/Payment/MethodSelector/PaymentMethodSelectorBase.php b/payment/src/Plugin/Payment/MethodSelector/PaymentMethodSelectorBase.php
index 26383c0..780acbb 100644
--- a/payment/src/Plugin/Payment/MethodSelector/PaymentMethodSelectorBase.php
+++ b/payment/src/Plugin/Payment/MethodSelector/PaymentMethodSelectorBase.php
@@ -178,6 +178,9 @@ abstract class PaymentMethodSelectorBase extends PluginBase implements Container
    */
   public function setPayment(PaymentInterface $payment) {
     $this->payment = $payment;
+    if ($payment->getPaymentMethod()) {
+      $this->setPaymentMethod($payment->getPaymentMethod());
+    }
 
     return $this;
   }
diff --git a/payment/src/Plugin/Payment/MethodSelector/PaymentSelect.php b/payment/src/Plugin/Payment/MethodSelector/PaymentSelect.php
index b67c0c8..b4a097f 100644
--- a/payment/src/Plugin/Payment/MethodSelector/PaymentSelect.php
+++ b/payment/src/Plugin/Payment/MethodSelector/PaymentSelect.php
@@ -79,9 +79,6 @@ class PaymentSelect extends PaymentMethodSelectorBase {
 
     $form['container'] = array(
       '#available_payment_methods' => $available_payment_methods,
-      // The element does not actually have input, but we need the #name
-      // property to be populated by form API.
-      '#input' => TRUE,
       '#process' => array(array($this, $callback_method)),
       '#tree' => TRUE,
       '#type' => 'container',
@@ -164,7 +161,6 @@ class PaymentSelect extends PaymentMethodSelectorBase {
    * Implements a form #process callback.
    *
    * Builds the form elements for when there are no available payment methods.
-   *
    */
   public function buildNoAvailablePaymentMethods(array $element, FormStateInterface $form_state, array $form) {
     $element['select'] = array(
@@ -236,13 +232,15 @@ class PaymentSelect extends PaymentMethodSelectorBase {
     foreach ($payment_methods as $payment_method) {
       $payment_method_options[$payment_method->getPluginId()] = $payment_method->getPluginLabel();
     }
+    $root_element_parents = $root_element['#parents'];
+    $change_button_name = array_shift($root_element_parents) . ($root_element_parents ? '[' . implode('][', $root_element_parents) . ']' : NULL) . '[select][change]';
     $element['payment_method_id'] = array(
       '#ajax' => array(
         'callback' => array(get_class(), 'ajaxSubmitConfigurationForm'),
         'effect' => 'fade',
         'event' => 'change',
         'trigger_as' => array(
-          'name' => $root_element['#name'] . '[select][change]',
+          'name' => $change_button_name,
         ),
         'wrapper' => $this->getElementId(),
       ),
@@ -261,7 +259,7 @@ class PaymentSelect extends PaymentMethodSelectorBase {
         'class' => array('js-hide')
       ),
       '#limit_validation_errors' => array(array_merge($root_element['#parents'], array('select', 'payment_method_id'))),
-      '#name' => $root_element['#name'] . '[select][change]',
+      '#name' => $change_button_name,
       '#submit' => array(array($this, 'rebuildForm')),
       '#type' => 'submit',
       '#value' => $this->t('Choose payment method'),
diff --git a/payment/tests/src/Plugin/Payment/MethodSelector/PaymentMethodSelectorBaseUnitTest.php b/payment/tests/src/Plugin/Payment/MethodSelector/PaymentMethodSelectorBaseUnitTest.php
index 9462b88..1258825 100644
--- a/payment/tests/src/Plugin/Payment/MethodSelector/PaymentMethodSelectorBaseUnitTest.php
+++ b/payment/tests/src/Plugin/Payment/MethodSelector/PaymentMethodSelectorBaseUnitTest.php
@@ -126,11 +126,24 @@ class PaymentMethodSelectorBaseUnitTest extends UnitTestCase {
    * @covers ::getPayment
    */
   public function testGetPayment() {
-    $payment = $this->getMockBuilder('\Drupal\payment\Entity\Payment')
+    $payment_method = $this->getMock('\Drupal\payment\Plugin\Payment\Method\PaymentMethodInterface');
+
+    $payment_1 = $this->getMockBuilder('\Drupal\payment\Entity\Payment')
+      ->disableOriginalConstructor()
+      ->getMock();
+    $payment_2 = $this->getMockBuilder('\Drupal\payment\Entity\Payment')
       ->disableOriginalConstructor()
       ->getMock();
-    $this->assertSame($this->paymentMethodSelectorPlugin, $this->paymentMethodSelectorPlugin->setPayment($payment));
-    $this->assertSame($payment, $this->paymentMethodSelectorPlugin->getPayment());
+    $payment_2->expects($this->atLeastOnce())
+      ->method('getPaymentMethod')
+      ->willReturn($payment_method);
+
+
+    $this->assertSame($this->paymentMethodSelectorPlugin, $this->paymentMethodSelectorPlugin->setPayment($payment_1));
+    $this->assertSame($payment_1, $this->paymentMethodSelectorPlugin->getPayment());
+    $this->paymentMethodSelectorPlugin->setPayment($payment_2);
+    $this->assertSame($payment_2, $this->paymentMethodSelectorPlugin->getPayment());
+    $this->assertSame($payment_method, $this->paymentMethodSelectorPlugin->getPaymentMethod());
   }
 
   /**
diff --git a/payment/tests/src/Plugin/Payment/MethodSelector/PaymentSelectUnitTest.php b/payment/tests/src/Plugin/Payment/MethodSelector/PaymentSelectUnitTest.php
index 2cbf12a..8a469d8 100644
--- a/payment/tests/src/Plugin/Payment/MethodSelector/PaymentSelectUnitTest.php
+++ b/payment/tests/src/Plugin/Payment/MethodSelector/PaymentSelectUnitTest.php
@@ -145,9 +145,6 @@ class PaymentSelectUnitTest extends UnitTestCase {
     $expected_build = array(
       'container' => array(
         '#available_payment_methods' => array(),
-        // The element does not actually have input, but we need the #name
-        // property to be populated by form API.
-        '#input' => TRUE,
         '#process' => array(array($this->paymentMethodSelector, 'buildNoAvailablePaymentMethods')),
         '#tree' => TRUE,
         '#type' => 'container',
@@ -188,9 +185,6 @@ class PaymentSelectUnitTest extends UnitTestCase {
     $expected_build = array(
       'container' => array(
         '#available_payment_methods' => array($payment_method),
-        // The element does not actually have input, but we need the #name
-        // property to be populated by form API.
-        '#input' => TRUE,
         '#process' => array(array($payment_method_selector, 'buildOneAvailablePaymentMethod')),
         '#tree' => TRUE,
         '#type' => 'container',
@@ -232,9 +226,6 @@ class PaymentSelectUnitTest extends UnitTestCase {
     $expected_build = array(
       'container' => array(
         '#available_payment_methods' => array($payment_method_a, $payment_method_b),
-        // The element does not actually have input, but we need the #name
-        // property to be populated by form API.
-        '#input' => TRUE,
         '#process' => array(array($payment_method_selector, 'buildMultipleAvailablePaymentMethods')),
         '#tree' => TRUE,
         '#type' => 'container',
@@ -461,7 +452,6 @@ class PaymentSelectUnitTest extends UnitTestCase {
     $this->paymentMethodSelector->setPaymentMethod($payment_method);
 
     $element = array(
-      '#name' => $this->randomMachineName(),
       '#parents' => array('foo', 'bar'),
     );
     $form_state = $this->getMock('\Drupal\Core\Form\FormStateInterface');
@@ -474,7 +464,7 @@ class PaymentSelectUnitTest extends UnitTestCase {
           'effect' => 'fade',
           'event' => 'change',
           'trigger_as' => array(
-            'name' => $element['#name'] . '[select][change]',
+            'name' => 'foo[bar][select][change]',
           ),
           'wrapper' => $get_element_id_method->invokeArgs($this->paymentMethodSelector, array($form_state)),
         ),
@@ -495,7 +485,7 @@ class PaymentSelectUnitTest extends UnitTestCase {
           'class' => array('js-hide')
         ),
         '#limit_validation_errors' => array(array('foo', 'bar', 'select', 'payment_method_id')),
-        '#name' => $element['#name'] . '[select][change]',
+        '#name' => 'foo[bar][select][change]',
         '#submit' => array(array($this->paymentMethodSelector, 'rebuildForm')),
         '#type' => 'submit',
         '#value' => 'Choose payment method',
diff --git a/payment_form/tests/src/Plugin/Field/FieldFormatter/PaymentFormUnitTest.php b/payment_form/tests/src/Plugin/Field/FieldFormatter/PaymentFormUnitTest.php
index c728399..ff96c6a 100644
--- a/payment_form/tests/src/Plugin/Field/FieldFormatter/PaymentFormUnitTest.php
+++ b/payment_form/tests/src/Plugin/Field/FieldFormatter/PaymentFormUnitTest.php
@@ -278,9 +278,8 @@ class PaymentFormUnitTest extends UnitTestCase {
       'token' => $this->randomMachineName(),
     );
 
-    $method = new \ReflectionMethod($this->fieldFormatter, 'viewElementsPostRenderCache');
-    $method->setAccessible(TRUE);
-    $this->assertSame($element, $method->invoke($this->fieldFormatter, $element, $context));
+    $field_formatter = $this->fieldFormatter;
+    $this->assertSame($element, $field_formatter::viewElementsPostRenderCache($element, $context));
   }
 
 }
diff --git a/payment_reference/css/payment_reference.css b/payment_reference/css/payment_reference.css
new file mode 100644
index 0000000..582b369
--- /dev/null
+++ b/payment_reference/css/payment_reference.css
@@ -0,0 +1,3 @@
+.payment-reference-hidden {
+  display: none !important;
+}
diff --git a/payment_reference/js/payment_reference.js b/payment_reference/js/payment_reference.js
index e42e3c6..b178c9b 100644
--- a/payment_reference/js/payment_reference.js
+++ b/payment_reference/js/payment_reference.js
@@ -1,6 +1,6 @@
-(function($) {
+(function ($, Drupal, drupalSettings) {
   /**
-   * Refresh this window's opener's payment references.
+   * Refreshes this window's opener's payment references.
    */
   $(document).ready(function() {
     if (window.opener && window.opener.Drupal.PaymentReferenceRefreshButtons) {
@@ -9,33 +9,45 @@
   });
 
   /**
-   * Convert "close this window" messages to links.
+   * Converts "close this window" messages to links.
    */
   Drupal.behaviors.PaymentReferenceWindowCloseLink = {
     attach: function(context) {
-      $('span.payment_reference-window-close').each(function() {
-        $(this).replaceWith('<a href="#" class="payment_reference-window-close">' + this.innerHTML + '</a>');
-      });
-      $('a.payment_reference-window-close').bind('click', function() {
-        window.opener.focus();
-        window.close();
-      });
+      if (window.opener) {
+        $('span.payment_reference-window-close').each(function() {
+          $(this).replaceWith('<a href="#" class="payment_reference-window-close">' + this.innerHTML + '</a>');
+        });
+        $('a.payment_reference-window-close').bind('click', function() {
+          window.opener.focus();
+          window.close();
+        });
+      }
     }
   }
 
   /**
-   * Refresh all payment references.
+   * Binds a listener on dialog creation to handle the payment completion link.
+   */
+  $(window).on('dialog:aftercreate', function (e, dialog, $element, settings) {
+    $element.on('click.dialog', '.payment_reference-complete-payment-link', function (e) {
+      dialog.close('complete-payment');
+    });
+  });
+
+  /**
+   * Refreshes all payment references.
    */
   Drupal.PaymentReferenceRefreshButtons = function() {
     $('.payment_reference-refresh-button').each(function() {
-      if (!Drupal.settings.PaymentReferencePaymentAvailable[Drupal.settings.ajax[this.id].wrapper]) {
+      if (!drupalSettings.PaymentReferencePaymentAvailable[drupalSettings.ajax[this.id].wrapper]) {
         $(this).trigger('mousedown');
       }
     });
   }
 
   /**
-   * Set an interval to refresh all payment references.
+   * Sets an interval to refresh all payment references.
    */
   setInterval(Drupal.PaymentReferenceRefreshButtons, 30000);
-})(jQuery);
\ No newline at end of file
+
+})(jQuery, Drupal, drupalSettings);
diff --git a/payment_reference/payment_reference.module b/payment_reference/payment_reference.module
index 2cc8475..0705824 100644
--- a/payment_reference/payment_reference.module
+++ b/payment_reference/payment_reference.module
@@ -16,36 +16,6 @@ use Drupal\payment_reference\PaymentReference;
 use Symfony\Cmf\Component\Routing\RouteObjectInterface;
 
 /**
- * Implements hook_element_info().
- */
-function payment_reference_element_info() {
-  $elements['payment_reference'] = array(
-    // The bundle of the entity the element is used for.
-    '#bundle' => NULL,
-    // The ID of a payment as the default value.
-    '#default_value' => NULL,
-    // The ID of the entity type the element is used for.
-    '#entity_type_id' => NULL,
-    // The name of the field the element is used for.
-    '#field_name' => NULL,
-    '#input' => TRUE,
-    // The ID of the account that must own the payment.
-    '#owner_id' => NULL,
-    // Values are arrays with two keys:
-    // - plugin_id: the ID of the line item plugin instance.
-    // - plugin_configuration: the configuration of the line item plugin
-    //   instance.
-    '#payment_line_items_data' => array(),
-    '#payment_currency_code' => '',
-    '#process' => array(array('Drupal\payment_reference\Element\PaymentReference', 'process')),
-    '#theme_wrappers' => array('form_element'),
-    '#value_callback' => 'payment_reference_element_payment_reference_value',
-  );
-
-  return $elements;
-}
-
-/**
  * Implements hook_page_alter().
  */
 function payment_reference_page_alter(&$page) {
@@ -58,15 +28,6 @@ function payment_reference_page_alter(&$page) {
 }
 
 /**
- * Implements hook_entity_type_alter().
- */
-function payment_reference_entity_type_alter(&$entity_types) {
-  /** @var \Drupal\Core\Entity\EntityTypeInterface $entity_type */
-  $entity_type = $entity_types['payment'];
-  $entity_type->setFormClass('payment_reference', 'Drupal\payment_reference\Entity\Payment\PaymentForm');
-}
-
-/**
  * Implements hook_ENTITY_TYPE_delete().
  */
 function payment_reference_field_config_delete(FieldStorageConfigInterface $field) {
@@ -89,7 +50,7 @@ function payment_reference_payment_insert(PaymentInterface $payment) {
   if ($payment->bundle() == 'payment_reference') {
     /** @var \Drupal\payment_reference\Plugin\Payment\Type\PaymentReference $payment_type */
     $payment_type = $payment->getPaymentType();
-    PaymentReference::queue()->save($payment_type->getFieldId(), $payment->id());
+    PaymentReference::queue()->save($payment_type->getEntityTypeId() . '.' . $payment_type->getBundle() . '.' . $payment_type->getFieldName(), $payment->id());
   }
 }
 
@@ -108,16 +69,3 @@ function payment_reference_entity_field_access($operation, FieldDefinitionInterf
     return (bool) $account->id();
   }
 }
-
-/**
- * Implements form #value_callback callback.
- *
- * @todo Move this to \Drupal\payment_reference\Element\PaymentReference once
- * https://drupal.org/node/2040559 has been fixed.
- */
-function payment_reference_element_payment_reference_value(array $element, $input, \Drupal\Core\Form\FormStateInterface $form_state) {
-  $payment_ids = PaymentReference::queue()->loadPaymentIds($element['#entity_type_id'] . '.' . $element['#bundle'] . '.' . $element['#field_name'], $element['#owner_id']);
-  if ($payment_ids) {
-    return reset($payment_ids);
-  }
-}
diff --git a/payment_reference/payment_reference.routing.yml b/payment_reference/payment_reference.routing.yml
index 394d6d2..4f4766f 100644
--- a/payment_reference/payment_reference.routing.yml
+++ b/payment_reference/payment_reference.routing.yml
@@ -1,14 +1,15 @@
 payment_reference.pay:
-  path: '/payment_reference/pay/{entity_type_id}/{bundle}/{field_name}'
+  path: '/payment_reference/pay/{storage_key}'
   defaults:
-    _content: '\Drupal\payment_reference\Controller\PaymentReference::pay'
-    _title_callback: '\Drupal\payment_reference\Controller\PaymentReference::payLabel'
+    _content: '\Drupal\payment_reference\Controller\Pay::execute'
   requirements:
-    _custom_access: '\Drupal\payment_reference\Controller\PaymentReference::payAccess'
+    _custom_access: '\Drupal\payment_reference\Controller\Pay::access'
+    _csrf_token: 'TRUE'
 payment_reference.resume_context:
   path: '/payment_reference/resume/{payment}'
   defaults:
-    _content: '\Drupal\payment_reference\Controller\PaymentReference::resumeContext'
-    _title_callback: '\Drupal\payment_reference\Controller\PaymentReference::resumeContextLabel'
+    _content: '\Drupal\payment_reference\Controller\ResumeContext::execute'
+    _title_callback: '\Drupal\payment_reference\Controller\ResumeContext::title'
   requirements:
-    _custom_access: '\Drupal\payment_reference\Controller\PaymentReference::resumeContextAccess'
+    _custom_access: '\Drupal\payment_reference\Controller\ResumeContext::access'
+    _csrf_token: 'TRUE'
diff --git a/payment_reference/payment_reference.services.yml b/payment_reference/payment_reference.services.yml
index 4bd7840..34646c1 100644
--- a/payment_reference/payment_reference.services.yml
+++ b/payment_reference/payment_reference.services.yml
@@ -1,4 +1,7 @@
 services:
+  payment_reference.payment_factory:
+    class: Drupal\payment_reference\PaymentFactory
+    arguments: ['@entity.manager', '@plugin.manager.payment.line_item']
   payment_reference.queue:
     arguments: ['payment_reference', '@database', '@event_dispatcher', '@plugin.manager.payment.status']
-    class: Drupal\payment\Queue
\ No newline at end of file
+    class: Drupal\payment\Queue
diff --git a/payment_reference/src/Controller/Pay.php b/payment_reference/src/Controller/Pay.php
new file mode 100644
index 0000000..469bfb6
--- /dev/null
+++ b/payment_reference/src/Controller/Pay.php
@@ -0,0 +1,73 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\payment_reference\Controller\Pay.
+ */
+
+namespace Drupal\payment_reference\Controller;
+
+use Drupal\Core\Access\AccessInterface;
+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, AccessInterface {
+
+  /**
+   * 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.
+   */
+  public function execute($storage_key) {
+    $storage = $this->keyValueFactory->get('payment.payment_method_selector.payment_select');
+    /** @var \Drupal\payment\Entity\PaymentInterface $payment */
+    $payment = $storage->get($storage_key);
+    $storage->delete($storage_key);
+    $payment->execute();
+    $payment->getPaymentType()->resumeContext();
+  }
+
+  /**
+   * Checks if the user has access to make a payment.
+   *
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   * @param string $storage_key
+   *   The storage key with which the payment can be loaded.
+   *
+   * @return string
+   */
+  public function access(Request $request, $storage_key) {
+    return $this->keyValueFactory->get('payment.payment_method_selector.payment_select')->has($storage_key) ? static::ALLOW : static::DENY;
+  }
+
+}
diff --git a/payment_reference/src/Controller/PaymentReference.php b/payment_reference/src/Controller/PaymentReference.php
deleted file mode 100644
index f8d14b0..0000000
--- a/payment_reference/src/Controller/PaymentReference.php
+++ /dev/null
@@ -1,239 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\payment_reference\Controller\PaymentReference.
- */
-
-namespace Drupal\payment_reference\Controller;
-
-use Drupal\Core\Access\AccessInterface;
-use Drupal\Core\Controller\ControllerBase;
-use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
-use Drupal\Core\Entity\EntityFormBuilderInterface;
-use Drupal\Core\Entity\EntityManagerInterface;
-use Drupal\Core\Session\AccountInterface;
-use Drupal\Core\StringTranslation\TranslationInterface;
-use Drupal\payment\Entity\PaymentInterface;
-use Drupal\payment\Plugin\Payment\LineItem\PaymentLineItemManagerInterface;
-use Drupal\payment\QueueInterface;
-use Symfony\Component\DependencyInjection\ContainerInterface;
-use Symfony\Component\HttpFoundation\Request;
-
-/**
- * Returns responses for payment reference routes.
- */
-class PaymentReference extends ControllerBase implements ContainerInjectionInterface, AccessInterface {
-
-  /**
-   * The payment line item manager.
-   *
-   * @var \Drupal\payment\Plugin\Payment\LineItem\PaymentLineItemManagerInterface
-   */
-  protected $paymentLineItemManager;
-
-  /**
-   * The payment reference queue.
-   *
-   * @var \Drupal\payment\QueueInterface
-   */
-  protected $queue;
-
-  /**
-   * Constructs a new class instance.
-   *
-   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
-   * @param \Drupal\Core\Session\AccountInterface $current_user
-   * @param \Drupal\Core\Entity\EntityFormBuilderInterface $entity_form_builder
-   * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
-   * @param \Drupal\payment\Plugin\Payment\LineItem\PaymentLineItemManagerInterface $payment_line_item_manager
-   * @param \Drupal\payment\QueueInterface $queue
-   */
-  public function __construct(EntityManagerInterface $entity_manager, AccountInterface $current_user, EntityFormBuilderInterface $entity_form_builder, TranslationInterface $string_translation, PaymentLineItemManagerInterface $payment_line_item_manager, QueueInterface $queue) {
-    $this->currentUser = $current_user;
-    $this->entityManager = $entity_manager;
-    $this->entityFormBuilder = $entity_form_builder;
-    $this->paymentLineItemManager = $payment_line_item_manager;
-    $this->queue = $queue;
-    $this->stringTranslation = $string_translation;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function create(ContainerInterface $container) {
-    return new static(
-      $container->get('entity.manager'),
-      $container->get('current_user'),
-      $container->get('entity.form_builder'),
-      $container->get('string_translation'),
-      $container->get('plugin.manager.payment.line_item'),
-      $container->get('payment_reference.queue')
-    );
-  }
-
-  /**
-   * Returns a payment page.
-   *
-   * @param string $entity_type_id
-   *   The ID of the entity type the payment reference field is attached to.
-   * @param string $bundle
-   *   The bundle of the entity type the payment reference field is attached to.
-   * @param string $field_name
-   *   The name of the payment reference field.
-   *
-   * @return array
-   *   A render array.
-   */
-  public function pay($entity_type_id, $bundle, $field_name) {
-    /** @var \Drupal\payment\Entity\PaymentInterface $payment */
-    $payment = $this->entityManager
-      ->getStorage('payment')
-      ->create(array(
-        'bundle' => 'payment_reference',
-      ));
-
-    $field_definitions = $this->entityManager->getFieldDefinitions($entity_type_id, $bundle);
-    $field_definition = $field_definitions[$field_name];
-    $payment->setCurrencyCode($field_definition->getSetting('currency_code'));
-    /** @var \Drupal\payment_reference\Plugin\Payment\Type\PaymentReference $payment_type */
-    $payment_type = $payment->getPaymentType();
-    $payment_type->setEntityTypeId($entity_type_id);
-    $payment_type->setBundle($bundle);
-    $payment_type->setFieldName($field_name);
-    foreach ($field_definition->getSetting('line_items_data') as $line_item_data) {
-      $line_item = $this->paymentLineItemManager->createInstance($line_item_data['plugin_id'], $line_item_data['plugin_configuration']);
-      $payment->setLineItem($line_item);
-    }
-
-    return $this->entityFormBuilder->getForm($payment, 'payment_reference');
-  }
-
-  /**
-   * Checks if the user has access to add a payment for a field instance.
-   *
-   * @param \Symfony\Component\HttpFoundation\Request $request
-   * @param string $entity_type_id
-   *   The ID of the entity type the payment reference field is attached to.
-   * @param string $bundle
-   *   The bundle of the entity type the payment reference field is attached to.
-   * @param string $field_name
-   *   The name of the payment reference field.
-   *
-   * @return string
-   */
-  public function payAccess(Request $request, $entity_type_id, $bundle, $field_name) {
-    if ($this->fieldExists($entity_type_id, $bundle, $field_name)) {
-      $field_definitions = $this->entityManager->getFieldDefinitions($entity_type_id, $bundle);
-      $access_controller = $this->entityManager->getAccessControlHandler('payment');
-      $payment_ids = $this->queue->loadPaymentIds($entity_type_id . '.' . $field_name, $this->currentUser->id());
-      // Only grant access if the current user does not already have payments
-      // available for this instance, and they have the permission to create them.
-      if (empty($payment_ids) && $access_controller->createAccess('payment_reference') && $access_controller->fieldAccess('edit', $field_definitions[$field_name])) {
-        return static::ALLOW;
-      }
-    }
-    return static::DENY;
-  }
-
-  /**
-   * Returns the label of a field.
-   *
-   * @param string $entity_type_id
-   *   The ID of the entity type the payment reference field is attached to.
-   * @param string $bundle
-   *   The bundle of the entity type the payment reference field is attached to.
-   * @param string $field_name
-   *   The name of the payment reference field.
-   *
-   * @return string
-   */
-  public function payLabel($entity_type_id, $bundle, $field_name) {
-    $field_definitions = $this->entityManager->getFieldDefinitions($entity_type_id, $bundle);
-
-    return $field_definitions[$field_name]->getLabel();
-  }
-
-  /**
-   * Returns the label of a field instance.
-   *
-   * @param \Drupal\payment\Entity\PaymentInterface $payment
-   *
-   * @return string
-   */
-  public function resumeContextLabel(PaymentInterface $payment) {
-    /** @var \Drupal\payment_reference\Plugin\Payment\Type\PaymentReference $payment_type */
-    $payment_type = $payment->getPaymentType();
-    $field_definitions = $this->entityManager->getFieldDefinitions($payment_type->getEntityTypeId(), $payment_type->getBundle());
-    $field_definition = $field_definitions[$payment_type->getFieldName()];
-
-    return $field_definition->getLabel();
-  }
-
-  /**
-   * Resumes the payment context.
-   *
-   * @param \Drupal\payment\Entity\PaymentInterface $payment
-   *
-   * @return array
-   *   A renderable array.
-   */
-  public function resumeContext(PaymentInterface $payment) {
-    $message = $this->t('You can now <span class="payment_reference-window-close">close this window</span>.');
-    if ($payment->access('view')) {
-      $message = $this->t('Your payment is %status.', array(
-        '%status' => $payment->getStatus()->getLabel(),
-      )) . ' ' . $message;
-    }
-
-    return array(
-      '#type' => 'markup',
-      '#markup' => $message,
-      '#attached' => array(
-        'js' => array(drupal_get_path('module', 'payment_reference') . '/js/payment_reference.js'),
-      ),
-    );
-  }
-
-  /**
-   * Checks if the user has access to resume a payment's context.
-   *
-   * @param \Drupal\payment\Entity\PaymentInterface $payment
-   *
-   * @return string
-   */
-  public function resumeContextAccess(PaymentInterface $payment) {
-    /** @var \Drupal\payment_reference\Plugin\Payment\Type\PaymentReference $payment_type */
-    $payment_type = $payment->getPaymentType();
-
-    return $this->fieldExists($payment_type->getEntityTypeId(), $payment_type->getBundle(), $payment_type->getFieldName()) ? static::ALLOW : static::DENY;
-  }
-
-  /**
-   * Checks if a field exists.
-   *
-   * @param string $entity_type_id
-   *   The ID of the entity type the payment reference field is attached to.
-   * @param string $bundle
-   *   The bundle of the entity type the payment reference field is attached to.
-   * @param string $field_name
-   *   The name of the payment reference field.
-   *
-   * @return string
-   */
-  protected function fieldExists($entity_type_id, $bundle, $field_name) {
-    if (!$this->entityManager->hasDefinition($entity_type_id)) {
-      return FALSE;
-    }
-    $bundle_info = $this->entityManager->getBundleInfo($entity_type_id);
-    if (!isset($bundle_info[$bundle])) {
-      return FALSE;
-    }
-    $field_definitions = $this->entityManager->getFieldDefinitions($entity_type_id, $bundle);
-    if (!isset($field_definitions[$field_name])) {
-      return FALSE;
-    }
-    return TRUE;
-  }
-
-}
diff --git a/payment_reference/src/Controller/ResumeContext.php b/payment_reference/src/Controller/ResumeContext.php
new file mode 100644
index 0000000..31c55f8
--- /dev/null
+++ b/payment_reference/src/Controller/ResumeContext.php
@@ -0,0 +1,90 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\payment_reference\Controller\ResumeContext.
+ */
+
+namespace Drupal\payment_reference\Controller;
+
+use Drupal\Core\Access\AccessInterface;
+use Drupal\Core\Controller\ControllerBase;
+use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\Core\StringTranslation\TranslationInterface;
+use Drupal\payment\Entity\PaymentInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Handles the "resume context" route.
+ */
+class ResumeContext extends ControllerBase implements ContainerInjectionInterface, AccessInterface {
+
+  /**
+   * Constructs a new instance.
+   *
+   * @param \Drupal\Core\Session\AccountInterface $current_user
+   * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
+   */
+  public function __construct(AccountInterface $current_user, TranslationInterface $string_translation) {
+    $this->currentUser = $current_user;
+    $this->stringTranslation = $string_translation;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static($container->get('current_user'), $container->get('string_translation'));
+  }
+
+  /**
+   * Resumes the payment context.
+   *
+   * @param \Drupal\payment\Entity\PaymentInterface $payment
+   *
+   * @return array
+   *   A renderable array.
+   */
+  public function execute(PaymentInterface $payment) {
+    $message = $this->t('You can now <span class="payment_reference-window-close">close this window</span>.');
+    if ($payment->access('view')) {
+      $message = $this->t('Your payment is %status.', array(
+          '%status' => $payment->getStatus()->getLabel(),
+        )) . ' ' . $message;
+    }
+
+    return array(
+      '#type' => 'markup',
+      '#markup' => $message,
+      '#attached' => array(
+        'js' => array(drupal_get_path('module', 'payment_reference') . '/js/payment_reference.js'),
+      ),
+    );
+  }
+
+  /**
+   * Returns the label of a field instance.
+   *
+   * @param \Drupal\payment\Entity\PaymentInterface $payment
+   *
+   * @return string
+   */
+  public function title(PaymentInterface $payment) {
+    return $payment->label();
+  }
+
+  /**
+   * Checks if the user has access to resume a payment's context.
+   *
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   * @param \Drupal\payment\Entity\PaymentInterface $payment
+   *
+   * @return string
+   */
+  public function access(Request $request, PaymentInterface $payment) {
+    return $payment->getPaymentType()->resumeContextAccess($this->currentUser) ? static::ALLOW : static::DENY;
+  }
+
+}
diff --git a/payment_reference/src/Element/PaymentReference.php b/payment_reference/src/Element/PaymentReference.php
index 85ef8d6..d2f86be 100644
--- a/payment_reference/src/Element/PaymentReference.php
+++ b/payment_reference/src/Element/PaymentReference.php
@@ -7,140 +7,88 @@
 
 namespace Drupal\payment_reference\Element;
 
-use Drupal\Core\Form\FormStateInterface;
-use Drupal\payment\Entity\Payment;
-use Drupal\payment\Payment as PaymentServiceWrapper;
-use Drupal\payment_reference\PaymentReference as PaymentReferenceServiceWrapper;
+use Drupal\Component\Utility\Random;
+use Drupal\Core\Datetime\DateFormatter;
+use Drupal\Core\Entity\EntityStorageInterface;
+use Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface;
+use Drupal\Core\StringTranslation\TranslationInterface;
+use Drupal\Core\Utility\LinkGeneratorInterface;
+use Drupal\payment\Plugin\Payment\MethodSelector\PaymentMethodSelectorManagerInterface;
+use Drupal\payment\QueueInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\HttpFoundation\RequestStack;
 
 /**
- * Provides form callbacks for the payment_reference form element.
+ * Provides a payment reference element.
+ *
+ * @FormElement("payment_reference")
  */
-class PaymentReference {
+class PaymentReference extends PaymentReferenceBase {
 
   /**
-   * Implements form #process callback.
+   * The payment queue.
+   *
+   * @var \Drupal\payment\QueueInterface
    */
-  public static function process(array $element, FormStateInterface $form_state, array $form) {
-    // Validate the element's configuration.
-    if (!is_string($element['#bundle'])) {
-      throw new \InvalidArgumentException('#bundle must be a string, but ' . gettype($element['#bundle']) . ' was given.');
-    }
-    if (!is_int($element['#default_value']) && !is_null($element['#default_value'])) {
-      throw new \InvalidArgumentException('The default value must be an integer or NULL, but ' . gettype($element['#default_value']) . ' was given.');
-    }
-    if (!is_string($element['#entity_type_id'])) {
-      throw new \InvalidArgumentException('#entity_type_id must be a string, but ' . gettype($element['#entity_type_id']) . ' was given.');
-    }
-    if (!is_string($element['#field_name'])) {
-      throw new \InvalidArgumentException('#field_name must be a string, but ' . gettype($element['#field_name']) . ' was given.');
-    }
-    if (!is_int($element['#owner_id'])) {
-      throw new \InvalidArgumentException('The owner ID must be an integer, but ' . gettype($element['#owner_id']) . ' was given.');
-    }
-    if (!is_string($element['#payment_currency_code'])) {
-      throw new \InvalidArgumentException('The currency code must be a string, but ' . gettype($element['#payment_currency_code']) . ' was given.');
-    }
+  protected $paymentQueue;
 
-    // Find the default payment to use.
-    $payment_id = $element['#default_value'];
-    if (!$payment_id) {
-      $payment_ids = PaymentReferenceServiceWrapper::queue()->loadPaymentIds($element['#entity_type_id'] . '.' . $element['#bundle'] . '.' . $element['#field_name'], $element['#owner_id']);
-      $payment_id = reset($payment_ids);
-    }
-    // Form API considers an empty string to be an empty value, but not NULL.
-    $element['#value'] = $payment_id ? $payment_id : '';
+  /**
+   * The temporary payment storage.
+   *
+   * @var \Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface
+   */
+  protected $temporaryPaymentStorage;
+
+  /**
+   * Creates a new 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 mixed $plugin_definition
+   *   The plugin implementation definition.
+   * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
+   * @param \Drupal\Core\Entity\EntityStorageInterface $payment_storage
+   * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
+   * @param \Drupal\Core\DateTime\DateFormatter $date_formatter
+   * @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) {
+    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;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    /** @var \Drupal\Core\Entity\EntityManagerInterface $entity_manager */
+    $entity_manager = $container->get('entity.manager');
 
-    // AJAX.
-    $ajax_wrapper_id = drupal_html_id('payment_reference-' . $element['#name']);
-    $element['#prefix'] = '<div id="' . $ajax_wrapper_id . '">';
-    $element['#suffix'] = '</div>';
-    $element['#attached']['js'] = array(
-      drupal_get_path('module', 'payment_reference') . '/js/payment_reference.js',
-      array(
-      'type' => 'setting',
-        'data' => array(
-          'PaymentReferencePaymentAvailable' => array(
-            $ajax_wrapper_id => !empty($payment_id),
-          ),
-        ),
-      ),
-    );
+    /** @var \Drupal\Core\KeyValueStore\KeyValueExpirableFactoryInterface $key_value_expirable */
+    $key_value_expirable = $container->get('keyvalue.expirable');
 
-    // Payment information.
-    $element['payment'] = array(
-      '#empty' => \Drupal::translation()->translate('There are no line items.'),
-      '#header' => array(\Drupal::translation()->translate('Amount'), \Drupal::translation()->translate('Status'), \Drupal::translation()->translate('Last updated')),
-      '#type' => 'table',
-    );
-    if (!$payment_id) {
-      $amount = 0;
-      foreach ($element['#payment_line_items_data'] as $line_item_data) {
-        $line_item = PaymentServiceWrapper::lineItemManager()->createInstance($line_item_data['plugin_id'], $line_item_data['plugin_configuration']);
-        $amount += $line_item->getTotalAmount();
-      }
-      /** @var \Drupal\currency\Entity\CurrencyInterface $currency */
-      $currency = entity_load('currency', $element['#payment_currency_code']);
-      $element['payment'][0]['amount'] = array(
-        '#markup' => $currency->formatAmount($amount),
-      );
-      $element['payment'][0]['add'] = array(
-        '#attributes' => array(
-          'colspan' => 2,
-        ),
-        '#markup' => \Drupal::translation()->translate('<a href="@url" target="_blank">Add a new payment</a> (opens in a new window)', array(
-          '@url' => \Drupal::urlGenerator()->generateFromRoute('payment_reference.pay', array(
-              'bundle' => $element['#bundle'],
-              'entity_type_id' => $element['#entity_type_id'],
-              'field_name' => $element['#field_name'],
-          )),
-        )),
-      );
-    }
-    else {
-      /** @var \Drupal\payment\Entity\PaymentInterface $payment */
-      $payment = Payment::load($payment_id);
-      /** @var \Drupal\currency\Entity\CurrencyInterface $currency */
-      $currency = entity_load('currency', $payment->getCurrencyCode());
-      $status = $payment->getStatus();
-      $status_definition = $status->getPluginDefinition();
-      $element['payment'][0]['amount'] = array(
-        '#markup' => $currency->formatAmount($payment->getAmount()),
-      );
-      $element['payment'][0]['status'] = array(
-        '#markup' => $status_definition['label'],
-      );
-      $element['payment'][0]['updated'] = array(
-        '#markup' => format_date($status->getCreated()),
-      );
-      if ($payment->access('view')) {
-        $uri = $payment->urlInfo();
-        $element['payment']['header'][] = \Drupal::translation()->translate('Operations');
-        $element['payment'][0]['view'] = \Drupal::translation()->translate('<a href="@url" target="_blank">View payment details</a> (opens in a new window)', array(
-          '@url' => url($uri['path'], $uri['options']),
-        ));
-      }
-    }
+    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_method_selector.payment_select'), $container->get('payment_reference.queue'));
+  }
 
-    // Refresh button.
-    $element['refresh'] = array(
-      '#type' => 'submit',
-      '#value' => \Drupal::translation()->translate('Re-check available payments'),
-      '#submit' => isset($element['#submit']) ? $element['#submit'] : array(),
-      '#limit_validation_errors' => array(),
-      '#ajax' => array(
-        'callback' => 'payment_reference_form_process_payment_reference_ajax_callback',
-        'effect' => 'fade',
-        'event' => 'mousedown',
-        'wrapper' => $ajax_wrapper_id,
-        'progress' => array(),
-      ),
-      '#attributes' => array(
-        'class' => array('payment_reference-refresh-button', 'js-hide'),
-      ),
-      '#name' => $element['#name'] . '_refresh',
-    );
-    $form_state->set($element['refresh']['#name'], $element['#parents']);
+  /**
+   * {@inheritdoc}
+   */
+  protected function getPaymentQueue() {
+    return $this->paymentQueue;
+  }
 
-    return $element;
+  /**
+   * {@inheritdoc}
+   */
+  protected function getTemporaryPaymentStorage() {
+    return $this->temporaryPaymentStorage;
   }
+
 }
diff --git a/payment_reference/src/Element/PaymentReferenceBase.php b/payment_reference/src/Element/PaymentReferenceBase.php
new file mode 100644
index 0000000..5755f43
--- /dev/null
+++ b/payment_reference/src/Element/PaymentReferenceBase.php
@@ -0,0 +1,609 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\payment_reference\Element\PaymentReferenceBase.
+ */
+
+namespace Drupal\payment_reference\Element;
+
+use Drupal\Component\Utility\Html;
+use Drupal\Component\Utility\NestedArray;
+use Drupal\Component\Utility\Random;
+use Drupal\Core\Ajax\AjaxResponse;
+use Drupal\Core\Ajax\OpenModalDialogCommand;
+use Drupal\Core\Ajax\ReplaceCommand;
+use Drupal\Core\Datetime\DateFormatter;
+use Drupal\Core\Entity\EntityStorageInterface;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Drupal\Core\Render\Element\FormElementInterface;
+use Drupal\Core\Render\Element;
+use Drupal\Core\Routing\UrlGeneratorTrait;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Drupal\Core\StringTranslation\TranslationInterface;
+use Drupal\Core\Utility\LinkGeneratorInterface;
+use Drupal\payment\Entity\Payment;
+use Drupal\payment\Entity\PaymentInterface;
+use Drupal\payment\Plugin\Payment\MethodSelector\PaymentMethodSelectorManagerInterface;
+use Symfony\Component\HttpFoundation\RequestStack;
+
+/**
+ * Provides a plugin base and form callbacks for payment reference elements.
+ */
+abstract class PaymentReferenceBase extends Element\FormElement implements FormElementInterface, ContainerFactoryPluginInterface {
+
+  /**
+   * The number of seconds a payment should remain stored.
+   */
+  const KEY_VALUE_TTL = 3600;
+
+  /**
+   * The date formatter.
+   *
+   * @var \Drupal\Core\DateTime\DateFormatter
+   */
+  protected $dateFormatter;
+
+  /**
+   * The link generator.
+   *
+   * @var \Drupal\Core\Utility\LinkGeneratorInterface
+   */
+  protected $linkGenerator;
+
+  /**
+   * The payment method selector manager.
+   *
+   * @var \Drupal\payment\Plugin\Payment\MethodSelector\PaymentMethodSelectorManagerInterface
+   */
+  protected $paymentMethodSelectorManager;
+
+  /**
+   * The payment storage.
+   *
+   * @var \Drupal\Core\Entity\EntityStorageInterface
+   */
+  protected $paymentStorage;
+
+  /**
+   * The random generator.
+   *
+   * @var \Drupal\Component\Utility\Random
+   */
+  protected $random;
+
+  /**
+   * The request stack.
+   *
+   * @var \Symfony\Component\HttpFoundation\RequestStack
+   */
+  protected $requestStack;
+
+  /**
+   * Creates a new 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 mixed $plugin_definition
+   *   The plugin implementation definition.
+   * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
+   * @param \Drupal\Core\Entity\EntityStorageInterface $payment_storage
+   * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
+   * @param \Drupal\Core\DateTime\DateFormatter $date_formatter
+   * @param \Drupal\Core\Utility\LinkGeneratorInterface $link_generator
+   * @param \Drupal\payment\Plugin\Payment\MethodSelector\PaymentMethodSelectorManagerInterface $payment_method_selector_manager
+   * @param \Drupal\Component\Utility\Random $random
+   */
+  public function __construct(array $configuration, $plugin_id, array $plugin_definition, RequestStack $request_stack, EntityStorageInterface $payment_storage, TranslationInterface $string_translation, DateFormatter $date_formatter, LinkGeneratorInterface $link_generator, PaymentMethodSelectorManagerInterface $payment_method_selector_manager, Random $random) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+    $this->dateFormatter = $date_formatter;
+    $this->linkGenerator = $link_generator;
+    $this->paymentMethodSelectorManager = $payment_method_selector_manager;
+    $this->paymentStorage = $payment_storage;
+    $this->random = $random;
+    $this->requestStack = $request_stack;
+    $this->stringTranslation = $string_translation;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getInfo() {
+    $plugin_id = $this->getPluginId();
+
+    return array(
+      // The ID of a payment as the (default) value. Changing the value is not
+      // supported, so #default_value must be NULL if the user should be allowed
+      // to select/add a payment.
+      '#default_value' => NULL,
+      // An array with IDs of the payment methods the payer is allowed to pay the
+      // payment with, or NULL to allow all.
+      '#limit_allowed_payment_method_ids' => NULL,
+      // The ID of the payment method selector plugin to use.
+      '#payment_method_selector_id' => NULL,
+      '#process' => array(function(array $element, FormStateInterface $form_state, array $form) use ($plugin_id) {
+        /** @var \Drupal\Component\Plugin\PluginManagerInterface $element_info_manager */
+        $element_info_manager = \Drupal::service('plugin.manager.element_info');
+        /** @var \Drupal\payment_reference\Element\PaymentReferenceBase $element_plugin */
+        $element_plugin = $element_info_manager->createInstance($plugin_id);
+
+        return $element_plugin->process($element, $form_state, $form);
+      }),
+      // The payment that must be made if none are available in the queue yet. It
+      // must be an instance of \Drupal\payment\Entity\PaymentInterface.
+      '#prototype_payment' => NULL,
+      // The ID of the queue category the element is used for.
+      '#queue_category_id' => NULL,
+      // The ID of the account that must own the payment.
+      '#queue_owner_id' => NULL,
+    );
+  }
+
+  /**
+   * Implements form #process callback.
+   */
+  public function process(array &$element, FormStateInterface $form_state, array $form) {
+    $plugin_id = $this->getPluginId();
+
+    // Set internal configuration.
+    $element['#available_payment_id'] = NULL;
+    $element['#element_validate'] = array(function(array &$element, FormStateInterface $form_state, array &$form) use ($plugin_id) {
+      /** @var \Drupal\Component\Plugin\PluginManagerInterface $element_info_manager */
+      $element_info_manager = \Drupal::service('plugin.manager.element_info');
+      /** @var \Drupal\payment_reference\Element\PaymentReferenceBase $element_plugin */
+      $element_plugin = $element_info_manager->createInstance($plugin_id);
+
+      $element_plugin->getPaymentMethodSelector($element, $form_state)->validateConfigurationForm($element['container']['payment_form']['payment_method'], $form_state);
+    });
+    $element['#theme_wrappers'] = array('form_element');
+    $element['#tree'] = TRUE;
+
+    // Validate the element's configuration.
+    if (!is_int($element['#default_value']) && !is_null($element['#default_value'])) {
+      throw new \InvalidArgumentException('#default_value must be an integer or NULL, but ' . gettype($element['#default_value']) . ' was given.');
+    }
+    if (!is_null($element['#limit_allowed_payment_method_ids']) && !is_array($element['#limit_allowed_payment_method_ids'])) {
+      throw new \InvalidArgumentException('#limit_allowed_payment_method_ids must be an array or NULL, but ' . gettype($element['#limit_allowed_payment_method_ids']) . ' was given.');
+    }
+    if (!is_string($element['#queue_category_id'])) {
+      throw new \InvalidArgumentException('#queue_category_id must be a string, but ' . gettype($element['#queue_category_id']) . ' was given.');
+    }
+    if (!is_int($element['#queue_owner_id'])) {
+      throw new \InvalidArgumentException('#queue_owner_id must be an integer, but ' . gettype($element['#queue_owner_id']) . ' was given.');
+    }
+    if (!is_string($element['#payment_method_selector_id'])) {
+      throw new \InvalidArgumentException('#payment_method_selector_id must be a string, but ' . gettype($element['#payment_method_selector_id']) . ' was given.');
+    }
+    if (!($element['#prototype_payment'] instanceof PaymentInterface)) {
+      throw new \InvalidArgumentException('#prototype_payment must implement \Drupal\payment\Entity\PaymentInterface.');
+    }
+
+    // Find the default payment to use.
+    if (!$element['#default_value']) {
+      $payment_ids = $this->getPaymentQueue()->loadPaymentIds($element['#queue_category_id'], $element['#queue_owner_id']);
+      $element['#available_payment_id'] = $payment_ids ? reset($payment_ids) : NULL;
+    }
+
+    // AJAX.
+    $ajax_wrapper_id = Html::getClass('payment_reference-' . $element['#name']);
+    $element['container'] = array(
+      '#attached' => array(
+        'js' => array(
+          drupal_get_path('module', 'payment_reference') . '/js/payment_reference.js',
+          array(
+            'type' => 'setting',
+            'data' => array(
+              'PaymentReferencePaymentAvailable' => array(
+                $ajax_wrapper_id => $element['#default_value'] || $element['#available_payment_id'],
+              ),
+            ),
+          ),
+        ),
+      ),
+      '#id' => $ajax_wrapper_id,
+      '#type' => 'container',
+    );
+    $element['container']['payment_form'] = $this->buildPaymentForm($element, $form_state);
+    $element['container']['payment_form']['#access'] = !$element['#default_value'] && !$element['#available_payment_id'];
+
+    $element['container']['payment_view'] = $this->buildPaymentView($element, $form_state);
+    $element['container']['payment_view']['#access'] = $element['#default_value'] || $element['#available_payment_id'];
+
+    $element['container']['refresh'] = $this->buildRefreshButton($element, $form_state);
+
+    return $element;
+  }
+
+  /**
+   * Builds the payment form.
+   *
+   * @param array $element
+   *   The root element.
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *
+   * @return array
+   *   A render array.
+   */
+  protected function buildPaymentForm(array $element, FormStateInterface $form_state) {
+    // @todo Use entity form display.
+    $build = array(
+      '#type' => 'container',
+    );
+    $payment_method_selector = $this->getPaymentMethodSelector($element, $form_state);
+    $build['line_items'] = array(
+      '#payment' => $payment_method_selector->getPayment(),
+      '#type' => 'payment_line_items_display',
+    );
+    $build['payment_method'] = $payment_method_selector->buildConfigurationForm(array(), $form_state);
+    if ($this->hasTemporaryPayment($element, $form_state)) {
+      $this->disableChildren($build['payment_method']);
+    }
+    $build['pay_link'] = $this->buildCompletePaymentLink($element, $form_state);
+    $build['pay_link']['#access'] = $this->hasTemporaryPayment($element, $form_state);
+    $plugin_id = $this->getPluginId();
+    $build['pay_button'] = array(
+      '#ajax' => array(
+        'callback' => function(array $form, FormStateInterface $form_state) use ($plugin_id) {
+            /** @var \Drupal\Component\Plugin\PluginManagerInterface $element_info_manager */
+            $element_info_manager = \Drupal::service('plugin.manager.element_info');
+            /** @var \Drupal\payment_reference\Element\PaymentReferenceBase $element_plugin */
+            $element_plugin = $element_info_manager->createInstance($plugin_id);
+
+            return $element_plugin->ajaxPay($form, $form_state);
+          },
+      ),
+      '#limit_validation_errors' => array(array_merge($element['#parents'], array('container', 'payment_form'))),
+      '#submit' => array(function(array $form, FormStateInterface $form_state) use ($plugin_id) {
+        /** @var \Drupal\Component\Plugin\PluginManagerInterface $element_info_manager */
+        $element_info_manager = \Drupal::service('plugin.manager.element_info');
+        /** @var \Drupal\payment_reference\Element\PaymentReferenceBase $element_plugin */
+        $element_plugin = $element_info_manager->createInstance($plugin_id);
+        $element_plugin->pay($form, $form_state);
+      }),
+      '#type' => 'submit',
+      '#value' => $this->t('Pay'),
+    );
+
+    return $build;
+  }
+
+  /**
+   * Builds the refresh button.
+   *
+   * @param array $element
+   *   The root element.
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *
+   * @return array
+   *   A render array.
+   */
+  protected function buildRefreshButton(array $element, FormStateInterface $form_state) {
+    $class = array('payment_reference-refresh-button');
+    if (!$element['#default_value'] && $element['#available_payment_id'] && !$this->hasTemporaryPayment($element, $form_state)) {
+      $class[] = 'payment-reference-hidden';
+    }
+    $plugin_id = $this->getPluginId();
+    $build = array(
+      '#ajax' => array(
+        'callback' => function(array $form, FormStateInterface $form_state) use ($plugin_id) {
+          /** @var \Drupal\Component\Plugin\PluginManagerInterface $element_info_manager */
+          $element_info_manager = \Drupal::service('plugin.manager.element_info');
+          /** @var \Drupal\payment_reference\Element\PaymentReferenceBase $element_plugin */
+          $element_plugin = $element_info_manager->createInstance($plugin_id);
+
+          return $element_plugin->ajaxRefresh($form, $form_state);
+        },
+        'event' => 'mousedown',
+        // The AJAX behavior itself does not need a wrapper, but
+        // payment_reference.js does.
+        'wrapper' => $element['container']['#id'],
+      ),
+      '#attached' => array(
+        'css' => array(
+          drupal_get_path('module', 'payment_reference') . '/css/payment_reference.css',
+        ),
+      ),
+      '#attributes' => array(
+        // system.module.css's .hidden class's is overridden by button styling,
+        // so this needs a custom class.
+        'class' => $class,
+      ),
+      '#limit_validation_errors' => array(),
+      '#submit' => array(array($this->pluginDefinition['class'], 'refresh')),
+      '#type' => 'submit',
+      '#value' => $this->t('Re-check available payments'),
+    );
+
+    return $build;
+  }
+
+  /**
+   * Builds the payment view.
+   *
+   * @param array $element
+   *   The root element.
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *
+   * @return array
+   *   A render array.
+   */
+  protected function buildPaymentView(array $element, FormStateInterface $form_state) {
+    $payment_id = $element['#default_value'] ?: $element['#available_payment_id'];
+    /** @var \Drupal\payment\Entity\PaymentInterface|null $payment */
+    $payment = $payment_id ? $this->paymentStorage->load($payment_id) : NULL;
+
+    $build = array();
+    if ($payment) {
+      $currency = $payment->getCurrency();
+      $status = $payment->getStatus();
+      $status_definition = $status->getPluginDefinition();
+      $build = array(
+        '#empty' => $this->t('There are no line items.'),
+        '#header' => array($this->t('Amount'), $this->t('Status'), $this->t('Last updated')),
+        '#type' => 'table',
+      );
+      $build[0]['amount'] = array(
+        '#markup' => $currency->formatAmount($payment->getAmount()),
+      );
+      $build[0]['status'] = array(
+        '#markup' => $status_definition['label'],
+      );
+      $build[0]['updated'] = array(
+        '#markup' => $this->dateFormatter->format($status->getCreated()),
+      );
+      if ($payment->access('view')) {
+        $build['#header'][] = $this->t('Operations');
+        $build[0]['view'] = array(
+          '#markup' => $this->t('<a href="@url" target="_blank">View payment details</a> (opens in a new window)', array(
+              '@url' => $payment->url(),
+            )),
+        );
+      }
+    }
+
+    return $build;
+  }
+
+  /**
+   * Builds the "Complete payment" link.
+   *
+   * @param array $element
+   *   The root element.
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *
+   * @return array
+   *   A render array.
+   */
+  protected function buildCompletePaymentLink(array $element, FormStateInterface $form_state) {
+    $payment_method_selector = $this->getPaymentMethodSelector($element, $form_state);
+    $payment_method = $payment_method_selector->getPaymentMethod();
+
+    $build = array();
+    if ($payment_method) {
+      $build['message'] = array(
+        '#markup' => $this->t('@payment_method_label requires the payment to be completed in a new window.', array(
+            '@payment_method_label' => $payment_method->getPluginLabel(),
+          )),
+      );
+      $build['link'] = array(
+        '#attributes' => array(
+          'class' => array('button', 'payment_reference-complete-payment-link'),
+          'target' => '_blank',
+        ),
+        '#route_name' => 'payment_reference.pay',
+        '#route_parameters' => array(
+          'storage_key' => $this->getTemporaryPaymentStorageKey($element, $form_state),
+        ),
+        '#title' => $this->t('Complete payment'),
+        '#type' => 'link',
+      );
+    }
+
+    return $build;
+  }
+
+  /**
+   * Disables all child elements.
+   *
+   * @param mixed[] $elements
+   */
+  protected function disableChildren(array &$elements) {
+    foreach (Element::children($elements) as $child_key) {
+      $elements[$child_key]['#disabled'] = TRUE;
+      $this->disableChildren($elements[$child_key]);
+    }
+  }
+
+  /**
+   * Implements form submit handler.
+   */
+  public function pay(array $form, FormStateInterface $form_state) {
+    $triggering_element = $form_state->get('triggering_element');
+    $root_element_parents = array_slice($triggering_element['#array_parents'], 0, -3);
+    $root_element = NestedArray::getValue($form, $root_element_parents);
+
+    $payment_method_selector = $this->getPaymentMethodSelector($root_element, $form_state);
+    $payment_method_selector->submitConfigurationForm($root_element['container']['payment_form']['payment_method'], $form_state);
+
+    $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).'), '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();
+    }
+    $form_state->setRebuild();
+  }
+
+  /**
+   * Implements form AJAX callback.
+   */
+  public function ajaxPay(array &$form, FormStateInterface $form_state) {
+    $triggering_element = $form_state->get('triggering_element');
+    $root_element_parents = array_slice($triggering_element['#array_parents'], 0, -3);
+    $root_element = NestedArray::getValue($form, $root_element_parents);
+
+    $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()) {
+      $link = $this->buildCompletePaymentLink($root_element, $form_state);
+      $response->addCommand(new OpenModalDialogCommand($this->t('Complete payment'), drupal_render($link)));
+    }
+
+    return $response;
+  }
+
+  /**
+   * Implements form submit handler.
+   */
+  public static function refresh(array $form, FormStateInterface $form_state) {
+    $form_state->setRebuild();
+  }
+
+  /**
+   * Implements form AJAX callback.
+   */
+  public function ajaxRefresh(array &$form, FormStateInterface $form_state) {
+    $triggering_element = $form_state->get('triggering_element');
+    $root_element_parents = array_slice($triggering_element['#array_parents'], 0, -2);
+    $root_element = NestedArray::getValue($form, $root_element_parents);
+
+    $response = new AjaxResponse();
+    $response->addCommand(new ReplaceCommand('#' . $root_element['container']['#id'], drupal_render($root_element['container'])));
+
+    return $response;
+  }
+
+  /**
+   * Gets the payment method selector.
+   *
+   * @param array $element
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *
+   * @return \Drupal\payment\Plugin\Payment\MethodSelector\PaymentMethodSelectorInterface
+   */
+  protected function getPaymentMethodSelector(array $element, FormStateInterface $form_state) {
+    $key = 'payment_reference.element.payment_reference.payment_method_selector.' . $element['#name'];
+    if (!$form_state->has($key)) {
+      /** @var \Drupal\payment\Entity\PaymentInterface $prototype_payment */
+      $prototype_payment = $element['#prototype_payment'];
+      $payment = $prototype_payment->createDuplicate();
+
+      $payment_method_selector = $this->paymentMethodSelectorManager->createInstance($element['#payment_method_selector_id']);
+      $payment_method_selector->setPayment($payment);
+      $payment_method_selector->setRequired($element['#required']);
+      if (!is_null($element['#limit_allowed_payment_method_ids'])) {
+        $payment_method_selector->setAllowedPaymentMethods($element['#limit_allowed_payment_method_ids']);
+      }
+
+      $form_state->set($key, $payment_method_selector);
+    }
+
+    return $form_state->get($key);
+  }
+
+  /**
+   * 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
+   */
+  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) {
+
+    // Ignore $input, because the element's value does not come from submitted
+    // form values, but from the payment queue.
+    if ($element['#default_value']) {
+      return $element['#default_value'];
+    }
+    else {
+      /** @var \Drupal\Component\Plugin\PluginManagerInterface $element_info_manager */
+      $element_info_manager = \Drupal::service('plugin.manager.element_info');
+      /** @var \Drupal\payment_reference\Element\PaymentReferenceBase $element_plugin */
+      $element_plugin = $element_info_manager->createInstance($element['#type']);
+      $payment_ids = $element_plugin->getPaymentQueue()->loadPaymentIds($element['#queue_category_id'], $element['#queue_owner_id']);
+
+      return $payment_ids ? (int) reset($payment_ids) : NULL;
+    }
+  }
+
+}
diff --git a/payment_reference/src/Entity/Payment/PaymentForm.php b/payment_reference/src/Entity/Payment/PaymentForm.php
deleted file mode 100644
index 6b45027..0000000
--- a/payment_reference/src/Entity/Payment/PaymentForm.php
+++ /dev/null
@@ -1,123 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\payment_reference\Entity\PaymentFormController.
- */
-
-namespace Drupal\payment_reference\Entity\Payment;
-
-use Drupal\Core\Entity\ContentEntityForm;
-use Drupal\Core\Entity\EntityManagerInterface;
-use Drupal\Core\Form\FormStateInterface;
-use Drupal\Core\StringTranslation\TranslationInterface;
-use Drupal\payment\Plugin\Payment\MethodSelector\PaymentMethodSelectorManagerInterface;
-use Symfony\Component\DependencyInjection\ContainerInterface;
-
-/**
- * Provides the payment form.
- */
-class PaymentForm extends ContentEntityForm {
-
-  /**
-   * The payment method selector manager.
-   *
-   * @var \Drupal\payment\Plugin\Payment\MethodSelector\PaymentMethodSelectorManagerInterface
-   */
-  protected $paymentMethodSelectorManager;
-
-  /**
-   * Constructs a new instance.
-   *
-   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
-   *   The entity manager.
-   * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
-   * @param \Drupal\payment\Plugin\Payment\MethodSelector\PaymentMethodSelectorManagerInterface $payment_method_selector_manager
-   *   The payment method selector manager.
-   */
-  public function __construct(EntityManagerInterface $entity_manager, TranslationInterface $string_translation, PaymentMethodSelectorManagerInterface $payment_method_selector_manager) {
-    parent::__construct($entity_manager);
-    $this->paymentMethodSelectorManager = $payment_method_selector_manager;
-    $this->stringTranslation = $string_translation;
-  }
-
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function create(ContainerInterface $container) {
-    return new static($container->get('entity.manager'), $container->get('string_translation'), $container->get('plugin.manager.payment.method_selector'));
-  }
-
-
-  /**
-   * {@inheritdoc}
-   */
-  public function form(array $form, FormStateInterface $form_state) {
-    $payment = $this->getEntity();
-
-    $form['line_items'] = array(
-      '#payment' => $payment,
-      '#type' => 'payment_line_items_display',
-    );
-
-    if ($form_state->has('payment_method_selector')) {
-      $payment_method_selector = $form_state->get('payment_method_selector');
-    }
-    else {
-      $config = $this->config('payment_reference.payment_type');
-      $payment_method_selector_id = $config->get('payment_method_selector_id');
-      $limit_allowed_payment_methods = $config->get('limit_allowed_payment_methods');
-      $allowed_payment_method_ids = $config->get('allowed_payment_method_ids');
-      $payment_method_selector = $this->paymentMethodSelectorManager->createInstance($payment_method_selector_id);
-      if ($limit_allowed_payment_methods) {
-        $payment_method_selector->setAllowedPaymentMethods($allowed_payment_method_ids);
-      }
-      $payment_method_selector->setPayment($payment);
-      $payment_method_selector->setRequired();
-      $form_state->set('payment_method_selector', $payment_method_selector);
-    }
-
-    $form['payment_method'] = $payment_method_selector->buildConfigurationForm(array(), $form_state, $payment);
-
-    return parent::form($form, $form_state);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function validateForm(array &$form, FormStateInterface $form_state) {
-    /** @var \Drupal\payment\Plugin\Payment\MethodSelector\PaymentMethodSelectorInterface $payment_method_selector */
-    $payment_method_selector = $form_state->get('payment_method_selector');
-    $payment_method_selector->validateConfigurationForm($form['payment_method'], $form_state);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function submit(array $form, FormStateInterface $form_state) {
-    /** @var \Drupal\payment\Entity\PaymentInterface $payment */
-    $payment = $this->getEntity();
-    /** @var \Drupal\payment\Plugin\Payment\MethodSelector\PaymentMethodSelectorInterface $payment_method_selector */
-    $payment_method_selector = $form_state->get('payment_method_selector');
-    $payment_method_selector->submitConfigurationForm($form['payment_method'], $form_state);
-    $payment->setPaymentMethod($payment_method_selector->getPaymentMethod());
-    $payment->save();
-    $payment->execute();
-  }
-
-  /**
-   * Returns an array of supported actions for the current entity form.
-   */
-  protected function actions(array $form, FormStateInterface $form_state) {
-    // Only use the existing submit action.
-    $actions = parent::actions($form, $form_state);
-    $actions = array(
-      'submit' => $actions['submit'],
-    );
-    $actions['submit']['#value'] = $this->t('Pay');
-
-    return $actions;
-  }
-
-}
diff --git a/payment_reference/src/PaymentFactory.php b/payment_reference/src/PaymentFactory.php
new file mode 100644
index 0000000..3941991
--- /dev/null
+++ b/payment_reference/src/PaymentFactory.php
@@ -0,0 +1,69 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\payment_reference\PaymentFactory.
+ */
+
+namespace Drupal\payment_reference;
+
+use Drupal\Core\Entity\EntityManagerInterface;
+use Drupal\Core\Field\FieldDefinitionInterface;
+use Drupal\payment\Plugin\Payment\LineItem\PaymentLineItemManagerInterface;
+
+/**
+ * Provides a payment factory service.
+ */
+class PaymentFactory implements PaymentFactoryInterface {
+
+  /**
+   * The entity manager.
+   *
+   * @var \Drupal\Core\Entity\EntityManagerInterface
+   */
+  protected $entityManager;
+
+  /**
+   * The payment line item manager.
+   *
+   * @var \Drupal\payment\Plugin\Payment\LineItem\PaymentLineItemManagerInterface
+   */
+  protected $paymentLineItemManager;
+
+  /**
+   * Constructs a new class instance.
+   *
+   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
+   *   The entity manager.
+   * @param \Drupal\payment\Plugin\Payment\LineItem\PaymentLineItemManagerInterface $payment_line_item_manager
+   *   The payment line item manager.
+   */
+  public function __construct(EntityManagerInterface $entity_manager, PaymentLineItemManagerInterface $payment_line_item_manager) {
+    $this->entityManager = $entity_manager;
+    $this->paymentLineItemManager = $payment_line_item_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function createPayment(FieldDefinitionInterface $field_definition) {
+    /** @var \Drupal\payment\Entity\PaymentInterface $payment */
+    $payment = $this->entityManager
+      ->getStorage('payment')
+      ->create(array(
+        'bundle' => 'payment_reference',
+      ));
+    /** @var \Drupal\payment_reference\Plugin\Payment\Type\PaymentReference $payment_type */
+    $payment_type = $payment->getPaymentType();
+    $payment_type->setEntityTypeId($field_definition->getFieldStorageDefinition()->getTargetEntityTypeId());
+    $payment_type->setBundle($field_definition->getBundle());
+    $payment_type->setFieldName($field_definition->getName());
+    $payment->setCurrencyCode($field_definition->getSetting('currency_code'));
+    foreach ($field_definition->getSetting('line_items_data') as $line_item_data) {
+      $line_item = $this->paymentLineItemManager->createInstance($line_item_data['plugin_id'], $line_item_data['plugin_configuration']);
+      $payment->setLineItem($line_item);
+    }
+
+    return $payment;
+  }
+}
diff --git a/payment_reference/src/PaymentFactoryInterface.php b/payment_reference/src/PaymentFactoryInterface.php
new file mode 100644
index 0000000..de99c21
--- /dev/null
+++ b/payment_reference/src/PaymentFactoryInterface.php
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\payment_reference\PaymentFactoryInterface.
+ */
+
+namespace Drupal\payment_reference;
+
+use Drupal\Core\Field\FieldDefinitionInterface;
+
+/**
+ * Defines a payment factory service.
+ */
+interface PaymentFactoryInterface {
+
+  /**
+   * Creates a payment for a field.
+   *
+   * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
+   *
+   * @return \Drupal\payment\Entity\PaymentInterface
+   */
+  public function createPayment(FieldDefinitionInterface $field_definition);
+
+}
diff --git a/payment_reference/src/PaymentReference.php b/payment_reference/src/PaymentReference.php
index 97969d9..29b270d 100644
--- a/payment_reference/src/PaymentReference.php
+++ b/payment_reference/src/PaymentReference.php
@@ -13,6 +13,15 @@ namespace Drupal\payment_reference;
 class PaymentReference {
 
   /**
+   * Returns the payment factory.
+   *
+   * @return \Drupal\payment_reference\PaymentFactoryInterface
+   */
+  public static function factory() {
+    return \Drupal::service('payment_reference.payment_factory');
+  }
+
+  /**
    * Returns the payment reference queue.
    *
    * @return \Drupal\payment\QueueInterface
diff --git a/payment_reference/src/Plugin/Field/FieldType/PaymentReference.php b/payment_reference/src/Plugin/Field/FieldType/PaymentReference.php
index 7055e9b..ef05173 100644
--- a/payment_reference/src/Plugin/Field/FieldType/PaymentReference.php
+++ b/payment_reference/src/Plugin/Field/FieldType/PaymentReference.php
@@ -23,7 +23,7 @@ use Drupal\payment_reference\PaymentReference as PaymentReferenceServiceWrapper;
  * @FieldType(
  *   configurable = "true",
  *   constraints = {
- *     "ValidReference" = TRUE
+ *     "ValidReference" = {}
  *   },
  *   default_formatter = "entity_reference_label",
  *   default_widget = "payment_reference",
@@ -38,9 +38,10 @@ class PaymentReference extends ConfigurableEntityReferenceItem {
    * {@inheritdoc}
    */
   public static function defaultSettings() {
-    return parent::defaultSettings() + array(
+    return array(
+      'target_bundle' => 'payment_reference',
       'target_type' => 'payment',
-    );
+    ) + parent::defaultSettings();
   }
 
   /**
@@ -147,14 +148,31 @@ class PaymentReference extends ConfigurableEntityReferenceItem {
    * {@inheritdoc}
    */
   public function preSave() {
-    $payment_id = $this->get('target_id')->getValue();
-    $queue = PaymentReferenceServiceWrapper::queue();
-    $acquisition_code = $queue->claimPayment($payment_id);
-    if ($acquisition_code !== FALSE) {
-      $queue->acquirePayment($payment_id, $acquisition_code);
+    $entity_type_id = $this->getFieldDefinition()->getFieldStorageDefinition()->getTargetEntityTypeId();
+    $entity_storage = \Drupal::entityManager()->getStorage($entity_type_id);
+    /** @var \Drupal\Core\Entity\ContentEntityInterface $current_entity */
+    $current_entity = $this->getRoot();
+    $unchanged_payment_id = NULL;
+    if ($current_entity->id()) {
+      /** @var \Drupal\Core\Entity\ContentEntityInterface $unchanged_entity */
+      $unchanged_entity = $entity_storage->loadUnchanged($current_entity->id());
+      if ($unchanged_entity) {
+        $unchanged_payment_id = $unchanged_entity->get($this->getFieldDefinition()->getName())->get($this->name)->get('target_id')->getValue();
+      }
     }
-    else {
-      $this->get('target_id')->setValue(0);
+    $current_payment_id = $this->get('target_id')->getValue();
+
+    // Only claim the payment if the payment ID in this field has changed since
+    // the field's target entity was last saved or if the entity is new.
+    if (!$current_entity->id() || $current_payment_id != $unchanged_payment_id) {
+      $queue = PaymentReferenceServiceWrapper::queue();
+      $acquisition_code = $queue->claimPayment($current_payment_id);
+      if ($acquisition_code !== FALSE) {
+        $queue->acquirePayment($current_payment_id, $acquisition_code);
+      }
+      else {
+        $this->get('target_id')->setValue(0);
+      }
     }
   }
 
diff --git a/payment_reference/src/Plugin/Field/FieldWidget/PaymentReference.php b/payment_reference/src/Plugin/Field/FieldWidget/PaymentReference.php
index 7fd893b..31ff965 100644
--- a/payment_reference/src/Plugin/Field/FieldWidget/PaymentReference.php
+++ b/payment_reference/src/Plugin/Field/FieldWidget/PaymentReference.php
@@ -7,12 +7,14 @@
 
 namespace Drupal\payment_reference\Plugin\Field\FieldWidget;
 
+use Drupal\Core\Config\ConfigFactoryInterface;
 use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\Field\FieldItemListInterface;
 use Drupal\Core\Field\WidgetBase;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
 use Drupal\Core\Session\AccountInterface;
+use Drupal\payment_reference\PaymentFactoryInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -31,6 +33,13 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
 class PaymentReference extends WidgetBase implements ContainerFactoryPluginInterface {
 
   /**
+   * The config factory.
+   *
+   * @var \Drupal\Core\Config\ConfigFactoryInterface
+   */
+  protected $configFactory;
+
+  /**
    * The current user.
    *
    * @var \Drupal\Core\Session\AccountInterface
@@ -38,6 +47,13 @@ class PaymentReference extends WidgetBase implements ContainerFactoryPluginInter
   protected $currentUser;
 
   /**
+   * The payment reference factory.
+   *
+   * @var \Drupal\payment_reference\PaymentFactoryInterface
+   */
+  protected $paymentFactory;
+
+  /**
    * Constructs a new class instance.
    *
    * @param array $plugin_id
@@ -50,34 +66,41 @@ class PaymentReference extends WidgetBase implements ContainerFactoryPluginInter
    *   The widget settings.
    * @param array $third_party_settings
    *   Any third party settings.
+   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
+   *   The config factory.
    * @param \Drupal\Core\Session\AccountInterface $current_user
    *   The current user.
+   * @param \Drupal\payment_reference\PaymentFactoryInterface $payment_factory
+   *   The payment reference factory.
    */
-  public function __construct($plugin_id, array $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, array $third_party_settings, AccountInterface $current_user) {
+  public function __construct($plugin_id, array $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, array $third_party_settings, ConfigFactoryInterface $config_factory, AccountInterface $current_user, PaymentFactoryInterface $payment_factory) {
     parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $third_party_settings);
+    $this->configFactory = $config_factory;
     $this->currentUser = $current_user;
+    $this->paymentFactory = $payment_factory;
   }
 
   /**
    * {@inheritdoc}
    */
   public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
-    return new static($plugin_id, $plugin_definition, $configuration['field_definition'], $configuration['settings'], $configuration['third_party_settings'], $container->get('current_user'));
+    return new static($plugin_id, $plugin_definition, $configuration['field_definition'], $configuration['settings'], $configuration['third_party_settings'], $container->get('config.factory'), $container->get('current_user'), $container->get('payment_reference.payment_factory'));
   }
 
   /**
    * {@inheritdoc}
    */
   public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
-    $element['payment_id'] = array(
-      '#bundle' => $items->getEntity()->bundle(),
-      '#default_value' => isset($items[$delta]) ? $items[$delta]->target_id : NULL,
-      '#entity_type_id' => $items->getEntity()->getEntityTypeId(),
-      '#field_name' => $this->fieldDefinition->getName(),
+    $config = $this->configFactory->get('payment_reference.payment_type');
+    $payment = $this->paymentFactory->createPayment($this->fieldDefinition);
+    $element['target_id'] = array(
+      '#default_value' => isset($items[$delta]) ? (int) $items[$delta]->target_id : NULL,
+      '#limit_allowed_payment_method_ids' => $config->get('limit_allowed_payment_methods') ? $config->get('allowed_payment_method_ids') : NULL,
+      '#payment_method_selector_id' => $config->get('payment_method_selector_id'),
+      '#prototype_payment' => $payment,
+      '#queue_category_id' => $items->getEntity()->getEntityTypeId() . '.' . $items->getEntity()->bundle() . '.' . $this->fieldDefinition->getName(),
       // The requested user account may contain a string numeric ID.
-      '#owner_id' => (int) $this->currentUser->id(),
-      '#payment_line_items_data' => $this->getFieldSetting('line_items_data'),
-      '#payment_currency_code' => $this->getFieldSetting('currency_code'),
+      '#queue_owner_id' => (int) $this->currentUser->id(),
       '#required' => $this->fieldDefinition->isRequired(),
       '#type' => 'payment_reference',
     );
@@ -85,4 +108,13 @@ class PaymentReference extends WidgetBase implements ContainerFactoryPluginInter
     return $element;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function massageFormValues(array $values, array $form, FormStateInterface $form_state) {
+    return array(
+      'target_id' => $form[$this->fieldDefinition->getName()]['widget']['target_id']['#value'],
+    );
+  }
+
 }
diff --git a/payment_reference/src/Plugin/Payment/Type/PaymentReference.php b/payment_reference/src/Plugin/Payment/Type/PaymentReference.php
index 2e7af17..5947555 100644
--- a/payment_reference/src/Plugin/Payment/Type/PaymentReference.php
+++ b/payment_reference/src/Plugin/Payment/Type/PaymentReference.php
@@ -7,7 +7,6 @@
 namespace Drupal\payment_reference\Plugin\Payment\Type;
 
 use Drupal\Core\Entity\EntityManagerInterface;
-use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
 use Drupal\Core\Routing\UrlGeneratorInterface;
 use Drupal\Core\Session\AccountInterface;
@@ -100,23 +99,28 @@ class PaymentReference extends PaymentTypeBase implements ContainerFactoryPlugin
    * {@inheritdoc}
    */
   protected function doResumeContext() {
-    $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);
+    // 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);
+    }
   }
 
   /**
    * {@inheritdoc}
    */
   public function resumeContextAccess(AccountInterface $account) {
-    return TRUE;
+    return $this->getPayment()->getOwnerId() == $account->id();
   }
 
   /**
@@ -194,15 +198,4 @@ class PaymentReference extends PaymentTypeBase implements ContainerFactoryPlugin
     return $this->configuration['field_name'];
   }
 
-  /**
-   * Gets the 'ID' of the field the payment was made for.
-   *
-   * The ID is formatted as "$entity_type_id.$bundle.$field_name".
-   *
-   * @return string
-   */
-  public function getFieldId() {
-    return $this->getEntityTypeId() . '.' . $this->getBundle() . '.' . $this->getFieldName();
-  }
-
 }
diff --git a/payment_reference/src/Tests/Element/PaymentReferenceWebTest.php b/payment_reference/src/Tests/Element/PaymentReferenceWebTest.php
index aa5e17e..bf9f20d 100644
--- a/payment_reference/src/Tests/Element/PaymentReferenceWebTest.php
+++ b/payment_reference/src/Tests/Element/PaymentReferenceWebTest.php
@@ -8,6 +8,8 @@
 namespace Drupal\payment_reference\Tests\Element;
 
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
+use Drupal\payment\Entity\Payment;
+use Drupal\payment\Entity\PaymentInterface;
 use Drupal\payment\Tests\Generate;
 use Drupal\payment_reference\PaymentReference;
 use Drupal\simpletest\WebTestBase;
@@ -22,12 +24,15 @@ class PaymentReferenceWebTest extends WebTestBase {
   /**
    * {@inheritdoc}
    */
-  public static $modules = array('payment', 'payment_reference', 'payment_reference_test');
+  public static $modules = array('payment', 'payment_reference', 'payment_reference_test', 'payment_test');
 
   /**
    * Tests the element.
    */
   protected function testElement() {
+    $user = $this->drupalCreateUser();
+    $this->drupalLogin($user);
+
     // Create the field and field instance.
     $field_name = 'foobarbaz';
     entity_create('field_storage_config', array(
@@ -50,22 +55,38 @@ class PaymentReferenceWebTest extends WebTestBase {
     $state = \Drupal::state();
     $path = 'payment_reference_test-element-payment_reference';
 
-    // Test without queued payments.
+    // Test without selecting a payment method.
     $this->drupalGet($path);
-    $this->assertLinkByHref('payment_reference/pay/user/user/' . $field_name);
-    $this->drupalPostForm($path, array(), t('Submit'));
-    $this->assertText('FooBarBaz field is required');
+    $this->drupalPostForm(NULL, array(), t('Pay'));
+    $this->assertText('Payment method field is required');
     $value = $state->get('payment_reference_test_payment_reference_element');
     $this->assertNull($value);
 
-    // Test with a queued payment.
-    $payment = Generate::createPayment(2);
-    $payment->setStatus(\Drupal::service('plugin.manager.payment.status')->createInstance('payment_success'));
-    $payment->save();
-    PaymentReference::queue()->save('user.user.' . $field_name, $payment->id());
-    $this->drupalGet($path);
-    $this->drupalPostForm($path, array(), t('Submit'));
-    $value = $state->get('payment_reference_test_payment_reference_element');
-    $this->assertEqual($value, $payment->id());
+    // Test with a non-interruptive payment method.
+    $this->drupalPostForm($path, array(
+      'payment_reference[container][payment_form][payment_method][container][select][payment_method_id]' => 'payment_test_uninterruptive',
+    ), t('Choose payment method'));
+    $this->drupalPostForm(NULL, array(), t('Pay'));
+    $this->drupalPostForm(NULL, array(), t('Submit'));
+    $payment_id = $state->get('payment_reference_test_payment_reference_element');
+    $this->assertTrue(is_int($payment_id));
+    $this->assertTrue(Payment::load($payment_id) instanceof PaymentInterface);
+
+    // Remove the payment from the queue.
+    // @todo Remove this when https://www.drupal.org/node/2327669 is fixed.
+    PaymentReference::queue()->deleteByPaymentId($payment_id);
+
+    // Test with an interruptive payment method.
+    // @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][payment_method_id]' => 'payment_test_interruptive',
+    ), t('Choose payment method'));
+    $this->drupalPostForm(NULL, array(), t('Pay'));
+    $this->clickLink(t('Complete payment'));
+    /** @var \Drupal\payment\Entity\PaymentInterface $payment */
+    $payment = Payment::loadMultiple()[2];
+    $this->assertEqual($payment->getStatus()->getPluginId(), 'payment_success');
   }
+
 }
diff --git a/payment_reference/src/Tests/Entity/Payment/PaymentFormWebTest.php b/payment_reference/src/Tests/Entity/Payment/PaymentFormWebTest.php
deleted file mode 100644
index c94614c..0000000
--- a/payment_reference/src/Tests/Entity/Payment/PaymentFormWebTest.php
+++ /dev/null
@@ -1,68 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\payment_reference\Tests\Entity\Payment\PaymentFormWebTest.
- */
-
-namespace Drupal\payment_reference\Tests\Entity\Payment;
-
-use Drupal\Core\Field\FieldStorageDefinitionInterface;
-use Drupal\payment\Tests\Generate;
-use Drupal\simpletest\WebTestBase;
-
-/**
- * \Drupal\payment_reference\Entity\Payment\PaymentForm web test.
- *
- * @group Payment Reference Field
- */
-class PaymentFormWebTest extends WebTestBase {
-
-  /**
-   * {@inheritdoc}
-   */
-  public static $modules = array('filter', 'payment', 'payment_reference', 'payment_reference_test');
-
-  /**
-   * Tests the form.
-   */
-  protected function testForm() {
-    // Create a user.
-    $user = $this->drupalCreateUser(array('administer users'));
-    $this->drupalLogin($user);
-
-    // Create a payment method.
-    $payment_method = \Drupal\payment\Tests\Generate::createPaymentMethodConfiguration(2, 'payment_basic');
-    $payment_method->setPluginConfiguration(array(
-      'status' => 'payment_success',
-    ));
-    $payment_method->save();
-
-    // Create the field and field instance.
-    $field_name = strtolower($this->randomMachineName());
-    entity_create('field_storage_config', array(
-      'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
-      'entity_type' => 'user',
-      'name' => $field_name,
-      'type' => 'payment_reference',
-    ))->save();
-
-    /** @var \Drupal\field\FieldInstanceConfigInterface $field_instance_config */
-    $field_instance_config = entity_create('field_instance_config', array(
-      'bundle' => 'user',
-      'entity_type' => 'user',
-      'field_name' => $field_name,
-      'settings' => array(
-        'currency_code' => 'EUR',
-        'line_items_data' => array(),
-      ),
-    ));
-    $field_instance_config->save();
-
-    $path = '/payment_reference/pay/user/user/' . $field_name;
-    $this->drupalGet($path);
-    $this->drupalPostForm($path, array(), t('Pay'));
-    // This actually tests the payment_reference payment type plugin, but it lets
-    $this->assertUrl('payment_reference/resume/1');
-  }
-}
diff --git a/payment_reference/src/Tests/Plugin/Field/FieldWidget/PaymentReferenceWebTest.php b/payment_reference/src/Tests/Plugin/Field/FieldWidget/PaymentReferenceWebTest.php
index cafd2c9..aa6123d 100644
--- a/payment_reference/src/Tests/Plugin/Field/FieldWidget/PaymentReferenceWebTest.php
+++ b/payment_reference/src/Tests/Plugin/Field/FieldWidget/PaymentReferenceWebTest.php
@@ -8,6 +8,7 @@
 namespace Drupal\payment_reference\Tests\Plugin\Field\FieldWidget;
 
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
+use Drupal\payment\Tests\Generate;
 use Drupal\simpletest\WebTestBase;
 
 /**
@@ -49,13 +50,22 @@ class PaymentReferenceWebTest extends WebTestBase {
       ->setComponent($field_name, array())
       ->save();
 
-    $user = $this->drupalCreateUser();
+    $user = $this->drupalCreateUser(array('payment.payment.view.own'));
     $this->drupalLogin($user);
 
-    // Test the widget when creating an entity.
+    $payment_method = Generate::createPaymentMethodConfiguration(mt_rand(), 'payment_basic');
+    $payment_method->setPluginConfiguration(array(
+      'brand_label' => $this->randomMachineName(),
+      'execute_status_id' => 'payment_success',
+      'message_text' => $this->randomMachineName(),
+    ));
+    $payment_method->save();
+
+    // Test the widget when editing an entity.
     $this->drupalGet('user/' . $user->id() . '/edit');
-    $this->clickLink(t('Add a new payment'));
-    $this->assertUrl('/payment_reference/pay/user/user/' . $field_name);
-    $this->assertResponse('200');
+    $this->drupalPostForm(NULL, array(), t('Re-check available payments'));
+    $this->drupalPostForm(NULL, array(), t('Pay'));
+    $this->assertNoFieldByXPath('//input[@value="Pay"]');
+    $this->assertLinkByHref('payment/1');
   }
 }
diff --git a/payment_reference/tests/src/Controller/PayUnitTest.php b/payment_reference/tests/src/Controller/PayUnitTest.php
new file mode 100644
index 0000000..41032ff
--- /dev/null
+++ b/payment_reference/tests/src/Controller/PayUnitTest.php
@@ -0,0 +1,132 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\payment_reference\Tests\Controller\PayUnitTest.
+ */
+
+namespace Drupal\payment_reference\Tests\Controller;
+
+use Drupal\Core\Access\AccessInterface;
+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_method_selector.payment_select')
+      ->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_method_selector.payment_select')
+      ->willReturn($storage);
+
+    $request = $this->getMockBuilder('\Symfony\Component\HttpFoundation\Request')
+      ->disableOriginalConstructor()
+      ->getMock();
+
+    $this->assertSame($expected, $this->controller->access($request, $storage_key));
+  }
+
+  /**
+   * Provides data to testAccess().
+   */
+  public function providerTestAccess() {
+    return array(
+      array(AccessInterface::ALLOW, TRUE),
+      array(AccessInterface::DENY, FALSE),
+    );
+  }
+
+}
diff --git a/payment_reference/tests/src/Controller/PaymentReferenceUnitTest.php b/payment_reference/tests/src/Controller/PaymentReferenceUnitTest.php
deleted file mode 100644
index 8403abd..0000000
--- a/payment_reference/tests/src/Controller/PaymentReferenceUnitTest.php
+++ /dev/null
@@ -1,443 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\payment_reference\Tests\Controller\PaymentReferenceUnitTest.
- */
-
-namespace Drupal\payment_reference\Tests\Controller {
-
-use Drupal\Core\Access\AccessInterface;
-use Drupal\payment_reference\Controller\PaymentReference;
-use Drupal\Tests\UnitTestCase;
-use Symfony\Component\DependencyInjection\ContainerInterface;
-
-/**
- * @coversDefaultClass \Drupal\payment_reference\Controller\PaymentReference
- *
- * @group Payment Reference Field
- */
-class PaymentReferenceUnitTest extends UnitTestCase {
-
-  /**
-   * The controller under test.
-   *
-   * @var \Drupal\payment_reference\Controller\PaymentReference
-   */
-  protected $controller;
-
-  /**
-   * The current user.
-   *
-   * @var \Drupal\Core\Session\AccountInterface|\PHPUnit_Framework_MockObject_MockObject
-   */
-  protected $currentUser;
-
-  /**
-   * The entity form builder.
-   *
-   * @var \Drupal\Core\Entity\EntityFormBuilderInterface|\PHPUnit_Framework_MockObject_MockObject
-   */
-  protected $entityFormBuilder;
-
-  /**
-   * The entity manager.
-   *
-   * @var \Drupal\Core\Entity\EntityManager|\PHPUnit_Framework_MockObject_MockObject
-   */
-  protected $entityManager;
-
-  /**
-   * The payment line item manager.
-   *
-   * @var \Drupal\payment\Plugin\Payment\LineItem\PaymentLineItemManagerInterface|\PHPUnit_Framework_MockObject_MockObject
-   */
-  protected $paymentLineItemManager;
-
-  /**
-   * The payment reference queue.
-   *
-   * @var \Drupal\payment\QueueInterface|\PHPUnit_Framework_MockObject_MockObject
-   */
-  protected $queue;
-
-  /**
-   * The string translation service.
-   *
-   * @var \Drupal\Core\StringTranslation\TranslationInterface|\PHPUnit_Framework_MockObject_MockObject
-   */
-  protected $stringTranslation;
-
-  /**
-   * {@inheritdoc}
-   *
-   * @covers ::__construct
-   */
-  protected function setUp() {
-    $this->entityManager = $this->getMock('\Drupal\Core\Entity\EntityManagerInterface');
-
-    $this->currentUser = $this->getMock('\Drupal\Core\Session\AccountInterface');
-
-    $this->entityFormBuilder = $this->getMock('\Drupal\Core\Entity\EntityFormBuilderInterface');
-
-    $this->paymentLineItemManager = $this->getMock('\Drupal\payment\Plugin\Payment\LineItem\PaymentLineItemManagerInterface');
-
-    $this->queue = $this->getMock('\Drupal\payment\QueueInterface');
-
-    $this->stringTranslation = $this->getStringTranslationStub();
-
-    $this->controller = new PaymentReference($this->entityManager, $this->currentUser, $this->entityFormBuilder, $this->stringTranslation, $this->paymentLineItemManager, $this->queue);
-  }
-
-  /**
-   * @covers ::create
-   */
-  function testCreate() {
-    $container = $this->getMock('\Symfony\Component\DependencyInjection\ContainerInterface');
-    $map = array(
-      array('current_user', ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, $this->currentUser),
-      array('entity.form_builder', ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, $this->entityFormBuilder),
-      array('entity.manager', ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, $this->entityManager),
-      array('plugin.manager.payment.line_item', ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, $this->paymentLineItemManager),
-      array('string_translation', ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, $this->stringTranslation),
-      array('payment_reference.queue', ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, $this->queue),
-    );
-    $container->expects($this->any())
-      ->method('get')
-      ->will($this->returnValueMap($map));
-
-    $form = PaymentReference::create($container);
-    $this->assertInstanceOf('\Drupal\payment_reference\Controller\PaymentReference', $form);
-  }
-
-  /**
-   * @covers ::pay
-   */
-  public function testPay() {
-    $entity_type_id = $this->randomMachineName();
-    $bundle = $this->randomMachineName();
-    $field_name = $this->randomMachineName();
-    $currency_code = $this->randomMachineName();
-    $line_items_data = array(array(
-      'plugin_configuration' => array(),
-      'plugin_id' => $this->randomMachineName(),
-    ));
-
-    $field_definition = $this->getMock('\Drupal\Core\Field\\FieldDefinitionInterface');
-    $map = array(
-      array('currency_code', $currency_code),
-      array('line_items_data', $line_items_data),
-    );
-    $field_definition->expects($this->atLeastOnce())
-      ->method('getSetting')
-      ->will($this->returnValueMap($map));
-
-    $definitions = array(
-      $field_name => $field_definition,
-    );
-
-    $this->entityManager->expects($this->once())
-      ->method('getFieldDefinitions')
-      ->with($entity_type_id, $bundle)
-      ->will($this->returnValue($definitions));
-
-    $payment_type = $this->getMockBuilder('\Drupal\payment_reference\Plugin\Payment\Type\PaymentReference')
-      ->disableOriginalConstructor()
-      ->getMock();
-    $payment_type->expects($this->once())
-      ->method('setEntityTypeId')
-      ->with($entity_type_id);
-    $payment_type->expects($this->once())
-      ->method('setBundle')
-      ->with($bundle);
-    $payment_type->expects($this->once())
-      ->method('setFieldName')
-      ->with($field_name);
-
-    $payment_line_item = $this->getMock('\Drupal\payment\Plugin\Payment\LineItem\PaymentLineItemInterface');
-
-    $this->paymentLineItemManager->expects($this->once())
-      ->method('createInstance')
-      ->with($line_items_data[0]['plugin_id'])
-      ->will($this->returnValue($payment_line_item));
-
-    $payment = $this->getMockBuilder('\Drupal\payment\Entity\Payment')
-      ->disableOriginalConstructor()
-      ->getMock();
-    $payment->expects($this->once())
-      ->method('getPaymentType')
-      ->will($this->returnValue($payment_type));
-    $payment->expects($this->once())
-      ->method('setCurrencyCode')
-      ->with($currency_code);
-    $payment->expects($this->once())
-      ->method('setLineItem')
-      ->with($payment_line_item);
-
-    $storage_controller = $this->getMock('\Drupal\Core\Entity\EntityStorageInterface');
-    $storage_controller->expects($this->once())
-      ->method('create')
-      ->will($this->returnValue($payment));
-
-    $this->entityManager->expects($this->once())
-      ->method('getStorage')
-      ->with($this->equalTo('payment'))
-      ->will($this->returnValue($storage_controller));
-
-    $form = $this->getMockBuilder('\Drupal\payment_reference\Entity\PaymentFormController')
-      ->disableOriginalConstructor()
-      ->getMock();
-
-    $this->entityFormBuilder->expects($this->once())
-      ->method('getForm')
-      ->with($payment, 'payment_reference')
-      ->will($this->returnValue($form));
-
-
-    $this->assertSame($form, $this->controller->pay($entity_type_id, $bundle, $field_name));
-  }
-
-  /**
-   * @covers ::payAccess
-   * @covers ::fieldExists
-   *
-   * @dataProvider providerTestPayAccess
-   */
-  public function testPayAccess($expected, $entity_type_exists, $bundle_exists, $field_exists, $entity_access, $field_access, $queued_payments) {
-    $entity_type_id = $this->randomMachineName();
-    $bundle = $this->randomMachineName();
-    $field_name = $this->randomMachineName();
-
-    $field_definition = $this->getMock('\Drupal\Core\Field\FieldDefinitionInterface');
-
-    $user_id = mt_rand();
-    $this->currentUser->expects($this->any())
-      ->method('id')
-      ->will($this->returnValue($user_id));
-
-    $this->entityManager->expects($this->any())
-      ->method('hasDefinition')
-      ->with($entity_type_id)
-      ->will($this->returnValue($entity_type_exists));
-    $this->entityManager->expects($this->any())
-      ->method('getBundleInfo')
-      ->with($entity_type_id)
-      ->will($this->returnValue($bundle_exists ? array(
-        $bundle => array(),
-      ) : array()));
-    $this->entityManager->expects($this->any())
-      ->method('getFieldDefinitions')
-      ->with($entity_type_id, $bundle)
-      ->will($this->returnValue($field_exists ? array(
-        $field_name => $field_definition,
-      ) : array()));
-
-    $request = $this->getMockBuilder('\Symfony\Component\HttpFoundation\Request')
-      ->disableOriginalConstructor()
-      ->getMock();
-
-    $payment_id = mt_rand();
-
-    $access_controller = $this->getMock('\Drupal\Core\Entity\EntityAccessControlHandlerInterface');
-    $access_controller->expects($this->any())
-      ->method('createAccess')
-      ->with('payment_reference')
-      ->will($this->returnValue($entity_access));
-    $access_controller->expects($this->any())
-      ->method('fieldAccess')
-      ->with('edit', $field_definition)
-      ->will($this->returnValue($field_access));
-    $this->queue->expects($this->any())
-      ->method('loadPaymentIds')
-      ->will($this->returnValue($queued_payments ? array($payment_id) : array()));
-
-    $this->entityManager->expects($this->any())
-      ->method('getAccessControlHandler')
-      ->will($this->returnValue($access_controller));
-
-    $this->assertSame($expected, $this->controller->payAccess($request, $entity_type_id, $bundle, $field_name));
-  }
-
-  /**
-   * Provides data to testPayAccess().
-   */
-  public function providerTestPayAccess() {
-    return array(
-      array(AccessInterface::ALLOW, TRUE, TRUE, TRUE, TRUE, TRUE, FALSE),
-      array(AccessInterface::DENY, FALSE, TRUE, TRUE, TRUE, TRUE, FALSE),
-      array(AccessInterface::DENY, TRUE, FALSE, TRUE, TRUE, TRUE, FALSE),
-      array(AccessInterface::DENY, TRUE, TRUE, FALSE, TRUE, TRUE, FALSE),
-      array(AccessInterface::DENY, TRUE, TRUE, TRUE, FALSE, TRUE, FALSE),
-      array(AccessInterface::DENY, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE),
-      array(AccessInterface::DENY, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE),
-    );
-  }
-
-  /**
-   * @covers ::payLabel
-   */
-  public function testPayLabel() {
-    $entity_type_id = $this->randomMachineName();
-    $bundle = $this->randomMachineName();
-    $field_name = $this->randomMachineName();
-    $label = $this->randomMachineName();
-
-    $field_definition = $this->getMock('\Drupal\Core\Field\FieldDefinitionInterface');
-    $field_definition->expects($this->atLeastOnce())
-      ->method('getLabel')
-      ->will($this->returnValue($label));
-
-    $field_definitions = array(
-      $field_name => $field_definition,
-    );
-
-    $this->entityManager->expects($this->atLeastOnce())
-      ->method('getFieldDefinitions')
-      ->with($entity_type_id, $bundle)
-      ->will($this->returnValue($field_definitions));
-
-    $this->assertSame($label, $this->controller->payLabel($entity_type_id, $bundle, $field_name));
-  }
-
-  /**
-   * @covers ::resumeContext
-   */
-  public function testResumeContext() {
-    $payment_status = $this->getMock('\Drupal\payment\Plugin\Payment\Status\PaymentStatusInterface');
-
-    $payment = $this->getMockBuilder('\Drupal\payment\Entity\Payment')
-      ->disableOriginalConstructor()
-      ->getMock();
-    $payment->expects($this->once())
-      ->method('access')
-      ->with('view')
-      ->will($this->returnValue(TRUE));
-    $payment->expects($this->once())
-      ->method('getStatus')
-      ->will($this->returnValue($payment_status));
-
-    $this->controller->resumeContext($payment);
-  }
-
-  /**
-   * @covers ::resumeContextLabel
-   */
-  public function testResumeContextLabel() {
-    $entity_type_id = $this->randomMachineName();
-    $bundle = $this->randomMachineName();
-    $field_name = $this->randomMachineName();
-    $label = $this->randomMachineName();
-    $field_definition = $this->getMock('\Drupal\Core\Field\\FieldDefinitionInterface');
-    $field_definition->expects($this->atLeastOnce())
-      ->method('getLabel')
-      ->will($this->returnValue($label));
-
-    $definitions = array(
-      $field_name => $field_definition,
-    );
-
-    $this->entityManager->expects($this->once())
-      ->method('getFieldDefinitions')
-      ->with($entity_type_id, $bundle)
-      ->will($this->returnValue($definitions));
-
-    $payment_type = $this->getMockBuilder('\Drupal\payment_reference\Plugin\Payment\Type\PaymentReference')
-      ->disableOriginalConstructor()
-      ->getMock();
-    $payment_type->expects($this->once())
-      ->method('getEntityTypeId')
-      ->will($this->returnValue($entity_type_id));
-    $payment_type->expects($this->once())
-      ->method('getBundle')
-      ->will($this->returnValue($bundle));
-    $payment_type->expects($this->once())
-      ->method('getFieldName')
-      ->will($this->returnValue($field_name));
-
-    $payment = $this->getMockBuilder('\Drupal\payment\Entity\Payment')
-      ->disableOriginalConstructor()
-      ->getMock();
-    $payment->expects($this->once())
-      ->method('getPaymentType')
-      ->will($this->returnValue($payment_type));
-
-    $this->assertSame($label, $this->controller->resumeContextLabel($payment));
-  }
-
-  /**
-   * @covers ::resumeContextAccess
-   * @covers ::fieldExists
-   *
-   * @dataProvider providerTestResumeContextAccess
-   */
-  public function testResumeContextAccess($expected, $entity_type_exists, $bundle_exists, $field_exists) {
-    $entity_type_id = $this->randomMachineName();
-    $bundle = $this->randomMachineName();
-    $field_name = $this->randomMachineName();
-
-    $payment_type = $this->getMockBuilder('\Drupal\payment_reference\Plugin\Payment\Type\PaymentReference')
-      ->disableOriginalConstructor()
-      ->getMock();
-    $payment_type->expects($this->atLeastOnce())
-      ->method('getEntityTypeId')
-      ->will($this->returnValue($entity_type_id));
-    $payment_type->expects($this->atLeastOnce())
-      ->method('getBundle')
-      ->will($this->returnValue($bundle));
-    $payment_type->expects($this->atLeastOnce())
-      ->method('getFieldName')
-      ->will($this->returnValue($field_name));
-
-    $payment = $this->getMockBuilder('\Drupal\payment\Entity\Payment')
-      ->disableOriginalConstructor()
-      ->getMock();
-    $payment->expects($this->atLeastOnce())
-      ->method('getPaymentType')
-      ->will($this->returnValue($payment_type));
-
-    $field_definition = $this->getMock('\Drupal\Core\Field\FieldDefinitionInterface');
-
-    $this->entityManager->expects($this->any())
-      ->method('hasDefinition')
-      ->with($entity_type_id)
-      ->will($this->returnValue($entity_type_exists));
-    $this->entityManager->expects($this->any())
-      ->method('getBundleInfo')
-      ->with($entity_type_id)
-      ->will($this->returnValue($bundle_exists ? array(
-        $bundle => array(),
-      ) : array()));
-    $this->entityManager->expects($this->any())
-      ->method('getFieldDefinitions')
-      ->with($entity_type_id, $bundle)
-      ->will($this->returnValue($field_exists ? array(
-        $field_name => $field_definition,
-      ) : array()));
-
-    $this->assertSame($expected, $this->controller->resumeContextAccess($payment));
-  }
-
-  /**
-   * Provides data to testResumeCOntextAccess().
-   */
-  public function providerTestResumeContextAccess() {
-    return array(
-      array(AccessInterface::ALLOW, TRUE, TRUE, TRUE),
-      array(AccessInterface::DENY, FALSE, TRUE, TRUE),
-      array(AccessInterface::DENY, TRUE, FALSE, TRUE),
-      array(AccessInterface::DENY, TRUE, TRUE, FALSE),
-    );
-  }
-
-}
-
-}
-
-namespace {
-
-if (!function_exists('drupal_get_path')) {
-  function drupal_get_path() {
-  }
-}
-
-}
diff --git a/payment_reference/tests/src/Controller/ResumeContextUnitTest.php b/payment_reference/tests/src/Controller/ResumeContextUnitTest.php
new file mode 100644
index 0000000..174bec2
--- /dev/null
+++ b/payment_reference/tests/src/Controller/ResumeContextUnitTest.php
@@ -0,0 +1,156 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\payment_reference\Tests\Controller\ResumeContextUnitTest.
+ */
+
+namespace Drupal\payment_reference\Tests\Controller {
+
+use Drupal\Core\Access\AccessInterface;
+use Drupal\payment_reference\Controller\ResumeContext;
+use Drupal\Tests\UnitTestCase;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * @coversDefaultClass \Drupal\payment_reference\Controller\ResumeContext
+ *
+ * @group Payment Reference Field
+ */
+class ResumeContextUnitTest extends UnitTestCase {
+
+  /**
+   * The controller under test.
+   *
+   * @var \Drupal\payment_reference\Controller\ResumeContext
+   */
+  protected $controller;
+
+  /**
+   * The current user.
+   *
+   * @var \Drupal\Core\Session\AccountInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $currentUser;
+
+  /**
+   * The string translation service.
+   *
+   * @var \Drupal\Core\StringTranslation\TranslationInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $stringTranslation;
+
+  /**
+   * {@inheritdoc}
+   *
+   * @covers ::__construct
+   */
+  protected function setUp() {
+    $this->currentUser = $this->getMock('\Drupal\Core\Session\AccountInterface');
+
+    $this->stringTranslation = $this->getStringTranslationStub();
+
+    $this->controller = new ResumeContext($this->currentUser, $this->stringTranslation);
+  }
+
+  /**
+   * @covers ::create
+   */
+  function testCreate() {
+    $container = $this->getMock('\Symfony\Component\DependencyInjection\ContainerInterface');
+    $map = array(
+      array('current_user', ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, $this->currentUser),
+      array('string_translation', ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, $this->stringTranslation),
+    );
+    $container->expects($this->any())
+      ->method('get')
+      ->will($this->returnValueMap($map));
+
+    $form = ResumeContext::create($container);
+    $this->assertInstanceOf('\Drupal\payment_reference\Controller\ResumeContext', $form);
+  }
+
+  /**
+   * @covers ::execute
+   */
+  public function testExecute() {
+    $payment_status = $this->getMock('\Drupal\payment\Plugin\Payment\Status\PaymentStatusInterface');
+
+    $payment = $this->getMockBuilder('\Drupal\payment\Entity\Payment')
+      ->disableOriginalConstructor()
+      ->getMock();
+    $payment->expects($this->once())
+      ->method('access')
+      ->with('view')
+      ->will($this->returnValue(TRUE));
+    $payment->expects($this->once())
+      ->method('getStatus')
+      ->will($this->returnValue($payment_status));
+
+    $build = $this->controller->execute($payment);
+    $this->assertInternalType('array', $build);
+  }
+
+  /**
+   * @covers ::title
+   */
+  public function testTitle() {
+    $label = $this->randomMachineName();
+
+    $payment = $this->getMockBuilder('\Drupal\payment\Entity\Payment')
+      ->disableOriginalConstructor()
+      ->getMock();
+    $payment->expects($this->once())
+      ->method('label')
+      ->willReturn($label);
+
+    $this->assertSame($label, $this->controller->title($payment));
+  }
+
+  /**
+   * @covers ::access
+   *
+   * @dataProvider providerTestAccess
+   */
+  public function testAccess($expected, $payment_type_access) {
+    $payment_type = $this->getMock('\Drupal\payment\Plugin\Payment\Type\PaymentTypeInterface');
+    $payment_type->expects($this->once())
+      ->method('resumeContextAccess')
+      ->willReturn($payment_type_access);
+
+    $payment = $this->getMockBuilder('\Drupal\payment\Entity\Payment')
+      ->disableOriginalConstructor()
+      ->getMock();
+    $payment->expects($this->once())
+      ->method('getPaymentType')
+      ->willReturn($payment_type);
+
+    $request = $this->getMockBuilder('Symfony\Component\HttpFoundation\Request')
+      ->disableOriginalConstructor()
+      ->getMock();
+
+    $this->assertSame($expected, $this->controller->access($request, $payment));
+  }
+
+  /**
+   * Provides data to testResumeContextAccess().
+   */
+  public function providerTestAccess() {
+    return array(
+      array(AccessInterface::ALLOW, TRUE),
+      array(AccessInterface::DENY, FALSE),
+    );
+  }
+
+}
+
+}
+
+namespace {
+
+if (!function_exists('drupal_get_path')) {
+  function drupal_get_path() {
+  }
+}
+
+}
diff --git a/payment_reference/tests/src/Element/PaymentReferenceBaseUnitTest.php b/payment_reference/tests/src/Element/PaymentReferenceBaseUnitTest.php
new file mode 100644
index 0000000..2d00c35
--- /dev/null
+++ b/payment_reference/tests/src/Element/PaymentReferenceBaseUnitTest.php
@@ -0,0 +1,993 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\payment_reference\Tests\Element\PaymentReferenceBaseUnitTest.
+ */
+
+namespace Drupal\payment_reference\Tests\Element {
+
+use Drupal\Component\Utility\Random;
+use Drupal\Core\Form\FormState;
+use Drupal\payment_reference\Element\PaymentReferenceBase;
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * @coversDefaultClass \Drupal\payment_reference\Element\PaymentReferenceBase
+ *
+ * @group Payment Reference Field
+ */
+class PaymentReferenceBaseUnitTest extends UnitTestCase {
+
+  /**
+   * The element under test.
+   *
+   * @var \Drupal\payment_reference\Element\PaymentReferenceBase|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $element;
+
+  /**
+   * The date formatter.
+   *
+   * @var \Drupal\Core\DateTime\DateFormatter|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $dateFormatter;
+
+  /**
+   * The link generator.
+   *
+   * @var \Drupal\Core\Utility\LinkGeneratorInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $linkGenerator;
+
+  /**
+   * The payment method selector manager.
+   *
+   * @var \Drupal\payment\Plugin\Payment\MethodSelector\PaymentMethodSelectorManagerInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $paymentMethodSelectorManager;
+
+  /**
+   * The payment queue.
+   *
+   * @var \Drupal\payment\QueueInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $paymentQueue;
+
+  /**
+   * The payment storage.
+   *
+   * @var \Drupal\Core\Entity\EntityStorageInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $paymentStorage;
+
+  /**
+   * The plugin definition.
+   *
+   * @var array
+   */
+  protected $pluginDefinition = array();
+
+  /**
+   * The request stack.
+   *
+   * @var \Symfony\Component\HttpFoundation\RequestStack|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $requestStack;
+
+  /**
+   * The string translation service.
+   *
+   * @var \Drupal\Core\StringTranslation\TranslationInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  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
+   */
+  protected $urlGenerator;
+
+  /**
+   * {@inheritdoc}
+   *
+   * @covers ::__construct
+   */
+  public function setUp() {
+    $this->dateFormatter = $this->getMockBuilder('\Drupal\Core\DateTime\DateFormatter')
+      ->disableOriginalConstructor()
+      ->getMock();
+
+    $this->linkGenerator = $this->getMock('\Drupal\Core\Utility\LinkGeneratorInterface');
+
+    $this->paymentMethodSelectorManager = $this->getMock('\Drupal\payment\Plugin\Payment\MethodSelector\PaymentMethodSelectorManagerInterface');
+
+    $this->paymentQueue = $this->getMock('\Drupal\payment\QueueInterface');
+
+    $this->paymentStorage = $this->getMock('\Drupal\Core\Entity\EntityStorageInterface');
+
+    $this->requestStack = $this->getMockBuilder('\Symfony\Component\HttpFoundation\RequestStack')
+      ->disableOriginalConstructor()
+      ->getMock();
+
+    $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();
+
+    $this->pluginDefinition['class'] = $this->randomMachineName();
+
+    $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()))
+      ->getMockForAbstractClass();
+    $this->element->expects($this->any())
+      ->method('getPaymentQueue')
+      ->willReturn($this->paymentQueue);
+    $this->element->expects($this->any())
+      ->method('getTemporaryPaymentStorage')
+      ->willReturn($this->temporaryPaymentStorage);
+  }
+
+  /**
+   * @covers ::getInfo
+   */
+  public function testGetInfo() {
+    $info = $this->element->getInfo();
+    $this->assertInternalType('array', $info);
+    $this->assertInstanceOf('\Closure', $info['#process'][0]);
+  }
+
+  /**
+   * @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() {
+    $payment_id = mt_rand();
+    $input = $this->randomMachineName();
+    $form_state = $this->getMock('\Drupal\Core\Form\FormStateInterface');
+    $element = array(
+      '#default_value' => $payment_id,
+    );
+
+    $element_sut = $this->element;
+    $this->assertSame($payment_id, $element_sut::valueCallback($element, $input, $form_state));
+  }
+
+  /**
+   * @covers ::valueCallback
+   */
+  public function testValueCallbackWithoutDefaultValue() {
+    $queue_category_id = $this->randomMachineName();
+    $queue_owner_id = $this->randomMachineName();
+    $payment_id = mt_rand();
+
+    $element = array(
+      '#default_value' => NULL,
+      '#queue_category_id' => $queue_category_id,
+      '#queue_owner_id' => $queue_owner_id,
+      '#type' => $this->randomMachineName(),
+    );
+    $input = $this->randomMachineName();
+    $form_state = $this->getMock('\Drupal\Core\Form\FormStateInterface');
+
+    $element_plugin = $this->getMockBuilder('\Drupal\payment_reference\Element\PaymentReferenceBase')
+      ->disableOriginalConstructor()
+      ->getMockForAbstractClass();
+    $element_plugin->expects($this->atLeastOnce())
+      ->method('getPaymentQueue')
+      ->willReturn($this->paymentQueue);
+
+    // We cannot mock ElementInfoManagerInterface, because it does not extend
+    // PluginManagerInterface.
+    $element_info_manager = $this->getMock('\Drupal\Component\Plugin\PluginManagerInterface');
+    $element_info_manager->expects($this->once())
+      ->method('createInstance')
+      ->with($element['#type'])
+      ->willReturn($element_plugin);
+
+    $container = $this->getMock('\Symfony\Component\DependencyInjection\ContainerInterface');
+    $container->expects($this->once())
+      ->method('get')
+      ->with('plugin.manager.element_info')
+      ->willReturn($element_info_manager);
+
+    \Drupal::setContainer($container);
+
+    $this->paymentQueue->expects($this->once())
+      ->method('loadPaymentIds')
+      ->with($queue_category_id, $queue_owner_id)
+      ->willReturn(array($payment_id));
+
+    $element_sut = $this->element;
+    $this->assertSame($payment_id, $element_sut::valueCallback($element, $input, $form_state));
+  }
+
+  /**
+   * @covers ::pay
+   *
+   * @dataProvider providerTestPay
+   */
+  public function testPay($is_payment_execution_interruptive, $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('getPaymentMethodSelector', 'getTemporaryPaymentStorageKey', 'setTemporaryPayment'))
+      ->getMockForAbstractClass();
+
+    $form = array(
+      'foo' => array(
+        'bar' => array(
+          'container' => array(
+            '#id' => $this->randomMachineName(),
+            'payment_form' => array(
+              'pay' => array(
+                '#array_parents' => array('foo', 'bar', 'container', 'payment_form', 'pay'),
+              ),
+              'payment_method' => array(
+                '#foo' => $this->randomMachineName(),
+              ),
+            ),
+          ),
+        ),
+      ),
+    );
+    $form_state = $this->getMock('\Drupal\Core\Form\FormStateInterface');
+    $map = array(
+      array('triggering_element', $form['foo']['bar']['container']['payment_form']['pay']),
+    );
+    $form_state->expects($this->atLeastOnce())
+      ->method('get')
+      ->willReturnMap($map);
+    $form_state->expects($this->once())
+      ->method('setRebuild')
+      ->with(TRUE);
+
+    $request = $this->getMockBuilder('\Symfony\Component\HttpFoundation\Request')
+      ->disableOriginalConstructor()
+      ->getMock();
+    $request->expects($is_payment_execution_interruptive ? $this->atLeastOnce() : $this->never())
+      ->method('isXmlHttpRequest')
+      ->willReturn($is_xml_http_request);
+
+    $this->requestStack->expects($is_payment_execution_interruptive ? $this->atLeastOnce() : $this->never())
+      ->method('getCurrentRequest')
+      ->willReturn($request);
+
+    $payment_method = $this->getMock('\Drupal\payment\Plugin\Payment\Method\PaymentMethodInterface');
+    $payment_method->expects($this->atLeastOnce())
+      ->method('isPaymentExecutionInterruptive')
+      ->willReturn($is_payment_execution_interruptive);
+
+    $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())
+      ->method('setPaymentMethod')
+      ->with($payment_method);
+
+    $payment_method_selector = $this->getMock('\Drupal\payment\Plugin\Payment\MethodSelector\PaymentMethodSelectorInterface');
+    $payment_method_selector->expects($this->atLeastOnce())
+      ->method('getPayment')
+      ->willReturn($payment);
+    $payment_method_selector->expects($this->atLeastOnce())
+      ->method('getPaymentMethod')
+      ->willReturn($payment_method);
+    $payment_method_selector->expects($this->once())
+      ->method('submitConfigurationForm')
+      ->with($form['foo']['bar']['container']['payment_form']['payment_method'], $form_state);
+
+    $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);
+  }
+
+  /**
+   * Provides data to self::testPay().
+   */
+  public function providerTestPay() {
+    return array(
+      array(TRUE, TRUE),
+      array(TRUE, FALSE),
+      array(FALSE, FALSE),
+    );
+  }
+
+  /**
+   * @covers ::ajaxPay
+   *
+   * @dataProvider providerTestAjaxPay
+   */
+  public function testAjaxPay($is_payment_execution_interruptive, $number_of_commands) {
+    $payment = $this->getMockBuilder('\Drupal\payment\Entity\Payment')
+      ->disableOriginalConstructor()
+      ->getMock();
+    $payment->expects($this->atLeastOnce())
+      ->method('createDuplicate')
+      ->willReturnSelf();
+
+    $payment_method = $this->getMock('\Drupal\payment\Plugin\Payment\Method\PaymentMethodInterface');
+    $payment_method->expects($this->once())
+      ->method('isPaymentExecutionInterruptive')
+      ->willReturn($is_payment_execution_interruptive);
+
+    $payment_method_selector_plugin_id = $this->randomMachineName();
+
+    $payment_method_selector = $this->getMock('\Drupal\payment\Plugin\Payment\MethodSelector\PaymentMethodSelectorInterface');
+    $payment_method_selector->expects($this->atLeastOnce())
+      ->method('getPaymentMethod')
+      ->willReturn($payment_method);
+
+    $this->paymentMethodSelectorManager->expects($this->atLeastOnce())
+    ->method('createInstance')
+      ->with($payment_method_selector_plugin_id)
+      ->willReturn($payment_method_selector);
+
+    $form = array(
+      'foo' => array(
+        'bar' => array(
+          '#limit_allowed_payment_method_ids' => array(),
+          '#name' => $this->randomMachineName(),
+          '#payment_method_selector_id' => $payment_method_selector_plugin_id,
+          '#prototype_payment' => $payment,
+          '#required' => TRUE,
+          'container' => array(
+            '#id' => $this->randomMachineName(),
+            'payment_form' => array(
+              'pay' => array(
+                '#array_parents' => array('foo', 'bar', 'container', 'payment_form', 'pay'),
+              ),
+            ),
+          ),
+        ),
+      ),
+    );
+    $form_state = $this->getMock('\Drupal\Core\Form\FormStateInterface');
+    $map = array(
+      array('triggering_element', $form['foo']['bar']['container']['payment_form']['pay']),
+      array('payment_reference.element.payment_reference.payment_method_selector.' . $form['foo']['bar']['#name'], $payment_method_selector),
+    );
+    $form_state->expects($this->atLeastOnce())
+      ->method('get')
+      ->willReturnMap($map);
+
+    $response = $this->element->ajaxPay($form, $form_state);
+    $this->assertInstanceOf('\Drupal\Core\Ajax\AjaxResponse', $response);
+    $this->assertCount($number_of_commands, $response->getCommands());
+  }
+
+  /**
+   * Provides data to self::testAjaxPay().
+   */
+  public function providerTestAjaxPay() {
+    return array(
+      array(TRUE, 2),
+      array(FALSE, 1),
+    );
+  }
+
+  /**
+   * @covers ::refresh
+   */
+  public function testRefresh() {
+    $form = array();
+    $form_state = $this->getMock('\Drupal\Core\Form\FormStateInterface');
+    $form_state->expects($this->once())
+      ->method('setRebuild')
+      ->with(TRUE);
+
+    $this->element->refresh($form, $form_state);
+  }
+
+  /**
+   * @covers ::ajaxRefresh
+   */
+  public function testAjaxRefresh() {
+    $form = array(
+      'foo' => array(
+        'bar' => array(
+          'container' => array(
+            '#id' => $this->randomMachineName(),
+            'refresh' => array(
+              '#array_parents' => array('foo', 'bar', 'container', 'refresh'),
+            ),
+          ),
+        ),
+      ),
+    );
+    $form_state = $this->getMock('\Drupal\Core\Form\FormStateInterface');
+    $form_state->expects($this->once())
+      ->method('get')
+      ->with('triggering_element')
+      ->willReturn($form['foo']['bar']['container']['refresh']);
+
+    $response = $this->element->ajaxRefresh($form, $form_state);
+    $this->assertInstanceOf('\Drupal\Core\Ajax\AjaxResponse', $response);
+  }
+
+  /**
+   * @covers ::disableChildren
+   */
+  public function testDisableChildren() {
+    $element = array(
+      'foo' => array(
+        '#foo' => $this->randomMachineName(),
+        'bar' => array(
+          '#bar' => $this->randomMachineName(),
+        ),
+      ),
+    );
+
+    $expected_element = $element;
+    $expected_element['foo']['#disabled'] = TRUE;
+    $expected_element['foo']['bar']['#disabled'] = TRUE;
+
+    $method = new \ReflectionMethod($this->element, 'disableChildren');
+    $method->setAccessible(TRUE);
+
+    $method->invokeArgs($this->element, array(&$element));
+    $this->assertSame($expected_element, $element);
+  }
+
+  /**
+   * @covers ::getPaymentMethodSelector
+   *
+   * @dataProvider providerGetPaymentMethodSelector
+   */
+  public function testGetPaymentMethodSelector($limit_allowed_payment_method_ids) {
+    $payment = $this->getMockBuilder('\Drupal\payment\Entity\Payment')
+      ->disableOriginalConstructor()
+      ->getMock();
+    $payment->expects($this->once())
+      ->method('createDuplicate')
+      ->willReturnSelf();
+
+    $payment_method_selector_plugin_id = $this->randomMachineName();
+    $required = $this->randomMachineName();
+
+    $element = array(
+      '#limit_allowed_payment_method_ids' => $limit_allowed_payment_method_ids,
+      '#name' => $this->randomMachineName(),
+      '#payment_method_selector_id' => $payment_method_selector_plugin_id,
+      '#prototype_payment' => $payment,
+      '#required' => $required,
+    );
+    $form_state = new FormState();
+
+    $payment_method_selector = $this->getMock('\Drupal\payment\Plugin\Payment\MethodSelector\PaymentMethodSelectorInterface');
+    $payment_method_selector->expects(is_null($limit_allowed_payment_method_ids) ? $this->never() : $this->once())
+      ->method('setAllowedPaymentMethods')
+      ->with($limit_allowed_payment_method_ids);
+    $payment_method_selector->expects($this->once())
+      ->method('setPayment')
+      ->with($payment);
+    $payment_method_selector->expects($this->once())
+      ->method('setRequired')
+      ->with($required);
+
+    $this->paymentMethodSelectorManager->expects($this->once())
+      ->method('createInstance')
+      ->with($payment_method_selector_plugin_id)
+      ->willReturn($payment_method_selector);
+
+    $method = new \ReflectionMethod($this->element, 'getPaymentMethodSelector');
+    $method->setAccessible(TRUE);
+
+    $retrieved_payment_method_selector = $method->invoke($this->element, $element, $form_state);
+    $this->assertInstanceOf('\Drupal\payment\Plugin\Payment\MethodSelector\PaymentMethodSelectorInterface', $retrieved_payment_method_selector);
+    $this->assertSame($retrieved_payment_method_selector, $method->invoke($this->element, $element, $form_state));
+  }
+
+  /**
+   * Provides data to self::testProviderGetPaymentMethodSelector().
+   */
+  public function providerGetPaymentMethodSelector() {
+    return array(
+      array(NULL),
+      array(array()),
+      array(array($this->randomMachineName(), $this->randomMachineName())),
+    );
+  }
+
+  /**
+   * @covers ::buildCompletePaymentLink
+   */
+  public function testBuildCompletePaymentLinkWithoutPaymentMethod() {
+    $configuration = array();
+    $plugin_id = $this->randomMachineName();
+    $this->pluginDefinition = array();
+
+    $element = array(
+      '#foo' => $this->randomMachineName(),
+    );
+    $form_state = $this->getMock('\Drupal\Core\Form\FormStateInterface');
+
+    $payment_method_selector = $this->getMock('\Drupal\payment\Plugin\Payment\MethodSelector\PaymentMethodSelectorInterface');
+
+    $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'))
+      ->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');
+    $method->setAccessible(TRUE);
+
+    $build = $method->invoke($this->element, $element, $form_state);
+    $this->assertSame(array(), $build);
+  }
+
+  /**
+   * @covers ::buildCompletePaymentLink
+   */
+  public function testBuildCompletePaymentLinkWithPaymentMethod() {
+    $configuration = array();
+    $plugin_id = $this->randomMachineName();
+    $this->pluginDefinition = array();
+
+    $element = array(
+      '#foo' => $this->randomMachineName(),
+    );
+    $form_state = $this->getMock('\Drupal\Core\Form\FormStateInterface');
+
+    $payment_method = $this->getMock('\Drupal\payment\Plugin\Payment\Method\PaymentMethodInterface');
+
+    $payment_method_selector = $this->getMock('\Drupal\payment\Plugin\Payment\MethodSelector\PaymentMethodSelectorInterface');
+    $payment_method_selector->expects($this->atLeastOnce())
+      ->method('getPaymentMethod')
+      ->willReturn($payment_method);
+
+    $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'))
+      ->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');
+    $method->setAccessible(TRUE);
+
+    $build = $method->invoke($this->element, $element, $form_state);
+    $this->assertInternalType('array', $build);
+    $this->assertSame('link', $build['link']['#type']);
+  }
+
+  /**
+   * @covers ::buildPaymentView
+   */
+  public function testBuildPaymentViewWithoutPaymentWithDefaultValue() {
+    $element = array(
+      '#default_value' => mt_rand(),
+      '#available_payment_id' => mt_rand(),
+    );
+    $form_state = $this->getMock('\Drupal\Core\Form\FormStateInterface');
+
+    $this->paymentStorage->expects($this->once())
+      ->method('load')
+      ->with($element['#default_value']);
+
+    $method = new \ReflectionMethod($this->element, 'buildPaymentView');
+    $method->setAccessible(TRUE);
+
+    $build = $method->invoke($this->element, $element, $form_state);
+    $this->assertSame(array(), $build);
+  }
+
+  /**
+   * @covers ::buildPaymentView
+   */
+  public function testBuildPaymentViewWithoutPaymentWithOutDefaultValue() {
+    $element = array(
+      '#default_value' => NULL,
+      '#available_payment_id' => mt_rand(),
+    );
+    $form_state = $this->getMock('\Drupal\Core\Form\FormStateInterface');
+
+    $this->paymentStorage->expects($this->once())
+      ->method('load')
+      ->with($element['#available_payment_id']);
+
+    $method = new \ReflectionMethod($this->element, 'buildPaymentView');
+    $method->setAccessible(TRUE);
+
+    $build = $method->invoke($this->element, $element, $form_state);
+    $this->assertSame(array(), $build);
+  }
+
+  /**
+   * @covers ::buildPaymentView
+   *
+   * @dataProvider providerTestBuildPaymentViewWithPayment
+   */
+  public function testBuildPaymentViewWithPayment($view_access) {
+    $element = array(
+      '#default_value' => mt_rand(),
+    );
+    $form_state = $this->getMock('\Drupal\Core\Form\FormStateInterface');
+
+    $currency = $this->getMockBuilder('\Drupal\currency\Entity\Currency')
+      ->disableOriginalConstructor()
+      ->getMock();
+
+    $payment_status = $this->getMock('\Drupal\payment\Plugin\Payment\Status\PaymentStatusInterface');
+
+    $payment = $this->getMockBuilder('\Drupal\payment\Entity\Payment')
+      ->disableOriginalConstructor()
+      ->getMock();
+    $payment->expects($this->atLeastOnce())
+      ->method('access')
+      ->willReturn($view_access);
+    $payment->expects($this->atLeastOnce())
+      ->method('getCurrency')
+      ->willReturn($currency);
+    $payment->expects($this->atLeastOnce())
+      ->method('getStatus')
+      ->willReturn($payment_status);
+    $payment->expects($view_access ? $this->once() : $this->never())
+      ->method('url');
+
+    $this->paymentStorage->expects($this->once())
+      ->method('load')
+      ->with($element['#default_value'])
+      ->willReturn($payment);
+
+    $method = new \ReflectionMethod($this->element, 'buildPaymentView');
+    $method->setAccessible(TRUE);
+
+    $build = $method->invoke($this->element, $element, $form_state);
+    $this->assertInternalType('array', $build);
+  }
+
+  /**
+   * Provides data to self::testBuildPaymentViewWithPayment().
+   */
+  public function providerTestBuildPaymentViewWithPayment() {
+    return array(
+      array(TRUE),
+      array(FALSE),
+    );
+  }
+
+  /**
+   * @covers ::buildRefreshButton
+   */
+  public function testBuildRefreshButton() {
+    $element = array(
+      '#default_value' => mt_rand(),
+      'container' => array(
+        '#id' => $this->randomMachineName(),
+      ),
+    );
+    $form_state = $this->getMock('\Drupal\Core\Form\FormStateInterface');
+
+    $method = new \ReflectionMethod($this->element, 'buildRefreshButton');
+    $method->setAccessible(TRUE);
+
+    $build = $method->invoke($this->element, $element, $form_state);
+    $this->assertInternalType('array', $build);
+    $this->assertInstanceOf('\Closure', $build['#ajax']['callback']);
+    $this->assertSame($build['#submit'][0][0], $this->pluginDefinition['class']);
+  }
+
+  /**
+   * @covers ::buildPaymentForm
+   */
+  public function testBuildPaymentForm() {
+    $element = array(
+      '#parents' => array($this->randomMachineName()),
+    );
+    $form_state = $this->getMock('\Drupal\Core\Form\FormStateInterface');
+
+    $method = new \ReflectionMethod($this->element, 'buildPaymentForm');
+    $method->setAccessible(TRUE);
+
+    $payment_method_selector = $this->getMock('\Drupal\payment\Plugin\Payment\MethodSelector\PaymentMethodSelectorInterface');
+    $payment_method_selector->expects($this->atLeastOnce())
+      ->method('buildConfigurationForm')
+      ->willReturn(array());
+
+    $configuration = array();
+    $plugin_id = $this->randomMachineName();
+
+    $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', 'hasTemporaryPayment'))
+      ->getMockForAbstractClass();
+    $this->element->expects($this->atLeastOnce())
+      ->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);
+    $this->assertInstanceOf('\Closure', $build['pay_button']['#ajax']['callback']);
+    $this->assertInstanceOf('\Closure', $build['pay_button']['#submit'][0]);
+  }
+
+  /**
+   * @covers ::process
+   *
+   * @depends testGetInfo
+   */
+  public function testProcess() {
+    $name = $this->randomMachineName();
+    $prototype_payment = $this->getMockBuilder('\Drupal\payment\Entity\Payment')
+      ->disableOriginalConstructor()
+      ->getMock();
+    $payment_method_selector_id = $this->randomMachineName();
+    $queue_category_id = $this->randomMachineName();
+    $queue_owner_id = mt_rand();
+
+    $element = array(
+      '#default_value' => NULL,
+      '#limit_allowed_payment_method_ids' => NULL,
+      '#name' => $name,
+      '#payment_method_selector_id' => $payment_method_selector_id,
+      '#prototype_payment' => $prototype_payment,
+      '#queue_category_id' => $queue_category_id,
+      '#queue_owner_id' => $queue_owner_id,
+    );
+    $form_state = $this->getMock('\Drupal\Core\Form\FormStateInterface');
+    $form = array();
+
+    $configuration = array();
+    $plugin_id = $this->randomMachineName();
+
+    $payment_form = array(
+      '#foo' => $this->randomMachineName(),
+    );
+
+    $payment_view = array(
+      '#foo' => $this->randomMachineName(),
+    );
+
+    $refresh_button = array(
+      '#foo' => $this->randomMachineName(),
+    );
+
+    $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('buildPaymentForm', 'buildPaymentView', 'buildRefreshButton'))
+      ->getMockForAbstractClass();
+    $this->element->expects($this->atLeastOnce())
+      ->method('buildPaymentForm')
+      ->with($this->isType('array'), $form_state)
+      ->willReturn($payment_form);
+    $this->element->expects($this->atLeastOnce())
+      ->method('buildPaymentView')
+      ->with($this->isType('array'), $form_state)
+      ->willReturn($payment_view);
+    $this->element->expects($this->atLeastOnce())
+      ->method('buildRefreshButton')
+      ->with($this->isType('array'), $form_state)
+      ->willReturn($refresh_button);
+    $this->element->expects($this->atLeastOnce())
+      ->method('getPaymentQueue')
+      ->willReturn($this->paymentQueue);
+
+    $build = $this->element->process($element, $form_state, $form);
+    $this->assertInstanceOf('\Closure', $build['#element_validate'][0]);
+    $this->assertTrue($build['#tree']);
+    unset($build['container']['payment_form']['#access']);
+    $this->assertSame($payment_form, $build['container']['payment_form']);
+    unset($build['container']['payment_view']['#access']);
+    $this->assertSame($payment_view, $build['container']['payment_view']);
+  }
+
+  /**
+   * @covers ::process
+   *
+   * @expectedException \InvalidArgumentException
+   *
+   * @dataProvider providerTestProcess
+   */
+  public function testProcessWithInvalidElementConfiguration(array $element) {
+    $form_state = $this->getMock('\Drupal\Core\Form\FormStateInterface');
+    $form = array();
+
+    $this->element->process($element, $form_state, $form);
+  }
+
+  /**
+   * Provides data to self::testProcess().
+   */
+  public function providerTestProcess() {
+    $name = $this->randomMachineName();
+    $prototype_payment = $this->getMockBuilder('\Drupal\payment\Entity\Payment')
+      ->disableOriginalConstructor()
+      ->getMock();
+    $payment_method_selector_id = $this->randomMachineName();
+    $queue_category_id = $this->randomMachineName();
+    $queue_owner_id = mt_rand();
+
+    $element = array(
+      '#default_value' => NULL,
+      '#limit_allowed_payment_method_ids' => NULL,
+      '#name' => $name,
+      '#payment_method_selector_id' => $payment_method_selector_id,
+      '#prototype_payment' => $prototype_payment,
+      '#queue_category_id' => $queue_category_id,
+      '#queue_owner_id' => $queue_owner_id,
+    );
+
+    return array(
+      array(array_merge($element, array(
+        '#default_value' => $this->randomMachineName(),
+      ))),
+      array(array_merge($element, array(
+        '#limit_allowed_payment_method_ids' => $this->randomMachineName(),
+      ))),
+      array(array_merge($element, array(
+        '#queue_category_id' => mt_rand(),
+      ))),
+      array(array_merge($element, array(
+        '#queue_owner_id' => $this->randomMachineName(),
+      ))),
+      array(array_merge($element, array(
+        '#payment_method_selector_id' => mt_rand(),
+      ))),
+      array(array_merge($element, array(
+        '#prototype_payment' => $this->randomMachineName(),
+      ))),
+    );
+  }
+}
+
+}
+
+namespace {
+
+  if (!function_exists('drupal_render')) {
+    function drupal_render() {
+    }
+  }
+  if (!function_exists('drupal_set_message')) {
+    function drupal_set_message() {
+    }
+  }
+
+}
diff --git a/payment_reference/tests/src/Element/PaymentReferenceUnitTest.php b/payment_reference/tests/src/Element/PaymentReferenceUnitTest.php
new file mode 100644
index 0000000..a2719bb
--- /dev/null
+++ b/payment_reference/tests/src/Element/PaymentReferenceUnitTest.php
@@ -0,0 +1,184 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\payment_reference\Tests\Element\PaymentReferenceUnitTest.
+ */
+
+namespace Drupal\payment_reference\Tests\Element;
+
+use Drupal\Component\Utility\Random;
+use Drupal\payment_reference\Element\PaymentReference;
+use Drupal\Tests\UnitTestCase;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * @coversDefaultClass \Drupal\payment_reference\Element\PaymentReference
+ *
+ * @group Payment Reference Field
+ */
+class PaymentReferenceUnitTest extends UnitTestCase {
+
+  /**
+   * The element under test.
+   *
+   * @var \Drupal\payment_reference\Element\PaymentReference
+   */
+  protected $element;
+
+  /**
+   * The date formatter.
+   *
+   * @var \Drupal\Core\DateTime\DateFormatter|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $dateFormatter;
+
+  /**
+   * The link generator.
+   *
+   * @var \Drupal\Core\Utility\LinkGeneratorInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $linkGenerator;
+
+  /**
+   * The payment method selector manager.
+   *
+   * @var \Drupal\payment\Plugin\Payment\MethodSelector\PaymentMethodSelectorManagerInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $paymentMethodSelectorManager;
+
+  /**
+   * The payment queue.
+   *
+   * @var \Drupal\payment\QueueInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $paymentQueue;
+
+  /**
+   * The payment storage.
+   *
+   * @var \Drupal\Core\Entity\EntityStorageInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $paymentStorage;
+
+  /**
+   * The request stack.
+   *
+   * @var \Symfony\Component\HttpFoundation\RequestStack|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $requestStack;
+
+  /**
+   * The string translation service.
+   *
+   * @var \Drupal\Core\StringTranslation\TranslationInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  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
+   */
+  protected $urlGenerator;
+
+  /**
+   * {@inheritdoc}
+   *
+   * @covers ::__construct
+   */
+  public function setUp() {
+    $this->dateFormatter = $this->getMockBuilder('\Drupal\Core\DateTime\DateFormatter')
+      ->disableOriginalConstructor()
+      ->getMock();
+
+    $this->linkGenerator = $this->getMock('\Drupal\Core\Utility\LinkGeneratorInterface');
+
+    $this->paymentMethodSelectorManager = $this->getMock('\Drupal\payment\Plugin\Payment\MethodSelector\PaymentMethodSelectorManagerInterface');
+
+    $this->paymentQueue = $this->getMock('\Drupal\payment\QueueInterface');
+
+    $this->paymentStorage = $this->getMock('\Drupal\Core\Entity\EntityStorageInterface');
+
+    $this->requestStack = $this->getMockBuilder('\Symfony\Component\HttpFoundation\RequestStack')
+      ->disableOriginalConstructor()
+      ->getMock();
+
+    $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);
+  }
+
+  /**
+   * @covers ::create
+   */
+  function testCreate() {
+    $entity_manager = $this->getMock('\Drupal\Core\Entity\EntityManagerInterface');
+    $entity_manager->expects($this->once())
+      ->method('getStorage')
+      ->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_method_selector.payment_select')
+      ->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),
+      array('request_stack', ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, $this->requestStack),
+      array('string_translation', ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, $this->stringTranslation),
+    );
+    $container->expects($this->any())
+      ->method('get')
+      ->will($this->returnValueMap($map));
+
+    $configuration = array();
+    $plugin_id = $this->randomMachineName();
+    $plugin_definition = array();
+
+    $form = PaymentReference::create($container, $configuration, $plugin_id, $plugin_definition);
+    $this->assertInstanceOf('\Drupal\payment_reference\Element\PaymentReference', $form);
+  }
+
+  /**
+   * @covers ::getPaymentQueue
+   */
+  public function testGetPaymentQueue() {
+    $method = new \ReflectionMethod($this->element, 'getPaymentQueue');
+    $method->setAccessible(TRUE);
+    $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/payment_reference/tests/src/Entity/Payment/PaymentFormUnitTest.php b/payment_reference/tests/src/Entity/Payment/PaymentFormUnitTest.php
deleted file mode 100644
index 3a91574..0000000
--- a/payment_reference/tests/src/Entity/Payment/PaymentFormUnitTest.php
+++ /dev/null
@@ -1,255 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\payment_reference\Tests\Entity\Payment\PaymentFormUnitTest.
- */
-
-namespace Drupal\payment_reference\Tests\Entity\Payment;
-
-use Drupal\Core\Form\FormState;
-use Drupal\payment_reference\Entity\Payment\PaymentForm;
-use Drupal\Tests\UnitTestCase;
-use Symfony\Component\DependencyInjection\ContainerInterface;
-
-/**
- * @coversDefaultClass \Drupal\payment_reference\Entity\Payment\PaymentForm
- *
- * @group Payment Form Field
- */
-class PaymentFormUnitTest extends UnitTestCase {
-
-  /**
-   * The config factory used for testing.
-   *
-   * @var \Drupal\Core\Config\ConfigFactory|\PHPUnit_Framework_MockObject_MockObject
-   */
-  protected $configFactory;
-
-  /**
-   * The configuration the config factory returns.
-   *
-   * @see self::__construct
-   *
-   * @var array
-   */
-  protected $configFactoryConfiguration = array();
-
-  /**
-   * The entity manager.
-   *
-   * @var \Drupal\Core\Entity\EntityManagerInterface
-   */
-  protected $entityManager;
-
-  /**
-   * The form under test.
-   *
-   * @var \Drupal\payment_reference\Entity\Payment\PaymentForm
-   */
-  protected $form;
-
-  /**
-   * The form display.
-   *
-   * @var \Drupal\Core\Entity\Display\EntityFormDisplayInterface|\PHPUnit_Framework_MockObject_MockObject
-   */
-  protected $formDisplay;
-
-  /**
-   * A payment entity used for testing.
-   *
-   * @var \Drupal\payment\Entity\Payment|\PHPUnit_Framework_MockObject_MockObject
-   */
-  protected $payment;
-
-  /**
-   * The payment method selector used for testing.
-   *
-   * @var \Drupal\payment\Plugin\Payment\MethodSelector\PaymentMethodSelectorInterface|\PHPUnit_Framework_MockObject_MockObject
-   */
-  protected $paymentMethodSelector;
-
-  /**
-   * The payment method selector manager used for testing.
-   *
-   * @var \Drupal\payment\Plugin\Payment\MethodSelector\PaymentMethodSelectorManagerInterface|\PHPUnit_Framework_MockObject_MockObject
-   */
-  protected $paymentMethodSelectorManager;
-
-  /**
-   * The string translation service.
-   *
-   * @var \Drupal\Core\StringTranslation\TranslationInterface|\PHPUnit_Framework_MockObject_MockObject
-   */
-  protected $stringTranslation;
-
-  /**
-   * {@inheritdoc}
-   *
-   * @covers ::__construct
-   */
-  protected function setUp() {
-    $this->entityManager = $this->getMock('\Drupal\Core\Entity\EntityManagerInterface');
-
-    $this->formDisplay = $this->getMock('\Drupal\Core\Entity\Display\EntityFormDisplayInterface');
-
-    $this->payment = $this->getMockBuilder('\Drupal\payment\Entity\Payment')
-      ->disableOriginalConstructor()
-      ->getMock();
-
-    $this->paymentMethodSelector = $this->getMock('\Drupal\payment\Plugin\Payment\MethodSelector\PaymentMethodSelectorInterface');
-
-    $this->paymentMethodSelectorManager = $this->getMock('\Drupal\payment\Plugin\Payment\MethodSelector\PaymentMethodSelectorManagerInterface');
-
-    $this->stringTranslation = $this->getStringTranslationStub();
-
-    $this->configFactoryConfiguration = array(
-      'payment_reference.payment_type' => array(
-        'limit_allowed_payment_methods' => TRUE,
-        'allowed_payment_method_ids' => array($this->randomMachineName()),
-        'payment_method_selector_id' => $this->randomMachineName(),
-      ),
-    );
-
-    $this->configFactory = $this->getConfigFactoryStub($this->configFactoryConfiguration);
-
-    $this->form = new PaymentForm($this->entityManager, $this->stringTranslation, $this->paymentMethodSelectorManager);
-    $this->form->setConfigFactory($this->configFactory);
-    $this->form->setEntity($this->payment);
-  }
-
-  /**
-   * @covers ::create
-   */
-  function testCreate() {
-    $container = $this->getMock('\Symfony\Component\DependencyInjection\ContainerInterface');
-    $map = array(
-      array('entity.manager', ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, $this->entityManager),
-      array('plugin.manager.payment.method_selector', ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, $this->paymentMethodSelectorManager),
-      array('string_translation', ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, $this->stringTranslation),
-    );
-    $container->expects($this->any())
-      ->method('get')
-      ->will($this->returnValueMap($map));
-
-    $form = PaymentForm::create($container);
-    $this->assertInstanceOf('\Drupal\payment_reference\Entity\Payment\PaymentForm', $form);
-  }
-
-  /**
-   * @covers ::form
-   */
-  public function testForm() {
-    $payment_method_selector_build = array(
-      '#type' => $this->randomMachineName(),
-    );
-    $this->paymentMethodSelector->expects($this->atLeastOnce())
-      ->method('buildConfigurationForm')
-      ->will($this->returnValue($payment_method_selector_build));
-    $this->paymentMethodSelector->expects($this->once())
-      ->method('setAllowedPaymentMethods')
-      ->with($this->configFactoryConfiguration['payment_reference.payment_type']['allowed_payment_method_ids']);
-
-    $this->paymentMethodSelectorManager->expects($this->once())
-      ->method('createInstance')
-      ->with($this->configFactoryConfiguration['payment_reference.payment_type']['payment_method_selector_id'])
-      ->will($this->returnValue($this->paymentMethodSelector));
-
-    $payment_type = $this->getMock('\Drupal\payment\Plugin\Payment\Type\PaymentTypeInterface');
-    $this->payment->expects($this->any())
-      ->method('getPaymentType')
-      ->will($this->returnValue($payment_type));
-    $entity_type = $this->getMock('\Drupal\Core\Entity\EntityTypeInterface');
-    $this->payment->expects($this->any())
-      ->method('entityInfo')
-      ->will($this->returnValue($entity_type));
-
-    $form = array(
-      'langcode' => array(),
-    );
-    // @todo Mock FormStateInterface once ContentEntityForm no longer uses
-    //   array access.
-    $form_state = new FormState();
-    $this->form->setFormDisplay($this->formDisplay, $form_state);
-    $build = $this->form->form($form, $form_state);
-    // Build the form a second time to make sure the payment method selector is
-    // only instantiated once.
-    $this->form->form($form, $form_state);
-    $this->assertInternalType('array', $build);
-    $this->assertArrayHasKey('line_items', $build);
-    $this->assertSame($this->payment, $build['line_items']['#payment']);
-    $this->assertArrayHasKey('payment_method', $build);
-    $this->assertSame($payment_method_selector_build, $build['payment_method']);
-  }
-
-  /**
-   * @covers ::validateForm
-   */
-  public function testValidateForm() {
-    $form = array(
-      'payment_method' => array(
-        '#type' => $this->randomMachineName(),
-      ),
-    );
-    $form_state = $this->getMock('\Drupal\Core\Form\FormStateInterface');
-    $form_state->expects($this->atLeastOnce())
-      ->method('get')
-      ->with('payment_method_selector')
-      ->willReturn($this->paymentMethodSelector);
-
-    $this->paymentMethodSelector->expects($this->once())
-      ->method('validateConfigurationForm')
-      ->with($form['payment_method'], $form_state);
-
-    $this->form->validateForm($form, $form_state);
-  }
-
-  /**
-   * @covers ::submit
-   */
-  public function testSubmit() {
-    $form = array(
-      'payment_method' => array(
-        '#type' => $this->randomMachineName(),
-      ),
-    );
-    $form_state = $this->getMock('\Drupal\Core\Form\FormStateInterface');
-    $form_state->expects($this->atLeastOnce())
-      ->method('get')
-      ->with('payment_method_selector')
-      ->willReturn($this->paymentMethodSelector);
-
-    $payment_method = $this->getMock('\Drupal\payment\Plugin\Payment\Method\PaymentMethodInterface');
-
-    $this->paymentMethodSelector->expects($this->once())
-      ->method('getPaymentMethod')
-      ->will($this->returnValue($payment_method));
-    $this->paymentMethodSelector->expects($this->once())
-      ->method('submitConfigurationForm')
-      ->with($form['payment_method'], $form_state);
-
-    $this->payment->expects($this->once())
-      ->method('setPaymentMethod')
-      ->with($payment_method);
-    $this->payment->expects($this->once())
-      ->method('save');
-    $this->payment->expects($this->once())
-      ->method('execute');
-
-    $this->form->submit($form, $form_state);
-  }
-
-  /**
-   * @covers ::actions
-   */
-  public function testActions() {
-    $form = array();
-    $form_state = $this->getMock('\Drupal\Core\Form\FormStateInterface');
-
-    $method = new \ReflectionMethod($this->form, 'actions');
-    $method->setAccessible(TRUE);
-    $method->invokeArgs($this->form, array($form, $form_state));
-  }
-
-}
diff --git a/payment_reference/tests/src/PaymentFactoryUnitTest.php b/payment_reference/tests/src/PaymentFactoryUnitTest.php
new file mode 100644
index 0000000..115d411
--- /dev/null
+++ b/payment_reference/tests/src/PaymentFactoryUnitTest.php
@@ -0,0 +1,153 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\payment_reference\Tests\PaymentFactoryUnitTest.
+ */
+
+namespace Drupal\payment_reference\Tests;
+
+use Drupal\payment_reference\PaymentFactory;
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * @coversDefaultClass \Drupal\payment_reference\PaymentFactory
+ *
+ * @group Payment Reference Field
+ */
+class PaymentFactoryUnitTest extends UnitTestCase {
+
+  /**
+   * The entity manager used for testing.
+   *
+   * @var \Drupal\Core\Entity\EntityManagerInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $entityManager;
+
+  /**
+   * The payment line item manager used for testing.
+   *
+   * @var \Drupal\payment\Plugin\Payment\LineItem\PaymentLineItemManagerInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $paymentLineItemManager;
+
+  /**
+   * The factory under test.
+   *
+   * @var \Drupal\payment_reference\PaymentFactory
+   */
+  protected $factory;
+
+  /**
+   * {@inheritdoc}
+   *
+   * @covers ::__construct
+   */
+  public function setUp() {
+    $this->entityManager = $this->getMock('\Drupal\Core\Entity\EntityManagerInterface');
+
+    $this->paymentLineItemManager = $this->getMock('\Drupal\payment\Plugin\Payment\LineItem\PaymentLineItemManagerInterface');
+
+    $this->factory = new PaymentFactory($this->entityManager, $this->paymentLineItemManager);
+  }
+
+  /**
+   * @covers ::createPayment
+   */
+  public function testCreatePayment() {
+    $bundle = $this->randomMachineName();
+    $currency_code = $this->randomMachineName();
+    $entity_type_id = $this->randomMachineName();
+    $field_name = $this->randomMachineName();
+
+    $payment_type = $this->getMockBuilder('\Drupal\payment_reference\Plugin\Payment\Type\PaymentReference')
+      ->disableOriginalConstructor()
+      ->getMock();
+    $payment_type->expects($this->once())
+      ->method('setBundle')
+      ->with($bundle);
+    $payment_type->expects($this->once())
+      ->method('setEntityTypeId')
+      ->with($entity_type_id);
+    $payment_type->expects($this->once())
+      ->method('setFieldName')
+      ->with($field_name);
+
+    $payment = $this->getMockBuilder('\Drupal\payment\Entity\Payment')
+      ->disableOriginalConstructor()
+      ->getMock();
+    $payment->expects($this->once())
+      ->method('setCurrencyCode')
+      ->with($currency_code);
+    $payment->expects($this->once())
+      ->method('getPaymentType')
+      ->willReturn($payment_type);
+
+    $payment_storage = $this->getMock('\Drupal\Core\Entity\EntityStorageInterface');
+    $payment_storage->expects($this->once())
+      ->method('create')
+      ->with(array(
+        'bundle' => 'payment_reference',
+      ))
+      ->willReturn($payment);
+
+    $this->entityManager->expects($this->once())
+      ->method('getStorage')
+      ->with('payment')
+      ->willReturn($payment_storage);
+
+    $line_item_a = $this->getMock('\Drupal\payment\Plugin\Payment\LineItem\PaymentLineItemInterface');
+    $line_item_plugin_id_a = $this->randomMachineName();
+    $line_item_plugin_configuration_a = array(
+      'foo' => $this->randomMachineName(),
+    );
+    $line_item_b = $this->getMock('\Drupal\payment\Plugin\Payment\LineItem\PaymentLineItemInterface');
+    $line_item_plugin_id_b = $this->randomMachineName();
+    $line_item_plugin_configuration_b = array(
+      'bar' => $this->randomMachineName(),
+    );
+
+    $field_storage_definition = $this->getMock('\Drupal\Core\Field\FieldStorageDefinitionInterface');
+    $field_storage_definition->expects($this->once())
+      ->method('getTargetEntityTypeId')
+      ->willReturn($entity_type_id);
+
+    $field_definition = $this->getMock('\Drupal\Core\Field\FieldDefinitionInterface');
+    $field_definition->expects($this->once())
+      ->method('getBundle')
+      ->willReturn($bundle);
+    $field_definition->expects($this->once())
+      ->method('getFieldStorageDefinition')
+      ->willReturn($field_storage_definition);
+    $field_definition->expects($this->once())
+      ->method('getName')
+      ->willreturn($field_name);
+    $map = array(
+      array('currency_code', $currency_code),
+      array('line_items_data', array(
+        array(
+          'plugin_configuration' => $line_item_plugin_configuration_a,
+          'plugin_id' => $line_item_plugin_id_a,
+        ),
+        array(
+          'plugin_configuration' => $line_item_plugin_configuration_b,
+          'plugin_id' => $line_item_plugin_id_b,
+        ),
+      )),
+    );
+    $field_definition->expects($this->exactly(2))
+      ->method('getSetting')
+      ->willReturnMap($map);
+
+    $this->paymentLineItemManager->expects($this->at(0))
+      ->method('createInstance')
+      ->with($line_item_plugin_id_a, $line_item_plugin_configuration_a)
+      ->willReturn($line_item_a);
+    $this->paymentLineItemManager->expects($this->at(1))
+      ->method('createInstance')
+      ->with($line_item_plugin_id_b, $line_item_plugin_configuration_b)
+      ->willReturn($line_item_b);
+
+    $this->assertSame($payment, $this->factory->createPayment($field_definition));
+  }
+}
diff --git a/payment_reference/tests/src/PaymentReferenceUnitTest.php b/payment_reference/tests/src/PaymentReferenceUnitTest.php
index 6bd2129..1c24cfe 100644
--- a/payment_reference/tests/src/PaymentReferenceUnitTest.php
+++ b/payment_reference/tests/src/PaymentReferenceUnitTest.php
@@ -29,4 +29,15 @@ class PaymentReferenceUnitTest extends UnitTestCase {
     $this->assertSame($queue, PaymentReference::queue());
   }
 
+  /**
+   * @covers ::factory
+   */
+  public function testFactory() {
+    $container = new Container();
+    $factory = $this->getMock('\Drupal\payment\FactoryInterface');
+    $container->set('payment_reference.payment_factory', $factory);
+    \Drupal::setContainer($container);
+    $this->assertSame($factory, PaymentReference::factory());
+  }
+
 }
diff --git a/payment_reference/tests/src/Plugin/Field/FieldType/PaymentReferenceUnitTest.php b/payment_reference/tests/src/Plugin/Field/FieldType/PaymentReferenceUnitTest.php
index a0a38e6..c730c86 100644
--- a/payment_reference/tests/src/Plugin/Field/FieldType/PaymentReferenceUnitTest.php
+++ b/payment_reference/tests/src/Plugin/Field/FieldType/PaymentReferenceUnitTest.php
@@ -123,23 +123,4 @@ class PaymentReferenceUnitTest extends UnitTestCase {
     $this->assertSame(array(), $this->fieldType->settingsForm($form, $form_state, $has_data));
   }
 
-  /**
-   * @covers ::preSave
-   */
-  public function testPreSave() {
-    $payment_id = mt_rand();
-    $acquisition_code = $this->randomMachineName();
-    $this->targetId->expects($this->once())
-      ->method('getValue')
-      ->will($this->returnValue($payment_id));
-    $this->queue->expects($this->once())
-      ->method('claimPayment')
-      ->with($payment_id)
-      ->will($this->returnValue($acquisition_code));
-    $this->queue->expects($this->once())
-      ->method('acquirePayment')
-      ->with($payment_id, $acquisition_code);
-    $this->fieldType->preSave();
-  }
-
 }
diff --git a/payment_reference/tests/src/Plugin/Field/FieldWidget/PaymentReferenceUnitTest.php b/payment_reference/tests/src/Plugin/Field/FieldWidget/PaymentReferenceUnitTest.php
index e0aa63a..6ef7ffd 100644
--- a/payment_reference/tests/src/Plugin/Field/FieldWidget/PaymentReferenceUnitTest.php
+++ b/payment_reference/tests/src/Plugin/Field/FieldWidget/PaymentReferenceUnitTest.php
@@ -20,6 +20,22 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
 class PaymentReferenceUnitTest extends UnitTestCase {
 
   /**
+   * The config factory used for testing.
+   *
+   * @var \Drupal\Core\Config\ConfigFactoryInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $configFactory;
+
+  /**
+   * The configuration the config factory returns.
+   *
+   * @see self::setUp
+   *
+   * @var array
+   */
+  protected $configFactoryConfiguration = array();
+
+  /**
    * A user account used for testing.
    *
    * @var \Drupal\Core\Session\AccountInterface|\PHPUnit_Framework_MockObject_MockObject
@@ -34,6 +50,13 @@ class PaymentReferenceUnitTest extends UnitTestCase {
   protected $fieldDefinition;
 
   /**
+   * The payment reference factory used for testing.
+   *
+   * @var \Drupal\payment_reference\PaymentFactoryInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $paymentFactory;
+
+  /**
    * The field widget plugin under test.
    *
    * @var \Drupal\payment_reference\Plugin\Field\FieldWidget\PaymentReference|\PHPUnit_Framework_MockObject_MockObject
@@ -48,9 +71,21 @@ class PaymentReferenceUnitTest extends UnitTestCase {
   protected function setUp() {
     $this->fieldDefinition = $this->getMock('\Drupal\Core\Field\FieldDefinitionInterface');
 
+    $this->configFactoryConfiguration = array(
+      'payment_reference.payment_type' => array(
+        'limit_allowed_payment_methods' => TRUE,
+        'allowed_payment_method_ids' => array($this->randomMachineName()),
+        'payment_method_selector_id' => $this->randomMachineName(),
+      ),
+    );
+
+    $this->configFactory = $this->getConfigFactoryStub($this->configFactoryConfiguration);
+
     $this->currentUser = $this->getMock('\Drupal\Core\Session\AccountInterface');
 
-    $this->widget = new PaymentReference($this->randomMachineName(), array(), $this->fieldDefinition, array(), array(), $this->currentUser);
+    $this->paymentFactory = $this->getMock('\Drupal\payment_reference\PaymentFactoryInterface');
+
+    $this->widget = new PaymentReference($this->randomMachineName(), array(), $this->fieldDefinition, array(), array(), $this->configFactory, $this->currentUser, $this->paymentFactory);
   }
 
   /**
@@ -59,7 +94,9 @@ class PaymentReferenceUnitTest extends UnitTestCase {
   function testCreate() {
     $container = $this->getMock('\Symfony\Component\DependencyInjection\ContainerInterface');
     $map = array(
+      array('config.factory', ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, $this->configFactory),
       array('current_user', ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, $this->currentUser),
+      array('payment_reference.payment_factory', ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, $this->paymentFactory),
     );
     $container->expects($this->any())
       ->method('get')
@@ -73,7 +110,7 @@ class PaymentReferenceUnitTest extends UnitTestCase {
     $plugin_definition = array();
     $plugin_id = $this->randomMachineName();
     $plugin = PaymentReference::create($container, $configuration, $plugin_id, $plugin_definition);
-    $this->assertInstanceOf('\Drupal\payment_reference\Plugin\Field\FIeldWidget\PaymentReference', $plugin);
+    $this->assertInstanceOf('\Drupal\payment_reference\Plugin\Field\FieldWidget\PaymentReference', $plugin);
   }
 
   /**
@@ -100,20 +137,15 @@ class PaymentReferenceUnitTest extends UnitTestCase {
     $this->fieldDefinition->expects($this->once())
       ->method('isRequired')
       ->will($this->returnValue($required));
-    $currency_code = 'EUR';
-    $line_items_data = array(
-      array(
-        'plugin_configuration' => array(),
-        'plugin_id' => $this->randomMachineName(),
-      ),
-    );
-    $map = array(
-      array('currency_code', $currency_code),
-      array('line_items_data', $line_items_data),
-    );
-    $this->fieldDefinition->expects($this->exactly(2))
-      ->method('getSetting')
-      ->will($this->returnValueMap($map));
+
+    $payment = $this->getMockBuilder('\Drupal\payment\ENtity\Payment')
+      ->disableOriginalConstructor()
+      ->getMock();
+
+    $this->paymentFactory->expects($this->once())
+      ->method('createPayment')
+      ->with($this->fieldDefinition)
+      ->will($this->returnValue($payment));
 
     $this->currentUser->expects($this->exactly(1))
       ->method('id')
@@ -130,14 +162,13 @@ class PaymentReferenceUnitTest extends UnitTestCase {
     $form_state = $this->getMock('\Drupal\Core\Form\FormStateInterface');
     $build = $this->widget->formElement($items, 3, array(), $form, $form_state);
     $expected_build = array(
-      'payment_id' => array(
-        '#bundle' => $bundle,
+      'target_id' => array(
         '#default_value' => NULL,
-        '#entity_type_id' => $entity_type_id,
-        '#field_name' => $field_name,
-        '#owner_id' => (int) $user_id,
-        '#payment_line_items_data' => $line_items_data,
-        '#payment_currency_code' => $currency_code,
+        '#limit_allowed_payment_method_ids' => $this->configFactoryConfiguration['payment_reference.payment_type']['allowed_payment_method_ids'],
+        '#payment_method_selector_id' => $this->configFactoryConfiguration['payment_reference.payment_type']['payment_method_selector_id'],
+        '#prototype_payment' => $payment,
+        '#queue_category_id' => $entity_type_id . '.' . $bundle . '.' . $field_name,
+        '#queue_owner_id' => (int) $user_id,
         '#required' => $required,
         '#type' => 'payment_reference',
       ),
@@ -145,4 +176,25 @@ class PaymentReferenceUnitTest extends UnitTestCase {
     $this->assertSame($expected_build, $build);
   }
 
+  /**
+   * @covers ::massageFormValues
+   */
+  public function testMassageFormValues() {
+    $field_name = $this->randomMachineName();
+    $payment_id = mt_rand();
+
+    $this->fieldDefinition->expects($this->atLeastOnce())
+      ->method('getName')
+      ->willReturn($field_name);
+
+    $form_state = $this->getMock('\Drupal\Core\Form\FormStateInterface');
+    $form[$field_name]['widget']['target_id']['#value'] = $payment_id;
+    $values = array();
+
+    $expected_value = array(
+      'target_id' => $payment_id,
+    );
+    $this->assertSame($expected_value, $this->widget->massageFormValues($values, $form, $form_state));
+  }
+
 }
diff --git a/payment_reference/tests/src/Plugin/Payment/Type/PaymentReferenceUnitTest.php b/payment_reference/tests/src/Plugin/Payment/Type/PaymentReferenceUnitTest.php
index ddca98f..e9a7708 100644
--- a/payment_reference/tests/src/Plugin/Payment/Type/PaymentReferenceUnitTest.php
+++ b/payment_reference/tests/src/Plugin/Payment/Type/PaymentReferenceUnitTest.php
@@ -149,25 +149,6 @@ class PaymentReferenceUnitTest extends UnitTestCase {
   }
 
   /**
-   * @covers ::getFieldId
-   *
-   * @depends testGetEntityTypeId
-   * @depends testGetBundle
-   * @depends testGetFieldName
-   */
-  public function testGetFieldId() {
-    $entity_type_id = $this->randomMachineName();
-    $bundle = $this->randomMachineName();
-    $field_name = $this->randomMachineName();
-
-    $this->paymentType->setEntityTypeId($entity_type_id);
-    $this->paymentType->setBundle($bundle);
-    $this->paymentType->setFieldName($field_name);
-
-    $this->assertSame("$entity_type_id.$bundle.$field_name", $this->paymentType->getFieldId());
-  }
-
-  /**
    * @covers ::paymentDescription
    *
    * @depends testGetEntityTypeId
@@ -220,19 +201,65 @@ class PaymentReferenceUnitTest extends UnitTestCase {
 
   /**
    * @covers ::resumeContextAccess
+   *
+   * @dataProvider providerTestResumeContextAccess
    */
-  public function testResumeContextAccess() {
+  public function testResumeContextAccess($expected, $payment_owner_id, $account_id) {
     $account = $this->getMock('\Drupal\Core\Session\AccountInterface');
+    $account->expects($this->atLeastOnce())
+      ->method('id')
+      ->willReturn($account_id);
 
-    $this->assertTrue($this->paymentType->resumeContextAccess($account));
+    $this->payment->expects($this->atLeastOnce())
+      ->method('getOwnerId')
+      ->willReturn($payment_owner_id);
+
+    $this->assertSame($expected, $this->paymentType->resumeContextAccess($account));
+  }
+
+  /**
+   * Provides data to self::testResumeContextAccess().
+   */
+  public function providerTestResumeContextAccess() {
+    $id_a = mt_rand();
+    $id_b = mt_rand();
+
+    return array(
+      array(TRUE, $id_a, $id_a),
+      array(TRUE, $id_b, $id_b),
+      array(FALSE, $id_a, $id_b),
+    );
+  }
+
+  /**
+   * @covers ::doResumeContext
+   */
+  public function testDoResumeContextWithoutInterruption() {
+    $payment_method = $this->getMock('\Drupal\payment\Plugin\Payment\Method\PaymentMethodInterface');
+
+    $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 testDoResumeContext() {
+  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()
diff --git a/payment_reference_test/src/PaymentReferenceElement.php b/payment_reference_test/src/PaymentReferenceElement.php
index 2f3c3c6..e95146d 100644
--- a/payment_reference_test/src/PaymentReferenceElement.php
+++ b/payment_reference_test/src/PaymentReferenceElement.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\payment_reference_test;
 
+use Drupal\Component\Utility\Random;
 use Drupal\Core\Form\FormInterface;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\payment\Tests\Generate;
@@ -27,13 +28,35 @@ class PaymentReferenceElement implements FormInterface {
    * {@inheritdoc}
    */
   public function buildForm(array $form, FormStateInterface $form_state) {
+    $key = 'payment_reference_element_prototype_payment';
+    if ($form_state->has($key)) {
+      $prototype_payment = $form_state->get($key);
+      /** @var \Drupal\payment_reference\Plugin\Payment\Type\PaymentReference $payment_type */
+      $payment_type = $prototype_payment->getPaymentType();
+    }
+    else {
+      $entity_type_id = 'user';
+      $bundle = 'user';
+      $field_name = 'foobarbaz';
+      /** @var \Drupal\payment\Entity\PaymentInterface $prototype_payment */
+      $prototype_payment = entity_create('payment', array(
+        'bundle' => 'payment_reference',
+      ));
+      $prototype_payment->setCurrencyCode('EUR')
+        ->setOwnerId(2)
+        ->setLineItems(Generate::createPaymentLineItems());
+      /** @var \Drupal\payment_reference\Plugin\Payment\Type\PaymentReference $payment_type */
+      $payment_type = $prototype_payment->getPaymentType();
+      $payment_type->setEntityTypeId($entity_type_id);
+      $payment_type->setBundle($bundle);
+      $payment_type->setFieldName($field_name);
+      $form_state->set($key, $prototype_payment);
+    }
     $form['payment_reference'] = array(
-      '#entity_type_id' => 'user',
-      '#bundle' => 'user',
-      '#field_name' => 'foobarbaz',
-      '#owner_id' => 2,
-      '#payment_line_items' => Generate::createPaymentLineItems(),
-      '#payment_currency_code' => 'EUR',
+      '#payment_method_selector_id' => 'payment_select',
+      '#prototype_payment' => $prototype_payment,
+      '#queue_category_id' => $payment_type->getEntityTypeId() . '.' . $payment_type->getBundle(). '.' . $payment_type->getFieldName(),
+      '#queue_owner_id' => 2,
       '#required' => TRUE,
       '#title' => 'FooBarBaz',
       '#type' => 'payment_reference',
@@ -56,6 +79,7 @@ class PaymentReferenceElement implements FormInterface {
    * {@inheritdoc}
    */
   public function submitForm(array &$form, FormStateInterface $form_state) {
-    \Drupal::state()->set('payment_reference_test_payment_reference_element', $form_state->getValues()['payment_reference']);
+
+    \Drupal::state()->set('payment_reference_test_payment_reference_element', $form['payment_reference']['#value']);
   }
 }
diff --git a/payment_test/src/PaymentSelectPaymentMethodSelectorForm.php b/payment_test/src/PaymentSelectPaymentMethodSelectorForm.php
index e29a2ea..5a5042a 100644
--- a/payment_test/src/PaymentSelectPaymentMethodSelectorForm.php
+++ b/payment_test/src/PaymentSelectPaymentMethodSelectorForm.php
@@ -12,6 +12,7 @@ use Drupal\Core\DependencyInjection\DependencySerializationTrait;
 use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Form\FormInterface;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\payment\Entity\PaymentMethodConfiguration;
 use Drupal\payment\Tests\Generate;
 use Drupal\payment\Plugin\Payment\MethodSelector\PaymentMethodSelectorManagerInterface;
 use Drupal\payment\Plugin\Payment\Type\PaymentTypeManagerInterface;
@@ -84,6 +85,13 @@ class PaymentSelectPaymentMethodSelectorForm implements ContainerInjectionInterf
       $payment_method_selector = $this->paymentMethodSelectorManager->createInstance('payment_select');
       $payment_method_selector->setPayment($payment);
       $payment_method_selector->setRequired();
+      /** @var \Drupal\payment\Entity\PaymentMethodConfigurationInterface[] $payment_method_configurations */
+      $payment_method_configurations = PaymentMethodConfiguration::loadMultiple();
+      $allowed_payment_method_ids = array();
+      foreach ($payment_method_configurations as $payment_method_configuration) {
+        $allowed_payment_method_ids[] = 'payment_basic:' . $payment_method_configuration->id();
+      }
+      $payment_method_selector->setAllowedPaymentMethods($allowed_payment_method_ids);
       $form_state->set('payment_method_selector', $payment_method_selector);
     }
 
diff --git a/payment_test/src/Plugin/Payment/Method/PaymentTest.php b/payment_test/src/Plugin/Payment/Method/PaymentTest.php
deleted file mode 100644
index eea6aec..0000000
--- a/payment_test/src/Plugin/Payment/Method/PaymentTest.php
+++ /dev/null
@@ -1,61 +0,0 @@
-<?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",
- *   label = @Translation("Test method")
- * )
- */
-class PaymentTest extends PaymentMethodBase {
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function getSupportedCurrencies() {
-    return array();
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function doExecutePayment() {
-  }
-
-  /**
-   * {@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/payment_test/src/Plugin/Payment/Method/PaymentTestInterruptive.php b/payment_test/src/Plugin/Payment/Method/PaymentTestInterruptive.php
new file mode 100644
index 0000000..ab6f448
--- /dev/null
+++ b/payment_test/src/Plugin/Payment/Method/PaymentTestInterruptive.php
@@ -0,0 +1,106 @@
+<?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_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 \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, EventDispatcherInterface $event_dispatcher, Token $token, PaymentStatusManagerInterface $payment_status_manager) {
+    $configuration += $this->defaultConfiguration();
+    parent::__construct($configuration, $plugin_id, $plugin_definition, $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('event_dispatcher'), $container->get('token'), $container->get('plugin.manager.payment.status'));
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getSupportedCurrencies() {
+    return TRUE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function doExecutePayment() {
+    $this->getPayment()->setStatus($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/payment_test/src/Plugin/Payment/Method/PaymentTestUninterruptive.php b/payment_test/src/Plugin/Payment/Method/PaymentTestUninterruptive.php
new file mode 100644
index 0000000..2d8448d
--- /dev/null
+++ b/payment_test/src/Plugin/Payment/Method/PaymentTestUninterruptive.php
@@ -0,0 +1,36 @@
+<?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/payment_test/src/Plugin/Payment/Type/PaymentTest.php b/payment_test/src/Plugin/Payment/Type/PaymentTest.php
index ff5435d..cbcd31a 100644
--- a/payment_test/src/Plugin/Payment/Type/PaymentTest.php
+++ b/payment_test/src/Plugin/Payment/Type/PaymentTest.php
@@ -6,6 +6,7 @@
 
 namespace Drupal\payment_test\Plugin\Payment\Type;
 
+use Drupal\Core\Session\AccountInterface;
 use Drupal\payment\Plugin\Payment\Type\PaymentTypeBase;
 
 /**
@@ -28,6 +29,14 @@ class PaymentTest extends PaymentTypeBase {
   /**
    * {@inheritdoc
    */
+  public function resumeContextAccess(AccountInterface $account) {
+    return FALSE;
+  }
+
+  /**
+   * {@inheritdoc
+   */
   public function doResumeContext() {
   }
+
 }
