diff --git a/authorizenetwebform.module b/authorizenetwebform.module
index 91f7f74..bd15768 100644
--- a/authorizenetwebform.module
+++ b/authorizenetwebform.module
@@ -1,5 +1,8 @@
 <?php 
 
+// define('AUTHORIZENETWEBFORM_MODE','test');
+define('AUTHORIZENETWEBFORM_MODE','live');
+
 /**
  * @file
  * Authorize.Net Webform Module File
@@ -26,7 +29,15 @@ function authorizenetwebform_form_alter(&$form, &$form_state, $form_id) {
     // only deal with this form if it is supposed to go to Authorize.Net
     if ( empty($node->use_authorizenet) ) {
       return ;
-    }
+		} else {
+			// new validation handler
+			$form['#validate'][] = 'authorizenetwebform_validate';
+
+			// add new submit handler. based on webform_php model
+			// adding it as *second* handler (not sure why...)
+			$first = array_shift($form['#submit']);
+			array_unshift($form['#submit'], $first, 'authorizenetwebform_submit');
+		}
   }
   
   // handle editing of webform nodes
@@ -45,13 +56,13 @@ function authorizenetwebform_form_alter(&$form, &$form_state, $form_id) {
       );
     
       $authorizenet_form['authorizenetsettings']['use_authorizenet'] = array(
-      '#type' => 'radios',
-      '#title' => t('Allow Payment via Authorize.Net'),
-      '#options' => array(
-        1 => t('Yes'), 0 => t('No')
-      ),
-      '#description' => t('If yes, the form\'s results will be processed via Authorize.Net.  The credit card enetered will be validated & authorized before the form is formally submitted.'),
-      '#default_value' => ($form['#node']->use_authorizenet) ? $form['#node']->use_authorizenet : 0,
+        '#type' => 'radios',
+        '#title' => t('Allow Payment via Authorize.Net'),
+        '#options' => array(
+          1 => t('Yes'), 0 => t('No')
+        ),
+        '#description' => t('If yes, the form\'s results will be processed via Authorize.Net.  The credit card enetered will be validated & authorized before the form is formally submitted.'),
+        '#default_value' => ($form['#node']->use_authorizenet) ? $form['#node']->use_authorizenet : 0,
       );
       /* End Authorize.Net settings form */
 
@@ -67,9 +78,17 @@ function authorizenetwebform_form_alter(&$form, &$form_state, $form_id) {
     if ( $form['#parameters'][2]->use_authorizenet == 1 ) {
       module_load_include('inc', 'authorizenetwebform', 'authorizenetwebform_fields');
       foreach (authorizenetwebform_available_fields() as $anwafkey => $anwafvalue) {
-      $anwafkey = strtolower($anwafkey);
-      $anwaf_array[$anwafkey] = $anwafvalue;
+				$anwafkey = strtolower($anwafkey);
+				$anwaf_array[$anwafkey] = $anwafvalue;
       }
+			$variable = 'authorizenetwebform_key_map_' . $form['nid']['#value'];
+			$map = variable_get($variable,array());
+			$form_key = $form['form_key']['#default_value'];
+			if(array_key_exists($form_key,$map)) {
+				$selected = $map[$form_key];
+			} else {
+				$selected = '';
+			}
       $valid_fields = array_merge($anwaf_array, authorizenetwebform_load_custom_fields('webform'));
       $anet_form_key = array(
         '#type' => 'select',
@@ -78,15 +97,29 @@ function authorizenetwebform_form_alter(&$form, &$form_state, $form_id) {
         '#options' => $valid_fields,
         '#description' => t('Select an Authorize.Net field that this form field will map to.  If you switch the form to an email for this will be used as the machine readable key.'),
         '#weight' => $form['field']['form_key']['#weight'],
-        '#default_value' => empty($form['#parameters'][3]['form_key']) ? '' : $form['#parameters'][3]['form_key'],
+        '#default_value' => $selected,
       ); 
-      $form['advanced']['form_key'] = $anet_form_key;
+			$form['#submit'][] = 'authorizenet_field_key_submit';
+      $form['advanced']['anet_field_key'] = $anet_form_key;
       $form['advanced']['#collapsed'] = FALSE;
     }
   }
  }
 
 /**
+ * submit function for storing the field key mapping
+ */
+function authorizenet_field_key_submit($form,&$form_state) {
+	$variable = 'authorizenetwebform_key_map_' . $form_state['values']['nid'];
+	$map = variable_get($variable,array());
+	$values = $form_state['values'];
+	$form_key = $values['form_key'];
+	$anet_form_key = $values['advanced']['anet_field_key'];
+	$map[$form_key] = $anet_form_key;
+	variable_set($variable,$map);
+}
+
+/**
  * Implementation of hook_nodeapi()
  *
  * Intercept operations on the webform node to assure that the Authorize.net fields are tracked.
@@ -219,6 +252,32 @@ function authorizenet_create_custom_field_spec($custom_fields) {
   return $spec;
 }
 
+/**
+ * Return an array of all entries submitted, but with the
+ * key replaced based on the authorizenet mapping
+ * for the web form
+ **/
+function authorizenetwebform_translate_keys($submitted,$map) {
+  $ret = array();
+  reset($submitted);
+  
+  while(list($k,$v) = each($submitted)) {
+    if (is_array($v)) {
+      foreach ($v as $key => $value) {
+        if (array_key_exists($key, $map)) {
+          $ret[$map[$key]] = $value;
+        }
+      }
+    } else {
+      if(array_key_exists($k,$map)) {
+        $k = $map[$k];
+      }
+      $ret[$k] = $v;
+    }
+  }
+  return $ret;
+}
+
 /* 
  * Process a webform submission through Authorize.Net
  *
@@ -235,67 +294,61 @@ function authorizenet_create_custom_field_spec($custom_fields) {
  *  A form state array
  */
 function authorizenetwebform_process($step, $node, $form, $form_state) {
-  module_load_include('inc', 'authorizenetwebform', '/authorizenetwebform_fields');
-  $form_values = $form_state['values'];
-
   // DO NOT forward to Authorize.net if there are errors in the form validation
   if (form_get_errors()) {
     return $form_state;
   }
+  module_load_include('inc', 'authorizenetwebform', '/authorizenetwebform_fields');
   
-  $wfkeys = array();
-  $wfkeys[0] = "";
-  //Not the greatest way to store the transaction id (kind of hackish), but the best we can do until #288199 is fixed.
-  if ($form_values['submitted_tree']['x_trans_id'] == "" && $step == "submit") {
-    $form_values['submitted_tree']['x_trans_id'] = $_SESSION['anwf_trans_id'];
-    unset($_SESSION['anwf_trans_id']);
+  // store values in our own private variable 
+  $form_values = $form_state['values'];
+
+  // Flatten the user-submitted data (in case the data is nested within fieldsets).
+  $submitted_data = _array_flatten($form_state['values']['submitted'], true);
+
+  // get key mappings for this webform
+  $nid = $form_values['details']['nid'];
+  $variable = 'authorizenetwebform_key_map_' . $nid;
+  $map = variable_get($variable,array());
+  
+  // we also want to access keys in the other direction
+  $flipped_map = array_flip($map);
+  
+  // remove any non-numeric characters from expiration date
+  // like / or -
+  $exp_date_key = $flipped_map['x_exp_date'];
+  if(array_key_exists($exp_date_key, $form_values['submitted'])) {
+    $submitted_data[$exp_date_key] = preg_replace('/[^0-9]+/', '', $submitted_data[$exp_date_key]);
   }
   
-  foreach ($form_values['submitted_tree'] as $key => $value) {
-    $wfkeys[] = $key;
+  // get an array with the webform keys replaced with the authorizenet keys
+  $submitted = authorizenetwebform_translate_keys($submitted_data, $map);
+  
+  // Not the greatest way to store the transaction id (kind of hackish), but the best we can do until #288199 is fixed.
+  if ($submitted['x_trans_id'] == "" && $step == "submit") {
+    $submitted['x_trans_id'] = $_SESSION['anwf_trans_id'];
+    unset($_SESSION['anwf_trans_id']);
   }
   
-  //Map the webform fields to the pre-defined Authorize.Net fields
+  // Build a submission array that will be sent to authorizenet
   $master_fields = array_merge(authorizenetwebform_available_fields(), authorizenetwebform_load_custom_fields(NULL));
   $submission = array();
   foreach ($master_fields as $mkey => $mvalue) {
-    if (array_key_exists(strtolower($mkey), $form_values['submitted_tree'])) {
-      $submission[$mkey] = $form_values['submitted_tree'][strtolower($mkey)];
-    }
-    else {
-      foreach ($form_values['submitted_tree'] as $fkey => $fvalue) {
-        if (is_array($fvalue)) {
-          if (array_key_exists(strtolower($mkey), $fvalue)) {
-            $submission[$mkey] = $form_values['submitted_tree'][$fkey][strtolower($mkey)];
-          }
-          else {
-            foreach ($fvalue as $gkey => $gvalue) {
-              if (is_array($gvalue)) {
-                if (array_key_exists(strtolower($mkey), $gvalue)) {
-                  $submission[$mkey] = $form_values['submitted_tree'][$fkey][$gkey][strtolower($mkey)];
-                }
-                else {
-                 foreach ($gvalue as $hkey => $hvalue) {
-                   if (is_array($hvalue)) {
-                     if (array_key_exists(strtolower($mkey), $hvalue)) {
-                       $submission[$mkey] = $form_values['submitted_tree'][$fkey][$gkey][$hkey][strtolower($mkey)];
-                     }
-                   }
-                 }
-               }
-              }
-            }
-          }
-        }
-      }
+    if (array_key_exists(strtolower($mkey), $submitted)) {
+      $submission[$mkey] = $submitted[strtolower($mkey)];
     }
   }
+  
+  if (isset($submission['anwf_quantity']) && is_numeric($submission['anwf_quantity'])) {
+    $submission['x_amount'] = ((double) $submission['x_amount']) * ((double) $submission['anwf_quantity']);
+  }
   $post_vars = array();
   foreach ($submission as $key => $value) {
     if (strcmp(substr($key, 0, 2), "x_") == 0 && $value != "") {
       $post_vars[$key] = $value;
     }
   }
+  
   // Authorize.Net required field information
   $posturl = variable_get('authorizenetwebform_url', NULL);
   $post_url = ($posturl == "main") ? "https://secure.authorize.net/gateway/transact.dll" : "https://test.authorize.net/gateway/transact.dll";
@@ -306,28 +359,25 @@ function authorizenetwebform_process($step, $node, $form, $form_state) {
   $post_vars['x_delim_char']    = "|";
   $post_vars['x_relay_response']  = "FALSE";
   $post_vars['x_method'] = "CC";
+  if(AUTHORIZENETWEBFORM_MODE == 'test') {
+    $post_vars['x_test_request '] = "TRUE";
+  }
   
   if ($step == "validate") {
     $post_vars['x_type'] = 'AUTH_ONLY';
   }
   if ($step == "submit") {
-    if (!array_key_exists('x_trans_id', $post_vars)) {
-      //There was a problem getting the Auth previously, so we need to auth & capture in one step.
-      $post_vars['x_type'] = 'AUTH_CAPTURE';
-    }
-    else {
-      $post_vars['x_type'] = 'PRIOR_AUTH_CAPTURE';
-    }
+    // If there was a problem getting the Auth previously, we need to auth & capture in one step.
+    $post_vars['x_type'] = !array_key_exists('x_trans_id', $post_vars) ? 'AUTH_CAPTURE' : 'PRIOR_AUTH_CAPTURE';
   }
   
-  
   // This section takes the input fields and converts them to the proper format
   // for an http post.  For example: "x_login=username&x_tran_key=a1B2c3D4"
   $post_string = "";
   foreach ( $post_vars as $key => $value ) { 
-    $post_string .= "$key=". urlencode( $value ) ."&"; 
+    $post_string .= "$key=" . urlencode($value) . "&"; 
   } 
-  $post_string = rtrim( $post_string, "& " );
+  $post_string = rtrim($post_string, "& ");
   $request = curl_init($post_url); // initiate curl object
     curl_setopt($request, CURLOPT_HEADER, 0); // set to 0 to eliminate header info from response
     curl_setopt($request, CURLOPT_RETURNTRANSFER, 1); // Returns response data instead of TRUE(1)
@@ -346,53 +396,108 @@ function authorizenetwebform_process($step, $node, $form, $form_state) {
       form_set_error('submitted][x_card_num', t('There was an error processing your credit card: %anetresponse.  If the error persists, please try another card.', array("%anetresponse" => $response_array[3])));
     }
     if ($response_array[6] != "") {
-      //We have a transaction ID.  Set it in the webform
-      $form_values['submitted_tree']['x_trans_id'] = $response_array[6];
-      $wfkeynum = webform_get_cid($node, 'x_trans_id', 0);
-      $form_values['submitted'][$wfkeynum] = $form_values['submitted_tree']['x_trans_id'];
+      // Setting items in the original form_values to replace.
+      $replacements = array(
+        $flipped_map['x_trans_id'] => $response_array[6],
+      );
+
       //Not the greatest way to store the transaction id (kind of hackish), but the best we can do until #288199 is fixed.
-      $_SESSION['anwf_trans_id'] = $form_values['submitted_tree']['x_trans_id'];
+      $_SESSION['anwf_trans_id'] = $response_array[6];
     }  
   }
   if  ($step == "submit") {
     if ($response_array[2] > 1) {
       watchdog('authorizenetwebform', 'Error response from Authorize.net: %resposne', array("%resposne" => "<pre>". print_r($response_array, TRUE) ."</pre>"));
       //There was some type of error getting an authorization.  Flag it
-      form_set_error('submitted][x_card_num', t('There was an error processing your credit card: %anetresponse.  If the error persists, please try another card.', array("%anetresponse" => $response_array[3])));
+      $cc_key = $flipped_map['x_card_num'];
+      $form_key = "submitted][$cc_key";
+      form_set_error($form_key, t('There was an error processing your credit card: %anetresponse.  If the error persists, please try another card.', array("%anetresponse" => $response_array[3])));
     }
-    else {
-      //blank out all but the last 4 CC #'s
-      $ccsize = strlen($form_values['submitted_tree']['x_card_num']);
-      $cclen = $ccsize - 4;
-      for ($i = 0; $i < $cclen; $i++) {
-        $form_values['submitted_tree']['x_card_num'][$i] = 'x';
-      }
-      $wfkeynum = webform_get_cid($node, 'x_card_num', 0);
-      $form_values['submitted'][$wfkeynum] = $form_values['submitted_tree']['x_card_num'];
-      
-      //blank out the expiration date as well
-      $form_values['submitted_tree']['x_exp_date'] = 'xx/xx';
-      $wfkeynum = webform_get_cid($node, 'x_exp_date', 0);
-      $form_values['submitted'][$wfkeynum] = $form_values['submitted_tree']['x_exp_date'];
-      
-      //blank out the security code as well - it's an optional field, so check if it's set first.
-      if (isset($form_values['submitted_tree']['x_card_code'])) {
-        $form_values['submitted_tree']['x_card_code'] = 'xxx';
-        $wfkeynum = webform_get_cid($node, 'x_card_code', 0);
-        $form_values['submitted'][$wfkeynum] = $form_values['submitted_tree']['x_card_code'];
-      }
       
-      if ($response_array[6] != "") {
-        //We have a transaction ID.  Set it in the webform
-        $form_values['submitted_tree']['x_trans_id'] = $response_array[6];
-        $wfkeynum = webform_get_cid($node, 'x_trans_id', 0);
-        $form_values['submitted'][$wfkeynum] = $form_values['submitted_tree']['x_trans_id'];
-      }
+    $replacements = array();
+    $cids = flatten_array_by_cids($node);
+    
+    //blank out all but the last 4 CC #'s
+    $cc = $submitted[$cids[$flipped_map['x_card_num']]];
+    $cclen = strlen($cc);
+    $end = $cclen - 4;
+    $anon_cc = preg_replace("/^[0-9]{0,$end}/",'xxxxxxxxx',$cc);
+    $replacements[$cids[$flipped_map['x_card_num']]] = $anon_cc;
+    
+    //blank out the expiration date as well
+    $replacements[$cids[$flipped_map['x_exp_date']]] = 'xxxxxx';
+    
+    //blank out the security code as well
+    $replacements[$cids[$flipped_map['x_card_code']]] = 'xxx';
+    
+    if ($response_array[6] != "") {
+      //We have a transaction ID.  Set it in the webform
+      $replacements[$cids[$flipped_map['x_trans_id']]] = $response_array[6];
     }
   }
 
+  // Replace the old values with the new ones.
+  array_walk_recursive($form_values['submitted'], '_replace_value_in_recursive', $replacements);
   //Return our temporary values to the $form_state array
   $form_state['values'] = $form_values;
   
   return $form_state;
 }
\ No newline at end of file
+
+/**
+ * Code to execute when webform is validated 
+ **/
+function authorizenetwebform_validate($form, &$form_state) {
+	$node = node_load($form_state['values']['details']['nid']);
+	$form_state = authorizenetwebform_process("validate", $node, $form_id, $form_state);
+}
+
+/**
+ * Code to execute when webform is submitted
+ **/
+function authorizenetwebform_submit($form, &$form_state) {
+	$node = node_load($form_state['values']['details']['nid']);
+	$form_state =  authorizenetwebform_process("submit", $node, $form_id, $form_state);
+}
+
+/**
+ * Utility function to flatten a multidimensional array.
+ */
+function _array_flatten($array, $preserve_keys = false) {
+  if (!$preserve_keys) {
+    // ensure keys are numeric values to avoid overwritting when array_merge gets called
+    $array = array_values($array);
+  }
+
+  $flattened_array = array();
+  foreach ($array as $k => $v) {
+    if (is_array($v)) {
+      $flattened_array = array_merge($flattened_array, call_user_func(__FUNCTION__, $v, $preserve_keys));
+    } elseif ($preserve_keys) {
+      $flattened_array[$k] = $v;
+    } else {
+      $flattened_array[] = $v;
+    }
+  }
+  return $flattened_array;
+}
+
+/**
+ * Utility function for replacing the transaction ID in the original
+ * (unflattened) array.
+ */
+function _replace_value_in_recursive(&$item, $key, $replacements) {
+  if (array_key_exists($key, $replacements)) {
+    $item = $replacements[$key];
+  }
+}
+
+function flatten_array_by_cids($node) {
+  $cids = array();
+  foreach ($node->webform['components'] as $component) {
+    if ($component['type'] != 'fieldset') {
+      $cids[$component['form_key']] = $component['cid'];
+    }
+  }
+  return $cids;
+}
\ No newline at end of file
diff --git a/authorizenetwebform_fields.inc b/authorizenetwebform_fields.inc
index 40cbc0b..3d220de 100644
--- a/authorizenetwebform_fields.inc
+++ b/authorizenetwebform_fields.inc
@@ -21,6 +21,7 @@ function authorizenetwebform_available_fields() {
 
     // Transaction Info
     'x_amount' => 'Amount',
+    'anwf_quantity' => 'Quantity - Multiplies this number by Amount - 1 if not set',
     'x_card_num' => 'Card Number',
     'x_card_code' => 'Card Security Code',
     'x_description' => 'Transaction Description',
