diff --git a/modules/payment/src/Form/PaymentMethodAddForm.php b/modules/payment/src/Form/PaymentMethodAddForm.php
index ef5182e..f35262b 100644
--- a/modules/payment/src/Form/PaymentMethodAddForm.php
+++ b/modules/payment/src/Form/PaymentMethodAddForm.php
@@ -82,6 +82,9 @@ class PaymentMethodAddForm extends FormBase implements ContainerInjectionInterfa
       $form = $this->buildPaymentMethodTypeForm($form, $form_state);
     }
     elseif ($step == 'payment_method') {
+      if ($js_library = $payment_gateway->getPlugin()->getJsLibrary()) {
+        $form['#attached']['library'][] = $js_library;
+      }
       $form = $this->buildPaymentMethodForm($form, $form_state);
     }
 
diff --git a/modules/payment/src/Form/PaymentMethodEditForm.php b/modules/payment/src/Form/PaymentMethodEditForm.php
index 72d2c9e..840c546 100644
--- a/modules/payment/src/Form/PaymentMethodEditForm.php
+++ b/modules/payment/src/Form/PaymentMethodEditForm.php
@@ -19,17 +19,19 @@ class PaymentMethodEditForm extends EntityForm {
       '#operation' => 'edit-payment-method',
       '#default_value' => $this->entity,
     ];
-
+    /** @var \Drupal\commerce_payment\Entity\PaymentMethod $payment_method */
+    $payment_method = $this->entity;
+    if ($js_library = $payment_method->getPaymentGateway()->getPlugin()->getJsLibrary()) {
+      $form['#attached']['library'][] = $js_library;
+    }
     return $form;
   }
 
   /**
    * {@inheritdoc}
    */
-  public function validateForm(array &$form, FormStateInterface $form_state) {
-    parent::validateForm($form, $form_state);
-
-    $this->entity = $form_state->getValue('payment_method');
+  public function buildEntity(array $form, FormStateInterface $form_state) {
+    return $form_state->getValue('payment_method');
   }
 
   /**
@@ -41,7 +43,7 @@ class PaymentMethodEditForm extends EntityForm {
       '%label' => $this->entity->label(),
       '@entity-type' => $this->entity->getEntityType()->getLowercaseLabel(),
     ]));
-    $form_state->setRedirect($this->entity->toUrl('collection'));
+    $form_state->setRedirectUrl($this->entity->toUrl('collection'));
   }
 
 }
diff --git a/modules/payment/src/Plugin/Commerce/PaymentGateway/SupportsUpdatingStoredPaymentMethodsInterface.php b/modules/payment/src/Plugin/Commerce/PaymentGateway/SupportsUpdatingStoredPaymentMethodsInterface.php
index 1f3b2ca..03fe926 100644
--- a/modules/payment/src/Plugin/Commerce/PaymentGateway/SupportsUpdatingStoredPaymentMethodsInterface.php
+++ b/modules/payment/src/Plugin/Commerce/PaymentGateway/SupportsUpdatingStoredPaymentMethodsInterface.php
@@ -10,7 +10,7 @@ use Drupal\commerce_payment\Entity\PaymentMethodInterface;
 interface SupportsUpdatingStoredPaymentMethodsInterface {
 
   /**
-   * Updates the given payment.
+   * Updates the given payment method.
    *
    * @param \Drupal\commerce_payment\Entity\PaymentMethodInterface $payment_method
    *   The payment method.
diff --git a/modules/payment/src/PluginForm/PaymentMethodAddForm.php b/modules/payment/src/PluginForm/PaymentMethodAddForm.php
index 44c6c69..21cbb5a 100644
--- a/modules/payment/src/PluginForm/PaymentMethodAddForm.php
+++ b/modules/payment/src/PluginForm/PaymentMethodAddForm.php
@@ -52,8 +52,6 @@ class PaymentMethodAddForm extends PaymentGatewayFormBase {
       $form['payment_details'] = $this->buildPayPalForm($form['payment_details'], $form_state);
     }
 
-    /** @var \Drupal\commerce_payment\Entity\PaymentMethodInterface $payment_method */
-    $payment_method = $this->entity;
     /** @var \Drupal\profile\Entity\ProfileInterface $billing_profile */
     $billing_profile = $payment_method->getBillingProfile();
     if (!$billing_profile) {
diff --git a/modules/payment/src/PluginForm/PaymentMethodEditForm.php b/modules/payment/src/PluginForm/PaymentMethodEditForm.php
index 11dc962..52c63a2 100644
--- a/modules/payment/src/PluginForm/PaymentMethodEditForm.php
+++ b/modules/payment/src/PluginForm/PaymentMethodEditForm.php
@@ -2,9 +2,58 @@
 
 namespace Drupal\commerce_payment\PluginForm;
 
-/**
- * @todo
- */
-class PaymentMethodEditForm extends PaymentMethodAddForm {
+use Drupal\commerce_payment\Exception\DeclineException;
+use Drupal\commerce_payment\Exception\PaymentGatewayException;
+use Drupal\Core\Form\FormStateInterface;
+
+class PaymentMethodEditForm extends PaymentGatewayFormBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
+    /** @var \Drupal\commerce_payment\Entity\PaymentMethodInterface $payment_method */
+    $payment_method = $this->entity;
+    $billing_profile = $payment_method->getBillingProfile();
+    /** @var \Drupal\commerce_store\StoreStorageInterface $store_storage */
+    $store_storage = \Drupal::entityTypeManager()->getStorage('commerce_store');
+    $store = $store_storage->loadDefault();
+
+    $form['#tree'] = TRUE;
+    $form['billing_information'] = [
+      '#parents' => array_merge($form['#parents'], ['billing_information']),
+      '#type' => 'commerce_profile_select',
+      '#default_value' => $billing_profile,
+      '#default_country' => $store ? $store->getAddress()->getCountryCode() : NULL,
+      '#available_countries' => $store ? $store->getBillingCountries() : [],
+    ];
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
+    /** @var \Drupal\commerce_payment\Entity\PaymentMethodInterface $payment_method */
+    $payment_method = $this->entity;
+    $payment_method->setBillingProfile($form['billing_information']['#profile']);
+
+    /** @var \Drupal\commerce_payment\Plugin\Commerce\PaymentGateway\SupportsUpdatingStoredPaymentMethodsInterface $payment_gateway_plugin */
+    $payment_gateway_plugin = $this->plugin;
+    // The payment method form is customer facing. For security reasons
+    // the returned errors need to be more generic.
+    try {
+      $payment_gateway_plugin->updatePaymentMethod($payment_method);
+    }
+    catch (DeclineException $e) {
+      \Drupal::logger('commerce_payment')->warning($e->getMessage());
+      throw new DeclineException(t('We encountered an error processing your payment method. Please verify your details and try again.'));
+    }
+    catch (PaymentGatewayException $e) {
+      \Drupal::logger('commerce_payment')->error($e->getMessage());
+      throw new PaymentGatewayException(t('We encountered an unexpected error processing your payment method. Please try again later.'));
+    }
+  }
 
 }
diff --git a/modules/payment/tests/src/Functional/PaymentMethodTest.php b/modules/payment/tests/src/Functional/PaymentMethodTest.php
index c859ef3..d885ca6 100644
--- a/modules/payment/tests/src/Functional/PaymentMethodTest.php
+++ b/modules/payment/tests/src/Functional/PaymentMethodTest.php
@@ -75,9 +75,9 @@ class PaymentMethodTest extends CommerceBrowserTestBase {
   }
 
   /**
-   * Tests creating a payment method.
+   * Tests creating and updating a payment method.
    */
-  public function testPaymentMethodCreation() {
+  public function testPaymentMethodCreationAndUpdate() {
     /** @var \Drupal\commerce_payment_example\Plugin\Commerce\PaymentGateway\OnsiteInterface $plugin */
     $this->drupalGet($this->collectionUrl);
     $this->getSession()->getPage()->clickLink('Add payment method');
@@ -100,7 +100,30 @@ class PaymentMethodTest extends CommerceBrowserTestBase {
     $this->assertSession()->pageTextContains('Visa ending in 1111 saved to your payment methods.');
 
     $payment_method = PaymentMethod::load(1);
+    $billing_profile = $payment_method->getBillingProfile();
     $this->assertEquals($this->user->id(), $payment_method->getOwnerId());
+    $this->assertEquals('NY', $billing_profile->get('address')->first()->getAdministrativeArea());
+    $this->assertEquals(1, $payment_method->getBillingProfile()->id());
+
+    $this->drupalGet($this->collectionUrl . '/' . $payment_method->id() . '/edit');
+    $form_values = [
+      'payment_method[billing_information][address][0][address][given_name]' => 'Johnny',
+      'payment_method[billing_information][address][0][address][family_name]' => 'Appleseed',
+      'payment_method[billing_information][address][0][address][address_line1]' => '123 New York Drive',
+      'payment_method[billing_information][address][0][address][locality]' => 'Greenville',
+      'payment_method[billing_information][address][0][address][administrative_area]' => 'SC',
+      'payment_method[billing_information][address][0][address][postal_code]' => '29615',
+    ];
+    $this->submitForm($form_values, 'Save');
+    $this->assertSession()->addressEquals($this->collectionUrl);
+
+    \Drupal::entityTypeManager()->getStorage('commerce_payment_method')->resetCache([1]);
+    \Drupal::entityTypeManager()->getStorage('profile')->resetCache([1]);
+    $payment_method = PaymentMethod::load(1);
+    $billing_profile = $payment_method->getBillingProfile();
+    $this->assertEquals($this->user->id(), $payment_method->getOwnerId());
+    $this->assertEquals('SC', $billing_profile->get('address')->first()->getAdministrativeArea());
+    $this->assertEquals(1, $payment_method->getBillingProfile()->id());
   }
 
   /**
diff --git a/modules/payment_example/src/Plugin/Commerce/PaymentGateway/Onsite.php b/modules/payment_example/src/Plugin/Commerce/PaymentGateway/Onsite.php
index 6b258dd..b9eaaca 100644
--- a/modules/payment_example/src/Plugin/Commerce/PaymentGateway/Onsite.php
+++ b/modules/payment_example/src/Plugin/Commerce/PaymentGateway/Onsite.php
@@ -23,6 +23,7 @@ use Drupal\Core\Form\FormStateInterface;
  *   display_label = "Example",
  *   forms = {
  *     "add-payment-method" = "Drupal\commerce_payment_example\PluginForm\Onsite\PaymentMethodAddForm",
+ *     "edit-payment-method" = "Drupal\commerce_payment\PluginForm\PaymentMethodEditForm",
  *   },
  *   payment_method_types = {"credit_card"},
  *   credit_card_types = {
@@ -223,4 +224,15 @@ class Onsite extends OnsitePaymentGatewayBase implements OnsiteInterface {
     $payment_method->delete();
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function updatePaymentMethod(PaymentMethodInterface $payment_method) {
+    // The default payment method edit form only supports updating billing info.
+    $billing_profile = $payment_method->getBillingProfile();
+
+    // Perform the update request here, throw an exception if it fails.
+    // See \Drupal\commerce_payment\Exception for the available exceptions.
+  }
+
 }
diff --git a/modules/payment_example/src/Plugin/Commerce/PaymentGateway/OnsiteInterface.php b/modules/payment_example/src/Plugin/Commerce/PaymentGateway/OnsiteInterface.php
index f9e115c..e5f5e39 100644
--- a/modules/payment_example/src/Plugin/Commerce/PaymentGateway/OnsiteInterface.php
+++ b/modules/payment_example/src/Plugin/Commerce/PaymentGateway/OnsiteInterface.php
@@ -5,6 +5,7 @@ namespace Drupal\commerce_payment_example\Plugin\Commerce\PaymentGateway;
 use Drupal\commerce_payment\Plugin\Commerce\PaymentGateway\OnsitePaymentGatewayInterface;
 use Drupal\commerce_payment\Plugin\Commerce\PaymentGateway\SupportsAuthorizationsInterface;
 use Drupal\commerce_payment\Plugin\Commerce\PaymentGateway\SupportsRefundsInterface;
+use Drupal\commerce_payment\Plugin\Commerce\PaymentGateway\SupportsUpdatingStoredPaymentMethodsInterface;
 
 /**
  * Provides the interface for the example_onsite payment gateway.
@@ -14,6 +15,6 @@ use Drupal\commerce_payment\Plugin\Commerce\PaymentGateway\SupportsRefundsInterf
  * the gateway has. The gateway plugin is free to expose additional methods,
  * which would be defined below.
  */
-interface OnsiteInterface extends OnsitePaymentGatewayInterface, SupportsAuthorizationsInterface, SupportsRefundsInterface {
+interface OnsiteInterface extends OnsitePaymentGatewayInterface, SupportsAuthorizationsInterface, SupportsRefundsInterface, SupportsUpdatingStoredPaymentMethodsInterface {
 
 }
