summaryrefslogtreecommitdiff
path: root/FS/t/suite/14-tokenization_refund.t
blob: 1a0f8405e9079691085378d5c9c37d35b059c2bd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
#!/usr/bin/perl

use strict;
use FS::Test;
use Test::More;
use FS::Conf;
use FS::cust_main;
use Business::CreditCard qw(generate_last_digit);
use DateTime;
if ( stat('/usr/local/etc/freeside/cardfortresstest.txt') ) {
  plan tests => 66;
} else {
  plan skip_all => 'CardFortress test encryption key is not installed.';
}

#local $FS::cust_main::Billing_Realtime::DEBUG = 2;

my $fs = FS::Test->new( user => 'admin' );
my $conf = FS::Conf->new;
my $err;
my @bopconf;

### can only run on test database (company name "Freeside Test")
like( $conf->config('company_name'), qr/^Freeside Test/, 'using test database' ) or BAIL_OUT('');

# these will just get in the way for now
foreach my $apg ($fs->qsearch('agent_payment_gateway')) {
  $err = $apg->delete;
  last if $err;
}
ok( !$err, 'removing agent gateway overrides' ) or BAIL_OUT($err);

# will need this
my $reason = FS::reason->new_or_existing(
  reason => 'Token Test',
  type   => 'Refund',
  class  => 'F',
);
isa_ok ( $reason, 'FS::reason', "refund reason" ) or BAIL_OUT('');

# non-tokenizing gateway
push @bopconf,
'IPPay
TESTTERMINAL';

# tokenizing gateway
push @bopconf,
'CardFortress
cardfortresstest
(TEST54)
Normal Authorization
gateway
IPPay
gateway_login
TESTTERMINAL
gateway_password

private_key
/usr/local/etc/freeside/cardfortresstest.txt';

foreach my $voiding (0,1) {
  my $noun = $voiding ? 'void' : 'refund';

  if ($voiding) {
    $conf->delete('disable_void_after');
    ok( !$conf->exists('disable_void_after'), 'set disable_void_after to produce voids' ) or BAIL_OUT('');
  } else {
    $conf->set('disable_void_after' => '0');
    is( $conf->config('disable_void_after'), '0', 'set disable_void_after to produce refunds' ) or BAIL_OUT('');
  }

  # for attempting refund post-tokenization
  my $n_cust_main;
  my $n_cust_pay;

  foreach my $tokenizing (0,1) {
    my $adj = $tokenizing ? 'tokenizable' : 'non-tokenizable';

    # set payment gateway
    $conf->set('business-onlinepayment' => $bopconf[$tokenizing]);
    is( join("\n",$conf->config('business-onlinepayment')), $bopconf[$tokenizing], "set $adj $noun default gateway" ) or BAIL_OUT('');

    # make sure we're upgraded, only need to do it once,
    # use non-tokenizing gateway for speed,
    # but doesn't matter if existing records are tokenized or not,
    # this suite is all about testing new record creation
    if (!$tokenizing && !$voiding) {
      $err = system('freeside-upgrade','-q','admin');
      ok( !$err, 'upgrade freeside' ) or BAIL_OUT('Error string: '.$!);
    }

    if ($tokenizing) {

      my $n_paynum = $n_cust_pay->paynum;

      # refund the previous non-tokenized payment through CF
      $err = $n_cust_main->realtime_refund_bop({
        reasonnum => $reason->reasonnum,
        paynum    => $n_paynum,
        method    => 'CC',
      });
      ok( !$err, "run post-switch $noun" ) or BAIL_OUT($err);

      my $n_cust_pay_void = $fs->qsearchs('cust_pay_void',{ paynum => $n_paynum });
      my $n_cust_refund   = $fs->qsearchs('cust_refund',{ source_paynum => $n_paynum });

      if ($voiding) {

        # check for void record
        isa_ok( $n_cust_pay_void, 'FS::cust_pay_void', 'post-switch void') or BAIL_OUT("paynum $n_paynum");

        # check that void tokenized
        ok ( $n_cust_pay_void->tokenized, "post-switch void tokenized" ) or BAIL_OUT("paynum $n_paynum");

        # check for no refund record
        ok( !$n_cust_refund, "post-switch void did not generate cust_refund" ) or BAIL_OUT("paynum $n_paynum");

      } else {

        # check for refund record
        isa_ok( $n_cust_refund, 'FS::cust_refund', 'post-switch refund') or BAIL_OUT("paynum $n_paynum");

        # check that refund tokenized
        ok ( $n_cust_refund->tokenized, "post-switch refund tokenized" ) or BAIL_OUT("paynum $n_paynum");

        # check for no refund record
        ok( !$n_cust_pay_void, "post-switch refund did not generate cust_pay_void" ) or BAIL_OUT("paynum $n_paynum");

      }

    }

    # create customer
    my $cust_main = $fs->new_customer($adj.'X'.$noun);
    isa_ok ( $cust_main, 'FS::cust_main', "$adj $noun customer" ) or BAIL_OUT('');

    # insert customer
    $err = $cust_main->insert;
    ok( !$err, "insert $adj $noun customer" ) or BAIL_OUT($err);

    # add card
    my $cust_payby;
    my %card = random_card();
    $err = $cust_main->save_cust_payby(
      %card,
      payment_payby => $card{'payby'},
      auto => 1,
      saved_cust_payby => \$cust_payby
    );
    ok( !$err, "save $adj $noun card" ) or BAIL_OUT($err);

    # retrieve card
    isa_ok ( $cust_payby, 'FS::cust_payby', "$adj $noun card" ) or BAIL_OUT('');

    # check that card tokenized or not
    if ($tokenizing) {
      ok( $cust_payby->tokenized, "new $noun cust card tokenized" ) or BAIL_OUT('');
    } else {
      ok( !$cust_payby->tokenized, "new $noun cust card not tokenized" ) or BAIL_OUT('');
    }

    # run a payment
    $err = $cust_main->realtime_cust_payby( amount => '1.00' );
    ok( !$err, "run $adj $noun payment" ) or BAIL_OUT($err);

    # get the payment
    my $cust_pay = $fs->qsearchs('cust_pay',{ custnum => $cust_main->custnum }); 
    isa_ok ( $cust_pay, 'FS::cust_pay', "$adj $noun payment" ) or BAIL_OUT('');

    # refund the payment
    $err = $cust_main->realtime_refund_bop({
      reasonnum => $reason->reasonnum,
      paynum    => $cust_pay->paynum,
      method    => 'CC',
    });
    ok( !$err, "run $adj $noun" ) or BAIL_OUT($err);

    unless ($tokenizing) {

      # run a second payment, to refund after switch
      $err = $cust_main->realtime_cust_payby( amount => '2.00' );
      ok( !$err, "run $adj $noun second payment" ) or BAIL_OUT($err);
    
      # get the second payment
      $n_cust_pay = $fs->qsearchs('cust_pay',{ custnum => $cust_main->custnum, paid => '2.00' });
      isa_ok ( $n_cust_pay, 'FS::cust_pay', "$adj $noun second payment" ) or BAIL_OUT('');

      $n_cust_main = $cust_main;

    }

    #check that all transactions tokenized or not
    foreach my $table (qw(cust_pay_pending cust_pay cust_pay_void cust_refund)) {
      foreach my $record ($fs->qsearch($table,{ custnum => $cust_main->custnum })) {
        if ($tokenizing) {
          $err = "record not tokenized: $table ".$record->get($record->primary_key)
            unless $record->tokenized;
        } else {
          $err = "record tokenized: $table ".$record->get($record->primary_key)
            if $record->tokenized;
        }
        last if $err;
      }
    }
    ok( !$err, "$adj transaction token check" ) or BAIL_OUT($err);

    if ($voiding) {

      #make sure we voided
      ok( $fs->qsearch('cust_pay_void',{ custnum => $cust_main->custnum}), "$adj $noun record found" ) or BAIL_OUT('');

      #make sure we didn't generate refund records
      ok( !$fs->qsearch('cust_refund',{ custnum => $cust_main->custnum}), "$adj $noun did not generate cust_refund" ) or BAIL_OUT('');

    } else {

      #make sure we refunded
      ok( $fs->qsearch('cust_refund',{ custnum => $cust_main->custnum}), "$adj $noun record found" ) or BAIL_OUT('');

      #make sure we didn't generate void records
      ok( !$fs->qsearch('cust_pay_void',{ custnum => $cust_main->custnum}), "$adj $noun did not generate cust_pay_void" ) or BAIL_OUT('');

    }

  } #end of tokenizing or not

} # end of voiding or not

exit;

sub random_card {
  my $payinfo = '4111' . join('', map { int(rand(10)) } 1 .. 11);
  $payinfo .= generate_last_digit($payinfo);
  my $paydate = DateTime->now
                ->add('years' => 1)
                ->truncate(to => 'month')
                ->strftime('%F');
  return ( 'payby'    => 'CARD',
           'payinfo'  => $payinfo,
           'paydate'  => $paydate,
           'payname'  => 'Tokenize Me',
  );
}

1;