line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Business::OnlinePayment::GlobalPayments; |
2
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
24099
|
use warnings; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
37
|
|
4
|
1
|
|
|
1
|
|
6
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
35
|
|
5
|
1
|
|
|
1
|
|
5
|
use Carp qw(croak); |
|
1
|
|
|
|
|
6
|
|
|
1
|
|
|
|
|
80
|
|
6
|
1
|
|
|
1
|
|
4
|
use vars qw($VERSION $DEBUG @ISA $me); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
113
|
|
7
|
1
|
|
|
1
|
|
6
|
use base 'Business::OnlinePayment::HTTPS'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
1065
|
|
8
|
1
|
|
|
1
|
|
58583
|
use XML::Simple 'XMLin'; # for parsing reply |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
$VERSION = 0.02; |
11
|
|
|
|
|
|
|
$DEBUG = 0; |
12
|
|
|
|
|
|
|
$me = __PACKAGE__; |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
my %trans_type = ( |
15
|
|
|
|
|
|
|
'normal authorization' => 'Sale', |
16
|
|
|
|
|
|
|
'authorization only' => 'Auth', |
17
|
|
|
|
|
|
|
'post authorization' => 'Force', |
18
|
|
|
|
|
|
|
'void' => 'Void', |
19
|
|
|
|
|
|
|
'credit' => 'Return', |
20
|
|
|
|
|
|
|
); |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
my %cc_fields = ( |
23
|
|
|
|
|
|
|
'GlobalUserName' => 'login', |
24
|
|
|
|
|
|
|
'GlobalPassword' => 'password', |
25
|
|
|
|
|
|
|
'TransType' => sub { my %c = @_; $trans_type{ lc($c{action}) } }, |
26
|
|
|
|
|
|
|
'CardNum' => 'card_number', |
27
|
|
|
|
|
|
|
'ExpDate' => sub { my %c = @_; join('', split /\D/,$c{'expiration'}) }, |
28
|
|
|
|
|
|
|
'MagData' => 'track2', |
29
|
|
|
|
|
|
|
'NameOnCard' => sub { my %c = @_; $c{'first_name'} . ' ' . $c{'last_name'} }, |
30
|
|
|
|
|
|
|
'Amount' => 'amount', |
31
|
|
|
|
|
|
|
'InvNum' => 'invoice_number', |
32
|
|
|
|
|
|
|
'Zip' => 'zip', |
33
|
|
|
|
|
|
|
'Street' => 'address', |
34
|
|
|
|
|
|
|
'CVNum' => 'cvv2', |
35
|
|
|
|
|
|
|
'PNRef' => 'order_number', |
36
|
|
|
|
|
|
|
'ExtData' => \&ext_data, |
37
|
|
|
|
|
|
|
); |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
sub ext_data { |
40
|
|
|
|
|
|
|
my %c = @_; # = $self->{_content} |
41
|
|
|
|
|
|
|
my $ext_data = ''; |
42
|
|
|
|
|
|
|
if($c{'authorization'}) { |
43
|
|
|
|
|
|
|
$ext_data .= ''.$c{'authorization'}.''; |
44
|
|
|
|
|
|
|
} |
45
|
|
|
|
|
|
|
if($c{'force_duplicate'}) { # set to any true value |
46
|
|
|
|
|
|
|
$ext_data .= 'T'; |
47
|
|
|
|
|
|
|
} |
48
|
|
|
|
|
|
|
return $ext_data; |
49
|
|
|
|
|
|
|
} |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
my %required_fields = ( |
52
|
|
|
|
|
|
|
'All' => [ qw(GlobalUserName GlobalPassword TransType) ], |
53
|
|
|
|
|
|
|
'Sale' => [ qw(CardNum ExpDate Amount) ], |
54
|
|
|
|
|
|
|
'Auth' => [ qw(CardNum ExpDate Amount) ], |
55
|
|
|
|
|
|
|
'Force' => [ ], |
56
|
|
|
|
|
|
|
'Void' => [ 'PNRef' ], |
57
|
|
|
|
|
|
|
'Return' => [ ], |
58
|
|
|
|
|
|
|
'Return.blind' => [ qw(CardNum ExpDate Amount) ], |
59
|
|
|
|
|
|
|
); |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
sub set_defaults { |
62
|
|
|
|
|
|
|
my $self = shift; |
63
|
|
|
|
|
|
|
$self->port(443); |
64
|
|
|
|
|
|
|
$self->path('/GlobalPay/transact.asmx/ProcessCreditCard'); |
65
|
|
|
|
|
|
|
$self->build_subs('domain', 'avs_code', 'cvv2_response' ); |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
return; |
68
|
|
|
|
|
|
|
} |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
sub remap_fields { |
71
|
|
|
|
|
|
|
my ($self, %map) = @_; |
72
|
|
|
|
|
|
|
my %content = $self->content(); |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
foreach (keys(%map)) { |
75
|
|
|
|
|
|
|
if(ref($map{$_}) eq 'CODE') { |
76
|
|
|
|
|
|
|
$content{$_} = $map{$_}->(%content); |
77
|
|
|
|
|
|
|
} |
78
|
|
|
|
|
|
|
else { |
79
|
|
|
|
|
|
|
$content{$_} = $content{$map{$_}} if defined( $content{$map{$_}} ); |
80
|
|
|
|
|
|
|
} |
81
|
|
|
|
|
|
|
} |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
if(lc($content{'action'}) eq 'post authorization') { |
84
|
|
|
|
|
|
|
# GlobalPayments uses this transaction type to complete an authorized |
85
|
|
|
|
|
|
|
# transaction, given either its PNRef (if it was authorized by an Auth |
86
|
|
|
|
|
|
|
# transaction to the gateway) or its AuthCode (if it was authorized by |
87
|
|
|
|
|
|
|
# telephone). |
88
|
|
|
|
|
|
|
if(!exists($content{'PNRef'}) and !exists($content{'authorization'})) { |
89
|
|
|
|
|
|
|
croak("missing required field(s): PNRef or AuthCode\n"); |
90
|
|
|
|
|
|
|
} |
91
|
|
|
|
|
|
|
} |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
$self->content(%content); |
94
|
|
|
|
|
|
|
return; |
95
|
|
|
|
|
|
|
} |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
sub submit { |
98
|
|
|
|
|
|
|
my $self = shift; |
99
|
|
|
|
|
|
|
my $content = $self->{_content}; |
100
|
|
|
|
|
|
|
$DB::single = 1 if $DEBUG; |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
$self->setup_test if $self->test_transaction(); |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
die "missing required option: domain\n" if !$self->domain(); |
105
|
|
|
|
|
|
|
$self->server($self->domain() . '.globalpay.com'); |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
$self->remap_fields(%cc_fields); |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
my $action = $content->{'TransType'} or |
110
|
|
|
|
|
|
|
croak "unknown action: '".$content->{'action'}."'\n"; |
111
|
|
|
|
|
|
|
$self->required_fields(@{ $required_fields{'All'} }); |
112
|
|
|
|
|
|
|
$self->required_fields(@{ $required_fields{$action} }); |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
if($action eq 'Return' and !exists($content->{'PNRef'})) { |
115
|
|
|
|
|
|
|
# This handles the case where a credit is ordered "blind", without |
116
|
|
|
|
|
|
|
# an order_number. Card information must be supplied. Allowing |
117
|
|
|
|
|
|
|
# these is somewhat risky, and can be disabled at the account level |
118
|
|
|
|
|
|
|
# by the "Require Original PNRef" flag. |
119
|
|
|
|
|
|
|
$self->required_fields(@{ $required_fields{'Return.blind'} }); |
120
|
|
|
|
|
|
|
} |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
tie my %request, 'Tie::IxHash', |
123
|
|
|
|
|
|
|
map { $_ => $self->{_content}->{$_} } keys(%cc_fields); |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
$Business::OnlinePayment::HTTPS::DEBUG = $DEBUG; |
126
|
|
|
|
|
|
|
$DB::single = 1 if $DEBUG; |
127
|
|
|
|
|
|
|
my ($page, $response, %headers) = $self->https_post(\%request); |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
$self->server_response($page); |
130
|
|
|
|
|
|
|
$self->is_success(0); |
131
|
|
|
|
|
|
|
if(not $response =~ /^200/) { |
132
|
|
|
|
|
|
|
$self->error_message("Connection failed: '$response'\n"); |
133
|
|
|
|
|
|
|
return; |
134
|
|
|
|
|
|
|
} |
135
|
|
|
|
|
|
|
my $data = XMLin($page); |
136
|
|
|
|
|
|
|
if(!$data or !exists($data->{'Result'})) { |
137
|
|
|
|
|
|
|
$self->error_message("Malformed server response: '$page'\n"); |
138
|
|
|
|
|
|
|
return; |
139
|
|
|
|
|
|
|
} |
140
|
|
|
|
|
|
|
$self->result_code($data->{'Result'}); |
141
|
|
|
|
|
|
|
$self->avs_code($data->{'GetAVSResult'}); |
142
|
|
|
|
|
|
|
$self->cvv2_response($data->{'GetCVResult'}); |
143
|
|
|
|
|
|
|
if($data->{'Result'} != 0) { |
144
|
|
|
|
|
|
|
$self->error_message($data->{'Message'}); |
145
|
|
|
|
|
|
|
return; |
146
|
|
|
|
|
|
|
} |
147
|
|
|
|
|
|
|
else { |
148
|
|
|
|
|
|
|
$self->is_success(1); |
149
|
|
|
|
|
|
|
$self->authorization($data->{'AuthCode'}); |
150
|
|
|
|
|
|
|
$self->order_number($data->{'PNRef'}); |
151
|
|
|
|
|
|
|
return; |
152
|
|
|
|
|
|
|
} |
153
|
|
|
|
|
|
|
} |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
sub setup_test { |
156
|
|
|
|
|
|
|
my $self = shift; |
157
|
|
|
|
|
|
|
$self->domain('certapia'); |
158
|
|
|
|
|
|
|
# For test card information, see Global Transport API documentation. |
159
|
|
|
|
|
|
|
} |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
=head1 NAME |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
Business::OnlinePayment::GlobalPayments - Global Transport backend for Business::OnlinePayment |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
=head1 SYNOPSIS |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
=head2 Initialization |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
my $trans = new Business::OnlinePayment('GlobalPayments', |
171
|
|
|
|
|
|
|
domain => 'mymerchant' # Your account rep will supply this |
172
|
|
|
|
|
|
|
); |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
=head2 Sale transaction |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
$trans->content( |
177
|
|
|
|
|
|
|
login => 'login', |
178
|
|
|
|
|
|
|
password => 'password', |
179
|
|
|
|
|
|
|
type => 'CC', |
180
|
|
|
|
|
|
|
card_number => '5500000000000004', |
181
|
|
|
|
|
|
|
expiration => '0211', |
182
|
|
|
|
|
|
|
cvv2 => '255', |
183
|
|
|
|
|
|
|
invoice_number => '123321', |
184
|
|
|
|
|
|
|
first_name => 'Joe', |
185
|
|
|
|
|
|
|
last_name => 'Schmoe', |
186
|
|
|
|
|
|
|
address => '123 Anystreet', |
187
|
|
|
|
|
|
|
city => 'Sacramento', |
188
|
|
|
|
|
|
|
state => 'CA', |
189
|
|
|
|
|
|
|
zip => '95824', |
190
|
|
|
|
|
|
|
action => 'normal authorization', |
191
|
|
|
|
|
|
|
amount => '24.99' |
192
|
|
|
|
|
|
|
); |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
=head2 Processing |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
$trans->submit; |
197
|
|
|
|
|
|
|
if($trans->is_approved) { |
198
|
|
|
|
|
|
|
print "Approved\n", |
199
|
|
|
|
|
|
|
"Authorization: ", $trans->authorization, "\n", |
200
|
|
|
|
|
|
|
"Order ID: ", $trans->order_number, "\n" |
201
|
|
|
|
|
|
|
} |
202
|
|
|
|
|
|
|
else { |
203
|
|
|
|
|
|
|
print "Failed: ".$trans->error_message; |
204
|
|
|
|
|
|
|
} |
205
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
=head2 Void transaction |
207
|
|
|
|
|
|
|
(or Return (credit) for full amount of original sale) |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
$trans->content( |
210
|
|
|
|
|
|
|
login => 'login', |
211
|
|
|
|
|
|
|
password => 'password', |
212
|
|
|
|
|
|
|
action => 'void', # or 'credit' for a Return |
213
|
|
|
|
|
|
|
order_number => '1001245', |
214
|
|
|
|
|
|
|
); |
215
|
|
|
|
|
|
|
$trans->submit; |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
=head1 NOTES |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
The following transaction types are supported: |
220
|
|
|
|
|
|
|
Normal Authorization |
221
|
|
|
|
|
|
|
Authorization Only |
222
|
|
|
|
|
|
|
Post Authorization |
223
|
|
|
|
|
|
|
Credit |
224
|
|
|
|
|
|
|
Void |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
For Post Authorization, Credit, and Void, I should be set to |
227
|
|
|
|
|
|
|
the order_number of the previous transaction. |
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
Alternately, Post Authorization can be sent with I set to an |
230
|
|
|
|
|
|
|
auth code obtained by telephone. Similarly, Credit can be sent with credit |
231
|
|
|
|
|
|
|
account information instead of an I. |
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
By default, Global Transport will reject duplicate transactions (identical |
234
|
|
|
|
|
|
|
card number, expiration date, and amount) sent on the same day. This can be |
235
|
|
|
|
|
|
|
overridden by setting I => 1. |
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
=head1 AUTHOR |
238
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
Mark Wells |
240
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
=head1 SUPPORT |
242
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
Support for commercial users is available from Freeside Internet Services, |
244
|
|
|
|
|
|
|
Inc. |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
=head1 COPYRIGHT & LICENSE |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
Copyright 2009 Mark Wells, all rights reserved. |
249
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify it |
251
|
|
|
|
|
|
|
under the same terms as Perl itself. |
252
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
=cut |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
1; # End of Business::OnlinePayment::GlobalPayments |