cvv!
authorivan <ivan>
Fri, 24 Oct 2003 19:30:44 +0000 (19:30 +0000)
committerivan <ivan>
Fri, 24 Oct 2003 19:30:44 +0000 (19:30 +0000)
14 files changed:
FS/FS/ClientAPI/Signup.pm
FS/FS/cust_main.pm
FS/bin/freeside-setup
fs_signup/FS-SignupClient/SignupClient.pm
fs_signup/FS-SignupClient/cgi/cvv2.html [new file with mode: 0644]
fs_signup/FS-SignupClient/cgi/cvv2.png [new file with mode: 0644]
fs_signup/FS-SignupClient/cgi/cvv2_amex.png [new file with mode: 0644]
fs_signup/FS-SignupClient/cgi/signup.cgi
fs_signup/FS-SignupClient/cgi/signup.html
httemplate/docs/cvv2.html [new file with mode: 0644]
httemplate/docs/schema.html
httemplate/docs/upgrade10.html
httemplate/edit/cust_main.cgi
httemplate/edit/process/cust_main.cgi

index 5ca1f93..92fc636 100644 (file)
@@ -60,6 +60,8 @@ sub signup_info {
 
     'payby' => [ $conf->config('signup_server-payby') ],
 
+    'cvv_enabled' => defined dbdef->table('cust_main')->column('paycvv'),
+
     'msgcat' => { map { $_=>gettext($_) } qw(
       passwords_dont_match invalid_card unknown_card_type not_a
     ) },
@@ -116,7 +118,8 @@ sub new_customer {
 
     map { $_ => $packet->{$_} } qw(
       last first ss company address1 address2 city county state zip country
-      daytime night fax payby payinfo paydate payname referral_custnum comments
+      daytime night fax payby payinfo paycvv paydate payname referral_custnum
+      comments
     ),
 
   } );
index 2ba0ff0..0ab2aa5 100644 (file)
@@ -172,6 +172,8 @@ FS::Record.  The following fields are currently supported:
 
 =item payinfo - card number, P.O., comp issuer (4-8 lowercase alphanumerics; think username) or prepayment identifier (see L<FS::prepay_credit>)
 
+=item paycvv - Card Verification Value, "CVV2" (also known as CVC2 or CID), the 3 or 4 digit number on the back (or front, for American Express) of the credit card
+
 =item paydate - expiration date, mm/yyyy, m/yyyy, mm/yy or m/yy
 
 =item payname - name on card or billing name
@@ -773,6 +775,21 @@ sub check {
       or return gettext('invalid_card'); # . ": ". $self->payinfo;
     return gettext('unknown_card_type')
       if cardtype($self->payinfo) eq "Unknown";
+    if ( defined $self->dbdef_table->column('paycvv') ) {
+      if ( length($self->paycvv) ) {
+        if ( cardtype($self->payinfo) eq 'American Express card' ) {
+          $self->paycvv =~ /^(\d{4})$/
+            or return "CVV2 (CID) for American Express cards is four digits.";
+          $self->paycvv($1);
+        } else {
+          $self->paycvv =~ /^(\d{3})$/
+            or return "CVV2 (CVC2/CID) is three digits.";
+          $self->paycvv($1);
+        }
+      } else {
+        $self->paycvv('');
+      }
+    }
 
   } elsif ( $self->payby eq 'CHEK' || $self->payby eq 'DCHK' ) {
 
@@ -781,6 +798,7 @@ sub check {
     $payinfo =~ /^(\d+)\@(\d{9})$/ or return 'invalid echeck account@aba';
     $payinfo = "$1\@$2";
     $self->payinfo($payinfo);
+    $self->paycvv('') if $self->dbdef_table->column('paycvv');
 
   } elsif ( $self->payby eq 'LECB' ) {
 
@@ -789,11 +807,13 @@ sub check {
     $payinfo =~ /^1?(\d{10})$/ or return 'invalid btn billing telephone number';
     $payinfo = $1;
     $self->payinfo($payinfo);
+    $self->paycvv('') if $self->dbdef_table->column('paycvv');
 
   } elsif ( $self->payby eq 'BILL' ) {
 
     $error = $self->ut_textn('payinfo');
     return "Illegal P.O. number: ". $self->payinfo if $error;
+    $self->paycvv('') if $self->dbdef_table->column('paycvv');
 
   } elsif ( $self->payby eq 'COMP' ) {
 
@@ -804,6 +824,7 @@ sub check {
 
     $error = $self->ut_textn('payinfo');
     return "Illegal comp account issuer: ". $self->payinfo if $error;
+    $self->paycvv('') if $self->dbdef_table->column('paycvv');
 
   } elsif ( $self->payby eq 'PREPAY' ) {
 
@@ -814,6 +835,7 @@ sub check {
     return "Illegal prepayment identifier: ". $self->payinfo if $error;
     return "Unknown prepayment identifier"
       unless qsearchs('prepay_credit', { 'identifier' => $self->payinfo } );
+    $self->paycvv('') if $self->dbdef_table->column('paycvv');
 
   }
 
@@ -1666,15 +1688,20 @@ sub realtime_bop {
 
   my %content;
   if ( $method eq 'CC' ) { 
+
     $content{card_number} = $self->payinfo;
     $self->paydate =~ /^\d{2}(\d{2})[\/\-](\d+)[\/\-]\d+$/;
     $content{expiration} = "$2/$1";
-    if ( qsearch('cust_pay', { 'custnum' => $self->custnum,
+
+    $content{cvv2} = $self->paycvv
+      if defined $self->dbdef_table->column('paycvv')
+         && length($self->paycvv);
+
+    $content{recurring_billing} = 'YES'
+      if qsearch('cust_pay', { 'custnum' => $self->custnum,
                                'payby'   => 'CARD',
-                               'payinfo' => $self->payinfo, } )
-    ) { 
-      $content{recurring_billing} = 'YES';
-    }
+                               'payinfo' => $self->payinfo, } );
+
   } elsif ( $method eq 'ECHECK' ) {
     my($account_number,$routing_code) = $self->payinfo;
     ( $content{account_number}, $content{routing_code} ) =
@@ -1759,6 +1786,20 @@ sub realtime_bop {
 
   }
 
+  #remove paycvv after initial transaction
+  #make this disable-able via a config option if anyone insists?  
+  # (though that probably violates cardholder agreements)
+  if ( defined $self->dbdef_table->column('paycvv')
+       && length($self->paycvv)
+  ) {
+    my $new = new FS::cust_main { $self->hash };
+    $new->paycvv('');
+    my $error = $new->replace($self);
+    if ( $error ) {
+      warn "error removing cvv: $error\n";
+    }
+  }
+
   #result handling
   if ( $transaction->is_success() ) {
 
index 375a63c..80b74c1 100755 (executable)
@@ -502,6 +502,7 @@ sub tables_hash_hack {
         'ship_fax',      'varchar', 'NULL', 12,
         'payby',    'char', '',     4,
         'payinfo',  'varchar', 'NULL', $char_d,
+        'paycvv',   'varchar', 'NULL', 4,
         #'paydate',  @date_type,
         'paydate',  'varchar', 'NULL', 10,
         'payname',  'varchar', 'NULL', $char_d,
index 1d91a41..fb2b12f 100644 (file)
@@ -43,6 +43,7 @@ FS::SignupClient - Freeside signup client API
     'fax'              => $fax,
     'payby'            => $payby,
     'payinfo'          => $payinfo,
+    'paycvv'           => $paycvv,
     'paydate'          => $paydate,
     'payname'          => $payname,
     'invoicing_list'   => $invoicing_list,
@@ -125,6 +126,7 @@ a paramater with the following keys:
   fax
   payby
   payinfo
+  paycvv
   paydate
   payname
   invoicing_list
diff --git a/fs_signup/FS-SignupClient/cgi/cvv2.html b/fs_signup/FS-SignupClient/cgi/cvv2.html
new file mode 100644 (file)
index 0000000..b178c85
--- /dev/null
@@ -0,0 +1,25 @@
+<HTML>
+  <HEAD>
+    <TITLE>
+      CVV2 information
+    </TITLE>
+  </HEAD>
+  <BODY BGCOLOR="#e8e8e8">
+  The CVV2 number (also called CVC2 or CID) is a three- or four-digit
+  security code used to reduce credit card fraud.<BR><BR>
+  <TABLE BORDER=0 CELLSPACING=4>
+    <TR>
+      <TH>Visa / MasterCard / Discover</TH>
+      <TH>American Express</TH>
+    </TR>
+    <TR>
+      <TD>
+        <IMG BORDER=0 ALT="Visa/MasterCard/Discover" SRC="cvv2.png">
+      </TD>
+      <TD>
+        <IMG BORDER=0 ALT="American Express" SRC="cvv2_amex.png">
+      </TD>
+  </TABLE>
+    <CENTER><A HREF="javascript:close()">(close window)</A></CENTER>
+  </BODY>
+</HTML>
diff --git a/fs_signup/FS-SignupClient/cgi/cvv2.png b/fs_signup/FS-SignupClient/cgi/cvv2.png
new file mode 100644 (file)
index 0000000..4610dcb
Binary files /dev/null and b/fs_signup/FS-SignupClient/cgi/cvv2.png differ
diff --git a/fs_signup/FS-SignupClient/cgi/cvv2_amex.png b/fs_signup/FS-SignupClient/cgi/cvv2_amex.png
new file mode 100644 (file)
index 0000000..21c36a0
Binary files /dev/null and b/fs_signup/FS-SignupClient/cgi/cvv2_amex.png differ
index 2458bcf..70facb5 100755 (executable)
@@ -1,6 +1,6 @@
 #!/usr/bin/perl -Tw
 #
-# $Id: signup.cgi,v 1.46 2003-10-14 17:00:00 ivan Exp $
+# $Id: signup.cgi,v 1.47 2003-10-24 19:28:49 ivan Exp $
 
 use strict;
 use vars qw( @payby $cgi $locales $packages
@@ -8,7 +8,7 @@ use vars qw( @payby $cgi $locales $packages
              $init_data $error
              $last $first $ss $company $address1 $address2 $city $state $county
              $country $zip $daytime $night $fax $invoicing_list $payby $payinfo
-             $paydate $payname $referral_custnum $init_popstate
+             $paycvv $paydate $payname $referral_custnum $init_popstate
              $pkgpart $username $password $password2 $sec_phrase $popnum
              $agentnum
              $ieak_file $ieak_template $cck_file $cck_template
@@ -178,6 +178,9 @@ if ( defined $cgi->param('magic') ) {
     $paydate =
       $cgi->param( $payby. '_month' ). '-'. $cgi->param( $payby. '_year' );
     $payname = $cgi->param( $payby. '_payname' );
+    $paycvv = defined $cgi->param( $payby. '_paycvv' )
+                ? $cgi->param( $payby. '_paycvv' )
+                : '';
 
     if ( $invoicing_list = $cgi->param('invoicing_list') ) {
       $invoicing_list .= ', POST' if $cgi->param('invoicing_list_POST');
@@ -252,6 +255,7 @@ if ( defined $cgi->param('magic') ) {
         'fax'              => $fax,
         'payby'            => $payby,
         'payinfo'          => $payinfo,
+        'paycvv'           => $paycvv,
         'paydate'          => $paydate,
         'payname'          => $payname,
         'invoicing_list'   => $invoicing_list,
index 8077409..dc4252c 100755 (executable)
@@ -1,5 +1,18 @@
 <HTML><HEAD><TITLE>ISP Signup form</TITLE></HEAD>
-<BODY BGCOLOR="#e8e8e8"><FONT SIZE=7>ISP Signup form</FONT><BR><BR>
+<BODY BGCOLOR="#e8e8e8" onUnload="myclose()">
+<script language="JavaScript"><!--
+  var mywindow = -1;
+  function myopen(filename,windowname,properties) {
+    myclose();
+    mywindow = window.open(filename,windowname,properties);
+  }
+  function myclose() {
+    if ( mywindow != -1 )
+      mywindow.close();
+    mywindow = -1
+  }
+//--></script>
+<FONT SIZE=7>ISP Signup form</FONT><BR><BR>
 <FONT SIZE="+1" COLOR="#ff0000"><%= $error %></FONT>
 <FORM ACTION="<%= $self_url %>" METHOD=POST>
 <INPUT TYPE="hidden" NAME="magic" VALUE="process">
@@ -101,6 +114,12 @@ Contact Information
       'PREPAY' => qq!Prepaid card<BR><font color="#ff0000">*</font><INPUT TYPE="text" NAME="PREPAY_payinfo" VALUE="" MAXLENGTH=80>!,
     );
 
+    if ( $init_data->{'cvv_enabled'} ) {
+      foreach my $payby ( grep { exists $payby{$_} } qw(CARD DCRD) ) { #1.4/1.5
+        $payby{$payby} .= qq!<BR>CVV2&nbsp;(<A HREF="javascript:myopen('cvv2.html','cvv2','toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=no,resizable=yes,copyhistory=no,width=480,height=288')">help</A>)&nbsp;<INPUT TYPE="text" NAME=${payby}_paycvv VALUE="" SIZE=4 MAXLENGTH=4>!;
+      }
+    }
+
     my( $account, $aba ) = split('@', $payinfo);
     my %paybychecked = (
       'CARD' => qq!Credit card<BR><font color="#ff0000">*</font>$cardselect<INPUT TYPE="text" NAME="CARD_payinfo" VALUE="$payinfo" MAXLENGTH=19><BR><font color="#ff0000">*</font>Exp !. expselect("CARD", $paydate). qq!<BR><font color="#ff0000">*</font>Name on card<BR><INPUT TYPE="text" NAME="CARD_payname" VALUE="$payname">!,
@@ -113,6 +132,12 @@ Contact Information
       'PREPAY' => qq!Prepaid card<BR><font color="#ff0000">*</font><INPUT TYPE="text" NAME="PREPAY_payinfo" VALUE="$payinfo" MAXLENGTH=80>!,
     );
 
+    if ( $init_data->{'cvv_enabled'} ) {
+      foreach my $payby ( grep { exists $payby{$_} } qw(CARD DCRD) ) { #1.4/1.5
+        $paybychecked{$payby} .= qq!<BR>CVV2&nbsp;(<A HREF="javascript:myopen('cvv2.html','cvv2','toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=no,resizable=yes,copyhistory=no,width=480,height=288')">help</A>)&nbsp;<INPUT TYPE="text" NAME=${payby}_paycvv VALUE="$paycvv" SIZE=4 MAXLENGTH=4>!;
+      }
+    }
+
     for (@payby) {
       if ( scalar(@payby) == 1) {
         $OUT .= '<TD VALIGN=TOP>'.
diff --git a/httemplate/docs/cvv2.html b/httemplate/docs/cvv2.html
new file mode 100644 (file)
index 0000000..fe8a17f
--- /dev/null
@@ -0,0 +1,25 @@
+<HTML>
+  <HEAD>
+    <TITLE>
+      CVV2 information
+    </TITLE>
+  </HEAD>
+  <BODY BGCOLOR="#e8e8e8">
+  The CVV2 number (also called CVC2 or CID) is a three- or four-digit
+  security code used to reduce credit card fraud.<BR><BR>
+  <TABLE BORDER=0 CELLSPACING=4>
+    <TR>
+      <TH>Visa / MasterCard / Discover</TH>
+      <TH>American Express</TH>
+    </TR>
+    <TR>
+      <TD>
+        <IMG BORDER=0 ALT="Visa/MasterCard/Discover" SRC="../images/cvv2.png">
+      </TD>
+      <TD>
+        <IMG BORDER=0 ALT="American Express" SRC="../images/cvv2_amex.png">
+      </TD>
+  </TABLE>
+    <CENTER><A HREF="javascript:close()">(close window)</A></CENTER>
+  </BODY>
+</HTML>
index bef5671..06be896 100644 (file)
         <li><i>ship_fax</i>
         <li>payby - CARD, DCHK, CHEK, DCHK, LECB, BILL, or COMP
         <li>payinfo - card number, P.O.#, or comp issuer
+        <li>paycvv - Card Verification Value, "CVV2" (also known as CVC2 or CID), the 3 or 4 digit number on the back (or front, for American Express) of the credit card
         <li>paydate - expiration date
         <li>payname - billing name (name on card)
         <li>tax - tax exempt, Y or null
index 4460905..080528b 100644 (file)
@@ -1,7 +1,7 @@
 <pre>
 this is incomplete
 
-install DBIx::DBSchema 0.21
+install DBIx::DBSchema 0.22
 
 install NetAddr::IP and Chart::Base
 
@@ -142,6 +142,8 @@ ALTER TABLE agent ADD username varchar(80) NULL;
 ALTER TABLE h_agent ADD username varchar(80) NULL;
 ALTER TABLE agent ADD _password varchar(80) NULL;
 ALTER TABLE h_agent ADD _password varchar(80) NULL;
+ALTER TABLE cust_main ADD paycvv varchar(4) NULL;
+ALTER TABLE h_cust_main ADD paycvv varchar(4) NULL;
 
 dump database, edit:
 - cust_main: increase otaker from 8 to 32
index 73a0eef..1909b54 100755 (executable)
@@ -39,6 +39,12 @@ if ( $cgi->param('error') ) {
   $query =~ /^(\d+)$/;
   $custnum=$1;
   $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } );
+  if ( $cust_main->dbdef_table->column('paycvv')
+       && length($cust_main->paycvv)             ) {
+    my $paycvv = $cust_main->paycvv;
+    $paycvv =~ s/./*/g;
+    $cust_main->paycvv($paycvv);
+  }
   $saved_pkgpart = 0;
   $username = '';
   $password = '';
@@ -61,7 +67,7 @@ my $action = $custnum ? 'Edit' : 'Add';
 # top
 
 my $p1 = popurl(1);
-print header("Customer $action", '');
+print header("Customer $action", '', ' onUnload="myclose()"');
 print qq!<FONT SIZE="+1" COLOR="#ff0000">Error: !, $error, "</FONT>"
   if $error;
 
@@ -400,7 +406,19 @@ if ( $payby_default eq 'HIDE' ) {
   print qq!<TR><TD>Email invoice <INPUT TYPE="text" NAME="invoicing_list" VALUE="$invoicing_list"></TD></TR>!;
 
   print "<TR><TD>Billing type</TD></TR>",
-        "</TABLE>",
+        "</TABLE>", '<script language="JavaScript"><!--
+                       var mywindow = -1;
+                       function myopen(filename,windowname,properties) {
+                         myclose();
+                         mywindow = window.open(filename,windowname,properties);
+                       }
+                       function myclose() {
+                         if ( mywindow != -1 )
+                           mywindow.close();
+                         mywindow = -1;
+                       }
+
+                     //--></script>',
         &table("#cccccc"), "<TR>";
 
   my($payinfo, $payname)=(
@@ -418,6 +436,12 @@ if ( $payby_default eq 'HIDE' ) {
     'COMP' => qq!Complimentary<BR>${r}Approved by<INPUT TYPE="text" NAME="COMP_payinfo" VALUE=""><BR>${r}Exp !. expselect("COMP"),
 );
 
+  if ( $cust_main->dbdef_table->column('paycvv') ) {
+    foreach my $payby ( grep { exists $payby{$_} } qw(CARD DCRD) ) { #1.4/1.5 bs
+      $payby{$payby} .= qq!<BR>CVV2&nbsp;(<A HREF="javascript:myopen('../docs/cvv2.html','cvv2','toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=no,resizable=yes,copyhistory=no,width=480,height=288')">help</A>)&nbsp;<INPUT TYPE="text" NAME=${payby}_paycvv VALUE="" SIZE=4 MAXLENGTH=4>!;
+    }
+  }
+
   my( $account, $aba ) = split('@', $payinfo);
 
   my %paybychecked = (
@@ -430,6 +454,15 @@ if ( $payby_default eq 'HIDE' ) {
     'COMP' => qq!Complimentary<BR>${r}Approved by<INPUT TYPE="text" NAME="COMP_payinfo" VALUE="$payinfo"><BR>${r}Exp !. expselect("COMP", $cust_main->paydate),
 );
 
+  if ( $cust_main->dbdef_table->column('paycvv') ) {
+    my $paycvv = $cust_main->paycvv;
+
+    foreach my $payby ( grep { exists $payby{$_} } qw(CARD DCRD) ) { #1.4/1.5 bs
+      $paybychecked{$payby} .= qq!<BR>CVV2&nbsp;(<A HREF="javascript:myopen('../docs/cvv2.html','cvv2','toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=no,resizable=yes,copyhistory=no,width=480,height=288')">help</A>)&nbsp;<INPUT TYPE="text" NAME=${payby}_paycvv VALUE="$paycvv" SIZE=4 MAXLENGTH=4>!;
+    }
+  }
+
+
   $cust_main->payby($payby_default) unless $cust_main->payby;
   for (qw(CARD DCRD CHEK DCHK LECB BILL COMP)) {
     print qq!<TD VALIGN=TOP><INPUT TYPE="radio" NAME="payby" VALUE="$_"!;
index 8183828..25c346e 100755 (executable)
@@ -19,6 +19,8 @@ if ( $payby ) {
   $cgi->param('paydate',
     $cgi->param( $payby. '_month' ). '-'. $cgi->param( $payby. '_year' ) );
   $cgi->param('payname', $cgi->param( $payby. '_payname' ) );
+  $cgi->param('paycvv', $cgi->param( $payby. '_paycvv' ) )
+    if defined $cgi->param( $payby. '_paycvv' );
 }
 
 $cgi->param('otaker', &getotaker );
@@ -27,6 +29,7 @@ my @invoicing_list = split( /\s*\,\s*/, $cgi->param('invoicing_list') );
 push @invoicing_list, 'POST' if $cgi->param('invoicing_list_POST');
 $cgi->param('invoicing_list', join(',', @invoicing_list) );
 
+
 #create new record object
 
 my $new = new FS::cust_main ( {
@@ -113,6 +116,11 @@ if ( $new->custnum eq '' ) {
 } else { #create old record object
   my $old = qsearchs( 'cust_main', { 'custnum' => $new->custnum } ); 
   $error ||= "Old record not found!" unless $old;
+  if ( defined dbdef->table('cust_main')->column('paycvv')
+       && length($old->paycvv)
+       && $new->paycvv =~ /^\s*\*+\s*$/ ) {
+    $new->paycvv($old->paycvv);
+  }
   $error ||= $new->replace($old, \@invoicing_list);
 }