Index: includes/handlers/gateways/pay_method_gateway.inc
===================================================================
--- includes/handlers/gateways/pay_method_gateway.inc	(revision 1651)
+++ includes/handlers/gateways/pay_method_gateway.inc	(working copy)
@@ -23,6 +23,7 @@
   var $cc_ccv2;
   var $cc_exp_month;
   var $cc_exp_year;
+  var $cc_issue_number;
 
   /**
    * Returning NULL will prevent anything from executing.  Subclasses are
@@ -64,16 +65,41 @@
     }
   }
 
+  function cc_issue_number_required($val = NULL) {
+    $issue_number_cc_types = array('switch');
+
+    // For validation of form submission.
+    if ($val && in_array($val, $issue_number_cc_types)) {
+      return TRUE;
+    }
+    // For display of form field.
+    if (!$val) {
+      foreach ($issue_number_cc_types as $cc_type) {
+        if ($this->payment_types[$cc_type]) {
+          return TRUE;
+        }
+      }
+    }
+    return FALSE;
+  }
+  
+  function set_cc_issue_number($val) {
+    $this->cc_issue_number = preg_replace('/[^\d]/', '', $val);
+  }
+
   function set_cc_number($val) {
     $this->cc_number = preg_replace('/[^\d]/', '', $val);
 
     // Set the credit card type rather than relying on user input.
+    $orig_cc_type = $this->cc_type;
     $this->cc_type = NULL;
 
     $prefix1 = substr($this->cc_number, 0, 1);
     $prefix2 = substr($this->cc_number, 0, 2);
     $prefix3 = substr($this->cc_number, 0, 3);
     $prefix4 = substr($this->cc_number, 0, 4);
+    $prefix5 = substr($this->cc_number, 0, 5);
+    $prefix6 = substr($this->cc_number, 0, 6);
 
    /**
     * References: http://www.beachnet.com/~hstiles/cardtype.html
@@ -110,18 +136,37 @@
       case 4:
         // Visa: prefix 4, length 13, 16
         $this->cc_type = 'visa';
+        // Switch: prefix 4903, 4905, 4911, 4936
+        if (in_array($prefix4, array(4903, 4905, 4911, 4936))) $this->cc_type = 'switch';
         break;
 
       case 5:
         // Mastercard: prefix 51-55, length 16
         if (in_array($prefix2, array(51, 52, 53, 54, 55))) $this->cc_type = 'mc';
+        // Switch: prefix 564182
+        if ($prefix6 == 564182) $this->cc_type = 'switch';
+        // Maestro: prefix 5018, 5020, 5038
+        if (in_array($prefix4, array(5018, 5020, 5038))) $this->cc_type = 'maestro';
         break;
 
       case 6:
         // Discover: prefix 6011, length 16
         if ($prefix4 == 6011) $this->cc_type = 'discover';
+        // Laser: prefix 6304 or 6706 or 6709 or 6771, length 19
+        if ($prefix4 == 6304 || $prefix4 == 6706 || $prefix4 == 6709 || $prefix4 == 6771) $this->cc_type = 'laser';
+        // Switch: prefix 633110, 6333, 6759,
+        if ($prefix6 == 633110 || $prefix4 == 6333) $this->cc_type = 'switch';
+        // Maestro: prefix 6759, 6761, 6763
+        if (in_array($prefix4, array(6759, 6761, 6763))) $this->cc_type = 'maestro';
+        // Solo: prefix 6334 or 6767
+        if ($prefix4 == 6334 || $prefix4 == 6767) $this->cc_type = 'solo';
         break;
     }
+    // Only if we still fail to determine card type, reset it to user supplied
+    // value.
+    if (empty($this->cc_type)) {
+      $this->cc_type = $orig_cc_type;
+    }
   }
 
   function set_cc_ccv2($val) {
@@ -166,6 +211,14 @@
     return TRUE;
   }
 
+  function cc_issue_number_validate() {
+    if ($this->cc_issue_number_required($this->cc_type) && (strlen($this->cc_issue_number) != 2|| !is_numeric($this->cc_issue_number))) {
+      $this->error_message = t('Invalid issue number.');
+      return FALSE;
+    }
+    return TRUE;
+  }
+
   function cc_ccv2_validate() {
     if ($this->cc_ccv2) {
       if ($this->cc_type == 'amex') {
@@ -191,6 +244,11 @@
         'mc'   => t('Mastercard'),
         'amex' => t('American Express'),
         'discover' => t('Discover'),
+        'diners' => t("Diner's Club"),
+        'laser' => t('Laser'),
+        'maestro' => t('Maestro'),
+        'switch' => t('Switch'),
+        'solo' => t('Solo'),
       );
     }
     if ($this->gateway_supports_ach) {
@@ -302,6 +360,15 @@
       '#maxlength'  => 4,
       '#attributes' => array('autocomplete' => 'off'),
     );
+    if ($this->cc_issue_number_required()) {
+      $form['cc_issue_number'] = array(
+        '#type'       => 'textfield',
+        '#title'      => t('Issue number'),
+        '#size'       => 2,
+        '#maxlength'  => 2,
+        '#attributes' => array('autocomplete' => 'off'),
+      );
+    }
 
     $months = array();
     foreach (range(1, 12) as $i) {
@@ -357,6 +426,11 @@
 
       // Set the "payment_type" value to the specific card type.
       form_set_value($element['payment_type'], $this->cc_type, $form_state);
+
+      // Validate the issue number field.
+      if (!$this->cc_issue_number_validate()) {
+        form_set_error('cc_issue_number', $this->error_message);
+      }
     }
   }
 }
Index: theme/pay_cc.css
===================================================================
--- theme/pay_cc.css	(revision 1651)
+++ theme/pay_cc.css	(working copy)
@@ -45,7 +45,11 @@
 }
 .pay-cc-info .pay-cc-ccv2 input {
   width: 90px;
+  margin-right: 5px;
 }
+.pay-cc-issue-number input {
+  width: 90px;
+}
 .pay-cc-exp .cc-exp {
   float: left;
   padding: .25em 1em .25em 0;
Index: theme/pay_cc_form.tpl.php
===================================================================
--- theme/pay_cc_form.tpl.php	(revision 1651)
+++ theme/pay_cc_form.tpl.php	(working copy)
@@ -51,6 +51,11 @@
     <div class="pay-cc-ccv2 cc-info">
       <?php print $pay['cc_ccv2']; ?>
     </div>
+    <?php if ($pay['cc_issue_number']) :?>
+      <div class="pay-cc-issue-number cc-issue-number cc-info">
+        <?php print $pay['cc_issue_number']; ?>
+      </div>
+    <?php endif ?>
   </div>
   <div class="pay-cc-exp">
     <div class="pay-cc-exp-month cc-exp">
Index: theme/pay_cc.js
===================================================================
--- theme/pay_cc.js	(revision 1651)
+++ theme/pay_cc.js	(working copy)
@@ -25,7 +25,12 @@
     $payCcForm.find('.pay-cc-type input').change(function() {
       $payCcForm.find('.pay-cc-type img').css('opacity', '.35');
       $payCcForm.find('.pay-cc-type input:checked').parents('label:first').find('img').css('opacity','1');
+      Drupal.pay.setFieldVisibility($payCcForm, $(this).val());
     }).trigger('change');
+    var $initial_cctype = $payCcForm.find('.pay-cc-type input:checked').val();
+    if ($initial_cctype != undefined && $initial_cctype != null) {
+      Drupal.pay.setFieldVisibility($payCcForm, $payCcForm.find('.pay-cc-type input:checked').val());
+    }
 
     // Automatically set the credit card type based on the number.
     $payCcForm.find('.pay-cc-number input').keyup(function() {
@@ -56,6 +61,8 @@
   var prefix2 = parseInt(number.substr(0, 2));
   var prefix3 = parseInt(number.substr(0, 3));
   var prefix4 = parseInt(number.substr(0, 4));
+  var prefix5 = parseInt(number.substr(0, 5));
+  var prefix6 = parseInt(number.substr(0, 6));
 
  /**
   * References: http://www.beachnet.com/~hstiles/cardtype.html
@@ -92,18 +99,57 @@
     case 4:
       // Visa: prefix 4, length 13, 16
       type = 'visa';
+      // Switch: prefix 4903, 4905, 4911, 4936
+      if ($.inArray(prefix4, [4903, 4905, 4911, 4936]) != -1) type = 'switch';
       break;
 
     case 5:
       // Mastercard: prefix 51-55, length 16
       if ($.inArray(prefix2, [51, 52, 53, 54, 55]) != -1) type = 'mc';
+      // Switch: prefix 564182
+      if (prefix6 == 564182) type = 'switch';
+      // Maestro: prefix 5018, 5020, 5038
+      if ($.inArray(prefix4, [5018, 5020, 5038]) != -1) type = 'maestro';
       break;
 
     case 6:
       // Discover: prefix 6011, length 16
       if (prefix4 == 6011) type = 'discover';
+      // Laser: prefix 6304 or 6706 or 6709 or 6771, length 19
+      if (prefix4 == 6304 || prefix4 == 6706 || prefix4 == 6709 || prefix4 == 6771) type = 'laser';
+      // Switch: prefix 633110, 6333
+      if (prefix6 == 633110 || prefix4 == 6333) type = 'switch';
+      // Maestro: prefix 6759, 6761, 6763
+      if ($.inArray(prefix4, [6759, 6761, 6763]) != -1) type = 'maestro';
+      // Solo: prefix 6334 or 6767
+      if (prefix4 == 6334 || prefix4 == 6767) type = 'solo';
       break;
   }
 
   return type;
 }
+
+Drupal.pay.getCVVLength = function(type) {
+  var length = 0;
+  if (!type || type == 'amex') length = 4;
+  else if ($.inArray(type, ['visa', 'mc', 'discover', 'diners', 'enroute', 'jcb']) != -1) length = 3;
+  return length;
+}
+
+Drupal.pay.issueNumberRequired = function(type) {
+  var required = false;
+  if ($.inArray(type, ['switch']) != -1) required = true;
+  return required;
+}
+
+Drupal.pay.setFieldVisibility = function($payCcForm, type) {
+  var ccv2_length = Drupal.pay.getCVVLength(type);
+  if (!ccv2_length) $payCcForm.find('.pay-cc-ccv2').hide();
+  else {
+    $payCcForm.find('.pay-cc-ccv2 input').attr('maxlength', ccv2_length);
+    $payCcForm.find('.pay-cc-ccv2').show();
+  }
+  var show_issue_number = Drupal.pay.issueNumberRequired(type);
+  if (show_issue_number) $payCcForm.find('.pay-cc-issue-number').show();
+  else $payCcForm.find('.pay-cc-issue-number').hide();
+}
