diff --git a/payment/uc_2checkout/config/install/uc_2checkout.settings.yml b/payment/uc_2checkout/config/install/uc_2checkout.settings.yml
index be733e4..bce396f 100644
--- a/payment/uc_2checkout/config/install/uc_2checkout.settings.yml
+++ b/payment/uc_2checkout/config/install/uc_2checkout.settings.yml
@@ -3,5 +3,6 @@ language: 'en'
 sid: ''
 secret_word: 'tango'
 demo: TRUE
-checkout_type: 'multi'
+checkout_type: 'dynamic'
 check: FALSE
+server_url: 'https://www.2checkout.com/checkout/purchase'
diff --git a/payment/uc_2checkout/config/schema/uc_2checkout.schema.yml b/payment/uc_2checkout/config/schema/uc_2checkout.schema.yml
new file mode 100644
index 0000000..d33c261
--- /dev/null
+++ b/payment/uc_2checkout/config/schema/uc_2checkout.schema.yml
@@ -0,0 +1,36 @@
+# Schema for the configuration files of the uc_2checkout module.
+
+uc_2checkout.settings:
+  type: config_object
+  label: '2Checkout settings'
+  mapping:
+    check:
+      type: boolean
+      label: ''
+    checkout_type:
+      type: string
+      label: 'Use direct or dynamic checkout'
+    currency_code:
+      type: string
+      label: 'Currency to use for transaction'
+    demo:
+      type: boolean
+      label: 'Flag to indicate demo mode for gateway - no actual money will be transfered if TRUE'
+    language:
+      type: string
+      label: 'Language code'
+    method_title:
+      type: label
+      label: 'Default title for this payment method'
+    notification_url:
+      type: uri
+      label: 'Notification callback URL'
+    secret_word:
+      type: string
+      label: ''
+    server_url:
+      type: uri
+      label: '2Checkout server for POSTing transaction'
+    sid:
+      type: string
+      label: ''
diff --git a/payment/uc_2checkout/src/Controller/CheckoutController.php b/payment/uc_2checkout/src/Controller/CheckoutController.php
deleted file mode 100644
index 08258e0..0000000
--- a/payment/uc_2checkout/src/Controller/CheckoutController.php
+++ /dev/null
@@ -1,89 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\uc_2checkout\Controller\CheckoutController.
- */
-
-namespace Drupal\uc_2checkout\Controller;
-
-use Drupal\Component\Utility\SafeMarkup;
-use Drupal\Component\Utility\Unicode;
-use Drupal\Core\Controller\ControllerBase;
-use Drupal\Core\Url;
-use Drupal\uc_order\Entity\Order;
-use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
-
-/**
- * Controller routines for uc_2checkout.
- */
-class CheckoutController extends ControllerBase {
-
-  /**
-   * Finalizes 2checkout transaction.
-   */
-  public function complete($cart_id = 0) {
-    $cart_config = \Drupal::config('uc_cart.settings');
-    $module_config = \Drupal::config('uc_2checkout.settings');
-    $this->logger('2Checkout')->notice('Receiving new order notification for order @order_id.', ['@order_id' => SafeMarkup::checkPlain($_REQUEST['merchant_order_id'])]);
-
-    $order = Order::load($_REQUEST['merchant_order_id']);
-
-    if (!$order || $order->getStateId() != 'in_checkout') {
-      return t('An error has occurred during payment.  Please contact us to ensure your order has submitted.');
-    }
-
-    $key = $_REQUEST['key'];
-    $order_number = $module_config->get('demo') ? 1 : $_REQUEST['order_number'];
-    $valid = md5($module_config->get('secret_word') . $_REQUEST['sid'] . $order_number . $_REQUEST['total']);
-    if (Unicode::strtolower($key) != Unicode::strtolower($valid)) {
-      uc_order_comment_save($order->id(), 0, t('Attempted unverified 2Checkout completion for this order.'), 'admin');
-      throw new AccessDeniedHttpException();
-    }
-
-    if ($_REQUEST['demo'] == 'Y' xor $module_config->get('demo')) {
-      $this->logger('uc_2checkout')->error('The 2checkout payment for order <a href=":order_url">@order_id</a> demo flag was set to %flag, but the module is set to %mode mode.', array(
-        ':order_url' => Url::fromRoute('entity.uc_order.canonical', ['uc_order' => $order->id()])->toString(),
-        '@order_id' => $order->id(),
-        '%flag' => $_REQUEST['demo'] == 'Y' ? 'Y' : 'N',
-        '%mode' => $module_config->get('demo') ? 'Y' : 'N',
-      ));
-
-      if (!$module_config->get('demo')) {
-        throw new AccessDeniedHttpException();
-      }
-    }
-
-    $order->billing_street1 = $_REQUEST['street_address'];
-    $order->billing_street2 = $_REQUEST['street_address2'];
-    $order->billing_city = $_REQUEST['city'];
-    $order->billing_postal_code = $_REQUEST['zip'];
-    $order->billing_phone = $_REQUEST['phone'];
-    $order->billing_zone = $_REQUEST['state'];
-    $order->billing_country = $_REQUEST['country'];
-    $order->save();
-
-    if (Unicode::strtolower($_REQUEST['email']) !== Unicode::strtolower($order->getEmail())) {
-      uc_order_comment_save($order->id(), 0, t('Customer used a different e-mail address during payment: @email', ['@email' => SafeMarkup::checkPlain($_REQUEST['email'])]), 'admin');
-    }
-
-    if ($_REQUEST['credit_card_processed'] == 'Y' && is_numeric($_REQUEST['total'])) {
-      $comment = t('Paid by @type, 2Checkout.com order #@order.', ['@type' => $_REQUEST['pay_method'] == 'CC' ? t('credit card') : t('echeck'), '@order' => SafeMarkup::checkPlain($_REQUEST['order_number'])]);
-      uc_payment_enter($order->id(), '2checkout', $_REQUEST['total'], 0, NULL, $comment);
-    }
-    else {
-      drupal_set_message(t('Your order will be processed as soon as your payment clears at 2Checkout.com.'));
-      uc_order_comment_save($order->id(), 0, t('@type payment is pending approval at 2Checkout.com.', ['@type' => $_REQUEST['pay_method'] == 'CC' ? t('Credit card') : t('eCheck')]), 'admin');
-    }
-
-    // Empty that cart...
-    uc_cart_empty($cart_id);
-
-    // Add a comment to let sales team know this came in through the site.
-    uc_order_comment_save($order->id(), 0, t('Order created through website.'), 'admin');
-
-    $build = uc_cart_complete_sale($order, $cart_config->get('new_customer_login'));
-
-    return $build;
-  }
-}
diff --git a/payment/uc_2checkout/src/Controller/TwoCheckoutController.php b/payment/uc_2checkout/src/Controller/TwoCheckoutController.php
new file mode 100644
index 0000000..2714e3f
--- /dev/null
+++ b/payment/uc_2checkout/src/Controller/TwoCheckoutController.php
@@ -0,0 +1,183 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\uc_2checkout\Controller\TwoCheckoutController.
+ */
+
+namespace Drupal\uc_2checkout\Controller;
+
+use Drupal\Component\Utility\SafeMarkup;
+use Drupal\Component\Utility\Unicode;
+use Drupal\Core\Controller\ControllerBase;
+use Drupal\Core\Url;
+use Drupal\uc_cart\CartManagerInterface;
+use Drupal\uc_order\Entity\Order;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
+
+/**
+ * Controller routines for uc_2checkout.
+ */
+class TwoCheckoutController extends ControllerBase {
+
+  /**
+   * The cart manager.
+   *
+   * @var \Drupal\uc_cart\CartManager
+   */
+  protected $cartManager;
+
+  /**
+   * Constructs a TwoCheckoutController.
+   *
+   * @param \Drupal\uc_cart\CartManagerInterface $cart_manager
+   *   The cart manager.
+   */
+  public function __construct(CartManagerInterface $cart_manager) {
+    $this->cartManager = $cart_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    // @todo: Also need to inject logger
+    return new static(
+      $container->get('uc_cart.manager')
+    );
+  }
+
+  /**
+   * Finalizes 2Checkout transaction.
+   *
+   * @param int $cart_id
+   *   The cart identifier.
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   The request of the page.
+   */
+  public function complete($cart_id = 0, Request $request) {
+    $cart_config = $this->config('uc_cart.settings');
+    $module_config = $this->config('uc_2checkout.settings');
+
+    \Drupal::logger('uc_2checkout')->notice('Receiving new order notification for order @order_id.', ['@order_id' => SafeMarkup::checkPlain($request->request->get('merchant_order_id'))]);
+
+    $order = Order::load($request->request->get('merchant_order_id'));
+
+    if (!$order || $order->getStateId() != 'in_checkout') {
+      return $this->t('An error has occurred during payment.  Please contact us to ensure your order has submitted.');
+    }
+
+    $key = $request->request->get('key');
+    $order_number = $module_config->get('demo') ? 1 : $request->request->get('order_number');
+    $valid = md5($module_config->get('secret_word') . $request->request->get('sid') . $order_number . $request->request->get('total'));
+    if (Unicode::strtolower($key) != Unicode::strtolower($valid)) {
+      uc_order_comment_save($order->id(), 0, $this->t('Attempted unverified 2Checkout completion for this order.'), 'admin');
+      throw new AccessDeniedHttpException();
+    }
+
+    if ($request->request->get('demo') == 'Y' xor $module_config->get('demo')) {
+      \Drupal::logger('uc_2checkout')->error('The 2Checkout payment for order <a href=":order_url">@order_id</a> demo flag was set to %flag, but the module is set to %mode mode.', array(
+        ':order_url' => Url::fromRoute('entity.uc_order.canonical', ['uc_order' => $order->id()])->toString(),
+        '@order_id' => $order->id(),
+        '%flag' => $request->request->get('demo') == 'Y' ? 'Y' : 'N',
+        '%mode' => $module_config->get('demo') ? 'Y' : 'N',
+      ));
+
+      if (!$module_config->get('demo')) {
+        throw new AccessDeniedHttpException();
+      }
+    }
+
+//@todo: Check if this is the right way to save the order
+    $order->billing_street1 = $request->request->get('street_address');
+    $order->billing_street2 = $request->request->get('street_address2');
+    $order->billing_city = $request->request->get('city');
+    $order->billing_postal_code = $request->request->get('zip');
+    $order->billing_phone = $request->request->get('phone');
+    $order->billing_zone = $request->request->get('state');
+    $order->billing_country = $request->request->get('country');
+    $order->save();
+
+    if (Unicode::strtolower($request->request->get('email')) !== Unicode::strtolower($order->getEmail())) {
+      uc_order_comment_save($order->id(), 0, $this->t('Customer used a different e-mail address during payment: @email', ['@email' => SafeMarkup::checkPlain($request->request->get('email'))]), 'admin');
+    }
+
+    if ($request->request->get('credit_card_processes') == 'Y' && is_numeric($request->request->get('total'))) {
+      $comment = $this->t('Paid by @type, 2Checkout.com order #@order.', ['@type' => $request->request->get('pay_method') == 'CC' ? $this->t('credit card') : $this->t('echeck'), '@order' => SafeMarkup::checkPlain($request->request->get('order_number'))]);
+      uc_payment_enter($order->id(), '2Checkout', $request->request->get('total'), 0, NULL, $comment);
+    }
+    else {
+      drupal_set_message($this->t('Your order will be processed as soon as your payment clears at 2Checkout.com.'));
+      uc_order_comment_save($order->id(), 0, $this->t('@type payment is pending approval at 2Checkout.com.', ['@type' => $request->request->get('pay_method') == 'CC' ? $this->t('Credit card') : $this->t('eCheck')]), 'admin');
+    }
+
+    // Empty that cart...
+    $cart = $this->cartManager->get($cart_id);
+    $cart->emptyCart();
+
+    // Add a comment to let sales team know this came in through the site.
+    uc_order_comment_save($order->id(), 0, $this->t('Order created through website.'), 'admin');
+
+    $build = $cart->completeSale($order, $cart_config->get('new_customer_login'));
+
+    return $build;
+  }
+
+  /**
+   * React on INS messages from 2Checkout.
+   *
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   The request of the page.
+   */
+  public function notification(Request $request) {
+    $values = $request->request;
+    \Drupal::logger('uc_2checkout')->notice('Received 2Checkout notification with following data: @data', ['@data' => print_r($values->all(), TRUE)]);
+    $module_config = $this->config('uc_2checkout.settings');
+
+    if ($values->has('message_type') && $values->has('md5_hash') && $values->has('message_id')) {
+      // Validate the hash
+      $secret_word = $module_config->get('secret_word');
+      $sid = $module_config->get('sid');
+      $twocheckout_order_id = $values->get('sale_id');
+      $twocheckout_invoice_id = $values->get('invoice_id');
+      $hash = strtoupper(md5($twocheckout_order_id . $sid . $twocheckout_invoice_id . $secret_word));
+
+      if ($hash != $values->get('md5_hash')) {
+        \Drupal::logger('uc_2checkout')->notice('2Checkout notification #@num had a wrong hash.', ['@num' => $values->get('message_id')]);
+        die('Hash Incorrect');
+      }
+
+      $order_id = $values->get('vendor_order_id');
+      $order = Order::load($order_id);
+      if ($values->get('message_type') == 'FRAUD_STATUS_CHANGED') {
+        switch ($values->get('fraud_status')) {
+// @todo: I think this still needs a lot of work, I don't see anywhere that it
+// validates the INS against an order in the DB then changes order status if the
+// payment was successful, like PayPal IPN does ...
+          case 'pass':
+            break;
+
+          case 'wait':
+            break;
+
+          case 'fail':
+            // @todo uc_order_update_status($order_id, uc_order_state_default('canceled'));
+            $order->setStatusId('canceled')->save();
+            uc_order_comment_save($order_id, 0, $this->t('Order have not passed 2Checkout fraud review.'));
+            die('fraud');
+            break;
+        }
+      }
+      elseif ($values->get('message_type') == 'REFUND_ISSUED') {
+        // @todo uc_order_update_status($order_id, uc_order_state_default('canceled'));
+        $order->setStatusId('canceled')->save();
+        uc_order_comment_save($order_id, 0, $this->t('Order have been refunded through 2Checkout.'));
+        die('refund');
+      }
+    }
+    die('ok');
+  }
+
+}
diff --git a/payment/uc_2checkout/src/Form/TwoCheckoutForm.php b/payment/uc_2checkout/src/Form/TwoCheckoutForm.php
new file mode 100644
index 0000000..60f7e03
--- /dev/null
+++ b/payment/uc_2checkout/src/Form/TwoCheckoutForm.php
@@ -0,0 +1,102 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\uc_2checkout\Form\TwoCheckoutForm.
+ */
+
+namespace Drupal\uc_2checkout\Form;
+
+use Drupal\Component\Utility\SafeMarkup;
+use Drupal\Component\Utility\Unicode;
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Url;
+use Drupal\uc_order\Entity\Order;
+use Drupal\uc_order\OrderInterface;
+
+/**
+ * Form to build the submission to 2Checkout.com.
+ */
+class TwoCheckoutForm extends FormBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'uc_2checkout_form';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state, OrderInterface $order = NULL) {
+    $module_config = $this->config('uc_2checkout.settings');
+    $country = \Drupal::service('country_manager')->getCountry($order->getAddress('billing')->country);
+
+    $data = array(
+      'sid' => $module_config->get('sid'),
+      'mode' => '2CO',
+      'card_holder_name' => Unicode::substr($order->getAddress('billing')->first_name . ' ' . $order->getAddress('billing')->last_name, 0, 128),
+      'street_address' => Unicode::substr($order->getAddress('billing')->street1, 0, 64),
+      'street_address2' => Unicode::substr($order->getAddress('billing')->street2, 0, 64),
+      'city' => Unicode::substr($order->getAddress('billing')->city, 0, 64),
+      'state' => $order->getAddress('billing')->zone,
+      'zip' => Unicode::substr($order->getAddress('billing')->postal_code, 0, 16),
+      'country' => $country ? $country->getAlpha3() : 'USA',
+      'email' => Unicode::substr($order->getEmail(), 0, 64),
+      'phone' => Unicode::substr($order->getAddress('billing')->phone, 0, 16),
+      'purchase_step' => 'payment-method',
+
+      'demo' => $module_config->get('demo') ? 'Y' : 'N',
+      'lang' => $module_config->get('language'),
+      'merchant_order_id' => $order->id(),
+      'pay_method' => 'CC',
+      'x_receipt_link_url' => Url::fromRoute('uc_2checkout.complete', ['cart_id' => \Drupal::service('uc_cart.manager')->get()->getId()], ['absolute' => TRUE])->toString(),
+
+      'total' => uc_currency_format($order->getTotal(), FALSE, FALSE, '.'),
+      'cart_order_id' => $order->id(),
+    );
+
+    if ($currency_code = $module_config->get('currency_code')) {
+      $data['currency_code'] = $currency_code;
+    }
+
+    $i = 0;
+    $order->products = \Drupal::entityTypeManager()->getStorage('uc_order_product')->loadByProperties(['order_id' => $order->id()]);
+    foreach ($order->products as $product) {
+      $i++;
+      $data['li_' . $i . '_type'] = 'product';
+      $data['li_' . $i . '_name'] = $product->title->value; // @todo: HTML escape and limit to 128 chars
+      $data['li_' . $i . '_quantity'] = $product->qty->value;
+      $data['li_' . $i . '_product_id'] = $product->model->value;
+      $data['li_' . $i . '_price'] = uc_currency_format($product->price->value, FALSE, FALSE, '.');
+    }
+
+    if ('direct' == $module_config->get('checkout_type')) {
+      $form['#attached']['library'][] = 'uc_2checkout/2checkout.direct';
+    }
+
+    $form['#action'] = $module_config->get('server_url');
+
+    foreach ($data as $name => $value) {
+      $form[$name] = array('#type' => 'hidden', '#value' => $value);
+    }
+
+    $form['actions'] = array('#type' => 'actions');
+    $form['actions']['submit'] = array(
+      '#type' => 'submit',
+      '#value' => $this->t('Submit order'),
+    );
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    parent::submitForm($form, $form_state);
+  }
+
+}
diff --git a/payment/uc_2checkout/src/Plugin/Ubercart/PaymentMethod/TwoCheckout.php b/payment/uc_2checkout/src/Plugin/Ubercart/PaymentMethod/TwoCheckout.php
new file mode 100644
index 0000000..7f4e346
--- /dev/null
+++ b/payment/uc_2checkout/src/Plugin/Ubercart/PaymentMethod/TwoCheckout.php
@@ -0,0 +1,202 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\uc_2checkout\Plugin\Ubercart\PaymentMethod\TwoCheckout.
+ */
+
+namespace Drupal\uc_2checkout\Plugin\Ubercart\PaymentMethod;
+
+use Drupal\Component\Utility\Html;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Url;
+use Drupal\uc_order\OrderInterface;
+use Drupal\uc_payment\PaymentMethodPluginBase;
+
+/**
+ * Defines the 2Checkout payment method.
+ *
+ * @UbercartPaymentMethod(
+ *   id = "2checkout",
+ *   name = @Translation("2Checkout"),
+ *   redirect = "\Drupal\uc_2checkout\Form\TwoCheckoutForm",
+ * )
+ */
+class TwoCheckout extends PaymentMethodPluginBase {
+
+// *   redirect => "\Drupal\uc_2checkout\Form\TwoCheckoutForm",
+//  $module_config = \Drupal::config('uc_2checkout.settings');
+//  $title = $module_config->get('method_title');
+//  $title .= '<br />' . theme('image', array(
+//    'uri' => drupal_get_path('module', 'uc_2checkout') . '/images/2co_logo.jpg',
+//    'attributes' => array('class' => array('uc-2checkout-logo')),
+//  ));
+
+  /**
+   * {@inheritdoc}
+   */
+  public function defaultConfiguration() {
+    return [
+      'check' => FALSE,
+      'checkout_type' => 'dynamic',
+      'currency_code' => '',
+      'demo' => TRUE,
+      'language' => 'en',
+      'notification_url' => '',
+      'method_title' => 'Credit card on a secure server:',
+      'secret_word' => 'tango',
+      'server_url' => 'https://www.2checkout.com/checkout/purchase',
+      'sid' => '',
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
+    $form['sid'] = array(
+      '#type' => 'textfield',
+      '#title' => $this->t('Vendor account number'),
+      '#description' => $this->t('Your 2Checkout vendor account number.'),
+      '#default_value' => $this->configuration['sid'],
+      '#size' => 16,
+    );
+    $form['secret_word'] = array(
+      '#type' => 'textfield',
+      '#title' => $this->t('Secret word for order verification'),
+      '#description' => $this->t('The secret word entered in your 2Checkout account Look and Feel settings.'),
+      '#default_value' => $this->configuration['secret_word'],
+      '#size' => 16,
+    );
+    $form['demo'] = array(
+      '#type' => 'checkbox',
+      '#title' => $this->t('Enable demo mode, allowing you to process fake orders for testing purposes.'),
+      '#default_value' => $this->configuration['demo'],
+    );
+    $form['language'] = array(
+      '#type' => 'select',
+      '#title' => $this->t('Language preference'),
+      '#description' => $this->t('Adjust language on 2Checkout pages.'),
+      '#options' => array(
+        'en' => $this->t('English'),
+        'sp' => $this->t('Spanish'),
+      ),
+      '#default_value' => $this->configuration['language'],
+    );
+    $form['uc_2checkout_currency_code'] = array(
+      '#type' => 'select',
+      '#title' => $this->t('Currency for the sale'),
+      '#options' => array(
+        '' => $this->t('Auto detected by 2Checkout'),
+        'USD', 'EUR', 'ARS', 'AUD', 'BRL', 'GBP', 'CAD', 'DKK', 'HKD', 'INR',
+        'ILS', 'JPY', 'LTL', 'MYR', 'MXN', 'NZD', 'NOK', 'PHP', 'RON', 'RUB',
+        'SGD', 'ZAR', 'SEK', 'CHF', 'TRY', 'AED',
+      ),
+      '#default_value' => $this->configuration['currency_code'],
+    );
+    $form['check'] = array(
+      '#type' => 'checkbox',
+      '#title' => $this->t('Allow customers to choose to pay by credit card or online check.'),
+      '#default_value' => $this->configuration['check'],
+    );
+    $form['method_title'] = array(
+      '#type' => 'textfield',
+      '#title' => $this->t('Payment method title'),
+      '#default_value' => $this->configuration['method_title'],
+    );
+    $form['checkout_type'] = array(
+      '#type' => 'radios',
+      '#title' => $this->t('Checkout type'),
+      '#options' => array(
+        'dynamic' => $this->t('Dynamic checkout (user is redirected to 2CO)'),
+        'direct' => $this->t('Direct checkout (payment page opens in iframe popup)'),
+      ),
+      '#default_value' => $this->configuration['checkout_type'],
+    );
+    $form['notification_url'] = array(
+      '#type' => 'url',
+      '#title' => $this->t('Instant notification settings URL'),
+      '#description' => $this->t('Pass this URL to the <a href=":help_url">instant notification settings</a> parameter in your 2Checkout account. This way, any refunds or failed fraud reviews will automatically cancel the Ubercart order.', [':help_url' => Url::fromUri('https://www.2checkout.com/static/va/documentation/INS/index.html')->toString()]),
+      '#default_value' => Url::fromRoute('uc_2checkout.notification', [], ['absolute' => TRUE])->toString(),
+      '#disabled' => TRUE,
+    );
+    $form['server_url'] = array(
+      '#type' => 'url',
+      '#title' => $this->t('2Checkout server URL'),
+      '#description' => $this->t('URL used to POST payments to the 2Checkout server.'),
+      '#default_value' => Url::fromUri($this->configuration['server_url'])->toString(),
+      '#disabled' => TRUE,
+    );
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
+    $this->configuration['check'] = $form_state->getValue('check');
+    $this->configuration['checkout_type'] = $form_state->getValue('checkout_type');
+    $this->configuration['currency_code'] = $form_state->getValue('currency_code');
+    $this->configuration['demo'] = $form_state->getValue('demo');
+    $this->configuration['language'] = $form_state->getValue('language');
+    $this->configuration['notification_url'] = $form_state->getValue('notification_url');
+    $this->configuration['method_title'] = $form_state->getValue('method_title');
+    $this->configuration['secret_word'] = $form_state->getValue('secret_word');
+    $this->configuration['server_url'] = $form_state->getValue('server_url');
+    $this->configuration['sid'] = $form_state->getValue('sid');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function cartDetails(OrderInterface $order, array $form, FormStateInterface $form_state) {
+    $build = array();
+
+    if ($this->configuration['check']) {
+      $build['pay_method'] = array(
+        '#type' => 'select',
+        '#title' => $this->t('Select your payment type:'),
+        '#default_value' => $_SESSION['pay_method'] == 'CK' ? 'CK' : 'CC',
+        '#options' => array(
+          'CC' => $this->t('Credit card'),
+          'CK' => $this->t('Online check'),
+        ),
+      );
+      unset($_SESSION['pay_method']);
+    }
+
+    return $build;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function cartProcess(OrderInterface $order, array $form, FormStateInterface $form_state) {
+//    if ($this->configuration['pay_method']) {
+//      $order->payment_details = $form_state->getValue(['panes', 'payment', 'details']);
+//    }
+
+    if (NULL != $form_state->getValue(['panes', 'payment', 'details', 'pay_method'])) {
+      $_SESSION['pay_method'] = $form_state->getValue(['panes', 'payment', 'details', 'pay_method']);
+    }
+    return TRUE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function cartReview(OrderInterface $order) {
+    $review = array();
+
+    if ($this->configuration['check']) {
+      $review[] = array('title' => $this->t('Credit card/eCheck'), 'data' => NULL);
+    }
+    else {
+      $review[] = array('title' => $this->t('Credit card'), 'data' => NULL);
+    }
+
+    return $review;
+  }
+
+}
diff --git a/payment/uc_2checkout/uc_2checkout.libraries.yml b/payment/uc_2checkout/uc_2checkout.libraries.yml
new file mode 100644
index 0000000..a4bb105
--- /dev/null
+++ b/payment/uc_2checkout/uc_2checkout.libraries.yml
@@ -0,0 +1,3 @@
+2checkout.direct:
+  js:
+    https://www.2checkout.com/static/checkout/javascript/direct.min.js: { type: external, minified: true }
diff --git a/payment/uc_2checkout/uc_2checkout.module b/payment/uc_2checkout/uc_2checkout.module
index 8736ce3..892083d 100644
--- a/payment/uc_2checkout/uc_2checkout.module
+++ b/payment/uc_2checkout/uc_2checkout.module
@@ -5,32 +5,21 @@
  * Integrates 2Checkout.com's redirected payment service.
  */
 
-use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Routing\RouteMatchInterface;
-use Drupal\Core\Url;
 
 /**
  * Implements hook_help().
  */
-function uc_2checkout_help($path, RouteMatchInterface $route_match) {
-  switch ($path) {
-    case 'admin/store/config/payment/method/%':
-      if ($arg[5] == '2checkout') {
-        return '<p>' . t('To accept PayPal payments in 2Checkout, please ensure that demo mode is disabled and your store currency is one of USD, AUD, CAD, EUR or GBP.') . '</p>';
-      }
+function uc_2checkout_help($route_name, RouteMatchInterface $route_match) {
+  // Provide information and instructions on the payment method add and edit forms.
+  if ($route_name == 'entity.uc_payment_method.add_form' &&
+      $route_match->getRawParameter('plugin_id') == '2checkout') {
+    return '<p>' . t('To accept PayPal payments in 2Checkout, please ensure that demo mode is disabled and your store currency is one of USD, AUD, CAD, EUR or GBP.') . '</p>';
+  }
+  else if ($route_name == 'entity.uc_payment_method.edit_form' &&
+           $route_match->getParameter('uc_payment_method')->getPlugin()->getPluginId() == '2checkout') {
+    return '<p>' . t('To accept PayPal payments in 2Checkout, please ensure that demo mode is disabled and your store currency is one of USD, AUD, CAD, EUR or GBP.') . '</p>';
   }
-}
-
-/**
- * Implements hook_menu().
- */
-function uc_2checkout_menu() {
-  $items['cart/2checkout/complete'] = array(
-    'title' => 'Order complete',
-    'route_name' => 'uc_2checkout.complete',
-  );
-
-  return $items;
 }
 
 /**
@@ -39,187 +28,10 @@ function uc_2checkout_menu() {
 function uc_2checkout_ucga_display() {
   // Tell UC Google Analytics to display the e-commerce JS on the custom
   // order completion page for this module.
+
+  //@todo, inject $route_name so we can do
+  //if ($route_name == 'uc_2checkout.complete')
   if (arg(0) == 'cart' && arg(1) == '2checkout' && arg(2) == 'complete') {
     return TRUE;
   }
 }
-
-/**
- * Implements hook_uc_payment_method().
- *
- * @see uc_payment_method_2checkout()
- */
-function uc_2checkout_uc_payment_method() {
-  $module_config = \Drupal::config('uc_2checkout.settings');
-  $path = base_path() . drupal_get_path('module', 'uc_2checkout');
-  $title = $module_config->get('method_title');
-  $title .= '<br />' . theme('image', array(
-    'uri' => drupal_get_path('module', 'uc_2checkout') . '/images/2co_logo.jpg',
-    'attributes' => array('class' => array('uc-2checkout-logo')),
-  ));
-
-  $methods['2checkout'] = array(
-    'name' => t('2Checkout'),
-    'title' => $title,
-    'review' => $module_config->get('check') ? t('Credit card/eCheck') : t('Credit card'),
-    'callback' => 'uc_payment_method_2checkout',
-    'redirect' => 'uc_2checkout_form',
-    'weight' => 3,
-    'checkout' => TRUE,
-    'no_gateway' => TRUE,
-  );
-
-  return $methods;
-}
-
-/**
- * Adds 2Checkout settings to the payment method settings form.
- *
- * @see uc_2checkout_uc_payment_method()
- */
-function uc_payment_method_2checkout($op, &$order, $form = NULL, &$form_state = NULL) {
-  $module_config = \Drupal::config('uc_2checkout.settings');
-  switch ($op) {
-    case 'cart-details':
-      $build = array();
-
-      if ($module_config->get('check')) {
-        $build['pay_method'] = array(
-          '#type' => 'select',
-          '#title' => t('Select your payment type:'),
-          '#default_value' => $_SESSION['pay_method'] == 'CK' ? 'CK' : 'CC',
-          '#options' => array(
-            'CC' => t('Credit card'),
-            'CK' => t('Online check'),
-          ),
-        );
-        unset($_SESSION['pay_method']);
-      }
-
-      return $build;
-
-    case 'cart-process':
-      if (isset($form_state['values']['panes']['payment']['details']['pay_method'])) {
-        $_SESSION['pay_method'] = $form_state['values']['panes']['payment']['details']['pay_method'];
-      }
-      return;
-
-    case 'settings':
-      $form['uc_2checkout_sid'] = array(
-        '#type' => 'textfield',
-        '#title' => t('Vendor account number'),
-        '#description' => t('Your 2Checkout vendor account number.'),
-        '#default_value' => $module_config->get('sid'),
-        '#size' => 16,
-      );
-      $form['uc_2checkout_secret_word'] = array(
-        '#type' => 'textfield',
-        '#title' => t('Secret word for order verification'),
-        '#description' => t('The secret word entered in your 2Checkout account Look and Feel settings.'),
-        '#default_value' => $module_config->get('secret_word'),
-        '#size' => 16,
-      );
-      $form['uc_2checkout_demo'] = array(
-        '#type' => 'checkbox',
-        '#title' => t('Enable demo mode, allowing you to process fake orders for testing purposes.'),
-        '#default_value' => $module_config->get('demo'),
-      );
-      $form['uc_2checkout_language'] = array(
-        '#type' => 'select',
-        '#title' => t('Language preference'),
-        '#description' => t('Adjust language on 2Checkout pages.'),
-        '#options' => array(
-          'en' => t('English'),
-          'sp' => t('Spanish'),
-        ),
-        '#default_value' => $module_config->get('language'),
-      );
-      $form['uc_2checkout_check'] = array(
-        '#type' => 'checkbox',
-        '#title' => t('Allow customers to choose to pay by credit card or online check.'),
-        '#default_value' => $module_config->get('check'),
-      );
-      $form['uc_2checkout_method_title'] = array(
-        '#type' => 'textfield',
-        '#title' => t('Payment method title'),
-        '#default_value' => $module_config->get('method_title'),
-      );
-      $form['uc_2checkout_checkout_type'] = array(
-        '#type' => 'select',
-        '#title' => t('2Checkout.com checkout type'),
-        '#description' => t('Single page checkout only works for stores selling intangible products using credit card payments.'),
-        '#options' => array(
-          'multi' => t('Multi-page checkout'),
-          'single' => t('Single page checkout'),
-        ),
-        '#default_value' => $module_config->get('checkout_type'),
-      );
-      return $form;
-  }
-}
-
-/**
- * Form to build the submission to 2Checkout.com.
- */
-function uc_2checkout_form($form, &$form_state, $order) {
-  $module_config = \Drupal::config('uc_2checkout.settings');
-  $country = \Drupal::service('country_manager')->getCountry($order->billing_country);
-
-  $data = array(
-    'sid' => $module_config->get('sid'),
-    'total' => uc_currency_format($order->getTotal(), FALSE, FALSE, '.'),
-    'cart_order_id' => $order->id(),
-    'demo' => $module_config->get('demo') ? 'Y' : 'N',
-    'fixed' => 'Y',
-    'lang' => $module_config->get('language'),
-    'x_receipt_link_url' => Url::fromRoute('uc_2checkout.complete', ['cart_id' => uc_cart_get_id()], ['absolute' => TRUE])->toString(),
-    'merchant_order_id' => $order->id(),
-    'pay_method' => isset($_SESSION['pay_method']) ? $_SESSION['pay_method'] : 'CC',
-    'card_holder_name' => Unicode::substr($order->billing_first_name . ' ' . $order->billing_last_name, 0, 128),
-    'street_address' => Unicode::substr($order->billing_street1, 0, 64),
-    'street_address2' => Unicode::substr($order->billing_street2, 0, 64),
-    'city' => Unicode::substr($order->billing_city, 0, 64),
-    'state' => $order->billing_zone,
-    'zip' => Unicode::substr($order->billing_postal_code, 0, 16),
-    'country' => $country ? $country->getAlpha3() : 'USA',
-    'email' => Unicode::substr($order->getEmail(), 0, 64),
-    'phone' => Unicode::substr($order->billing_phone, 0, 16),
-    'id_type' => 1,
-  );
-
-  $i = 0;
-  foreach ($order->products as $product) {
-    $i++;
-    $data['c_prod_' . $i] = $product->model . ',' . $product->qty;
-    $data['c_name_' . $i] = $product->title;
-    $data['c_description_' . $i] = '';
-    $data['c_price_' . $i] = uc_currency_format($product->price, FALSE, FALSE, '.');
-  }
-
-  $form['#action'] = _uc_2checkout_post_url($module_config->get('checkout_type'));
-
-  foreach ($data as $name => $value) {
-    $form[$name] = array('#type' => 'hidden', '#value' => $value);
-  }
-
-  $form['actions'] = array('#type' => 'actions');
-  $form['actions']['submit'] = array(
-    '#type' => 'submit',
-    '#value' => t('Submit order'),
-  );
-
-  return $form;
-}
-
-/**
- * Helper function to obtain 2Checkout URL for a transaction.
- */
-function _uc_2checkout_post_url($type) {
-  switch ($type) {
-    case 'single':
-      return 'https://www.2checkout.com/checkout/spurchase';
-    case 'multi':
-    default:
-      return 'https://www.2checkout.com/2co/buyer/purchase';
-  }
-}
diff --git a/payment/uc_2checkout/uc_2checkout.routing.yml b/payment/uc_2checkout/uc_2checkout.routing.yml
index deb9345..9deffc1 100644
--- a/payment/uc_2checkout/uc_2checkout.routing.yml
+++ b/payment/uc_2checkout/uc_2checkout.routing.yml
@@ -1,5 +1,15 @@
 uc_2checkout.complete:
   path: '/cart/2checkout/complete'
   defaults:
-    _controller: '\Drupal\uc_2checkout\Controller\CheckoutController::complete'
+    _controller: '\Drupal\uc_2checkout\Controller\TwoCheckoutController::complete'
+    _title: 'Order complete'
   requirements:
+    _permission: 'access content'
+
+uc_2checkout.notification:
+  path: '/cart/2checkout/notification'
+  defaults:
+    _controller: '\Drupal\uc_2checkout\Controller\TwoCheckoutController::notification'
+    _title: 'Notification callback'
+  requirements:
+    _permission: 'access content'
