diff --git a/payment/lib/Drupal/payment/Annotations/PaymentMethod.php b/payment/lib/Drupal/payment/Annotations/PaymentMethod.php
index 36b1888..cf2f232 100644
--- a/payment/lib/Drupal/payment/Annotations/PaymentMethod.php
+++ b/payment/lib/Drupal/payment/Annotations/PaymentMethod.php
@@ -36,4 +36,27 @@ class PaymentMethod extends Plugin {
    * @var string
    */
   public $label;
+
+  /**
+   * Available payment operations.
+   *
+   * For interoperability, there are a few operations with standard names that
+   * plugins may or must implement:
+   * - execute (required): This is a special operation that must always be
+   *   implemented, and will usually result in the authorization and optionally
+   *   the capture of the payment by the payer. The exact implementation depends
+   *   on the plugin and its configuration.
+   * - authorize: the authorization of the payment by the payer, but not the
+   *   transfer of funds.
+   * - cancel: the cancellation of a pending payment.
+   * - capture: the transfer of funds after authorization was given previously.
+   * - refund: the refund of an already completed payment.
+   *
+   * @var array
+   *   Keys are operation names. Values are associative arrays:
+   *   - interrupts_execution (bool): whether executing the operation will
+   *     interrupt the execution of the script. Defaults to TRUE.
+   *   - label (string): The human-readable label. Required.
+   */
+  public $operations = array();
 }
diff --git a/payment/lib/Drupal/payment/PaymentProcessingInterface.php b/payment/lib/Drupal/payment/PaymentProcessingInterface.php
index 8cdaf3d..63c89dc 100644
--- a/payment/lib/Drupal/payment/PaymentProcessingInterface.php
+++ b/payment/lib/Drupal/payment/PaymentProcessingInterface.php
@@ -40,22 +40,28 @@ interface PaymentProcessingInterface {
   public function paymentFormElements(array $form, array &$form_state);
 
   /**
-   * Executes a payment.
+   * Checks access to execute a payment operation.
    *
-   * @param Payment $payment
+   * @see \Drupal\payment\Annotations\PaymentMethod
+   *
+   * @param \Drupal\payment\Plugin\Core\entity\PaymentInterface $payment
+   * @param string $operation
+   *   See \Drupal\payment\Annotations\PaymentMethod::operations for the
+   *   definition of available operations.
+   *
+   * @return bool
    */
-  public function executePayment(PaymentInterface $payment);
+  function paymentOperationAccess(PaymentInterface $payment, $operation);
 
   /**
-   * Validates a payment against a payment method and this controller. Don't
-   * call directly. Use PaymentMethod::validate() instead.
-   *
-   * @see PaymentMethod::validate()
+   * Executes a payment operation.
    *
-   * @param Payment $payment
-   * @param PaymentMethod $payment_method
+   * @see \Drupal\payment\Annotations\PaymentMethod
    *
-   * @throws PaymentValidationException
+   * @param \Drupal\payment\Plugin\Core\entity\PaymentInterface $payment
+   * @param string $operation
+   *   See \Drupal\payment\Annotations\PaymentMethod::operations for the
+   *   definition of available operations.
    */
-  public function validatePayment(PaymentInterface $payment);
+  function executePaymentOperation(PaymentInterface $payment, $operation);
 }
\ No newline at end of file
diff --git a/payment/lib/Drupal/payment/Plugin/Core/entity/Payment.php b/payment/lib/Drupal/payment/Plugin/Core/entity/Payment.php
index b7eb655..e3bc216 100644
--- a/payment/lib/Drupal/payment/Plugin/Core/entity/Payment.php
+++ b/payment/lib/Drupal/payment/Plugin/Core/entity/Payment.php
@@ -278,17 +278,14 @@ class Payment extends EntityNG implements PaymentInterface {
   /**
    * {@inheritdoc}
    */
-  public function getAvailablePaymentMethods(array $paymentMethods = array()) {
-    if (!$paymentMethods) {
-      $paymentMethods = entity_load_multiple('payment_method');
+  public function getAvailablePaymentMethods(array $payment_methods = array()) {
+    if (!$payment_methods) {
+      $payment_methods = entity_load_multiple('payment_method');
     }
     $available = array();
-    foreach ($paymentMethods as $paymentMethod) {
-      try {
-        $paymentMethod->validatePayment($this);
-        $available[$paymentMethod->id()] = $paymentMethod;
-      }
-      catch (PaymentValidationException $e) {
+    foreach ($payment_methods as $payment_method) {
+      if ($payment_method->paymentOperationAccess($this, 'execute')) {
+        $available[$payment_method->id()] = $payment_method;
       }
     }
 
@@ -325,27 +322,8 @@ class Payment extends EntityNG implements PaymentInterface {
     }
     else {
       $this->setStatus($manager->createInstance('payment_pending'));
-      $this->getPaymentMethod()->executePayment($this);
-    }
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function validate() {
-    $violations = parent::validate();
-    // Do not call the payment method if it does not exist, in which case a
-    // violation will already have been added.
-    if ($this->getPaymentMethodId()) {
-      try {
-        $this->getPaymentMethod()->validatePayment($this);
-      }
-      catch (PaymentValidationException $exception) {
-        $violations->add(new ConstraintViolation($exception->getMessage(), $exception->getMessage(), array(), $this, 'paymentMethodId', $this->getPaymentMethodId()));
-      }
+      $this->getPaymentMethod()->executePaymentOperation('execute', $this);
     }
-
-    return $violations;
   }
 
   /**
diff --git a/payment/lib/Drupal/payment/Plugin/Core/entity/PaymentMethod.php b/payment/lib/Drupal/payment/Plugin/Core/entity/PaymentMethod.php
index c406d5a..c70f963 100644
--- a/payment/lib/Drupal/payment/Plugin/Core/entity/PaymentMethod.php
+++ b/payment/lib/Drupal/payment/Plugin/Core/entity/PaymentMethod.php
@@ -77,6 +77,7 @@ class PaymentMethod extends ConfigEntityBase implements PaymentMethodInterface {
     $properties['label'] = $this->label();
     $properties['ownerId'] = $this->getOwnerId();
     $properties['pluginConfiguration'] = $this->getPlugin()->getConfiguration();
+    unset($properties['pluginConfiguration']['paymentMethod']);
     $properties['pluginId'] = $this->getPlugin()->getPluginId();
 
     return $properties;
@@ -149,15 +150,15 @@ class PaymentMethod extends ConfigEntityBase implements PaymentMethodInterface {
   /**
    * {@inheritdoc}
    */
-  public function validatePayment(PaymentInterface $payment) {
-    return $this->getPlugin()->validatePayment($payment);
+  function paymentOperationAccess(PaymentInterface $payment, $operation) {
+    return $this->getPlugin()->paymentOperationAccess($payment, $operation);
   }
 
   /**
    * {@inheritdoc}
    */
-  public function executePayment(PaymentInterface $payment) {
-    return $this->getPlugin()->executePayment($payment);
+  function executePaymentOperation(PaymentInterface $payment, $operation) {
+    return $this->getPlugin()->executePaymentOperation($payment, $operation);
   }
 
   /**
diff --git a/payment/lib/Drupal/payment/Plugin/Core/entity/PaymentMethodStorageController.php b/payment/lib/Drupal/payment/Plugin/Core/entity/PaymentMethodStorageController.php
index f2575bf..5725b90 100644
--- a/payment/lib/Drupal/payment/Plugin/Core/entity/PaymentMethodStorageController.php
+++ b/payment/lib/Drupal/payment/Plugin/Core/entity/PaymentMethodStorageController.php
@@ -23,7 +23,9 @@ class PaymentMethodStorageController extends ConfigStorageController {
     $payment_methods = parent::buildQuery($ids, $revision_id);
     $manager = \Drupal::service('plugin.manager.payment.payment_method');
     foreach ($payment_methods as $payment_method) {
-      $payment_method->setPlugin($manager->createInstance($payment_method->pluginId), $payment_method->pluginConfiguration);
+      $configuration = $payment_method->pluginConfiguration;
+      $configuration['paymentMethod'] = $payment_method;
+      $payment_method->setPlugin($manager->createInstance($payment_method->pluginId), $configuration);
       unset($payment_method->pluginId);
       unset($payment_method->pluginConfiguration);
       $payment_method->setOwnerId((int) $payment_method->getOwnerId());
diff --git a/payment/lib/Drupal/payment/Plugin/payment/method/Base.php b/payment/lib/Drupal/payment/Plugin/payment/method/Base.php
index 99b00b2..dee55ec 100644
--- a/payment/lib/Drupal/payment/Plugin/payment/method/Base.php
+++ b/payment/lib/Drupal/payment/Plugin/payment/method/Base.php
@@ -6,7 +6,9 @@
 
 namespace Drupal\payment\Plugin\payment\method;
 
+use Doctrine\Tests\Common\Annotations\False;
 use Drupal\Component\Plugin\PluginBase;
+use Drupal\Core\Extension\ModuleHandler;
 use Drupal\payment\Plugin\payment\method\PaymentMethodInterface;
 use Drupal\payment\Plugin\Core\entity\PaymentInterface;
 
@@ -16,19 +18,13 @@ use Drupal\payment\Plugin\Core\entity\PaymentInterface;
 abstract class Base extends PluginBase implements PaymentMethodInterface {
 
   /**
-   * The payment method this plugin is for.
-   *
-   * @var \Drupal\payment\Plugin\Core\entity\PaymentMethodInterface
-   */
-  protected $paymentMethod;
-
-  /**
    * {@inheritdoc}
    */
   public function __construct(array $configuration, $plugin_id, array $plugin_definition) {
     $configuration += array(
       'messageText' => '',
       'messageTextFormat' => '',
+      'paymentMethod' => NULL,
     );
     parent::__construct($configuration, $plugin_id, $plugin_definition);
   }
@@ -41,6 +37,13 @@ abstract class Base extends PluginBase implements PaymentMethodInterface {
   }
 
   /**
+   * {@inheritdoc}
+   */
+  public function getPaymentMethod() {
+    return $this->configuration['paymentMethod'];
+  }
+
+  /**
    * Sets payer message text.
    *
    * @param string $text
@@ -125,56 +128,60 @@ abstract class Base extends PluginBase implements PaymentMethodInterface {
   /**
    * {@inheritdoc}
    */
-  public function validatePayment(PaymentInterface $payment) {
-    // Confirm the payment method is enabled, and thus available in general.
+  function paymentOperationAccess(PaymentInterface $payment, $operation) {
     if (!$this->getPaymentMethod()->status()) {
-      throw new PaymentValidationPaymentMethodDisabledException(t('The payment method is disabled.'));
+      return FALSE;
     }
-
-    if (!$payment->currency_code) {
-      throw new PaymentValidationMissingCurrencyException(t('The payment has no currency set.'));
+    if (!$this->paymentOperationAccessCurrency($payment, $operation)) {
+      return FALSE;
     }
-
-    $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.'));
+    if (!$this->paymentOperationAccessEvent($payment, $operation)) {
+      return FALSE;
     }
+    return TRUE;
+  }
 
-    // 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.'));
+  /**
+   * Checks a payment's currency against this plugin.
+   */
+  protected function paymentOperationAccessCurrency(PaymentInterface $payment, $operation) {
+    if (!$payment->getCurrencyCode()) {
+      return FALSE;
     }
-
-    // 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.'));
+    $currencies = $this->currencies();
+    // Confirm the payment's currency is supported.
+    $currencies = $this->currencies();
+    if (!empty($currencies) && !isset($currencies[$payment->getCurrencyCode()])) {
+      return FALSE;
     }
-
     // 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),
-      )));
+    if (isset($currencies[$payment->getCurrencyCode()]['minimum']) && $payment->getAmount() < $currencies[$payment->getCurrencyCode()]['minimum']) {
+      return FALSE;
     }
-
     // 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),
-      )));
+    if (isset($currencies[$payment->getCurrencyCode()]['maximum']) && $payment->getAmount() > $currencies[$payment->getCurrencyCode()]['maximum']) {
+      return FALSE;
     }
+  }
 
-    // Invoke events.
-    module_invoke_all('payment_validate', $payment, $this->getPaymentMethod());
-    if (module_exists('rules')) {
-      rules_invoke_event('payment_validate', $payment, $this->getPaymentMethod());
+  /**
+   * Invokes events for self::paymentOperationAccess().
+   *
+   * @param \Drupal\payment\Plugin\Core\entity\PaymentInterface $payment
+   * @param string $operation
+   *
+   * @return bool
+   */
+  protected function paymentOperationAccessEvent(PaymentInterface $payment, $operation) {
+    $handler = \Drupal::moduleHandler();
+    foreach ($handler->getImplementations('payment_operation_access') as $module) {
+      $module_access = $handler->invoke($module, 'payment_operation_access', $payment, $this->getPaymentMethod(), $operation);
+      if ($module_access === FALSE) {
+        return FALSE;
+      }
     }
+    // @todo invoke Rules event.
+
+    return TRUE;
   }
 }
diff --git a/payment/lib/Drupal/payment/Plugin/payment/method/Basic.php b/payment/lib/Drupal/payment/Plugin/payment/method/Basic.php
index d4d340d..fe2b683 100644
--- a/payment/lib/Drupal/payment/Plugin/payment/method/Basic.php
+++ b/payment/lib/Drupal/payment/Plugin/payment/method/Basic.php
@@ -17,7 +17,13 @@ use Drupal\payment\Plugin\Core\entity\PaymentInterface;
  *   description = "A 'dumb' payment method type that always successfully executes payments, but never actually transfers money. It can be useful for <em>collect on delivery</em>, for instance.",
  *   id = "payment_basic",
  *   label = @Translation("Basic"),
- *   module = "payment"
+ *   module = "payment",
+ *   operations = {
+ *     "execute" = {
+ *       "interrupts_execution" = "false",
+ *       "label" = @Translation("Execute")
+ *     }
+ *   }
  * )
  */
 class Basic extends Base {
@@ -64,13 +70,6 @@ class Basic extends Base {
   }
 
   /**
-   * {@inheritdoc}.
-   */
-  public function executePayment(PaymentInterface $payment) {
-    $payment->setStatus(\Drupal::service('plugin.manager.payment.status')->createInstance($this->getStatus()));
-  }
-
-  /**
    * {@inheritdoc}
    */
   public function paymentMethodFormElements(array $form, array &$form_state) {
@@ -87,11 +86,38 @@ class Basic extends Base {
     return $elements;
   }
 
-    /**
-     * Implements form validate callback for self::paymentMethodFormElements().
-     */
-    public function paymentMethodFormElementsValidateStatus(array $element, array &$form_state, array $form) {
-      $values = drupal_array_get_nested_value($form_state['values'], $element['#parents']);
-      $this->setStatus($values['status']);
+  /**
+   * Implements form validate callback for self::paymentMethodFormElements().
+   */
+  public function paymentMethodFormElementsValidateStatus(array $element, array &$form_state, array $form) {
+    $values = drupal_array_get_nested_value($form_state['values'], $element['#parents']);
+    $this->setStatus($values['status']);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  function paymentOperationAccess(PaymentInterface $payment, $operation) {
+    // This plugin only supports the execute operation.
+    return $operation == 'execute' && parent::paymentOperationAccess($payment, $operation);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function paymentOperationAccessCurrency(PaymentInterface $payment, $operation) {
+    // This plugin supports any currency.
+    return TRUE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  function executePaymentOperation(PaymentInterface $payment, $operation) {
+    if ($this->paymentOperationAccess($payment, $operation)) {
+      if ($operation == 'execute') {
+        $payment->setStatus(\Drupal::service('plugin.manager.payment.status')->createInstance($this->getStatus()));
+      }
     }
+  }
 }
diff --git a/payment/lib/Drupal/payment/Plugin/payment/method/Manager.php b/payment/lib/Drupal/payment/Plugin/payment/method/Manager.php
index 0159ed4..90b99d2 100644
--- a/payment/lib/Drupal/payment/Plugin/payment/method/Manager.php
+++ b/payment/lib/Drupal/payment/Plugin/payment/method/Manager.php
@@ -7,33 +7,39 @@
 namespace Drupal\payment\Plugin\payment\method;
 
 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;
+use Drupal\Component\Utility\String;
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Language\LanguageManager;
+use Drupal\Core\Plugin\DefaultPluginManager;
 
 /**
  * Manages discovery and instantiation of payment method controller plugins.
  *
  * @see \Drupal\payment\Plugin\payment\method\PaymentMethodInterface
  */
-class Manager extends PluginManagerBase {
+class Manager extends DefaultPluginManager {
 
   /**
    * Constructor.
    *
-   * @param array $namespaces
-   *   An array of paths keyed by their corresponding namespaces.
+   * @param \Traversable $namespaces
+   *   An object that implements \Traversable which contains the root paths
+   *   keyed by the corresponding namespace to look for plugin implementations.
+   * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
+   *   Cache backend instance to use.
+   * @param \Drupal\Core\Language\LanguageManager $language_manager
+   *   The language manager.
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
+   *   The module handler to invoke the alter hook with.
    */
-  public function __construct(\Traversable $namespaces) {
+  public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, LanguageManager $language_manager, ModuleHandlerInterface $module_handler) {
     $annotation_namespaces = array(
       'Drupal\payment\Annotations' => drupal_get_path('module', 'payment') . '/lib',
     );
-    $this->discovery = new AnnotatedClassDiscovery('payment/method', $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);
+    parent::__construct('payment\method', $namespaces, $annotation_namespaces, 'Drupal\payment\Annotations\PaymentMethod');
+    $this->alterInfo($module_handler, 'payment_method');
+    $this->setCacheBackend($cache_backend, $language_manager, 'payment_method');
   }
 
   /**
@@ -48,4 +54,23 @@ class Manager extends PluginManagerBase {
       return parent::createInstance('payment_unavailable', $configuration);
     }
   }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function processDefinition(&$definition, $plugin_id) {
+    parent::processDefinition($definition, $plugin_id);
+    // Merge in default operation values.
+    foreach ($definition['operations'] as $operation => &$operation_definition) {
+      if (empty($operation_definition['label'])) {
+        throw new \InvalidArgumentException(String::format('Plugin !plugin_id does not define a label for operation !operation.', array(
+          '!operation' => $operation,
+          '!plugin_id' => $plugin_id,
+        )));
+      }
+      $operation_definition += array(
+        'interrupts_execution' => TRUE,
+      );
+    }
+  }
 }
diff --git a/payment/lib/Drupal/payment/Plugin/payment/method/PaymentMethodInterface.php b/payment/lib/Drupal/payment/Plugin/payment/method/PaymentMethodInterface.php
index 71174ea..51919c3 100644
--- a/payment/lib/Drupal/payment/Plugin/payment/method/PaymentMethodInterface.php
+++ b/payment/lib/Drupal/payment/Plugin/payment/method/PaymentMethodInterface.php
@@ -26,6 +26,13 @@ interface PaymentMethodInterface extends PaymentProcessingInterface, PluginInspe
   public function getConfiguration();
 
   /**
+   * Gets the payment method the plugin instance is for.
+   *
+   * @return \Drupal\payment\Plugin\Core\entity\PaymentMethodInterface
+   */
+  public function getPaymentMethod();
+
+  /**
    * Returns the form elements to configure payment methods.
    *
    * $form_state['payment_method'] contains the payment method that is added or
diff --git a/payment/lib/Drupal/payment/Plugin/payment/method/Unavailable.php b/payment/lib/Drupal/payment/Plugin/payment/method/Unavailable.php
index 430e025..b15eec3 100644
--- a/payment/lib/Drupal/payment/Plugin/payment/method/Unavailable.php
+++ b/payment/lib/Drupal/payment/Plugin/payment/method/Unavailable.php
@@ -28,6 +28,23 @@ use Drupal\payment\Plugin\payment\method\PaymentMethodInterface;
 class Unavailable extends PluginBase implements PaymentMethodInterface {
 
   /**
+   * {@inheritdoc}
+   */
+  public function __construct(array $configuration, $plugin_id, array $plugin_definition) {
+    $configuration += array(
+      'paymentMethod' => NULL,
+    );
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPaymentMethod() {
+    return $this->configuration['paymentMethod'];
+  }
+
+  /**
    * {@inheritdoc}.
    */
   public function currencies() {
@@ -35,17 +52,16 @@ class Unavailable extends PluginBase implements PaymentMethodInterface {
   }
 
   /**
-   * {@inheritdoc}.
+   * {@inheritdoc}
    */
-  public function executePayment(PaymentInterface $payment) {
-    $payment->setStatus(\Drupal::service('plugin.manager.payment.status')->createInstance('payment_unknown'));
+  function paymentOperationAccess(PaymentInterface $payment, $operation) {
+    return FALSE;
   }
 
   /**
-   * {@inheritdoc}.
+   * {@inheritdoc}
    */
-  public function validatePayment(PaymentInterface $payment) {
-    throw new PaymentValidationException(t('This payment method plugin is unavailable.'));
+  function executePaymentOperation(PaymentInterface $payment, $operation) {
   }
 
   /**
diff --git a/payment/lib/Drupal/payment/Tests/Plugin/Core/entity/PaymentMethodTest.php b/payment/lib/Drupal/payment/Tests/Plugin/Core/entity/PaymentMethodTest.php
index 8422c2e..fa20cd7 100644
--- a/payment/lib/Drupal/payment/Tests/Plugin/Core/entity/PaymentMethodTest.php
+++ b/payment/lib/Drupal/payment/Tests/Plugin/Core/entity/PaymentMethodTest.php
@@ -87,20 +87,4 @@ class PaymentMethodTest extends DrupalUnitTestBase {
     $this->paymentMethod->setPlugin($this->manager->createInstance('payment_unavailable'));
     $this->assertTrue(is_array($this->paymentMethod->currencies()));
   }
-
-  /**
-   * Tests currencies().
-   */
-  function testValidatePayment() {
-    // @todo Finish this test.
-    $this->assertTrue(FALSE);
-  }
-
-  /**
-   * Tests currencies().
-   */
-  function testExecutePayment() {
-    // @todo Finish this test.
-    $this->assertTrue(FALSE);
-  }
 }
diff --git a/payment/lib/Drupal/payment/Tests/Plugin/Core/entity/PaymentTest.php b/payment/lib/Drupal/payment/Tests/Plugin/Core/entity/PaymentTest.php
index e5bcf23..9108613 100644
--- a/payment/lib/Drupal/payment/Tests/Plugin/Core/entity/PaymentTest.php
+++ b/payment/lib/Drupal/payment/Tests/Plugin/Core/entity/PaymentTest.php
@@ -186,14 +186,6 @@ class PaymentTest extends DrupalUnitTestBase {
   }
 
   /**
-   * Tests validate().
-   */
-  function testValidate() {
-    // @todo Finish this test.
-    $this->assertTrue(FALSE);
-  }
-
-  /**
    * Tests execute().
    */
   function testExecute() {
diff --git a/payment/lib/Drupal/payment/Tests/Plugin/payment/method/BasicTest.php b/payment/lib/Drupal/payment/Tests/Plugin/payment/method/BasicTest.php
index 4c7ab1b..d9b7b27 100644
--- a/payment/lib/Drupal/payment/Tests/Plugin/payment/method/BasicTest.php
+++ b/payment/lib/Drupal/payment/Tests/Plugin/payment/method/BasicTest.php
@@ -32,7 +32,10 @@ class BasicTest extends DrupalUnitTestBase {
    */
   function setUp() {
     parent::setUp();
-    $this->method = \Drupal::service('plugin.manager.payment.payment_method')->createInstance('payment_basic');
+    $this->methodEntity= entity_create('payment_method', array());
+    $this->method = \Drupal::service('plugin.manager.payment.payment_method')->createInstance('payment_basic', array(
+      'paymentMethod' => $this->methodEntity,
+    ));
   }
 
   /**
@@ -45,6 +48,7 @@ class BasicTest extends DrupalUnitTestBase {
     $this->assertEqual($this->method->getConfiguration(), array(
       'messageText' => 'foo',
       'messageTextFormat' => 'bar',
+      'paymentMethod' => $this->methodEntity,
       'status' => 'baz',
     ));
   }
@@ -84,4 +88,24 @@ class BasicTest extends DrupalUnitTestBase {
     $form_state = array();
     $this->assertTrue(is_array($this->method->paymentMethodFormElements($form, $form_state)));
   }
+
+  /**
+   * Tests paymentOperationAccess().
+   */
+  function testPaymentOperationAccess() {
+    $payment = entity_create('payment', array());
+    $this->assertTrue($this->method->paymentOperationAccess($payment, 'execute'));
+    $this->assertFalse($this->method->paymentOperationAccess($payment, $this->randomName()));
+  }
+
+  /**
+   * Tests executePaymentOperation().
+   */
+  function testExecutePaymentOperation() {
+    $plugin_id = 'payment_unknown';
+    $payment = entity_create('payment', array());
+    $this->method->setStatus($plugin_id);
+    $this->method->executePaymentOperation($payment, 'execute');
+    $this->assertTrue($payment->getStatus()->getPluginId() == $plugin_id);
+  }
 }
diff --git a/payment/payment.api.php b/payment/payment.api.php
index 3b81634..00c0780 100644
--- a/payment/payment.api.php
+++ b/payment/payment.api.php
@@ -6,6 +6,7 @@
  */
 
 use Drupal\payment\Plugin\Core\Entity\PaymentInterface;
+use Drupal\payment\Plugin\Core\Entity\PaymentMethodInterface;
 use Drupal\payment\Plugin\payment\status\PaymentStatusInterface;
 
 /**
@@ -94,21 +95,18 @@ function hook_payment_pre_finish(PaymentInterface $payment) {
 }
 
 /**
- * Validate a payment against a payment method.
- *
- * This hook may be called multiple times for the exact same payment, but for
- * different payment methods, for example when looking for payment methods that
- * are capable of processing a payment.
+ * Checks access for performing a payment method operation on a payment.
  *
  * @param \Drupal\payment\Plugin\Core\Entity\PaymentInterface $payment
- *   $payment->getMethod() contains the method currently configured, but NOT the
+ *   $payment->getPaymentMethod() contains the method currently configured, but NOT the
  *   method that $payment should be tested against, which is $payment_method.
- * @param PaymentMethod $payment_method
+ * @param \Drupal\payment\Plugin\Core\entity\PaymentMethodInterface $payment_method
+ * @param string $operation
  *
  * @return boolean
- *   Whether the payment and/or the payment method are valid.
+ *   Whether the operation can be performed on the payment.
  */
-function hook_payment_validate(PaymentInterface $payment, PaymentMethod $payment_method) {}
+function hook_payment_operation_access(PaymentInterface $payment, PaymentMethodInterface $payment_method, $operation) {}
 
 /**
  * Alter the payment form.
diff --git a/payment/payment.module b/payment/payment.module
index 86d5493..c36178f 100644
--- a/payment/payment.module
+++ b/payment/payment.module
@@ -14,11 +14,6 @@ module_load_include('inc', 'payment', 'payment.ui');
 define('PAYMENT_LINE_ITEM_TOKEN_PREFIX', 'line_item-');
 
 /**
- * The absolute minimum payment amount.
- */
-define('PAYMENT_MINIMUM_AMOUNT', 0);
-
-/**
  * Implements hook_init().
  */
 function payment_init() {
diff --git a/payment/payment.services.yml b/payment/payment.services.yml
index 4673227..4d22a22 100644
--- a/payment/payment.services.yml
+++ b/payment/payment.services.yml
@@ -4,7 +4,7 @@ services:
     arguments: ['@container.namespaces']
   plugin.manager.payment.payment_method:
     class: Drupal\payment\Plugin\payment\method\Manager
-    arguments: ['@container.namespaces']
+    arguments: ['@container.namespaces', '@cache.cache', '@language_manager', '@module_handler']
   plugin.manager.payment.status:
     class: Drupal\payment\Plugin\payment\status\Manager
     arguments: ['@container.namespaces']
