8 use Business::CreditCard qw(generate_last_digit);
10 if ( stat('/usr/local/etc/freeside/cardfortresstest.txt') ) {
13 plan skip_all => 'CardFortress test encryption key is not installed.';
16 #local $FS::cust_main::Billing_Realtime::DEBUG = 2;
18 my $fs = FS::Test->new( user => 'admin' );
19 my $conf = FS::Conf->new;
23 ### can only run on test database (company name "Freeside Test")
24 like( $conf->config('company_name'), qr/^Freeside Test/, 'using test database' ) or BAIL_OUT('');
26 # these will just get in the way for now
27 foreach my $apg ($fs->qsearch('agent_payment_gateway')) {
31 ok( !$err, 'removing agent gateway overrides' ) or BAIL_OUT($err);
34 my $reason = FS::reason->new_or_existing(
35 reason => 'Token Test',
39 isa_ok ( $reason, 'FS::reason', "refund reason" ) or BAIL_OUT('');
41 # non-tokenizing gateway
59 /usr/local/etc/freeside/cardfortresstest.txt';
61 foreach my $voiding (0,1) {
62 my $noun = $voiding ? 'void' : 'refund';
65 $conf->delete('disable_void_after');
66 ok( !$conf->exists('disable_void_after'), 'set disable_void_after to produce voids' ) or BAIL_OUT('');
68 $conf->set('disable_void_after' => '0');
69 is( $conf->config('disable_void_after'), '0', 'set disable_void_after to produce refunds' ) or BAIL_OUT('');
72 # for attempting refund post-tokenization
76 foreach my $tokenizing (0,1) {
77 my $adj = $tokenizing ? 'tokenizable' : 'non-tokenizable';
80 $conf->set('business-onlinepayment' => $bopconf[$tokenizing]);
81 is( join("\n",$conf->config('business-onlinepayment')), $bopconf[$tokenizing], "set $adj $noun default gateway" ) or BAIL_OUT('');
83 # make sure we're upgraded, only need to do it once,
84 # use non-tokenizing gateway for speed,
85 # but doesn't matter if existing records are tokenized or not,
86 # this suite is all about testing new record creation
87 if (!$tokenizing && !$voiding) {
88 $err = system('freeside-upgrade','-q','admin');
89 ok( !$err, 'upgrade freeside' ) or BAIL_OUT('Error string: '.$!);
94 my $n_paynum = $n_cust_pay->paynum;
96 # refund the previous non-tokenized payment through CF
97 $err = $n_cust_main->realtime_refund_bop({
98 reasonnum => $reason->reasonnum,
102 ok( !$err, "run post-switch $noun" ) or BAIL_OUT($err);
104 my $n_cust_pay_void = $fs->qsearchs('cust_pay_void',{ paynum => $n_paynum });
105 my $n_cust_refund = $fs->qsearchs('cust_refund',{ source_paynum => $n_paynum });
109 # check for void record
110 isa_ok( $n_cust_pay_void, 'FS::cust_pay_void', 'post-switch void') or BAIL_OUT("paynum $n_paynum");
112 # check that void tokenized
113 ok ( $n_cust_pay_void->tokenized, "post-switch void tokenized" ) or BAIL_OUT("paynum $n_paynum");
115 # check for no refund record
116 ok( !$n_cust_refund, "post-switch void did not generate cust_refund" ) or BAIL_OUT("paynum $n_paynum");
120 # check for refund record
121 isa_ok( $n_cust_refund, 'FS::cust_refund', 'post-switch refund') or BAIL_OUT("paynum $n_paynum");
123 # check that refund tokenized
124 ok ( $n_cust_refund->tokenized, "post-switch refund tokenized" ) or BAIL_OUT("paynum $n_paynum");
126 # check for no refund record
127 ok( !$n_cust_pay_void, "post-switch refund did not generate cust_pay_void" ) or BAIL_OUT("paynum $n_paynum");
134 my $cust_main = $fs->new_customer($adj.'X'.$noun);
135 isa_ok ( $cust_main, 'FS::cust_main', "$adj $noun customer" ) or BAIL_OUT('');
138 $err = $cust_main->insert;
139 ok( !$err, "insert $adj $noun customer" ) or BAIL_OUT($err);
143 my %card = random_card();
144 $err = $cust_main->save_cust_payby(
146 payment_payby => $card{'payby'},
148 saved_cust_payby => \$cust_payby
150 ok( !$err, "save $adj $noun card" ) or BAIL_OUT($err);
153 isa_ok ( $cust_payby, 'FS::cust_payby', "$adj $noun card" ) or BAIL_OUT('');
155 # check that card tokenized or not
157 ok( $cust_payby->tokenized, "new $noun cust card tokenized" ) or BAIL_OUT('');
159 ok( !$cust_payby->tokenized, "new $noun cust card not tokenized" ) or BAIL_OUT('');
163 $err = $cust_main->realtime_cust_payby( amount => '1.00' );
164 ok( !$err, "run $adj $noun payment" ) or BAIL_OUT($err);
167 my $cust_pay = $fs->qsearchs('cust_pay',{ custnum => $cust_main->custnum });
168 isa_ok ( $cust_pay, 'FS::cust_pay', "$adj $noun payment" ) or BAIL_OUT('');
171 $err = $cust_main->realtime_refund_bop({
172 reasonnum => $reason->reasonnum,
173 paynum => $cust_pay->paynum,
176 ok( !$err, "run $adj $noun" ) or BAIL_OUT($err);
178 unless ($tokenizing) {
180 # run a second payment, to refund after switch
181 $err = $cust_main->realtime_cust_payby( amount => '2.00' );
182 ok( !$err, "run $adj $noun second payment" ) or BAIL_OUT($err);
184 # get the second payment
185 $n_cust_pay = $fs->qsearchs('cust_pay',{ custnum => $cust_main->custnum, paid => '2.00' });
186 isa_ok ( $n_cust_pay, 'FS::cust_pay', "$adj $noun second payment" ) or BAIL_OUT('');
188 $n_cust_main = $cust_main;
192 #check that all transactions tokenized or not
193 foreach my $table (qw(cust_pay_pending cust_pay cust_pay_void cust_refund)) {
194 foreach my $record ($fs->qsearch($table,{ custnum => $cust_main->custnum })) {
196 $err = "record not tokenized: $table ".$record->get($record->primary_key)
197 unless $record->tokenized;
199 $err = "record tokenized: $table ".$record->get($record->primary_key)
200 if $record->tokenized;
205 ok( !$err, "$adj transaction token check" ) or BAIL_OUT($err);
210 ok( $fs->qsearch('cust_pay_void',{ custnum => $cust_main->custnum}), "$adj $noun record found" ) or BAIL_OUT('');
212 #make sure we didn't generate refund records
213 ok( !$fs->qsearch('cust_refund',{ custnum => $cust_main->custnum}), "$adj $noun did not generate cust_refund" ) or BAIL_OUT('');
217 #make sure we refunded
218 ok( $fs->qsearch('cust_refund',{ custnum => $cust_main->custnum}), "$adj $noun record found" ) or BAIL_OUT('');
220 #make sure we didn't generate void records
221 ok( !$fs->qsearch('cust_pay_void',{ custnum => $cust_main->custnum}), "$adj $noun did not generate cust_pay_void" ) or BAIL_OUT('');
225 } #end of tokenizing or not
227 } # end of voiding or not
232 # my $payinfo = '4111' . join('', map { int(rand(10)) } 1 .. 11);
233 # $payinfo .= generate_last_digit($payinfo);
234 # Use AmEx for everything, to make sure cardtype gets set correctly
235 my $payinfo = '347594362484937'; #American Express
236 my $paydate = DateTime->now
238 ->truncate(to => 'month')
240 return ( 'payby' => 'CARD',
241 'payinfo' => $payinfo,
242 'paydate' => $paydate,
243 'payname' => 'Tokenize Me',
244 'paycvv' => '1234', #American Express