File Coverage

blib/lib/Business/OnlinePayment/CardFortress.pm
Criterion Covered Total %
statement 27 66 40.9
branch 3 24 12.5
condition 0 3 0.0
subroutine 8 11 72.7
pod 1 3 33.3
total 39 107 36.4


line stmt bran cond sub pod time code
1             package Business::OnlinePayment::CardFortress;
2              
3 2     2   13165 use base qw( Business::OnlinePayment::HTTPS );
  2         4  
  2         828  
4              
5 2     2   35253 use warnings;
  2         2  
  2         39  
6 2     2   6 use strict;
  2         4  
  2         29  
7             #use vars qw( $DEBUG $me );
8 2     2   538 use File::Slurp;
  2         7095  
  2         103  
9 2     2   778 use MIME::Base64;
  2         950  
  2         92  
10 2     2   810 use Crypt::OpenSSL::RSA;
  2         6134  
  2         155  
11              
12             our $VERSION = '0.04';
13              
14             sub _info {
15 0     0   0 my $self = shift;
16              
17 0         0 my $info = {
18             'info_compat' => '0.01',
19             'module_version' => $VERSION,
20             'gateway_name' => 'Card Fortress',
21             'gateway_url' => 'http://www.cardfortress.com/',
22             'supported_types' => [ 'CC' ],
23             'supported_actions' => { 'CC' => [
24             'Normal Authorization',
25             'Authorization Only',
26             'Post Authorization',
27             'Void',
28             'Credit',
29             'Tokenize',
30             ],
31             },
32             'token_support' => 1,
33             };
34              
35 0         0 my $cf_info = $self->cf_info;
36              
37 2     2   1112 use Data::Dumper;
  2         9869  
  2         1104  
38 0         0 warn Dumper($cf_info);
39              
40             $info->{$_} = $cf_info->{$_}
41 0         0 for qw( CC_void_requires_card ECHECK_void_requires_account partial_auth );
42              
43 0         0 $info;
44             }
45              
46             sub set_defaults {
47 2     2 0 1606 my $self = shift;
48 2         6 my %opts = @_;
49            
50 2 50       55 $self->server('gw.cardfortress.com') unless $self->server;
51              
52 2 50       108 $self->port('443') unless $self->port;
53 2 50       97 $self->path('/bop/index.html') unless $self->path;
54              
55 2         89 $self->build_subs(qw( order_number avs_code cvv2_response
56             response_page response_code response_headers
57             card_token private_key txn_date
58             ));
59             }
60              
61             sub submit {
62 0     0 1   my $self = shift;
63              
64 0 0         $self->server('test.cardfortress.com') if $self->test_transaction;
65              
66 0           my %content = $self->content;
67 0           $content{$_} = $self->$_() for qw( gateway gateway_login gateway_password );
68              
69 0           $content{$_} = $self->$_() for grep $self->can($_), qw( bop_options );
70              
71 0           my ($page,$server_response,%headers) = $self->https_post(%content);
72              
73 0 0         die "$server_response\n" unless $server_response =~ /^200/;
74              
75 0           my %response = ();
76             #this encoding good enough? wfm... if something's easier for other
77             #languages they can always use a different URL
78 0           foreach my $line ( grep /^\w+=/, split(/\n/, $page) ) {
79 0 0         $line =~ /^(\w+)=(.*)$/ or next;
80 0           $response{$1} = $2;
81             }
82              
83 0           foreach (qw( is_success error_message failure_status
84             authorization order_number
85             fraud_score fraud_transaction_id
86             result_code avs_code cvv2_response
87             card_token
88             txn_date
89             )) {
90 0           $self->$_($response{$_});
91             }
92              
93             #map these to gateway_response_code, etc?
94             # response_code()
95             # response_headers()
96             # response_page()
97              
98             #handle the challenge/response handshake
99 0 0         if ( $self->error_message eq '_challenge' ) { #XXX infinite loop protection?
100              
101 0 0         my $private_key = $self->private_key
102             or die "no private key available";
103              
104 0 0 0       $private_key = read_file($private_key)
105             if $private_key !~ /-----BEGIN/ && -r $private_key;
106              
107             #decrypt the challenge with the private key
108 0           my $challenge = decode_base64($response{'card_challenge'});
109              
110             #here is the hardest part to implement at each client side
111 0           my $rsa_priv = Crypt::OpenSSL::RSA->new_private_key($private_key);
112 0           my $response = $rsa_priv->decrypt($challenge);
113              
114             #try the transaction again with the challenge response
115             # (B:OP could sure use a better way to alter one value)
116 0           my %content = $self->content;
117 0           $content{'card_response'} = encode_base64($response, '');
118 0           $self->content(%content);
119 0           $self->submit;
120             }
121              
122             }
123              
124             sub cf_info {
125 0     0 0   my $self = shift;
126              
127 0 0         $self->server('test.cardfortress.com') if $self->test_transaction;
128              
129 0           my %content = ( 'gateway_info' => $self->gateway(), );
130              
131 0           my ($page,$server_response,%headers) = $self->https_post(%content);
132              
133 0 0         die "$server_response\n" unless $server_response =~ /^200/;
134              
135 0           my %response = ();
136             #this encoding good enough? wfm... if something's easier for other
137             #languages they can always use a different URL
138 0           foreach my $line ( grep /^\w+=/, split(/\n/, $page) ) {
139 0 0         $line =~ /^(\w+)=(.*)$/ or next;
140 0           $response{$1} = $2;
141             }
142              
143 0           \%response;
144              
145             }
146              
147             1;
148              
149             __END__