Merge branch 'FREESIDE_4_BRANCH' of git.freeside.biz:/home/git/freeside into 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 => 27;
12 } else {
13   plan skip_all => 'CardFortress test encryption key is not installed.';
14 }
15
16 #local $FS::cust_main::Billing_Realtime::DEBUG = 2;
17
18 ### can only run on test database (company name "Freeside Test")
19 ### will run upgrade, which uses lots of prints & warns beyond regular test output
20
21 my $fs = FS::Test->new( user => 'admin' );
22 my $conf = FS::Conf->new;
23 my $err;
24 my $bopconf;
25
26 like( $conf->config('company_name'), qr/^Freeside Test/, 'using test database' ) or BAIL_OUT('');
27
28 # some pre-upgrade cleanup, upgrade will fail if these are still configured
29 foreach my $cust_main ( $fs->qsearch('cust_main') ) {
30   my @count = $fs->qsearch('agent_payment_gateway', { agentnum => $cust_main->agentnum } );
31   if (@count > 1) {
32     note("DELETING CARDTYPE GATEWAYS");
33     foreach my $apg (@count) {
34       $err = $apg->delete if $apg->cardtype;
35       last if $err;
36     }
37     @count = $fs->qsearch('agent_payment_gateway', { agentnum => $cust_main->agentnum } );
38     if (@count > 1) {
39       $err = "Still found ".@count." gateways for custnum ".$cust_main->custnum;
40       last;
41     }
42   }
43 }
44 ok( !$err, "remove obsolete payment gateways" ) or BAIL_OUT($err);
45
46 $bopconf = 
47 'IPPay
48 TESTTERMINAL';
49 $conf->set('business-onlinepayment' => $bopconf);
50 is( join("\n",$conf->config('business-onlinepayment')), $bopconf, "setting first default gateway" ) or BAIL_OUT('');
51
52 # generate a few void/refund records for upgrading
53 my $counter = 20;
54 foreach my $cust_pay ( $fs->qsearch('cust_pay',{ payby => 'CARD' }) ) {
55   if ($counter % 2) {
56     $err = $cust_pay->void('Testing');
57     $err = "Voiding: $err" if $err;
58   } else {
59     # from realtime_refund_bop, just the important bits    
60     while ( $cust_pay->unapplied < $cust_pay->paid ) {
61       my @cust_bill_pay = $cust_pay->cust_bill_pay;
62       last unless @cust_bill_pay;
63       my $cust_bill_pay = pop @cust_bill_pay;
64       $err = $cust_bill_pay->delete;
65       $err = "Refund unapply: $err" if $err;
66       last if $err;
67     }
68     last if $err;
69     my $cust_refund = new FS::cust_refund ( {
70       'custnum'  => $cust_pay->cust_main->custnum,
71       'paynum'   => $cust_pay->paynum,
72       'source_paynum' => $cust_pay->paynum,
73       'refund'   => $cust_pay->paid,
74       '_date'    => '',
75       'payby'    => $cust_pay->payby,
76       'payinfo'  => $cust_pay->payinfo,
77       'reason'     => 'Testing',
78       'gatewaynum'    => $cust_pay->gatewaynum,
79       'processor'     => $cust_pay->payment_gateway ? $cust_pay->payment_gateway->processor : '',
80       'auth'          => $cust_pay->auth,
81       'order_number'  => $cust_pay->order_number,
82     } );
83     $err = $cust_refund->insert( reason_type => 'Refund' );
84     $err = "Refunding: $err" if $err;
85   }
86   last if $err;
87   $counter -= 1;
88   last unless $counter > 0;
89 }
90 ok( !$err, "create some refunds and voids" ) or BAIL_OUT($err);
91
92 # also, just to test behavior in this case, create a record for an aborted
93 # verification payment. this will have no customer number.
94
95 my $pending_failed = FS::cust_pay_pending->new({
96   'custnum_pending' => 1,
97   'paid'    => '1.00',
98   '_date'   => time - 86400,
99   random_card(),
100   'status'  => 'failed',
101   'statustext' => 'Tokenization upgrade test',
102 });
103 $err = $pending_failed->insert;
104 ok( !$err, "create a failed payment attempt" ) or BAIL_OUT($err);
105
106 # create two customers with an AmEx card & paycvv,
107 # then run a payment with one, just to generate some test AmEx data
108
109 my $amex_cust;
110 foreach my $i (0,1) {
111   my $cust_main = $fs->new_customer("AmEx $i");
112   isa_ok ( $cust_main, 'FS::cust_main', "AmEx $i customer" ) or BAIL_OUT('');
113   $err = $cust_main->insert;
114   ok( !$err, "insert AmEx $i customer" ) or BAIL_OUT($err);
115   # add card
116   my $cust_payby;
117   my %card = random_card();
118   $card{'payinfo'} = '347594362484937';
119   $card{'paycvv'}  = '1234';
120   $err = $cust_main->save_cust_payby(
121     %card,
122     payment_payby => $card{'payby'},
123     auto => 1,
124     saved_cust_payby => \$cust_payby
125   );
126   ok( !$err, "save AmEx $i card" ) or BAIL_OUT($err);
127   $amex_cust = $cust_main;
128 }
129 $err = $amex_cust->realtime_cust_payby( amount => '1.00' );
130 ok( !$err, "run AmEx payment" ) or BAIL_OUT($err);
131
132 # find two stored credit cards.
133 my @cust = map { FS::cust_main->by_key($_) } (10, 12);
134 my @payby = map { ($_->cust_payby)[0] } @cust;
135 my @payment;
136
137 ok( $payby[0]->payby eq 'CARD' && !$payby[0]->tokenized,
138   "first customer has a non-tokenized card"
139   ) or BAIL_OUT();
140
141 $err = $cust[0]->realtime_cust_payby(amount => '2.00');
142 ok( !$err, "create a payment through IPPay" )
143   or BAIL_OUT($err);
144 $payment[0] = $fs->qsearchs('cust_pay', { custnum => $cust[0]->custnum,
145                                      paid => '2.00' })
146   or BAIL_OUT("can't find payment record");
147
148 $err = system('freeside-upgrade','admin');
149 ok( !$err, 'initial upgrade' ) or BAIL_OUT('Error string: '.$!);
150
151 # switch to CardFortress
152 $bopconf =
153 'CardFortress
154 cardfortresstest
155 (TEST54)
156 Normal Authorization
157 gateway
158 IPPay
159 gateway_login
160 TESTTERMINAL
161 gateway_password
162
163 private_key
164 /usr/local/etc/freeside/cardfortresstest.txt';
165 $conf->set('business-onlinepayment' => $bopconf);
166 is( join("\n",$conf->config('business-onlinepayment')), $bopconf, "setting tokenizable default gateway" ) or BAIL_OUT('');
167
168 foreach my $pg ($fs->qsearch('payment_gateway')) {
169   unless ($pg->gateway_module eq 'CardFortress') {
170     note('UPGRADING NON-CF PAYMENT GATEWAY');
171     my %pgopts = (
172       gateway          => $pg->gateway_module,
173       gateway_login    => $pg->gateway_username,
174       gateway_password => $pg->gateway_password,
175       private_key      => '/usr/local/etc/freeside/cardfortresstest.txt',
176     );
177     $pg->gateway_module('CardFortress');
178     $pg->gateway_username('cardfortresstest');
179     $pg->gateway_password('(TEST54)');
180     $err = $pg->replace(\%pgopts);
181     last if $err;
182   }
183 }
184 ok( !$err, "remove non-CF payment gateways" ) or BAIL_OUT($err);
185
186 # create a payment using a non-tokenized card. this should immediately
187 # trigger tokenization.
188 ok( $payby[1]->payby eq 'CARD' && ! $payby[1]->tokenized,
189   "second customer has a non-tokenized card"
190   ) or BAIL_OUT();
191
192 $err = $cust[1]->realtime_cust_payby(amount => '3.00');
193 ok( !$err, "tokenize a card when it's first used for payment" )
194   or BAIL_OUT($err);
195 $payment[1] = $fs->qsearchs('cust_pay', { custnum => $cust[1]->custnum,
196                                      paid => '3.00' })
197   or BAIL_OUT("can't find payment record");
198 ok( $payment[1]->tokenized, "payment is tokenized" );
199 $payby[1] = $payby[1]->replace_old;
200 ok( $payby[1]->tokenized, "card is now tokenized" );
201
202 # invoke the part of freeside-upgrade that tokenizes
203 FS::cust_main->queueable_upgrade();
204 #$err = system('freeside-upgrade','admin');
205 #ok( !$err, 'tokenizable upgrade' ) or BAIL_OUT('Error string: '.$!);
206
207 $payby[0] = $payby[0]->replace_old;
208 ok( $payby[0]->tokenized, "old card was tokenized during upgrade" );
209 $payment[0] = $payment[0]->replace_old;
210 ok( $payment[0]->tokenized, "old payment was tokenized during upgrade" );
211 ok( ($payment[0]->cust_pay_pending)[0]->tokenized, "old cust_pay_pending was tokenized during upgrade" );
212
213 $pending_failed = $pending_failed->replace_old;
214 ok( $pending_failed->tokenized, "cust_pay_pending with no customer was tokenized" );
215
216 # add a new payment card to one customer
217 $payby[2] = FS::cust_payby->new({
218   custnum => $cust[0]->custnum,
219   random_card(),
220 });
221 $err = $payby[2]->insert;
222 ok( !$err, "new card was saved" );
223 ok($payby[2]->tokenized, "new card is tokenized" );
224
225 sub random_card {
226   my $payinfo = '4111' . join('', map { int(rand(10)) } 1 .. 11);
227   $payinfo .= generate_last_digit($payinfo);
228   my $paydate = DateTime->now
229                 ->add('years' => 1)
230                 ->truncate(to => 'month')
231                 ->strftime('%F');
232   return ( 'payby'    => 'CARD',
233            'payinfo'  => $payinfo,
234            'paydate'  => $paydate,
235            'payname'  => 'Tokenize Me',
236   );
237 }
238
239
240 1;
241