diff --git a/modules/payment_test/src/Plugin/Field/FieldType/MockPluginBagItem.php b/modules/payment_test/src/Plugin/Field/FieldType/MockPluginBagItem.php
new file mode 100644
index 0000000..49d58d8
--- /dev/null
+++ b/modules/payment_test/src/Plugin/Field/FieldType/MockPluginBagItem.php
@@ -0,0 +1,30 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\payment_test\Plugin\Field\FieldType\PluginBagItem.
+ */
+
+namespace Drupal\payment_test\Plugin\Field\FieldType;
+
+use Drupal\payment\Plugin\Field\FieldType\PluginBagItemBase;
+use Drupal\payment_test\Plugin\PluginTest\MockManager;
+
+/**
+ * Provides a plugin bag field item for testing.
+ *
+ * @FieldType(
+ *   id = "payment_test_plugin_bag",
+ *   label = @Translation("Plugin bag")
+ * )
+ */
+class MockPluginBagItem extends PluginBagItemBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPluginManager() {
+    return new MockManager();
+  }
+
+}
diff --git a/modules/payment_test/src/Plugin/PluginTest/MockConfigurablePlugin.php b/modules/payment_test/src/Plugin/PluginTest/MockConfigurablePlugin.php
new file mode 100644
index 0000000..69a0b9c
--- /dev/null
+++ b/modules/payment_test/src/Plugin/PluginTest/MockConfigurablePlugin.php
@@ -0,0 +1,67 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\payment_test\Plugin\PluginTest\MockPlugin.
+ */
+
+namespace Drupal\payment_test\Plugin\PluginTest;
+
+use Drupal\Component\Plugin\ConfigurablePluginInterface;
+use Drupal\Core\Plugin\PluginBase;
+use Drupal\payment\Plugin\Payment\FieldTypePluginInterface;
+use Drupal\payment\Plugin\Payment\FieldTypePluginTrait;
+
+/**
+ * Provides a configurable mock plugin.
+ */
+class MockConfigurablePlugin extends PluginBase implements ConfigurablePluginInterface, FieldTypePluginInterface {
+
+  use FieldTypePluginTrait;
+
+  /**
+   * Constructs a Drupal\Component\Plugin\PluginBase object.
+   *
+   * @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.
+   */
+  public function __construct(array $configuration, $plugin_id, $plugin_definition) {
+    $this->configuration = $configuration;
+    $this->pluginId = $plugin_id;
+    $this->pluginDefinition = $plugin_definition;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function defaultConfiguration() {
+    return [];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setConfiguration(array $configuration) {
+    $this->configuration = $configuration + $this->defaultConfiguration();
+    $this->setContainingFieldItemConfiguration($this->configuration);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getConfiguration() {
+    return $this->configuration;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function calculateDependencies() {
+    return [];
+  }
+
+}
diff --git a/modules/payment_test/src/Plugin/PluginTest/MockManager.php b/modules/payment_test/src/Plugin/PluginTest/MockManager.php
new file mode 100644
index 0000000..ff0738f
--- /dev/null
+++ b/modules/payment_test/src/Plugin/PluginTest/MockManager.php
@@ -0,0 +1,37 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\payment_test\Plugin\PluginTest\MockManager.
+ */
+
+namespace Drupal\payment_test\Plugin\PluginTest;
+
+use Drupal\Component\Plugin\Factory\DefaultFactory;
+use Drupal\Component\Plugin\PluginManagerBase;
+use Drupal\Component\Plugin\Discovery\StaticDiscovery;
+
+/**
+ * Provides a plugin manager for testing plugni-related functionality.
+ */
+class MockManager extends PluginManagerBase {
+
+  /**
+   * Constructs a new instance.
+   */
+  public function __construct() {
+    $this->discovery = new StaticDiscovery();
+
+    $this->discovery->setDefinition('payment_test_plugin', array(
+      'label' => t('Plugin'),
+      'class' => 'Drupal\payment_test\Plugin\PluginTest\MockPlugin',
+    ));
+
+    $this->discovery->setDefinition('payment_test_configurable_plugin', array(
+      'label' => t('Configurable plugin'),
+      'class' => 'Drupal\payment_test\Plugin\PluginTest\MockConfigurablePlugin',
+    ));
+
+    $this->factory = new DefaultFactory($this->discovery);
+  }
+}
diff --git a/modules/payment_test/src/Plugin/PluginTest/MockPlugin.php b/modules/payment_test/src/Plugin/PluginTest/MockPlugin.php
new file mode 100644
index 0000000..9b7e129
--- /dev/null
+++ b/modules/payment_test/src/Plugin/PluginTest/MockPlugin.php
@@ -0,0 +1,21 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\payment_test\Plugin\PluginTest\MockPlugin.
+ */
+
+namespace Drupal\payment_test\Plugin\PluginTest;
+
+use Drupal\Core\Plugin\PluginBase;
+use Drupal\payment\Plugin\Payment\FieldTypePluginInterface;
+use Drupal\payment\Plugin\Payment\FieldTypePluginTrait;
+
+/**
+ * Provides a mock plugin.
+ */
+class MockPlugin extends PluginBase implements FieldTypePluginInterface {
+
+  use FieldTypePluginTrait;
+
+}
diff --git a/src/Entity/Payment.php b/src/Entity/Payment.php
index bddb36e..9aaadd5 100644
--- a/src/Entity/Payment.php
+++ b/src/Entity/Payment.php
@@ -7,7 +7,6 @@
 
 namespace Drupal\payment\Entity;
 
-use Drupal\Component\Plugin\Discovery\CachedDiscoveryInterface;
 use Drupal\Core\Entity\ContentEntityBase;
 use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\Core\Entity\EntityTypeInterface;
@@ -61,54 +60,6 @@ use Drupal\user\UserInterface;
 class Payment extends ContentEntityBase implements PaymentInterface {
 
   /**
-   * The payment method.
-   *
-   * @var \Drupal\payment\Plugin\Payment\Method\PaymentMethodInterface
-   */
-  protected $method;
-
-  /**
-   * The payment type.
-   *
-   * @var \Drupal\payment\Plugin\Payment\Type\PaymentTypeInterface
-   */
-  protected $type;
-
-  /**
-   * Line items.
-   *
-   * @var \Drupal\payment\Plugin\Payment\LineItem\PaymentLineItemInterface[]
-   *   Keys are line item machine names.
-   */
-  protected $lineItems = [];
-
-  /**
-   * Payment statuses.
-   *
-   * @var array
-   *   Values are \Drupal\payment\Plugin\Payment\Status\PaymentStatusInterface
-   *   objects.
-   */
-  protected $statuses = [];
-
-  /**
-   * {@inheritdoc}
-   */
-  public function __construct(array $values, $entity_type, $bundle = FALSE, $translations = []) {
-    parent::__construct($values, $entity_type, $bundle, $translations);
-    $payment_type_manager = PaymentServiceWrapper::typeManager();
-    if ($payment_type_manager instanceof CachedDiscoveryInterface) {
-      $payment_type_manager->clearCachedDefinitions();
-    }
-    // When a payment is newly created, its bundle is set, but there is no
-    // plugin yet.
-    if (!isset($values['type'])) {
-      $this->type = PaymentServiceWrapper::typeManager()->createInstance($this->bundle());
-    }
-    $this->type->setPayment($this);
-  }
-
-  /**
    * {@inheritdoc}
    */
   public function getChangedTime() {
@@ -126,7 +77,10 @@ class Payment extends ContentEntityBase implements PaymentInterface {
    * {@inheritdoc}
    */
   public function getPaymentType() {
-    return $this->type;
+    /** @var \Drupal\payment\Plugin\Field\FieldType\PluginBagItemBase $payment_type_item */
+    $payment_type_item = $this->get('payment_type')[0];
+
+    return $payment_type_item->getContainedPluginInstance();
   }
 
   /**
@@ -172,6 +126,11 @@ class Payment extends ContentEntityBase implements PaymentInterface {
     $line_item->setPayment($this);
     $this->lineItems[$line_item->getName()] = $line_item;
 
+
+    $line_items = $this->getLineItems();
+    $line_items[] = $line_item;
+    $this->set('line_items', $line_items);
+
     return $this;
   }
 
@@ -220,7 +179,7 @@ class Payment extends ContentEntityBase implements PaymentInterface {
     foreach ($statuses as $status) {
       $status->setPayment($this);
     }
-    $this->statuses = array_values($statuses);
+    $this->get('payment_statuses')->setValue($statuses, FALSE);
 
     return $this;
   }
@@ -265,7 +224,9 @@ class Payment extends ContentEntityBase implements PaymentInterface {
    */
   public function setPaymentMethod(PluginPaymentMethodInterface $payment_method) {
     $payment_method->setPayment($this);
-    $this->method = $payment_method;
+    /** @var \Drupal\payment\Plugin\Field\FieldType\PluginBagItemBase $payment_type_item */
+    $payment_type_item = $this->get('payment_method')[0];
+    $payment_type_item->setContainedPluginInstance($payment_method);
 
     return $this;
   }
@@ -274,7 +235,10 @@ class Payment extends ContentEntityBase implements PaymentInterface {
    * {@inheritdoc}
    */
   public function getPaymentMethod() {
-    return $this->method;
+    /** @var \Drupal\payment\Plugin\Field\FieldType\PluginBagItemBase $payment_type_item */
+    $payment_type_item = $this->get('payment_method')[0];
+
+    return $payment_type_item->getContainedPluginInstance();
   }
 
   /**
@@ -351,39 +315,6 @@ class Payment extends ContentEntityBase implements PaymentInterface {
   /**
    * {@inheritdoc}
    */
-  public static function postLoad(EntityStorageInterface $storage, array &$entities) {
-    /** @var \Drupal\payment\Entity\PaymentInterface[] $entities */
-    foreach ($entities as $payment) {
-      if ($payment->getPaymentMethod()) {
-        $payment->getPaymentMethod()->setPayment($payment);
-      }
-    }
-    /** @var \Drupal\payment\Entity\Payment\PaymentStorageInterface $storage */
-    $storage->loadLineItems($entities);
-    $storage->loadPaymentStatuses($entities);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function postSave(EntityStorageInterface $storage, $update = TRUE) {
-    /** @var \Drupal\payment\Entity\Payment\PaymentStorageInterface $storage */
-    $storage->saveLineItems($this->getLineItems());
-    $storage->savePaymentStatuses($this->getPaymentStatuses());
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function postDelete(EntityStorageInterface $storage, array $entities) {
-    /** @var \Drupal\payment\Entity\Payment\PaymentStorageInterface $storage */
-    $storage->deleteLineItems(array_keys($entities));
-    $storage->deletePaymentStatuses(array_keys($entities));
-  }
-
-  /**
-   * {@inheritdoc}
-   */
   public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
     $fields['bundle'] = BaseFieldDefinition::create('string')
       ->setLabel(t('Payment type'))
@@ -403,6 +334,16 @@ class Payment extends ContentEntityBase implements PaymentInterface {
       ->setSettings(array(
         'target_type' => 'user',
       ));
+    $fields['line_items'] = BaseFieldDefinition::create('payment_line_item')
+      ->setLabel(t('Line items'))
+      ->setCardinality(BaseFieldDefinition::CARDINALITY_UNLIMITED);
+    $fields['payment_method'] = BaseFieldDefinition::create('payment_method')
+      ->setLabel(t('Payment method'));
+    $fields['payment_statuses'] = BaseFieldDefinition::create('payment_status')
+      ->setLabel(t('Payment statuses'))
+      ->setCardinality(BaseFieldDefinition::CARDINALITY_UNLIMITED);
+    $fields['payment_type'] = BaseFieldDefinition::create('payment_type')
+      ->setLabel(t('Payment type'));
     $fields['uuid'] = BaseFieldDefinition::create('uuid')
       ->setLabel(t('Universally Unique ID'))
       ->setReadOnly(TRUE);
@@ -410,33 +351,4 @@ class Payment extends ContentEntityBase implements PaymentInterface {
     return $fields;
   }
 
-  /**
-   * Clones the instance.
-   */
-  public function __clone() {
-    parent::__clone();
-    // Clone the payment type.
-    $this->type = clone $this->type;
-    $this->type->setPayment($this);
-
-    // Clone the payment method.
-    if ($this->getPaymentMethod()) {
-      $this->setPaymentMethod(clone $this->getPaymentMethod());
-    }
-
-    // Clone the line items.
-    $cloned_line_items = [];
-    foreach ($this->getLineItems() as $line_item) {
-      $cloned_line_items[] = clone $line_item;
-    }
-    $this->setLineItems($cloned_line_items);
-
-    // Clone the payment statuses.
-    $cloned_statuses = [];
-    foreach ($this->getPaymentStatuses() as $status) {
-      $cloned_statuses[] = clone $status;
-    }
-    $this->setPaymentStatuses($cloned_statuses);
-  }
-
 }
diff --git a/src/Entity/Payment/PaymentStorage.php b/src/Entity/Payment/PaymentStorage.php
index 69fefa2..397c186 100644
--- a/src/Entity/Payment/PaymentStorage.php
+++ b/src/Entity/Payment/PaymentStorage.php
@@ -13,8 +13,6 @@ use Drupal\Core\Entity\ContentEntityInterface;
 use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Entity\Sql\SqlContentEntityStorage;
-use Drupal\payment\Plugin\Payment\LineItem\PaymentLineItemManagerInterface;
-use Drupal\payment\Plugin\Payment\Method\PaymentMethodManagerInterface;
 use Drupal\payment\Plugin\Payment\Status\PaymentStatusManagerInterface;
 use Drupal\payment\Plugin\Payment\Type\PaymentTypeManagerInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -22,21 +20,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
 /**
  * Handles storage for payment entities.
  */
-class PaymentStorage extends SqlContentEntityStorage implements PaymentStorageInterface {
-
-  /**
-   * The payment line item manager.
-   *
-   * @var \Drupal\payment\Plugin\Payment\LineItem\PaymentLineItemManagerInterface
-   */
-  protected $paymentLineItemManager;
-
-  /**
-   * The payment method manager.
-   *
-   * @var \Drupal\payment\Plugin\Payment\Method\PaymentMethodManagerInterface
-   */
-  protected $paymentMethodManager;
+class PaymentStorage extends SqlContentEntityStorage {
 
   /**
    * The payment status manager.
@@ -63,19 +47,13 @@ class PaymentStorage extends SqlContentEntityStorage implements PaymentStorageIn
    *   The entity manager.
    * @param \Drupal\Core\Cache\CacheBackendInterface $cache
    *   The entity cache.
-   * @param \Drupal\payment\Plugin\Payment\LineItem\PaymentLineItemManagerInterface $payment_line_item_manager
-   *   The payment line item manager.
-   * @param \Drupal\payment\Plugin\Payment\Method\PaymentMethodManagerInterface $payment_method_manager
-   *   The payment method manager.
    * @param \Drupal\payment\Plugin\Payment\Status\PaymentStatusManagerInterface $payment_status_manager
    *   The payment status manager.
    * @param \Drupal\payment\Plugin\Payment\Type\PaymentTypeManagerInterface $payment_type_manager
    *   The payment type manager.
    */
-  public function __construct(EntityTypeInterface $entity_type, Connection $database, EntityManagerInterface $entity_manager, CacheBackendInterface $cache, PaymentLineItemManagerInterface $payment_line_item_manager, PaymentMethodManagerInterface $payment_method_manager, PaymentStatusManagerInterface $payment_status_manager, PaymentTypeManagerInterface $payment_type_manager) {
+  public function __construct(EntityTypeInterface $entity_type, Connection $database, EntityManagerInterface $entity_manager, CacheBackendInterface $cache, PaymentStatusManagerInterface $payment_status_manager, PaymentTypeManagerInterface $payment_type_manager) {
     parent::__construct($entity_type, $database, $entity_manager, $cache);
-    $this->paymentLineItemManager = $payment_line_item_manager;
-    $this->paymentMethodManager = $payment_method_manager;
     $this->paymentStatusManager = $payment_status_manager;
     $this->paymentTypeManager = $payment_type_manager;
   }
@@ -84,7 +62,7 @@ class PaymentStorage extends SqlContentEntityStorage implements PaymentStorageIn
    * {@inheritdoc}
    */
   public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
-    return new static($entity_type, $container->get('database'), $container->get('entity.manager'), $container->get('cache.entity'), $container->get('plugin.manager.payment.line_item'), $container->get('plugin.manager.payment.method'), $container->get('plugin.manager.payment.status'), $container->get('plugin.manager.payment.type'));
+    return new static($entity_type, $container->get('database'), $container->get('entity.manager'), $container->get('cache.entity'), $container->get('plugin.manager.payment.status'), $container->get('plugin.manager.payment.type'));
   }
 
   /**
@@ -93,6 +71,9 @@ class PaymentStorage extends SqlContentEntityStorage implements PaymentStorageIn
   public function create(array $values = []) {
     /** @var \Drupal\payment\Entity\PaymentInterface $payment */
     $payment = parent::create($values);
+    $payment_type = $this->paymentTypeManager->createInstance($values['bundle']);
+    $payment_type->setPayment($payment);
+    $payment->get('payment_type')->setValue($payment_type);
     $status = $this->paymentStatusManager->createInstance('payment_created')
       ->setCreated(time());
     $payment->setPaymentStatus($status);
@@ -104,6 +85,7 @@ class PaymentStorage extends SqlContentEntityStorage implements PaymentStorageIn
    * {@inheritdoc}
    */
   protected function buildQuery($ids, $revision_id = FALSE) {
+    // @todo  What does this code do?
     $query = $this->database->select('payment', 'payment');
     $query->addTag('payment_load_multiple');
     $query->fields('payment');
@@ -117,159 +99,20 @@ class PaymentStorage extends SqlContentEntityStorage implements PaymentStorageIn
   /**
    * {@inheritdoc}
    */
-  function mapFromStorageRecords(array $records) {
-    foreach ($records as $id => $record) {
-      $payment_method = $record->payment_method_id ? $this->paymentMethodManager->createInstance($record->payment_method_id, unserialize($record->payment_method_configuration)) : NULL;
-      $payment_type = $this->paymentTypeManager->createInstance($record->payment_type_id, unserialize($record->payment_type_configuration));
-      $records[$id] = (object) array(
-        'currency' => $record->currency,
-        'id' => (int) $record->id,
-        'owner' => (int) $record->owner,
-        'method' => $payment_method,
-        'type' => $payment_type,
-        'bundle' => $payment_type->getPluginId(),
-        'uuid' => $record->uuid,
-      );
-    }
-
-    return parent::mapFromStorageRecords($records);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
   protected function mapToStorageRecord(ContentEntityInterface $entity, $table_name = NULL) {
     /** @var \Drupal\payment\Entity\PaymentInterface $payment */
     $payment = $entity;
 
-    $record = new \stdClass();
-    $record->bundle = $payment->bundle();
-    $record->currency = $payment->getCurrencyCode();
-    $record->id = $payment->id();
-    $record->first_payment_status_id = current($payment->getPaymentStatuses())->getId();
-    $record->last_payment_status_id = $payment->getPaymentStatus()->getId();
-    $record->owner = $payment->getOwnerId();
-    $record->payment_method_configuration = serialize($payment->getPaymentMethod() ? $payment->getPaymentMethod()->getConfiguration() : []);
-    $record->payment_method_id = $payment->getPaymentMethod() ? $payment->getPaymentMethod()->getPluginId() : NULL;
-    $record->payment_type_configuration = serialize($payment->getPaymentType()->getConfiguration());
-    $record->payment_type_id = $payment->getPaymentType()->getPluginId();
-    $record->uuid = $payment->uuid();
-
-    return $record;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function loadLineItems(array $entities) {
-    /** @var \Drupal\payment\Entity\PaymentInterface[] $entities */
-    $result = $this->database->select('payment_line_item', 'pli')
-      ->fields('pli', array('payment_id', 'plugin_configuration', 'plugin_id'))
-      ->condition('payment_id', array_keys($entities))
-      ->execute();
-    while ($line_item_data = $result->fetchAssoc()) {
-      /** @var \Drupal\payment\Plugin\Payment\LineItem\PaymentLineItemInterface $line_item */
-      $line_item = $this->paymentLineItemManager->createInstance($line_item_data['plugin_id'], unserialize($line_item_data['plugin_configuration']));
-      $line_item->setPayment($entities[$line_item_data['payment_id']]);
-      $entities[$line_item_data['payment_id']]->setLineItem($line_item);
-    }
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function saveLineItems(array $line_items) {
-    /** @var \Drupal\payment\Plugin\Payment\LineItem\PaymentLineItemInterface[] $line_items */
-    if ($line_items) {
-      $this->deleteLineItems(array(reset($line_items)->getPayment()->id()));
-      $query = $this->database->insert('payment_line_item')
-        ->fields(array('amount', 'amount_total', 'currency_code', 'name', 'payment_id', 'plugin_configuration', 'plugin_id', 'quantity'));
-      foreach ($line_items as $line_item) {
-        $query->values(array(
-          'amount' => $line_item->getAmount(),
-          'amount_total' => $line_item->getTotalAmount(),
-          'currency_code' => $line_item->getCurrencyCode(),
-          'name' => $line_item->getName(),
-          'payment_id' => $line_item->getPayment()->id(),
-          'plugin_configuration' => serialize($line_item->getConfiguration()),
-          'plugin_id' => $line_item->getPluginId(),
-          'quantity' => $line_item->getQuantity(),
-        ));
-      }
-      $query->execute();
-    }
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function deleteLineItems(array $ids) {
-    $this->database->delete('payment_line_item')
-      ->condition('payment_id', $ids)
-      ->execute();
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function loadPaymentStatuses(array $entities) {
-    /** @var \Drupal\payment\Entity\PaymentInterface[] $entities */
-    $result = $this->database->select('payment_status', 'ps')
-      ->fields('ps')
-      ->condition('payment_id', array_keys($entities))
-      ->orderBy('id', 'ASC')
-      ->execute();
-    $statuses= array_fill_keys(array_keys($entities), []);
-    while ($status_data = $result->fetchAssoc()) {
-      $status = $this->paymentStatusManager->createInstance($status_data['plugin_id']);
-      $status->setCreated((int) $status_data['created']);
-      $status->setId((int) $status_data['id']);
-      $status->setPayment($entities[$status_data['payment_id']]);
-
-      $statuses[$status->getPayment()->id()][] = $status;
-    }
-    foreach ($entities as $payment) {
-      $payment->setPaymentStatuses($statuses[$payment->id()]);
+    $record = parent::mapToStorageRecord($entity, $table_name);
+    $deltas = [];
+    foreach ($payment->get('payment_statuses') as $delta => $item) {
+      $deltas[] = $delta;
     }
-  }
+    $record->first_payment_status_delta = min($deltas);
+    $record->last_payment_status_delta = max($deltas);
+    debug($record);
 
-  /**
-   * {@inheritdoc}
-   */
-  public function savePaymentStatuses(array $statuses) {
-    /** @var \Drupal\payment\Plugin\Payment\Status\PaymentStatusInterface[] $statuses */
-    if ($statuses) {
-      foreach ($statuses as $status) {
-        // Statuses cannot be edited, so only save the ones without an ID.
-        if (!$status->getId()) {
-          $record = array(
-            'created' => $status->getCreated(),
-            'payment_id' => $status->getPayment()->id(),
-            'plugin_id' => $status->getPluginId(),
-          );
-          $id = $this->database->insert('payment_status')
-            ->fields($record)
-            ->execute();
-          $status->setId($id);
-        }
-      }
-      $this->database->update('payment')
-        ->condition('id', reset($statuses)->getPayment()->id())
-        ->fields(array(
-          'first_payment_status_id' => reset($statuses)->getId(),
-          'last_payment_status_id' => end($statuses)->getId(),
-        ))
-        ->execute();
-    }
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function deletePaymentStatuses(array $ids) {
-    $this->database->delete('payment_status')
-      ->condition('payment_id', $ids)
-      ->execute();
+    return $record;
   }
 
 }
diff --git a/src/Entity/Payment/PaymentStorageInterface.php b/src/Entity/Payment/PaymentStorageInterface.php
deleted file mode 100644
index 3655ba6..0000000
--- a/src/Entity/Payment/PaymentStorageInterface.php
+++ /dev/null
@@ -1,71 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\payment\Entity\Payment\PaymentStorageInterface.
- */
-
-namespace Drupal\payment\Entity\Payment;
-
-use Drupal\Core\Entity\EntityStorageInterface;
-
-/**
- * A storage controller for payment entities.
- */
-interface PaymentStorageInterface extends EntityStorageInterface {
-
-  /**
-   * Loads payment line items.
-   *
-   * @param int[] $ids
-   *   Payment IDs.
-   *
-   * @return array[]
-   *   Keys are payment IDs, values are arrays of which keys are line item names
-   *   and values are \Drupal\payment\Plugin\Payment\LineItem\PaymentLineItemInterface
-   *   objects.
-   */
-  public function loadLineItems(array $ids);
-
-  /**
-   * Saves payment line items.
-   *
-   * @param \Drupal\payment\Plugin\Payment\LineItem\PaymentLineItemInterface[] $line_items
-   */
-  public function saveLineItems(array $line_items);
-
-  /**
-   * Deletes payment line items.
-   *
-   * @param string[] $ids
-   *   Keys are payment IDs. Values are line item names.
-   */
-  public function deleteLineItems(array $ids);
-
-  /**
-   * Loads payment statuses.
-   *
-   * @param int[] $ids
-   *   Payment IDs.
-   *
-   * @return array[]
-   *   Keys are payment IDs, values are arrays of
-   *   \Drupal\payment\Plugin\Payment\Status\PaymentStatusInterface objects.
-   */
-  public function loadPaymentStatuses(array $ids);
-
-  /**
-   * Saves payment statuses.
-   *
-   * @param \Drupal\payment\Plugin\Payment\Status\PaymentStatusInterface[] $statuses
-   */
-  public function savePaymentStatuses(array $statuses);
-
-  /**
-   * Deletes payment statuses.
-   *
-   * @param int[] $ids
-   *   Payment IDs.
-   */
-  public function deletePaymentStatuses(array $ids);
-}
diff --git a/src/Entity/Payment/PaymentStorageSchema.php b/src/Entity/Payment/PaymentStorageSchema.php
index b0b2971..88d8cf5 100644
--- a/src/Entity/Payment/PaymentStorageSchema.php
+++ b/src/Entity/Payment/PaymentStorageSchema.php
@@ -26,54 +26,39 @@ class PaymentStorageSchema extends SqlContentEntityStorageSchema {
   }
 
   /**
+   * Adds non-field columns to the schema.
    *
+   * @param array[] $schema
+   *   The existing schema.
    */
   protected function alterEntitySchemaWithNonFieldColumns(array &$schema) {
     $schema['payment']['fields'] += array(
-      'first_payment_status_id' => array(
-        'description' => "The {payment_status_item}.id of this payment's first status item.",
+      'first_payment_status_delta' => array(
+        'description' => "The {payment__payment_statuses}.delta of this payment's first status item.",
         'type' => 'int',
         'unsigned' => TRUE,
         'default' => 0,
         'not null' => TRUE,
       ),
-      'last_payment_status_id' => array(
-        'description' => "The {payment_status_item}.id of this payment's most recent status item.",
+      'last_payment_status_delta' => array(
+        'description' => "The {payment__payment_statuses}.delta of this payment's most recent status item.",
         'type' => 'int',
         'unsigned' => TRUE,
         'default' => 0,
         'not null' => TRUE,
       ),
-      'payment_method_configuration' => array(
-        'type' => 'blob',
-        'not null' => TRUE,
-        'serialize' => TRUE,
-      ),
-      'payment_method_id' => array(
-        'type' => 'varchar',
-        'length' => 255,
-      ),
-      'payment_type_configuration' => array(
-        'type' => 'blob',
-        'not null' => TRUE,
-        'serialize' => TRUE,
-      ),
-      'payment_type_id' => array(
-        'type' => 'varchar',
-        'length' => 255,
-      ),
     );
     $schema['payment']['foreign keys'] += array(
-      'first_payment_status_id' => array(
-        'table' => 'payment_status_item',
+      'first_payment_status_delta' => array(
+        'table' => 'payment__payment_statuses',
         'columns' => array(
-          'first_payment_status_id' => 'id',
+          'first_payment_status_delta' => 'delta',
         ),
       ),
-      'last_payment_status_id' => array(
-        'table' => 'payment_status_item',
+      'last_payment_status_delta' => array(
+        'table' => 'payment__payment_statuses',
         'columns' => array(
-          'last_payment_status_id' => 'id',
+          'last_payment_status_delta' => 'delta',
         ),
       ),
       'owner' => array(
@@ -83,8 +68,6 @@ class PaymentStorageSchema extends SqlContentEntityStorageSchema {
         ),
       ),
     );
-
-    return $schema;
   }
 
 }
diff --git a/src/Entity/PaymentInterface.php b/src/Entity/PaymentInterface.php
index 10fe99f..435b46f 100644
--- a/src/Entity/PaymentInterface.php
+++ b/src/Entity/PaymentInterface.php
@@ -108,8 +108,7 @@ interface PaymentInterface extends ContentEntityInterface, EntityChangedInterfac
   /**
    * Sets/replaces all statuses without notifications.
    *
-   * @param array $statuses
-   *   \Drupal\payment\Plugin\Payment\Status\PaymentStatusInterface objects.
+   * @param \Drupal\payment\Plugin\Payment\Status\PaymentStatusInterface[] $statuses
    *
    * @return \Drupal\payment\Entity\PaymentInterface
    */
diff --git a/src/Plugin/DataType/ArrayData.php b/src/Plugin/DataType/ArrayData.php
new file mode 100644
index 0000000..b89033e
--- /dev/null
+++ b/src/Plugin/DataType/ArrayData.php
@@ -0,0 +1,36 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\payment\Plugin\DataType\Any.
+ */
+
+namespace Drupal\payment\Plugin\DataType;
+
+use Drupal\Core\TypedData\TypedData;
+
+/**
+ * Provides an array data type.
+ *
+ * @DataType(
+ *   id = "payment_array",
+ *   label = @Translation("Array")
+ * )
+ */
+class ArrayData extends TypedData {
+
+  /**
+   * The data value.
+   *
+   * @var mixed
+   */
+  protected $value;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setValue($value, $notify = TRUE) {
+    parent::setValue((array) $value, $notify);
+  }
+
+}
diff --git a/src/Plugin/Field/FieldType/LineItemItem.php b/src/Plugin/Field/FieldType/LineItemItem.php
new file mode 100644
index 0000000..7c6e701
--- /dev/null
+++ b/src/Plugin/Field/FieldType/LineItemItem.php
@@ -0,0 +1,31 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\payment\Plugin\Field\FieldType\LineItemItem.
+ */
+
+namespace Drupal\payment\Plugin\Field\FieldType;
+
+use Drupal\payment\Payment;
+
+/**
+ * Provides a plugin bag for payment line item plugins.
+ *
+ * @FieldType(
+ *   id = "payment_line_item",
+ *   label = @Translation("Payment line item plugins"),
+ *   list_class = "\Drupal\payment\Plugin\Field\FieldType\PluginBagItemList"
+ * )
+ */
+class LineItemItem extends PluginBagItemBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPluginManager() {
+    // @todo Unit-test this.
+    return Payment::lineItemManager();
+  }
+
+}
diff --git a/src/Plugin/Field/FieldType/PaymentMethodItem.php b/src/Plugin/Field/FieldType/PaymentMethodItem.php
new file mode 100644
index 0000000..e31447e
--- /dev/null
+++ b/src/Plugin/Field/FieldType/PaymentMethodItem.php
@@ -0,0 +1,31 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\payment\Plugin\Field\FieldType\PaymentMethodItem.
+ */
+
+namespace Drupal\payment\Plugin\Field\FieldType;
+
+use Drupal\payment\Payment;
+
+/**
+ * Provides a plugin bag for payment method plugins.
+ *
+ * @FieldType(
+ *   id = "payment_method",
+ *   label = @Translation("Payment method plugins"),
+ *   list_class = "\Drupal\payment\Plugin\Field\FieldType\PluginBagItemList"
+ * )
+ */
+class PaymentMethodItem extends PluginBagItemBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPluginManager() {
+    // @todo Unit-test this.
+    return Payment::methodManager();
+  }
+
+}
diff --git a/src/Plugin/Field/FieldType/PaymentStatusItem.php b/src/Plugin/Field/FieldType/PaymentStatusItem.php
new file mode 100644
index 0000000..a92f1c1
--- /dev/null
+++ b/src/Plugin/Field/FieldType/PaymentStatusItem.php
@@ -0,0 +1,31 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\payment\Plugin\Field\FieldType\PaymentStatusItem.
+ */
+
+namespace Drupal\payment\Plugin\Field\FieldType;
+
+use Drupal\payment\Payment;
+
+/**
+ * Provides a plugin bag for payment type plugins.
+ *
+ * @FieldType(
+ *   id = "payment_status",
+ *   label = @Translation("Payment status plugins"),
+ *   list_class = "\Drupal\payment\Plugin\Field\FieldType\PluginBagItemList"
+ * )
+ */
+class PaymentStatusItem extends PluginBagItemBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPluginManager() {
+    // @todo Unit-test this.
+    return Payment::statusManager();
+  }
+
+}
diff --git a/src/Plugin/Field/FieldType/PaymentTypeItem.php b/src/Plugin/Field/FieldType/PaymentTypeItem.php
new file mode 100644
index 0000000..32054b9
--- /dev/null
+++ b/src/Plugin/Field/FieldType/PaymentTypeItem.php
@@ -0,0 +1,31 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\payment\Plugin\Field\FieldType\PaymentTypeItem.
+ */
+
+namespace Drupal\payment\Plugin\Field\FieldType;
+
+use Drupal\payment\Payment;
+
+/**
+ * Provides a plugin bag for payment type plugins.
+ *
+ * @FieldType(
+ *   id = "payment_type",
+ *   label = @Translation("Payment type plugins"),
+ *   list_class = "\Drupal\payment\Plugin\Field\FieldType\PluginBagItemList"
+ * )
+ */
+class PaymentTypeItem extends PluginBagItemBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPluginManager() {
+    // @todo Unit-test this.
+    return Payment::typeManager();
+  }
+
+}
diff --git a/src/Plugin/Field/FieldType/PluginBagItemBase.php b/src/Plugin/Field/FieldType/PluginBagItemBase.php
new file mode 100644
index 0000000..1e63a62
--- /dev/null
+++ b/src/Plugin/Field/FieldType/PluginBagItemBase.php
@@ -0,0 +1,309 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Field\Plugin\Field\FieldType\PluginBagItemBase.
+ */
+
+namespace Drupal\payment\Plugin\Field\FieldType;
+
+use Drupal\Component\Plugin\ConfigurablePluginInterface;
+use Drupal\Component\Plugin\PluginInspectionInterface;
+use Drupal\Core\Field\FieldStorageDefinitionInterface;
+use Drupal\Core\Field\FieldItemBase;
+use Drupal\Core\TypedData\DataDefinition;
+use Drupal\Core\TypedData\MapDataDefinition;
+use Drupal\payment\Plugin\Payment\FieldTypePluginInterface;
+
+/**
+ * Provides a base for plugin bag field items.
+ */
+abstract class PluginBagItemBase extends FieldItemBase {
+
+  /**
+   * Whether the field properties and plugin are being synchronized.
+   *
+   * @var bool
+   */
+  protected $synchronizing = FALSE;
+
+  /**
+   * The previously contained plugin instance.
+   *
+   * @var \Drupal\Component\Plugin\PluginInspectionInterface|\Drupal\payment\Plugin\Payment\FieldTypePluginInterface
+   */
+  protected $previouslyContainedPluginInstance;
+
+  /**
+   * Returns the plugin manager.
+   *
+   * @return \Drupal\Component\Plugin\PluginManagerInterface
+   */
+  abstract protected function getPluginManager();
+
+  /**
+   * Validates a plugin instance.
+   *
+   * @param \Drupal\Component\Plugin\PluginInspectionInterface $plugin_instance
+   *
+   * @throws \Exception
+   */
+  protected function validatePluginInstance(PluginInspectionInterface $plugin_instance) {
+    if (!$this->getPluginManager()->hasDefinition($plugin_instance->getPluginId())) {
+      throw new \Exception(sprintf('Plugin manager %s does not have a definition for plugin %s.', get_class($this->getPluginManager()), $plugin_instance->getPluginId()));
+    }
+    if (!$plugin_instance instanceof FieldTypePluginInterface) {
+      throw new \Exception(sprintf('%s must implement \Drupal\payment\Plugin\Payment\FieldTypePluginInterface if it is to be contained by a field item.', get_class($plugin_instance)));
+    }
+  }
+
+  /**
+   * Creates a plugin instance.
+   *
+   * @param string $plugin_id
+   * @param mixed[] $plugin_configuration
+   *
+   * @return \Drupal\Component\Plugin\PluginInspectionInterface
+   */
+  protected function createContainedPluginInstance($plugin_id, array $plugin_configuration = []) {
+    /** @var \Drupal\payment\Plugin\Payment\FieldTypePluginInterface $plugin_instance */
+    $plugin_instance = $this->getPluginManager()->createInstance($plugin_id, $plugin_configuration);
+    $this->validatePluginInstance($plugin_instance);
+    $plugin_instance->setFieldItem($this);
+
+    return $plugin_instance;
+  }
+
+  /**
+   * Gets the instantiated plugin.
+   *
+   * @return \Drupal\Component\Plugin\PluginInspectionInterface|null
+   *   The plugin or NULL of no plugin ID was configured yet.
+   */
+  public function getContainedPluginInstance() {
+    return $this->get('plugin_instance')->getValue();
+  }
+
+  /**
+   * Sets the instantiated plugin.
+   *
+   * @param \Drupal\Component\Plugin\PluginInspectionInterface $plugin_instance
+   *
+   * @return $this
+   */
+  public function setContainedPluginInstance(PluginInspectionInterface $plugin_instance) {
+    $this->get('plugin_instance')->setValue($plugin_instance);
+
+    return $this;
+  }
+
+  /**
+   * Resets the instantiated plugin.
+   *
+   * @return $this
+   */
+  public function resetContainedPluginInstance() {
+    $this->get('plugin_instance')->applyDefaultValue();
+
+    return $this;
+  }
+
+  /**
+   * Gets the plugin ID.
+   *
+   * @return string
+   */
+  public function getContainedPluginId() {
+    return $this->get('plugin_id')->getValue();
+  }
+
+  /**
+   * Sets the plugin ID.
+   *
+   * @param string $plugin_id
+   *
+   * @return $this
+   */
+  public function setContainedPluginId($plugin_id) {
+    $this->get('plugin_id')->setValue($plugin_id);
+
+    return $this;
+  }
+
+  /**
+   * Sets the plugin configuration.
+   *
+   * @return mixed[]
+   */
+  public function getContainedPluginConfiguration() {
+    return $this->get('plugin_configuration')->getValue();
+  }
+
+  /**
+   * Sets the plugin configuration.
+   *
+   * @param mixed[] $configuration
+   *
+   * @return $this
+   */
+  public function setContainedPluginConfiguration(array $configuration) {
+    $this->get('plugin_configuration')->setValue($configuration);
+
+    return $this;
+  }
+
+  /**
+   * Sets the plugin configuration without typed data notifications.
+   *
+   * @param mixed[] $configuration
+   *
+   * @return $this
+   */
+  public function _setContainedPluginConfigurationFromContainedPlugin(array $configuration) {
+    $this->synchronizing = TRUE;
+    $this->setContainedPluginConfiguration($configuration);
+    $this->synchronizing = FALSE;
+
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
+    $properties['plugin_id'] = DataDefinition::create('string')
+      ->setLabel(t('Plugin ID'));
+    $properties['plugin_configuration'] = MapDataDefinition::create('payment_array')
+      ->setLabel(t('Plugin configuration'));
+    $properties['plugin_instance'] = MapDataDefinition::create('any')
+      ->setLabel(t('Plugin instance'));
+
+    return $properties;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function onChange($property_name) {
+    if ($property_name == 'plugin_id') {
+      if ($this->get($property_name)->getValue()) {
+        $plugin_instance = $this->createContainedPluginInstance($this->getContainedPluginId());
+        if ($this->previouslyContainedPluginInstance) {
+          $this->previouslyContainedPluginInstance->unsetFieldItem();
+        }
+        $this->previouslyContainedPluginInstance = $plugin_instance;
+        $this->get('plugin_instance')->setValue($plugin_instance, FALSE);
+        if ($plugin_instance instanceof ConfigurablePluginInterface) {
+          $this->get('plugin_configuration')->setValue($plugin_instance->getConfiguration(), FALSE);
+        }
+        else {
+          $this->get('plugin_configuration')->applyDefaultValue(FALSE);
+        }
+      }
+      else {
+        $this->get('plugin_instance')->applyDefaultValue(FALSE);
+        $this->get('plugin_configuration')->applyDefaultValue(FALSE);
+      }
+    }
+
+    if ($property_name == 'plugin_configuration' && !$this->synchronizing) {
+      /** @var \Drupal\Component\Plugin\ConfigurablePluginInterface $plugin_instance */
+      $plugin_instance = $this->getContainedPluginInstance();
+      if ($plugin_instance instanceof ConfigurablePluginInterface) {
+        $plugin_instance->setConfiguration($this->get($property_name)->getValue());
+      }
+      else {
+        $this->get('plugin_configuration')->applyDefaultValue(FALSE);
+      }
+    }
+
+    if ($property_name == 'plugin_instance') {
+      if ($this->previouslyContainedPluginInstance) {
+        $this->previouslyContainedPluginInstance->unsetFieldItem();
+      }
+      $plugin_instance = $this->getContainedPluginInstance();
+      $this->previouslyContainedPluginInstance = $plugin_instance;
+      if ($plugin_instance) {
+        $plugin_instance->setFieldItem($this);
+        $this->validatePluginInstance($plugin_instance);
+        $this->get('plugin_id')
+          ->setValue($plugin_instance->getPluginId(), FALSE);
+        if ($plugin_instance instanceof ConfigurablePluginInterface) {
+          $this->get('plugin_configuration')
+            ->setValue($plugin_instance->getConfiguration(), FALSE);
+        }
+        else {
+          $this->get('plugin_configuration')->applyDefaultValue(FALSE);
+        }
+      }
+      else {
+        $this->get('plugin_id')->applyDefaultValue(FALSE);
+        $this->get('plugin_configuration')->applyDefaultValue(FALSE);
+      }
+    }
+
+    parent::onChange($property_name);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function mainPropertyName() {
+    return 'plugin_instance';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function schema(FieldStorageDefinitionInterface $field_definition) {
+    $columns = array(
+      'plugin_id' => array(
+        'description' => 'The plugin ID.',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+      ),
+      'plugin_configuration' => array(
+        'description' => 'The plugin configuration.',
+        'type' => 'blob',
+        'not null' => TRUE,
+        'serialize' => TRUE,
+      ),
+    );
+
+    $schema = array(
+      'columns' => $columns,
+    );
+
+    return $schema;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isEmpty() {
+    return !$this->getContainedPluginInstance();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setValue($values, $notify = TRUE) {
+    if (!is_array($values)) {
+      $this->get('plugin_instance')->setValue($values);
+      return;
+    }
+    parent::setValue($values, $notify);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function __clone() {
+    $plugin_instance = $this->get('plugin_instance')->getValue();
+    if (is_object($plugin_instance)) {
+      $this->get('plugin_instance')->setValue(clone $plugin_instance, FALSE);
+    }
+  }
+
+}
diff --git a/src/Plugin/Field/FieldType/PluginBagItemList.php b/src/Plugin/Field/FieldType/PluginBagItemList.php
new file mode 100644
index 0000000..1b73d7f
--- /dev/null
+++ b/src/Plugin/Field/FieldType/PluginBagItemList.php
@@ -0,0 +1,28 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\payment\Plugin\Field\FieldType\PluginBagItemList.
+ */
+
+namespace Drupal\payment\Plugin\Field\FieldType;
+
+use Drupal\Core\Field\FieldItemList;
+
+/**
+ * Provides a plugin bag field item list.
+ */
+class PluginBagItemList extends FieldItemList {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function __clone() {
+    $cloned_items = [];
+    foreach ($this->list as $delta => $item) {
+      $cloned_items[$delta] = clone $item;
+    }
+    $this->list = $cloned_items;
+  }
+
+}
diff --git a/src/Plugin/Payment/FieldTypePluginInterface.php b/src/Plugin/Payment/FieldTypePluginInterface.php
new file mode 100644
index 0000000..795087c
--- /dev/null
+++ b/src/Plugin/Payment/FieldTypePluginInterface.php
@@ -0,0 +1,32 @@
+<?php
+
+/**
+ * Contains \Drupal\payment\Plugin\Payment\FieldTypePluginInterface.
+ */
+
+namespace Drupal\payment\Plugin\Payment;
+
+use Drupal\payment\Plugin\Field\FieldType\PluginBagItemBase;
+
+/**
+ * Defines a plugin that can be contained in a field.
+ */
+interface FieldTypePluginInterface {
+
+  /**
+   * Sets the containing field item.
+   *
+   * @param \Drupal\payment\Plugin\Field\FieldType\PluginBagItemBase $field_item
+   *
+   * @return $this
+   */
+  public function setFieldItem(PluginBagItemBase $field_item);
+
+  /**
+   * Unsets the containing field item.
+   *
+   * @return $this
+   */
+  public function unsetFieldItem();
+
+}
diff --git a/src/Plugin/Payment/FieldTypePluginTrait.php b/src/Plugin/Payment/FieldTypePluginTrait.php
new file mode 100644
index 0000000..bcd7cf9
--- /dev/null
+++ b/src/Plugin/Payment/FieldTypePluginTrait.php
@@ -0,0 +1,54 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\payment\Plugin\Payment\FieldTypePluginTrait.
+ */
+
+namespace Drupal\payment\Plugin\Payment;
+
+use Drupal\payment\Plugin\Field\FieldType\PluginBagItemBase;
+
+/**
+ * Provides a trait that implements
+ * \Drupal\payment\Plugin\Payment\FieldTypePluginInterface and parts of
+ * \Drupal\Component\Plugin\PluginInspectionInterface.
+ */
+trait FieldTypePluginTrait {
+
+  /**
+   * The containing field item.
+   *
+   * @var \Drupal\payment\Plugin\Field\FieldType\PluginBagItemBase
+   */
+  protected $fieldItem;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setFieldItem(PluginBagItemBase $field_item) {
+    $this->fieldItem = $field_item;
+
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function unsetFieldItem() {
+    $this->fieldItem = NULL;
+
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setContainingFieldItemConfiguration() {
+    if ($this->fieldItem) {
+      /** @var \Drupal\payment\Plugin\Payment\FieldTypePluginTrait|\Drupal\Component\Plugin\ConfigurablePluginInterface $this */
+      $this->fieldItem->_setContainedPluginConfigurationFromContainedPlugin($this->getConfiguration());
+    }
+  }
+
+}
diff --git a/src/Plugin/Payment/Method/PaymentMethodBase.php b/src/Plugin/Payment/Method/PaymentMethodBase.php
index 6eb6747..b2da542 100644
--- a/src/Plugin/Payment/Method/PaymentMethodBase.php
+++ b/src/Plugin/Payment/Method/PaymentMethodBase.php
@@ -18,6 +18,8 @@ use Drupal\payment\Event\PaymentExecuteAccess;
 use Drupal\payment\Event\PaymentPreCapture;
 use Drupal\payment\Event\PaymentPreExecute;
 use Drupal\payment\Event\PaymentPreRefund;
+use Drupal\payment\Plugin\Payment\FieldTypePluginInterface;
+use Drupal\payment\Plugin\Payment\FieldTypePluginTrait;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
 
@@ -30,7 +32,9 @@ use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  *   form.
  * - message_text_format: The ID of the text format to format message_text with.
  */
-abstract class PaymentMethodBase extends PluginBase implements ContainerFactoryPluginInterface, PaymentMethodInterface, PaymentMethodCapturePaymentInterface, PaymentMethodRefundPaymentInterface {
+abstract class PaymentMethodBase extends PluginBase implements ContainerFactoryPluginInterface, FieldTypePluginInterface, PaymentMethodInterface, PaymentMethodCapturePaymentInterface, PaymentMethodRefundPaymentInterface {
+
+  use FieldTypePluginTrait;
 
   /**
    * The event dispatcher.
diff --git a/src/Plugin/Payment/Method/Unavailable.php b/src/Plugin/Payment/Method/Unavailable.php
index 0fcf81e..65caf78 100644
--- a/src/Plugin/Payment/Method/Unavailable.php
+++ b/src/Plugin/Payment/Method/Unavailable.php
@@ -10,6 +10,8 @@ use Drupal\Component\Plugin\PluginBase;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Session\AccountInterface;
 use Drupal\payment\Entity\PaymentInterface;
+use Drupal\payment\Plugin\Payment\FieldTypePluginInterface;
+use Drupal\payment\Plugin\Payment\FieldTypePluginTrait;
 
 /**
  * A payment method controller that essentially disables payment methods.
@@ -25,7 +27,9 @@ use Drupal\payment\Entity\PaymentInterface;
  *   module = "payment"
  * )
  */
-class Unavailable extends PluginBase implements PaymentMethodInterface {
+class Unavailable extends PluginBase implements PaymentMethodInterface, FieldTypePluginInterface {
+
+  use FieldTypePluginTrait;
 
   /**
    * The payment this payment method is for.
diff --git a/src/Plugin/Payment/Status/PaymentStatusBase.php b/src/Plugin/Payment/Status/PaymentStatusBase.php
index 75cb79b..9404d97 100644
--- a/src/Plugin/Payment/Status/PaymentStatusBase.php
+++ b/src/Plugin/Payment/Status/PaymentStatusBase.php
@@ -9,6 +9,8 @@ namespace Drupal\payment\Plugin\Payment\Status;
 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
 use Drupal\Core\Plugin\PluginBase;
 use Drupal\payment\Entity\PaymentInterface;
+use Drupal\payment\Plugin\Payment\FieldTypePluginInterface;
+use Drupal\payment\Plugin\Payment\FieldTypePluginTrait;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -17,7 +19,9 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
  * Plugins extending this class should provide a configuration schema that
  * extends payment.plugin_configuration.payment_status.payment_base.
  */
-abstract class PaymentStatusBase extends PluginBase implements ContainerFactoryPluginInterface, PaymentStatusInterface {
+abstract class PaymentStatusBase extends PluginBase implements ContainerFactoryPluginInterface, FieldTypePluginInterface, PaymentStatusInterface {
+
+  use FieldTypePluginTrait;
 
   /**
    * The payment this payment status is for.
diff --git a/src/Plugin/Payment/Type/PaymentTypeBase.php b/src/Plugin/Payment/Type/PaymentTypeBase.php
index 664310d..1702a57 100644
--- a/src/Plugin/Payment/Type/PaymentTypeBase.php
+++ b/src/Plugin/Payment/Type/PaymentTypeBase.php
@@ -11,13 +11,17 @@ use Drupal\Core\Plugin\PluginBase;
 use Drupal\payment\Entity\PaymentInterface;
 use Drupal\payment\Event\PaymentEvents;
 use Drupal\payment\Event\PaymentTypePreResumeContext;
+use Drupal\payment\Plugin\Payment\FieldTypePluginInterface;
+use Drupal\payment\Plugin\Payment\FieldTypePluginTrait;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
 
 /**
  * Defines base payment type.
  */
-abstract class PaymentTypeBase extends PluginBase implements ContainerFactoryPluginInterface, PaymentTypeInterface {
+abstract class PaymentTypeBase extends PluginBase implements ContainerFactoryPluginInterface, FieldTypePluginInterface, PaymentTypeInterface {
+
+  use FieldTypePluginTrait;
 
   /**
    * The event dispatcher.
diff --git a/src/Tests/Entity/PaymentUnitTest.php b/src/Tests/Entity/PaymentUnitTest.php
index 14718f2..b6dda0b 100644
--- a/src/Tests/Entity/PaymentUnitTest.php
+++ b/src/Tests/Entity/PaymentUnitTest.php
@@ -69,7 +69,7 @@ class PaymentUnitTest extends KernelTestBase {
    * Tests getChangedTime().
    */
   protected function testGetChangedTime() {
-    $status = $this->statusManager->createInstance('payment_succes');
+    $status = $this->statusManager->createInstance('payment_success');
     $this->payment->setPaymentStatus($status);
     $this->assertIdentical($this->payment->getChangedTime(), $status->getCreated());
   }
diff --git a/src/Tests/Plugin/Field/FieldType/PluginBagItemBaseTest.php b/src/Tests/Plugin/Field/FieldType/PluginBagItemBaseTest.php
new file mode 100644
index 0000000..d07a716
--- /dev/null
+++ b/src/Tests/Plugin/Field/FieldType/PluginBagItemBaseTest.php
@@ -0,0 +1,143 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\field\Tests\PluginBagItemBaseTest.
+ */
+
+namespace Drupal\payment\Tests\Plugin\Field\FieldType;
+
+use Drupal\Core\Field\BaseFieldDefinition;
+use Drupal\payment_test\Plugin\PluginTest\MockConfigurablePlugin;
+use Drupal\payment_test\Plugin\PluginTest\MockManager;
+use Drupal\simpletest\KernelTestBase;
+
+/**
+ * Tests \Drupal\payment\Plugin\Field\Plugin\Field\FieldType\PluginBagItemBase.
+ *
+ * @group Payment
+ */
+class PluginBagItemBaseTest extends KernelTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['payment', 'payment_test', 'plugin_test'];
+
+  /**
+   * The field item under test.
+   *
+   * @var \Drupal\payment\Plugin\Field\FieldType\PluginBagItemBase
+   */
+  protected $fieldItem;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $field_definition = BaseFieldDefinition::create('payment_test_plugin_bag');
+
+    $this->fieldItem = \Drupal::typedDataManager()->create($field_definition)[0];
+  }
+
+  /**
+   * Tests the field.
+   */
+  protected function testField() {
+    $plugin_id = 'payment_test_plugin';
+    $plugin_id_configurable = 'payment_test_configurable_plugin';
+    $plugin_configuration = [
+      'foo' => $this->randomMachineName()
+    ];
+
+    // Test default values.
+    $this->assertNull($this->fieldItem->getContainedPluginId());
+    $this->assertEqual($this->fieldItem->getContainedPluginConfiguration(), []);
+    $this->assertNull($this->fieldItem->getContainedPluginInstance());
+
+    // Test setting values and auto-instantiation for a non-configurable plugin.
+    $this->fieldItem->setContainedPluginId($plugin_id);
+    $this->assertEqual($this->fieldItem->getContainedPluginId(), $plugin_id);
+    $this->fieldItem->setContainedPluginConfiguration($plugin_configuration);
+    $this->assertEqual($this->fieldItem->getContainedPluginConfiguration(), []);
+    $this->assertEqual($this->fieldItem->getContainedPluginInstance()->getPluginId(), $plugin_id);
+
+    // Test setting values and auto-instantiation for a configurable plugin.
+    $this->fieldItem->setContainedPluginId($plugin_id_configurable);
+    $this->assertEqual($this->fieldItem->getContainedPluginId(), $plugin_id_configurable);
+    $this->fieldItem->setContainedPluginConfiguration($plugin_configuration);
+    $this->assertEqual($this->fieldItem->getContainedPluginConfiguration(), $plugin_configuration);
+    $this->assertEqual($this->fieldItem->getContainedPluginInstance()->getPluginId(), $plugin_id_configurable);
+    /** @var \Drupal\payment_test\Plugin\PluginTest\MockConfigurablePlugin $plugin_instance_a */
+    $plugin_instance_a = $this->fieldItem->getContainedPluginInstance();
+    $this->assertTrue($plugin_instance_a instanceof MockConfigurablePlugin);
+    $this->assertEqual($plugin_instance_a->getConfiguration(), $plugin_configuration);
+    $altered_plugin_configuration = $plugin_configuration += [
+      'bar' => $this->randomMachineName(),
+    ];
+    $plugin_instance_a->setConfiguration($altered_plugin_configuration);
+    $this->assertEqual($plugin_instance_a->getConfiguration(), $altered_plugin_configuration);
+    $this->assertEqual($this->fieldItem->getContainedPluginConfiguration(), $altered_plugin_configuration);
+
+    // Test resetting the values.
+    $this->fieldItem->resetContainedPluginInstance();
+    $this->assertNull($this->fieldItem->getContainedPluginId());
+    $this->assertEqual($this->fieldItem->getContainedPluginConfiguration(), []);
+    $this->assertNull($this->fieldItem->getContainedPluginInstance());
+
+    // Test setting values again and auto-instantiation.
+    $this->fieldItem->resetContainedPluginInstance();
+    $this->fieldItem->setContainedPluginId($plugin_id_configurable);
+    $this->assertEqual($this->fieldItem->getContainedPluginId(), $plugin_id_configurable);
+    $this->fieldItem->setContainedPluginConfiguration($plugin_configuration);
+    $this->assertEqual($this->fieldItem->getContainedPluginConfiguration(), $plugin_configuration);
+    /** @var \Drupal\payment_test\Plugin\PluginTest\MockConfigurablePlugin $plugin_instance_b */
+    $plugin_instance_b = $this->fieldItem->getContainedPluginInstance();
+    $this->assertTrue($plugin_instance_b instanceof MockConfigurablePlugin);
+    $this->assertEqual($plugin_instance_b->getConfiguration(), $plugin_configuration);
+    // Make sure this is indeed a new instance and not the old one.
+    $this->assertNotIdentical(spl_object_hash($plugin_instance_b), spl_object_hash($plugin_instance_a));
+    // Make sure changing the configuration on the new instance changes the
+    // configuration in the field item.
+    $altered_plugin_configuration_a = $plugin_configuration + [
+      'bar' => $this->randomMachineName(),
+    ];
+    $altered_plugin_configuration_b = $plugin_configuration + [
+      'baz' => $this->randomMachineName(),
+    ];
+    $plugin_instance_b->setConfiguration($altered_plugin_configuration_b);
+    $this->assertEqual($this->fieldItem->getContainedPluginConfiguration(), $altered_plugin_configuration_b);
+    // Make sure changing the configuration on the old instance no longer has
+    // any effect on the field item.
+    $plugin_instance_a->setConfiguration($altered_plugin_configuration_a);
+    $this->assertEqual($this->fieldItem->getContainedPluginConfiguration(), $altered_plugin_configuration_b);
+
+    // Test feedback from the plugin back to the field item.
+    $plugin_manager = new MockManager();
+    /** @var \Drupal\payment_test\Plugin\PluginTest\MockConfigurablePlugin $plugin_instance_c */
+    $plugin_configuration_c = $plugin_configuration + [
+        'qux' => $this->randomMachineName(),
+      ];
+    $plugin_instance_c = $plugin_manager->createInstance($plugin_id_configurable, $plugin_configuration_c);
+    $this->fieldItem->setContainedPluginInstance($plugin_instance_c);
+    $this->assertEqual(spl_object_hash($this->fieldItem->getContainedPluginInstance()), spl_object_hash($plugin_instance_c));
+    $this->assertEqual($this->fieldItem->getContainedPluginConfiguration(), $plugin_configuration_c);
+    $altered_plugin_configuration_c = $plugin_configuration_c + [
+        'foobar' => $this->randomMachineName(),
+      ];
+    $plugin_instance_c->setConfiguration($altered_plugin_configuration_c);
+    $this->assertEqual($this->fieldItem->getContainedPluginConfiguration(), $altered_plugin_configuration_c);
+
+    // Test setting the main property.
+    /** @var \Drupal\payment_test\Plugin\PluginTest\MockConfigurablePlugin $plugin_instance_d */
+    $plugin_instance_d = $plugin_manager->createInstance($plugin_id_configurable);
+    $plugin_instance_d->setConfiguration([
+      'oman' => '42',
+    ]);
+    $this->fieldItem->setValue($plugin_instance_d);
+    $this->assertEqual(spl_object_hash($this->fieldItem->getContainedPluginInstance()), spl_object_hash($plugin_instance_d));
+  }
+
+}
