diff --git a/commerce_authnet.module b/commerce_authnet.module
index e696d99..ef10383 100644
--- a/commerce_authnet.module
+++ b/commerce_authnet.module
@@ -10,10 +10,9 @@ define('AUTHNET_TXN_MODE_LIVE', 'live');
 define('AUTHNET_TXN_MODE_LIVE_TEST', 'live_test');
 define('AUTHNET_TXN_MODE_DEVELOPER', 'developer');
 
-
 /**
  * Implements hook_menu().
-*/
+ */
 function commerce_authnet_menu() {
   $items = array();
 
@@ -162,6 +161,16 @@ function commerce_authnet_commerce_payment_method_info() {
       'update callback' => 'commerce_authnet_cim_cardonfile_update',
       'delete callback' => 'commerce_authnet_cim_cardonfile_delete',
     ),
+    'file' => 'includes/commerce_authnet_payment.cc.inc',
+  );
+
+  $payment_methods['authnet_echeck'] = array(
+    'base' => 'commerce_authnet_echeck',
+    'title' => t('Authorize.Net AIM - eCheck.Net'),
+    'short_title' => t('Authorize.Net eCheck'),
+    'display_title' => t('eCheck'),
+    'description' => t('Integrates Authorize.Net Advanced Integration Method for eCheck transactions.'),
+    'file' => 'includes/commerce_authnet_payment.echeck.inc',
   );
 
   return $payment_methods;
@@ -185,226 +194,21 @@ function commerce_authnet_aim_default_settings() {
 }
 
 /**
- * Payment method callback: settings form.
- */
-function commerce_authnet_aim_settings_form($settings = NULL) {
-  module_load_include('inc', 'commerce_payment', 'includes/commerce_payment.credit_card');
-
-  // Merge default settings into the stored settings array.
-  $settings = (array) $settings + commerce_authnet_aim_default_settings();
-
-  $form = array();
-
-  $form['login'] = array(
-    '#type' => 'textfield',
-    '#title' => t('API Login ID'),
-    '#description' => t('Your API Login ID is different from the username you use to login to your Authorize.Net account. Once you login, browse to your Account tab and click the <em>API Login ID and Transaction Key</em> link to find your API Login ID. If you are using a new Authorize.Net account, you may still need to generate an ID.'),
-    '#default_value' => $settings['login'],
-    '#required' => TRUE,
-  );
-  $form['tran_key'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Transaction Key'),
-    '#description' => t('Your Transaction Key can be found on the same screen as your API Login ID. However, it will not be readily displayed. You must answer your security question and submit a form to see your Transaction Key.'),
-    '#default_value' => $settings['tran_key'],
-    '#required' => TRUE,
-  );
-  $form['txn_mode'] = array(
-    '#type' => 'radios',
-    '#title' => t('Transaction mode'),
-    '#description' => t('Adjust to live transactions when you are ready to start processing real payments.') . '<br />' . t('Only specify a developer test account if you login to your account through https://test.authorize.net.'),
-    '#options' => array(
-      AUTHNET_TXN_MODE_LIVE => t('Live transactions in a live account'),
-      AUTHNET_TXN_MODE_LIVE_TEST => t('Test transactions in a live account'),
-      AUTHNET_TXN_MODE_DEVELOPER => t('Developer test account transactions'),
-    ),
-    '#default_value' => $settings['txn_mode'],
-  );
-  $form['txn_type'] = array(
-    '#type' => 'radios',
-    '#title' => t('Default credit card transaction type'),
-    '#description' => t('The default will be used to process transactions during checkout.'),
-    '#options' => array(
-      COMMERCE_CREDIT_AUTH_CAPTURE => t('Authorization and capture'),
-      COMMERCE_CREDIT_AUTH_ONLY => t('Authorization only (requires manual or automated capture after checkout)'),
-    ),
-    '#default_value' => $settings['txn_type'],
-  );
-
-  $form['card_types'] = array(
-    '#type' => 'checkboxes',
-    '#title' => t('Limit accepted credit cards to the following types'),
-    '#description' => t('If you want to limit acceptable card types, you should only select those supported by your merchant account.') . '<br />' . t('If none are checked, any credit card type will be accepted.'),
-    '#options' => commerce_payment_credit_card_types(),
-    '#default_value' => $settings['card_types'],
-  );
-
-  // CIM support in conjunction with AIM requires the Card on File module.
-  if (module_exists('commerce_cardonfile')) {
-    $form['cardonfile'] = array(
-      '#type' => 'checkbox',
-      '#title' => t('Enable Card on File functionality with this payment method using Authorize.Net CIM.'),
-      '#description' => t('This requires an Authorize.Net account upgraded to include support for CIM (Customer Information Manager).'),
-      '#default_value' => $settings['cardonfile'],
-    );
-
-    $form['continuous'] = array(
-      '#type' => 'checkbox',
-      '#title' => t('Use continuous authority transactions.'),
-      '#description' => t('A continuous authority merchant account will be required.'),
-      '#default_value' => $settings['continuous'],
-    );
-  }
-
-  else {
-    $form['cardonfile'] = array(
-      '#type' => 'markup',
-      '#markup' => t('To enable Card on File funcitionality download and install the Card on File module.'),
-    );
-  }
-
-  $form['email_customer'] = array(
-    '#type' => 'checkbox',
-    '#title' => t('Tell Authorize.net to e-mail the customer a receipt based on your account settings.'),
-    '#default_value' => $settings['email_customer'],
-  );
-  $form['log'] = array(
-    '#type' => 'checkboxes',
-    '#title' => t('Log the following messages for debugging'),
-    '#options' => array(
-      'request' => t('API request messages'),
-      'response' => t('API response messages'),
-    ),
-    '#default_value' => $settings['log'],
-  );
-
-  return $form;
-}
-
-/**
- * Payment method callback: checkout form.
- */
-function commerce_authnet_aim_submit_form($payment_method, $pane_values, $checkout_pane, $order) {
-  module_load_include('inc', 'commerce_payment', 'includes/commerce_payment.credit_card');
-
-  // Prepare the fields to include on the credit card form.
-  $fields = array(
-    'code' => '',
-  );
-
-  // Add the credit card types array if necessary.
-  if (isset($payment_method['settings']['card_types'])) {
-    $card_types = array_diff(array_values($payment_method['settings']['card_types']), array(0));
-
-    if (!empty($card_types)) {
-      $fields['type'] = $card_types;
-    }
-  }
-
-  return commerce_payment_credit_card_form($fields);
-}
-
-/**
- * Payment method callback: checkout form validation.
- */
-function commerce_authnet_aim_submit_form_validate($payment_method, $pane_form, $pane_values, $order, $form_parents = array()) {
-  // If the customer specified a card on file, skip the normal validation.
-  if (module_exists('commerce_cardonfile') && !empty($payment_method['settings']['cardonfile']) &&
-    !empty($pane_values['cardonfile']) && $pane_values['cardonfile'] !== 'new') {
-    return;
-  }
-
-  module_load_include('inc', 'commerce_payment', 'includes/commerce_payment.credit_card');
-
-  // Validate the credit card fields.
-  $settings = array(
-    'form_parents' => array_merge($form_parents, array('credit_card')),
-  );
-
-  if (!commerce_payment_credit_card_validate($pane_values['credit_card'], $settings)) {
-    return FALSE;
-  }
-}
-
-/**
  * Payment method callback: checkout form submission.
+ * Build the order details required to make a request.
+ *
+ * @param stdClass $order
+ *   The order entity that the payment is being made for.
+ *
+ * @return array
+ *   Array of order data for the Authorize.Net request.
  */
-function commerce_authnet_aim_submit_form_submit($payment_method, $pane_form, $pane_values, $order, $charge) {
-  // If Card on File is enabled and active for this payment method, then we need
-  // to figure out what to do.
-  if (module_exists('commerce_cardonfile') && $payment_method['settings']['cardonfile'] && !empty($pane_values['cardonfile'])) {
-    if (!empty($pane_values['cardonfile']) && $pane_values['cardonfile'] !== 'new') {
-      // We're using a stored payment profile. Pass it along to CIM.
-      return commerce_authnet_cim_submit_form_submit($payment_method, $pane_form, $pane_values, $order, $charge);
-    }
-    else {
-      if (!empty($pane_values['credit_card']['cardonfile_store']) && $pane_values['credit_card']['cardonfile_store']) {
-        // We've got a request to store a new card.
-        return commerce_authnet_cim_submit_new_card_form_submit($payment_method, $pane_form, $pane_values, $order, $charge);
-      }
-    }
-  }
-
-  // Determine the credit card type if possible for use in later code.
-  if (!empty($pane_values['credit_card']['number'])) {
-    module_load_include('inc', 'commerce_payment', 'includes/commerce_payment.credit_card');
-    $card_type = commerce_payment_validate_credit_card_type($pane_values['credit_card']['number'], array_keys(commerce_payment_credit_card_types()));
-  }
-
-  // If the charge amount is 0...
-  if ($charge['amount'] == 0) {
-    // Prevent the transaction except under limited circumstances.
-    $prevent_transaction = TRUE;
-
-    // Allow 0 amount authorizations on Visa cards.
-    if ($payment_method['settings']['txn_type'] == COMMERCE_CREDIT_AUTH_ONLY && $card_type == 'visa') {
-      $prevent_transaction = FALSE;
-    }
-
-    // If the transaction should still be prevented...
-    if ($prevent_transaction) {
-      // Create a transaction to log the skipped transaction and display a
-      // helpful message to the customer.
-      $transaction = commerce_payment_transaction_new('authnet_aim', $order->order_id);
-      $transaction->amount = $charge['amount'];
-      $transaction->currency_code = $charge['currency_code'];
-      $transaction->status = COMMERCE_PAYMENT_STATUS_FAILURE;
-      $transaction->message = t('Invalid @amount transaction not attempted.', array('@amount' => commerce_currency_format($charge['amount'], $charge['currency_code'])));
-      commerce_payment_transaction_save($transaction);
-
-      drupal_set_message(t('We encountered an error processing your transaction. Please contact us to resolve the issue.'), 'error');
-      return FALSE;
-    }
-  }
-
+function commerce_authnet_aim_request_order_details($order) {
   $order_wrapper = entity_metadata_wrapper('commerce_order', $order);
 
-  // Get the default transaction type from the payment method settings.
-  $txn_type = $payment_method['settings']['txn_type'];
-
-  // If txn_type has been specified in the pane values array, such as through
-  // the special select element we alter onto the payment terminal form, use
-  // that instead.
-  if (!empty($pane_values['txn_type'])) {
-    $txn_type = $pane_values['txn_type'];
-  }
-
-  // Build a name-value pair array for this transaction.
-  $nvp = array(
-    'x_type' => commerce_authnet_txn_type($txn_type),
-    'x_method' => 'CC',
-    'x_amount' => number_format(commerce_currency_amount_to_decimal($charge['amount'], $charge['currency_code']), 2, '.', ''),
-    'x_currency_code' => $charge['currency_code'],
-    'x_card_num' => $pane_values['credit_card']['number'],
-    'x_exp_date' => $pane_values['credit_card']['exp_month'] . $pane_values['credit_card']['exp_year'],
-  );
-
-  if (isset($pane_values['credit_card']['code'])) {
-    $nvp['x_card_code'] = $pane_values['credit_card']['code'];
-  }
-
   // Build a description for the order.
   $description = array();
+
   // Descriptions come from products, though not all environments have them. So check first.
   if (function_exists('commerce_product_line_item_types')) {
     foreach ($order_wrapper->commerce_line_items as $delta => $line_item_wrapper) {
@@ -414,8 +218,7 @@ function commerce_authnet_aim_submit_form_submit($payment_method, $pane_form, $p
     }
   }
 
-  // Add additional transaction invormation to the request array.
-  $nvp += array(
+  $details = array(
     // Order Information
     'x_invoice_num' => $order->order_number,
     'x_description' => substr(implode(', ', $description), 0, 255),
@@ -436,7 +239,7 @@ function commerce_authnet_aim_submit_form_submit($payment_method, $pane_form, $p
       $billing_address['last_name'] = implode(' ', $name_parts);
     }
 
-    $nvp += array(
+    $details += array(
       // Customer Billing Address
       'x_first_name' => substr($billing_address['first_name'], 0, 50),
       'x_last_name' => substr($billing_address['last_name'], 0, 50),
@@ -459,7 +262,7 @@ function commerce_authnet_aim_submit_form_submit($payment_method, $pane_form, $p
       $shipping_address['last_name'] = implode(' ', $name_parts);
     }
 
-    $nvp += array(
+    $details += array(
       // Customer shipping Address
       'x_ship_to_first_name' => substr($shipping_address['first_name'], 0, 50),
       'x_ship_to_last_name' => substr($shipping_address['last_name'], 0, 50),
@@ -472,65 +275,7 @@ function commerce_authnet_aim_submit_form_submit($payment_method, $pane_form, $p
     );
   }
 
-  // Submit the request to Authorize.Net.
-  $response = commerce_authnet_aim_request($payment_method, $nvp);
-
-  // Prepare a transaction object to log the API response.
-  $transaction = commerce_payment_transaction_new('authnet_aim', $order->order_id);
-  $transaction->instance_id = $payment_method['instance_id'];
-  $transaction->remote_id = $response[6];
-  $transaction->amount = $charge['amount'];
-  $transaction->currency_code = $charge['currency_code'];
-  $transaction->payload[REQUEST_TIME] = $response;
-
-  // If we didn't get an approval response code...
-  if ($response[0] != '1') {
-    // Create a failed transaction with the error message.
-    $transaction->status = COMMERCE_PAYMENT_STATUS_FAILURE;
-  }
-  else {
-    // Set the transaction status based on the type of transaction this was.
-    switch ($txn_type) {
-      case COMMERCE_CREDIT_AUTH_ONLY:
-        $transaction->status = COMMERCE_PAYMENT_STATUS_PENDING;
-        break;
-
-      case COMMERCE_CREDIT_AUTH_CAPTURE:
-        $transaction->status = COMMERCE_PAYMENT_STATUS_SUCCESS;
-        break;
-
-      case COMMERCE_CREDIT_CAPTURE_ONLY:
-        $transaction->status = COMMERCE_PAYMENT_STATUS_SUCCESS;
-        break;
-    }
-  }
-
-  // Store the type of transaction in the remote status.
-  $transaction->remote_status = $response[11];
-
-  // Build a meaningful response message.
-  $message = array(
-    '<b>' . commerce_authnet_reverse_txn_type($response[11]) . '</b>',
-    '<b>' . ($response[0] != '1' ? t('REJECTED') : t('ACCEPTED')) . ':</b> ' . check_plain($response[3]),
-    t('AVS response: @avs', array('@avs' => commerce_authnet_avs_response($response[5]))),
-  );
-
-  // Add the CVV response if enabled.
-  if (isset($nvp['x_card_code'])) {
-    $message[] = t('CVV match: @cvv', array('@cvv' => commerce_authnet_cvv_response($response[38])));
-  }
-
-  $transaction->message = implode('<br />', $message);
-
-  // Save the transaction information.
-  commerce_payment_transaction_save($transaction);
-
-  // If the payment failed, display an error and rebuild the form.
-  if ($response[0] != '1') {
-    drupal_set_message(t('We received the following error processing your card. Please enter your information again or try a different card.'), 'error');
-    drupal_set_message(check_plain($response[3]), 'error');
-    return FALSE;
-  }
+  return array_filter($details);
 }
 
 /**
@@ -554,175 +299,34 @@ function commerce_authnet_form_commerce_payment_order_transaction_add_form_alter
 }
 
 /**
- * Handles advanced logic relating to creating CIM orders with new card data.
- *
- * @see commerce_authnet_aim_submit_form_submit()
+ * Imitates the checkout form submission callback for the AIM payment method.
  */
-function commerce_authnet_cim_submit_new_card_form_submit($payment_method, $pane_form, $pane_values, $order, $charge) {
-  // At this point, we have a few choices to make. We know the user is logged in
-  // and so we know if they have any existing card on file profiles. If they do
-  // not, then we can assume that they need a new profile. If they do have one,
-  // then we need take appropriate steps to add the CC info to the existing
-  // profile and process the payment based on their profile.
-  $order_wrapper = entity_metadata_wrapper('commerce_order', $order);
-
-  $payment_details = array(
-    'cardNumber' => $pane_values['credit_card']['number'],
-    'expirationDate' => $pane_values['credit_card']['exp_year'] . '-' . $pane_values['credit_card']['exp_month'],
-  );
-
-  if (isset($pane_values['credit_card']['code'])) {
-    $payment_details['cardCode'] = $pane_values['credit_card']['code'];
-  }
-
-  // Prepare the billing address for use in the request.
-  if (isset($order->commerce_customer_billing) && $order_wrapper->commerce_customer_billing->value()) {
-    $billing_address = $order_wrapper->commerce_customer_billing->commerce_customer_address->value();
-
-    if (empty($billing_address['first_name'])) {
-      $name_parts = explode(' ', $billing_address['name_line']);
-      $billing_address['first_name'] = array_shift($name_parts);
-      $billing_address['last_name'] = implode(' ', $name_parts);
-    }
-  }
-
-  $remote_id = FALSE;
-
-  // First look to see if we already have cards on file for the user.
-  $stored_cards = array();
-  if (!user_is_anonymous()) {
-    $stored_cards = commerce_cardonfile_load_multiple_by_uid($order->uid, $payment_method['instance_id']);
-  }
-  $add_to_profile = NULL;
-
-  if (empty($stored_cards)) {
-    // We do not, create the profile (which includes the payment details).
-    if ($response = commerce_authnet_cim_create_customer_profile_request($payment_method, $order, $payment_details)) {
-      // If the Customer Profile creation was a success, store the new card on
-      // file data locally.
-      if ((string) $response->messages->resultCode == 'Ok') {
-        // Build a remote ID that includes the Customer Profile ID and the
-        // Payment Profile ID.
-        $remote_id = (string) $response->customerProfileId . '|' . (string) $response->customerPaymentProfileIdList->numericString;
-      }
-      elseif ((string) $response->messages->message->code == 'E00039') {
-        // But if a Customer Profile already existed for this user, attempt
-        // instead to add this card as a new Payment Profile to it.
-        $result = array_filter(explode(' ', (string) $response->messages->message->text), 'is_numeric');
-        $add_to_profile = reset($result);
-      }
-    }
-  }
-  else {
-    // Extract the user's Customer Profile ID from the first card's remote ID.
-    $card_data = reset($stored_cards);
-    list($cim_customer_profile_id, ) = explode('|', $card_data->remote_id);
-
-    // Attempt to add the card as a new payment profile to this Customer Profile.
-    $add_to_profile = $cim_customer_profile_id;
-  }
-
-  // Attempt to add the card to an existing Customer Profile if specified.
-  if (!empty($add_to_profile)) {
-    $response = commerce_authnet_cim_create_customer_payment_profile_request($payment_method, $add_to_profile, $order, $payment_details);
-    // If the Payment Profile creation was a success, store the new card on
-    // file data locally.
-    if ((string) $response->messages->resultCode == 'Ok' || (string) $response->messages->message->code == 'E00039') {
-      // If we got a duplicate code, then the payment profile
-      // at Authorize.Net and needs to be represented locally.
-      if ((string) $response->messages->message->code == 'E00039') {
-        $cim_profile_response = commerce_authnet_cim_get_customer_profile_request($payment_method, $add_to_profile);
-        if ((string) $cim_profile_response->messages->resultCode == 'Ok') {
-          // Inspect the returned payment profiles to find the one that
-          // generated the duplicate error code.
-          $cim_payment_profiles = $cim_profile_response->profile->paymentProfiles;
-          if (!is_array($cim_payment_profiles)) {
-            $cim_payment_profiles = array($cim_payment_profiles);
-          }
+function commerce_authnet_cim_submit_form_submit($payment_method, $pane_form, $pane_values, $order, $charge) {
 
-          foreach ($cim_payment_profiles as $key => $payment_profile) {
-            // We match the submitted values against the existing payment
-            // profiles using the last 4 digits of the card number. This could
-            // potentially create a conflict if the same customer has two
-            // different cards that end in the same four digits, but that is
-            // highly unlikely.
-            if (substr($pane_values['credit_card']['number'], -4) == substr($payment_profile->payment->creditCard->cardNumber, -4)) {
-              $payment_profile_id = (string) $payment_profile->customerPaymentProfileId;
-              break;
-            }
-          }
-        }
-      }
-      else {
-        $payment_profile_id = (string) $response->customerPaymentProfileId;
-      }
+  if (isset($pane_values['cardonfile'])) {
+    // First attempt to load the card on file.
+    $card_data = commerce_cardonfile_load($pane_values['cardonfile']);
 
-      // Build a remote ID that includes the customer profile ID and the new
-      // or existing payment profile ID. We don't do any check here to ensure
-      // we found a payment profile ID, as we shouldn't have got a duplicate
-      // error if it didn't actually exist.
-      $remote_id = $add_to_profile . '|' . $payment_profile_id;
+    // Fail now if it is no longer available or the card is inactive.
+    if (empty($card_data) || $card_data->status == 0) {
+      drupal_set_message(t('The requested payment method is no longer valid.'), 'error');
+      return FALSE;
     }
-    elseif ($response->messages->message->code == 'E00040') {
-      // But if we could not find a customer profile, create a new one.
-      if ($response = commerce_authnet_cim_create_customer_profile_request($payment_method, $order, $payment_details)) {
-        // If the Customer Profile creation was a success, store the new card on
-        // file data locally.
-        if ((string) $response->messages->resultCode == 'Ok') {
-          // Build a remote ID that includes the Customer Profile ID and the
-          // Payment Profile ID.
-          $remote_id = (string) $response->customerProfileId . '|' . (string) $response->customerPaymentProfileIdList->numericString;
-        }
-      }
+  
+    return commerce_authnet_cim_cardonfile_charge($payment_method, $card_data, $order, $charge);
+  }
+  else if (isset($pane_values['bank_account_onfile'])) {
+    $check_data = commerce_bank_account_onfile_load($pane_values['bank_account_onfile']);
+    // Fail now if it is no longer available or the card is inactive.
+    if (empty($check_data) || $check_data->status == 0) {
+      drupal_set_message(t('The requested payment method is no longer valid.'), 'error');
+      return FALSE;
     }
+  
+    return commerce_authnet_cim_bankonfile_charge($payment_method, $check_data, $order, $charge);
   }
 
-  // We couldn't store a profile. Abandon ship!
-  if (!$remote_id) {
-    return FALSE;
-  }
-
-  // Build our card for storing with card on file.
-  $card_data = commerce_cardonfile_new();
-  $card_data->uid = $order->uid;
-  $card_data->payment_method = $payment_method['method_id'];
-  $card_data->instance_id = $payment_method['instance_id'];
-  $card_data->remote_id = $remote_id;
-  $card_data->card_type = !empty($card_type) ? $card_type : 'card';
-  $card_data->card_name = !empty($billing_address['name_line']) ? $billing_address['name_line'] : '';
-  $card_data->card_number = substr($pane_values['credit_card']['number'], -4);
-  $card_data->card_exp_month = $pane_values['credit_card']['exp_month'];
-  $card_data->card_exp_year = $pane_values['credit_card']['exp_year'];
-  $card_data->status = 1;
-
-  // Save and log the creation of the new card on file.
-  commerce_cardonfile_save($card_data);
-
-  if ($add_to_profile) {
-    watchdog('commerce_authnet', 'CIM Payment Profile added to Customer Profile @profile_id for user @uid.', array('@profile_id' => $add_to_profile, '@uid' => $order->uid));
-  }
-  else {
-    watchdog('commerce_authnet', 'CIM Customer Profile @profile_id created and saved to user @uid.', array('@profile_id' => (string) $response->customerProfileId, '@uid' => $order->uid));
-  }
-
-  // Process the payment
-  return commerce_authnet_cim_cardonfile_charge($payment_method, $card_data, $order, $charge);
-}
-
-/**
- * Imitates the checkout form submission callback for the AIM payment method.
- */
-function commerce_authnet_cim_submit_form_submit($payment_method, $pane_form, $pane_values, $order, $charge) {
-  // First attempt to load the card on file.
-  $card_data = commerce_cardonfile_load($pane_values['cardonfile']);
-
-  // Fail now if it is no longer available or the card is inactive.
-  if (empty($card_data) || $card_data->status == 0) {
-    drupal_set_message(t('The requested card on file is no longer valid.'), 'error');
-    return FALSE;
-  }
-
-  return commerce_authnet_cim_cardonfile_charge($payment_method, $card_data, $order, $charge);
+  
 }
 
 /**
@@ -1295,6 +899,30 @@ function commerce_authnet_cim_credit_card_array($payment_details) {
 }
 
 /**
+ * Generates a bankAccount array for CIM API requests.
+ *
+ * @param $payment_details
+ *   An array of payment details used in a CIM API request that doesn't have to
+ *   include credit card data. If it does, the following keys are expected:
+ *   - routingNumber: the bank routing number
+ *   - accountNumber: the bank account number
+ *   - accountType: the account type
+ *   - bankName: the name of the bank
+ *   - nameOnAccount: the name on the account
+ */
+function commerce_authnet_cim_bank_account_array($payment_details) {
+  $bank_account = array();
+
+  foreach (array('accountType', 'routingNumber', 'accountNumber', 'nameOnAccount', 'bankName') as $key) {
+    if (!empty($payment_details[$key])) {
+      $bank_account[$key] = $payment_details[$key];
+    }
+  }
+
+  return $bank_account;
+}
+
+/**
  * Submits a createCustomerProfileRequest XML CIM API request to Authorize.Net.
  *
  * This function will attempt to create a CIM Customer Profile and a default
@@ -1351,6 +979,14 @@ function commerce_authnet_cim_create_customer_profile_request($payment_method, $
   if (!empty($credit_card)) {
     $api_request_data['profile']['paymentProfiles']['payment']['creditCard'] = $credit_card;
   }
+  
+  // Add bank account on file payment details to the
+  // default payment profile if given.
+  $bank_account = commerce_authnet_cim_bank_account_array($payment_details);
+
+  if (!empty($bank_account)) {
+    $api_request_data['profile']['paymentProfiles']['payment']['bankAccount'] = $bank_account;
+  }
 
   return commerce_authnet_cim_request($payment_method, 'createCustomerProfileRequest', $api_request_data);
 }
@@ -1393,6 +1029,14 @@ function commerce_authnet_cim_create_customer_payment_profile_request($payment_m
   if (!empty($credit_card)) {
     $api_request_data['paymentProfile']['payment']['creditCard'] = $credit_card;
   }
+  
+  // Add bank account on file payment details to the
+  // default payment profile if given.
+  $bank_account = commerce_authnet_cim_bank_account_array($payment_details);
+
+  if (!empty($bank_account)) {
+    $api_request_data['paymentProfile']['payment']['bankAccount'] = $bank_account;
+  }
 
   return commerce_authnet_cim_request($payment_method, 'createCustomerPaymentProfileRequest', $api_request_data);
 }
@@ -1570,7 +1214,7 @@ function commerce_authnet_aim_server_url($txn_mode) {
   switch ($txn_mode) {
     case AUTHNET_TXN_MODE_LIVE:
     case AUTHNET_TXN_MODE_LIVE_TEST:
-      return variable_get('commerce_authnet_aim_server_url_live', 'https://secure2.authorize.net/gateway/transact.dll');
+      return variable_get('commerce_authnet_aim_server_url_live', 'https://secure.authorize.net/gateway/transact.dll');
     case AUTHNET_TXN_MODE_DEVELOPER:
       return variable_get('commerce_authnet_aim_server_url_dev', 'https://test.authorize.net/gateway/transact.dll');
   }
@@ -1725,7 +1369,7 @@ function commerce_authnet_cim_server_url($txn_mode) {
   switch ($txn_mode) {
     case AUTHNET_TXN_MODE_LIVE:
     case AUTHNET_TXN_MODE_LIVE_TEST:
-      return variable_get('commerce_authnet_cim_server_url_live', 'https://api2.authorize.net/xml/v1/request.api');
+      return variable_get('commerce_authnet_cim_server_url_live', 'https://api.authorize.net/xml/v1/request.api');
     case AUTHNET_TXN_MODE_DEVELOPER:
       return variable_get('commerce_authnet_cim_server_url_dev', 'https://apitest.authorize.net/xml/v1/request.api');
   }
@@ -1845,22 +1489,121 @@ function commerce_authnet_avs_response($code) {
   return '-';
 }
 
+
 /**
- * Returns the message text for a CVV match.
+ * Card on file callback: background charge payment
+ *
+ * @param object $payment_method
+ *  The payment method instance definition array.
+ * @param object $card_data
+ *   The stored credit card data array to be processed
+ * @param object $order
+ *   The order object that is being processed
+ * @param array $charge
+ *   The price array for the charge amount with keys of 'amount' and 'currency'
+ *   If null the total value of the order is used.
+ *
+ * @return
+ *   TRUE if the transaction was successful
  */
-function commerce_authnet_cvv_response($code) {
-  switch ($code) {
-    case 'M':
-      return t('Match');
-    case 'N':
-      return t('No Match');
-    case 'P':
-      return t('Not Processed');
-    case 'S':
-      return t('Should have been present');
-    case 'U':
-      return t('Issuer unable to process request');
+function commerce_authnet_cim_bankonfile_charge($payment_method, $check_data, $order, $charge = NULL) {
+  watchdog('MIDNET', '<pre>' . print_r(get_defined_vars(), TRUE) . '</pre>');
+  // Format order total for transaction.
+  if (!isset($charge)) {
+    $wrapper = entity_metadata_wrapper('commerce_order', $order);
+    $charge = commerce_line_items_total($wrapper->commerce_line_items);
   }
 
-  return '-';
-}
+  // Extract the Customer Profile and Payment Profile IDs from the remote_id.
+  list($cim_customer_profile_id, $cim_payment_profile_id) = explode('|', $check_data->remote_id);
+
+  // Determine the proper transaction element to use inside the XML.
+  $element_name = commerce_authnet_cim_transaction_element_name(COMMERCE_CREDIT_AUTH_CAPTURE);
+  
+
+  // Build a data array for the transaction API request.
+  $api_request_data = array(
+    'transaction' => array(
+      $element_name => array(
+        'amount' => number_format(commerce_currency_amount_to_decimal($charge['amount'], $charge['currency_code']), 2, '.', ''),
+        'customerProfileId' => $cim_customer_profile_id,
+        'customerPaymentProfileId' => $cim_payment_profile_id,
+        'order' => array(
+          'invoiceNumber' => $order->order_number,
+        ),
+      ),
+    ),
+    'extraOptions' => '<![CDATA[x_delim_data=TRUE&amp;x_delim_char=|&amp;x_encap_char="&amp;x_solution_id=A1000009&amp;x_currency_code=' . $charge['currency_code'] . '&amp;x_customer_ip=' . substr(ip_address(), 0, 15) . '&amp;x_email_customer=' . $payment_method['settings']['email_customer'] . ']]>',
+  );
+
+  // If we get a response from the API server...
+  $xml_response = commerce_authnet_cim_request($payment_method, 'createCustomerProfileTransactionRequest', $api_request_data);
+
+  if (!empty($xml_response->directResponse)) {
+    // Extract the response data from the XML.
+    $response = explode('|', (string) $xml_response->directResponse);
+
+    for ($i = 0; $i < count($response); $i++) {
+      $response[$i] = substr($response[$i], 1, strlen($response[$i]) - 2);
+    }
+
+    // Prepare a transaction object to represent the transaction attempt.
+    $transaction = commerce_payment_transaction_new($payment_method['method_id'], $order->order_id);
+    $transaction->instance_id = $payment_method['instance_id'];
+    $transaction->remote_id = $response[6];
+    $transaction->amount = $charge['amount'];
+    $transaction->currency_code = $charge['currency_code'];
+    $transaction->payload[REQUEST_TIME] = $response;
+
+    // If we didn't get an approval response code...
+    // Set transaction status from response code.
+    if ($response[0] == '1') {
+      $transaction->status = COMMERCE_PAYMENT_STATUS_SUCCESS;
+      $reason_text = t('APPROVED');
+    }
+    elseif ($response[0] == '4') {
+      $transaction->status = COMMERCE_PAYMENT_STATUS_PENDING;
+      $reason_text = t('PENDING');
+    }
+    else {
+      $transaction->status = COMMERCE_PAYMENT_STATUS_FAILURE;
+      $reason_text = t('REJECTED');
+    }
+
+    // Store the type of transaction in the remote status.
+    $transaction->remote_status = $response[11];
+
+    // Build a meaningful response message.
+    $message = array(
+      '<b>' . commerce_authnet_reverse_txn_type(commerce_authnet_txn_type($payment_method['settings']['txn_type'])) . '</b>',
+      '<b>' . ($response[0] != '1' ? t('REJECTED') : t('ACCEPTED')) . ':</b> ' . check_plain($response[3]),
+      t('AVS response: @avs', array('@avs' => commerce_authnet_avs_response($response[5]))),
+    );
+
+    $transaction->message = implode('<br />', $message);
+
+    // Save the transaction information.
+    commerce_payment_transaction_save($transaction);
+
+    // If the payment failed, display an error and rebuild the form.
+    if ($response[0] != '1') {
+      drupal_set_message(t('We received the following error processing your account. Please enter your information again or try a different account.'), 'error');
+      drupal_set_message(check_plain($response[3]), 'error');
+      return FALSE;
+    }
+
+    return (isset($transaction->status)) ? $transaction->status : TRUE;
+  }
+  elseif ((string) $xml_response->messages->message->code == 'E00040') {
+    // If the response indicated a non-existent profile, deactive it now.
+    $check_data->status = 0;
+    //commerce_cardonfile_save($card_data);
+
+    drupal_set_message(t('The account you selected is no longer valid. Please use a different account to complete payment.'), 'error');
+    return FALSE;
+  }
+
+  drupal_set_message(t('We could not process your account on file at this time. Please try again or use a different account.'), 'error');
+  return FALSE;
+
+}
\ No newline at end of file
diff --git a/includes/commerce_authnet_payment.cc.inc b/includes/commerce_authnet_payment.cc.inc
new file mode 100644
index 0000000..293f0bb
--- /dev/null
+++ b/includes/commerce_authnet_payment.cc.inc
@@ -0,0 +1,475 @@
+<?php
+/**
+ * @file
+ * Commerce Authorize.Net Credit Card Payment Methods.
+ */
+
+/**
+ * Payment method callback: settings form.
+ */
+function commerce_authnet_aim_settings_form($settings = NULL) {
+  module_load_include('inc', 'commerce_payment', 'includes/commerce_payment.credit_card');
+
+  // Merge default settings into the stored settings array.
+  $settings = (array) $settings + commerce_authnet_aim_default_settings();
+
+  $form = array();
+
+  $form['login'] = array(
+    '#type' => 'textfield',
+    '#title' => t('API Login ID'),
+    '#description' => t('Your API Login ID is different from the username you use to login to your Authorize.Net account. Once you login, browse to your Account tab and click the <em>API Login ID and Transaction Key</em> link to find your API Login ID. If you are using a new Authorize.Net account, you may still need to generate an ID.'),
+    '#default_value' => $settings['login'],
+    '#required' => TRUE,
+  );
+  $form['tran_key'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Transaction Key'),
+    '#description' => t('Your Transaction Key can be found on the same screen as your API Login ID. However, it will not be readily displayed. You must answer your security question and submit a form to see your Transaction Key.'),
+    '#default_value' => $settings['tran_key'],
+    '#required' => TRUE,
+  );
+  $form['txn_mode'] = array(
+    '#type' => 'radios',
+    '#title' => t('Transaction mode'),
+    '#description' => t('Adjust to live transactions when you are ready to start processing real payments.') . '<br />' . t('Only specify a developer test account if you login to your account through https://test.authorize.net.'),
+    '#options' => array(
+      AUTHNET_TXN_MODE_LIVE => t('Live transactions in a live account'),
+      AUTHNET_TXN_MODE_LIVE_TEST => t('Test transactions in a live account'),
+      AUTHNET_TXN_MODE_DEVELOPER => t('Developer test account transactions'),
+    ),
+    '#default_value' => $settings['txn_mode'],
+  );
+  $form['txn_type'] = array(
+    '#type' => 'radios',
+    '#title' => t('Default credit card transaction type'),
+    '#description' => t('The default will be used to process transactions during checkout.'),
+    '#options' => array(
+      COMMERCE_CREDIT_AUTH_CAPTURE => t('Authorization and capture'),
+      COMMERCE_CREDIT_AUTH_ONLY => t('Authorization only (requires manual or automated capture after checkout)'),
+    ),
+    '#default_value' => $settings['txn_type'],
+  );
+
+  $form['card_types'] = array(
+    '#type' => 'checkboxes',
+    '#title' => t('Limit accepted credit cards to the following types'),
+    '#description' => t('If you want to limit acceptable card types, you should only select those supported by your merchant account.') . '<br />' . t('If none are checked, any credit card type will be accepted.'),
+    '#options' => commerce_payment_credit_card_types(),
+    '#default_value' => $settings['card_types'],
+  );
+
+  // CIM support in conjunction with AIM requires the Card on File module.
+  if (module_exists('commerce_cardonfile')) {
+    $form['cardonfile'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Enable Card on File functionality with this payment method using Authorize.Net CIM.'),
+      '#description' => t('This requires an Authorize.Net account upgraded to include support for CIM (Customer Information Manager).'),
+      '#default_value' => $settings['cardonfile'],
+    );
+
+    $form['continuous'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Use continuous authority transactions.'),
+      '#description' => t('A continuous authority merchant account will be required.'),
+      '#default_value' => $settings['continuous'],
+    );
+  }
+
+  else {
+    $form['cardonfile'] = array(
+      '#type' => 'markup',
+      '#markup' => t('To enable Card on File funcitionality download and install the Card on File module.'),
+    );
+  }
+
+  $form['email_customer'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Tell Authorize.net to e-mail the customer a receipt based on your account settings.'),
+    '#default_value' => $settings['email_customer'],
+  );
+  $form['log'] = array(
+    '#type' => 'checkboxes',
+    '#title' => t('Log the following messages for debugging'),
+    '#options' => array(
+      'request' => t('API request messages'),
+      'response' => t('API response messages'),
+    ),
+    '#default_value' => $settings['log'],
+  );
+
+  return $form;
+}
+
+/**
+ * Payment method callback: checkout form.
+ */
+function commerce_authnet_aim_submit_form($payment_method, $pane_values, $checkout_pane, $order) {
+  module_load_include('inc', 'commerce_payment', 'includes/commerce_payment.credit_card');
+
+  // Prepare the fields to include on the credit card form.
+  $fields = array(
+    'code' => '',
+  );
+
+  // Add the credit card types array if necessary.
+  if (isset($payment_method['settings']['card_types'])) {
+    $card_types = array_diff(array_values($payment_method['settings']['card_types']), array(0));
+
+    if (!empty($card_types)) {
+      $fields['type'] = $card_types;
+    }
+  }
+
+  return commerce_payment_credit_card_form($fields);
+}
+
+/**
+ * Payment method callback: checkout form validation.
+ */
+function commerce_authnet_aim_submit_form_validate($payment_method, $pane_form, $pane_values, $order, $form_parents = array()) {
+  // If the customer specified a card on file, skip the normal validation.
+  if (module_exists('commerce_cardonfile') && !empty($payment_method['settings']['cardonfile']) &&
+    !empty($pane_values['cardonfile']) && $pane_values['cardonfile'] !== 'new') {
+    return;
+  }
+
+  module_load_include('inc', 'commerce_payment', 'includes/commerce_payment.credit_card');
+
+  // Validate the credit card fields.
+  $settings = array(
+    'form_parents' => array_merge($form_parents, array('credit_card')),
+  );
+
+  if (!commerce_payment_credit_card_validate($pane_values['credit_card'], $settings)) {
+    return FALSE;
+  }
+}
+
+/**
+ * Payment method callback: checkout form submission.
+ */
+function commerce_authnet_aim_submit_form_submit($payment_method, $pane_form, $pane_values, $order, $charge) {
+  // If Card on File is enabled and active for this payment method, then we need
+  // to figure out what to do.
+  if (module_exists('commerce_cardonfile') && $payment_method['settings']['cardonfile'] && !empty($pane_values['cardonfile'])) {
+    if (!empty($pane_values['cardonfile']) && $pane_values['cardonfile'] !== 'new') {
+      // We're using a stored payment profile. Pass it along to CIM.
+      return commerce_authnet_cim_submit_form_submit($payment_method, $pane_form, $pane_values, $order, $charge);
+    }
+    else {
+      if (!empty($pane_values['credit_card']['cardonfile_store']) && $pane_values['credit_card']['cardonfile_store']) {
+        // We've got a request to store a new card.
+        return commerce_authnet_cim_submit_new_card_form_submit($payment_method, $pane_form, $pane_values, $order, $charge);
+      }
+    }
+  }
+
+  // Determine the credit card type if possible for use in later code.
+  if (!empty($pane_values['credit_card']['number'])) {
+    module_load_include('inc', 'commerce_payment', 'includes/commerce_payment.credit_card');
+    $card_type = commerce_payment_validate_credit_card_type($pane_values['credit_card']['number'], array_keys(commerce_payment_credit_card_types()));
+  }
+
+  // If the charge amount is 0...
+  if ($charge['amount'] == 0) {
+    // Prevent the transaction except under limited circumstances.
+    $prevent_transaction = TRUE;
+
+    // Allow 0 amount authorizations on Visa cards.
+    if ($payment_method['settings']['txn_type'] == COMMERCE_CREDIT_AUTH_ONLY && $card_type == 'visa') {
+      $prevent_transaction = FALSE;
+    }
+
+    // If the transaction should still be prevented...
+    if ($prevent_transaction) {
+      // Create a transaction to log the skipped transaction and display a
+      // helpful message to the customer.
+      $transaction = commerce_payment_transaction_new('authnet_aim', $order->order_id);
+      $transaction->amount = $charge['amount'];
+      $transaction->currency_code = $charge['currency_code'];
+      $transaction->status = COMMERCE_PAYMENT_STATUS_FAILURE;
+      $transaction->message = t('Invalid @amount transaction not attempted.', array('@amount' => commerce_currency_format($charge['amount'], $charge['currency_code'])));
+      commerce_payment_transaction_save($transaction);
+
+      drupal_set_message(t('We encountered an error processing your transaction. Please contact us to resolve the issue.'), 'error');
+      return FALSE;
+    }
+  }
+
+  $order_wrapper = entity_metadata_wrapper('commerce_order', $order);
+
+  // Get the default transaction type from the payment method settings.
+  $txn_type = $payment_method['settings']['txn_type'];
+
+  // If txn_type has been specified in the pane values array, such as through
+  // the special select element we alter onto the payment terminal form, use
+  // that instead.
+  if (!empty($pane_values['txn_type'])) {
+    $txn_type = $pane_values['txn_type'];
+  }
+
+  // Build a name-value pair array for this transaction.
+  $nvp = array(
+    'x_type' => commerce_authnet_txn_type($txn_type),
+    'x_method' => 'CC',
+    'x_amount' => number_format(commerce_currency_amount_to_decimal($charge['amount'], $charge['currency_code']), 2, '.', ''),
+    'x_currency_code' => $charge['currency_code'],
+    'x_card_num' => $pane_values['credit_card']['number'],
+    'x_exp_date' => $pane_values['credit_card']['exp_month'] . $pane_values['credit_card']['exp_year'],
+  );
+
+  if (isset($pane_values['credit_card']['code'])) {
+    $nvp['x_card_code'] = $pane_values['credit_card']['code'];
+  }
+
+  // Build a description for the order.
+  $description = array();
+  // Descriptions come from products, though not all environments have them. So check first.
+  if (function_exists('commerce_product_line_item_types')) {
+    foreach ($order_wrapper->commerce_line_items as $delta => $line_item_wrapper) {
+      if (in_array($line_item_wrapper->type->value(), commerce_product_line_item_types())) {
+        $description[] = round($line_item_wrapper->quantity->value(), 2) . 'x ' . $line_item_wrapper->line_item_label->value();
+      }
+    }
+  }
+
+  // Add additional transaction information to the request array.
+  $nvp += commerce_authnet_aim_request_order_details($order);
+
+  // Submit the request to Authorize.Net.
+  $response = commerce_authnet_aim_request($payment_method, $nvp);
+
+  // Prepare a transaction object to log the API response.
+  $transaction = commerce_payment_transaction_new('authnet_aim', $order->order_id);
+  $transaction->instance_id = $payment_method['instance_id'];
+  $transaction->remote_id = $response[6];
+  $transaction->amount = $charge['amount'];
+  $transaction->currency_code = $charge['currency_code'];
+  $transaction->payload[REQUEST_TIME] = $response;
+
+  // If we didn't get an approval response code...
+  if ($response[0] != '1') {
+    // Create a failed transaction with the error message.
+    $transaction->status = COMMERCE_PAYMENT_STATUS_FAILURE;
+  }
+  else {
+    // Set the transaction status based on the type of transaction this was.
+    switch ($txn_type) {
+      case COMMERCE_CREDIT_AUTH_ONLY:
+        $transaction->status = COMMERCE_PAYMENT_STATUS_PENDING;
+        break;
+
+      case COMMERCE_CREDIT_AUTH_CAPTURE:
+        $transaction->status = COMMERCE_PAYMENT_STATUS_SUCCESS;
+        break;
+
+      case COMMERCE_CREDIT_CAPTURE_ONLY:
+        $transaction->status = COMMERCE_PAYMENT_STATUS_SUCCESS;
+        break;
+    }
+  }
+
+  // Store the type of transaction in the remote status.
+  $transaction->remote_status = $response[11];
+
+  // Build a meaningful response message.
+  $message = array(
+    '<b>' . commerce_authnet_reverse_txn_type($response[11]) . '</b>',
+    '<b>' . ($response[0] != '1' ? t('REJECTED') : t('ACCEPTED')) . ':</b> ' . check_plain($response[3]),
+    t('AVS response: @avs', array('@avs' => commerce_authnet_avs_response($response[5]))),
+  );
+
+  // Add the CVV response if enabled.
+  if (isset($nvp['x_card_code'])) {
+    $message[] = t('CVV match: @cvv', array('@cvv' => commerce_authnet_cvv_response($response[38])));
+  }
+
+  $transaction->message = implode('<br />', $message);
+
+  // Save the transaction information.
+  commerce_payment_transaction_save($transaction);
+
+  // If the payment failed, display an error and rebuild the form.
+  if ($response[0] != '1') {
+    drupal_set_message(t('We received the following error processing your card. Please enter your information again or try a different card.'), 'error');
+    drupal_set_message(check_plain($response[3]), 'error');
+    return FALSE;
+  }
+}
+
+/**
+ * Handles advanced logic relating to creating CIM orders with new card data.
+ *
+ * @see commerce_authnet_aim_submit_form_submit()
+ */
+function commerce_authnet_cim_submit_new_card_form_submit($payment_method, $pane_form, $pane_values, $order, $charge) {
+  // At this point, we have a few choices to make. We know the user is logged in
+  // and so we know if they have any existing card on file profiles. If they do
+  // not, then we can assume that they need a new profile. If they do have one,
+  // then we need take appropriate steps to add the CC info to the existing
+  // profile and process the payment based on their profile.
+  $order_wrapper = entity_metadata_wrapper('commerce_order', $order);
+
+  $payment_details = array(
+    'cardNumber' => $pane_values['credit_card']['number'],
+    'expirationDate' => $pane_values['credit_card']['exp_year'] . '-' . $pane_values['credit_card']['exp_month'],
+  );
+
+  if (isset($pane_values['credit_card']['code'])) {
+    $payment_details['cardCode'] = $pane_values['credit_card']['code'];
+  }
+
+  // Prepare the billing address for use in the request.
+  if (isset($order->commerce_customer_billing) && $order_wrapper->commerce_customer_billing->value()) {
+    $billing_address = $order_wrapper->commerce_customer_billing->commerce_customer_address->value();
+
+    if (empty($billing_address['first_name'])) {
+      $name_parts = explode(' ', $billing_address['name_line']);
+      $billing_address['first_name'] = array_shift($name_parts);
+      $billing_address['last_name'] = implode(' ', $name_parts);
+    }
+  }
+
+  $remote_id = FALSE;
+
+  // First look to see if we already have cards on file for the user.
+  $stored_cards = array();
+  if (!user_is_anonymous()) {
+    $stored_cards = commerce_cardonfile_load_multiple_by_uid($order->uid, $payment_method['instance_id']);
+  }
+  $add_to_profile = NULL;
+
+  if (empty($stored_cards)) {
+    // We do not, create the profile (which includes the payment details).
+    if ($response = commerce_authnet_cim_create_customer_profile_request($payment_method, $order, $payment_details)) {
+      // If the Customer Profile creation was a success, store the new card on
+      // file data locally.
+      if ((string) $response->messages->resultCode == 'Ok') {
+        // Build a remote ID that includes the Customer Profile ID and the
+        // Payment Profile ID.
+        $remote_id = (string) $response->customerProfileId . '|' . (string) $response->customerPaymentProfileIdList->numericString;
+      }
+      elseif ((string) $response->messages->message->code == 'E00039') {
+        // But if a Customer Profile already existed for this user, attempt
+        // instead to add this card as a new Payment Profile to it.
+        $result = array_filter(explode(' ', (string) $response->messages->message->text), 'is_numeric');
+        $add_to_profile = reset($result);
+      }
+    }
+  }
+  else {
+    // Extract the user's Customer Profile ID from the first card's remote ID.
+    $card_data = reset($stored_cards);
+    list($cim_customer_profile_id, ) = explode('|', $card_data->remote_id);
+
+    // Attempt to add the card as a new payment profile to this Customer Profile.
+    $add_to_profile = $cim_customer_profile_id;
+  }
+
+  // Attempt to add the card to an existing Customer Profile if specified.
+  if (!empty($add_to_profile)) {
+    $response = commerce_authnet_cim_create_customer_payment_profile_request($payment_method, $add_to_profile, $order, $payment_details);
+    // If the Payment Profile creation was a success, store the new card on
+    // file data locally.
+    if ((string) $response->messages->resultCode == 'Ok' || (string) $response->messages->message->code == 'E00039') {
+      // If we got a duplicate code, then the payment profile
+      // at Authorize.Net and needs to be represented locally.
+      if ((string) $response->messages->message->code == 'E00039') {
+        $cim_profile_response = commerce_authnet_cim_get_customer_profile_request($payment_method, $add_to_profile);
+        if ((string) $cim_profile_response->messages->resultCode == 'Ok') {
+          // Inspect the returned payment profiles to find the one that
+          // generated the duplicate error code.
+          $cim_payment_profiles = $cim_profile_response->profile->paymentProfiles;
+          if (!is_array($cim_payment_profiles)) {
+            $cim_payment_profiles = array($cim_payment_profiles);
+          }
+
+          foreach ($cim_payment_profiles as $key => $payment_profile) {
+            // We match the submitted values against the existing payment
+            // profiles using the last 4 digits of the card number. This could
+            // potentially create a conflict if the same customer has two
+            // different cards that end in the same four digits, but that is
+            // highly unlikely.
+            if (substr($pane_values['credit_card']['number'], -4) == substr($payment_profile->payment->creditCard->cardNumber, -4)) {
+              $payment_profile_id = (string) $payment_profile->customerPaymentProfileId;
+              break;
+            }
+          }
+        }
+      }
+      else {
+        $payment_profile_id = (string) $response->customerPaymentProfileId;
+      }
+
+      // Build a remote ID that includes the customer profile ID and the new
+      // or existing payment profile ID. We don't do any check here to ensure
+      // we found a payment profile ID, as we shouldn't have got a duplicate
+      // error if it didn't actually exist.
+      $remote_id = $add_to_profile . '|' . $payment_profile_id;
+    }
+    elseif ($response->messages->message->code == 'E00040') {
+      // But if we could not find a customer profile, create a new one.
+      if ($response = commerce_authnet_cim_create_customer_profile_request($payment_method, $order, $payment_details)) {
+        // If the Customer Profile creation was a success, store the new card on
+        // file data locally.
+        if ((string) $response->messages->resultCode == 'Ok') {
+          // Build a remote ID that includes the Customer Profile ID and the
+          // Payment Profile ID.
+          $remote_id = (string) $response->customerProfileId . '|' . (string) $response->customerPaymentProfileIdList->numericString;
+        }
+      }
+    }
+  }
+
+  // We couldn't store a profile. Abandon ship!
+  if (!$remote_id) {
+    return FALSE;
+  }
+
+  // Build our card for storing with card on file.
+  $card_data = commerce_cardonfile_new();
+  $card_data->uid = $order->uid;
+  $card_data->payment_method = $payment_method['method_id'];
+  $card_data->instance_id = $payment_method['instance_id'];
+  $card_data->remote_id = $remote_id;
+  $card_data->card_type = !empty($card_type) ? $card_type : 'card';
+  $card_data->card_name = !empty($billing_address['name_line']) ? $billing_address['name_line'] : '';
+  $card_data->card_number = substr($pane_values['credit_card']['number'], -4);
+  $card_data->card_exp_month = $pane_values['credit_card']['exp_month'];
+  $card_data->card_exp_year = $pane_values['credit_card']['exp_year'];
+  $card_data->status = 1;
+
+  // Save and log the creation of the new card on file.
+  commerce_cardonfile_save($card_data);
+
+  if ($add_to_profile) {
+    watchdog('commerce_authnet', 'CIM Payment Profile added to Customer Profile @profile_id for user @uid.', array('@profile_id' => $add_to_profile, '@uid' => $order->uid));
+  }
+  else {
+    watchdog('commerce_authnet', 'CIM Customer Profile @profile_id created and saved to user @uid.', array('@profile_id' => (string) $response->customerProfileId, '@uid' => $order->uid));
+  }
+
+  // Process the payment
+  return commerce_authnet_cim_cardonfile_charge($payment_method, $card_data, $order, $charge);
+}
+
+/**
+ * Returns the message text for a CVV match.
+ */
+function commerce_authnet_cvv_response($code) {
+  switch ($code) {
+    case 'M':
+      return t('Match');
+    case 'N':
+      return t('No Match');
+    case 'P':
+      return t('Not Processed');
+    case 'S':
+      return t('Should have been present');
+    case 'U':
+      return t('Issuer unable to process request');
+  }
+
+  return '-';
+}
diff --git a/includes/commerce_authnet_payment.echeck.inc b/includes/commerce_authnet_payment.echeck.inc
new file mode 100644
index 0000000..5dcfc2d
--- /dev/null
+++ b/includes/commerce_authnet_payment.echeck.inc
@@ -0,0 +1,359 @@
+<?php
+/**
+ * @file
+ * Commerce Authorize.Net eCheck Payment Methods.
+ */
+
+/**
+ * Payment method callback: settings form.
+ */
+function commerce_authnet_echeck_settings_form($settings = NULL) {
+  $form = array();
+
+  // Merge default settings into the stored settings array.
+  $settings = (array) $settings + array(
+    'login' => '',
+    'tran_key' => '',
+    'txn_mode' => AUTHNET_TXN_MODE_LIVE_TEST,
+    'email_customer' => FALSE,
+    'log' => array('request' => '0', 'response' => '0'),
+  );
+
+  $form['login'] = array(
+    '#type' => 'textfield',
+    '#title' => t('API Login ID'),
+    '#description' => t('Where this can be found.'),
+    '#default_value' => $settings['login'],
+    '#required' => TRUE,
+  );
+
+  $form['tran_key'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Transaction Key'),
+    '#description' => t('Where this can be found.'),
+    '#default_value' => $settings['tran_key'],
+    '#required' => TRUE,
+  );
+
+  $form['txn_type'] = array(
+    '#type' => 'radios',
+    '#title' => t('Default credit card transaction type'),
+    '#description' => t('The default will be used to process transactions during checkout.'),
+    '#options' => array(
+      COMMERCE_CREDIT_AUTH_CAPTURE => t('Authorization and capture'),
+      COMMERCE_CREDIT_AUTH_ONLY => t('Authorization only (requires manual or automated capture after checkout)'),
+    ),
+    '#default_value' => $settings['txn_type'],
+  );
+
+  $form['txn_mode'] = array(
+    '#type' => 'radios',
+    '#title' => t('Transaction mode'),
+    '#description' => t('Adjust to live transactions when you are ready to start processing real payments.') . '<br />' . t('Only specify a developer test account if you login to your account through https://test.authorize.net.'),
+    '#options' => array(
+      AUTHNET_TXN_MODE_LIVE => t('Live transactions in a live account'),
+      AUTHNET_TXN_MODE_LIVE_TEST => t('Test transactions in a live account'),
+      AUTHNET_TXN_MODE_DEVELOPER => t('Developer test account transactions'),
+    ),
+    '#default_value' => $settings['txn_mode'],
+  );
+
+  $form['email_customer'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Tell Authorize.net to e-mail the customer a receipt based on your account settings.'),
+    '#default_value' => $settings['email_customer'],
+  );
+
+  $form['log'] = array(
+    '#type' => 'checkboxes',
+    '#title' => t('Log the following messages for debugging'),
+    '#options' => array(
+      'request' => t('API request messages'),
+      'response' => t('API response messages'),
+    ),
+    '#default_value' => $settings['log'],
+  );
+
+  return $form;
+}
+
+/**
+ * Payment method callback: checkout form.
+ */
+function commerce_authnet_echeck_submit_form($payment_method, $pane_values, $checkout_pane, $order) {
+  module_load_include('inc', 'commerce_payment', 'includes/commerce_payment.echeck');
+
+  $fields = drupal_map_assoc(array('bank_name', 'acct_name'));
+  $fields['type'] = drupal_map_assoc(array('checking', 'business_checking', 'savings'));
+
+  $form = commerce_payment_echeck_form($fields);
+  $form['echeck']['bank_name']['#maxlength'] = 50;
+  $form['echeck']['acct_name']['#maxlength'] = 50;
+
+  return $form;
+}
+
+/**
+ * Payment method callback: checkout form validation.
+ */
+function commerce_authnet_echeck_submit_form_validate($payment_method, $pane_form, $pane_values, $order, $form_parents = array()) {
+  module_load_include('inc', 'commerce_payment', 'includes/commerce_payment.echeck');
+
+  // Validate the credit card fields.
+  $settings = array(
+    'form_parents' => array_merge($form_parents, array('echeck')),
+  );
+
+  // Make sure the echeck pane is accessible.
+  if ($pane_form['echeck']['#access'] && !commerce_payment_echeck_validate($pane_values['echeck'], $settings)) {
+    return FALSE;
+  }
+}
+
+/**
+ * Payment method callback: checkout form submission.
+ */
+function commerce_authnet_echeck_submit_form_submit($payment_method, $pane_form, $pane_values, $order, $charge) {
+  // If Card on File is enabled and active for this payment method, then we need
+  // to figure out what to do.
+  if (module_exists('commerce_bank_account_onfile') && $payment_method['settings']['bank_account_onfile'] && !empty($pane_values['bank_account_onfile'])) {
+    if (!empty($pane_values['bank_account_onfile']) && $pane_values['bank_account_onfile'] !== 'new') {
+      // We're using a stored payment profile. Pass it along to CIM.
+      return commerce_authnet_cim_submit_form_submit($payment_method, $pane_form, $pane_values, $order, $charge);
+    }
+    else {
+      if (!empty($pane_values['echeck']['bank_account_store']) && $pane_values['echeck']['bank_account_store']) {
+        // We've got a request to store a new card.
+        return commerce_authnet_cim_submit_new_echeck_form_submit($payment_method, $pane_form, $pane_values, $order, $charge);
+      }
+    }
+  }
+  
+  // Build a name-value pair array for this transaction.
+  $nvp = array(
+    'x_method' => 'ECHECK',
+    'x_bank_aba_code' => $pane_values['echeck']['aba_code'],
+    'x_bank_acct_num' => $pane_values['echeck']['acct_num'],
+    'x_bank_acct_type' => str_replace('', '_', strtoupper($pane_values['echeck']['type'])),
+    'x_bank_name' => substr($pane_values['echeck']['bank_name'], 0, 50),
+    'x_bank_acct_name' => substr($pane_values['echeck']['acct_name'], 0, 50),
+    'x_echeck_type' => 'WEB',
+    'x_recurring_billing' => 'FALSE',
+    'x_amount' => commerce_currency_amount_to_decimal($charge['amount'], $charge['currency_code']),
+  );
+
+  // Add additional transaction invormation to the request array.
+  $nvp += commerce_authnet_aim_request_order_details($order);
+
+  // Submit the request to Authorize.Net.
+  $response = commerce_authnet_aim_request($payment_method, $nvp);
+
+  // Prepare a transaction object to log the API response.
+  $transaction = commerce_payment_transaction_new($payment_method['method_id'], $order->order_id);
+  $transaction->instance_id = $payment_method['instance_id'];
+  $transaction->remote_id = $response[6];
+  $transaction->amount = $charge['amount'];
+  $transaction->currency_code = $charge['currency_code'];
+  $transaction->payload[REQUEST_TIME] = $response;
+
+  // Set transaction status from response code.
+  if ($response[0] == '1') {
+    $transaction->status = COMMERCE_PAYMENT_STATUS_SUCCESS;
+    $reason_text = t('APPROVED');
+  }
+  elseif ($response[0] == '4') {
+    $transaction->status = COMMERCE_PAYMENT_STATUS_PENDING;
+    $reason_text = t('PENDING');
+  }
+  else {
+    $transaction->status = COMMERCE_PAYMENT_STATUS_FAILURE;
+    $reason_text = t('REJECTED');
+  }
+
+  // Store the type of transaction in the remote status.
+  $transaction->remote_status = $response[11];
+
+  // Build a meaningful response message.
+  $message = array(
+    '<b>ECHECK</b>',
+    '<b>' . $reason_text . ':</b> ' . check_plain($response[3]),
+  );
+
+  // Add the CVV response if enabled.
+  if (!empty($respnsee[6])) {
+    $message[] = t('Transaction ID: @txn_id', array('@txn_id' => $response[6]));
+  }
+
+  $transaction->message = implode('<br />', $message);
+
+  // Save the transaction information.
+  commerce_payment_transaction_save($transaction);
+
+  // If the payment failed, display an error and rebuild the form.
+  if ($response[0] == '4') {
+    drupal_set_message(t('We received the following notice processing your eCheck. Please contact support.'), 'warning');
+    drupal_set_message(check_plain($response[3]), 'warning');
+  }
+  elseif ($response[0] != '1') {
+    drupal_set_message(t('We received the following error processing your eCheck. Please enter your information again or try a different account.'), 'error');
+    drupal_set_message(check_plain($response[3]), 'error');
+    return FALSE;
+  }
+}
+
+
+
+/**
+ * Handles advanced logic relating to creating CIM orders with new card data.
+ *
+ * @see commerce_authnet_aim_submit_form_submit()
+ */
+function commerce_authnet_cim_submit_new_echeck_form_submit($payment_method, $pane_form, $pane_values, $order, $charge) {
+  // At this point, we have a few choices to make. We know the user is logged in
+  // and so we know if they have any existing card on file profiles. If they do
+  // not, then we can assume that they need a new profile. If they do have one,
+  // then we need take appropriate steps to add the CC info to the existing
+  // profile and process the payment based on their profile.
+  $order_wrapper = entity_metadata_wrapper('commerce_order', $order);
+
+  $payment_details = array(
+    'routingNumber' => $pane_values['echeck']['aba_code'],
+    'accountNumber' => $pane_values['echeck']['acct_num'],
+    'accountType' => ($pane_values['echeck']['type'] == 'business_checking' ? 'businessChecking' : $pane_values['echeck']['type']),
+    'bankName' => $pane_values['echeck']['bank_name'],
+    'nameOnAccount' => $pane_values['echeck']['acct_name'],
+  );
+
+  // Prepare the billing address for use in the request.
+  if (isset($order->commerce_customer_billing) && $order_wrapper->commerce_customer_billing->value()) {
+    $billing_address = $order_wrapper->commerce_customer_billing->commerce_customer_address->value();
+
+    if (empty($billing_address['first_name'])) {
+      $name_parts = explode(' ', $billing_address['name_line']);
+      $billing_address['first_name'] = array_shift($name_parts);
+      $billing_address['last_name'] = implode(' ', $name_parts);
+    }
+  }
+
+  $remote_id = FALSE;
+
+  // First look to see if we already have cards on file for the user.
+  $stored_accounts = array();
+  if (!user_is_anonymous()) {
+    $stored_accounts = commerce_bank_account_onfile_load_multiple_by_uid($order->uid, $payment_method['instance_id']);
+  }
+  $add_to_profile = NULL;
+
+  if (empty($stored_accounts)) {
+    // We do not, create the profile (which includes the payment details).
+    if ($response = commerce_authnet_cim_create_customer_profile_request($payment_method, $order, $payment_details)) {
+      // If the Customer Profile creation was a success, store the new card on
+      // file data locally.
+      if ((string) $response->messages->resultCode == 'Ok') {
+        // Build a remote ID that includes the Customer Profile ID and the
+        // Payment Profile ID.
+        $remote_id = (string) $response->customerProfileId . '|' . (string) $response->customerPaymentProfileIdList->numericString;
+      }
+      elseif ((string) $response->messages->message->code == 'E00039') {
+        // But if a Customer Profile already existed for this user, attempt
+        // instead to add this card as a new Payment Profile to it.
+        $result = array_filter(explode(' ', (string) $response->messages->message->text), 'is_numeric');
+        $add_to_profile = reset($result);
+      }
+    }
+  }
+  else {
+    // Extract the user's Customer Profile ID from the first card's remote ID.
+    $account_data = reset($stored_accounts);
+    list($cim_customer_profile_id, ) = explode('|', $account_data->remote_id);
+
+    // Attempt to add the card as a new payment profile to this Customer Profile.
+    $add_to_profile = $cim_customer_profile_id;
+  }
+
+  // Attempt to add the card to an existing Customer Profile if specified.
+  if (!empty($add_to_profile)) {
+    $response = commerce_authnet_cim_create_customer_payment_profile_request($payment_method, $add_to_profile, $order, $payment_details);
+    // If the Payment Profile creation was a success, store the new card on
+    // file data locally.
+    if ((string) $response->messages->resultCode == 'Ok' || (string) $response->messages->message->code == 'E00039') {
+      // If we got a duplicate code, then the payment profile
+      // at Authorize.Net and needs to be represented locally.
+      if ((string) $response->messages->message->code == 'E00039') {
+        $cim_profile_response = commerce_authnet_cim_get_customer_profile_request($payment_method, $add_to_profile);
+        if ((string) $cim_profile_response->messages->resultCode == 'Ok') {
+          // Inspect the returned payment profiles to find the one that
+          // generated the duplicate error code.
+          $cim_payment_profiles = $cim_profile_response->profile->paymentProfiles;
+          if (!is_array($cim_payment_profiles)) {
+            $cim_payment_profiles = array($cim_payment_profiles);
+          }
+
+          foreach ($cim_payment_profiles as $key => $payment_profile) {
+            // We match the submitted values against the existing payment
+            // profiles using the last 4 digits of the card number. This could
+            // potentially create a conflict if the same customer has two
+            // different cards that end in the same four digits, but that is
+            // highly unlikely.
+            if (substr($pane_values['echeck']['acct_num'], -4) == substr($payment_profile->payment->bankAccount->accountNumber, -4)) {
+              $payment_profile_id = (string) $payment_profile->customerPaymentProfileId;
+              break;
+            }
+          }
+        }
+      }
+      else {
+        $payment_profile_id = (string) $response->customerPaymentProfileId;
+      }
+
+      // Build a remote ID that includes the customer profile ID and the new
+      // or existing payment profile ID. We don't do any check here to ensure
+      // we found a payment profile ID, as we shouldn't have got a duplicate
+      // error if it didn't actually exist.
+      $remote_id = $add_to_profile . '|' . $payment_profile_id;
+    }
+    elseif ($response->messages->message->code == 'E00040') {
+      // But if we could not find a customer profile, create a new one.
+      if ($response = commerce_authnet_cim_create_customer_profile_request($payment_method, $order, $payment_details)) {
+        // If the Customer Profile creation was a success, store the new card on
+        // file data locally.
+        if ((string) $response->messages->resultCode == 'Ok') {
+          // Build a remote ID that includes the Customer Profile ID and the
+          // Payment Profile ID.
+          $remote_id = (string) $response->customerProfileId . '|' . (string) $response->customerPaymentProfileIdList->numericString;
+        }
+      }
+    }
+  }
+
+  // We couldn't store a profile. Abandon ship!
+  if (!$remote_id) {
+    return FALSE;
+  }
+
+  // Build our account for storage.
+  $account_data = commerce_bank_account_onfile_new();
+  $account_data->payment_method = $payment_method['method_id'];
+  $account_data->instance_id = $payment_method['instance_id'];
+  $account_data->uid = $order->uid;
+  $account_data->remote_id = $remote_id;
+  $account_data->bank_account_type = $pane_values['echeck']['type'];
+  $account_data->bank_name = $pane_values['echeck']['bank_name'];
+  $account_data->bank_account_name = $pane_values['echeck']['acct_name'];
+  $account_data->aba_code = substr($pane_values['echeck']['aba_code'], -4);
+  $account_data->bank_account_number = substr($pane_values['echeck']['acct_num'], -4);
+  $account_data->instance_default = $pane_value['bank_account_instance_default'];
+
+  // Save and log the creation of the new card on file.
+  commerce_bank_account_onfile_save($account_data);
+
+  if ($add_to_profile) {
+    watchdog('commerce_authnet', 'CIM Payment Profile added to Customer Profile @profile_id for user @uid.', array('@profile_id' => $add_to_profile, '@uid' => $order->uid));
+  }
+  else {
+    watchdog('commerce_authnet', 'CIM Customer Profile @profile_id created and saved to user @uid.', array('@profile_id' => (string) $response->customerProfileId, '@uid' => $order->uid));
+  }
+
+  // Process the payment
+  return commerce_bank_account_onfile_cim_charge($payment_method, $account_data, $order, $charge);
+}
\ No newline at end of file
