diff --git a/payment/lib/Drupal/payment/Annotations/PaymentMethod.php b/payment/lib/Drupal/payment/Annotations/PaymentMethod.php
new file mode 100644
index 0000000..36b1888
--- /dev/null
+++ b/payment/lib/Drupal/payment/Annotations/PaymentMethod.php
@@ -0,0 +1,39 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\payment\Annotations\PaymentMethod.
+ */
+
+namespace Drupal\payment\Annotations;
+
+use Drupal\Component\Annotation\Plugin;
+
+/**
+ * Defines a payment method plugin annotation.
+ *
+ * @Annotation
+ */
+class PaymentMethod extends Plugin {
+
+  /**
+   * The translated human-readable plugin name (optional).
+   *
+   * @var string
+   */
+  public $description = '';
+
+  /**
+   * The plugin ID.
+   *
+   * @var string
+   */
+  public $id;
+
+  /**
+   * The translated human-readable plugin name.
+   *
+   * @var string
+   */
+  public $label;
+}
diff --git a/payment/lib/Drupal/payment/PaymentMethodControllerUnavailable.php b/payment/lib/Drupal/payment/PaymentMethodControllerUnavailable.php
deleted file mode 100644
index 91aa667..0000000
--- a/payment/lib/Drupal/payment/PaymentMethodControllerUnavailable.php
+++ /dev/null
@@ -1,37 +0,0 @@
-<?php
-
-/**
- * Contains \Drupal\payment\PaymentMethodControllerUnavailable.
- */
-
-namespace Drupal\payment;
-
-use Drupal\payment\PaymentMethodController;
-
-/**
- * A payment method controller that essentially disables payment methods.
- *
- * This is a 'placeholder' controller that returns defaults and doesn't really
- * do anything else. It is used when no working controller is available for a
- * payment method, so other modules don't have to check for that.
- */
-class PaymentMethodControllerUnavailable extends PaymentMethodController {
-
-  function __construct() {
-    $this->title = t('Unavailable');
-  }
-
-  /**
-   * Implements PaymentMethodController::execute().
-   */
-  function execute(Payment $payment) {
-    $payment->setStatus(new PaymentStatusItem(PAYMENT_STATUS_UNKNOWN));
-  }
-
-  /**
-   * Implements PaymentMethodController::validate().
-   */
-  function validate(Payment $payment, PaymentMethod $payment_method, $strict) {
-    throw new PaymentValidationException(t('This payment method type is unavailable.'));
-  }
-}
\ No newline at end of file
diff --git a/payment/lib/Drupal/payment/PaymentMethodInterface.php b/payment/lib/Drupal/payment/PaymentMethodInterface.php
deleted file mode 100644
index efac8dd..0000000
--- a/payment/lib/Drupal/payment/PaymentMethodInterface.php
+++ /dev/null
@@ -1,30 +0,0 @@
-<?php
-
-/**
- * @file
- * Definition of Drupal\payment\PaymentMethodInterface.
- */
-
-namespace Drupal\payment;
-
-use Drupal\Core\Entity\EntityInterface;
-
-/**
- * Defines payment methods.
- */
-interface PaymentMethodInterface extends EntityInterface {
-
-  /**
-   * Validates a payment against this payment method.
-   *
-   * @param Payment $payment
-   * @param boolean $strict
-   *   Whether to validate everything a payment method needs or to validate the
-   *   most important things only. Useful when finding available payment methods,
-   *   for instance, which does not require unimportant things to be a 100%
-   *   valid.
-   *
-   * @throws PaymentValidationException
-   */
-  public function validatePayment(\Payment $payment, $strict = TRUE);
-}
diff --git a/payment/lib/Drupal/payment/PaymentMethodStorageController.php b/payment/lib/Drupal/payment/PaymentMethodStorageController.php
new file mode 100644
index 0000000..b00d665
--- /dev/null
+++ b/payment/lib/Drupal/payment/PaymentMethodStorageController.php
@@ -0,0 +1,32 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\payment\PaymentMethodStorageController.
+ */
+
+namespace Drupal\payment;
+
+use Drupal\Core\Config\Entity\ConfigStorageController;
+
+/**
+ * Handles storage for payment_method entities.
+ */
+class PaymentMethodStorageController extends ConfigStorageController {
+
+  /**
+   * {@inheritdoc}
+   *
+   * @see \Drupal\payment\Plugin\Core\Entity\PaymentMethod::getExportProperties
+   */
+  protected function buildQuery($ids, $revision_id = FALSE) {
+    $payment_methods = parent::buildQuery($ids, $revision_id);
+    $manager = $this->container->get('plugin.manager.payment.payment_method');
+    foreach ($payment_methods as $payment_method) {
+      $payment_method->controller = $manager->createInstance($payment_method->controllerID, $payment_method->controllerConfiguration);
+      $payment_method->controllerID == $payment_method->controllerConfiguration = NULL;
+    }
+
+    return $payment_methods;
+  }
+}
diff --git a/payment/lib/Drupal/payment/PaymentProcessingInterface.php b/payment/lib/Drupal/payment/PaymentProcessingInterface.php
new file mode 100644
index 0000000..3a7f468
--- /dev/null
+++ b/payment/lib/Drupal/payment/PaymentProcessingInterface.php
@@ -0,0 +1,61 @@
+<?php
+
+/**
+ * Contains \Drupal\payment\PaymentProcessingInterface.
+ */
+
+namespace Drupal\payment;
+
+use Drupal\payment\Plugin\Core\entity\Payment;
+
+/**
+ * Defines anything that can process payments.
+ */
+interface PaymentProcessingInterface {
+
+  /**
+   * Returns the supported currencies.
+   *
+   * @var array
+   *   Keys are ISO 4217 currency codes. Values are associative arrays with
+   *   keys "minimum" and "maximum", whose values are the minimum and maximum
+   *   amount supported for the specified currency. Leave empty to allow all
+   *   currencies.
+   */
+  public function currencies();
+
+  /**
+   * Returns the form elements to configure payments.
+   *
+   * $form_state['payment'] contains the payment that is added or edited. All
+   * payment-specific information should be added to it during element
+   * validation. The payment will be saved automatically.
+   *
+   * @param array $form
+   * @param array $form_state
+   *
+   * @return array
+   *   A render array.
+   */
+  public function paymentFormElements(array $form, array &$form_state);
+
+  /**
+   * Executes a payment.
+   *
+   * @param Payment $payment
+   */
+  public function executePayment(Payment $payment);
+
+  /**
+   * Validates a payment against a payment method and this controller. Don't
+   * call directly. Use PaymentMethod::validate() instead.
+   *
+   * @see PaymentMethod::validate()
+   *
+   * @param Payment $payment
+   * @param PaymentMethod $payment_method
+   *
+   * @throws PaymentValidationException
+   */
+  public function validatePayment(Payment $payment);
+}
\ No newline at end of file
diff --git a/payment/lib/Drupal/payment/Plugin/Core/entity/PaymentMethod.php b/payment/lib/Drupal/payment/Plugin/Core/entity/PaymentMethod.php
index e3f6407..7e6fcef 100644
--- a/payment/lib/Drupal/payment/Plugin/Core/entity/PaymentMethod.php
+++ b/payment/lib/Drupal/payment/Plugin/Core/entity/PaymentMethod.php
@@ -10,7 +10,9 @@ namespace Drupal\payment\Plugin\Core\Entity;
 use Drupal\Core\Annotation\Translation;
 use Drupal\Core\Config\Entity\ConfigEntityBase;
 use Drupal\Core\Entity\Annotation\EntityType;
-use Drupal\payment\PaymentMethodInterface;
+use Drupal\payment\Plugin\payment\PaymentMethod\PaymentMethodInterface as PluginPaymentMethodInterface;
+use Drupal\payment\Plugin\Core\entity\Payment;
+use Drupal\payment\Plugin\Core\entity\PaymentMethodInterface;
 
 /**
  * Defines a payment method entity.
@@ -19,7 +21,7 @@ use Drupal\payment\PaymentMethodInterface;
  *   config_prefix = "payment.payment_method",
  *   controllers = {
  *     "access" = "Drupal\payment\PaymentMethodAccessController",
- *     "storage" = "Drupal\Core\Config\Entity\ConfigStorageController",
+ *     "storage" = "Drupal\payment\PaymentMethodStorageController",
  *   },
  *   entity_keys = {
  *     "id" = "name",
@@ -36,18 +38,29 @@ use Drupal\payment\PaymentMethodInterface;
 class PaymentMethod extends ConfigEntityBase implements PaymentMethodInterface {
 
   /**
-   * The payment method controller this merchant uses.
+   * The payment method plugin this entity uses.
    *
-   * @var PaymentMethodController
+   * @var \Drupal\payment\Plugin\payment\PaymentMethod\PaymentMethodInterface
    */
-  public $controller = NULL;
+  public $plugin;
 
   /**
-   * Information about this payment method that is specific to its controller.
+   * The configuration of the payment method plugin in self::plugin.
+   *
+   * This property exists for storage purposes only.
    *
    * @var array
    */
-  public $controller_data = array();
+  protected $pluginConfiguration;
+
+  /**
+   * The plugin ID of the payment method plugin in self::plugin.
+   *
+   * This property exists for storage purposes only.
+   *
+   * @var string
+   */
+  protected $pluginID;
 
   /**
    * The entity's unique machine name.
@@ -84,12 +97,56 @@ class PaymentMethod extends ConfigEntityBase implements PaymentMethodInterface {
 
   /**
    * {@inheritdoc}
+   *
+   * @see \Drupal\payment\PaymentMethodStorageController
    */
-  public function validatePayment(\Payment $payment, $strict = TRUE) {
-    $this->controller->validate($payment, $this, $strict);
-    module_invoke_all('payment_validate', $payment, $this, $strict);
-    if (module_exists('rules')) {
-      rules_invoke_event('payment_validate', $payment, $this, $strict);
-    }
+  public function getExportProperties() {
+    $properties = parent::getExportProperties();
+    $properties['pluginConfiguration'] = $this->getPlugin()->getConfiguration();
+    $properties['pluginID'] = $this->getPlugin()->getPluginId();
+
+    return $properties;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setPlugin(PluginPaymentMethodInterface $plugin) {
+    $this->plugin = $plugin;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPlugin() {
+    return $this->plugin;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function currencies() {
+    return $this->getPlugin()->currencies();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function paymentFormElements(array $form, array &$form_state) {
+    return $this->getPlugin()->paymentFormElements($form, $form_state);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validatePayment(Payment $payment) {
+    return $this->getPlugin()->validatePayment($payment);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function executePayment(Payment $payment) {
+    return $this->getPlugin()->executePayment($payment);
   }
 }
diff --git a/payment/lib/Drupal/payment/Plugin/Core/entity/PaymentMethodInterface.php b/payment/lib/Drupal/payment/Plugin/Core/entity/PaymentMethodInterface.php
new file mode 100644
index 0000000..76db233
--- /dev/null
+++ b/payment/lib/Drupal/payment/Plugin/Core/entity/PaymentMethodInterface.php
@@ -0,0 +1,32 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\payment\Plugin\Core\entity\PaymentMethodInterface.
+ */
+
+namespace Drupal\payment\Plugin\Core\entity;
+
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\payment\PaymentProcessingInterface;
+use Drupal\payment\Plugin\payment\PaymentMethod\PaymentMethodInterface as PluginPaymentMethodInterface;
+
+/**
+ * Defines payment methods.
+ */
+interface PaymentMethodInterface extends EntityInterface, PaymentProcessingInterface {
+
+  /**
+   * Sets the payment method controller plugin.
+   *
+   * @param \Drupal\payment\Plugin\payment\PaymentMethod\PaymentMethodInterface
+   */
+  public function setPlugin(PluginPaymentMethodInterface $payment_method_controller);
+
+  /**
+   * Gets the payment method controller plugin.
+   *
+   * @return \Drupal\payment\Plugin\payment\PaymentMethod\PaymentMethodInterface
+   */
+  public function getPlugin();
+}
diff --git "a/payment/lib/Drupal/payment/Plugin/Core/entity/PaymentMethodInterface.php\303\245" "b/payment/lib/Drupal/payment/Plugin/Core/entity/PaymentMethodInterface.php\303\245"
new file mode 100644
index 0000000..efac8dd
--- /dev/null
+++ "b/payment/lib/Drupal/payment/Plugin/Core/entity/PaymentMethodInterface.php\303\245"
@@ -0,0 +1,30 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\payment\PaymentMethodInterface.
+ */
+
+namespace Drupal\payment;
+
+use Drupal\Core\Entity\EntityInterface;
+
+/**
+ * Defines payment methods.
+ */
+interface PaymentMethodInterface extends EntityInterface {
+
+  /**
+   * Validates a payment against this payment method.
+   *
+   * @param Payment $payment
+   * @param boolean $strict
+   *   Whether to validate everything a payment method needs or to validate the
+   *   most important things only. Useful when finding available payment methods,
+   *   for instance, which does not require unimportant things to be a 100%
+   *   valid.
+   *
+   * @throws PaymentValidationException
+   */
+  public function validatePayment(\Payment $payment, $strict = TRUE);
+}
diff --git a/payment/lib/Drupal/payment/Plugin/payment/PaymentMethod/Base.php b/payment/lib/Drupal/payment/Plugin/payment/PaymentMethod/Base.php
new file mode 100644
index 0000000..4616f9a
--- /dev/null
+++ b/payment/lib/Drupal/payment/Plugin/payment/PaymentMethod/Base.php
@@ -0,0 +1,114 @@
+<?php
+
+/**
+ * Contains \Drupal\payment\PaymentMethod.
+ */
+
+namespace Drupal\payment\Plugin\payment\PaymentMethod;
+
+use Drupal\Component\Plugin\PluginBase;
+use Drupal\Component\Plugin\PluginInspectionInterface;
+use Drupal\payment\Plugin\payment\PaymentMethod\PaymentMethodInterface;
+use Drupal\payment\Plugin\Core\entity\Payment;
+use Drupal\payment\Plugin\Core\entity\PaymentMethod;
+
+/**
+ * A base payment method controller.
+ */
+abstract class Base extends PluginBase implements PaymentMethodInterface {
+
+  /**
+   * The payment method this plugin is for.
+   *
+   * @var \Drupal\payment\PaymentProcessingInterface
+   */
+  protected $paymentMethod;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setConfiguration(array $configuration) {
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getConfiguration() {
+    return array();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function paymentFormElements(array $form, array &$form_state) {
+    return array();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function paymentMethodFormElements(array $form, array &$form_state) {
+    return array();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function executePayment(Payment $payment) {}
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validatePayment(Payment $payment) {
+    // Confirm the payment method is enabled, and thus available in general.
+    if (!$this->getPaymentMethod()->status()) {
+      throw new PaymentValidationPaymentMethodDisabledException(t('The payment method is disabled.'));
+    }
+
+    if (!$payment->currency_code) {
+      throw new PaymentValidationMissingCurrencyException(t('The payment has no currency set.'));
+    }
+
+    $currencies = $this->getPaymentMethod()->controller->currencies;
+
+    // Confirm the payment's currency is supported.
+    $currencies = $this->currencies();
+    if (!empty($currencies) && !isset($currencies[$payment->currency_code])) {
+      throw new PaymentValidationUnsupportedCurrencyException(t('The currency is not supported by this payment method.'));
+    }
+
+    // Confirm the payment's description is set and valid.
+    if (empty($payment->description)) {
+      throw new PaymentValidationDescriptionMissing(t('The payment description is not set.'));
+    }
+    elseif (drupal_strlen($payment->description) > 255) {
+      throw new PaymentValidationDescriptionTooLong(t('The payment description exceeds 255 characters.'));
+    }
+
+    // Confirm the finish callback is set and the function exists.
+    if (empty($payment->finish_callback) || !function_exists($payment->finish_callback)) {
+      throw new PaymentValidationMissingFinishCallback(t('The finish callback is not set or not callable.'));
+    }
+
+    // Confirm the payment amount is higher than the supported minimum.
+    $minimum = isset($currencies[$payment->currency_code]['minimum']) ? $currencies[$payment->currency_code]['minimum'] : PAYMENT_MINIMUM_AMOUNT;
+    if ($payment->totalAmount(TRUE) < $minimum) {
+      throw new PaymentValidationAmountBelowMinimumException(t('The amount should be higher than !minimum.', array(
+        '!minimum' => payment_amount_human_readable($minimum, $payment->currency_code),
+      )));
+    }
+
+    // Confirm the payment amount does not exceed the maximum.
+    if (isset($currencies[$payment->currency_code]['maximum']) && $payment->totalAmount(TRUE) > $currencies[$payment->currency_code]['maximum']) {
+      throw new PaymentValidationAmountExceedsMaximumException(t('The amount should be lower than !maximum.', array(
+        '!maximum' => payment_amount_human_readable($currencies[$payment->currency_code]['maximum'], $payment->currency_code),
+      )));
+    }
+
+    // Invoke events.
+    module_invoke_all('payment_validate', $payment, $this->getPaymentMethod());
+    if (module_exists('rules')) {
+      rules_invoke_event('payment_validate', $payment, $this->getPaymentMethod());
+    }
+  }
+}
\ No newline at end of file
diff --git a/payment/lib/Drupal/payment/Plugin/payment/PaymentMethod/Manager.php b/payment/lib/Drupal/payment/Plugin/payment/PaymentMethod/Manager.php
new file mode 100644
index 0000000..eba15b4
--- /dev/null
+++ b/payment/lib/Drupal/payment/Plugin/payment/PaymentMethod/Manager.php
@@ -0,0 +1,51 @@
+<?php
+
+/**
+ * Contains \Drupal\payment\Plugin\payment\PaymentMethod\Manager.
+ */
+
+namespace Drupal\payment\Plugin\payment\PaymentMethod;
+
+use Drupal\Component\Plugin\Exception\PluginException;
+use Drupal\Component\Plugin\Factory\DefaultFactory;
+use Drupal\Component\Plugin\PluginManagerBase;
+use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
+use Drupal\Core\Plugin\Discovery\AlterDecorator;
+use Drupal\Core\Plugin\Discovery\CacheDecorator;
+
+/**
+ * Manages discovery and instantiation of payment method controller plugins.
+ *
+ * @see \Drupal\payment\Plugin\payment\PaymentMethod\PaymentMethodInterface
+ */
+class Manager extends PluginManagerBase {
+
+  /**
+   * Constructor.
+   *
+   * @param array $namespaces
+   *   An array of paths keyed by their corresponding namespaces.
+   */
+  public function __construct(\Traversable $namespaces) {
+    $annotation_namespaces = array(
+      'Drupal\payment\Annotations' => drupal_get_path('module', 'payment') . '/lib',
+    );
+    $this->discovery = new AnnotatedClassDiscovery('payment/PaymentMethod', $namespaces, $annotation_namespaces, 'Drupal\payment\Annotations\PaymentMethod');
+    $this->discovery = new AlterDecorator($this->discovery, 'payment_method');
+    $this->discovery = new CacheDecorator($this->discovery, 'payment_method');
+    $this->factory = new DefaultFactory($this->discovery);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function createInstance($plugin_id, array $configuration = array()) {
+    // If a plugin is missing, use the default.
+    try {
+      return parent::createInstance($plugin_id, $configuration);
+    }
+    catch (PluginException $e) {
+      return parent::createInstance('payment_unavailable', $configuration);
+    }
+  }
+}
diff --git a/payment/lib/Drupal/payment/Plugin/payment/PaymentMethod/PaymentMethodInterface.php b/payment/lib/Drupal/payment/Plugin/payment/PaymentMethod/PaymentMethodInterface.php
new file mode 100644
index 0000000..8baa8a9
--- /dev/null
+++ b/payment/lib/Drupal/payment/Plugin/payment/PaymentMethod/PaymentMethodInterface.php
@@ -0,0 +1,49 @@
+<?php
+
+/**
+ * Contains \Drupal\payment\plugin\payment\PaymentMethod\PaymentMethodInterface.
+ */
+
+namespace Drupal\payment\plugin\payment\PaymentMethod;
+
+use Drupal\Component\Plugin\PluginInspectionInterface;
+use Drupal\payment\PaymentProcessingInterface;
+use Drupal\payment\Plugin\Core\entity\Payment;
+
+/**
+ * A payment method plugin (the logic behind a payment method entity).
+ *
+ * @see \Drupal\payment\Plugin\Core\entity\PaymentMethod
+ */
+interface PaymentMethodInterface extends PaymentProcessingInterface, PluginInspectionInterface {
+
+  /**
+   * Sets the plugin configuration.
+   *
+   * @param array $configuration
+   */
+  public function setConfiguration(array $configuration);
+
+  /**
+   * Gets the plugin configuration.
+   *
+   * @return array
+   *  The data is not allowed to contain objects.
+   */
+  public function getConfiguration();
+
+  /**
+   * Returns the form elements to configure payment methods.
+   *
+   * $form_state['payment_method'] contains the payment method that is added or
+   * edited. All method-specific information should be added to it during
+   * element validation. The payment method will be saved automatically.
+   *
+   * @param array $form
+   * @param array $form_state
+   *
+   * @return array
+   *   A render array.
+   */
+  public function paymentMethodFormElements(array $form, array &$form_state);
+}
diff --git a/payment/lib/Drupal/payment/Plugin/payment/PaymentMethod/Unavailable.php b/payment/lib/Drupal/payment/Plugin/payment/PaymentMethod/Unavailable.php
new file mode 100644
index 0000000..6d276c9
--- /dev/null
+++ b/payment/lib/Drupal/payment/Plugin/payment/PaymentMethod/Unavailable.php
@@ -0,0 +1,48 @@
+<?php
+
+/**
+ * Contains \Drupal\payment\Plugin\payment\PaymentMethod\Unavailable.
+ */
+
+namespace Drupal\payment\Plugin\payment\PaymentMethod;
+
+use Drupal\Core\Annotation\Translation;
+use Drupal\payment\Annotations\PaymentMethod;
+use Drupal\payment\Plugin\Core\entity\Payment;
+
+/**
+ * A payment method controller that essentially disables payment methods.
+ *
+ * This is a 'placeholder' controller that returns defaults and doesn't really
+ * do anything else. It is used when no working controller is available for a
+ * payment method, so other modules don't have to check for that.
+ *
+ * @PaymentMethod(
+ *   id = "payment_unavailable",
+ *   label = @Translation("Unavailable"),
+ *   module = "payment"
+ * )
+ */
+class Unavailable extends Base {
+
+  /**
+   * {@inheritdoc}.
+   */
+  public function currencies() {
+    return array();
+  }
+
+  /**
+   * {@inheritdoc}.
+   */
+  public function executePayment(Payment $payment) {
+    $payment->setStatus(new PaymentStatusItem(PAYMENT_STATUS_UNKNOWN));
+  }
+
+  /**
+   * {@inheritdoc}.
+   */
+  public function validatePayment(Payment $payment) {
+    throw new PaymentValidationException(t('This payment method plugin is unavailable.'));
+  }
+}
diff --git a/payment/lib/Drupal/payment/Tests/Utility.php b/payment/lib/Drupal/payment/Tests/Utility.php
index b9d1dbd..88f45fb 100644
--- a/payment/lib/Drupal/payment/Tests/Utility.php
+++ b/payment/lib/Drupal/payment/Tests/Utility.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\payment\Tests;
 
+use Drupal\payment\Plugin\payment\PaymentMethod\PaymentMethodInterface as PluginPaymentMethodInterface;
 use Drupal\Tests\UnitTestCase;
 
 /**
@@ -49,18 +50,16 @@ class Utility {
    *
    * @param integer $uid
    *   The user ID of the payment method's owner.
-   * @param PaymentMethodController $controller
-   *   An optional controller to set. Defaults to
-   *   PaymentMethodControllerUnavailable.
+   * @param \Drupal\payment\Plugin\payment\PaymentMethod\PaymentMethodInterface $plugin
+   *   An optional plugin to set. Defaults to payment_unavailable.
    *
    * @return PaymentMethod
    */
-  static function createPaymentMethod($uid, PaymentMethodController $controller = NULL) {
+  static function createPaymentMethod($uid, PaymentMethodInterface $plugin = NULL) {
     $name = UnitTestCase::randomName();
-    $controller = $controller ? $controller : payment_method_controller_load('PaymentMethodControllerUnavailable');
+    $plugin = $plugin ? $plugin : \Drupal::service('plugin.manager.payment.payment_method')->createInstance('payment_unavailable');
     $payment_method = entity_create('payment_method', array(
-      'controller' => $controller,
-      'controller_data' => $controller->controller_data_defaults,
+      'plugin' => $plugin,
       'name' => $name,
       'label' => $name,
       'uid' => $uid,
diff --git a/payment/payment.api.php b/payment/payment.api.php
index f96d36a..9f1bd34 100644
--- a/payment/payment.api.php
+++ b/payment/payment.api.php
@@ -35,36 +35,17 @@ function hook_payment_status_info_alter(array &$statuses_info) {
 }
 
 /**
- * Defines payment method controllers.
+ * Alters payment method plugins.
  *
- * @return array
- *   An array with the names of payment method controller classes. Keys may
- *   either specific aliases for their values (the classes), or be numeric
- *   (left empty) to make them default to the controller class names they
- *   belong to. This allows hook_payment_method_controller_info_alter() to
- *   override payment method controller class by setting a different class name
- *   for an alias.
- */
-function hook_payment_method_controller_info() {
-  return array(
-    'DummyPaymentMethodController',
-    'CashOnDeliveryPaymentMethodController',
-  );
-}
-
-/**
- * Alters payment method controllers.
- *
- * @param array $controllers_info
- *   Keys are payment controller aliases, values are actual payment method
- *   controller class names.
+ * @param array $definitions
+ *   Keys are plugin IDs. Values are plugin definitions.
  */
-function hook_payment_method_controller_info_alter(array &$controllers_info) {
-  // Remvove a payment method controller.
-  unset($controllers_info['FooPaymentMethodController']);
+function hook_payment_method_controller_alter(array &$definitions) {
+  // Remvove a payment method plugin.
+  unset($definitions['foo_plugin_id']);
 
-  // Replace PaymentMethodControllerUnavailable with another controller.
-  $controllers_info['PaymentMethodControllerUnavailable'] = 'FooPaymentMethodControllerUnavailableAdvanced';
+  // Replace a payment method plugin with another.
+  $definitions['foo_plugin_id']['class'] = 'Drupal\foo\FooPaymentMethod';
 }
 
 /**
@@ -170,16 +151,11 @@ function hook_payment_pre_finish(Payment $payment) {
  *   $payment->method contains the method currently configured, but NOT the
  *   method that $payment should be tested against, which is $payment_method.
  * @param PaymentMethod $payment_method
- * @param boolean $strict
- *   Whether to validate everything a payment method needs or to validate the
- *   most important things only. Useful when finding available payment methods,
- *   for instance, which does not require unimportant things to be a 100%
- *   valid.
  *
  * @return boolean
  *   Whether the payment and/or the payment method are valid.
  */
-function hook_payment_validate(Payment $payment, PaymentMethod $payment_method, $strict) {}
+function hook_payment_validate(Payment $payment, PaymentMethod $payment_method) {}
 
 /**
  * Alter the payment form.
diff --git a/payment/payment.classes.inc b/payment/payment.classes.inc
index 816fe43..8edfb45 100644
--- a/payment/payment.classes.inc
+++ b/payment/payment.classes.inc
@@ -613,7 +613,7 @@ class PaymentMethodUnavailable extends PaymentMethod {
 
   function __construct() {
     $this->title_specific = $this->title_generic = t('Unavailable');
-    $this->controller = payment_method_controller_load('PaymentMethodControllerUnavailable');
+    $this->controller = \Drupal::service('plugin.manager.payment.payment_method')->createInstance('payment_unavailable');
   }
 }
 
diff --git a/payment/payment.generate.inc b/payment/payment.generate.inc
index 0bf1fda..113b8bc 100644
--- a/payment/payment.generate.inc
+++ b/payment/payment.generate.inc
@@ -21,7 +21,7 @@ class PaymentGenerate {
   static function paymentMethod($controller_class_name = 'PaymentMethodControllerUnavailable', $uid = 1) {
     require_once './modules/simpletest/drupal_web_test_case.php';
     $name = DrupalTestCase::randomName();
-    $controller = payment_method_controller_load($controller_class_name);
+    $controller = \Drupal::service('plugin.manager.payment.payment_method')->createInstance($controller_class_name);
     $payment_method = new PaymentMethod(array(
       'controller' => $controller,
       'controller_data' => $controller->controller_data_defaults,
diff --git a/payment/payment.module b/payment/payment.module
index 59391c7..f3ce027 100644
--- a/payment/payment.module
+++ b/payment/payment.module
@@ -361,12 +361,12 @@ function payment_permission() {
       'title' => t('View payment status overview'),
     ),
   );
-  $controllers = payment_method_controller_load_multiple();
-  unset($controllers['PaymentMethodControllerUnavailable']);
-  foreach ($controllers as $controller) {
-    $permissions['payment.payment_method.create.' . $controller->name] = array(
-      'title' => t('Create %controller_title payment methods', array(
-        '%controller_title' => $controller->title,
+  $definitions = \Drupal::service('plugin.manager.payment.payment_method')->getDefinitions();
+  unset($definitions['payment_unavailable']);
+  foreach ($definitions as $plugin_id => $definition) {
+    $permissions['payment.payment_method.create.' . $plugin_id] = array(
+      'title' => t('Create %plugin_label payment methods', array(
+        '%plugin_label' => $definition['label'],
       )),
     );
   }
@@ -381,7 +381,7 @@ function payment_hook_info() {
   $hooks['payment_status_info'] = array(
     'group' => 'payment',
   );
-  $hooks['payment_method_controller_info'] = array(
+  $hooks['payment_method_controller_alter'] = array(
     'group' => 'payment',
   );
   $hooks['payment_line_item_info'] = array(
@@ -780,80 +780,6 @@ function payment_status_is_or_has_ancestor($status, $ancestor_status) {
 }
 
 /**
- * Returns information about payment method controllers.
- *
- * @return array
- *   Keys are payment method controller class aliases, values are the real
- *   payment method controller class names. This allows
- *   hook_payment_method_controller_info_alter() to override payment method
- *   controllers.
- */
-function payment_method_controllers_info() {
-  $controllers_info = &drupal_static(__FUNCTION__);
-
-  if (!$controllers_info) {
-    $controllers_info = module_invoke_all('payment_method_controller_info');
-    foreach ($controllers_info as $controller_class_name_alias => $controller_class_name) {
-      if (is_numeric($controller_class_name_alias)) {
-        unset($controllers_info[$controller_class_name_alias]);
-        $controllers_info[$controller_class_name] = $controller_class_name;
-      }
-    }
-    drupal_alter('payment_method_controller_info', $controllers_info);
-  }
-
-  return $controllers_info;
-}
-
-/**
- * Load a payment method controller().
- *
- * @param string $controller_class_name
- *   The name of the controller class to load.
- *
- * @return mixed
- *   Either a PaymentMethodController object or FALSE in case of errors.
- */
-function payment_method_controller_load($controller_class_name) {
-  $controllers = payment_method_controller_load_multiple(array($controller_class_name));
-
-  return reset($controllers);
-}
-
-/**
- * Load multiple payment method controllers.
- *
- * @param array $controller_class_names
- *   An array with names of controller classes. Leave empty to load all
- *   controllers.
- *
- * @return array
- *   Keys are the values of $controller_class_names passed on to this function.
- *   Every value is either a PaymentMethodController object or FALSE if the
- *   controller could not be loaded.
- */
-function payment_method_controller_load_multiple(array $controller_class_names = array()) {
-  $controllers = NULL;
-
-  // Load all existing controllers.
-  if (is_null($controllers)) {
-    foreach (payment_method_controllers_info() as $controller_class_name_alias => $controller_class_name) {
-      $controller = new $controller_class_name();
-      // @todo The namespace removal is a temporary fix until controllers are
-      // plugins and have machine names.
-      $controller->name = substr($controller_class_name, strrpos($controller_class_name, '\\') + 1);
-      $controller_class_name_alias = substr($controller_class_name_alias, strrpos($controller_class_name_alias, '\\') + 1);
-      $controllers[$controller_class_name_alias] = $controller;
-    }
-  }
-
-  // Set FALSE for requested controllers that do not exist.
-  $controllers += array_fill_keys(array_diff($controller_class_names, array_keys($controllers)), FALSE);
-
-  return $controller_class_names ? array_intersect_key($controllers, array_flip($controller_class_names)) : array_filter($controllers);
-}
-
-/**
  * Convert an amount as a float to a human-readable format.
  *
  * @param float $amount
diff --git a/payment/payment.payment.inc b/payment/payment.payment.inc
index fffbff0..1551b70 100644
--- a/payment/payment.payment.inc
+++ b/payment/payment.payment.inc
@@ -62,13 +62,6 @@ function payment_payment_status_info() {
 }
 
 /**
- * Implements hook_payment_method_controller_info().
- */
-function payment_payment_method_controller_info() {
-  return array('Drupal\payment\PaymentMethodControllerUnavailable');
-}
-
-/**
  * Implements hook_payment_line_item_info().
  */
 function payment_payment_line_item_info() {
diff --git a/payment/payment.rules.inc b/payment/payment.rules.inc
index 58b2cba..f55188a 100644
--- a/payment/payment.rules.inc
+++ b/payment/payment.rules.inc
@@ -60,11 +60,6 @@ function payment_rules_event_info() {
         'label' => t('Payment method'),
         'description' => t('The payment method the variable is validated against.'),
       ),
-      'strict' => array(
-        'type' => 'boolean',
-        'label' => t('Strict validation'),
-        'description' => t('Whether to validate everything a payment method needs or to validate the most important things only. Useful when finding available payment methods, for instance, which does not require unimportant things to be a 100% valid.'),
-      ),
     ),
   );
 
diff --git a/payment/payment.services.yml b/payment/payment.services.yml
new file mode 100644
index 0000000..1112460
--- /dev/null
+++ b/payment/payment.services.yml
@@ -0,0 +1,4 @@
+services:
+  plugin.manager.payment.payment_method:
+    class: Drupal\payment\Plugin\payment\PaymentMethod\Manager
+    arguments: ['@container.namespaces']
\ No newline at end of file
diff --git a/payment/payment.ui.inc b/payment/payment.ui.inc
index 8849c13..350bfbc 100644
--- a/payment/payment.ui.inc
+++ b/payment/payment.ui.inc
@@ -232,19 +232,19 @@ function payment_form_payment_delete_submit(array $form, array &$form_state) {
  * @return string
  */
 function payment_page_payment_method_add_select_controller() {
-  $controllers = payment_method_controller_load_multiple();
-  unset($controllers['PaymentMethodControllerUnavailable']);
-  if ($controllers) {
+  $definitions = \Drupal::service('plugin.manager.payment.payment_method')->getDefinitions();
+  unset($definitions['payment_unavailable']);
+  if ($definitions) {
     $items = array();
-    foreach ($controllers as $controller) {
-      $payment_method = new PaymentMethod(array(
-        'controller' => $controller,
+    foreach ($definitions as $plugin_id => $definition) {
+      $payment_method = entity_create('payment_method', array(
+        'controller' => $manager->createInstance($plugin_id),
       ));
       if ($payment_method->access('create')) {
         $items[] = array(
-          'title' => $controller->title,
-          'href' => 'admin/config/services/payment/method/add/' . $controller->name,
-          'description' => $controller->description,
+          'title' => $definition['title'],
+          'href' => 'admin/config/services/payment/method/add/' . $plugin_id,
+          'description' => $definition['description'],
           'localized_options' => array(),
         );
       }
@@ -267,11 +267,12 @@ function payment_page_payment_method_add_select_controller() {
  *   has permission to create payment methods with.
  */
 function payment_page_payment_method_add_select_controller_access() {
-  $controllers = payment_method_controller_load_multiple();
-  unset($controllers['PaymentMethodControllerUnavailable']);
-  foreach ($controllers as $controller) {
-    $payment_method = new PaymentMethod(array(
-      'controller' => $controller,
+  $manager = \Drupal::service('plugin.manager.payment.payment_method');
+  $definitions = $manager->getDefinitions();
+  unset($definitions['payment_unavailable']);
+  foreach (array_keys($definitions) as $plugin_id) {
+    $payment_method = entity_create('payment_method', array(
+      'controller' => $manager->createInstance($plugin_id),
     ));
     if ($payment_method->access('create')) {
       return TRUE;
@@ -645,12 +646,8 @@ function payment_page_method_clone(PaymentMethod $payment_method) {
  * Implements form process callback for a payment_form_context element.
  */
 function payment_form_process_context(array $element, array &$form_state, array $form) {
-  if ($build_callback = payment_method_controller_form_callback(payment_method_controller_load($element['#payment_method_controller_name']), $element['#callback_type'], 'build')) {
-    $element = array_merge($element, $build_callback($element, $form_state));
-  }
-  if ($validate_callback = payment_method_controller_form_callback(payment_method_controller_load($element['#payment_method_controller_name']), $element['#callback_type'], 'validate')) {
-    $element['#element_validate'] = array($validate_callback);
-  }
+  $elements = \Drupal::service('plugin.manager.payment.payment_method')->createInstance($element['#payment_method_controller_name'])->paymentFormElements($element, $form_state);
+  $element = array_merge($element, $elements);
 
   return $element;
 }
@@ -1037,32 +1034,6 @@ function payment_form_process_method_submit_ajax_callback(array $form, array &$f
 }
 
 /**
- * Call one of a payment method controller's form callbacks.
- *
- * @param PaymentMethodController $controller
- * @param string $callback
- *   Either "payment" or "payment_method".
- * @param string $operation
- *   Either "build" or "validate".
- *
- * @return string
- *   The callback function's name.
- */
-function payment_method_controller_form_callback(PaymentMethodController $controller, $callback, $operation) {
-  $property = $callback . '_configuration_form_elements_callback';
-  switch ($operation) {
-    case 'build':
-      $function = $controller->$property;
-      break;
-    case 'validate':
-      $function = $controller->$property . '_validate';
-      break;
-  }
-
-  return isset($function) && function_exists($function) ? $function : FALSE;
-}
-
-/**
  * Returns a hierarchical representation of payment statuses.
  *
  * @return array
@@ -1168,8 +1139,10 @@ function payment_method_options() {
  */
 function payment_method_controller_options() {
   $options = array();
-  foreach (payment_method_controller_load_multiple() as $payment_method_controller) {
-    $options[$payment_method_controller->name] = $payment_method_controller->title;
+  $definitions = \Drupal::service('plugin.manager.payment.payment_method')->getDefinitions();
+  unset($definitions['payment_unavailable']);
+  foreach ($definitions as $plugin_id => $definition) {
+    $options[$plugin_id] = $definition['title'];
   }
   natcasesort($options);
 
diff --git a/payment/payment.xtools.inc b/payment/payment.xtools.inc
index fe2cd4d..4e67adc 100644
--- a/payment/payment.xtools.inc
+++ b/payment/payment.xtools.inc
@@ -14,11 +14,6 @@ function payment_xtools_blueprint_info() {
     new XtoolsBlueprintPlaceholder('PaymentLineItemInfo'),
   ), 'integer');
 
-  // hook_payment_method_controller_info() implementation return value.
-  $blueprints['hook_payment_method_controller_info'] = new XtoolsBlueprintArray(array(), array(
-    new XtoolsBlueprintString,
-  ));
-
   // hook_payment_status_info() implementation return value.
   $blueprints['hook_payment_status_info'] = new XtoolsBlueprintArray(array(), array(
     new XtoolsBlueprintPlaceholder('PaymentStatusInfo'),
@@ -104,12 +99,6 @@ function payment_xtools_callable_type_info() {
     new XtoolsCallableTypeHook('payment_line_item_info_alter', array(
       'signature' => new XtoolsSignaturePlaceholder('hook_payment_line_item_info_alter'),
     )),
-    new XtoolsCallableTypeHook('payment_method_controller_info', array(
-      'blueprint' => new XtoolsBlueprintPlaceholder('hook_payment_method_controller_info'),
-    )),
-    new XtoolsCallableTypeHook('payment_method_controller_info_alter', array(
-      'signature' => new XtoolsSignaturePlaceholder('hook_payment_method_controller_info_alter'),
-    )),
     new XtoolsCallableTypeHook('payment_pre_execute', array(
       'signature' => new XtoolsSignaturePlaceholder('hook_payment_pre_execute'),
     )),
@@ -156,13 +145,6 @@ function payment_xtools_signature_info() {
       'type' => 'array',
     )),
   ));
-  $signatures['hook_payment_method_controller_info_alter'] = new XtoolsSignature(array(
-    new XtoolsSignatureParameter(array(
-      'name' => 'controllers_info',
-      'reference' => TRUE,
-      'type' => 'array',
-    )),
-  ));
   $signatures['hook_payment_pre_execute'] = new XtoolsSignature(array(
     new XtoolsSignatureParameter(array(
       'name' => 'payment',
@@ -195,9 +177,6 @@ function payment_xtools_signature_info() {
       'name' => 'payment_method',
       'type' => 'PaymentMethod',
     )),
-    new XtoolsSignatureParameter(array(
-      'name' => 'strict',
-    )),
   ));
 
   return $signatures;
diff --git a/payment/tests/payment_test/payment_test.module b/payment/tests/payment_test/payment_test.module
index fec7302..30a62c9 100644
--- a/payment/tests/payment_test/payment_test.module
+++ b/payment/tests/payment_test/payment_test.module
@@ -54,7 +54,7 @@ function payment_test_payment_line_item_info() {
 /**
  * Implements hook_payment_validate().
  */
-function payment_test_payment_validate(Payment $payment, PaymentMethod $payment_method, $strict) {
+function payment_test_payment_validate(Payment $payment, PaymentMethod $payment_method) {
   if (isset($payment->payment_test_payment_validate)) {
     throw new PaymentValidationException('payment_test');
   }
diff --git a/payment/views/PaymentViewsHandlerFieldPaymentMethodControllerDescription.inc b/payment/views/PaymentViewsHandlerFieldPaymentMethodControllerDescription.inc
index 5af611c..c934cc0 100644
--- a/payment/views/PaymentViewsHandlerFieldPaymentMethodControllerDescription.inc
+++ b/payment/views/PaymentViewsHandlerFieldPaymentMethodControllerDescription.inc
@@ -14,6 +14,8 @@ class PaymentViewsHandlerFieldPaymentMethodControllerDescription extends views_h
    * Implements views_handler_field::render().
    */
   function render($values) {
-    return payment_method_controller_load($this->get_value($values))->description;
+    $definition = \Drupal::service('plugin.manager.payment.payment_method')->getDefinition($this->getValue($values));
+
+    return $definition['description'];
   }
 }
diff --git a/payment/views/PaymentViewsHandlerFieldPaymentMethodControllerTitle.inc b/payment/views/PaymentViewsHandlerFieldPaymentMethodControllerTitle.inc
index e124097..c2325d4 100644
--- a/payment/views/PaymentViewsHandlerFieldPaymentMethodControllerTitle.inc
+++ b/payment/views/PaymentViewsHandlerFieldPaymentMethodControllerTitle.inc
@@ -14,8 +14,8 @@ class PaymentViewsHandlerFieldPaymentMethodControllerTitle extends views_handler
    * Implements views_handler_field::render().
    */
   function render($values) {
-    if ($controller = payment_method_controller_load($this->get_value($values), TRUE)) {
-      return $controller->title;
-    }
+    $definition = \Drupal::service('plugin.manager.payment.payment_method')->getDefinition($this->getValue($values));
+
+    return $definition['label'];
   }
 }
