diff --git a/payment/lib/Drupal/payment/Plugin/field/field_type/FormField.php b/payment/lib/Drupal/payment/Plugin/field/field_type/FormField.php
new file mode 100644
index 0000000..1813a8d
--- /dev/null
+++ b/payment/lib/Drupal/payment/Plugin/field/field_type/FormField.php
@@ -0,0 +1,113 @@
+<?php
+
+/**
+ * Contains Drupal\payment\Plugin\field\field_type\FormField.
+ */
+
+namespace Drupal\payment\Plugin\field\field_type;
+
+use Drupal\field\Plugin\Core\Entity\Field;
+use Drupal\field\Plugin\Type\FieldType\ConfigFieldItemBase;
+use Drupal\currency\Plugin\Core\Entity\Currency;
+
+/**
+ * Defines a form field.
+ */
+class FormField extends ConfigFieldItemBase {
+
+  /**
+   * Definitions of the contained properties.
+   *
+   * @see static::getPropertyDefinitions()
+   *
+   * @var array
+   */
+  static $propertyDefinitions;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function schema(Field $field) {
+    $schema = array(
+      'columns' => array(
+        'amount' => array(
+          'type' => 'float',
+          'size' => 'big',
+          'not null' => TRUE,
+        ),
+        'description' => array(
+          'type' => 'varchar',
+          'length' => 255,
+          'default' => '',
+        ),
+        'name' => array(
+          'type' => 'varchar',
+          'length' => '255',
+          'not null' => TRUE,
+        ),
+        'quantity' => array(
+          'type' => 'int',
+          'not null' => TRUE,
+        ),
+        'tax_rate' => array(
+          'type' => 'float',
+          'size' => 'big',
+          'not null' => TRUE,
+        ),
+      ),
+    );
+
+    return $schema;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPropertyDefinitions() {
+    // @todo
+
+
+
+    $this->definition['settings']['target_type'] = 'file';
+    // Definitions vary by entity type and bundle, so key them accordingly.
+    $key = $this->definition['settings']['target_type'] . ':';
+    $key .= isset($this->definition['settings']['target_bundle']) ? $this->definition['settings']['target_bundle'] : '';
+
+    if (!isset(static::$propertyDefinitions[$key])) {
+      static::$propertyDefinitions[$key] = parent::getPropertyDefinitions();
+
+      static::$propertyDefinitions[$key]['alt'] = array(
+        'type' => 'string',
+        'label' => t("Alternative image text, for the image's 'alt' attribute."),
+      );
+      static::$propertyDefinitions[$key]['title'] = array(
+        'type' => 'string',
+        'label' => t("Image title text, for the image's 'title' attribute."),
+      );
+      static::$propertyDefinitions[$key]['width'] = array(
+        'type' => 'integer',
+        'label' => t('The width of the image in pixels.'),
+      );
+      static::$propertyDefinitions[$key]['height'] = array(
+        'type' => 'integer',
+        'label' => t('The height of the image in pixels.'),
+      );
+    }
+    return static::$propertyDefinitions[$key];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function instanceSettingsForm(array $form, array &$form_state) {
+    $form['currency_code'] = array(
+      '#type' => 'select',
+      '#title' => t('Currency'),
+      '#options' => Currency::options(),
+      '#default_value' => $this->getInstance()->settings['currency_code'],
+      '#required' => TRUE,
+    );
+
+    return $form;
+  }
+}
diff --git a/payment/lib/Drupal/payment/Plugin/field/formatter/FormField.php b/payment/lib/Drupal/payment/Plugin/field/formatter/FormField.php
new file mode 100644
index 0000000..8b1ffd9
--- /dev/null
+++ b/payment/lib/Drupal/payment/Plugin/field/formatter/FormField.php
@@ -0,0 +1,56 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\payment\Plugin\field\formatter\FormField.
+ */
+
+namespace Drupal\payment\Plugin\field\formatter;
+
+use Drupal\field\Annotation\FieldFormatter;
+use Drupal\Core\Annotation\Translation;
+use Drupal\field\Plugin\Type\Formatter\FormatterBase;
+use Drupal\Core\Entity\EntityInterface;
+
+/**
+ * Plugin implementation of the 'list_default' formatter.
+ *
+ * @FieldFormatter(
+ *   id = "payment_form",
+ *   label = @Translation("Payment form"),
+ *   field_types = {
+ *     "payment_form",
+ *   }
+ * )
+ */
+class FormField extends FormatterBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function viewElements(EntityInterface $entity, $langcode, array $items) {
+    // @todo
+
+
+
+
+    $context_manager = \Drupal::service('plugin.manager.payment.context');
+    $line_item_manager = \Drupal::service('plugin.manager.payment.line_item');
+
+    // @todo Set the entity reference.
+    $context = $context_manager->createInstance('payment_form');
+    $payment = entity_create('payment', array())
+      ->setPaymentContext($context)
+    // @todo
+      ->setCurrencyCode()
+      ->setLineItems($items);
+
+    // @todo return an entity controller form. See https://drupal.org/node/2047919.
+    return drupal_get_form('payment_form_standalone', $payment);
+
+
+
+
+  }
+
+}
diff --git a/payment/lib/Drupal/payment/Plugin/field/widget/FormField.php b/payment/lib/Drupal/payment/Plugin/field/widget/FormField.php
new file mode 100644
index 0000000..0cb22ec
--- /dev/null
+++ b/payment/lib/Drupal/payment/Plugin/field/widget/FormField.php
@@ -0,0 +1,89 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\payment\Plugin\field\widget\EmailDefaultWidget.
+ */
+
+namespace Drupal\payment\Plugin\field\widget;
+
+use Drupal\field\Annotation\FieldWidget;
+use Drupal\Core\Annotation\Translation;
+use Drupal\field\Plugin\Type\Widget\WidgetBase;
+
+/**
+ * A payment configuration widget.
+ *
+ * @FieldWidget(
+ *   field_types = {
+ *     "payment_form"
+ *   },
+ *   id = "payment_form",
+ *   label = @Translation("Payment configuration"),
+ *   multiple_values = "true",
+ *   settings = {
+ *     "placeholder" = ""
+ *   }
+ * )
+ */
+class FormField extends WidgetBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function settingsForm(array $form, array &$form_state) {
+    global $user;
+
+    $default_value = array();
+    foreach ($items as $line_item_data) {
+      $default_value[] = new PaymentLineItem($line_item_data);
+    }
+    if ($instance['widget']['type'] == 'paymentform_line_item') {
+      $element = array(
+          '#type' => 'value',
+          // Use a validation callback to convert the PaymentLineItem objects from
+          // the payment_line_item element to arrays usable by Field API.
+          '#element_validate' => array('paymentform_field_widget_form_validate'),
+        ) + $element;
+      $element['line_item'] = array(
+        '#type' => 'payment_line_item',
+        '#currency_code' => $instance['settings']['currency_code'],
+        '#cardinality' => (int) $field['cardinality'],
+        '#default_value' => $default_value,
+        '#required' => $element['#required'],
+      );
+    }
+
+    return $element;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function settingsSummary() {
+    $summary = array();
+
+    $placeholder = $this->getSetting('placeholder');
+    if (!empty($placeholder)) {
+      $summary[] = t('Placeholder: @placeholder', array('@placeholder' => $placeholder));
+    }
+    else {
+      $summary[] = t('No placeholder');
+    }
+
+    return $summary;
+  }
+
+  /**
+   * Implements Drupal\field\Plugin\Type\Widget\WidgetInterface::formElement().
+   */
+  public function formElement(array $items, $delta, array $element, $langcode, array &$form, array &$form_state) {
+    $element['value'] = $element + array(
+        '#type' => 'email',
+        '#default_value' => isset($items[$delta]['value']) ? $items[$delta]['value'] : NULL,
+        '#placeholder' => $this->getSetting('placeholder'),
+      );
+    return $element;
+  }
+
+}
diff --git a/payment/lib/Drupal/payment/Plugin/payment/context/Base.php b/payment/lib/Drupal/payment/Plugin/payment/context/Base.php
index 843f04f..82b7743 100644
--- a/payment/lib/Drupal/payment/Plugin/payment/context/Base.php
+++ b/payment/lib/Drupal/payment/Plugin/payment/context/Base.php
@@ -7,6 +7,7 @@
 namespace Drupal\payment\Plugin\payment\context;
 
 use Drupal\Component\Plugin\PluginBase;
+use \Drupal\payment\Plugin\Core\Entity\PaymentInterface;
 use Drupal\payment\Plugin\payment\context\ContextInterface;
 
 /**
@@ -18,24 +19,21 @@ abstract class Base extends PluginBase implements PaymentContextInterface {
    * {@inheritdoc}
    */
   public function __construct(array $configuration, $plugin_id, array $plugin_definition) {
-    $configuration += array(
-      'paymentId' => 0,
-    );
     parent::__construct($configuration, $plugin_id, $plugin_definition);
   }
 
   /**
    * {@inheritdoc}
    */
-  public function getPaymentId() {
-    return $this->configuration['paymentId'];
+  public function getPayment() {
+    return $this->payment;
   }
 
   /**
    * {@inheritdoc}
    */
-  public function setPaymentId($id) {
-    $this->configuration['paymentId'] = $id;
+  public function setPayment(PaymentInterface $payment) {
+    $this->payment = $payment;
 
     return $this;
   }
diff --git a/payment/lib/Drupal/payment/Plugin/payment/context/FormField.php b/payment/lib/Drupal/payment/Plugin/payment/context/FormField.php
new file mode 100644
index 0000000..bffa46c
--- /dev/null
+++ b/payment/lib/Drupal/payment/Plugin/payment/context/FormField.php
@@ -0,0 +1,62 @@
+<?php
+
+/**
+ * Contains \Drupal\payment\Plugin\payment\context\FormField.
+ */
+
+namespace Drupal\payment\Plugin\payment\context;
+
+
+use Drupal\Core\Annotation\Translation;
+use Drupal\payment\Annotations\PaymentContext;
+use Symfony\Component\HttpFoundation\RedirectResponse;
+
+/**
+ * An unavailable context.
+ *
+ * @PaymentContext(
+ *   id = "payment_form",
+ *   label = @Translation("Payment form field")
+ * )
+ */
+class FormField extends Base {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function resume() {
+    parent::resume();
+    $payment = $this->getPayment();
+    if ($payment->access('view')) {
+      $uri = $payment->uri();
+      $view = ' ' . l(t('View payment'), $uri['path'], $uri['options']) . '.';
+    }
+    if ($payment->getStatus()->isOrHasAncestor('payment_pending')) {
+      drupal_set_message(t('Your payment is still being processed.') . $view);
+    }
+    elseif ($payment->getStatus()->isOrHasAncestor('payment_success')) {
+      drupal_set_message(t('Your payment was successfully completed.') . $view);
+    }
+    elseif ($payment->getStatus()->isOrHasAncestor('payment_failed')) {
+      drupal_set_message(t('Your payment failed.') . $view);
+    }
+
+    // @todo Add an entity reference field to the payment and use that.
+    $entity = entity_load($payment->context_data['entity_type'], $payment->context_data['entity_id']);
+    if ($uri = entity_uri($payment->context_data['entity_type'], $entity)) {
+      return new RedirectResponse(url($uri['path'], $uri['options']));
+    }
+    return new RedirectResponse(url('<front>'));
+
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function paymentDescription() {
+    // @todo Add an entity reference field to the payment and use that, or
+    //   perhaps add a configurable description to the entity, or use the entity
+    //   label, or–like 7.x-1.x–use the field instance label.
+    return t('Unavailable');
+  }
+}
diff --git a/payment/lib/Drupal/payment/Plugin/payment/context/PaymentContextInterface.php b/payment/lib/Drupal/payment/Plugin/payment/context/PaymentContextInterface.php
index 137d7a5..2e48f29 100644
--- a/payment/lib/Drupal/payment/Plugin/payment/context/PaymentContextInterface.php
+++ b/payment/lib/Drupal/payment/Plugin/payment/context/PaymentContextInterface.php
@@ -7,6 +7,7 @@
 namespace Drupal\payment\Plugin\payment\context;
 
 use Drupal\Component\Plugin\PluginInspectionInterface;
+use Drupal\payment\Plugin\Core\Entity\PaymentInterface;
 
 /**
  * A payment context plugin.
@@ -14,20 +15,20 @@ use Drupal\Component\Plugin\PluginInspectionInterface;
 interface PaymentContextInterface extends PluginInspectionInterface {
 
   /**
-   * Sets the ID of the payment this context is of.
+   * Sets the payment this context is of.
    *
-   * @param int $id
-   * *
-   * * @return \Drupal\payment\Plugin\payment\context\ContextInterface
+   * @param \Drupal\payment\Plugin\Core\Entity\PaymentInterface $payment
+   *
+   * @return \Drupal\payment\Plugin\payment\context\ContextInterface
    */
-  public function setPaymentId($id);
+  public function setPayment(PaymentInterface $payment);
 
   /**
-   * Gets the ID of the payment this context is of.
+   * Gets the payment this context is of.
    *
-   * @param int
+   * @return \Drupal\payment\Plugin\Core\Entity\PaymentInterface
    */
-  public function getPaymentId();
+  public function getPayment();
 
   /**
    * Returns the description of the payment this context is of.
diff --git a/paymentform/paymentform.info.yml b/paymentform/paymentform.info.yml
deleted file mode 100644
index 083bcb3..0000000
--- a/paymentform/paymentform.info.yml
+++ /dev/null
@@ -1,8 +0,0 @@
-name: Payment Form Field
-description: Provides a field to add payment forms to entities.
-core: 8.x
-type: module
-package: Payment
-dependencies:
-  - field
-  - payment
diff --git a/paymentform/paymentform.install b/paymentform/paymentform.install
deleted file mode 100644
index 00c0bf8..0000000
--- a/paymentform/paymentform.install
+++ /dev/null
@@ -1,44 +0,0 @@
-<?php
-
-/**
- * @file
- * Installation and uninstallation functions.
- */
-
-/**
- * Implements hook_field_schema().
- */
-function paymentform_field_schema($field) {
-  if ($field['type'] == 'paymentform') {
-    $schema = array(
-      'columns' => array(
-        'amount' => array(
-          'type' => 'float',
-          'size' => 'big',
-          'not null' => TRUE,
-        ),
-        'description' => array(
-          'type' => 'varchar',
-          'length' => 255,
-          'default' => '',
-        ),
-        'name' => array(
-          'type' => 'varchar',
-          'length' => '255',
-          'not null' => TRUE,
-        ),
-        'quantity' => array(
-          'type' => 'int',
-          'not null' => TRUE,
-        ),
-        'tax_rate' => array(
-          'type' => 'float',
-          'size' => 'big',
-          'not null' => TRUE,
-        ),
-      ),
-    );
-  }
-
-  return $schema;
-}
\ No newline at end of file
diff --git a/paymentform/paymentform.module b/paymentform/paymentform.module
deleted file mode 100644
index b62b22d..0000000
--- a/paymentform/paymentform.module
+++ /dev/null
@@ -1,244 +0,0 @@
-<?php
-
-/**
- * @file
- *   Hook implementations and general functions.
- */
-
-/**
- * Implements hook_field_info().
- */
-function paymentform_field_info() {
-  $field['paymentform'] = array(
-    'label' => t('Payment form'),
-    'description' => t('Adds a payment form to the <em>view</em> page.'),
-    'instance_settings' => array(
-      'currency_code' => 'XXX',
-    ),
-    'default_widget' => 'paymentform_line_item',
-    'default_formatter' => 'paymentform',
-  );
-
-  return $field;
-}
-
-/**
- * Implements hook_field_widget_info().
- */
-function paymentform_field_widget_info() {
-  $widget['paymentform_line_item'] = array(
-    'label' => t('Payment line item configuration'),
-    'description' => t("Allows users to configure line items for the form's payment."),
-    'field types' => array('paymentform'),
-    'behaviors' => array(
-      'multiple values' => FIELD_BEHAVIOR_CUSTOM,
-      'default value' => FIELD_BEHAVIOR_NONE,
-    ),
-  );
-
-  return $widget;
-}
-
-/**
- * Implements hook_field_formatter_info().
- */
-function paymentform_field_formatter_info() {
-  $formatters['paymentform'] = array(
-    'label' => t('Payment form'),
-    'field types' => array('paymentform'),
-  );
-
-  return $formatters;
-}
-
-/**
- * Implements hook_field_instance_settings_form().
- */
-function paymentform_field_instance_settings_form($field, $instance) {
-  $form = array();
-
-  if ($instance['widget']['type'] == 'paymentform_line_item') {
-    $form['currency_code'] = array(
-      '#type' => 'select',
-      '#title' => t('Currency'),
-      '#options' => payment_currency_options(),
-      '#default_value' => $instance['settings']['currency_code'],
-      '#required' => TRUE,
-    );
-  }
-
-  return $form;
-}
-
-/**
- * Implements hook_field_widget_form().
- */
-function paymentform_field_widget_form(array &$form, array &$form_state, array $field, array $instance, $langcode, array $items, $delta, array $element) {
-  global $user;
-
-  $default_value = array();
-  foreach ($items as $line_item_data) {
-    $default_value[] = new PaymentLineItem($line_item_data);
-  }
-  if ($instance['widget']['type'] == 'paymentform_line_item') {
-    $element = array(
-      '#type' => 'value',
-      // Use a validation callback to convert the PaymentLineItem objects from
-      // the payment_line_item element to arrays usable by Field API.
-      '#element_validate' => array('paymentform_field_widget_form_validate'),
-    ) + $element;
-    $element['line_item'] = array(
-      '#type' => 'payment_line_item',
-      '#currency_code' => $instance['settings']['currency_code'],
-      '#cardinality' => (int) $field['cardinality'],
-      '#default_value' => $default_value,
-      '#required' => $element['#required'],
-    );
-  }
-
-  return $element;
-}
-
-/**
- * Implements hook_field_validate().
- */
-function paymentform_field_validate($entity_type, $entity, $field, $instance, $langcode, $items, &$errors) {
-  if ($field['type'] == 'paymentform') {
-    foreach ($items as $delta => $line_item_data) {
-      $base = array(
-        'delta' => $delta,
-      );
-
-      // Amount.
-      $base['property'] = 'amount';
-      if (!is_float($line_item_data['amount'])) {
-        $errors[$field['field_name']][$langcode][$delta][] = array(
-          'error' => 'paymentform_line_item_amount_no_float',
-          'message' => t('The amount is no floating point.'),
-        ) + $base;
-      }
-
-      // Quantity.
-      $base['property'] = 'quantity';
-      if (!is_int($line_item_data['quantity'])) {
-        $errors[$field['field_name']][$langcode][$delta][] = array(
-          'error' => 'paymentform_line_item_quantity_no_integer',
-          'message' => t('The quantity is no integer.'),
-        ) + $base;
-      }
-      elseif ($line_item_data['quantity'] < 0) {
-        $errors[$field['field_name']][$langcode][$delta][] = array(
-          'error' => 'paymentform_line_item_quantity_negative',
-          'message' => t('The quantity cannot be negative.'),
-        ) + $base;
-      }
-
-      // Tax rate.
-      $base['property'] = 'tax_rate';
-      if (!is_float($line_item_data['tax_rate'])) {
-        $errors[$field['field_name']][$langcode][$delta][] = array(
-          'error' => 'paymentform_line_item_tax_rate_no_float',
-          'message' => t('The tax rate is no floating point.'),
-        ) + $base;
-      }
-      elseif ($line_item_data['tax_rate'] < 0) {
-        $errors[$field['field_name']][$langcode][$delta][] = array(
-          'error' => 'paymentform_line_item_tax_rate_negative',
-          'message' => t('The tax rate cannot be negative.'),
-        ) + $base;
-      }
-
-      // Machine name.
-      $base['property'] = 'name';
-      if (empty($line_item_data['name'])) {
-        $errors[$field['field_name']][$langcode][$delta][] = array(
-          'error' => 'paymentform_line_item_name_empty',
-          'message' => t('The machine-readable name is empty.'),
-        ) + $base;
-      }
-    }
-  }
-}
-
-/**
- * Implements hook_field_widget_error().
- */
-function paymentform_field_widget_error($element, $error, $form, &$form_state) {
-  // This is a form-element specific error.
-  if (isset($error['delta']) && isset($error['property'])) {
-    form_error($element['line_item']['container_' . $error['delta']][$error['property']], $error['message']);
-  }
-  // This is a field-wide error.
-  else {
-    form_error($element['line_item'], $error['message']);
-  }
-}
-
-/**
- * Implements hook_field_is_empty().
- */
-function paymentform_field_is_empty($item, array $field) {
-  if ($field['type'] == 'paymentform') {
-    return empty($item);
-  }
-}
-
-/**
- * Implements hook_field_formatter_view().
- */
-function paymentform_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
-  if ($display['type'] == 'paymentform') {
-    list($entity_id) = entity_extract_ids($entity_type, $entity);
-    $payment = new Payment(array(
-      'context_data' => array(
-        'entity_type' => $entity_type,
-        'entity_id' => $entity_id,
-      ),
-      'currency_code' => $instance['settings']['currency_code'],
-      'description' => $instance['label'],
-      'finish_callback' => 'paymentform_payment_finish',
-    ));
-    foreach ($items as $line_item_data) {
-      $payment->setLineItem(new PaymentLineItem($line_item_data));
-    }
-
-    return drupal_get_form('payment_form_standalone', $payment);
-  }
-}
-
-/**
- * Implements Payment::finish_callback.
- */
-function paymentform_payment_finish(Payment $payment) {
-  if (payment_access('view', $payment)) {
-    $view = ' ' . l(t('View payment'), 'payment/' . $payment->pid) . '.';
-  }
-  if (payment_status_is_or_has_ancestor($payment->getStatus()->status, PAYMENT_STATUS_PENDING)) {
-    drupal_set_message(t('Your payment is still being processed.') . $view);
-  }
-  elseif (payment_status_is_or_has_ancestor($payment->getStatus()->status, PAYMENT_STATUS_SUCCESS)) {
-    drupal_set_message(t('Your payment was successfully completed.') . $view);
-  }
-  elseif (payment_status_is_or_has_ancestor($payment->getStatus()->status, PAYMENT_STATUS_FAILED)) {
-    drupal_set_message(t('Your payment failed.') . $view);
-  }
-
-  $entity = entity_load_single($payment->context_data['entity_type'], $payment->context_data['entity_id']);
-  if ($uri = entity_uri($payment->context_data['entity_type'], $entity)) {
-    drupal_goto($uri['path'], $uri['options']);
-  }
-  drupal_goto('<front>');
-}
-
-
-
-/**
- * Implements form validate callback for a paymentform_line_item field widget.
- */
-function paymentform_field_widget_form_validate(array $element, array &$form_state) {
-  $line_items = array();
-  foreach ($form_state['values'][$element['#field_name']][$element['#language']]['line_item'] as $line_item) {
-    $line_items[] = (array) $line_item;
-  }
-  form_set_value($element, $line_items, $form_state);
-}
\ No newline at end of file
