line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Business::OnlinePayment::CardConnect; |
2
|
2
|
|
|
2
|
|
54065
|
use warnings; |
|
2
|
|
|
|
|
7
|
|
|
2
|
|
|
|
|
76
|
|
3
|
2
|
|
|
2
|
|
13
|
use strict; |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
52
|
|
4
|
|
|
|
|
|
|
|
5
|
2
|
|
|
2
|
|
303
|
use Business::OnlinePayment; |
|
2
|
|
|
|
|
2490
|
|
|
2
|
|
|
|
|
57
|
|
6
|
2
|
|
|
2
|
|
628
|
use Business::OnlinePayment::HTTPS; |
|
2
|
|
|
|
|
33376
|
|
|
2
|
|
|
|
|
70
|
|
7
|
2
|
|
|
2
|
|
17
|
use vars qw(@ISA $me $DEBUG); |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
107
|
|
8
|
2
|
|
|
2
|
|
10
|
use URI::Escape; |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
89
|
|
9
|
2
|
|
|
2
|
|
927
|
use HTTP::Tiny; |
|
2
|
|
|
|
|
67446
|
|
|
2
|
|
|
|
|
88
|
|
10
|
2
|
|
|
2
|
|
916
|
use JSON qw(to_json from_json); |
|
2
|
|
|
|
|
15117
|
|
|
2
|
|
|
|
|
14
|
|
11
|
2
|
|
|
2
|
|
980
|
use Business::CreditCard qw(cardtype); |
|
2
|
|
|
|
|
2597
|
|
|
2
|
|
|
|
|
124
|
|
12
|
2
|
|
|
2
|
|
754
|
use Data::Dumper; |
|
2
|
|
|
|
|
10718
|
|
|
2
|
|
|
|
|
162
|
|
13
|
2
|
|
|
2
|
|
21
|
use Carp qw(croak); |
|
2
|
|
|
|
|
6
|
|
|
2
|
|
|
|
|
104
|
|
14
|
2
|
|
|
2
|
|
782
|
use Log::Scrubber qw(disable $SCRUBBER scrubber :Carp scrubber_add_scrubber); |
|
2
|
|
|
|
|
9577
|
|
|
2
|
|
|
|
|
31
|
|
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
@ISA = qw(Business::OnlinePayment::HTTPS); |
17
|
|
|
|
|
|
|
$me = 'Business::OnlinePayment::CardConnect'; |
18
|
|
|
|
|
|
|
$DEBUG = 0; |
19
|
|
|
|
|
|
|
our $VERSION = '0.004'; # VERSION |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
# PODNAME: Business::OnlinePayment::CardConnect |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
# ABSTRACT: Business::OnlinePayment::CardConnect - CardConnect Backend for Business::OnlinePayment |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
=head1 SYNOPSIS |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
This is a plugin for the Business::OnlinePayment interface. Please refer to that documentation for general usage, and here for CardConnect specific usage. |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
In order to use this module, you will need to have an account set up with CardConnect L |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
use Business::OnlinePayment; |
32
|
|
|
|
|
|
|
my $tx = Business::OnlinePayment->new("CardConnect"); |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
$tx->content( |
35
|
|
|
|
|
|
|
type => 'CC', |
36
|
|
|
|
|
|
|
login => 'testdrive', |
37
|
|
|
|
|
|
|
password => '123qwe', |
38
|
|
|
|
|
|
|
action => 'Normal Authorization', |
39
|
|
|
|
|
|
|
description => 'FOO*Business::OnlinePayment test', |
40
|
|
|
|
|
|
|
amount => '49.95', |
41
|
|
|
|
|
|
|
customer_id => 'tfb', |
42
|
|
|
|
|
|
|
name => 'Tofu Beast', |
43
|
|
|
|
|
|
|
address => '123 Anystreet', |
44
|
|
|
|
|
|
|
city => 'Anywhere', |
45
|
|
|
|
|
|
|
state => 'UT', |
46
|
|
|
|
|
|
|
zip => '84058', |
47
|
|
|
|
|
|
|
card_number => '4007000000027', |
48
|
|
|
|
|
|
|
expiration => '09/02', |
49
|
|
|
|
|
|
|
cvv2 => '1234', #optional |
50
|
|
|
|
|
|
|
invoice_number => '54123', |
51
|
|
|
|
|
|
|
); |
52
|
|
|
|
|
|
|
$tx->submit(); |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
if($tx->is_success()) { |
55
|
|
|
|
|
|
|
print "Card processed successfully: ".$tx->authorization."\n"; |
56
|
|
|
|
|
|
|
} else { |
57
|
|
|
|
|
|
|
print "Card was rejected: ".$tx->error_message."\n"; |
58
|
|
|
|
|
|
|
} |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
=head1 METHODS AND FUNCTIONS |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
See L for the complete list. The following methods either override the methods in L or provide additional functions. |
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
=head2 result_code |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
Returns the response error code. |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
=head2 error_message |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
Returns the response error description text. |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
=head2 server_request |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
Returns the complete request that was sent to the server. The request has been stripped of card_num, cvv2, and password. So it should be safe to log. |
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
=cut |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
sub server_request { |
79
|
2
|
|
|
2
|
1
|
5
|
my ( $self, $val, $tf ) = @_; |
80
|
2
|
100
|
|
|
|
6
|
if ($val) { |
81
|
1
|
|
|
|
|
4
|
$self->{server_request} = scrubber $val; |
82
|
1
|
50
|
|
|
|
102
|
$self->server_request_dangerous($val,1) unless $tf; |
83
|
|
|
|
|
|
|
} |
84
|
2
|
|
|
|
|
4
|
return $self->{server_request}; |
85
|
|
|
|
|
|
|
} |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
=head2 server_request_dangerous |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
Returns the complete request that was sent to the server. This could contain data that is NOT SAFE to log. It should only be used in a test environment, or in a PCI compliant manner. |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
=cut |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
sub server_request_dangerous { |
94
|
1
|
|
|
1
|
1
|
3
|
my ( $self, $val, $tf ) = @_; |
95
|
1
|
50
|
|
|
|
4
|
if ($val) { |
96
|
1
|
|
|
|
|
3
|
$self->{server_request_dangerous} = $val; |
97
|
1
|
50
|
|
|
|
3
|
$self->server_request($val,1) unless $tf; |
98
|
|
|
|
|
|
|
} |
99
|
1
|
|
|
|
|
2
|
return $self->{server_request_dangerous}; |
100
|
|
|
|
|
|
|
} |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
=head2 server_response |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
Returns the complete response from the server. The response has been stripped of card_num, cvv2, and password. So it should be safe to log. |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
=cut |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
sub server_response { |
109
|
2
|
|
|
2
|
1
|
436
|
my ( $self, $val, $tf ) = @_; |
110
|
2
|
100
|
|
|
|
9
|
if ($val) { |
111
|
1
|
|
|
|
|
9
|
$self->{server_response} = scrubber $val; |
112
|
1
|
50
|
|
|
|
1513
|
$self->server_response_dangerous($val,1) unless $tf; |
113
|
|
|
|
|
|
|
} |
114
|
2
|
|
|
|
|
7
|
return $self->{server_response}; |
115
|
|
|
|
|
|
|
} |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
=head2 server_response_dangerous |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
Returns the complete response from the server. This could contain data that is NOT SAFE to log. It should only be used in a test environment, or in a PCI compliant manner. |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
=cut |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
sub server_response_dangerous { |
124
|
1
|
|
|
1
|
1
|
7
|
my ( $self, $val, $tf ) = @_; |
125
|
1
|
50
|
|
|
|
5
|
if ($val) { |
126
|
1
|
|
|
|
|
3
|
$self->{server_response_dangerous} = $val; |
127
|
1
|
50
|
|
|
|
6
|
$self->server_response($val,1) unless $tf; |
128
|
|
|
|
|
|
|
} |
129
|
1
|
|
|
|
|
5
|
return $self->{server_response_dangerous}; |
130
|
|
|
|
|
|
|
} |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
=head1 Handling of content(%content) data: |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
=head2 action |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
The following actions are valid |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
normal authorization |
139
|
|
|
|
|
|
|
authorization only |
140
|
|
|
|
|
|
|
post authorization |
141
|
|
|
|
|
|
|
credit |
142
|
|
|
|
|
|
|
void |
143
|
|
|
|
|
|
|
auth reversal |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
=head1 TESTING |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
In order to run the provided test suite, you will first need to apply and get your account setup with CyberSource. Then you can use the test account information they give you to run the test suite. The scripts will look for three environment variables to connect: BOP_USERNAME, BOP_PASSWORD, BOP_MERCHANTID |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
=head1 FUNCTIONS |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
=head2 _info |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
Return the introspection hash for BOP 3.x |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
=cut |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
=head2 _info |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
Return the introspection hash for BOP 3.x |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
=cut |
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
sub _info { |
164
|
|
|
|
|
|
|
return { |
165
|
0
|
|
|
0
|
|
0
|
info_compat => '0.01', |
166
|
|
|
|
|
|
|
gateway_name => 'CyberSource - SOAP Toolkit API', |
167
|
|
|
|
|
|
|
gateway_url => 'http://www.cybersource.com', |
168
|
|
|
|
|
|
|
module_version => $Business::OnlinePayment::CardConnect::VERSION, |
169
|
|
|
|
|
|
|
supported_types => ['CC','ECHECK'], |
170
|
|
|
|
|
|
|
supported_actions => { |
171
|
|
|
|
|
|
|
CC => [ |
172
|
|
|
|
|
|
|
'Normal Authorization', |
173
|
|
|
|
|
|
|
'Post Authorization', |
174
|
|
|
|
|
|
|
'Authorization Only', |
175
|
|
|
|
|
|
|
'Credit', |
176
|
|
|
|
|
|
|
'Void', |
177
|
|
|
|
|
|
|
'Auth Reversal', |
178
|
|
|
|
|
|
|
], |
179
|
|
|
|
|
|
|
}, |
180
|
|
|
|
|
|
|
}; |
181
|
|
|
|
|
|
|
} |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
=head2 set_defaults |
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
Used by BOP to set default values during "new" |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
=cut |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
sub set_defaults { |
190
|
1
|
|
|
1
|
1
|
43
|
my $self = shift; |
191
|
1
|
|
|
|
|
2
|
my %opts = @_; |
192
|
|
|
|
|
|
|
|
193
|
1
|
|
|
|
|
7
|
$self->build_subs( |
194
|
|
|
|
|
|
|
qw( order_number md5 avs_code cvv2_response card_token cavv_response failure_status verify_SSL ) |
195
|
|
|
|
|
|
|
); |
196
|
|
|
|
|
|
|
|
197
|
1
|
|
|
|
|
232
|
$self->build_subs( # built only for backwards compatibily with old cybersource moose version |
198
|
|
|
|
|
|
|
qw( response_code response_headers response_page login password require_avs ) |
199
|
|
|
|
|
|
|
); |
200
|
|
|
|
|
|
|
|
201
|
1
|
|
|
|
|
206
|
$self->test_transaction(0); |
202
|
1
|
|
|
|
|
11
|
$self->{_scrubber} = \&_default_scrubber; |
203
|
|
|
|
|
|
|
} |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
=head2 test_transaction |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
Get/set the server used for processing transactions. Possible values are Live, Certification, and Sandbox |
208
|
|
|
|
|
|
|
Default: Live |
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
#Live |
211
|
|
|
|
|
|
|
$self->test_transaction(0); |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
#Test |
214
|
|
|
|
|
|
|
$self->test_transaction(1); # currently not different from live |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
#Read current value |
217
|
|
|
|
|
|
|
$val = $self->test_transaction(); |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
=cut |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
sub test_transaction { |
222
|
2
|
|
|
2
|
1
|
681
|
my $self = shift; |
223
|
2
|
|
|
|
|
5
|
my $testMode = shift; |
224
|
2
|
50
|
0
|
|
|
8
|
if (! defined $testMode) { $testMode = $self->{'test_transaction'} || 0; } |
|
0
|
|
|
|
|
0
|
|
225
|
|
|
|
|
|
|
|
226
|
2
|
|
|
|
|
51
|
$self->require_avs(0); |
227
|
2
|
|
|
|
|
78
|
$self->verify_SSL(0); |
228
|
2
|
|
|
|
|
43
|
$self->port('6443'); |
229
|
2
|
|
|
|
|
43
|
$self->path('/cardconnect/rest/auth'); |
230
|
2
|
|
|
|
|
43
|
$self->server('fts.cardconnect.com'); |
231
|
2
|
|
|
|
|
42
|
$self->SUPER::test_transaction($testMode); |
232
|
|
|
|
|
|
|
} |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
=head2 submit |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
Submit your transaction to cybersource |
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
=cut |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
sub submit { |
241
|
0
|
|
|
0
|
1
|
0
|
my ($self) = @_; |
242
|
0
|
|
|
|
|
0
|
local $SCRUBBER=1; |
243
|
0
|
|
|
|
|
0
|
$self->_cardconnect_init; |
244
|
0
|
|
|
|
|
0
|
my %content = $self->content(); |
245
|
|
|
|
|
|
|
|
246
|
0
|
|
|
|
|
0
|
my $action_map = { |
247
|
|
|
|
|
|
|
'Normal Authorization' => 'auth', # this method auto detects when capture is needed |
248
|
|
|
|
|
|
|
'Authorization Only' => 'auth', |
249
|
|
|
|
|
|
|
'Post Authorization' => 'capture', |
250
|
|
|
|
|
|
|
'Void' => 'void', |
251
|
|
|
|
|
|
|
'Auth Reversal' => 'void', |
252
|
|
|
|
|
|
|
'Credit' => 'refund', |
253
|
|
|
|
|
|
|
}; |
254
|
0
|
|
0
|
|
|
0
|
my $action = $action_map->{$content{'action'}} || die "Unsupported action: ".$content{'action'}; |
255
|
0
|
0
|
0
|
|
|
0
|
die 'Amount must contain a decimal' if defined $content{'amount'} && $content{'amount'} !~ /\./; |
256
|
|
|
|
|
|
|
|
257
|
0
|
|
|
|
|
0
|
my $method = '_cardconnect_'.$action; |
258
|
0
|
|
|
|
|
0
|
return $self->$method(); |
259
|
|
|
|
|
|
|
} |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
sub _cardconnect_void { |
262
|
0
|
|
|
0
|
|
0
|
my ($self) = @_; |
263
|
0
|
|
|
|
|
0
|
my %content = $self->content(); |
264
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
my $post_data = { |
266
|
|
|
|
|
|
|
retref => $content{'order_number'}, |
267
|
0
|
|
|
|
|
0
|
merchid => $content{'merchantid'}, |
268
|
|
|
|
|
|
|
}; |
269
|
|
|
|
|
|
|
|
270
|
0
|
|
|
|
|
0
|
my $page = $self->_do_put_request( 'void', $post_data ); |
271
|
0
|
|
|
|
|
0
|
my $response = $page->{'content_json'}; |
272
|
|
|
|
|
|
|
|
273
|
0
|
0
|
|
|
|
0
|
$self->is_success($response->{'respstat'} eq 'A' ? $response : undef); |
274
|
0
|
|
|
|
|
0
|
$self->result_code($response->{'respstat'}); |
275
|
0
|
|
|
|
|
0
|
$self->order_number($response->{'retref'}); |
276
|
0
|
|
|
|
|
0
|
$self->error_message($response->{'resptext'}); |
277
|
|
|
|
|
|
|
|
278
|
0
|
|
|
|
|
0
|
return $response; |
279
|
|
|
|
|
|
|
} |
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
sub _cardconnect_refund { |
282
|
0
|
|
|
0
|
|
0
|
my ($self) = @_; |
283
|
0
|
|
|
|
|
0
|
my %content = $self->content(); |
284
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
my $post_data = { |
286
|
|
|
|
|
|
|
retref => $content{'order_number'}, |
287
|
0
|
|
|
|
|
0
|
merchid => $content{'merchantid'}, |
288
|
|
|
|
|
|
|
}; |
289
|
0
|
0
|
|
|
|
0
|
$post_data->{'amount'} = $content{'amount'} if defined $content{'amount'}; |
290
|
|
|
|
|
|
|
|
291
|
0
|
|
|
|
|
0
|
my $page = $self->_do_put_request( 'capture', $post_data ); |
292
|
0
|
|
|
|
|
0
|
my $response = $page->{'content_json'}; |
293
|
|
|
|
|
|
|
|
294
|
0
|
0
|
|
|
|
0
|
$self->is_success($response->{'respstat'} eq 'A' ? $response : undef); |
295
|
0
|
|
|
|
|
0
|
$self->result_code($response->{'respstat'}); |
296
|
0
|
|
|
|
|
0
|
$self->order_number($response->{'retref'}); |
297
|
0
|
|
|
|
|
0
|
$self->error_message($response->{'resptext'}); |
298
|
0
|
|
|
|
|
0
|
$self->order_number($response->{'retref'}); |
299
|
0
|
|
|
|
|
0
|
$self->error_message($response->{'resptext'}); |
300
|
0
|
|
|
|
|
0
|
$self->card_token($response->{'token'}); |
301
|
|
|
|
|
|
|
|
302
|
0
|
|
|
|
|
0
|
return $response; |
303
|
|
|
|
|
|
|
} |
304
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
sub _cardconnect_capture { |
306
|
0
|
|
|
0
|
|
0
|
my ($self) = @_; |
307
|
0
|
|
|
|
|
0
|
my %content = $self->content(); |
308
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
my $post_data = { |
310
|
|
|
|
|
|
|
retref => $content{'order_number'}, |
311
|
0
|
|
|
|
|
0
|
merchid => $content{'merchantid'}, |
312
|
|
|
|
|
|
|
}; |
313
|
0
|
0
|
|
|
|
0
|
$post_data->{'amount'} = $content{'amount'} if defined $content{'amount'}; |
314
|
0
|
|
|
|
|
0
|
$self->_cardconnect_add_level2($post_data); |
315
|
|
|
|
|
|
|
|
316
|
0
|
|
|
|
|
0
|
my $page = $self->_do_put_request( 'capture', $post_data ); |
317
|
0
|
|
|
|
|
0
|
my $response = $page->{'content_json'}; |
318
|
|
|
|
|
|
|
|
319
|
0
|
0
|
|
|
|
0
|
$self->is_success($response->{'respstat'} eq 'A' ? $response : undef); |
320
|
0
|
|
|
|
|
0
|
$self->result_code($response->{'respstat'}); |
321
|
0
|
|
|
|
|
0
|
$self->order_number($response->{'retref'}); |
322
|
0
|
|
|
|
|
0
|
$self->error_message($response->{'resptext'}); |
323
|
0
|
|
|
|
|
0
|
$self->order_number($response->{'retref'}); |
324
|
0
|
|
|
|
|
0
|
$self->error_message($response->{'resptext'}); |
325
|
0
|
|
|
|
|
0
|
$self->card_token($response->{'token'}); |
326
|
|
|
|
|
|
|
|
327
|
0
|
|
|
|
|
0
|
return $response; |
328
|
|
|
|
|
|
|
} |
329
|
|
|
|
|
|
|
|
330
|
|
|
|
|
|
|
sub _cardconnect_add_level2 { |
331
|
1
|
|
|
1
|
|
3
|
my ($self,$post_data) = @_; |
332
|
1
|
|
|
|
|
2
|
my %content = $self->content(); |
333
|
1
|
50
|
|
|
|
14
|
$post_data->{'ponumber'} = $content{'po_number'} if defined $content{'po_number'}; |
334
|
1
|
50
|
|
|
|
6
|
$post_data->{'shiptozip'} = $content{'ship_zip'} if defined $content{'ship_zip'}; |
335
|
1
|
50
|
|
|
|
2
|
$post_data->{'taxamnt'} = $content{'tax'} if defined $content{'tax'}; |
336
|
1
|
50
|
33
|
|
|
5
|
if ( defined $content{'products'} && scalar( @{ $content{'products'} } ) < 100 ) { |
|
0
|
|
|
|
|
0
|
|
337
|
0
|
|
|
|
|
0
|
my @products; |
338
|
0
|
|
|
|
|
0
|
my $lineno = 0; |
339
|
0
|
|
|
|
|
0
|
foreach my $productOrig ( @{ $content{'products'} } ) { |
|
0
|
|
|
|
|
0
|
|
340
|
0
|
|
|
|
|
0
|
$lineno++; |
341
|
|
|
|
|
|
|
my $item = { |
342
|
|
|
|
|
|
|
"discamnt" => $productOrig->{'discount'}, |
343
|
|
|
|
|
|
|
"unitcost" => $productOrig->{'cost'}, |
344
|
|
|
|
|
|
|
"lineno" => $lineno, |
345
|
|
|
|
|
|
|
"description" => $productOrig->{'description'}, |
346
|
|
|
|
|
|
|
"taxamnt" => $productOrig->{'tax'}, |
347
|
|
|
|
|
|
|
"quantity" => $productOrig->{'quantity'}, |
348
|
0
|
|
|
|
|
0
|
"netamnt" => $productOrig->{'amount'}, |
349
|
|
|
|
|
|
|
#"upc" => "UPC-1", |
350
|
|
|
|
|
|
|
#"material" => "MATERIAL-1" |
351
|
|
|
|
|
|
|
}; |
352
|
0
|
|
|
|
|
0
|
push @products, $item; |
353
|
|
|
|
|
|
|
} |
354
|
|
|
|
|
|
|
} |
355
|
|
|
|
|
|
|
} |
356
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
sub _cardconnect_auth { |
358
|
1
|
|
|
1
|
|
2
|
my ($self) = @_; |
359
|
1
|
|
|
|
|
3
|
my %content = $self->content(); |
360
|
|
|
|
|
|
|
|
361
|
1
|
|
|
|
|
18
|
my $post_data = {}; |
362
|
1
|
50
|
33
|
|
|
5
|
if ($content{'routing_code'} && $content{'account_number'}) { |
|
|
50
|
|
|
|
|
|
363
|
|
|
|
|
|
|
$post_data = { |
364
|
|
|
|
|
|
|
accttype => 'ECHK', |
365
|
|
|
|
|
|
|
account => $content{'account_number'}, |
366
|
|
|
|
|
|
|
bankaba => $content{'routing_code'}, |
367
|
|
|
|
|
|
|
merchid => $content{'merchantid'}, |
368
|
|
|
|
|
|
|
name => $content{'first_name'}.' '.$content{'last_name'}, |
369
|
|
|
|
|
|
|
amount => $content{'amount'}, |
370
|
0
|
|
0
|
|
|
0
|
currency => $content{'currency'} || "USD", |
371
|
|
|
|
|
|
|
} |
372
|
|
|
|
|
|
|
} elsif ($content{'card_number'}) { |
373
|
1
|
|
|
|
|
4
|
$content{'expiration'} =~ s/\///; # CardConnect doesn't want the / between MM and YY |
374
|
|
|
|
|
|
|
$post_data = { |
375
|
|
|
|
|
|
|
merchid => $content{'merchantid'}, |
376
|
|
|
|
|
|
|
orderid => $content{'invoice_number'}, |
377
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
account => $content{'card_number'}, |
379
|
|
|
|
|
|
|
expiry => $content{'expiration'}, |
380
|
|
|
|
|
|
|
cvv2 => $content{'cvv2'}, |
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
amount => $content{'amount'}, |
383
|
|
|
|
|
|
|
currency => $content{'currency'} || "USD", |
384
|
|
|
|
|
|
|
name => $content{'first_name'}.' '.$content{'last_name'}, |
385
|
|
|
|
|
|
|
address => $content{'address'}, |
386
|
|
|
|
|
|
|
city => $content{'city'}, |
387
|
|
|
|
|
|
|
region => $content{'state'}, |
388
|
|
|
|
|
|
|
country => $content{'country'}, |
389
|
|
|
|
|
|
|
postal => $content{'zip'}, |
390
|
|
|
|
|
|
|
email => $content{'email'}, |
391
|
|
|
|
|
|
|
ecomind => "E", |
392
|
|
|
|
|
|
|
track => undef, |
393
|
|
|
|
|
|
|
tokenize => "Y", |
394
|
|
|
|
|
|
|
userfields => [ |
395
|
1
|
|
50
|
|
|
16
|
{ description => $content{'description'} }, |
396
|
|
|
|
|
|
|
], |
397
|
|
|
|
|
|
|
}; |
398
|
1
|
|
|
|
|
3
|
$self->_cardconnect_add_level2($post_data); |
399
|
|
|
|
|
|
|
} else { |
400
|
0
|
|
|
|
|
0
|
die 'Unsupported payment method'; |
401
|
|
|
|
|
|
|
} |
402
|
1
|
50
|
|
|
|
3
|
$post_data->{'capture'} = "Y" if $content{'action'} eq 'Normal Authorization'; |
403
|
|
|
|
|
|
|
|
404
|
1
|
|
|
|
|
3
|
my $page = $self->_do_put_request( 'auth', $post_data ); |
405
|
1
|
|
|
|
|
7
|
my $response = $page->{'content_json'}; |
406
|
|
|
|
|
|
|
|
407
|
1
|
50
|
|
|
|
43
|
$self->is_success($response->{'respstat'} eq 'A' ? $response : undef); |
408
|
1
|
|
|
|
|
55
|
$self->result_code($response->{'respstat'}); |
409
|
1
|
|
|
|
|
55
|
$self->authorization($response->{'authcode'}); |
410
|
1
|
|
|
|
|
56
|
$self->order_number($response->{'retref'}); |
411
|
1
|
|
|
|
|
50
|
$self->error_message($response->{'resptext'}); |
412
|
1
|
|
|
|
|
53
|
$self->card_token($response->{'token'}); |
413
|
1
|
|
|
|
|
57
|
$self->avs_code($response->{'avsresp'}); |
414
|
1
|
|
|
|
|
52
|
$self->cvv2_response($response->{'cvvresp'}); |
415
|
|
|
|
|
|
|
|
416
|
1
|
|
|
|
|
49
|
return $response; |
417
|
|
|
|
|
|
|
} |
418
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
sub _do_put_request { |
420
|
1
|
|
|
1
|
|
2
|
my ($self, $action, $post_data) = @_; |
421
|
1
|
|
|
|
|
3
|
my %content = $self->content(); # needed for basic auth |
422
|
1
|
|
|
|
|
19
|
my $options = { |
423
|
|
|
|
|
|
|
headers => { |
424
|
|
|
|
|
|
|
'Content-Type' => 'application/json', |
425
|
|
|
|
|
|
|
}, |
426
|
|
|
|
|
|
|
content => to_json $post_data, |
427
|
|
|
|
|
|
|
}; |
428
|
1
|
|
|
|
|
98
|
$self->login($content{'login'}); |
429
|
1
|
|
|
|
|
24
|
$self->password($content{'password'}); |
430
|
1
|
|
|
|
|
9
|
my $url= 'https://'.uri_escape($content{'login'}).':'.uri_escape($content{'password'}).'@fts.cardconnect.com:6443/cardconnect/rest/'.$action; |
431
|
1
|
|
|
|
|
39
|
$self->server_request( $url."\n\n".$options->{'content'} ); |
432
|
1
|
50
|
|
|
|
2
|
warn $self->server_request if $DEBUG; |
433
|
1
|
|
|
|
|
747
|
my $page = HTTP::Tiny->new->request('PUT', $url, $options); |
434
|
1
|
|
|
|
|
651745
|
$self->server_response( $page ); |
435
|
1
|
50
|
|
|
|
5
|
warn Dumper $self->server_response if $DEBUG; |
436
|
1
|
50
|
|
|
|
9
|
if ($page->{'status'} eq '200') { |
|
|
50
|
|
|
|
|
|
437
|
0
|
|
|
|
|
0
|
$page->{'content_json'} = eval { from_json $page->{'content'}; } |
|
0
|
|
|
|
|
0
|
|
438
|
|
|
|
|
|
|
} elsif ($page->{'status'} eq '401') { |
439
|
1
|
|
|
|
|
10
|
$page->{'content_json'} = { |
440
|
|
|
|
|
|
|
respstat => 'U', |
441
|
|
|
|
|
|
|
resptext => 'This request requires authentication.', |
442
|
|
|
|
|
|
|
}; |
443
|
|
|
|
|
|
|
} else { |
444
|
0
|
|
|
|
|
0
|
$page->{'content_json'} = { |
445
|
|
|
|
|
|
|
respstat => 'U', |
446
|
|
|
|
|
|
|
resptext => 'Unknown response from payment gateway.', |
447
|
|
|
|
|
|
|
}; |
448
|
|
|
|
|
|
|
} |
449
|
1
|
|
|
|
|
5
|
my $e = $@; |
450
|
1
|
50
|
|
|
|
6
|
die "Could not process JSON: ".$e if ($e); |
451
|
1
|
|
|
|
|
66
|
$self->response_code($page->{'status'}); |
452
|
1
|
|
|
|
|
55
|
$self->response_headers($page->{'headers'}); |
453
|
1
|
|
|
|
|
52
|
$self->response_page($page->{'content'}); |
454
|
1
|
|
|
|
|
30
|
return $page; |
455
|
|
|
|
|
|
|
} |
456
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
sub _default_scrubber { |
458
|
1
|
|
|
1
|
|
4
|
my $cc = shift; |
459
|
1
|
|
|
|
|
6
|
my $del = substr($cc,0,6).('X'x(length($cc)-10)).substr($cc,-4,4); # show first 6 and last 4 |
460
|
1
|
|
|
|
|
8
|
return $del; |
461
|
|
|
|
|
|
|
} |
462
|
|
|
|
|
|
|
|
463
|
|
|
|
|
|
|
sub _cardconnect_scrubber_add_card { |
464
|
1
|
|
|
1
|
|
2
|
my ( $self, $cc ) = @_; |
465
|
1
|
50
|
|
|
|
4
|
return if ! $cc; |
466
|
1
|
|
|
|
|
2
|
my $scrubber = $self->{_scrubber}; |
467
|
1
|
|
|
|
|
42
|
scrubber_add_scrubber({quotemeta($cc)=>&{$scrubber}($cc)}); |
|
1
|
|
|
|
|
6
|
|
468
|
|
|
|
|
|
|
} |
469
|
|
|
|
|
|
|
|
470
|
|
|
|
|
|
|
sub _cardconnect_init { |
471
|
1
|
|
|
1
|
|
3
|
my ( $self, $opts ) = @_; |
472
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
# initialize/reset the reporting methods |
474
|
1
|
|
|
|
|
22
|
$self->is_success(0); |
475
|
1
|
|
|
|
|
10
|
$self->server_request(''); |
476
|
1
|
|
|
|
|
7
|
$self->server_response(''); |
477
|
1
|
|
|
|
|
19
|
$self->error_message(''); |
478
|
|
|
|
|
|
|
|
479
|
|
|
|
|
|
|
# some calls are passed via the content method, others are direct arguments... this way we cover both |
480
|
1
|
|
|
|
|
8
|
my %content = $self->content(); |
481
|
1
|
|
|
|
|
20
|
foreach my $ptr (\%content,$opts) { |
482
|
2
|
100
|
|
|
|
19
|
next if ! $ptr; |
483
|
|
|
|
|
|
|
scrubber_init({ |
484
|
|
|
|
|
|
|
quotemeta($ptr->{'password'}||'')=>'DELETED', |
485
|
|
|
|
|
|
|
quotemeta($ptr->{'ftp_password'}||'')=>'DELETED', |
486
|
1
|
50
|
50
|
|
|
22
|
($ptr->{'cvv2'} ? '(?<=[^\d])'.quotemeta($ptr->{'cvv2'}).'(?=[^\d])' : '')=>'DELETED', |
|
|
|
50
|
|
|
|
|
487
|
|
|
|
|
|
|
}); |
488
|
1
|
|
|
|
|
202
|
$self->_cardconnect_scrubber_add_card($ptr->{'card_number'}); |
489
|
|
|
|
|
|
|
} |
490
|
|
|
|
|
|
|
} |
491
|
|
|
|
|
|
|
|
492
|
|
|
|
|
|
|
1; |