Merge branch 'master' of git.freeside.biz:/home/git/freeside
[freeside.git] / FS / t / suite / 13-tokenization.t
1 #!/usr/bin/perl
2
3 use strict;
4 use FS::Test;
5 use Test::More;
6 use FS::Conf;
7 use FS::cust_main;
8 use Business::CreditCard qw(generate_last_digit);
9 use DateTime;
10 if ( stat('/usr/local/etc/freeside/cardfortresstest.txt') ) {
11   plan tests => 18;
12 } else {
13   plan skip_all => 'CardFortress test encryption key is not installed.';
14 }
15
16 ### can only run on test database (company name "Freeside Test")
17 ### will run upgrade, which uses lots of prints & warns beyond regular test output
18
19 my $fs = FS::Test->new( user => 'admin' );
20 my $conf = FS::Conf->new;
21 my $err;
22 my $bopconf;
23
24 like( $conf->config('company_name'), qr/^Freeside Test/, 'using test database' ) or BAIL_OUT('');
25
26 # test db no longer contains cardtype overrides
27
28 $bopconf = 
29 'IPPay
30 TESTTERMINAL';
31 $conf->set('business-onlinepayment' => $bopconf);
32 is( join("\n",$conf->config('business-onlinepayment')), $bopconf, "setting first default gateway" ) or BAIL_OUT('');
33
34 # generate a few void/refund records for upgrading
35 my $counter = 20;
36 foreach my $cust_pay ( $fs->qsearch('cust_pay',{ payby => 'CARD' }) ) {
37   if ($counter % 2) {
38     $err = $cust_pay->void('Testing');
39     $err = "Voiding: $err" if $err;
40   } else {
41     # from realtime_refund_bop, just the important bits    
42     while ( $cust_pay->unapplied < $cust_pay->paid ) {
43       my @cust_bill_pay = $cust_pay->cust_bill_pay;
44       last unless @cust_bill_pay;
45       my $cust_bill_pay = pop @cust_bill_pay;
46       $err = $cust_bill_pay->delete;
47       $err = "Refund unapply: $err" if $err;
48       last if $err;
49     }
50     last if $err;
51     my $cust_refund = new FS::cust_refund ( {
52       'custnum'  => $cust_pay->cust_main->custnum,
53       'paynum'   => $cust_pay->paynum,
54       'source_paynum' => $cust_pay->paynum,
55       'refund'   => $cust_pay->paid,
56       '_date'    => '',
57       'payby'    => $cust_pay->payby,
58       'payinfo'  => $cust_pay->payinfo,
59       'reason'     => 'Testing',
60       'gatewaynum'    => $cust_pay->gatewaynum,
61       'processor'     => $cust_pay->payment_gateway ? $cust_pay->payment_gateway->processor : '',
62       'auth'          => $cust_pay->auth,
63       'order_number'  => $cust_pay->order_number,
64     } );
65     $err = $cust_refund->insert( reason_type => 'Refund' );
66     $err = "Refunding: $err" if $err;
67   }
68   last if $err;
69   $counter -= 1;
70   last unless $counter > 0;
71 }
72 ok( !$err, "create some refunds and voids" ) or BAIL_OUT($err);
73
74 # also, just to test behavior in this case, create a record for an aborted
75 # verification payment. this will have no customer number.
76
77 my $pending_failed = FS::cust_pay_pending->new({
78   'custnum_pending' => 1,
79   'paid'    => '1.00',
80   '_date'   => time - 86400,
81   random_card(),
82   'status'  => 'failed',
83   'statustext' => 'Tokenization upgrade test',
84 });
85 $err = $pending_failed->insert;
86 ok( !$err, "create a failed payment attempt" ) or BAIL_OUT($err);
87
88 # find two stored credit cards.
89 my @cust = map { FS::cust_main->by_key($_) } (10, 12);
90 my @payby = map { ($_->cust_payby)[0] } @cust;
91 my @payment;
92
93 ok( $payby[0]->payby eq 'CARD' && !$payby[0]->tokenized,
94   "first customer has a non-tokenized card"
95   ) or BAIL_OUT();
96
97 $err = $cust[0]->realtime_cust_payby(amount => '2.00');
98 ok( !$err, "create a payment through IPPay" )
99   or BAIL_OUT($err);
100 $payment[0] = $fs->qsearchs('cust_pay', { custnum => $cust[0]->custnum,
101                                      paid => '2.00' })
102   or BAIL_OUT("can't find payment record");
103
104 $err = system('freeside-upgrade','admin');
105 ok( !$err, 'initial upgrade' ) or BAIL_OUT('Error string: '.$!);
106
107 # switch to CardFortress
108 $bopconf =
109 'CardFortress
110 cardfortresstest
111 (TEST54)
112 Normal Authorization
113 gateway
114 IPPay
115 gateway_login
116 TESTTERMINAL
117 gateway_password
118
119 private_key
120 /usr/local/etc/freeside/cardfortresstest.txt';
121 $conf->set('business-onlinepayment' => $bopconf);
122 is( join("\n",$conf->config('business-onlinepayment')), $bopconf, "setting tokenizable default gateway" ) or BAIL_OUT('');
123
124 # create a payment using a non-tokenized card. this should immediately
125 # trigger tokenization.
126 ok( $payby[1]->payby eq 'CARD' && ! $payby[1]->tokenized,
127   "second customer has a non-tokenized card"
128   ) or BAIL_OUT();
129
130 $err = $cust[1]->realtime_cust_payby(amount => '3.00');
131 ok( !$err, "tokenize a card when it's first used for payment" )
132   or BAIL_OUT($err);
133 $payment[1] = $fs->qsearchs('cust_pay', { custnum => $cust[1]->custnum,
134                                      paid => '3.00' })
135   or BAIL_OUT("can't find payment record");
136 ok( $payment[1]->tokenized, "payment is tokenized" );
137 $payby[1] = $payby[1]->replace_old;
138 ok( $payby[1]->tokenized, "card is now tokenized" );
139
140 # invoke the part of freeside-upgrade that tokenizes
141 FS::cust_main->queueable_upgrade();
142 #$err = system('freeside-upgrade','admin');
143 #ok( !$err, 'tokenizable upgrade' ) or BAIL_OUT('Error string: '.$!);
144
145 $payby[0] = $payby[0]->replace_old;
146 ok( $payby[0]->tokenized, "old card was tokenized during upgrade" );
147 $payment[0] = $payment[0]->replace_old;
148 ok( $payment[0]->tokenized, "old payment was tokenized during upgrade" );
149 ok( ($payment[0]->cust_pay_pending)[0]->tokenized, "old cust_pay_pending was tokenized during upgrade" );
150
151 $pending_failed = $pending_failed->replace_old;
152 ok( $pending_failed->tokenized, "cust_pay_pending with no customer was tokenized" );
153
154 # add a new payment card to one customer
155 $payby[2] = FS::cust_payby->new({
156   custnum => $cust[0]->custnum,
157   random_card(),
158 });
159 $err = $payby[2]->insert;
160 ok( !$err, "new card was saved" );
161 ok($payby[2]->tokenized, "new card is tokenized" );
162
163 sub random_card {
164   my $payinfo = '4111' . join('', map { int(rand(10)) } 1 .. 11);
165   $payinfo .= generate_last_digit($payinfo);
166   my $paydate = DateTime->now
167                 ->add('years' => 1)
168                 ->truncate(to => 'month')
169                 ->strftime('%F');
170   return ( 'payby'    => 'CARD',
171            'payinfo'  => $payinfo,
172            'paydate'  => $paydate,
173            'payname'  => 'Tokenize Me',
174   );
175 }
176
177
178 1;
179