| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Business::OnlinePayment::Litle; |
|
2
|
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
|
|
4
|
4
|
|
|
4
|
|
29124
|
use warnings; |
|
|
4
|
|
|
|
|
9
|
|
|
|
4
|
|
|
|
|
117
|
|
|
5
|
4
|
|
|
4
|
|
20
|
use strict; |
|
|
4
|
|
|
|
|
7
|
|
|
|
4
|
|
|
|
|
67
|
|
|
6
|
|
|
|
|
|
|
|
|
7
|
4
|
|
|
4
|
|
438
|
use Business::OnlinePayment; |
|
|
4
|
|
|
|
|
2547
|
|
|
|
4
|
|
|
|
|
78
|
|
|
8
|
4
|
|
|
4
|
|
1570
|
use Business::OnlinePayment::HTTPS; |
|
|
4
|
|
|
|
|
69079
|
|
|
|
4
|
|
|
|
|
163
|
|
|
9
|
4
|
|
|
4
|
|
1817
|
use Business::OnlinePayment::Litle::ErrorCodes '%ERRORS'; |
|
|
4
|
|
|
|
|
14
|
|
|
|
4
|
|
|
|
|
401
|
|
|
10
|
4
|
|
|
4
|
|
29
|
use vars qw(@ISA $me $DEBUG); |
|
|
4
|
|
|
|
|
9
|
|
|
|
4
|
|
|
|
|
203
|
|
|
11
|
4
|
|
|
4
|
|
1687
|
use MIME::Base64; |
|
|
4
|
|
|
|
|
1995
|
|
|
|
4
|
|
|
|
|
198
|
|
|
12
|
4
|
|
|
4
|
|
2306
|
use HTTP::Tiny; |
|
|
4
|
|
|
|
|
128420
|
|
|
|
4
|
|
|
|
|
137
|
|
|
13
|
4
|
|
|
4
|
|
2142
|
use XML::Writer; |
|
|
4
|
|
|
|
|
21593
|
|
|
|
4
|
|
|
|
|
109
|
|
|
14
|
4
|
|
|
4
|
|
2711
|
use XML::Simple; |
|
|
4
|
|
|
|
|
25978
|
|
|
|
4
|
|
|
|
|
31
|
|
|
15
|
4
|
|
|
4
|
|
282
|
use Tie::IxHash; |
|
|
4
|
|
|
|
|
11
|
|
|
|
4
|
|
|
|
|
103
|
|
|
16
|
4
|
|
|
4
|
|
1825
|
use Business::CreditCard qw(cardtype); |
|
|
4
|
|
|
|
|
5704
|
|
|
|
4
|
|
|
|
|
240
|
|
|
17
|
4
|
|
|
4
|
|
1574
|
use Data::Dumper; |
|
|
4
|
|
|
|
|
13806
|
|
|
|
4
|
|
|
|
|
219
|
|
|
18
|
4
|
|
|
4
|
|
1740
|
use IO::String; |
|
|
4
|
|
|
|
|
7383
|
|
|
|
4
|
|
|
|
|
123
|
|
|
19
|
4
|
|
|
4
|
|
78
|
use Carp qw(croak); |
|
|
4
|
|
|
|
|
31
|
|
|
|
4
|
|
|
|
|
216
|
|
|
20
|
4
|
|
|
4
|
|
1408
|
use Log::Scrubber qw(disable $SCRUBBER scrubber :Carp scrubber_add_scrubber); |
|
|
4
|
|
|
|
|
10749
|
|
|
|
4
|
|
|
|
|
25
|
|
|
21
|
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
@ISA = qw(Business::OnlinePayment::HTTPS); |
|
23
|
|
|
|
|
|
|
$me = 'Business::OnlinePayment::Litle'; |
|
24
|
|
|
|
|
|
|
$DEBUG = 0; |
|
25
|
|
|
|
|
|
|
our $VERSION = '0.957'; # VERSION |
|
26
|
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
# PODNAME: Business::OnlinePayment::Litle |
|
28
|
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
# ABSTRACT: Business::OnlinePayment::Litle - Vantiv (was Litle & Co.) Backend for Business::OnlinePayment |
|
30
|
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
sub server_request { |
|
33
|
11
|
|
|
11
|
1
|
43
|
my ( $self, $val, $tf ) = @_; |
|
34
|
11
|
100
|
|
|
|
127
|
if ($val) { |
|
35
|
5
|
|
|
|
|
36
|
$self->{server_request} = scrubber $val; |
|
36
|
5
|
50
|
|
|
|
709
|
$self->server_request_dangerous($val,1) unless $tf; |
|
37
|
|
|
|
|
|
|
} |
|
38
|
11
|
|
|
|
|
29
|
return $self->{server_request}; |
|
39
|
|
|
|
|
|
|
} |
|
40
|
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
sub server_request_dangerous { |
|
43
|
5
|
|
|
5
|
1
|
18
|
my ( $self, $val, $tf ) = @_; |
|
44
|
5
|
50
|
|
|
|
17
|
if ($val) { |
|
45
|
5
|
|
|
|
|
12
|
$self->{server_request_dangerous} = $val; |
|
46
|
5
|
50
|
|
|
|
20
|
$self->server_request($val,1) unless $tf; |
|
47
|
|
|
|
|
|
|
} |
|
48
|
5
|
|
|
|
|
13
|
return $self->{server_request_dangerous}; |
|
49
|
|
|
|
|
|
|
} |
|
50
|
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
sub server_response { |
|
53
|
11
|
|
|
11
|
1
|
39
|
my ( $self, $val, $tf ) = @_; |
|
54
|
11
|
100
|
|
|
|
43
|
if ($val) { |
|
55
|
5
|
|
|
|
|
28
|
$self->{server_response} = scrubber $val; |
|
56
|
5
|
50
|
|
|
|
589
|
$self->server_response_dangerous($val,1) unless $tf; |
|
57
|
|
|
|
|
|
|
} |
|
58
|
11
|
|
|
|
|
28
|
return $self->{server_response}; |
|
59
|
|
|
|
|
|
|
} |
|
60
|
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
sub server_response_dangerous { |
|
63
|
5
|
|
|
5
|
1
|
19
|
my ( $self, $val, $tf ) = @_; |
|
64
|
5
|
50
|
|
|
|
19
|
if ($val) { |
|
65
|
5
|
|
|
|
|
17
|
$self->{server_response_dangerous} = $val; |
|
66
|
5
|
50
|
|
|
|
22
|
$self->server_response($val,1) unless $tf; |
|
67
|
|
|
|
|
|
|
} |
|
68
|
5
|
|
|
|
|
34
|
return $self->{server_response_dangerous}; |
|
69
|
|
|
|
|
|
|
} |
|
70
|
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
sub _info { |
|
74
|
|
|
|
|
|
|
return { |
|
75
|
0
|
|
|
0
|
|
0
|
info_compat => '0.01', |
|
76
|
|
|
|
|
|
|
gateway_name => 'Litle', |
|
77
|
|
|
|
|
|
|
gateway_url => 'http://www.vantiv.com', |
|
78
|
|
|
|
|
|
|
module_version => $VERSION, |
|
79
|
|
|
|
|
|
|
supported_types => ['CC'], |
|
80
|
|
|
|
|
|
|
supported_actions => { |
|
81
|
|
|
|
|
|
|
CC => [ |
|
82
|
|
|
|
|
|
|
'Normal Authorization', |
|
83
|
|
|
|
|
|
|
'Post Authorization', |
|
84
|
|
|
|
|
|
|
'Authorization Only', |
|
85
|
|
|
|
|
|
|
'Credit', |
|
86
|
|
|
|
|
|
|
'Void', |
|
87
|
|
|
|
|
|
|
'Auth Reversal', |
|
88
|
|
|
|
|
|
|
], |
|
89
|
|
|
|
|
|
|
}, |
|
90
|
|
|
|
|
|
|
}; |
|
91
|
|
|
|
|
|
|
} |
|
92
|
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
sub set_defaults { |
|
95
|
8
|
|
|
8
|
1
|
6987
|
my $self = shift; |
|
96
|
8
|
|
|
|
|
29
|
my %opts = @_; |
|
97
|
|
|
|
|
|
|
|
|
98
|
8
|
|
|
|
|
48
|
$self->build_subs( |
|
99
|
|
|
|
|
|
|
qw( order_number md5 avs_code cvv2_response card_token |
|
100
|
|
|
|
|
|
|
cavv_response api_version xmlns failure_status batch_api_version chargeback_api_version |
|
101
|
|
|
|
|
|
|
is_prepaid prepaid_balance get_affluence chargeback_server chargeback_port chargeback_path |
|
102
|
|
|
|
|
|
|
verify_SSL phoenixTxnId is_duplicate card_token card_token_response card_token_message |
|
103
|
|
|
|
|
|
|
) |
|
104
|
|
|
|
|
|
|
); |
|
105
|
|
|
|
|
|
|
|
|
106
|
8
|
|
|
|
|
3235
|
$self->test_transaction(0); |
|
107
|
|
|
|
|
|
|
|
|
108
|
8
|
50
|
|
|
|
27
|
if ( $opts{debug} ) { |
|
109
|
0
|
|
|
|
|
0
|
$self->debug( $opts{debug} ); |
|
110
|
0
|
|
|
|
|
0
|
delete $opts{debug}; |
|
111
|
|
|
|
|
|
|
} |
|
112
|
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
## load in the defaults |
|
114
|
8
|
|
|
|
|
19
|
my %_defaults = (); |
|
115
|
8
|
|
|
|
|
25
|
foreach my $key ( keys %opts ) { |
|
116
|
8
|
50
|
|
|
|
35
|
$key =~ /^default_(\w*)$/ or next; |
|
117
|
8
|
|
|
|
|
31
|
$_defaults{$1} = $opts{$key}; |
|
118
|
8
|
|
|
|
|
21
|
delete $opts{$key}; |
|
119
|
|
|
|
|
|
|
} |
|
120
|
|
|
|
|
|
|
|
|
121
|
8
|
|
|
|
|
31
|
$self->{_scrubber} = \&_default_scrubber; |
|
122
|
8
|
100
|
|
|
|
24
|
if( defined $_defaults{'Scrubber'} ) { |
|
123
|
2
|
|
|
|
|
5
|
my $code = $_defaults{'Scrubber'}; |
|
124
|
2
|
100
|
|
|
|
6
|
if( ref($code) ne 'CODE' ) { |
|
125
|
1
|
|
|
|
|
33
|
warn('default_Scrubber is not a code ref'); |
|
126
|
|
|
|
|
|
|
} |
|
127
|
|
|
|
|
|
|
else { |
|
128
|
1
|
|
|
|
|
2
|
$self->{_scrubber} = $code; |
|
129
|
|
|
|
|
|
|
} |
|
130
|
|
|
|
|
|
|
} |
|
131
|
|
|
|
|
|
|
|
|
132
|
8
|
50
|
|
|
|
140
|
$self->api_version('8.1') unless $self->api_version; |
|
133
|
8
|
50
|
|
|
|
332
|
$self->batch_api_version('8.1') unless $self->batch_api_version; |
|
134
|
8
|
50
|
|
|
|
346
|
$self->chargeback_api_version('2.2') unless $self->chargeback_api_version; |
|
135
|
8
|
50
|
|
|
|
358
|
$self->xmlns('http://www.litle.com/schema') unless $self->xmlns; |
|
136
|
|
|
|
|
|
|
} |
|
137
|
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
sub test_transaction { |
|
140
|
15
|
|
|
15
|
1
|
11249
|
my $self = shift; |
|
141
|
15
|
|
|
|
|
29
|
my $testMode = shift; |
|
142
|
15
|
50
|
0
|
|
|
48
|
if (! defined $testMode) { $testMode = $self->{'test_transaction'} || 0; } |
|
|
0
|
|
|
|
|
0
|
|
|
143
|
|
|
|
|
|
|
|
|
144
|
15
|
100
|
|
|
|
86
|
if (lc($testMode) eq 'sandbox') { |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
145
|
6
|
|
|
|
|
17
|
$self->{'test_transaction'} = 'sandbox'; |
|
146
|
6
|
|
|
|
|
124
|
$self->verify_SSL(0); |
|
147
|
|
|
|
|
|
|
|
|
148
|
6
|
|
|
|
|
150
|
$self->server('www.testlitle.com'); |
|
149
|
6
|
|
|
|
|
174
|
$self->port('443'); |
|
150
|
6
|
|
|
|
|
143
|
$self->path('/sandbox/communicator/online'); |
|
151
|
|
|
|
|
|
|
|
|
152
|
6
|
|
|
|
|
155
|
$self->chargeback_server('service-postlive.litle.com'); # no sandbox exists, so fallback to certify |
|
153
|
6
|
|
|
|
|
148
|
$self->chargeback_port('443'); |
|
154
|
6
|
|
|
|
|
133
|
$self->chargeback_path('/services/communicator/chargebacks/webCommunicator'); |
|
155
|
|
|
|
|
|
|
} elsif (lc($testMode) eq 'localhost') { |
|
156
|
|
|
|
|
|
|
# this allows the user to create a local web server to do generic testing with |
|
157
|
0
|
|
|
|
|
0
|
$self->{'test_transaction'} = 'localhost'; |
|
158
|
0
|
|
|
|
|
0
|
$self->verify_SSL(0); |
|
159
|
|
|
|
|
|
|
|
|
160
|
0
|
|
|
|
|
0
|
$self->server('localhost'); |
|
161
|
0
|
|
|
|
|
0
|
$self->port('443'); |
|
162
|
0
|
|
|
|
|
0
|
$self->path('/sandbox/communicator/online'); |
|
163
|
|
|
|
|
|
|
|
|
164
|
0
|
|
|
|
|
0
|
$self->chargeback_server('localhost'); |
|
165
|
0
|
|
|
|
|
0
|
$self->chargeback_port('443'); |
|
166
|
0
|
|
|
|
|
0
|
$self->chargeback_path('/services/communicator/chargebacks/webCommunicator'); |
|
167
|
|
|
|
|
|
|
} elsif (lc($testMode) eq 'prelive') { |
|
168
|
0
|
|
|
|
|
0
|
$self->{'test_transaction'} = $testMode; |
|
169
|
0
|
|
|
|
|
0
|
$self->verify_SSL(0); |
|
170
|
|
|
|
|
|
|
|
|
171
|
0
|
|
|
|
|
0
|
$self->server('payments.vantivprelive.com'); |
|
172
|
0
|
|
|
|
|
0
|
$self->port('443'); |
|
173
|
0
|
|
|
|
|
0
|
$self->path('/vap/communicator/online'); |
|
174
|
|
|
|
|
|
|
|
|
175
|
0
|
|
|
|
|
0
|
$self->chargeback_server('services.vantivprelive.com'); |
|
176
|
0
|
|
|
|
|
0
|
$self->chargeback_port('443'); |
|
177
|
0
|
|
|
|
|
0
|
$self->chargeback_path('/services/communicator/chargebacks/webCommunicator'); |
|
178
|
|
|
|
|
|
|
} elsif ($testMode) { |
|
179
|
1
|
|
|
|
|
6
|
$self->{'test_transaction'} = $testMode; |
|
180
|
1
|
|
|
|
|
18
|
$self->verify_SSL(0); |
|
181
|
|
|
|
|
|
|
|
|
182
|
1
|
|
|
|
|
23
|
$self->server('payments.vantivpostlive.com'); |
|
183
|
1
|
|
|
|
|
23
|
$self->port('443'); |
|
184
|
1
|
|
|
|
|
22
|
$self->path('/vap/communicator/online'); |
|
185
|
|
|
|
|
|
|
|
|
186
|
1
|
|
|
|
|
22
|
$self->chargeback_server('services.vantivpostlive.com'); |
|
187
|
1
|
|
|
|
|
22
|
$self->chargeback_port('443'); |
|
188
|
1
|
|
|
|
|
28
|
$self->chargeback_path('/services/communicator/chargebacks/webCommunicator'); |
|
189
|
|
|
|
|
|
|
} else { |
|
190
|
8
|
|
|
|
|
35
|
$self->{'test_transaction'} = 0; |
|
191
|
8
|
|
|
|
|
190
|
$self->verify_SSL(1); |
|
192
|
|
|
|
|
|
|
|
|
193
|
8
|
|
|
|
|
223
|
$self->server('payments.vantivcnp.com'); |
|
194
|
8
|
|
|
|
|
200
|
$self->port('443'); |
|
195
|
8
|
|
|
|
|
490
|
$self->path('/vap/communicator/online'); |
|
196
|
|
|
|
|
|
|
|
|
197
|
8
|
|
|
|
|
179
|
$self->chargeback_server('services.vantivcnp.com'); |
|
198
|
8
|
|
|
|
|
172
|
$self->chargeback_port('443'); |
|
199
|
8
|
|
|
|
|
176
|
$self->chargeback_path('/services/communicator/chargebacks/webCommunicator'); |
|
200
|
|
|
|
|
|
|
} |
|
201
|
|
|
|
|
|
|
|
|
202
|
15
|
|
|
|
|
133
|
return $self->{'test_transaction'}; |
|
203
|
|
|
|
|
|
|
} |
|
204
|
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
sub map_fields { |
|
207
|
6
|
|
|
6
|
1
|
16
|
my ( $self, $content ) = @_; |
|
208
|
|
|
|
|
|
|
|
|
209
|
6
|
|
|
|
|
16
|
my $action = lc( $content->{'action'} ); |
|
210
|
6
|
|
|
|
|
45
|
my %actions = ( |
|
211
|
|
|
|
|
|
|
'normal authorization' => 'sale', |
|
212
|
|
|
|
|
|
|
'authorization only' => 'authorization', |
|
213
|
|
|
|
|
|
|
'post authorization' => 'capture', |
|
214
|
|
|
|
|
|
|
'void' => 'void', |
|
215
|
|
|
|
|
|
|
'credit' => 'credit', |
|
216
|
|
|
|
|
|
|
'auth reversal' => 'authReversal', |
|
217
|
|
|
|
|
|
|
'account update' => 'accountUpdate', |
|
218
|
|
|
|
|
|
|
'tokenize' => 'registerTokenRequest', |
|
219
|
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
# AVS ONLY |
|
221
|
|
|
|
|
|
|
# Capture Given |
|
222
|
|
|
|
|
|
|
# Force Capture |
|
223
|
|
|
|
|
|
|
# |
|
224
|
|
|
|
|
|
|
); |
|
225
|
6
|
|
33
|
|
|
29
|
$content->{'TransactionType'} = $actions{$action} || $action; |
|
226
|
|
|
|
|
|
|
|
|
227
|
6
|
|
|
|
|
43
|
my $type_translate = { |
|
228
|
|
|
|
|
|
|
'VISA card' => 'VI', |
|
229
|
|
|
|
|
|
|
'MasterCard' => 'MC', |
|
230
|
|
|
|
|
|
|
'Discover card' => 'DI', |
|
231
|
|
|
|
|
|
|
'American Express card' => 'AX', |
|
232
|
|
|
|
|
|
|
'Diner\'s Club/Carte Blanche' => 'DI', |
|
233
|
|
|
|
|
|
|
'JCB' => 'DI', |
|
234
|
|
|
|
|
|
|
'China Union Pay' => 'DI', |
|
235
|
|
|
|
|
|
|
}; |
|
236
|
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
$content->{'card_type'} = |
|
238
|
|
|
|
|
|
|
$type_translate->{ cardtype( $content->{'card_number'} ) } |
|
239
|
6
|
100
|
33
|
|
|
35
|
|| $content->{'type'} if $content->{'card_number'}; |
|
240
|
|
|
|
|
|
|
|
|
241
|
6
|
50
|
33
|
|
|
165
|
if ( $content->{recurring_billing} |
|
242
|
|
|
|
|
|
|
&& $content->{recurring_billing} eq 'YES' ) |
|
243
|
|
|
|
|
|
|
{ |
|
244
|
0
|
|
|
|
|
0
|
$content->{'orderSource'} = 'recurring'; |
|
245
|
|
|
|
|
|
|
} |
|
246
|
|
|
|
|
|
|
else { |
|
247
|
6
|
|
|
|
|
16
|
$content->{'orderSource'} = 'ecommerce'; |
|
248
|
|
|
|
|
|
|
} |
|
249
|
|
|
|
|
|
|
$content->{'customerType'} = |
|
250
|
6
|
50
|
|
|
|
20
|
$content->{'orderSource'} eq 'recurring' |
|
251
|
|
|
|
|
|
|
? 'Existing' |
|
252
|
|
|
|
|
|
|
: 'New'; # new/Existing |
|
253
|
|
|
|
|
|
|
|
|
254
|
6
|
|
|
|
|
15
|
$content->{'deliverytype'} = 'SVC'; |
|
255
|
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
# stuff it back into %content |
|
257
|
6
|
50
|
33
|
|
|
43
|
if ( $content->{'products'} && ref( $content->{'products'} ) eq 'ARRAY' ) { |
|
258
|
6
|
|
|
|
|
12
|
my $count = 1; |
|
259
|
6
|
|
|
|
|
13
|
foreach ( @{ $content->{'products'} } ) { |
|
|
6
|
|
|
|
|
18
|
|
|
260
|
12
|
|
|
|
|
28
|
$_->{'itemSequenceNumber'} = $count++; |
|
261
|
|
|
|
|
|
|
} |
|
262
|
|
|
|
|
|
|
} |
|
263
|
|
|
|
|
|
|
|
|
264
|
6
|
50
|
0
|
|
|
26
|
if( $content->{'velocity_check'} && ( |
|
|
|
|
33
|
|
|
|
|
|
265
|
|
|
|
|
|
|
$content->{'velocity_check'} != 0 |
|
266
|
|
|
|
|
|
|
&& $content->{'velocity_check'} !~ m/false/i ) ) { |
|
267
|
0
|
|
|
|
|
0
|
$content->{'velocity_check'} = 'true'; |
|
268
|
|
|
|
|
|
|
} else { |
|
269
|
6
|
|
|
|
|
15
|
$content->{'velocity_check'} = 'false'; |
|
270
|
|
|
|
|
|
|
} |
|
271
|
|
|
|
|
|
|
|
|
272
|
6
|
50
|
0
|
|
|
21
|
if( $content->{'partial_auth'} && ( |
|
|
|
|
33
|
|
|
|
|
|
273
|
|
|
|
|
|
|
$content->{'partial_auth'} != 0 |
|
274
|
|
|
|
|
|
|
&& $content->{'partial_auth'} !~ m/false/i ) ) { |
|
275
|
0
|
|
|
|
|
0
|
$content->{'partial_auth'} = 'true'; |
|
276
|
|
|
|
|
|
|
} else { |
|
277
|
6
|
|
|
|
|
13
|
$content->{'partial_auth'} = 'false'; |
|
278
|
|
|
|
|
|
|
} |
|
279
|
|
|
|
|
|
|
|
|
280
|
6
|
|
|
|
|
12
|
$self->content( %{$content} ); |
|
|
6
|
|
|
|
|
49
|
|
|
281
|
6
|
|
|
|
|
424
|
return $content; |
|
282
|
|
|
|
|
|
|
} |
|
283
|
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
sub format_misc_field { |
|
286
|
288
|
|
|
288
|
1
|
511
|
my ($self, $content, $trunc) = @_; |
|
287
|
|
|
|
|
|
|
|
|
288
|
288
|
100
|
|
|
|
663
|
if( defined $content->{ $trunc->[0] } ) { |
|
|
|
50
|
|
|
|
|
|
|
289
|
232
|
|
|
|
|
636
|
utf8::upgrade($content->{ $trunc->[0] }); |
|
290
|
232
|
|
|
|
|
482
|
my $len = length( $content->{ $trunc->[0] } ); |
|
291
|
232
|
50
|
100
|
|
|
1291
|
if ( $trunc->[3] && $trunc->[2] && $len != 0 && $len < $trunc->[2] ) { |
|
|
|
50
|
100
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
292
|
|
|
|
|
|
|
# Zero is a valid length (mostly for cvv2 value) |
|
293
|
0
|
|
|
|
|
0
|
croak "$trunc->[0] has too few characters"; |
|
294
|
|
|
|
|
|
|
} |
|
295
|
|
|
|
|
|
|
elsif ( $trunc->[3] && $trunc->[1] && $len > $trunc->[1] ) { |
|
296
|
0
|
|
|
|
|
0
|
croak "$trunc->[0] has too many characters"; |
|
297
|
|
|
|
|
|
|
} |
|
298
|
232
|
|
|
|
|
657
|
$content->{ $trunc->[0] } = substr($content->{ $trunc->[0] } , 0, $trunc->[1] ); |
|
299
|
|
|
|
|
|
|
#warn "$trunc->[0] => $len => $content->{ $trunc->[0] }\n" if $DEBUG; |
|
300
|
|
|
|
|
|
|
} |
|
301
|
|
|
|
|
|
|
elsif ( $trunc->[4] ) { |
|
302
|
0
|
|
|
|
|
0
|
croak "$trunc->[0] is required"; |
|
303
|
|
|
|
|
|
|
} |
|
304
|
|
|
|
|
|
|
} |
|
305
|
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
sub format_amount_field { |
|
308
|
78
|
|
|
78
|
1
|
147
|
my ($self, $data, $field) = @_; |
|
309
|
78
|
100
|
|
|
|
198
|
if (defined ( $data->{$field} ) ) { |
|
310
|
54
|
|
|
|
|
324
|
$data->{$field} = sprintf( "%.2f", $data->{$field} ); |
|
311
|
54
|
|
|
|
|
207
|
$data->{$field} =~ s/\.//g; |
|
312
|
|
|
|
|
|
|
} |
|
313
|
|
|
|
|
|
|
} |
|
314
|
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
sub format_phone_field { |
|
317
|
6
|
|
|
6
|
1
|
15
|
my ($self, $data, $field) = @_; |
|
318
|
6
|
50
|
|
|
|
21
|
if (defined ( $data->{$field} ) ) { |
|
319
|
6
|
|
|
|
|
101
|
my $convertPhone = { |
|
320
|
|
|
|
|
|
|
'a' => 2, 'b' => 2, 'c' => 2, |
|
321
|
|
|
|
|
|
|
'd' => 3, 'e' => 3, 'f' => 3, |
|
322
|
|
|
|
|
|
|
'g' => 4, 'h' => 4, 'i' => 4, |
|
323
|
|
|
|
|
|
|
'j' => 5, 'k' => 5, 'l' => 5, |
|
324
|
|
|
|
|
|
|
'm' => 6, 'n' => 6, 'o' => 6, |
|
325
|
|
|
|
|
|
|
'p' => 7, 'q' => 7, 'r' => 7, 's' => 7, |
|
326
|
|
|
|
|
|
|
't' => 8, 'u' => 8, 'v' => 8, |
|
327
|
|
|
|
|
|
|
'w' => 9, 'x' => 9, 'y' => 9, 'z' => 9, |
|
328
|
|
|
|
|
|
|
}; |
|
329
|
6
|
50
|
|
|
|
27
|
$data->{$field} =~ s/(\D)/$$convertPhone{lc($1)}||''/eg; |
|
|
12
|
|
|
|
|
118
|
|
|
330
|
|
|
|
|
|
|
} |
|
331
|
|
|
|
|
|
|
} |
|
332
|
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
|
|
334
|
|
|
|
|
|
|
sub map_request { |
|
335
|
6
|
|
|
6
|
1
|
17
|
my ( $self, $content ) = @_; |
|
336
|
|
|
|
|
|
|
|
|
337
|
6
|
|
|
|
|
27
|
$self->map_fields($content); |
|
338
|
|
|
|
|
|
|
|
|
339
|
6
|
|
|
|
|
15
|
my $action = $content->{'TransactionType'}; |
|
340
|
|
|
|
|
|
|
|
|
341
|
6
|
|
|
|
|
19
|
my @required_fields = qw(action type); |
|
342
|
|
|
|
|
|
|
|
|
343
|
6
|
|
|
|
|
46
|
$self->required_fields(@required_fields); |
|
344
|
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
# for tabbing |
|
346
|
|
|
|
|
|
|
# set dollar amounts to the required format (eg $5.00 should be 500) |
|
347
|
6
|
|
|
|
|
253
|
foreach my $field ( 'amount', 'salesTax', 'discountAmount', 'shippingAmount', 'dutyAmount' ) { |
|
348
|
30
|
|
|
|
|
72
|
$self->format_amount_field($content, $field); |
|
349
|
|
|
|
|
|
|
} |
|
350
|
|
|
|
|
|
|
|
|
351
|
|
|
|
|
|
|
# make sure the date is in MMYY format |
|
352
|
6
|
|
|
|
|
53
|
$content->{'expiration'} =~ s/^(\d{1,2})\D*\d*?(\d{2})$/$1$2/; |
|
353
|
|
|
|
|
|
|
|
|
354
|
6
|
50
|
|
|
|
33
|
if ( ! defined $content->{'description'} ) { $content->{'description'} = ''; } # schema req |
|
|
0
|
|
|
|
|
0
|
|
|
355
|
6
|
|
|
|
|
27
|
$content->{'description'} =~ s/[^\w\s\*\,\-\'\#\&\.]//g; |
|
356
|
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
# Litle pre 0.934 used token, however BOP likes card_token |
|
358
|
6
|
50
|
66
|
|
|
36
|
$content->{'card_token'} = $content->{'token'} if ! defined $content->{'card_token'} && defined $content->{'card_token'}; |
|
359
|
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
# only numbers are allowed in company_phone |
|
361
|
6
|
|
|
|
|
31
|
$self->format_phone_field($content, 'company_phone'); |
|
362
|
|
|
|
|
|
|
|
|
363
|
6
|
|
33
|
|
|
47
|
$content->{'invoice_number_length_15'} ||= $content->{'invoice_number'}; # orderId = 25, invoiceReferenceNumber = 15 |
|
364
|
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
# put in a list of constraints |
|
366
|
6
|
|
|
|
|
151
|
my @validate = ( |
|
367
|
|
|
|
|
|
|
# field, maxLen, minLen, errorOnLength, isRequired |
|
368
|
|
|
|
|
|
|
[ 'name', 100, 0, 0, 0 ], |
|
369
|
|
|
|
|
|
|
[ 'email', 100, 0, 0, 0 ], |
|
370
|
|
|
|
|
|
|
[ 'address', 35, 0, 0, 0 ], |
|
371
|
|
|
|
|
|
|
[ 'city', 35, 0, 0, 0 ], |
|
372
|
|
|
|
|
|
|
[ 'state', 30, 0, 0, 0 ], # 30 is allowed, but it should be the 2 char code |
|
373
|
|
|
|
|
|
|
[ 'zip', 20, 0, 0, 0 ], |
|
374
|
|
|
|
|
|
|
[ 'country', 3, 0, 0, 0 ], # should use iso 3166-1 2 char code |
|
375
|
|
|
|
|
|
|
[ 'phone', 20, 0, 0, 0 ], |
|
376
|
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
[ 'ship_name', 100, 0, 0, 0 ], |
|
378
|
|
|
|
|
|
|
[ 'ship_email', 100, 0, 0, 0 ], |
|
379
|
|
|
|
|
|
|
[ 'ship_address',35, 0, 0, 0 ], |
|
380
|
|
|
|
|
|
|
[ 'ship_city', 35, 0, 0, 0 ], |
|
381
|
|
|
|
|
|
|
[ 'ship_state', 30, 0, 0, 0 ], # 30 is allowed, but it should be the 2 char code |
|
382
|
|
|
|
|
|
|
[ 'ship_zip', 20, 0, 0, 0 ], |
|
383
|
|
|
|
|
|
|
[ 'ship_country', 3, 0, 0, 0 ], # should use iso 3166-1 2 char code |
|
384
|
|
|
|
|
|
|
[ 'ship_phone', 20, 0, 0, 0 ], |
|
385
|
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
#[ 'customerType',13, 0, 0, 0 ], |
|
387
|
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
['company_phone',13, 0, 0, 0 ], |
|
389
|
|
|
|
|
|
|
[ 'description', 25, 0, 0, 0 ], |
|
390
|
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
[ 'po_number', 17, 0, 0, 0 ], |
|
392
|
|
|
|
|
|
|
[ 'salestax', 8, 0, 1, 0 ], |
|
393
|
|
|
|
|
|
|
[ 'discount', 8, 0, 1, 0 ], |
|
394
|
|
|
|
|
|
|
[ 'shipping', 8, 0, 1, 0 ], |
|
395
|
|
|
|
|
|
|
[ 'duty', 8, 0, 1, 0 ], |
|
396
|
|
|
|
|
|
|
['invoice_number',25, 0, 0, 0 ], |
|
397
|
|
|
|
|
|
|
['invoice_number_length_15',15,0, 0, 0 ], |
|
398
|
|
|
|
|
|
|
[ 'orderdate', 10, 0, 0, 0 ], # YYYY-MM-DD |
|
399
|
|
|
|
|
|
|
|
|
400
|
|
|
|
|
|
|
[ 'recycle_by', 8, 0, 0, 0 ], |
|
401
|
|
|
|
|
|
|
[ 'recycle_id', 25, 0, 0, 0 ], |
|
402
|
|
|
|
|
|
|
|
|
403
|
|
|
|
|
|
|
[ 'affiliate', 25, 0, 0, 0 ], |
|
404
|
|
|
|
|
|
|
|
|
405
|
|
|
|
|
|
|
[ 'card_type', 2, 2, 1, 0 ], |
|
406
|
|
|
|
|
|
|
[ 'card_number', 25, 13, 1, 0 ], |
|
407
|
|
|
|
|
|
|
[ 'expiration', 4, 4, 1, 0 ], # MMYY |
|
408
|
|
|
|
|
|
|
[ 'cvv2', 4, 3, 1, 0 ], |
|
409
|
|
|
|
|
|
|
# 'card_token' does not have a documented limit |
|
410
|
|
|
|
|
|
|
|
|
411
|
|
|
|
|
|
|
[ 'customer_id', 25, 0, 0, 0 ], |
|
412
|
|
|
|
|
|
|
); |
|
413
|
6
|
|
|
|
|
23
|
foreach my $trunc ( @validate ) { |
|
414
|
204
|
|
|
|
|
428
|
$self->format_misc_field($content,$trunc); |
|
415
|
|
|
|
|
|
|
#warn "$trunc->[0] => ".($content->{ $trunc->[0] }||'')."\n" if $DEBUG; |
|
416
|
|
|
|
|
|
|
} |
|
417
|
|
|
|
|
|
|
|
|
418
|
6
|
|
|
|
|
41
|
tie my %billToAddress, 'Tie::IxHash', $self->_revmap_fields( |
|
419
|
|
|
|
|
|
|
content => $content, |
|
420
|
|
|
|
|
|
|
name => 'name', |
|
421
|
|
|
|
|
|
|
email => 'email', |
|
422
|
|
|
|
|
|
|
addressLine1 => 'address', |
|
423
|
|
|
|
|
|
|
city => 'city', |
|
424
|
|
|
|
|
|
|
state => 'state', |
|
425
|
|
|
|
|
|
|
zip => 'zip', |
|
426
|
|
|
|
|
|
|
country => 'country' |
|
427
|
|
|
|
|
|
|
, #TODO: will require validation to the spec, this field wont' work as is |
|
428
|
|
|
|
|
|
|
phone => 'phone', |
|
429
|
|
|
|
|
|
|
); |
|
430
|
|
|
|
|
|
|
|
|
431
|
6
|
|
|
|
|
598
|
tie my %shipToAddress, 'Tie::IxHash', $self->_revmap_fields( |
|
432
|
|
|
|
|
|
|
content => $content, |
|
433
|
|
|
|
|
|
|
name => 'ship_name', |
|
434
|
|
|
|
|
|
|
email => 'ship_email', |
|
435
|
|
|
|
|
|
|
addressLine1 => 'ship_address', |
|
436
|
|
|
|
|
|
|
city => 'ship_city', |
|
437
|
|
|
|
|
|
|
state => 'ship_state', |
|
438
|
|
|
|
|
|
|
zip => 'ship_zip', |
|
439
|
|
|
|
|
|
|
country => 'ship_country' |
|
440
|
|
|
|
|
|
|
, #TODO: will require validation to the spec, this field wont' work as is |
|
441
|
|
|
|
|
|
|
phone => 'ship_phone', |
|
442
|
|
|
|
|
|
|
); |
|
443
|
|
|
|
|
|
|
|
|
444
|
6
|
|
|
|
|
514
|
tie my %customerinfo, 'Tie::IxHash', |
|
445
|
|
|
|
|
|
|
$self->_revmap_fields( |
|
446
|
|
|
|
|
|
|
content => $content, |
|
447
|
|
|
|
|
|
|
customerType => 'customerType', |
|
448
|
|
|
|
|
|
|
); |
|
449
|
|
|
|
|
|
|
|
|
450
|
6
|
|
|
|
|
191
|
tie my %custombilling, 'Tie::IxHash', |
|
451
|
|
|
|
|
|
|
$self->_revmap_fields( |
|
452
|
|
|
|
|
|
|
content => $content, |
|
453
|
|
|
|
|
|
|
phone => 'company_phone', |
|
454
|
|
|
|
|
|
|
descriptor => 'description', |
|
455
|
|
|
|
|
|
|
); |
|
456
|
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
## loop through product list and generate lineItemData for each |
|
458
|
|
|
|
|
|
|
# |
|
459
|
6
|
|
|
|
|
311
|
my @products = (); |
|
460
|
6
|
50
|
33
|
|
|
27
|
if( defined $content->{'products'} && scalar( @{ $content->{'products'} } ) < 100 ){ |
|
|
6
|
|
|
|
|
30
|
|
|
461
|
6
|
|
|
|
|
12
|
foreach my $prodOrig ( @{ $content->{'products'} } ) { |
|
|
6
|
|
|
|
|
21
|
|
|
462
|
|
|
|
|
|
|
# use a local copy of prod so that we do not have issues if they try to submit more then once. |
|
463
|
12
|
|
|
|
|
106
|
my %prod = %$prodOrig; |
|
464
|
12
|
|
|
|
|
33
|
foreach my $field ( 'tax','amount','totalwithtax','discount' ) { |
|
465
|
|
|
|
|
|
|
# Note: DO NOT format 'cost', it uses the decimal format |
|
466
|
48
|
|
|
|
|
109
|
$self->format_amount_field(\%prod, $field); |
|
467
|
|
|
|
|
|
|
} |
|
468
|
|
|
|
|
|
|
|
|
469
|
12
|
|
|
|
|
82
|
my @validate = ( |
|
470
|
|
|
|
|
|
|
# field, maxLen, minLen, errorOnLength, isRequired |
|
471
|
|
|
|
|
|
|
[ 'description', 26, 0, 0, 0 ], |
|
472
|
|
|
|
|
|
|
[ 'tax', 8, 0, 1, 0 ], |
|
473
|
|
|
|
|
|
|
[ 'amount', 8, 0, 1, 0 ], |
|
474
|
|
|
|
|
|
|
[ 'totalwithtax', 8, 0, 1, 0 ], |
|
475
|
|
|
|
|
|
|
[ 'discount', 8, 0, 1, 0 ], |
|
476
|
|
|
|
|
|
|
[ 'code', 12, 0, 0, 0 ], |
|
477
|
|
|
|
|
|
|
[ 'cost', 12, 0, 1, 0 ], |
|
478
|
|
|
|
|
|
|
); |
|
479
|
12
|
|
|
|
|
37
|
foreach my $trunc ( @validate ) { $self->format_misc_field(\%prod,$trunc); } |
|
|
84
|
|
|
|
|
181
|
|
|
480
|
|
|
|
|
|
|
|
|
481
|
12
|
|
|
|
|
47
|
tie my %lineitem, 'Tie::IxHash', |
|
482
|
|
|
|
|
|
|
$self->_revmap_fields( |
|
483
|
|
|
|
|
|
|
content => \%prod, |
|
484
|
|
|
|
|
|
|
itemSequenceNumber => 'itemSequenceNumber', |
|
485
|
|
|
|
|
|
|
itemDescription => 'description', |
|
486
|
|
|
|
|
|
|
productCode => 'code', |
|
487
|
|
|
|
|
|
|
quantity => 'quantity', |
|
488
|
|
|
|
|
|
|
unitOfMeasure => 'units', |
|
489
|
|
|
|
|
|
|
taxAmount => 'tax', |
|
490
|
|
|
|
|
|
|
lineItemTotal => 'amount', |
|
491
|
|
|
|
|
|
|
lineItemTotalWithTax => 'totalwithtax', |
|
492
|
|
|
|
|
|
|
itemDiscountAmount => 'discount', |
|
493
|
|
|
|
|
|
|
commodityCode => 'code', |
|
494
|
|
|
|
|
|
|
unitCost => 'cost', # This "amount" field uses decimals |
|
495
|
|
|
|
|
|
|
); |
|
496
|
12
|
|
|
|
|
1709
|
push @products, \%lineitem; |
|
497
|
|
|
|
|
|
|
} |
|
498
|
|
|
|
|
|
|
} |
|
499
|
|
|
|
|
|
|
|
|
500
|
|
|
|
|
|
|
# |
|
501
|
|
|
|
|
|
|
# |
|
502
|
6
|
|
|
|
|
35
|
tie my %enhanceddata, 'Tie::IxHash', $self->_revmap_fields( |
|
503
|
|
|
|
|
|
|
content => $content, |
|
504
|
|
|
|
|
|
|
customerReference => 'po_number', |
|
505
|
|
|
|
|
|
|
salesTax => 'salestax', |
|
506
|
|
|
|
|
|
|
discountAmount => 'discount', |
|
507
|
|
|
|
|
|
|
shippingAmount => 'shipping', |
|
508
|
|
|
|
|
|
|
dutyAmount => 'duty', |
|
509
|
|
|
|
|
|
|
invoiceReferenceNumber => 'invoice_number_length_15', |
|
510
|
|
|
|
|
|
|
orderDate => 'orderdate', |
|
511
|
|
|
|
|
|
|
lineItemData => \@products, |
|
512
|
|
|
|
|
|
|
); |
|
513
|
|
|
|
|
|
|
|
|
514
|
6
|
|
|
|
|
253
|
tie my %card, 'Tie::IxHash', $self->_revmap_fields( |
|
515
|
|
|
|
|
|
|
content => $content, |
|
516
|
|
|
|
|
|
|
type => 'card_type', |
|
517
|
|
|
|
|
|
|
number => 'card_number', |
|
518
|
|
|
|
|
|
|
expDate => 'expiration', |
|
519
|
|
|
|
|
|
|
cardValidationNum => 'cvv2', |
|
520
|
|
|
|
|
|
|
); |
|
521
|
|
|
|
|
|
|
|
|
522
|
6
|
|
|
|
|
341
|
tie my %token, 'Tie::IxHash', $self->_revmap_fields( |
|
523
|
|
|
|
|
|
|
content => $content, |
|
524
|
|
|
|
|
|
|
litleToken => 'card_token', |
|
525
|
|
|
|
|
|
|
expDate => 'expiration', |
|
526
|
|
|
|
|
|
|
cardValidationNum => 'cvv2', |
|
527
|
|
|
|
|
|
|
); |
|
528
|
|
|
|
|
|
|
|
|
529
|
6
|
|
|
|
|
276
|
tie my %processing, 'Tie::IxHash', $self->_revmap_fields( |
|
530
|
|
|
|
|
|
|
content => $content, |
|
531
|
|
|
|
|
|
|
bypassVelocityCheck => 'velocity_check', |
|
532
|
|
|
|
|
|
|
); |
|
533
|
|
|
|
|
|
|
|
|
534
|
6
|
|
|
|
|
204
|
tie my %cardholderauth, 'Tie::IxHash', |
|
535
|
|
|
|
|
|
|
$self->_revmap_fields( |
|
536
|
|
|
|
|
|
|
content => $content, |
|
537
|
|
|
|
|
|
|
authenticationValue => '3ds', |
|
538
|
|
|
|
|
|
|
authenticationTransactionId => 'visaverified', |
|
539
|
|
|
|
|
|
|
customerIpAddress => 'ip', |
|
540
|
|
|
|
|
|
|
authenticatedByMerchant => 'authenticated', |
|
541
|
|
|
|
|
|
|
); |
|
542
|
|
|
|
|
|
|
|
|
543
|
6
|
|
|
|
|
183
|
tie my %merchantdata, 'Tie::IxHash', |
|
544
|
|
|
|
|
|
|
$self->_revmap_fields( |
|
545
|
|
|
|
|
|
|
content => $content, |
|
546
|
|
|
|
|
|
|
affiliate => 'affiliate', |
|
547
|
|
|
|
|
|
|
merchantGroupingId => 'merchant_grouping_id', |
|
548
|
|
|
|
|
|
|
); |
|
549
|
|
|
|
|
|
|
|
|
550
|
6
|
|
|
|
|
187
|
tie my %recyclingrequest, 'Tie::IxHash', |
|
551
|
|
|
|
|
|
|
$self->_revmap_fields( |
|
552
|
|
|
|
|
|
|
content => $content, |
|
553
|
|
|
|
|
|
|
recycleBy => 'recycle_by', |
|
554
|
|
|
|
|
|
|
recycleId => 'recycle_id', |
|
555
|
|
|
|
|
|
|
); |
|
556
|
|
|
|
|
|
|
|
|
557
|
6
|
|
|
|
|
250
|
my %req; |
|
558
|
|
|
|
|
|
|
|
|
559
|
6
|
50
|
|
|
|
28
|
if ( $action eq 'registerTokenRequest' ) { |
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
560
|
0
|
0
|
0
|
|
|
0
|
croak 'missing card_number' if length($content->{'card_number'} || '') == 0; |
|
561
|
0
|
|
|
|
|
0
|
tie %req, 'Tie::IxHash', $self->_revmap_fields( |
|
562
|
|
|
|
|
|
|
content => $content, |
|
563
|
|
|
|
|
|
|
orderId => 'invoice_number', |
|
564
|
|
|
|
|
|
|
accountNumber => 'card_number', |
|
565
|
|
|
|
|
|
|
); |
|
566
|
|
|
|
|
|
|
} |
|
567
|
|
|
|
|
|
|
elsif ( $action eq 'sale' ) { |
|
568
|
5
|
100
|
100
|
|
|
252
|
croak 'missing card_token or card_number' if length($content->{'card_number'} || $content->{'card_token'} || '') == 0; |
|
569
|
|
|
|
|
|
|
tie %req, 'Tie::IxHash', $self->_revmap_fields( |
|
570
|
|
|
|
|
|
|
content => $content, |
|
571
|
|
|
|
|
|
|
orderId => 'invoice_number', |
|
572
|
|
|
|
|
|
|
amount => 'amount', |
|
573
|
|
|
|
|
|
|
orderSource => 'orderSource', |
|
574
|
|
|
|
|
|
|
billToAddress => \%billToAddress, |
|
575
|
|
|
|
|
|
|
card => $content->{'card_number'} ? \%card : {}, |
|
576
|
4
|
100
|
|
|
|
29
|
token => $content->{'card_token'} ? \%token : {}, |
|
|
|
100
|
|
|
|
|
|
|
577
|
|
|
|
|
|
|
|
|
578
|
|
|
|
|
|
|
#cardholderAuthentication => \%cardholderauth, |
|
579
|
|
|
|
|
|
|
customBilling => \%custombilling, |
|
580
|
|
|
|
|
|
|
enhancedData => \%enhanceddata, |
|
581
|
|
|
|
|
|
|
processingInstructions => \%processing, |
|
582
|
|
|
|
|
|
|
allowPartialAuth => 'partial_auth', |
|
583
|
|
|
|
|
|
|
merchantData => \%merchantdata, |
|
584
|
|
|
|
|
|
|
recyclingRequest => \%recyclingrequest, |
|
585
|
|
|
|
|
|
|
); |
|
586
|
|
|
|
|
|
|
} |
|
587
|
|
|
|
|
|
|
elsif ( $action eq 'authorization' ) { |
|
588
|
1
|
50
|
0
|
|
|
8
|
croak 'missing card_token or card_number' if length($content->{'card_number'} || $content->{'card_token'} || '') == 0; |
|
589
|
|
|
|
|
|
|
tie %req, 'Tie::IxHash', $self->_revmap_fields( |
|
590
|
|
|
|
|
|
|
content => $content, |
|
591
|
|
|
|
|
|
|
orderId => 'invoice_number', |
|
592
|
|
|
|
|
|
|
amount => 'amount', |
|
593
|
|
|
|
|
|
|
orderSource => 'orderSource', |
|
594
|
|
|
|
|
|
|
billToAddress => \%billToAddress, |
|
595
|
|
|
|
|
|
|
card => $content->{'card_number'} ? \%card : {}, |
|
596
|
1
|
50
|
|
|
|
10
|
token => $content->{'card_token'} ? \%token : {}, |
|
|
|
50
|
|
|
|
|
|
|
597
|
|
|
|
|
|
|
|
|
598
|
|
|
|
|
|
|
#cardholderAuthentication => \%cardholderauth, |
|
599
|
|
|
|
|
|
|
processingInstructions => \%processing, |
|
600
|
|
|
|
|
|
|
customBilling => \%custombilling, |
|
601
|
|
|
|
|
|
|
allowPartialAuth => 'partial_auth', |
|
602
|
|
|
|
|
|
|
merchantData => \%merchantdata, |
|
603
|
|
|
|
|
|
|
recyclingRequest => \%recyclingrequest, |
|
604
|
|
|
|
|
|
|
); |
|
605
|
|
|
|
|
|
|
} |
|
606
|
|
|
|
|
|
|
elsif ( $action eq 'capture' ) { |
|
607
|
0
|
|
|
|
|
0
|
push @required_fields, qw( order_number amount ); |
|
608
|
0
|
|
|
|
|
0
|
tie %req, 'Tie::IxHash', |
|
609
|
|
|
|
|
|
|
$self->_revmap_fields( |
|
610
|
|
|
|
|
|
|
content => $content, |
|
611
|
|
|
|
|
|
|
litleTxnId => 'order_number', |
|
612
|
|
|
|
|
|
|
amount => 'amount', |
|
613
|
|
|
|
|
|
|
enhancedData => \%enhanceddata, |
|
614
|
|
|
|
|
|
|
processingInstructions => \%processing, |
|
615
|
|
|
|
|
|
|
); |
|
616
|
|
|
|
|
|
|
} |
|
617
|
|
|
|
|
|
|
elsif ( $action eq 'credit' ) { |
|
618
|
|
|
|
|
|
|
|
|
619
|
|
|
|
|
|
|
# IF there is a litleTxnId, it's a normal linked credit |
|
620
|
0
|
0
|
|
|
|
0
|
if( $content->{'order_number'} ){ |
|
621
|
0
|
|
|
|
|
0
|
push @required_fields, qw( order_number amount ); |
|
622
|
0
|
|
|
|
|
0
|
tie %req, 'Tie::IxHash', $self->_revmap_fields( |
|
623
|
|
|
|
|
|
|
content => $content, |
|
624
|
|
|
|
|
|
|
litleTxnId => 'order_number', |
|
625
|
|
|
|
|
|
|
amount => 'amount', |
|
626
|
|
|
|
|
|
|
customBilling => \%custombilling, |
|
627
|
|
|
|
|
|
|
processingInstructions => \%processing, |
|
628
|
|
|
|
|
|
|
); |
|
629
|
|
|
|
|
|
|
} |
|
630
|
|
|
|
|
|
|
# ELSE it's an unlinked, which requires different data |
|
631
|
|
|
|
|
|
|
else { |
|
632
|
0
|
0
|
0
|
|
|
0
|
croak 'missing card_token or card_number' if length($content->{'card_number'} || $content->{'card_token'} || '') == 0; |
|
633
|
0
|
|
|
|
|
0
|
push @required_fields, qw( invoice_number amount ); |
|
634
|
|
|
|
|
|
|
tie %req, 'Tie::IxHash', $self->_revmap_fields( |
|
635
|
|
|
|
|
|
|
content => $content, |
|
636
|
|
|
|
|
|
|
orderId => 'invoice_number', |
|
637
|
|
|
|
|
|
|
amount => 'amount', |
|
638
|
|
|
|
|
|
|
orderSource => 'orderSource', |
|
639
|
|
|
|
|
|
|
billToAddress => \%billToAddress, |
|
640
|
|
|
|
|
|
|
card => $content->{'card_number'} ? \%card : {}, |
|
641
|
0
|
0
|
|
|
|
0
|
token => $content->{'card_token'} ? \%token : {}, |
|
|
|
0
|
|
|
|
|
|
|
642
|
|
|
|
|
|
|
customBilling => \%custombilling, |
|
643
|
|
|
|
|
|
|
processingInstructions => \%processing, |
|
644
|
|
|
|
|
|
|
); |
|
645
|
|
|
|
|
|
|
} |
|
646
|
|
|
|
|
|
|
} |
|
647
|
|
|
|
|
|
|
elsif ( $action eq 'void' ) { |
|
648
|
0
|
|
|
|
|
0
|
push @required_fields, qw( order_number ); |
|
649
|
0
|
|
|
|
|
0
|
tie %req, 'Tie::IxHash', |
|
650
|
|
|
|
|
|
|
$self->_revmap_fields( |
|
651
|
|
|
|
|
|
|
content => $content, |
|
652
|
|
|
|
|
|
|
litleTxnId => 'order_number', |
|
653
|
|
|
|
|
|
|
processingInstructions => \%processing, |
|
654
|
|
|
|
|
|
|
); |
|
655
|
|
|
|
|
|
|
} |
|
656
|
|
|
|
|
|
|
elsif ( $action eq 'authReversal' ) { |
|
657
|
0
|
|
|
|
|
0
|
push @required_fields, qw( order_number amount ); |
|
658
|
0
|
|
|
|
|
0
|
tie %req, 'Tie::IxHash', |
|
659
|
|
|
|
|
|
|
$self->_revmap_fields( |
|
660
|
|
|
|
|
|
|
content => $content, |
|
661
|
|
|
|
|
|
|
litleTxnId => 'order_number', |
|
662
|
|
|
|
|
|
|
amount => 'amount', |
|
663
|
|
|
|
|
|
|
); |
|
664
|
|
|
|
|
|
|
} |
|
665
|
|
|
|
|
|
|
elsif ( $action eq 'accountUpdate' ) { |
|
666
|
0
|
|
|
|
|
0
|
push @required_fields, qw( card_number expiration ); |
|
667
|
0
|
|
|
|
|
0
|
tie %req, 'Tie::IxHash', |
|
668
|
|
|
|
|
|
|
$self->_revmap_fields( |
|
669
|
|
|
|
|
|
|
content => $content, |
|
670
|
|
|
|
|
|
|
orderId => 'customer_id', |
|
671
|
|
|
|
|
|
|
card => \%card, |
|
672
|
|
|
|
|
|
|
); |
|
673
|
|
|
|
|
|
|
} |
|
674
|
|
|
|
|
|
|
|
|
675
|
5
|
|
|
|
|
742
|
$self->required_fields(@required_fields); |
|
676
|
5
|
|
|
|
|
336
|
return \%req; |
|
677
|
|
|
|
|
|
|
} |
|
678
|
|
|
|
|
|
|
|
|
679
|
|
|
|
|
|
|
sub submit { |
|
680
|
0
|
|
|
0
|
1
|
0
|
my ($self) = @_; |
|
681
|
|
|
|
|
|
|
|
|
682
|
0
|
|
|
|
|
0
|
local $SCRUBBER=1; |
|
683
|
0
|
|
|
|
|
0
|
$self->_litle_init; |
|
684
|
|
|
|
|
|
|
|
|
685
|
0
|
|
|
|
|
0
|
my %content = $self->content(); |
|
686
|
|
|
|
|
|
|
|
|
687
|
0
|
0
|
|
|
|
0
|
warn 'Pre processing: '.Dumper(\%content) if $DEBUG; |
|
688
|
0
|
|
|
|
|
0
|
my $req = $self->map_request( \%content ); |
|
689
|
0
|
0
|
|
|
|
0
|
warn 'Post processing: '.Dumper(\%content) if $DEBUG; |
|
690
|
0
|
|
|
|
|
0
|
my $post_data; |
|
691
|
|
|
|
|
|
|
|
|
692
|
0
|
|
|
|
|
0
|
my $writer = new XML::Writer( |
|
693
|
|
|
|
|
|
|
OUTPUT => \$post_data, |
|
694
|
|
|
|
|
|
|
DATA_MODE => 1, |
|
695
|
|
|
|
|
|
|
DATA_INDENT => 2, |
|
696
|
|
|
|
|
|
|
ENCODING => 'utf-8', |
|
697
|
|
|
|
|
|
|
); |
|
698
|
|
|
|
|
|
|
|
|
699
|
|
|
|
|
|
|
## set the authentication data |
|
700
|
0
|
|
|
|
|
0
|
tie my %authentication, 'Tie::IxHash', |
|
701
|
|
|
|
|
|
|
$self->_revmap_fields( |
|
702
|
|
|
|
|
|
|
content => \%content, |
|
703
|
|
|
|
|
|
|
user => 'login', |
|
704
|
|
|
|
|
|
|
password => 'password', |
|
705
|
|
|
|
|
|
|
); |
|
706
|
|
|
|
|
|
|
|
|
707
|
0
|
0
|
|
|
|
0
|
warn Dumper($req) if $DEBUG; |
|
708
|
|
|
|
|
|
|
## Start the XML Document, parent tag |
|
709
|
0
|
|
|
|
|
0
|
$writer->xmlDecl(); |
|
710
|
|
|
|
|
|
|
$writer->startTag( |
|
711
|
|
|
|
|
|
|
"litleOnlineRequest", |
|
712
|
|
|
|
|
|
|
version => $self->api_version, |
|
713
|
|
|
|
|
|
|
xmlns => $self->xmlns, |
|
714
|
0
|
|
|
|
|
0
|
merchantId => $content{'merchantid'}, |
|
715
|
|
|
|
|
|
|
); |
|
716
|
|
|
|
|
|
|
|
|
717
|
0
|
|
|
|
|
0
|
$self->_xmlwrite( $writer, 'authentication', \%authentication ); |
|
718
|
|
|
|
|
|
|
|
|
719
|
|
|
|
|
|
|
## partial capture modifier, odd location, because it modifies the start tag :( |
|
720
|
0
|
|
|
|
|
0
|
my %extra; |
|
721
|
0
|
0
|
|
|
|
0
|
if ($content{'TransactionType'} eq 'capture'){ |
|
722
|
0
|
0
|
|
|
|
0
|
$extra{'partial'} = $content{'partial'} ? 'true' : 'false'; |
|
723
|
|
|
|
|
|
|
} |
|
724
|
|
|
|
|
|
|
|
|
725
|
|
|
|
|
|
|
$writer->startTag( |
|
726
|
|
|
|
|
|
|
$content{'TransactionType'}, |
|
727
|
|
|
|
|
|
|
id => $content{'invoice_number'}, |
|
728
|
|
|
|
|
|
|
reportGroup => $content{'report_group'} || 'BOP', |
|
729
|
0
|
|
0
|
|
|
0
|
customerId => $content{'customer_id'} || 1, |
|
|
|
|
0
|
|
|
|
|
|
730
|
|
|
|
|
|
|
%extra, |
|
731
|
|
|
|
|
|
|
); |
|
732
|
0
|
|
|
|
|
0
|
foreach ( keys( %{$req} ) ) { |
|
|
0
|
|
|
|
|
0
|
|
|
733
|
0
|
|
|
|
|
0
|
$self->_xmlwrite( $writer, $_, $req->{$_} ); |
|
734
|
|
|
|
|
|
|
} |
|
735
|
|
|
|
|
|
|
|
|
736
|
0
|
|
|
|
|
0
|
$writer->endTag( $content{'TransactionType'} ); |
|
737
|
0
|
|
|
|
|
0
|
$writer->endTag("litleOnlineRequest"); |
|
738
|
0
|
|
|
|
|
0
|
$writer->end(); |
|
739
|
|
|
|
|
|
|
## END XML Generation |
|
740
|
|
|
|
|
|
|
|
|
741
|
0
|
|
|
|
|
0
|
$self->server_request( $post_data ); |
|
742
|
0
|
0
|
|
|
|
0
|
warn $self->server_request if $DEBUG; |
|
743
|
|
|
|
|
|
|
|
|
744
|
0
|
0
|
|
|
|
0
|
if ( $] ge '5.008' ) { |
|
745
|
|
|
|
|
|
|
# http_post expects data in this format |
|
746
|
0
|
0
|
|
|
|
0
|
utf8::encode($post_data) if utf8::is_utf8($post_data); |
|
747
|
|
|
|
|
|
|
} |
|
748
|
|
|
|
|
|
|
|
|
749
|
0
|
|
|
|
|
0
|
my ( $page, $status_code, %headers ) = $self->https_post( { 'Content-Type' => 'text/xml; charset=utf-8' } , $post_data); |
|
750
|
|
|
|
|
|
|
|
|
751
|
0
|
|
|
|
|
0
|
$self->server_response( $page ); |
|
752
|
0
|
0
|
|
|
|
0
|
warn Dumper $self->server_response, $status_code, \%headers if $DEBUG; |
|
753
|
|
|
|
|
|
|
|
|
754
|
0
|
|
|
|
|
0
|
my $response = $self->_parse_xml_response( $page, $status_code ); |
|
755
|
|
|
|
|
|
|
|
|
756
|
0
|
|
|
|
|
0
|
$content{'TransactionType'} =~ s/Request$//; # no clue why some of the types have a Request and some do not |
|
757
|
|
|
|
|
|
|
|
|
758
|
0
|
0
|
0
|
|
|
0
|
if ( exists( $response->{'response'} ) && $response->{'response'} == 1 ) { |
|
759
|
|
|
|
|
|
|
## parse error type error |
|
760
|
0
|
|
|
|
|
0
|
warn Dumper 'https://'.$self->server.':'.$self->port.$self->path,$response, $self->server_request; |
|
761
|
0
|
|
|
|
|
0
|
$self->error_message( $response->{'message'} ); |
|
762
|
0
|
|
|
|
|
0
|
return; |
|
763
|
|
|
|
|
|
|
} else { |
|
764
|
|
|
|
|
|
|
$self->error_message( |
|
765
|
|
|
|
|
|
|
$response->{ $content{'TransactionType'} . 'Response' } |
|
766
|
0
|
|
|
|
|
0
|
->{'message'} ); |
|
767
|
|
|
|
|
|
|
} |
|
768
|
0
|
|
|
|
|
0
|
$self->{_response} = $response; |
|
769
|
|
|
|
|
|
|
|
|
770
|
0
|
0
|
|
|
|
0
|
warn Dumper($response) if $DEBUG; |
|
771
|
|
|
|
|
|
|
|
|
772
|
|
|
|
|
|
|
## Set up the data: |
|
773
|
0
|
|
|
|
|
0
|
my $resp = $response->{ $content{'TransactionType'} . 'Response' }; |
|
774
|
0
|
|
|
|
|
0
|
$self->{_response} = $resp; |
|
775
|
0
|
|
0
|
|
|
0
|
$self->card_token( $resp->{'litleToken'} || $resp->{'tokenResponse'}->{'litleToken'} || $content{'card_token'} || '' ); |
|
776
|
0
|
|
0
|
|
|
0
|
$self->order_number( $resp->{'litleTxnId'} || '' ); |
|
777
|
0
|
|
0
|
|
|
0
|
$self->result_code( $resp->{'response'} || '' ); |
|
778
|
0
|
0
|
|
|
|
0
|
$resp->{'authCode'} =~ s/\D//g if $resp->{'authCode'}; |
|
779
|
0
|
|
0
|
|
|
0
|
$self->authorization( $resp->{'authCode'} || '' ); |
|
780
|
0
|
|
0
|
|
|
0
|
$self->cvv2_response( $resp->{'fraudResult'}->{'cardValidationResult'} |
|
781
|
|
|
|
|
|
|
|| '' ); |
|
782
|
0
|
|
0
|
|
|
0
|
$self->avs_code( $resp->{'fraudResult'}->{'avsResult'} || '' ); |
|
783
|
0
|
0
|
0
|
|
|
0
|
if( $resp->{enhancedAuthResponse} |
|
|
|
|
0
|
|
|
|
|
|
784
|
|
|
|
|
|
|
&& $resp->{enhancedAuthResponse}->{fundingSource} |
|
785
|
|
|
|
|
|
|
&& $resp->{enhancedAuthResponse}->{fundingSource}->{type} eq 'PREPAID' ) { |
|
786
|
|
|
|
|
|
|
|
|
787
|
0
|
|
|
|
|
0
|
$self->is_prepaid(1); |
|
788
|
0
|
|
|
|
|
0
|
$self->prepaid_balance( $resp->{enhancedAuthResponse}->{fundingSource}->{availableBalance} ); |
|
789
|
|
|
|
|
|
|
} else { |
|
790
|
0
|
|
|
|
|
0
|
$self->is_prepaid(0); |
|
791
|
|
|
|
|
|
|
} |
|
792
|
|
|
|
|
|
|
|
|
793
|
|
|
|
|
|
|
#$self->is_dupe( $resp->{'duplicate'} ? 1 : 0 ); |
|
794
|
0
|
0
|
0
|
|
|
0
|
if( defined $resp->{'duplicate'} && $resp->{'duplicate'} eq 'true' ) { |
|
795
|
0
|
|
|
|
|
0
|
$self->is_duplicate(1); |
|
796
|
|
|
|
|
|
|
} |
|
797
|
|
|
|
|
|
|
else { |
|
798
|
0
|
|
|
|
|
0
|
$self->is_duplicate(0); |
|
799
|
|
|
|
|
|
|
} |
|
800
|
|
|
|
|
|
|
|
|
801
|
0
|
0
|
|
|
|
0
|
if( defined $resp->{tokenResponse} ) { |
|
802
|
0
|
|
|
|
|
0
|
$self->card_token($resp->{tokenResponse}->{litleToken}); |
|
803
|
0
|
|
|
|
|
0
|
$self->card_token_response($resp->{tokenResponse}->{tokenResponseCode}); |
|
804
|
0
|
|
|
|
|
0
|
$self->card_token_message($resp->{tokenResponse}->{tokenMessage}); |
|
805
|
|
|
|
|
|
|
} |
|
806
|
|
|
|
|
|
|
|
|
807
|
0
|
0
|
0
|
|
|
0
|
if( $resp->{enhancedAuthResponse} |
|
808
|
|
|
|
|
|
|
&& $resp->{enhancedAuthResponse}->{affluence} |
|
809
|
|
|
|
|
|
|
){ |
|
810
|
0
|
|
|
|
|
0
|
$self->get_affluence( $resp->{enhancedAuthResponse}->{affluence} ); |
|
811
|
|
|
|
|
|
|
} |
|
812
|
0
|
0
|
|
|
|
0
|
$self->is_success( $self->result_code() eq '000' ? 1 : 0 ); |
|
813
|
0
|
0
|
0
|
|
|
0
|
if( |
|
|
|
|
0
|
|
|
|
|
|
814
|
|
|
|
|
|
|
$self->result_code() eq '010' # Partial approval, if they chose that option |
|
815
|
|
|
|
|
|
|
|| ($self->result_code() eq '802' && $self->card_token) # Card is already a token |
|
816
|
|
|
|
|
|
|
) { |
|
817
|
0
|
|
|
|
|
0
|
$self->is_success(1); |
|
818
|
|
|
|
|
|
|
} |
|
819
|
|
|
|
|
|
|
|
|
820
|
|
|
|
|
|
|
##Failure Status for 3.0 users |
|
821
|
0
|
0
|
|
|
|
0
|
if ( !$self->is_success ) { |
|
822
|
|
|
|
|
|
|
my $f_status = |
|
823
|
|
|
|
|
|
|
$ERRORS{ $self->result_code }->{'failure'} |
|
824
|
0
|
0
|
|
|
|
0
|
? $ERRORS{ $self->result_code }->{'failure'} |
|
825
|
|
|
|
|
|
|
: 'decline'; |
|
826
|
0
|
|
|
|
|
0
|
$self->failure_status($f_status); |
|
827
|
|
|
|
|
|
|
} |
|
828
|
|
|
|
|
|
|
|
|
829
|
0
|
0
|
|
|
|
0
|
unless ( $self->is_success() ) { |
|
830
|
0
|
0
|
|
|
|
0
|
unless ( $self->error_message() ) { |
|
831
|
|
|
|
|
|
|
$self->error_message( "(HTTPS response: $status_code) " |
|
832
|
|
|
|
|
|
|
. "(HTTPS headers: " |
|
833
|
0
|
|
|
|
|
0
|
. join( ", ", map { "$_ => " . $headers{$_} } keys %headers ) |
|
|
0
|
|
|
|
|
0
|
|
|
834
|
|
|
|
|
|
|
. ") " |
|
835
|
|
|
|
|
|
|
. "(Raw HTTPS content: ".$self->server_response().")" ); |
|
836
|
|
|
|
|
|
|
} |
|
837
|
|
|
|
|
|
|
} |
|
838
|
|
|
|
|
|
|
|
|
839
|
|
|
|
|
|
|
} |
|
840
|
|
|
|
|
|
|
|
|
841
|
|
|
|
|
|
|
|
|
842
|
|
|
|
|
|
|
sub chargeback_retrieve_support_doc { |
|
843
|
0
|
|
|
0
|
1
|
0
|
my ( $self ) = @_; |
|
844
|
0
|
|
|
|
|
0
|
$self->_litle_support_doc('RETRIEVE'); |
|
845
|
0
|
0
|
|
|
|
0
|
if ($self->is_success) { $self->{'fileContent'} = $self->{'server_response_dangerous'}; } else { $self->{'fileContent'} = undef; } |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
846
|
|
|
|
|
|
|
} |
|
847
|
|
|
|
|
|
|
|
|
848
|
|
|
|
|
|
|
|
|
849
|
|
|
|
|
|
|
sub chargeback_delete_support_doc { |
|
850
|
0
|
|
|
0
|
1
|
0
|
my ( $self ) = @_; |
|
851
|
0
|
|
|
|
|
0
|
$self->_litle_support_doc('DELETE' ); |
|
852
|
|
|
|
|
|
|
} |
|
853
|
|
|
|
|
|
|
|
|
854
|
|
|
|
|
|
|
|
|
855
|
|
|
|
|
|
|
sub chargeback_upload_support_doc { |
|
856
|
0
|
|
|
0
|
1
|
0
|
my ( $self ) = @_; |
|
857
|
0
|
|
|
|
|
0
|
$self->_litle_support_doc('UPLOAD' ); |
|
858
|
|
|
|
|
|
|
} |
|
859
|
|
|
|
|
|
|
|
|
860
|
|
|
|
|
|
|
|
|
861
|
|
|
|
|
|
|
sub chargeback_replace_support_doc { |
|
862
|
0
|
|
|
0
|
1
|
0
|
my ( $self ) = @_; |
|
863
|
0
|
|
|
|
|
0
|
$self->_litle_support_doc('REPLACE' ); |
|
864
|
|
|
|
|
|
|
} |
|
865
|
|
|
|
|
|
|
|
|
866
|
|
|
|
|
|
|
sub _litle_support_doc { |
|
867
|
0
|
|
|
0
|
|
0
|
my ( $self, $action ) = @_; |
|
868
|
|
|
|
|
|
|
|
|
869
|
0
|
|
|
|
|
0
|
local $SCRUBBER=1; |
|
870
|
0
|
|
|
|
|
0
|
$self->_litle_init; |
|
871
|
|
|
|
|
|
|
|
|
872
|
0
|
|
|
|
|
0
|
my %content = $self->content(); |
|
873
|
|
|
|
|
|
|
|
|
874
|
0
|
|
|
|
|
0
|
my $requiredargs = ['case_id','filename','merchantid']; |
|
875
|
0
|
0
|
|
|
|
0
|
if ($action =~ /(?:UPLOAD|REPLACE)/) { push @$requiredargs, 'filecontent', 'mimetype'; } |
|
|
0
|
|
|
|
|
0
|
|
|
876
|
0
|
|
|
|
|
0
|
foreach my $key (@$requiredargs) { |
|
877
|
0
|
0
|
|
|
|
0
|
croak "Missing arg $key" unless $content{$key}; |
|
878
|
|
|
|
|
|
|
} |
|
879
|
|
|
|
|
|
|
|
|
880
|
0
|
|
|
|
|
0
|
my $actionRESTful = { |
|
881
|
|
|
|
|
|
|
'DELETE' => 'DELETE', |
|
882
|
|
|
|
|
|
|
'RETRIEVE' => 'GET', |
|
883
|
|
|
|
|
|
|
'UPLOAD' => 'POST', |
|
884
|
|
|
|
|
|
|
'REPLACE' => 'PUT', |
|
885
|
|
|
|
|
|
|
}; |
|
886
|
0
|
0
|
|
|
|
0
|
die "UNDEFINED ACTION: $action" unless defined $actionRESTful->{$action}; |
|
887
|
|
|
|
|
|
|
|
|
888
|
|
|
|
|
|
|
{ |
|
889
|
4
|
|
|
4
|
|
15759
|
use bytes; |
|
|
4
|
|
|
|
|
12
|
|
|
|
4
|
|
|
|
|
27
|
|
|
|
0
|
|
|
|
|
0
|
|
|
890
|
0
|
0
|
|
|
|
0
|
if ( defined $content{'filecontent'} ) { |
|
891
|
0
|
0
|
|
|
|
0
|
if ( length($content{'filecontent'}) > 2097152 ) { # file limit of 2M |
|
892
|
0
|
|
|
|
|
0
|
my $msg = 'Filesize Exceeds Limit Of 2MB'; |
|
893
|
0
|
|
|
|
|
0
|
$self->result_code( 012 ); ## no critic |
|
894
|
0
|
|
|
|
|
0
|
$self->error_message( $msg ); |
|
895
|
0
|
|
|
|
|
0
|
croak $msg; |
|
896
|
|
|
|
|
|
|
} |
|
897
|
0
|
|
|
|
|
0
|
my $allowedTypes = { |
|
898
|
|
|
|
|
|
|
'application/pdf' => 1, |
|
899
|
|
|
|
|
|
|
'image/gif' => 1, |
|
900
|
|
|
|
|
|
|
'image/jpeg' => 1, |
|
901
|
|
|
|
|
|
|
'image/png' => 1, |
|
902
|
|
|
|
|
|
|
'image/tiff' => 1, |
|
903
|
|
|
|
|
|
|
}; |
|
904
|
0
|
0
|
0
|
|
|
0
|
if ( ! defined $allowedTypes->{$content{'mimetype'}||''} ) { |
|
905
|
0
|
|
|
|
|
0
|
croak "File must be one of PDF/GIF/JPG/PNG/TIFF".$content{'mimetype'}; |
|
906
|
|
|
|
|
|
|
} |
|
907
|
|
|
|
|
|
|
} |
|
908
|
|
|
|
|
|
|
} |
|
909
|
|
|
|
|
|
|
|
|
910
|
0
|
|
|
|
|
0
|
my $caseidURI = $content{'case_id'}; |
|
911
|
0
|
|
|
|
|
0
|
my $filenameURI = $content{'filename'}; |
|
912
|
0
|
|
|
|
|
0
|
my $merchantidURI = $content{'merchantid'}; |
|
913
|
0
|
|
|
|
|
0
|
foreach ( $caseidURI, $filenameURI, $merchantidURI ) { |
|
914
|
0
|
|
|
|
|
0
|
s/([^a-z0-9\.\-])/sprintf('%%%X',ord($1))/ige; |
|
|
0
|
|
|
|
|
0
|
|
|
915
|
|
|
|
|
|
|
} |
|
916
|
|
|
|
|
|
|
|
|
917
|
0
|
|
|
|
|
0
|
my $url = 'https://'.$self->chargeback_server.':'.$self->chargeback_port.'//services/chargebacks/documents/'.$merchantidURI.'/'.$caseidURI.'/'.$filenameURI; |
|
918
|
|
|
|
|
|
|
my $response = HTTP::Tiny->new( verify_SSL=>$self->verify_SSL )->request($actionRESTful->{$action}, $url, { |
|
919
|
|
|
|
|
|
|
headers => { |
|
920
|
|
|
|
|
|
|
'Authorization' => 'Basic ' . MIME::Base64::encode("$content{'login'}:$content{'password'}",''), |
|
921
|
|
|
|
|
|
|
'Content-Type' => $content{'mimetype'} || 'text/plain', |
|
922
|
|
|
|
|
|
|
}, |
|
923
|
0
|
|
0
|
|
|
0
|
content => $content{'filecontent'}, |
|
924
|
|
|
|
|
|
|
} ); |
|
925
|
|
|
|
|
|
|
|
|
926
|
0
|
|
|
|
|
0
|
$self->server_request( $content{'mimetype'} ); |
|
927
|
0
|
|
|
|
|
0
|
$self->server_response( $response->{'content'} ); |
|
928
|
|
|
|
|
|
|
|
|
929
|
0
|
0
|
0
|
|
|
0
|
if ( $action eq 'RETRIEVE' && $response->{'status'} =~ /^200/ && substr($response->{'content'},0,500) !~ /
|
|
|
|
|
0
|
|
|
|
|
|
930
|
|
|
|
|
|
|
# the RETRIEVE action returns the actual page as the file, rather then returning XML |
|
931
|
0
|
|
|
|
|
0
|
$self->is_success(1); |
|
932
|
|
|
|
|
|
|
} else { |
|
933
|
0
|
|
|
|
|
0
|
my $xml_response = $self->_parse_xml_response( $response->{'content'}, $response->{'status'} ); |
|
934
|
|
|
|
|
|
|
|
|
935
|
0
|
0
|
0
|
|
|
0
|
if (defined $xml_response && defined $xml_response->{'ChargebackCase'}{'Document'}{'ResponseCode'}) { |
|
936
|
0
|
0
|
|
|
|
0
|
$self->is_success( $xml_response->{'ChargebackCase'}{'Document'}{'ResponseCode'} eq '000' ? 1 : 0 ); |
|
937
|
0
|
|
|
|
|
0
|
$self->result_code( $xml_response->{'ChargebackCase'}{'Document'}{'ResponseCode'} ); |
|
938
|
0
|
|
|
|
|
0
|
$self->error_message( $xml_response->{'ChargebackCase'}{'Document'}{'ResponseMessage'} ); |
|
939
|
|
|
|
|
|
|
} else { |
|
940
|
0
|
|
|
|
|
0
|
croak "UNRECOGNIZED RESULT: ".$self->server_response; |
|
941
|
|
|
|
|
|
|
} |
|
942
|
|
|
|
|
|
|
} |
|
943
|
|
|
|
|
|
|
} |
|
944
|
|
|
|
|
|
|
|
|
945
|
|
|
|
|
|
|
|
|
946
|
|
|
|
|
|
|
sub chargeback_list_support_docs { |
|
947
|
0
|
|
|
0
|
1
|
0
|
my ( $self ) = @_; |
|
948
|
|
|
|
|
|
|
|
|
949
|
0
|
|
|
|
|
0
|
local $SCRUBBER=1; |
|
950
|
0
|
|
|
|
|
0
|
$self->_litle_init; |
|
951
|
|
|
|
|
|
|
|
|
952
|
0
|
|
|
|
|
0
|
my %content = $self->content(); |
|
953
|
|
|
|
|
|
|
|
|
954
|
0
|
0
|
|
|
|
0
|
croak "Missing arg case_id" unless $content{'case_id'}; |
|
955
|
0
|
0
|
|
|
|
0
|
croak "Missing arg merchantid" unless $content{'merchantid'}; |
|
956
|
0
|
|
|
|
|
0
|
my $caseidURI = $content{'case_id'}; |
|
957
|
0
|
|
|
|
|
0
|
my $merchantidURI = $content{'merchantid'}; |
|
958
|
0
|
|
|
|
|
0
|
foreach ( $caseidURI, $merchantidURI ) { |
|
959
|
0
|
|
|
|
|
0
|
s/([^a-z0-9\.\-])/sprintf('%%%X',ord($1))/ige; |
|
|
0
|
|
|
|
|
0
|
|
|
960
|
|
|
|
|
|
|
} |
|
961
|
|
|
|
|
|
|
|
|
962
|
0
|
|
|
|
|
0
|
my $url = 'https://'.$self->chargeback_server.':'.$self->chargeback_port.'//services/chargebacks/documents/'.$merchantidURI.'/'.$caseidURI.'/'; |
|
963
|
0
|
|
|
|
|
0
|
my $response = HTTP::Tiny->new( verify_SSL=>$self->verify_SSL )->request('GET', $url, { |
|
964
|
|
|
|
|
|
|
headers => { Authorization => 'Basic ' . MIME::Base64::encode("$content{'login'}:$content{'password'}",'') }, |
|
965
|
|
|
|
|
|
|
} ); |
|
966
|
|
|
|
|
|
|
|
|
967
|
0
|
|
|
|
|
0
|
$self->server_request( $url ); |
|
968
|
0
|
|
|
|
|
0
|
$self->server_response( $response->{'content'} ); |
|
969
|
|
|
|
|
|
|
|
|
970
|
0
|
|
|
|
|
0
|
my $xml_response = $self->_parse_xml_response( $response->{'content'}, $response->{'status'} ); |
|
971
|
|
|
|
|
|
|
|
|
972
|
0
|
0
|
0
|
|
|
0
|
if (defined $xml_response && $xml_response->{'ChargebackCase'}{'ResponseCode'}) { |
|
|
|
0
|
0
|
|
|
|
|
|
973
|
0
|
|
|
|
|
0
|
$self->result_code( $xml_response->{'ChargebackCase'}{'ResponseCode'} ); |
|
974
|
0
|
|
|
|
|
0
|
$self->error_message( $xml_response->{'ChargebackCase'}{'ResponseMessage'} ); |
|
975
|
|
|
|
|
|
|
} elsif (defined $xml_response && $xml_response->{'ChargebackCase'}{'DocumentEntry'}) { |
|
976
|
0
|
|
|
|
|
0
|
$self->is_success(1); |
|
977
|
0
|
|
|
|
|
0
|
$self->result_code( '000' ); |
|
978
|
|
|
|
|
|
|
|
|
979
|
0
|
|
|
|
|
0
|
my $ref = $xml_response->{'ChargebackCase'}{'DocumentEntry'}; |
|
980
|
0
|
0
|
0
|
|
|
0
|
if (defined $ref->{'id'} && ref $ref->{'id'} eq '') { |
|
981
|
|
|
|
|
|
|
# XMLin does not parse the result properly for a single document. This fixes the single document format to match the multi-doc format |
|
982
|
0
|
|
|
|
|
0
|
$ref = { $ref->{'id'} => $ref }; |
|
983
|
|
|
|
|
|
|
} |
|
984
|
0
|
|
|
|
|
0
|
return $ref; |
|
985
|
|
|
|
|
|
|
} else { |
|
986
|
0
|
|
|
|
|
0
|
croak "UNRECOGNIZED RESULT: ".$self->server_response; |
|
987
|
|
|
|
|
|
|
} |
|
988
|
0
|
|
|
|
|
0
|
return {}; |
|
989
|
|
|
|
|
|
|
} |
|
990
|
|
|
|
|
|
|
|
|
991
|
|
|
|
|
|
|
sub _parse_xml_response { |
|
992
|
5
|
|
|
5
|
|
33
|
my ( $self, $page, $status_code ) = @_; |
|
993
|
5
|
|
|
|
|
18
|
my $response = {}; |
|
994
|
5
|
50
|
|
|
|
40
|
if ( $status_code =~ /^200/ ) { |
|
995
|
5
|
50
|
|
|
|
23
|
if ( ! eval { $response = XMLin($page); } ) { |
|
|
5
|
|
|
|
|
43
|
|
|
996
|
0
|
|
|
|
|
0
|
die "XML PARSING FAILURE: $@"; |
|
997
|
|
|
|
|
|
|
} |
|
998
|
|
|
|
|
|
|
} |
|
999
|
|
|
|
|
|
|
else { |
|
1000
|
0
|
|
|
|
|
0
|
$status_code =~ s/[\r\n\s]+$//; # remove newline so you can see the error in a linux console |
|
1001
|
0
|
0
|
|
|
|
0
|
if ( $status_code =~ /^(?:900|599)/ ) { $status_code .= ' - verify Litle has whitelisted your IP'; } |
|
|
0
|
|
|
|
|
0
|
|
|
1002
|
0
|
|
|
|
|
0
|
die "CONNECTION FAILURE: $status_code"; |
|
1003
|
|
|
|
|
|
|
} |
|
1004
|
5
|
|
|
|
|
152331
|
return $response; |
|
1005
|
|
|
|
|
|
|
} |
|
1006
|
|
|
|
|
|
|
|
|
1007
|
|
|
|
|
|
|
sub _parse_batch_response { |
|
1008
|
0
|
|
|
0
|
|
0
|
my ( $self, $args ) = @_; |
|
1009
|
0
|
|
|
|
|
0
|
my @results; |
|
1010
|
0
|
|
|
|
|
0
|
my $resp = $self->{'batch_response'}; |
|
1011
|
0
|
|
|
|
|
0
|
$self->order_number( $resp->{'litleBatchId'} ); |
|
1012
|
|
|
|
|
|
|
|
|
1013
|
|
|
|
|
|
|
#$self->invoice_number( $resp->{'id'} ); |
|
1014
|
|
|
|
|
|
|
my @result_types = |
|
1015
|
0
|
|
|
|
|
0
|
grep { $_ =~ m/Response$/ } |
|
1016
|
0
|
|
|
|
|
0
|
keys %{$resp}; ## get a list of result types in this batch |
|
|
0
|
|
|
|
|
0
|
|
|
1017
|
|
|
|
|
|
|
return { |
|
1018
|
0
|
|
|
|
|
0
|
'account_update' => $self->_get_update_response, |
|
1019
|
|
|
|
|
|
|
## do the other response types now |
|
1020
|
|
|
|
|
|
|
}; |
|
1021
|
|
|
|
|
|
|
} |
|
1022
|
|
|
|
|
|
|
|
|
1023
|
|
|
|
|
|
|
|
|
1024
|
|
|
|
|
|
|
sub add_item { |
|
1025
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
1026
|
|
|
|
|
|
|
## do we want to render it now, or later? |
|
1027
|
0
|
|
|
|
|
0
|
push @{ $self->{'batch_entries'} }, shift; |
|
|
0
|
|
|
|
|
0
|
|
|
1028
|
|
|
|
|
|
|
} |
|
1029
|
|
|
|
|
|
|
|
|
1030
|
|
|
|
|
|
|
|
|
1031
|
|
|
|
|
|
|
sub create_batch { |
|
1032
|
0
|
|
|
0
|
1
|
0
|
my ( $self, %opts ) = @_; |
|
1033
|
|
|
|
|
|
|
|
|
1034
|
0
|
|
|
|
|
0
|
local $SCRUBBER=1; |
|
1035
|
0
|
|
|
|
|
0
|
$self->_litle_init(\%opts); |
|
1036
|
|
|
|
|
|
|
|
|
1037
|
0
|
0
|
0
|
|
|
0
|
if ( ! defined $self->{'batch_entries'} || scalar( @{ $self->{'batch_entries'} } ) < 1 ) { |
|
|
0
|
|
|
|
|
0
|
|
|
1038
|
0
|
|
|
|
|
0
|
$self->error_message('Cannot create an empty batch'); |
|
1039
|
0
|
|
|
|
|
0
|
return; |
|
1040
|
|
|
|
|
|
|
} |
|
1041
|
|
|
|
|
|
|
|
|
1042
|
0
|
|
|
|
|
0
|
my $post_data; |
|
1043
|
|
|
|
|
|
|
|
|
1044
|
0
|
|
|
|
|
0
|
my $writer = new XML::Writer( |
|
1045
|
|
|
|
|
|
|
OUTPUT => \$post_data, |
|
1046
|
|
|
|
|
|
|
DATA_MODE => 1, |
|
1047
|
|
|
|
|
|
|
DATA_INDENT => 2, |
|
1048
|
|
|
|
|
|
|
ENCODING => 'utf-8', |
|
1049
|
|
|
|
|
|
|
); |
|
1050
|
|
|
|
|
|
|
## set the authentication data |
|
1051
|
0
|
|
|
|
|
0
|
tie my %authentication, 'Tie::IxHash', |
|
1052
|
|
|
|
|
|
|
$self->_revmap_fields( |
|
1053
|
|
|
|
|
|
|
content => \%opts, |
|
1054
|
|
|
|
|
|
|
user => 'login', |
|
1055
|
|
|
|
|
|
|
password => 'password', |
|
1056
|
|
|
|
|
|
|
); |
|
1057
|
|
|
|
|
|
|
|
|
1058
|
|
|
|
|
|
|
## Start the XML Document, parent tag |
|
1059
|
0
|
|
|
|
|
0
|
$writer->xmlDecl(); |
|
1060
|
|
|
|
|
|
|
$writer->startTag( |
|
1061
|
|
|
|
|
|
|
"litleRequest", |
|
1062
|
|
|
|
|
|
|
version => $self->batch_api_version, |
|
1063
|
|
|
|
|
|
|
xmlns => $self->xmlns, |
|
1064
|
0
|
|
0
|
|
|
0
|
id => $opts{'batch_id'} || time, |
|
1065
|
|
|
|
|
|
|
numBatchRequests => 1, #hardcoded for now, not doing multiple merchants |
|
1066
|
|
|
|
|
|
|
); |
|
1067
|
|
|
|
|
|
|
|
|
1068
|
|
|
|
|
|
|
## authentication |
|
1069
|
0
|
|
|
|
|
0
|
$self->_xmlwrite( $writer, 'authentication', \%authentication ); |
|
1070
|
|
|
|
|
|
|
## batch Request tag |
|
1071
|
|
|
|
|
|
|
$writer->startTag( |
|
1072
|
|
|
|
|
|
|
'batchRequest', |
|
1073
|
|
|
|
|
|
|
id => $opts{'batch_id'} || time, |
|
1074
|
0
|
|
|
|
|
0
|
numAccountUpdates => scalar( @{ $self->{'batch_entries'} } ), |
|
1075
|
0
|
|
0
|
|
|
0
|
merchantId => $opts{'merchantid'}, |
|
1076
|
|
|
|
|
|
|
); |
|
1077
|
0
|
|
|
|
|
0
|
foreach my $entry ( @{ $self->{'batch_entries'} } ) { |
|
|
0
|
|
|
|
|
0
|
|
|
1078
|
0
|
|
|
|
|
0
|
$self->_litle_scrubber_add_card($entry->{'card_number'}); |
|
1079
|
0
|
|
|
|
|
0
|
my $req = $self->map_request( $entry ); |
|
1080
|
|
|
|
|
|
|
$writer->startTag( |
|
1081
|
|
|
|
|
|
|
$entry->{'TransactionType'}, |
|
1082
|
|
|
|
|
|
|
id => $entry->{'invoice_number'}, |
|
1083
|
|
|
|
|
|
|
reportGroup => $entry->{'report_group'} || 'BOP', |
|
1084
|
0
|
|
0
|
|
|
0
|
customerId => $entry->{'customer_id'} || 1, |
|
|
|
|
0
|
|
|
|
|
|
1085
|
|
|
|
|
|
|
); |
|
1086
|
0
|
|
|
|
|
0
|
foreach ( keys( %{$req} ) ) { |
|
|
0
|
|
|
|
|
0
|
|
|
1087
|
0
|
|
|
|
|
0
|
$self->_xmlwrite( $writer, $_, $req->{$_} ); |
|
1088
|
|
|
|
|
|
|
} |
|
1089
|
0
|
|
|
|
|
0
|
$writer->endTag( $entry->{'TransactionType'} ); |
|
1090
|
|
|
|
|
|
|
## need to also handle the action tag here, and custid info |
|
1091
|
|
|
|
|
|
|
} |
|
1092
|
0
|
|
|
|
|
0
|
$writer->endTag("batchRequest"); |
|
1093
|
0
|
|
|
|
|
0
|
$writer->endTag("litleRequest"); |
|
1094
|
0
|
|
|
|
|
0
|
$writer->end(); |
|
1095
|
|
|
|
|
|
|
## END XML Generation |
|
1096
|
|
|
|
|
|
|
|
|
1097
|
0
|
|
|
|
|
0
|
$self->server_request( $post_data ); |
|
1098
|
0
|
0
|
|
|
|
0
|
warn $self->server_request if $DEBUG; |
|
1099
|
|
|
|
|
|
|
|
|
1100
|
|
|
|
|
|
|
#----- Send it |
|
1101
|
0
|
0
|
0
|
|
|
0
|
if ( $opts{'method'} && $opts{'method'} eq 'sftp' ) { #FTP |
|
|
|
0
|
0
|
|
|
|
|
|
1102
|
0
|
|
|
|
|
0
|
my $sftp = $self->_sftp_connect(\%opts,'inbound'); |
|
1103
|
|
|
|
|
|
|
|
|
1104
|
|
|
|
|
|
|
## save the file out, can't put directly from var, and is multibyte, so issues from filehandle |
|
1105
|
0
|
|
0
|
|
|
0
|
my $filename = $opts{'batch_id'} || $opts{'login'} . "_" . time; |
|
1106
|
0
|
|
|
|
|
0
|
my $io = IO::String->new($post_data); |
|
1107
|
0
|
|
|
|
|
0
|
tie *IO, 'IO::String'; |
|
1108
|
|
|
|
|
|
|
|
|
1109
|
0
|
0
|
|
|
|
0
|
$sftp->put( $io, "$filename.prg" ) |
|
1110
|
|
|
|
|
|
|
or $self->_die("Cannot PUT $filename", $sftp->error); |
|
1111
|
0
|
0
|
|
|
|
0
|
$sftp->rename( "$filename.prg", |
|
1112
|
|
|
|
|
|
|
"$filename.asc" ) #once complete, you rename it, for pickup |
|
1113
|
|
|
|
|
|
|
or $self->die("Cannot RENAME file", $sftp->error); |
|
1114
|
0
|
|
|
|
|
0
|
$self->is_success(1); |
|
1115
|
0
|
|
|
|
|
0
|
$self->server_response( $sftp->message ); |
|
1116
|
|
|
|
|
|
|
} |
|
1117
|
|
|
|
|
|
|
elsif ( $opts{'method'} && $opts{'method'} eq 'https' ) { #https post |
|
1118
|
0
|
|
|
|
|
0
|
$self->port('15000'); |
|
1119
|
0
|
|
|
|
|
0
|
$self->path('/'); |
|
1120
|
0
|
|
|
|
|
0
|
my ( $page, $status_code, %headers ) = |
|
1121
|
|
|
|
|
|
|
$self->https_post($post_data); |
|
1122
|
0
|
|
|
|
|
0
|
$self->server_response( $page ); |
|
1123
|
|
|
|
|
|
|
|
|
1124
|
0
|
0
|
|
|
|
0
|
warn Dumper [ $page, $status_code, \%headers ] if $DEBUG; |
|
1125
|
|
|
|
|
|
|
|
|
1126
|
0
|
|
|
|
|
0
|
my $response = {}; |
|
1127
|
0
|
0
|
|
|
|
0
|
if ( $status_code =~ /^200/ ) { |
|
1128
|
0
|
0
|
0
|
|
|
0
|
if ( ! eval { $response = XMLin($page); } ) { |
|
|
0
|
0
|
|
|
|
0
|
|
|
1129
|
0
|
|
|
|
|
0
|
$self->_die("XML PARSING FAILURE: $@"); |
|
1130
|
|
|
|
|
|
|
} |
|
1131
|
|
|
|
|
|
|
elsif ( exists( $response->{'response'} ) |
|
1132
|
|
|
|
|
|
|
&& $response->{'response'} == 1 ) |
|
1133
|
|
|
|
|
|
|
{ |
|
1134
|
|
|
|
|
|
|
## parse error type error |
|
1135
|
0
|
|
|
|
|
0
|
warn Dumper( $response, $self->server_request ); |
|
1136
|
0
|
|
|
|
|
0
|
$self->error_message( $response->{'message'} ); |
|
1137
|
0
|
|
|
|
|
0
|
return; |
|
1138
|
|
|
|
|
|
|
} |
|
1139
|
|
|
|
|
|
|
else { |
|
1140
|
|
|
|
|
|
|
$self->error_message( |
|
1141
|
0
|
|
|
|
|
0
|
$response->{'batchResponse'}->{'message'} ); |
|
1142
|
|
|
|
|
|
|
} |
|
1143
|
|
|
|
|
|
|
} |
|
1144
|
|
|
|
|
|
|
else { |
|
1145
|
0
|
|
|
|
|
0
|
$self->_die("CONNECTION FAILURE: $status_code"); |
|
1146
|
|
|
|
|
|
|
} |
|
1147
|
0
|
|
|
|
|
0
|
$self->{_response} = $response; |
|
1148
|
|
|
|
|
|
|
|
|
1149
|
|
|
|
|
|
|
##parse out the batch info as our general status |
|
1150
|
0
|
|
|
|
|
0
|
my $resp = $response->{'batchResponse'}; |
|
1151
|
0
|
|
|
|
|
0
|
$self->order_number( $resp->{'litleSessionId'} ); |
|
1152
|
0
|
|
|
|
|
0
|
$self->result_code( $response->{'response'} ); |
|
1153
|
0
|
0
|
|
|
|
0
|
$self->is_success( $response->{'response'} eq '0' ? 1 : 0 ); |
|
1154
|
|
|
|
|
|
|
|
|
1155
|
0
|
0
|
|
|
|
0
|
warn Dumper($response) if $DEBUG; |
|
1156
|
0
|
0
|
|
|
|
0
|
unless ( $self->is_success() ) { |
|
1157
|
0
|
0
|
|
|
|
0
|
unless ( $self->error_message() ) { |
|
1158
|
|
|
|
|
|
|
$self->error_message( |
|
1159
|
|
|
|
|
|
|
"(HTTPS response: $status_code) " |
|
1160
|
|
|
|
|
|
|
. "(HTTPS headers: " |
|
1161
|
|
|
|
|
|
|
. join( ", ", |
|
1162
|
0
|
|
|
|
|
0
|
map { "$_ => " . $headers{$_} } keys %headers ) |
|
|
0
|
|
|
|
|
0
|
|
|
1163
|
|
|
|
|
|
|
. ") " |
|
1164
|
|
|
|
|
|
|
. "(Raw HTTPS content: $page)" |
|
1165
|
|
|
|
|
|
|
); |
|
1166
|
|
|
|
|
|
|
} |
|
1167
|
|
|
|
|
|
|
} |
|
1168
|
0
|
0
|
|
|
|
0
|
if ( $self->is_success() ) { |
|
1169
|
0
|
|
|
|
|
0
|
$self->{'batch_response'} = $resp; |
|
1170
|
|
|
|
|
|
|
} |
|
1171
|
|
|
|
|
|
|
} |
|
1172
|
|
|
|
|
|
|
|
|
1173
|
|
|
|
|
|
|
} |
|
1174
|
|
|
|
|
|
|
|
|
1175
|
|
|
|
|
|
|
|
|
1176
|
|
|
|
|
|
|
sub send_rfr { |
|
1177
|
0
|
|
|
0
|
1
|
0
|
my ( $self, $args ) = @_; |
|
1178
|
|
|
|
|
|
|
|
|
1179
|
0
|
|
|
|
|
0
|
local $SCRUBBER=1; |
|
1180
|
0
|
|
|
|
|
0
|
$self->_litle_init($args); |
|
1181
|
|
|
|
|
|
|
|
|
1182
|
0
|
|
|
|
|
0
|
my $post_data; |
|
1183
|
0
|
|
|
|
|
0
|
my $writer = new XML::Writer( |
|
1184
|
|
|
|
|
|
|
OUTPUT => \$post_data, |
|
1185
|
|
|
|
|
|
|
DATA_MODE => 1, |
|
1186
|
|
|
|
|
|
|
DATA_INDENT => 2, |
|
1187
|
|
|
|
|
|
|
ENCODING => 'utf-8', |
|
1188
|
|
|
|
|
|
|
); |
|
1189
|
|
|
|
|
|
|
## set the authentication data |
|
1190
|
0
|
|
|
|
|
0
|
tie my %authentication, 'Tie::IxHash', |
|
1191
|
|
|
|
|
|
|
$self->_revmap_fields( |
|
1192
|
|
|
|
|
|
|
content => $args, |
|
1193
|
|
|
|
|
|
|
user => 'login', |
|
1194
|
|
|
|
|
|
|
password => 'password', |
|
1195
|
|
|
|
|
|
|
); |
|
1196
|
|
|
|
|
|
|
|
|
1197
|
|
|
|
|
|
|
## Start the XML Document, parent tag |
|
1198
|
0
|
|
|
|
|
0
|
$writer->xmlDecl(); |
|
1199
|
0
|
|
|
|
|
0
|
$writer->startTag( |
|
1200
|
|
|
|
|
|
|
"litleRequest", |
|
1201
|
|
|
|
|
|
|
version => $self->batch_api_version, |
|
1202
|
|
|
|
|
|
|
xmlns => $self->xmlns, |
|
1203
|
|
|
|
|
|
|
numBatchRequests => 0, |
|
1204
|
|
|
|
|
|
|
); |
|
1205
|
|
|
|
|
|
|
|
|
1206
|
|
|
|
|
|
|
## authentication |
|
1207
|
0
|
|
|
|
|
0
|
$self->_xmlwrite( $writer, 'authentication', \%authentication ); |
|
1208
|
|
|
|
|
|
|
## batch Request tag |
|
1209
|
0
|
|
|
|
|
0
|
$writer->startTag('RFRRequest'); |
|
1210
|
0
|
|
|
|
|
0
|
$writer->startTag('accountUpdateFileRequestData'); |
|
1211
|
0
|
|
|
|
|
0
|
$writer->startTag('merchantId'); |
|
1212
|
0
|
|
|
|
|
0
|
$writer->characters( $args->{'merchantid'} ); |
|
1213
|
0
|
|
|
|
|
0
|
$writer->endTag('merchantId'); |
|
1214
|
0
|
|
|
|
|
0
|
$writer->startTag('postDay'); |
|
1215
|
0
|
|
|
|
|
0
|
$writer->characters( $args->{'date'} ); |
|
1216
|
0
|
|
|
|
|
0
|
$writer->endTag('postDay'); |
|
1217
|
0
|
|
|
|
|
0
|
$writer->endTag('accountUpdateFileRequestData'); |
|
1218
|
0
|
|
|
|
|
0
|
$writer->endTag("RFRRequest"); |
|
1219
|
0
|
|
|
|
|
0
|
$writer->endTag("litleRequest"); |
|
1220
|
0
|
|
|
|
|
0
|
$writer->end(); |
|
1221
|
|
|
|
|
|
|
## END XML Generation |
|
1222
|
|
|
|
|
|
|
# |
|
1223
|
0
|
|
|
|
|
0
|
$self->port('15000'); |
|
1224
|
0
|
|
|
|
|
0
|
$self->path('/'); |
|
1225
|
0
|
|
|
|
|
0
|
my ( $page, $status_code, %headers ) = $self->https_post($post_data); |
|
1226
|
|
|
|
|
|
|
|
|
1227
|
0
|
|
|
|
|
0
|
$self->server_request( $post_data ); |
|
1228
|
0
|
|
|
|
|
0
|
$self->server_response( $page ); |
|
1229
|
0
|
0
|
|
|
|
0
|
warn $self->server_request if $DEBUG; |
|
1230
|
|
|
|
|
|
|
|
|
1231
|
0
|
0
|
|
|
|
0
|
warn Dumper [ $page, $status_code, \%headers ] if $DEBUG; |
|
1232
|
|
|
|
|
|
|
|
|
1233
|
0
|
|
|
|
|
0
|
my $response = {}; |
|
1234
|
0
|
0
|
|
|
|
0
|
if ( $status_code =~ /^200/ ) { |
|
1235
|
0
|
0
|
0
|
|
|
0
|
if ( ! eval { $response = XMLin($page); } ) { |
|
|
0
|
0
|
|
|
|
0
|
|
|
1236
|
0
|
|
|
|
|
0
|
die "XML PARSING FAILURE: $@"; |
|
1237
|
|
|
|
|
|
|
} |
|
1238
|
|
|
|
|
|
|
elsif ( exists( $response->{'response'} ) && $response->{'response'} == 1 ) |
|
1239
|
|
|
|
|
|
|
{ |
|
1240
|
|
|
|
|
|
|
## parse error type error |
|
1241
|
0
|
|
|
|
|
0
|
warn Dumper( $response, $self->server_request ); |
|
1242
|
0
|
|
|
|
|
0
|
$self->error_message( $response->{'message'} ); |
|
1243
|
0
|
|
|
|
|
0
|
return; |
|
1244
|
|
|
|
|
|
|
} |
|
1245
|
|
|
|
|
|
|
else { |
|
1246
|
0
|
|
|
|
|
0
|
$self->error_message( $response->{'RFRResponse'}->{'message'} ); |
|
1247
|
|
|
|
|
|
|
} |
|
1248
|
|
|
|
|
|
|
} |
|
1249
|
|
|
|
|
|
|
else { |
|
1250
|
0
|
|
|
|
|
0
|
die "CONNECTION FAILURE: $status_code"; |
|
1251
|
|
|
|
|
|
|
} |
|
1252
|
0
|
|
|
|
|
0
|
$self->{_response} = $response; |
|
1253
|
0
|
0
|
|
|
|
0
|
if ( $response->{'RFRResponse'} ) { |
|
1254
|
|
|
|
|
|
|
## litle returns an 'error' if the file is not done. So it's not ready yet. |
|
1255
|
0
|
|
|
|
|
0
|
$self->result_code( $response->{'RFRResponse'}->{'response'} ); |
|
1256
|
0
|
|
|
|
|
0
|
return; |
|
1257
|
|
|
|
|
|
|
} |
|
1258
|
|
|
|
|
|
|
else { |
|
1259
|
|
|
|
|
|
|
|
|
1260
|
|
|
|
|
|
|
#if processed, it returns as a batch, so, success, and let get the details |
|
1261
|
0
|
|
|
|
|
0
|
my $resp = $response->{'batchResponse'}; |
|
1262
|
0
|
0
|
|
|
|
0
|
$self->is_success( $resp->{'response'} eq '000' ? 1 : 0 ); |
|
1263
|
0
|
|
|
|
|
0
|
$self->{'batch_response'} = $resp; |
|
1264
|
0
|
|
|
|
|
0
|
$self->_parse_batch_response; |
|
1265
|
|
|
|
|
|
|
} |
|
1266
|
|
|
|
|
|
|
} |
|
1267
|
|
|
|
|
|
|
|
|
1268
|
|
|
|
|
|
|
sub _sftp_connect { |
|
1269
|
0
|
|
|
0
|
|
0
|
my ($self,$args,$dir) = @_; |
|
1270
|
0
|
0
|
|
|
|
0
|
$self->_die("Missing ftp_username") if ! $args->{'ftp_username'}; |
|
1271
|
0
|
0
|
|
|
|
0
|
$self->_die("Missing ftp_password") if ! $args->{'ftp_password'}; |
|
1272
|
0
|
|
|
|
|
0
|
require Net::SFTP::Foreign; |
|
1273
|
|
|
|
|
|
|
my $sftp = Net::SFTP::Foreign->new( |
|
1274
|
|
|
|
|
|
|
$self->server(), |
|
1275
|
|
|
|
|
|
|
timeout => $args->{'ftp_timeout'} || 90, |
|
1276
|
|
|
|
|
|
|
stderr_discard => 1, |
|
1277
|
|
|
|
|
|
|
user => $args->{'ftp_username'}, |
|
1278
|
0
|
|
0
|
|
|
0
|
password => $args->{'ftp_password'}, |
|
1279
|
|
|
|
|
|
|
); |
|
1280
|
0
|
0
|
|
|
|
0
|
$sftp->error and $self->_die("SSH connection failed: " . $sftp->error); |
|
1281
|
|
|
|
|
|
|
|
|
1282
|
0
|
0
|
|
|
|
0
|
if ($dir) { |
|
1283
|
0
|
0
|
|
|
|
0
|
$sftp->setcwd($dir) |
|
1284
|
|
|
|
|
|
|
or $self->_die("Cannot change working directory ", $sftp->error); |
|
1285
|
|
|
|
|
|
|
} |
|
1286
|
|
|
|
|
|
|
|
|
1287
|
0
|
|
|
|
|
0
|
return $sftp; |
|
1288
|
|
|
|
|
|
|
} |
|
1289
|
|
|
|
|
|
|
|
|
1290
|
|
|
|
|
|
|
sub _die { |
|
1291
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
|
1292
|
0
|
|
|
|
|
0
|
my $msg = join '', @_; |
|
1293
|
0
|
|
|
|
|
0
|
$self->is_success(0); |
|
1294
|
0
|
|
|
|
|
0
|
$self->error_message( $msg ); |
|
1295
|
0
|
|
|
|
|
0
|
die $msg."\n"; |
|
1296
|
|
|
|
|
|
|
} |
|
1297
|
|
|
|
|
|
|
|
|
1298
|
|
|
|
|
|
|
|
|
1299
|
|
|
|
|
|
|
sub retrieve_batch_list { |
|
1300
|
0
|
|
|
0
|
1
|
0
|
my ($self, %opts ) = @_; |
|
1301
|
|
|
|
|
|
|
|
|
1302
|
0
|
|
|
|
|
0
|
local $SCRUBBER=1; |
|
1303
|
0
|
|
|
|
|
0
|
$self->_litle_init(\%opts); |
|
1304
|
|
|
|
|
|
|
|
|
1305
|
0
|
|
|
|
|
0
|
my $sftp = $self->_sftp_connect(\%opts,'outbound'); |
|
1306
|
|
|
|
|
|
|
|
|
1307
|
0
|
0
|
|
|
|
0
|
my $ls = $sftp->ls( wanted => qr/\.asc$/ ) |
|
1308
|
|
|
|
|
|
|
or $self->_die("Cannot get directory listing ", $sftp->error); |
|
1309
|
|
|
|
|
|
|
|
|
1310
|
0
|
|
|
|
|
0
|
my @filenames = map {$_->{'filename'}} @{ $ls }; |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
1311
|
0
|
|
|
|
|
0
|
$self->is_success(1); |
|
1312
|
0
|
|
|
|
|
0
|
return \@filenames; |
|
1313
|
|
|
|
|
|
|
} |
|
1314
|
|
|
|
|
|
|
|
|
1315
|
|
|
|
|
|
|
|
|
1316
|
|
|
|
|
|
|
sub retrieve_batch_delete { |
|
1317
|
0
|
|
|
0
|
1
|
0
|
my ( $self, %opts ) = @_; |
|
1318
|
|
|
|
|
|
|
|
|
1319
|
0
|
|
|
|
|
0
|
local $SCRUBBER=1; |
|
1320
|
0
|
|
|
|
|
0
|
$self->_litle_init(\%opts); |
|
1321
|
|
|
|
|
|
|
|
|
1322
|
0
|
0
|
|
|
|
0
|
$self->_die("Missing batch_id") if !$opts{'batch_id'}; |
|
1323
|
|
|
|
|
|
|
|
|
1324
|
0
|
|
|
|
|
0
|
my $sftp = $self->_sftp_connect(\%opts,'outbound'); |
|
1325
|
|
|
|
|
|
|
|
|
1326
|
0
|
|
|
|
|
0
|
my $filename = $opts{'batch_id'}; |
|
1327
|
0
|
0
|
|
|
|
0
|
$sftp->remove( $filename ) |
|
1328
|
|
|
|
|
|
|
or $self->_die("Cannot delete $filename: ", $sftp->error); |
|
1329
|
|
|
|
|
|
|
|
|
1330
|
0
|
|
|
|
|
0
|
$self->is_success(1); |
|
1331
|
|
|
|
|
|
|
} |
|
1332
|
|
|
|
|
|
|
|
|
1333
|
|
|
|
|
|
|
|
|
1334
|
|
|
|
|
|
|
sub retrieve_batch { |
|
1335
|
0
|
|
|
0
|
1
|
0
|
my ( $self, %opts ) = @_; |
|
1336
|
|
|
|
|
|
|
|
|
1337
|
0
|
|
|
|
|
0
|
local $SCRUBBER=1; |
|
1338
|
0
|
|
|
|
|
0
|
$self->_litle_init(\%opts); |
|
1339
|
|
|
|
|
|
|
|
|
1340
|
0
|
0
|
|
|
|
0
|
$self->_die("Missing batch_id") if !$opts{'batch_id'}; |
|
1341
|
|
|
|
|
|
|
|
|
1342
|
0
|
|
|
|
|
0
|
my $post_data; |
|
1343
|
0
|
0
|
|
|
|
0
|
if ( $opts{'batch_return'} ) { |
|
1344
|
|
|
|
|
|
|
## passed in data structure |
|
1345
|
0
|
|
|
|
|
0
|
$post_data = $opts{'batch_return'}; |
|
1346
|
0
|
|
|
|
|
0
|
$self->server_request('Data was provided using batch_return option'); |
|
1347
|
|
|
|
|
|
|
} |
|
1348
|
|
|
|
|
|
|
else { |
|
1349
|
|
|
|
|
|
|
## go download a batch |
|
1350
|
0
|
|
|
|
|
0
|
my $sftp = $self->_sftp_connect(\%opts,'outbound'); |
|
1351
|
|
|
|
|
|
|
|
|
1352
|
0
|
|
|
|
|
0
|
my $filename = $opts{'batch_id'}; |
|
1353
|
0
|
|
|
|
|
0
|
$self->server_request('SFTP requesting file: '.$filename,1); |
|
1354
|
0
|
0
|
|
|
|
0
|
$post_data = $sftp->get_content( $filename ) |
|
1355
|
|
|
|
|
|
|
or $self->_die("Cannot GET $filename", $sftp->error); |
|
1356
|
|
|
|
|
|
|
} |
|
1357
|
0
|
|
|
|
|
0
|
$self->server_response_dangerous($post_data,1); |
|
1358
|
0
|
|
|
|
|
0
|
$self->server_response('Litle scrubber not initialized yet, see server_response_dangerous for a copy of the server response. Please note it may contain data that is not appropriate to store.',1); |
|
1359
|
|
|
|
|
|
|
|
|
1360
|
0
|
|
|
|
|
0
|
my $response = {}; |
|
1361
|
0
|
0
|
0
|
|
|
0
|
if ( ! eval { $response = XMLin($post_data, |
|
|
0
|
0
|
|
|
|
0
|
|
|
1362
|
|
|
|
|
|
|
ForceArray => [ 'accountUpdateResponse' ], |
|
1363
|
|
|
|
|
|
|
KeyAttr => '-id', |
|
1364
|
|
|
|
|
|
|
); } ) { |
|
1365
|
0
|
|
|
|
|
0
|
$self->_die("XML PARSING FAILURE: $@"); |
|
1366
|
|
|
|
|
|
|
} |
|
1367
|
|
|
|
|
|
|
elsif ( exists( $response->{'response'} ) && $response->{'response'} == 1 ) { |
|
1368
|
|
|
|
|
|
|
## parse error type error |
|
1369
|
0
|
|
|
|
|
0
|
warn Dumper( $response, $self->{'_post_data'} ); |
|
1370
|
0
|
|
0
|
|
|
0
|
$self->_die($response->{'message'} || 'No reason given'); |
|
1371
|
|
|
|
|
|
|
} |
|
1372
|
|
|
|
|
|
|
else { |
|
1373
|
|
|
|
|
|
|
## update the status |
|
1374
|
0
|
|
|
|
|
0
|
$self->error_message( $response->{'batchResponse'}->{'message'} ); |
|
1375
|
|
|
|
|
|
|
} |
|
1376
|
|
|
|
|
|
|
|
|
1377
|
0
|
|
|
|
|
0
|
$self->{_response} = $response; |
|
1378
|
0
|
|
|
|
|
0
|
my $resp = $response->{'batchResponse'}; |
|
1379
|
0
|
|
|
|
|
0
|
$self->order_number( $resp->{'litleSessionId'} ); |
|
1380
|
0
|
|
|
|
|
0
|
$self->result_code( $response->{'response'} ); |
|
1381
|
0
|
0
|
|
|
|
0
|
$self->is_success( $response->{'response'} eq '0' ? 1 : 0 ); |
|
1382
|
0
|
0
|
|
|
|
0
|
if ( $self->is_success() ) { |
|
1383
|
0
|
|
|
|
|
0
|
$self->{'batch_response'} = $resp; |
|
1384
|
0
|
|
|
|
|
0
|
return $self->_parse_batch_response; |
|
1385
|
|
|
|
|
|
|
} |
|
1386
|
|
|
|
|
|
|
} |
|
1387
|
|
|
|
|
|
|
|
|
1388
|
|
|
|
|
|
|
sub _get_update_response { |
|
1389
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
|
1390
|
0
|
|
|
|
|
0
|
require Business::OnlinePayment::Litle::UpdaterResponse; |
|
1391
|
0
|
|
|
|
|
0
|
my @response; |
|
1392
|
0
|
|
|
|
|
0
|
foreach |
|
1393
|
0
|
|
|
|
|
0
|
my $item ( @{ $self->{'batch_response'}->{'accountUpdateResponse'} } ) |
|
1394
|
|
|
|
|
|
|
{ |
|
1395
|
0
|
|
|
|
|
0
|
push @response, |
|
1396
|
|
|
|
|
|
|
Business::OnlinePayment::Litle::UpdaterResponse->new( $item ); |
|
1397
|
|
|
|
|
|
|
} |
|
1398
|
0
|
|
|
|
|
0
|
return \@response; |
|
1399
|
|
|
|
|
|
|
} |
|
1400
|
|
|
|
|
|
|
|
|
1401
|
|
|
|
|
|
|
sub _revmap_fields { |
|
1402
|
88
|
|
|
88
|
|
160
|
my $self = shift; |
|
1403
|
88
|
|
|
|
|
335
|
tie my (%map), 'Tie::IxHash', @_; |
|
1404
|
88
|
|
|
|
|
7591
|
my %content; |
|
1405
|
88
|
50
|
33
|
|
|
298
|
if ( $map{'content'} && ref( $map{'content'} ) eq 'HASH' ) { |
|
1406
|
88
|
|
|
|
|
1445
|
%content = %{ delete( $map{'content'} ) }; |
|
|
88
|
|
|
|
|
308
|
|
|
1407
|
|
|
|
|
|
|
} |
|
1408
|
|
|
|
|
|
|
else { |
|
1409
|
0
|
|
|
|
|
0
|
warn "WARNING: This content has not been pre-processed with map_fields"; |
|
1410
|
0
|
|
|
|
|
0
|
%content = $self->content(); |
|
1411
|
|
|
|
|
|
|
} |
|
1412
|
|
|
|
|
|
|
|
|
1413
|
|
|
|
|
|
|
map { |
|
1414
|
88
|
|
|
|
|
3590
|
my $value; |
|
|
459
|
|
|
|
|
2990
|
|
|
1415
|
459
|
100
|
|
|
|
1212
|
if ( ref( $map{$_} ) eq 'HASH' ) { |
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
1416
|
39
|
100
|
|
|
|
243
|
$value = $map{$_} if ( keys %{ $map{$_} } ); |
|
|
39
|
|
|
|
|
125
|
|
|
1417
|
|
|
|
|
|
|
} |
|
1418
|
|
|
|
|
|
|
elsif ( ref( $map{$_} ) eq 'ARRAY' ) { |
|
1419
|
6
|
|
|
|
|
71
|
$value = $map{$_}; |
|
1420
|
|
|
|
|
|
|
} |
|
1421
|
|
|
|
|
|
|
elsif ( ref( $map{$_} ) ) { |
|
1422
|
0
|
|
|
|
|
0
|
$value = ${ $map{$_} }; |
|
|
0
|
|
|
|
|
0
|
|
|
1423
|
|
|
|
|
|
|
} |
|
1424
|
|
|
|
|
|
|
elsif ( exists( $content{ $map{$_} } ) ) { |
|
1425
|
330
|
|
|
|
|
7031
|
$value = $content{ $map{$_} }; |
|
1426
|
|
|
|
|
|
|
} |
|
1427
|
|
|
|
|
|
|
|
|
1428
|
459
|
100
|
|
|
|
5079
|
if ( defined($value) ) { |
|
1429
|
370
|
|
|
|
|
1227
|
( $_ => $value ); |
|
1430
|
|
|
|
|
|
|
} |
|
1431
|
|
|
|
|
|
|
else { |
|
1432
|
89
|
|
|
|
|
346
|
(); |
|
1433
|
|
|
|
|
|
|
} |
|
1434
|
|
|
|
|
|
|
} ( keys %map ); |
|
1435
|
|
|
|
|
|
|
} |
|
1436
|
|
|
|
|
|
|
|
|
1437
|
|
|
|
|
|
|
sub _xmlwrite { |
|
1438
|
257
|
|
|
257
|
|
724
|
my ( $self, $writer, $item, $value ) = @_; |
|
1439
|
257
|
100
|
|
|
|
1777
|
if ( ref($value) eq 'HASH' ) { |
|
|
|
100
|
|
|
|
|
|
|
1440
|
47
|
50
|
|
|
|
146
|
my $attr = $value->{'attr'} ? $value->{'attr'} : {}; |
|
1441
|
47
|
|
|
|
|
326
|
$writer->startTag( $item, %{$attr} ); |
|
|
47
|
|
|
|
|
163
|
|
|
1442
|
47
|
|
|
|
|
2595
|
foreach ( keys(%$value) ) { |
|
1443
|
190
|
50
|
|
|
|
5004
|
next if $_ eq 'attr'; |
|
1444
|
190
|
|
|
|
|
1255
|
$self->_xmlwrite( $writer, $_, $value->{$_} ); |
|
1445
|
|
|
|
|
|
|
} |
|
1446
|
47
|
|
|
|
|
1300
|
$writer->endTag($item); |
|
1447
|
|
|
|
|
|
|
} |
|
1448
|
|
|
|
|
|
|
elsif ( ref($value) eq 'ARRAY' ) { |
|
1449
|
4
|
|
|
|
|
12
|
foreach ( @{$value} ) { |
|
|
4
|
|
|
|
|
13
|
|
|
1450
|
8
|
|
|
|
|
148
|
$self->_xmlwrite( $writer, $item, $_ ); |
|
1451
|
|
|
|
|
|
|
} |
|
1452
|
|
|
|
|
|
|
} |
|
1453
|
|
|
|
|
|
|
else { |
|
1454
|
206
|
|
|
|
|
602
|
$writer->startTag($item); |
|
1455
|
206
|
|
|
|
|
11619
|
$writer->characters($value); |
|
1456
|
206
|
|
|
|
|
4866
|
$writer->endTag($item); |
|
1457
|
|
|
|
|
|
|
} |
|
1458
|
|
|
|
|
|
|
} |
|
1459
|
|
|
|
|
|
|
|
|
1460
|
|
|
|
|
|
|
sub _default_scrubber { |
|
1461
|
9
|
|
|
9
|
|
870
|
my $cc = shift; |
|
1462
|
9
|
|
|
|
|
48
|
my $del = substr($cc,0,6).('X'x(length($cc)-10)).substr($cc,-4,4); # show first 6 and last 4 |
|
1463
|
9
|
|
|
|
|
49
|
return $del; |
|
1464
|
|
|
|
|
|
|
} |
|
1465
|
|
|
|
|
|
|
|
|
1466
|
|
|
|
|
|
|
sub _litle_scrubber_add_card { |
|
1467
|
7
|
|
|
7
|
|
136
|
my ( $self, $cc ) = @_; |
|
1468
|
7
|
100
|
|
|
|
45
|
return if ! $cc; |
|
1469
|
5
|
|
|
|
|
15
|
my $scrubber = $self->{_scrubber}; |
|
1470
|
5
|
|
|
|
|
12
|
scrubber_add_scrubber({$cc=>&{$scrubber}($cc)}); |
|
|
5
|
|
|
|
|
19
|
|
|
1471
|
|
|
|
|
|
|
} |
|
1472
|
|
|
|
|
|
|
|
|
1473
|
|
|
|
|
|
|
sub _litle_init { |
|
1474
|
6
|
|
|
6
|
|
20
|
my ( $self, $opts ) = @_; |
|
1475
|
|
|
|
|
|
|
|
|
1476
|
|
|
|
|
|
|
# initialize/reset the reporting methods |
|
1477
|
6
|
|
|
|
|
128
|
$self->is_success(0); |
|
1478
|
6
|
|
|
|
|
81
|
$self->server_request(''); |
|
1479
|
6
|
|
|
|
|
29
|
$self->server_response(''); |
|
1480
|
6
|
|
|
|
|
123
|
$self->error_message(''); |
|
1481
|
|
|
|
|
|
|
|
|
1482
|
|
|
|
|
|
|
# some calls are passed via the content method, others are direct arguments... this way we cover both |
|
1483
|
6
|
|
|
|
|
61
|
my %content = $self->content(); |
|
1484
|
6
|
|
|
|
|
178
|
foreach my $ptr (\%content,$opts) { |
|
1485
|
12
|
100
|
|
|
|
81
|
next if ! $ptr; |
|
1486
|
|
|
|
|
|
|
scrubber_init({ |
|
1487
|
|
|
|
|
|
|
quotemeta($ptr->{'password'}||'')=>'DELETED', |
|
1488
|
|
|
|
|
|
|
quotemeta($ptr->{'ftp_password'}||'')=>'DELETED', |
|
1489
|
6
|
50
|
50
|
|
|
96
|
($ptr->{'cvv2'} ? '(?<=[^\d])'.quotemeta($ptr->{'cvv2'}).'(?=[^\d])' : '')=>'DELETED', |
|
|
|
|
50
|
|
|
|
|
|
1490
|
|
|
|
|
|
|
}); |
|
1491
|
6
|
|
|
|
|
1378
|
$self->_litle_scrubber_add_card($ptr->{'card_number'}); |
|
1492
|
|
|
|
|
|
|
} |
|
1493
|
|
|
|
|
|
|
} |
|
1494
|
|
|
|
|
|
|
|
|
1495
|
|
|
|
|
|
|
|
|
1496
|
|
|
|
|
|
|
sub chargeback_activity_request { |
|
1497
|
0
|
|
|
0
|
1
|
|
my ( $self ) = @_; |
|
1498
|
|
|
|
|
|
|
|
|
1499
|
0
|
|
|
|
|
|
local $SCRUBBER=1; |
|
1500
|
0
|
|
|
|
|
|
$self->_litle_init; |
|
1501
|
|
|
|
|
|
|
|
|
1502
|
0
|
|
|
|
|
|
my $post_data; |
|
1503
|
0
|
|
|
|
|
|
my %content = $self->content(); |
|
1504
|
|
|
|
|
|
|
|
|
1505
|
|
|
|
|
|
|
## activity_date |
|
1506
|
|
|
|
|
|
|
## Type = Date; Format = YYYY-MM-DD |
|
1507
|
0
|
0
|
0
|
|
|
|
if ( ! $content{'activity_date'} || $content{'activity_date'} !~ m/^\d{4}-(\d{2})-(\d{2})$/ || $1 > 12 || $2 > 31) { |
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
1508
|
0
|
|
0
|
|
|
|
$self->_die("Invalid Date Pattern, YYYY-MM-DD required:" . ( $content{'activity_date'} || 'undef')); |
|
1509
|
|
|
|
|
|
|
} |
|
1510
|
|
|
|
|
|
|
# |
|
1511
|
|
|
|
|
|
|
## financials only [true,false] |
|
1512
|
|
|
|
|
|
|
# The financialOnly element is an optional child of the litleChargebackActivitiesRequest element. |
|
1513
|
|
|
|
|
|
|
# You use this flag in combination with the activityDate element to specify a request for chargeback financial activities that occurred on the specified date. |
|
1514
|
|
|
|
|
|
|
# A value of true returns only activities that had financial impact on the specified date. |
|
1515
|
|
|
|
|
|
|
# A value of false returns all activities on the specified date. |
|
1516
|
|
|
|
|
|
|
#Type = Boolean; Valid Values = true or false |
|
1517
|
0
|
|
|
|
|
|
my $financials; |
|
1518
|
0
|
0
|
|
|
|
|
if ( defined( $content{'financial_only'} ) ) { |
|
1519
|
0
|
0
|
|
|
|
|
$financials = $content{'financial_only'} ? 'true' : 'false'; |
|
1520
|
|
|
|
|
|
|
} |
|
1521
|
|
|
|
|
|
|
else { |
|
1522
|
0
|
|
|
|
|
|
$financials = 'false'; |
|
1523
|
|
|
|
|
|
|
} |
|
1524
|
|
|
|
|
|
|
|
|
1525
|
0
|
|
|
|
|
|
my $writer = new XML::Writer( |
|
1526
|
|
|
|
|
|
|
OUTPUT => \$post_data, |
|
1527
|
|
|
|
|
|
|
DATA_MODE => 1, |
|
1528
|
|
|
|
|
|
|
DATA_INDENT => 2, |
|
1529
|
|
|
|
|
|
|
ENCODING => 'utf-8', |
|
1530
|
|
|
|
|
|
|
); |
|
1531
|
|
|
|
|
|
|
## set the authentication data |
|
1532
|
0
|
|
|
|
|
|
tie my %authentication, 'Tie::IxHash', |
|
1533
|
|
|
|
|
|
|
$self->_revmap_fields( |
|
1534
|
|
|
|
|
|
|
content => \%content, |
|
1535
|
|
|
|
|
|
|
user => 'login', |
|
1536
|
|
|
|
|
|
|
password => 'password', |
|
1537
|
|
|
|
|
|
|
); |
|
1538
|
|
|
|
|
|
|
|
|
1539
|
|
|
|
|
|
|
## Start the XML Document, parent tag |
|
1540
|
0
|
|
|
|
|
|
$writer->xmlDecl(); |
|
1541
|
0
|
|
|
|
|
|
$writer->startTag( |
|
1542
|
|
|
|
|
|
|
"litleChargebackActivitiesRequest", |
|
1543
|
|
|
|
|
|
|
version => $self->chargeback_api_version, |
|
1544
|
|
|
|
|
|
|
xmlns => $self->xmlns, |
|
1545
|
|
|
|
|
|
|
); |
|
1546
|
|
|
|
|
|
|
|
|
1547
|
|
|
|
|
|
|
## authentication |
|
1548
|
0
|
|
|
|
|
|
$self->_xmlwrite( $writer, 'authentication', \%authentication ); |
|
1549
|
|
|
|
|
|
|
## batch Request tag |
|
1550
|
0
|
|
|
|
|
|
$writer->startTag('activityDate'); |
|
1551
|
0
|
|
|
|
|
|
$writer->characters( $content{'activity_date'} ); |
|
1552
|
0
|
|
|
|
|
|
$writer->endTag('activityDate'); |
|
1553
|
0
|
|
|
|
|
|
$writer->startTag('financialOnly'); |
|
1554
|
0
|
|
|
|
|
|
$writer->characters($financials); |
|
1555
|
0
|
|
|
|
|
|
$writer->endTag('financialOnly'); |
|
1556
|
0
|
|
|
|
|
|
$writer->endTag("litleChargebackActivitiesRequest"); |
|
1557
|
0
|
|
|
|
|
|
$writer->end(); |
|
1558
|
|
|
|
|
|
|
## END XML Generation |
|
1559
|
|
|
|
|
|
|
|
|
1560
|
0
|
|
|
|
|
|
$self->{'_post_data'} = $post_data; |
|
1561
|
0
|
0
|
|
|
|
|
warn $self->{'_post_data'} if $DEBUG; |
|
1562
|
|
|
|
|
|
|
#my ( $page, $status_code, %headers ) = $self->https_post( { 'Content-Type' => 'text/xml; charset=utf-8' } , $post_data); |
|
1563
|
0
|
|
|
|
|
|
my $url = 'https://'.$self->chargeback_server.':'.$self->chargeback_port.'/'.$self->chargeback_path; |
|
1564
|
0
|
|
|
|
|
|
my $tiny_response = HTTP::Tiny->new( verify_SSL=>$self->verify_SSL )->request('POST', $url, { |
|
1565
|
|
|
|
|
|
|
headers => { 'Content-Type' => 'text/xml; charset=utf-8', }, |
|
1566
|
|
|
|
|
|
|
content => $post_data, |
|
1567
|
|
|
|
|
|
|
} ); |
|
1568
|
|
|
|
|
|
|
|
|
1569
|
0
|
|
|
|
|
|
my $page = $tiny_response->{'content'}; |
|
1570
|
0
|
|
|
|
|
|
$self->server_request( $post_data ); |
|
1571
|
0
|
|
|
|
|
|
$self->server_response( $page ); |
|
1572
|
0
|
|
|
|
|
|
my $status_code = $tiny_response->{'status'}; |
|
1573
|
0
|
|
|
|
|
|
my %headers = %{$tiny_response->{'headers'}}; |
|
|
0
|
|
|
|
|
|
|
|
1574
|
|
|
|
|
|
|
|
|
1575
|
0
|
0
|
|
|
|
|
warn Dumper $page, $status_code, \%headers if $DEBUG; |
|
1576
|
|
|
|
|
|
|
|
|
1577
|
0
|
|
|
|
|
|
my $response = {}; |
|
1578
|
0
|
0
|
|
|
|
|
if ( $status_code =~ /^200/ ) { |
|
1579
|
|
|
|
|
|
|
## Failed to parse |
|
1580
|
0
|
0
|
0
|
|
|
|
if ( !eval { $response = XMLin($page, |
|
|
0
|
0
|
|
|
|
|
|
|
1581
|
|
|
|
|
|
|
ForceArray => [ 'caseActivity' ], |
|
1582
|
|
|
|
|
|
|
); } ) { |
|
1583
|
0
|
|
|
|
|
|
$self->_die("XML PARSING FAILURE: $@, $page"); |
|
1584
|
|
|
|
|
|
|
} ## well-formed failure message |
|
1585
|
|
|
|
|
|
|
elsif ( exists( $response->{'response'} ) |
|
1586
|
|
|
|
|
|
|
&& $response->{'response'} == 1 ) |
|
1587
|
|
|
|
|
|
|
{ |
|
1588
|
|
|
|
|
|
|
## parse error type error |
|
1589
|
0
|
|
|
|
|
|
warn Dumper( $response, $self->{'_post_data'} ); |
|
1590
|
0
|
|
|
|
|
|
$self->error_message( $response->{'message'} ); |
|
1591
|
0
|
|
|
|
|
|
return; |
|
1592
|
|
|
|
|
|
|
} ## success message |
|
1593
|
|
|
|
|
|
|
else { |
|
1594
|
|
|
|
|
|
|
$self->error_message( |
|
1595
|
0
|
|
|
|
|
|
$response->{'litleChargebackActivitiesResponse'}->{'message'} ); |
|
1596
|
|
|
|
|
|
|
} |
|
1597
|
|
|
|
|
|
|
} |
|
1598
|
|
|
|
|
|
|
else { |
|
1599
|
0
|
|
|
|
|
|
$status_code =~ s/[\r\n\s]+$// |
|
1600
|
|
|
|
|
|
|
; # remove newline so you can see the error in a linux console |
|
1601
|
0
|
0
|
|
|
|
|
if ( $status_code =~ /^(?:900|599)/ ) { |
|
1602
|
0
|
|
|
|
|
|
$status_code .= ' - verify Litle has whitelisted your IP'; |
|
1603
|
|
|
|
|
|
|
} |
|
1604
|
0
|
|
|
|
|
|
$self->_die("CONNECTION FAILURE: $status_code"); |
|
1605
|
|
|
|
|
|
|
} |
|
1606
|
0
|
|
|
|
|
|
$self->{_response} = $response; |
|
1607
|
|
|
|
|
|
|
|
|
1608
|
0
|
|
|
|
|
|
my @response_list; |
|
1609
|
0
|
|
|
|
|
|
require Business::OnlinePayment::Litle::ChargebackActivityResponse; |
|
1610
|
0
|
|
|
|
|
|
foreach my $case ( @{ $response->{caseActivity} } ) { |
|
|
0
|
|
|
|
|
|
|
|
1611
|
0
|
|
|
|
|
|
push @response_list, |
|
1612
|
|
|
|
|
|
|
Business::OnlinePayment::Litle::ChargebackActivityResponse->new($case); |
|
1613
|
|
|
|
|
|
|
} |
|
1614
|
|
|
|
|
|
|
|
|
1615
|
0
|
0
|
|
|
|
|
warn Dumper($response) if $DEBUG; |
|
1616
|
0
|
|
|
|
|
|
$self->is_success(1); |
|
1617
|
0
|
|
|
|
|
|
return \@response_list; |
|
1618
|
|
|
|
|
|
|
} |
|
1619
|
|
|
|
|
|
|
|
|
1620
|
|
|
|
|
|
|
|
|
1621
|
|
|
|
|
|
|
sub chargeback_update_request { |
|
1622
|
0
|
|
|
0
|
1
|
|
my ( $self ) = @_; |
|
1623
|
|
|
|
|
|
|
|
|
1624
|
0
|
|
|
|
|
|
local $SCRUBBER=1; |
|
1625
|
0
|
|
|
|
|
|
$self->_litle_init; |
|
1626
|
|
|
|
|
|
|
|
|
1627
|
0
|
|
|
|
|
|
my $post_data; |
|
1628
|
0
|
|
|
|
|
|
my %content = $self->content(); |
|
1629
|
|
|
|
|
|
|
|
|
1630
|
0
|
|
|
|
|
|
foreach my $key (qw(case_id merchant_activity_id activity )) { |
|
1631
|
|
|
|
|
|
|
## case_id |
|
1632
|
|
|
|
|
|
|
## merchant_activity_id |
|
1633
|
|
|
|
|
|
|
## activity |
|
1634
|
0
|
0
|
|
|
|
|
croak "Missing arg $key" unless $content{$key}; |
|
1635
|
|
|
|
|
|
|
} |
|
1636
|
|
|
|
|
|
|
|
|
1637
|
0
|
|
|
|
|
|
my $writer = new XML::Writer( |
|
1638
|
|
|
|
|
|
|
OUTPUT => \$post_data, |
|
1639
|
|
|
|
|
|
|
DATA_MODE => 1, |
|
1640
|
|
|
|
|
|
|
DATA_INDENT => 2, |
|
1641
|
|
|
|
|
|
|
ENCODING => 'utf-8', |
|
1642
|
|
|
|
|
|
|
); |
|
1643
|
|
|
|
|
|
|
## set the authentication data |
|
1644
|
0
|
|
|
|
|
|
tie my %authentication, 'Tie::IxHash', |
|
1645
|
|
|
|
|
|
|
$self->_revmap_fields( |
|
1646
|
|
|
|
|
|
|
content => \%content, |
|
1647
|
|
|
|
|
|
|
user => 'login', |
|
1648
|
|
|
|
|
|
|
password => 'password', |
|
1649
|
|
|
|
|
|
|
); |
|
1650
|
|
|
|
|
|
|
|
|
1651
|
|
|
|
|
|
|
## Start the XML Document, parent tag |
|
1652
|
0
|
|
|
|
|
|
$writer->xmlDecl(); |
|
1653
|
0
|
|
|
|
|
|
$writer->startTag( |
|
1654
|
|
|
|
|
|
|
"litleChargebackUpdateRequest", |
|
1655
|
|
|
|
|
|
|
version => $self->chargeback_api_version, |
|
1656
|
|
|
|
|
|
|
xmlns => $self->xmlns, |
|
1657
|
|
|
|
|
|
|
); |
|
1658
|
|
|
|
|
|
|
|
|
1659
|
|
|
|
|
|
|
## authentication |
|
1660
|
0
|
|
|
|
|
|
$self->_xmlwrite( $writer, 'authentication', \%authentication ); |
|
1661
|
0
|
|
|
|
|
|
$writer->startTag('caseUpdate'); |
|
1662
|
0
|
|
|
|
|
|
$writer->startTag('caseId'); |
|
1663
|
0
|
|
|
|
|
|
$writer->characters( $content{'case_id'} ); |
|
1664
|
0
|
|
|
|
|
|
$writer->endTag('caseId'); |
|
1665
|
|
|
|
|
|
|
|
|
1666
|
0
|
|
|
|
|
|
$writer->startTag('merchantActivityId'); |
|
1667
|
0
|
|
|
|
|
|
$writer->characters( $content{'merchant_activity_id'} ); |
|
1668
|
0
|
|
|
|
|
|
$writer->endTag('merchantActivityId'); |
|
1669
|
|
|
|
|
|
|
|
|
1670
|
0
|
|
|
|
|
|
$writer->startTag('activity'); |
|
1671
|
0
|
|
|
|
|
|
$writer->characters( $content{'activity'} ); |
|
1672
|
0
|
|
|
|
|
|
$writer->endTag('activity'); |
|
1673
|
|
|
|
|
|
|
|
|
1674
|
0
|
|
|
|
|
|
$writer->endTag('caseUpdate'); |
|
1675
|
0
|
|
|
|
|
|
$writer->endTag("litleChargebackUpdateRequest"); |
|
1676
|
0
|
|
|
|
|
|
$writer->end(); |
|
1677
|
|
|
|
|
|
|
## END XML Generation |
|
1678
|
|
|
|
|
|
|
|
|
1679
|
0
|
|
|
|
|
|
$self->{'_post_data'} = $post_data; |
|
1680
|
0
|
0
|
|
|
|
|
warn $self->{'_post_data'} if $DEBUG; |
|
1681
|
|
|
|
|
|
|
#my ( $page, $status_code, %headers ) = $self->https_post($post_data); |
|
1682
|
0
|
|
|
|
|
|
my $url = 'https://'.$self->chargeback_server.':'.$self->chargeback_port.'/'.$self->chargeback_path; |
|
1683
|
0
|
|
|
|
|
|
my $tiny_response = HTTP::Tiny->new( verify_SSL=>$self->verify_SSL )->request('POST', $url, { |
|
1684
|
|
|
|
|
|
|
headers => { 'Content-Type' => 'text/xml; charset=utf-8', }, |
|
1685
|
|
|
|
|
|
|
content => $post_data, |
|
1686
|
|
|
|
|
|
|
} ); |
|
1687
|
|
|
|
|
|
|
|
|
1688
|
0
|
|
|
|
|
|
my $page = $tiny_response->{'content'}; |
|
1689
|
0
|
|
|
|
|
|
$self->server_response( $page ); |
|
1690
|
0
|
|
|
|
|
|
my $status_code = $tiny_response->{'status'}; |
|
1691
|
0
|
|
|
|
|
|
my %headers = %{$tiny_response->{'headers'}}; |
|
|
0
|
|
|
|
|
|
|
|
1692
|
|
|
|
|
|
|
|
|
1693
|
0
|
0
|
|
|
|
|
warn Dumper $page, $status_code, \%headers if $DEBUG; |
|
1694
|
|
|
|
|
|
|
|
|
1695
|
0
|
|
|
|
|
|
my $response = {}; |
|
1696
|
0
|
0
|
|
|
|
|
if ( $status_code =~ /^200/ ) { |
|
1697
|
|
|
|
|
|
|
## Failed to parse |
|
1698
|
0
|
0
|
|
|
|
|
if ( !eval { $response = XMLin($page); } ) { |
|
|
0
|
|
|
|
|
|
|
|
1699
|
0
|
|
|
|
|
|
die "XML PARSING FAILURE: $@, $page"; |
|
1700
|
|
|
|
|
|
|
} ## well-formed failure message |
|
1701
|
0
|
|
|
|
|
|
$self->{_response} = $response; |
|
1702
|
0
|
0
|
|
|
|
|
if ( exists( $response->{'response'} ) ) { |
|
1703
|
|
|
|
|
|
|
## parse error type error |
|
1704
|
0
|
|
|
|
|
|
warn Dumper( $response, $self->{'_post_data'} ); |
|
1705
|
0
|
|
|
|
|
|
$self->result_code( $response->{'response'} ); # 0 - success, 1 invalid xml |
|
1706
|
0
|
|
|
|
|
|
$self->error_message( $response->{'message'} ); |
|
1707
|
0
|
|
|
|
|
|
$self->phoenixTxnId( $response->{'caseUpdateResponse'}{'phoenixTxnId'} ); |
|
1708
|
0
|
|
|
|
|
|
$self->is_success(1); |
|
1709
|
0
|
|
|
|
|
|
return $response->{'caseUpdateResponse'}{'phoenixTxnId'}; |
|
1710
|
|
|
|
|
|
|
} |
|
1711
|
|
|
|
|
|
|
else { |
|
1712
|
0
|
|
|
|
|
|
die "UNKNOWN XML RESULT: $page"; |
|
1713
|
|
|
|
|
|
|
} |
|
1714
|
|
|
|
|
|
|
} |
|
1715
|
|
|
|
|
|
|
else { |
|
1716
|
0
|
|
|
|
|
|
$status_code =~ s/[\r\n\s]+$// |
|
1717
|
|
|
|
|
|
|
; # remove newline so you can see the error in a linux console |
|
1718
|
0
|
0
|
|
|
|
|
if ( $status_code =~ /^(?:900|599)/ ) { |
|
1719
|
0
|
|
|
|
|
|
$status_code .= ' - verify Litle has whitelisted your IP'; |
|
1720
|
|
|
|
|
|
|
} |
|
1721
|
0
|
|
|
|
|
|
die "CONNECTION FAILURE: $status_code"; |
|
1722
|
|
|
|
|
|
|
} |
|
1723
|
|
|
|
|
|
|
} |
|
1724
|
|
|
|
|
|
|
|
|
1725
|
|
|
|
|
|
|
|
|
1726
|
|
|
|
|
|
|
|
|
1727
|
|
|
|
|
|
|
1; # End of Business::OnlinePayment::Litle |
|
1728
|
|
|
|
|
|
|
|
|
1729
|
|
|
|
|
|
|
__END__ |