| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Business::OnlinePayment::WorldPay; |
|
2
|
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
39424
|
use 5.008008; |
|
|
1
|
|
|
|
|
4
|
|
|
|
1
|
|
|
|
|
54
|
|
|
4
|
1
|
|
|
1
|
|
6
|
use strict; |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
33
|
|
|
5
|
1
|
|
|
1
|
|
6
|
use warnings; |
|
|
1
|
|
|
|
|
14
|
|
|
|
1
|
|
|
|
|
27
|
|
|
6
|
1
|
|
|
1
|
|
6
|
use Carp; |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
128
|
|
|
7
|
|
|
|
|
|
|
|
|
8
|
1
|
|
|
1
|
|
854
|
use Template; # construct XML requests |
|
|
1
|
|
|
|
|
1055105
|
|
|
|
1
|
|
|
|
|
33
|
|
|
9
|
1
|
|
|
1
|
|
509
|
use XML::TreeBuilder; # parse XML responses |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
use Net::SSLeay qw(post_https make_headers); # submit requests |
|
12
|
|
|
|
|
|
|
use MIME::Base64; # basic authentication |
|
13
|
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
use base qw(Exporter Business::OnlinePayment); # Exporter just for $VERSION checking |
|
15
|
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
our $VERSION = '1.00'; |
|
18
|
|
|
|
|
|
|
our $DEBUG_FH = \*STDERR; # debugging output destination |
|
19
|
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
# From the "Submitting Transactions in the Direct Model" document |
|
22
|
|
|
|
|
|
|
# and the WorldPay DTD: http://dtd.worldpay.com/paymentService_v1.dtd |
|
23
|
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
my %Payment_Method = ( |
|
25
|
|
|
|
|
|
|
'visa' => { |
|
26
|
|
|
|
|
|
|
paymentType => 'VISA-SSL', |
|
27
|
|
|
|
|
|
|
_required => [ qw/card_number exp_date name/ ], |
|
28
|
|
|
|
|
|
|
}, |
|
29
|
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
'amex' => { |
|
31
|
|
|
|
|
|
|
paymentType => 'AMEX-SSL', |
|
32
|
|
|
|
|
|
|
_required => [ qw/card_number exp_date name/ ], |
|
33
|
|
|
|
|
|
|
}, |
|
34
|
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
'mastercard' => { |
|
36
|
|
|
|
|
|
|
paymentType => 'ECMC-SSL', |
|
37
|
|
|
|
|
|
|
_required => [ qw/card_number exp_date name/ ], |
|
38
|
|
|
|
|
|
|
}, |
|
39
|
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
'diners card' => { |
|
41
|
|
|
|
|
|
|
paymentType => 'DINERS-SSL', |
|
42
|
|
|
|
|
|
|
_required => [ qw/card_number exp_date name/ ], |
|
43
|
|
|
|
|
|
|
}, |
|
44
|
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
'solo card' => { |
|
46
|
|
|
|
|
|
|
paymentType => 'SOLO_GB-SSL', |
|
47
|
|
|
|
|
|
|
_required => [ qw/card_number exp_date name/ ], |
|
48
|
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
# from DTD: (issueNumber | (startDate, issueNumber?) ) |
|
50
|
|
|
|
|
|
|
# either issue number or start date must be included |
|
51
|
|
|
|
|
|
|
# have to handle as a special case in sub submit |
|
52
|
|
|
|
|
|
|
}, |
|
53
|
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
'maestro' => { |
|
55
|
|
|
|
|
|
|
paymentType => 'MAESTRO-SSL', |
|
56
|
|
|
|
|
|
|
_required => [ qw/card_number exp_date name/ ], |
|
57
|
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
# from DTD: startDate?, issueNumber? |
|
59
|
|
|
|
|
|
|
# issue number and start date both optional (?) |
|
60
|
|
|
|
|
|
|
}, |
|
61
|
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
'elv' => { |
|
63
|
|
|
|
|
|
|
paymentType => 'ELV-SSL', |
|
64
|
|
|
|
|
|
|
_required => [ qw/account_holder_name bank_account_number bank_name |
|
65
|
|
|
|
|
|
|
bank_location bank_location_id/ ], |
|
66
|
|
|
|
|
|
|
}, |
|
67
|
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
'jcb card' => { |
|
69
|
|
|
|
|
|
|
paymentType => 'JCB-SSL', |
|
70
|
|
|
|
|
|
|
_required => [ qw/card_number exp_date name/ ], |
|
71
|
|
|
|
|
|
|
}, |
|
72
|
|
|
|
|
|
|
); |
|
73
|
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
$Payment_Method{diners} = $Payment_Method{'diners card'}; |
|
75
|
|
|
|
|
|
|
$Payment_Method{jcb} = $Payment_Method{'jcb card'}; |
|
76
|
|
|
|
|
|
|
$Payment_Method{solo} = $Payment_Method{'solo card'}; |
|
77
|
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
my %Server = ( |
|
80
|
|
|
|
|
|
|
live => { |
|
81
|
|
|
|
|
|
|
server => 'secure.ims.worldpay.com', |
|
82
|
|
|
|
|
|
|
path => '/jsp/merchant/xml/paymentService.jsp', |
|
83
|
|
|
|
|
|
|
port => 443, |
|
84
|
|
|
|
|
|
|
}, |
|
85
|
|
|
|
|
|
|
test => { |
|
86
|
|
|
|
|
|
|
server => 'secure-test.wp3.rbsworldpay.com', |
|
87
|
|
|
|
|
|
|
path => '/jsp/merchant/xml/paymentService.jsp', |
|
88
|
|
|
|
|
|
|
port => 443, |
|
89
|
|
|
|
|
|
|
}, |
|
90
|
|
|
|
|
|
|
); |
|
91
|
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
sub debug |
|
94
|
|
|
|
|
|
|
{ |
|
95
|
|
|
|
|
|
|
my ($self, $value, $filename) = @_; |
|
96
|
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
$self->{debug} = $value if defined $value; |
|
98
|
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
if ( $filename ) { |
|
100
|
|
|
|
|
|
|
if (open(my $fh, ">>", $filename)) { |
|
101
|
|
|
|
|
|
|
my $old_fh = select($fh); |
|
102
|
|
|
|
|
|
|
$| = 1; # output autoflush |
|
103
|
|
|
|
|
|
|
select($old_fh); |
|
104
|
|
|
|
|
|
|
$DEBUG_FH = $fh; |
|
105
|
|
|
|
|
|
|
} |
|
106
|
|
|
|
|
|
|
else { |
|
107
|
|
|
|
|
|
|
carp "cannot open debugging file $filename: $!\n"; |
|
108
|
|
|
|
|
|
|
} |
|
109
|
|
|
|
|
|
|
} |
|
110
|
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
return $self->{debug}; |
|
112
|
|
|
|
|
|
|
} |
|
113
|
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
sub set_server |
|
116
|
|
|
|
|
|
|
{ |
|
117
|
|
|
|
|
|
|
my ($self, $server_type) = @_; |
|
118
|
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
$self->{'_server'} = $server_type; # 'live' or 'test' |
|
120
|
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
$self->server( $Server{$server_type}->{'server'} ); |
|
122
|
|
|
|
|
|
|
$self->path( $Server{$server_type}->{'path'} ); |
|
123
|
|
|
|
|
|
|
$self->port( $Server{$server_type}->{'port'} ); |
|
124
|
|
|
|
|
|
|
} |
|
125
|
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
sub set_defaults # called by B::OP constructor |
|
128
|
|
|
|
|
|
|
{ |
|
129
|
|
|
|
|
|
|
my $self = shift; |
|
130
|
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
$self->{'_content'} = {}; |
|
132
|
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
_launder_envariables(); # clean up WORLDPAY_* variables |
|
134
|
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
# B::OP creates the following accessors: |
|
136
|
|
|
|
|
|
|
# server, port, path, test_transaction, transaction_type, |
|
137
|
|
|
|
|
|
|
# server_response, is_success, authorization, |
|
138
|
|
|
|
|
|
|
# result_code, error_message, |
|
139
|
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
# let's create some more: |
|
141
|
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
$self->build_subs( |
|
143
|
|
|
|
|
|
|
qw/ |
|
144
|
|
|
|
|
|
|
installation login password version currency action |
|
145
|
|
|
|
|
|
|
cvv_response avs_response risk_score last_event |
|
146
|
|
|
|
|
|
|
/ |
|
147
|
|
|
|
|
|
|
); |
|
148
|
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
$self->test_transaction(0); # default to live server |
|
150
|
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
$self->version('1.4'); # paymentService "version" attribute |
|
152
|
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
$self->action('payment'); # default action is payment |
|
154
|
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
$self->currency('EUR'); # default value for currencyCode |
|
156
|
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
if ($ENV{WORLDPAY_DEBUG}) { |
|
158
|
|
|
|
|
|
|
if ($ENV{WORLDPAY_DEBUG} =~ /^\d+$/) { |
|
159
|
|
|
|
|
|
|
$self->debug( 1 ); |
|
160
|
|
|
|
|
|
|
} |
|
161
|
|
|
|
|
|
|
else { |
|
162
|
|
|
|
|
|
|
$self->debug( 1, $ENV{WORLDPAY_DEBUG} ); |
|
163
|
|
|
|
|
|
|
} |
|
164
|
|
|
|
|
|
|
} |
|
165
|
|
|
|
|
|
|
} |
|
166
|
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
sub submit |
|
169
|
|
|
|
|
|
|
{ |
|
170
|
|
|
|
|
|
|
my $self = shift; |
|
171
|
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
my %content = $self->content; |
|
173
|
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
if ($content{action}) { |
|
175
|
|
|
|
|
|
|
$content{action} = 'payment' |
|
176
|
|
|
|
|
|
|
if $content{action} =~ /^ \s* normal \s+ authori[zs]ation \s* $/ix; |
|
177
|
|
|
|
|
|
|
$self->action( lc $content{action} ); |
|
178
|
|
|
|
|
|
|
} |
|
179
|
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
################ initialize object ################ |
|
181
|
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
# standard B::OP attributes |
|
183
|
|
|
|
|
|
|
$self->$_( '' ) for qw/is_success |
|
184
|
|
|
|
|
|
|
authorization |
|
185
|
|
|
|
|
|
|
result_code |
|
186
|
|
|
|
|
|
|
error_message |
|
187
|
|
|
|
|
|
|
server |
|
188
|
|
|
|
|
|
|
port |
|
189
|
|
|
|
|
|
|
path |
|
190
|
|
|
|
|
|
|
server_response/; |
|
191
|
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
$self->transaction_type( '' ) unless $self->transaction_type; |
|
193
|
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
# additional B::OP::WorldPay attributes |
|
195
|
|
|
|
|
|
|
$self->$_( '' ) for qw/last_event |
|
196
|
|
|
|
|
|
|
cvv_response |
|
197
|
|
|
|
|
|
|
avs_response |
|
198
|
|
|
|
|
|
|
risk_score/; |
|
199
|
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
$self->set_server( $self->test_transaction ? 'test' : 'live' ); |
|
201
|
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
$self->installation( $content{installation} || $ENV{WORLDPAY_INSTALLATION_ID} ); |
|
203
|
|
|
|
|
|
|
$self->login( $content{login} || $ENV{WORLDPAY_MERCHANT_CODE} ); |
|
204
|
|
|
|
|
|
|
$self->password( $content{password} || $ENV{WORLDPAY_XML_PASSWORD} ); |
|
205
|
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
if ($self->debug) { |
|
207
|
|
|
|
|
|
|
print $DEBUG_FH "\n"; |
|
208
|
|
|
|
|
|
|
print $DEBUG_FH '*' x 80, "\n\n"; |
|
209
|
|
|
|
|
|
|
print $DEBUG_FH "installation: ", $self->installation, "\n"; |
|
210
|
|
|
|
|
|
|
print $DEBUG_FH "login: ", $self->login, "\n"; |
|
211
|
|
|
|
|
|
|
print $DEBUG_FH "password: ", $self->password, "\n"; |
|
212
|
|
|
|
|
|
|
} |
|
213
|
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
##### get template & variables for xml request #### |
|
215
|
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
my ($xml_template, $template_vars); |
|
217
|
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
my $action = $self->action; |
|
219
|
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
if ($action eq 'status' || $action eq 'cancel' || $action eq 'cancel_or_refund') { |
|
221
|
|
|
|
|
|
|
($xml_template, $template_vars) = $self->status_inquiry_or_cancel; |
|
222
|
|
|
|
|
|
|
} |
|
223
|
|
|
|
|
|
|
elsif ( $action eq 'refund' ) { |
|
224
|
|
|
|
|
|
|
($xml_template, $template_vars) = $self->refund; |
|
225
|
|
|
|
|
|
|
} |
|
226
|
|
|
|
|
|
|
elsif ($Payment_Method{lc $content{type}}{paymentType} eq 'ELV-SSL') { |
|
227
|
|
|
|
|
|
|
($xml_template, $template_vars) = $self->elv_payment; |
|
228
|
|
|
|
|
|
|
} |
|
229
|
|
|
|
|
|
|
else { |
|
230
|
|
|
|
|
|
|
($xml_template, $template_vars) = $self->payment; |
|
231
|
|
|
|
|
|
|
} |
|
232
|
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
############### create XML request ################ |
|
234
|
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
my $post_data_xml; |
|
236
|
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
my $tt = Template->new(); |
|
238
|
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
$tt->process( \$xml_template, $template_vars, \$post_data_xml ) || |
|
240
|
|
|
|
|
|
|
croak $tt->error(); |
|
241
|
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
if ($self->debug) { |
|
243
|
|
|
|
|
|
|
print $DEBUG_FH "\n", "=" x 80, "\n"; |
|
244
|
|
|
|
|
|
|
print $DEBUG_FH " " x 35, "REQUEST:\n"; |
|
245
|
|
|
|
|
|
|
print $DEBUG_FH "=" x 80, "\n\n"; |
|
246
|
|
|
|
|
|
|
print $DEBUG_FH "post_data_xml = \n$post_data_xml\n"; |
|
247
|
|
|
|
|
|
|
print $DEBUG_FH "POST_HTTPS: ", $self->server . $self->path, "\n"; |
|
248
|
|
|
|
|
|
|
} |
|
249
|
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
################# submit request ################## |
|
251
|
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
my ($page, $response, %headers) = post_https( |
|
253
|
|
|
|
|
|
|
$self->server, |
|
254
|
|
|
|
|
|
|
$self->port, |
|
255
|
|
|
|
|
|
|
$self->path, |
|
256
|
|
|
|
|
|
|
make_headers( |
|
257
|
|
|
|
|
|
|
Authorization => 'Basic ' . MIME::Base64::encode( |
|
258
|
|
|
|
|
|
|
$self->login . ":" . $self->password, '' |
|
259
|
|
|
|
|
|
|
) |
|
260
|
|
|
|
|
|
|
), |
|
261
|
|
|
|
|
|
|
$post_data_xml, |
|
262
|
|
|
|
|
|
|
); |
|
263
|
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
################ examine response ################# |
|
265
|
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
if ($self->debug) { |
|
267
|
|
|
|
|
|
|
my $prettier_xml; |
|
268
|
|
|
|
|
|
|
($prettier_xml = $page) =~ s/>>\n
|
|
269
|
|
|
|
|
|
|
print $DEBUG_FH "\n", "=" x 80, "\n"; |
|
270
|
|
|
|
|
|
|
print $DEBUG_FH " " x 35, "RESPONSE:\n"; |
|
271
|
|
|
|
|
|
|
print $DEBUG_FH "=" x 80, "\n\n"; |
|
272
|
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
print $DEBUG_FH "$response\n\n"; |
|
274
|
|
|
|
|
|
|
print $DEBUG_FH "HEADERS:\n"; |
|
275
|
|
|
|
|
|
|
print $DEBUG_FH " $_: $headers{$_}\n" for sort keys %headers; |
|
276
|
|
|
|
|
|
|
print $DEBUG_FH "\nXML:\n$prettier_xml\n"; |
|
277
|
|
|
|
|
|
|
print $DEBUG_FH "-" x 80, "\n\n"; |
|
278
|
|
|
|
|
|
|
} |
|
279
|
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
$self->server_response( $response ); |
|
281
|
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
if ($self->server_response =~ /\b401 Authorization Required/) { |
|
283
|
|
|
|
|
|
|
$self->is_success(0); |
|
284
|
|
|
|
|
|
|
$self->error_message('Authorization Required'); |
|
285
|
|
|
|
|
|
|
return; |
|
286
|
|
|
|
|
|
|
} |
|
287
|
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
unless ($page && $self->server_response =~ /\b200 OK/) { |
|
289
|
|
|
|
|
|
|
$self->is_success(0); |
|
290
|
|
|
|
|
|
|
$self->error_message( |
|
291
|
|
|
|
|
|
|
'There was a problem communicating with the payment server. ' . |
|
292
|
|
|
|
|
|
|
'Please try again.' |
|
293
|
|
|
|
|
|
|
); |
|
294
|
|
|
|
|
|
|
return; |
|
295
|
|
|
|
|
|
|
} |
|
296
|
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
############### parse XML response ################ |
|
298
|
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
my $xml = XML::TreeBuilder->new; |
|
300
|
|
|
|
|
|
|
$xml->parse($page); |
|
301
|
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
if ($self->debug) { |
|
303
|
|
|
|
|
|
|
print $DEBUG_FH "XML::TreeBuilder dump:\n"; |
|
304
|
|
|
|
|
|
|
$xml->dump($DEBUG_FH); |
|
305
|
|
|
|
|
|
|
print $DEBUG_FH "\n"; |
|
306
|
|
|
|
|
|
|
} |
|
307
|
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
# structure of the XML response varies |
|
309
|
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
my ( |
|
311
|
|
|
|
|
|
|
$error, $last_event, $return_code, $cvc_result, $avs_response, $risk, $ok, |
|
312
|
|
|
|
|
|
|
); |
|
313
|
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
# response for various errors: |
|
315
|
|
|
|
|
|
|
if ($error = $xml->find('error')) { |
|
316
|
|
|
|
|
|
|
$self->is_success(0); |
|
317
|
|
|
|
|
|
|
$self->authorization( 'ERROR' ); |
|
318
|
|
|
|
|
|
|
$self->error_message( $error->as_text ); |
|
319
|
|
|
|
|
|
|
} |
|
320
|
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
# response for payment requests or status inquiries: |
|
322
|
|
|
|
|
|
|
elsif ($last_event = $xml->find('lastEvent')) { # response for payments |
|
323
|
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
$self->last_event( $last_event->as_text ); |
|
325
|
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
if ($last_event->as_text eq 'AUTHORISED') { # UK spelling |
|
327
|
|
|
|
|
|
|
$self->is_success(1); |
|
328
|
|
|
|
|
|
|
$self->authorization( 'AUTHORIZED' ); # switching to US spelling |
|
329
|
|
|
|
|
|
|
$self->last_event( 'AUTHORIZED' ); |
|
330
|
|
|
|
|
|
|
} |
|
331
|
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
elsif ($last_event->as_text eq 'REFUSED') { |
|
333
|
|
|
|
|
|
|
if ($return_code = $xml->find('ISO8583ReturnCode')) { |
|
334
|
|
|
|
|
|
|
$self->is_success(0); |
|
335
|
|
|
|
|
|
|
$self->authorization( 'REFUSED' ); |
|
336
|
|
|
|
|
|
|
$self->result_code( $return_code->attr('code') ); |
|
337
|
|
|
|
|
|
|
$self->error_message( $return_code->attr('description') ); |
|
338
|
|
|
|
|
|
|
} |
|
339
|
|
|
|
|
|
|
} |
|
340
|
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
# if needed, can provide special handling for other last_event values: |
|
342
|
|
|
|
|
|
|
# CANCELLED, CAPTURED, SETTLED, SETTLED_BY_MERCHANT, |
|
343
|
|
|
|
|
|
|
# SENT_FOR_REFUND, REFUNDED_BY_MERCHANT, CHARGED_BACK |
|
344
|
|
|
|
|
|
|
# |
|
345
|
|
|
|
|
|
|
# elsif ($last_event->as_text eq 'XXX') { |
|
346
|
|
|
|
|
|
|
# } |
|
347
|
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
else { |
|
349
|
|
|
|
|
|
|
$self->is_success(1); |
|
350
|
|
|
|
|
|
|
} |
|
351
|
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
# get CVCResultCode description if present |
|
353
|
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
if ($cvc_result = $xml->find('CVCResultCode')) { |
|
355
|
|
|
|
|
|
|
$self->cvv_response( $cvc_result->attr('description') ); |
|
356
|
|
|
|
|
|
|
} |
|
357
|
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
# get AVSResultCode description if present |
|
359
|
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
if ($avs_response = $xml->find('AVSResultCode')) { |
|
361
|
|
|
|
|
|
|
$self->avs_response( $avs_response->attr('description') ); |
|
362
|
|
|
|
|
|
|
} |
|
363
|
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
# get riskScore if present |
|
365
|
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
if ($risk = $xml->find('riskScore')) { |
|
367
|
|
|
|
|
|
|
$self->risk_score( $risk->attr('value') ); |
|
368
|
|
|
|
|
|
|
} |
|
369
|
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
} |
|
371
|
|
|
|
|
|
|
|
|
372
|
|
|
|
|
|
|
# response for cancel request: |
|
373
|
|
|
|
|
|
|
elsif ( $ok = $xml->find('ok') and $ok->find('cancelReceived') ) { |
|
374
|
|
|
|
|
|
|
$self->is_success(1); |
|
375
|
|
|
|
|
|
|
$self->authorization( 'AUTHORIZED' ); |
|
376
|
|
|
|
|
|
|
} |
|
377
|
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
# response for refund request: |
|
379
|
|
|
|
|
|
|
elsif ( $ok = $xml->find('ok') and $ok->find('refundReceived') ) { |
|
380
|
|
|
|
|
|
|
$self->is_success(1); |
|
381
|
|
|
|
|
|
|
$self->authorization( 'AUTHORIZED' ); |
|
382
|
|
|
|
|
|
|
} |
|
383
|
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
# response for UNDOCUMENTED cancelOrRefund request: |
|
385
|
|
|
|
|
|
|
elsif ( $ok = $xml->find('ok') and $ok->find('voidReceived') ) { |
|
386
|
|
|
|
|
|
|
$self->is_success(1); |
|
387
|
|
|
|
|
|
|
$self->authorization( 'AUTHORIZED' ); |
|
388
|
|
|
|
|
|
|
} |
|
389
|
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
$xml->delete; |
|
391
|
|
|
|
|
|
|
|
|
392
|
|
|
|
|
|
|
if ($self->debug) { |
|
393
|
|
|
|
|
|
|
print $DEBUG_FH "\n", "=" x 80, "\n"; |
|
394
|
|
|
|
|
|
|
print $DEBUG_FH " " x 35, "RESULTS:\n"; |
|
395
|
|
|
|
|
|
|
print $DEBUG_FH "=" x 80, "\n\n"; |
|
396
|
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
print $DEBUG_FH "Standard B::OP attributes:\n"; |
|
398
|
|
|
|
|
|
|
|
|
399
|
|
|
|
|
|
|
print $DEBUG_FH "test_transaction = ", $self->test_transaction, "\n"; |
|
400
|
|
|
|
|
|
|
print $DEBUG_FH "transaction_type = ", $self->transaction_type, "\n"; |
|
401
|
|
|
|
|
|
|
print $DEBUG_FH "is_success = ", $self->is_success, "\n"; |
|
402
|
|
|
|
|
|
|
print $DEBUG_FH "authorization = ", $self->authorization, "\n"; |
|
403
|
|
|
|
|
|
|
print $DEBUG_FH "result_code = ", $self->result_code, "\n"; |
|
404
|
|
|
|
|
|
|
print $DEBUG_FH "error_message = ", $self->error_message, "\n"; |
|
405
|
|
|
|
|
|
|
print $DEBUG_FH "server = ", $self->server, "\n"; |
|
406
|
|
|
|
|
|
|
print $DEBUG_FH "port = ", $self->port, "\n"; |
|
407
|
|
|
|
|
|
|
print $DEBUG_FH "path = ", $self->path, "\n"; |
|
408
|
|
|
|
|
|
|
print $DEBUG_FH "server_response = ", $self->server_response, "\n\n"; |
|
409
|
|
|
|
|
|
|
|
|
410
|
|
|
|
|
|
|
print $DEBUG_FH "Additional B::OP::WorldPay attributes:\n"; |
|
411
|
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
print $DEBUG_FH "cvv_response = ", $self->cvv_response, "\n"; |
|
413
|
|
|
|
|
|
|
print $DEBUG_FH "avs_response = ", $self->avs_response, "\n"; |
|
414
|
|
|
|
|
|
|
print $DEBUG_FH "risk_score = ", $self->risk_score, "\n\n"; |
|
415
|
|
|
|
|
|
|
print $DEBUG_FH "last_event = ", $self->last_event, "\n\n"; |
|
416
|
|
|
|
|
|
|
|
|
417
|
|
|
|
|
|
|
print $DEBUG_FH "-" x 80, "\n\n"; |
|
418
|
|
|
|
|
|
|
} |
|
419
|
|
|
|
|
|
|
} |
|
420
|
|
|
|
|
|
|
|
|
421
|
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
sub payment |
|
423
|
|
|
|
|
|
|
{ |
|
424
|
|
|
|
|
|
|
my $self = shift; |
|
425
|
|
|
|
|
|
|
|
|
426
|
|
|
|
|
|
|
my %content = $self->content; |
|
427
|
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
$self->required_fields( 'type' ); |
|
429
|
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
croak("unrecognized credit card type: $content{type}\n") unless $Payment_Method{lc $content{type}}; |
|
431
|
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
if ($content{type} =~ /^solo/i) { |
|
433
|
|
|
|
|
|
|
croak("missing required field issue_number or start_date") |
|
434
|
|
|
|
|
|
|
unless exists $content{issue_number} || exists $content{start_date}; |
|
435
|
|
|
|
|
|
|
} |
|
436
|
|
|
|
|
|
|
|
|
437
|
|
|
|
|
|
|
my @required_fields = @{ $Payment_Method{ lc $content{type} }{_required} }; |
|
438
|
|
|
|
|
|
|
|
|
439
|
|
|
|
|
|
|
$self->required_fields( @required_fields ); |
|
440
|
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
########### initialize template variables ######### |
|
442
|
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
$self->currency( $content{currency} ) if $content{currency}; |
|
444
|
|
|
|
|
|
|
my $currency = $self->currency; |
|
445
|
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
my $amount_value = $content{amount}; |
|
447
|
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
# amount must be specified with an "exponent" |
|
449
|
|
|
|
|
|
|
# Example: $123.45 would be specified as value = "12345", exponent = "2" |
|
450
|
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
my $amount_exponent = 2; # works for all but Indonesian Rupiah |
|
452
|
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
if ($amount_value) { |
|
454
|
|
|
|
|
|
|
$amount_value =~ s/,(?=\d\d$)/./g; # 123,45 => 123.45 |
|
455
|
|
|
|
|
|
|
$amount_value =~ s/,//g; # 12,345.46 => 12345.46 |
|
456
|
|
|
|
|
|
|
$amount_value = sprintf "%.2f", $amount_value; # 123 => 123.00 |
|
457
|
|
|
|
|
|
|
$amount_value =~ s/\.//g; # 123.00 => 12300 |
|
458
|
|
|
|
|
|
|
} |
|
459
|
|
|
|
|
|
|
|
|
460
|
|
|
|
|
|
|
# strip non-digits from card number |
|
461
|
|
|
|
|
|
|
my $card_number = ''; |
|
462
|
|
|
|
|
|
|
if ( $content{card_number} ) { |
|
463
|
|
|
|
|
|
|
( $card_number = $content{card_number} ) =~ s/\D//g; |
|
464
|
|
|
|
|
|
|
} |
|
465
|
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
# separate month and year values for expiryDate |
|
467
|
|
|
|
|
|
|
|
|
468
|
|
|
|
|
|
|
my ($exp_month, $exp_year); |
|
469
|
|
|
|
|
|
|
if ( $content{exp_date} ) { |
|
470
|
|
|
|
|
|
|
($exp_month, $exp_year) = split /\//, $content{exp_date}; |
|
471
|
|
|
|
|
|
|
$exp_month = sprintf "%02d", $exp_month; |
|
472
|
|
|
|
|
|
|
$exp_year = $exp_year < 100 ? 2000 + $exp_year : $exp_year; # Y3K problem |
|
473
|
|
|
|
|
|
|
} |
|
474
|
|
|
|
|
|
|
|
|
475
|
|
|
|
|
|
|
# Solo & Maestro cards may have startDate |
|
476
|
|
|
|
|
|
|
# separate month and year values for startDate |
|
477
|
|
|
|
|
|
|
|
|
478
|
|
|
|
|
|
|
my ($start_month, $start_year); |
|
479
|
|
|
|
|
|
|
if ($content{start_date}) { |
|
480
|
|
|
|
|
|
|
($start_month, $start_year) = split /\//, $content{start_date}; |
|
481
|
|
|
|
|
|
|
$start_month = sprintf "%02d", $start_month; |
|
482
|
|
|
|
|
|
|
$start_year = $start_year < 100 ? 2000 + $start_year : $start_year; # Y3K problem |
|
483
|
|
|
|
|
|
|
} |
|
484
|
|
|
|
|
|
|
|
|
485
|
|
|
|
|
|
|
# get first and last names from cardHolderName |
|
486
|
|
|
|
|
|
|
|
|
487
|
|
|
|
|
|
|
my ($first_name, $last_name); |
|
488
|
|
|
|
|
|
|
if ($content{name}) { |
|
489
|
|
|
|
|
|
|
my @names = split ' ', $content{name}; |
|
490
|
|
|
|
|
|
|
$last_name = pop @names; |
|
491
|
|
|
|
|
|
|
$first_name = join ' ' => @names; |
|
492
|
|
|
|
|
|
|
} |
|
493
|
|
|
|
|
|
|
|
|
494
|
|
|
|
|
|
|
# WorldPay suggests putting the whole address in the street field |
|
495
|
|
|
|
|
|
|
# with the exception of the postal and country codes |
|
496
|
|
|
|
|
|
|
|
|
497
|
|
|
|
|
|
|
my @address; |
|
498
|
|
|
|
|
|
|
push @address, $content{address} if $content{address}; |
|
499
|
|
|
|
|
|
|
push @address, $content{city} if $content{city}; |
|
500
|
|
|
|
|
|
|
push @address, $content{state} if $content{state}; |
|
501
|
|
|
|
|
|
|
my $street = join ', ' => @address; |
|
502
|
|
|
|
|
|
|
|
|
503
|
|
|
|
|
|
|
########### get XML template for request ########## |
|
504
|
|
|
|
|
|
|
|
|
505
|
|
|
|
|
|
|
my $xml_template = _get_xml_template( $self->action ); |
|
506
|
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
########### initialize data for template ########## |
|
508
|
|
|
|
|
|
|
|
|
509
|
|
|
|
|
|
|
my $template_vars = { |
|
510
|
|
|
|
|
|
|
|
|
511
|
|
|
|
|
|
|
merchantCode => $self->login, |
|
512
|
|
|
|
|
|
|
|
|
513
|
|
|
|
|
|
|
version => $self->version, |
|
514
|
|
|
|
|
|
|
|
|
515
|
|
|
|
|
|
|
orderCode => $content{order_number}, |
|
516
|
|
|
|
|
|
|
|
|
517
|
|
|
|
|
|
|
installationId => $self->installation, |
|
518
|
|
|
|
|
|
|
|
|
519
|
|
|
|
|
|
|
description => $content{description}, |
|
520
|
|
|
|
|
|
|
|
|
521
|
|
|
|
|
|
|
amount => { |
|
522
|
|
|
|
|
|
|
value => $amount_value, |
|
523
|
|
|
|
|
|
|
currencyCode => $self->currency, |
|
524
|
|
|
|
|
|
|
exponent => $amount_exponent, |
|
525
|
|
|
|
|
|
|
}, |
|
526
|
|
|
|
|
|
|
|
|
527
|
|
|
|
|
|
|
paymentDetails => { |
|
528
|
|
|
|
|
|
|
paymentType => $Payment_Method{lc $content{type}}{paymentType}, |
|
529
|
|
|
|
|
|
|
action => 'AUTHORISE', |
|
530
|
|
|
|
|
|
|
cardNumber => $card_number, |
|
531
|
|
|
|
|
|
|
expiryDate => { |
|
532
|
|
|
|
|
|
|
month => $exp_month, |
|
533
|
|
|
|
|
|
|
year => $exp_year, |
|
534
|
|
|
|
|
|
|
}, |
|
535
|
|
|
|
|
|
|
cardHolderName => $content{name}, |
|
536
|
|
|
|
|
|
|
issueNumber => $content{issue_number}, |
|
537
|
|
|
|
|
|
|
startDate => { |
|
538
|
|
|
|
|
|
|
month => $start_month, |
|
539
|
|
|
|
|
|
|
year => $start_year, |
|
540
|
|
|
|
|
|
|
}, |
|
541
|
|
|
|
|
|
|
cvc => $content{cvc}, |
|
542
|
|
|
|
|
|
|
cardAddress => { |
|
543
|
|
|
|
|
|
|
firstName => $content{first_name}, |
|
544
|
|
|
|
|
|
|
lastName => $content{last_name}, |
|
545
|
|
|
|
|
|
|
street => $street, |
|
546
|
|
|
|
|
|
|
postalCode => $content{zip}, |
|
547
|
|
|
|
|
|
|
countryCode => $content{country}, |
|
548
|
|
|
|
|
|
|
telephoneNumber => $content{phone}, |
|
549
|
|
|
|
|
|
|
}, |
|
550
|
|
|
|
|
|
|
}, |
|
551
|
|
|
|
|
|
|
|
|
552
|
|
|
|
|
|
|
# required for 3D secure authentication |
|
553
|
|
|
|
|
|
|
session => { |
|
554
|
|
|
|
|
|
|
shopperIPAddress => $content{ip_address}, |
|
555
|
|
|
|
|
|
|
id => $content{session_id}, |
|
556
|
|
|
|
|
|
|
}, |
|
557
|
|
|
|
|
|
|
|
|
558
|
|
|
|
|
|
|
# required for 3D secure authentication |
|
559
|
|
|
|
|
|
|
shopper => { |
|
560
|
|
|
|
|
|
|
acceptHeader => $content{accept_header}, |
|
561
|
|
|
|
|
|
|
userAgentHeader => $content{user_agent}, |
|
562
|
|
|
|
|
|
|
}, |
|
563
|
|
|
|
|
|
|
|
|
564
|
|
|
|
|
|
|
}; |
|
565
|
|
|
|
|
|
|
|
|
566
|
|
|
|
|
|
|
return ($xml_template, $template_vars); |
|
567
|
|
|
|
|
|
|
} |
|
568
|
|
|
|
|
|
|
|
|
569
|
|
|
|
|
|
|
|
|
570
|
|
|
|
|
|
|
sub elv_payment |
|
571
|
|
|
|
|
|
|
{ |
|
572
|
|
|
|
|
|
|
my $self = shift; |
|
573
|
|
|
|
|
|
|
|
|
574
|
|
|
|
|
|
|
my %content = $self->content; |
|
575
|
|
|
|
|
|
|
|
|
576
|
|
|
|
|
|
|
$self->required_fields( 'type' ); |
|
577
|
|
|
|
|
|
|
|
|
578
|
|
|
|
|
|
|
my @required_fields = @{ $Payment_Method{ lc $content{type} }{_required} }; |
|
579
|
|
|
|
|
|
|
|
|
580
|
|
|
|
|
|
|
$self->required_fields( @required_fields ); |
|
581
|
|
|
|
|
|
|
|
|
582
|
|
|
|
|
|
|
########### initialize template variables ######### |
|
583
|
|
|
|
|
|
|
|
|
584
|
|
|
|
|
|
|
$self->currency( $content{currency} ) if $content{currency}; |
|
585
|
|
|
|
|
|
|
my $currency = $self->currency; |
|
586
|
|
|
|
|
|
|
|
|
587
|
|
|
|
|
|
|
my $amount_value = $content{amount}; |
|
588
|
|
|
|
|
|
|
|
|
589
|
|
|
|
|
|
|
# amount must be specified with an "exponent" |
|
590
|
|
|
|
|
|
|
# Example: $123.45 would be specified as value = "12345", exponent = "2" |
|
591
|
|
|
|
|
|
|
|
|
592
|
|
|
|
|
|
|
my $amount_exponent = 2; # works for all but Indonesian Rupiah |
|
593
|
|
|
|
|
|
|
|
|
594
|
|
|
|
|
|
|
if ($amount_value) { |
|
595
|
|
|
|
|
|
|
$amount_value =~ s/,(?=\d\d$)/./g; # 123,45 => 123.45 |
|
596
|
|
|
|
|
|
|
$amount_value =~ s/,//g; # 12,345.46 => 12345.46 |
|
597
|
|
|
|
|
|
|
$amount_value = sprintf "%.2f", $amount_value; # 123 => 123.00 |
|
598
|
|
|
|
|
|
|
$amount_value =~ s/\.//g; # 123.00 => 12300 |
|
599
|
|
|
|
|
|
|
} |
|
600
|
|
|
|
|
|
|
|
|
601
|
|
|
|
|
|
|
# strip non-digits from bank account number |
|
602
|
|
|
|
|
|
|
my $bank_account_number = ''; |
|
603
|
|
|
|
|
|
|
if ( $content{bank_account_number} ) { |
|
604
|
|
|
|
|
|
|
( $bank_account_number = $content{bank_account_number} ) =~ s/\D//g; |
|
605
|
|
|
|
|
|
|
} |
|
606
|
|
|
|
|
|
|
|
|
607
|
|
|
|
|
|
|
########### get XML template for request ########## |
|
608
|
|
|
|
|
|
|
|
|
609
|
|
|
|
|
|
|
my $xml_template = _get_xml_template( $self->action, 'ELV-SSL' ); |
|
610
|
|
|
|
|
|
|
|
|
611
|
|
|
|
|
|
|
########### initialize data for template ########## |
|
612
|
|
|
|
|
|
|
|
|
613
|
|
|
|
|
|
|
my $template_vars = { |
|
614
|
|
|
|
|
|
|
|
|
615
|
|
|
|
|
|
|
merchantCode => $self->login, |
|
616
|
|
|
|
|
|
|
|
|
617
|
|
|
|
|
|
|
version => $self->version, |
|
618
|
|
|
|
|
|
|
|
|
619
|
|
|
|
|
|
|
orderCode => $content{order_number}, |
|
620
|
|
|
|
|
|
|
|
|
621
|
|
|
|
|
|
|
installationId => $self->installation, |
|
622
|
|
|
|
|
|
|
|
|
623
|
|
|
|
|
|
|
description => $content{description}, |
|
624
|
|
|
|
|
|
|
|
|
625
|
|
|
|
|
|
|
amount => { |
|
626
|
|
|
|
|
|
|
value => $amount_value, |
|
627
|
|
|
|
|
|
|
currencyCode => $self->currency, |
|
628
|
|
|
|
|
|
|
exponent => $amount_exponent, |
|
629
|
|
|
|
|
|
|
}, |
|
630
|
|
|
|
|
|
|
|
|
631
|
|
|
|
|
|
|
paymentDetails => { |
|
632
|
|
|
|
|
|
|
paymentType => $Payment_Method{lc $content{type}}{paymentType}, |
|
633
|
|
|
|
|
|
|
action => 'AUTHORISE', |
|
634
|
|
|
|
|
|
|
bankAccountNr => $bank_account_number, |
|
635
|
|
|
|
|
|
|
bankName => $content{bank_name}, |
|
636
|
|
|
|
|
|
|
accountHolderName => $content{account_holder_name}, |
|
637
|
|
|
|
|
|
|
bankLocation => $content{bank_location}, |
|
638
|
|
|
|
|
|
|
bankLocationId => $content{bank_location_id}, |
|
639
|
|
|
|
|
|
|
}, |
|
640
|
|
|
|
|
|
|
|
|
641
|
|
|
|
|
|
|
# required for 3D secure authentication |
|
642
|
|
|
|
|
|
|
session => { |
|
643
|
|
|
|
|
|
|
shopperIPAddress => $content{ip_address}, |
|
644
|
|
|
|
|
|
|
id => $content{session_id}, |
|
645
|
|
|
|
|
|
|
}, |
|
646
|
|
|
|
|
|
|
|
|
647
|
|
|
|
|
|
|
# required for 3D secure authentication |
|
648
|
|
|
|
|
|
|
shopper => { |
|
649
|
|
|
|
|
|
|
acceptHeader => $content{accept_header}, |
|
650
|
|
|
|
|
|
|
userAgentHeader => $content{user_agent}, |
|
651
|
|
|
|
|
|
|
}, |
|
652
|
|
|
|
|
|
|
|
|
653
|
|
|
|
|
|
|
}; |
|
654
|
|
|
|
|
|
|
|
|
655
|
|
|
|
|
|
|
return ($xml_template, $template_vars); |
|
656
|
|
|
|
|
|
|
} |
|
657
|
|
|
|
|
|
|
|
|
658
|
|
|
|
|
|
|
|
|
659
|
|
|
|
|
|
|
sub status_inquiry_or_cancel |
|
660
|
|
|
|
|
|
|
{ |
|
661
|
|
|
|
|
|
|
my $self = shift; |
|
662
|
|
|
|
|
|
|
|
|
663
|
|
|
|
|
|
|
my %content = $self->content; |
|
664
|
|
|
|
|
|
|
|
|
665
|
|
|
|
|
|
|
$self->required_fields( qw/order_number/ ); |
|
666
|
|
|
|
|
|
|
|
|
667
|
|
|
|
|
|
|
########### get XML template for request ########## |
|
668
|
|
|
|
|
|
|
|
|
669
|
|
|
|
|
|
|
my $xml_template = _get_xml_template( $self->action ); |
|
670
|
|
|
|
|
|
|
|
|
671
|
|
|
|
|
|
|
########### initialize data for template ########## |
|
672
|
|
|
|
|
|
|
|
|
673
|
|
|
|
|
|
|
my $template_vars = { |
|
674
|
|
|
|
|
|
|
merchantCode => $self->login, |
|
675
|
|
|
|
|
|
|
version => $self->version, |
|
676
|
|
|
|
|
|
|
orderCode => $content{order_number}, |
|
677
|
|
|
|
|
|
|
}; |
|
678
|
|
|
|
|
|
|
|
|
679
|
|
|
|
|
|
|
return ($xml_template, $template_vars); |
|
680
|
|
|
|
|
|
|
} |
|
681
|
|
|
|
|
|
|
|
|
682
|
|
|
|
|
|
|
|
|
683
|
|
|
|
|
|
|
sub refund |
|
684
|
|
|
|
|
|
|
{ |
|
685
|
|
|
|
|
|
|
my $self = shift; |
|
686
|
|
|
|
|
|
|
|
|
687
|
|
|
|
|
|
|
my %content = $self->content; |
|
688
|
|
|
|
|
|
|
|
|
689
|
|
|
|
|
|
|
$self->required_fields( qw/order_number amount currency/ ); |
|
690
|
|
|
|
|
|
|
|
|
691
|
|
|
|
|
|
|
########### initialize template variables ######### |
|
692
|
|
|
|
|
|
|
|
|
693
|
|
|
|
|
|
|
$self->currency( $content{currency} ) if $content{currency}; |
|
694
|
|
|
|
|
|
|
my $currency = $self->currency; |
|
695
|
|
|
|
|
|
|
|
|
696
|
|
|
|
|
|
|
my $amount_value = $content{amount}; |
|
697
|
|
|
|
|
|
|
|
|
698
|
|
|
|
|
|
|
# amount must be specified with an "exponent" |
|
699
|
|
|
|
|
|
|
# Example: $123.45 would be specified as value = "12345", exponent = "2" |
|
700
|
|
|
|
|
|
|
|
|
701
|
|
|
|
|
|
|
my $amount_exponent = 2; # works for all but Indonesian Rupiah |
|
702
|
|
|
|
|
|
|
|
|
703
|
|
|
|
|
|
|
$amount_value =~ s/,(?=\d\d$)/./g; # 123,45 => 123.45 |
|
704
|
|
|
|
|
|
|
$amount_value =~ s/,//g; # 12,345.46 => 12345.46 |
|
705
|
|
|
|
|
|
|
$amount_value = sprintf "%.2f", $amount_value; # 123 => 123.00 |
|
706
|
|
|
|
|
|
|
$amount_value =~ s/\.//g; # 123.00 => 12300 |
|
707
|
|
|
|
|
|
|
|
|
708
|
|
|
|
|
|
|
########### get XML template for request ########## |
|
709
|
|
|
|
|
|
|
|
|
710
|
|
|
|
|
|
|
my $xml_template = _get_xml_template( $self->action ); |
|
711
|
|
|
|
|
|
|
|
|
712
|
|
|
|
|
|
|
########### initialize data for template ########## |
|
713
|
|
|
|
|
|
|
|
|
714
|
|
|
|
|
|
|
my $template_vars = { |
|
715
|
|
|
|
|
|
|
merchantCode => $self->login, |
|
716
|
|
|
|
|
|
|
version => $self->version, |
|
717
|
|
|
|
|
|
|
orderCode => $content{order_number}, |
|
718
|
|
|
|
|
|
|
amount => { |
|
719
|
|
|
|
|
|
|
value => $amount_value, |
|
720
|
|
|
|
|
|
|
currencyCode => $self->currency, |
|
721
|
|
|
|
|
|
|
exponent => $amount_exponent, |
|
722
|
|
|
|
|
|
|
}, |
|
723
|
|
|
|
|
|
|
}; |
|
724
|
|
|
|
|
|
|
|
|
725
|
|
|
|
|
|
|
return ($xml_template, $template_vars); |
|
726
|
|
|
|
|
|
|
} |
|
727
|
|
|
|
|
|
|
|
|
728
|
|
|
|
|
|
|
|
|
729
|
|
|
|
|
|
|
sub _launder_envariables |
|
730
|
|
|
|
|
|
|
{ |
|
731
|
|
|
|
|
|
|
my ($installation_id, $merchant_code, $xml_password, $debug); |
|
732
|
|
|
|
|
|
|
|
|
733
|
|
|
|
|
|
|
if ( $ENV{WORLDPAY_INSTALLATION_ID} ) { # must be all digits |
|
734
|
|
|
|
|
|
|
( $installation_id ) = $ENV{WORLDPAY_INSTALLATION_ID} =~ m/^ ( \d+ ) $/x; |
|
735
|
|
|
|
|
|
|
} |
|
736
|
|
|
|
|
|
|
|
|
737
|
|
|
|
|
|
|
if ( $ENV{WORLDPAY_MERCHANT_CODE} ) { # must be alphanumeric |
|
738
|
|
|
|
|
|
|
( $merchant_code ) = $ENV{WORLDPAY_MERCHANT_CODE} =~ m/^ ( \w+ ) $/x; |
|
739
|
|
|
|
|
|
|
} |
|
740
|
|
|
|
|
|
|
|
|
741
|
|
|
|
|
|
|
if ( $ENV{WORLDPAY_XML_PASSWORD} ) { # must be alphanumeric |
|
742
|
|
|
|
|
|
|
( $xml_password ) = $ENV{WORLDPAY_XML_PASSWORD} =~ m/^ ( \w+ ) $/x; |
|
743
|
|
|
|
|
|
|
} |
|
744
|
|
|
|
|
|
|
|
|
745
|
|
|
|
|
|
|
if ( $ENV{WORLDPAY_DEBUG} ) { # digits or pathname |
|
746
|
|
|
|
|
|
|
( $debug ) = $ENV{WORLDPAY_DEBUG} =~ m{^ ( \d+ | [-/.\w]+ ) $}x; |
|
747
|
|
|
|
|
|
|
} |
|
748
|
|
|
|
|
|
|
|
|
749
|
|
|
|
|
|
|
@ENV{qw/ |
|
750
|
|
|
|
|
|
|
WORLDPAY_INSTALLATION_ID |
|
751
|
|
|
|
|
|
|
WORLDPAY_MERCHANT_CODE |
|
752
|
|
|
|
|
|
|
WORLDPAY_XML_PASSWORD |
|
753
|
|
|
|
|
|
|
WORLDPAY_DEBUG |
|
754
|
|
|
|
|
|
|
/} = ( |
|
755
|
|
|
|
|
|
|
$installation_id || '', |
|
756
|
|
|
|
|
|
|
$merchant_code || '', |
|
757
|
|
|
|
|
|
|
$xml_password || '', |
|
758
|
|
|
|
|
|
|
$debug || '', |
|
759
|
|
|
|
|
|
|
); |
|
760
|
|
|
|
|
|
|
|
|
761
|
|
|
|
|
|
|
return; |
|
762
|
|
|
|
|
|
|
} |
|
763
|
|
|
|
|
|
|
|
|
764
|
|
|
|
|
|
|
|
|
765
|
|
|
|
|
|
|
sub required_fields |
|
766
|
|
|
|
|
|
|
{ |
|
767
|
|
|
|
|
|
|
my($self, @fields) = @_; |
|
768
|
|
|
|
|
|
|
|
|
769
|
|
|
|
|
|
|
my %content = $self->content(); |
|
770
|
|
|
|
|
|
|
|
|
771
|
|
|
|
|
|
|
foreach (@fields) { |
|
772
|
|
|
|
|
|
|
# croak("missing required field $_") unless exists $content{$_}; # standard B::OP check |
|
773
|
|
|
|
|
|
|
croak("missing required field $_") unless $content{$_}; # modified for B::OP::WorldPay |
|
774
|
|
|
|
|
|
|
} |
|
775
|
|
|
|
|
|
|
} |
|
776
|
|
|
|
|
|
|
|
|
777
|
|
|
|
|
|
|
|
|
778
|
|
|
|
|
|
|
sub _get_xml_template |
|
779
|
|
|
|
|
|
|
{ |
|
780
|
|
|
|
|
|
|
my ($action, $payment_type) = @_;; |
|
781
|
|
|
|
|
|
|
|
|
782
|
|
|
|
|
|
|
my ($payment_xml, $elv_payment_xml, $inquiry_xml, $cancel_xml, $refund_xml, $cancel_or_refund_xml); |
|
783
|
|
|
|
|
|
|
|
|
784
|
|
|
|
|
|
|
$payment_xml = <<'EOS'; |
|
785
|
|
|
|
|
|
|
|
|
786
|
|
|
|
|
|
|
|
|
787
|
|
|
|
|
|
|
"-//WorldPay/DTD WorldPay PaymentService v1//EN" |
|
788
|
|
|
|
|
|
|
"http://dtd.wp3.rbsworldpay.com/paymentService_v1.dtd"> |
|
789
|
|
|
|
|
|
|
|
|
790
|
|
|
|
|
|
|
|
|
791
|
|
|
|
|
|
|
|
|
792
|
|
|
|
|
|
|
|
|
793
|
|
|
|
|
|
|
[% description %] |
|
794
|
|
|
|
|
|
|
|
|
795
|
|
|
|
|
|
|
[%- IF orderContent -%] |
|
796
|
|
|
|
|
|
|
|
|
797
|
|
|
|
|
|
|
[%- END %] |
|
798
|
|
|
|
|
|
|
|
|
799
|
|
|
|
|
|
|
<[% paymentDetails.paymentType %]> |
|
800
|
|
|
|
|
|
|
[% paymentDetails.cardNumber %] |
|
801
|
|
|
|
|
|
|
|
|
802
|
|
|
|
|
|
|
|
|
803
|
|
|
|
|
|
|
|
|
804
|
|
|
|
|
|
|
[% paymentDetails.cardHolderName %] |
|
805
|
|
|
|
|
|
|
[%- IF paymentDetails.startDate.month && paymentDetails.startDate.year %] |
|
806
|
|
|
|
|
|
|
|
|
807
|
|
|
|
|
|
|
|
|
808
|
|
|
|
|
|
|
|
|
809
|
|
|
|
|
|
|
[%- END -%] |
|
810
|
|
|
|
|
|
|
[%- IF paymentDetails.issueNumber %] |
|
811
|
|
|
|
|
|
|
[% paymentDetails.issueNumber %] |
|
812
|
|
|
|
|
|
|
[%- END -%] |
|
813
|
|
|
|
|
|
|
[%- IF paymentDetails.cvc %] |
|
814
|
|
|
|
|
|
|
[% paymentDetails.cvc %] |
|
815
|
|
|
|
|
|
|
[%- END -%] |
|
816
|
|
|
|
|
|
|
[%- IF paymentDetails.cardAddress.firstName || |
|
817
|
|
|
|
|
|
|
paymentDetails.cardAddress.lastName || |
|
818
|
|
|
|
|
|
|
paymentDetails.cardAddress.street || |
|
819
|
|
|
|
|
|
|
paymentDetails.cardAddress.postalCode || |
|
820
|
|
|
|
|
|
|
paymentDetails.cardAddress.countryCode || |
|
821
|
|
|
|
|
|
|
paymentDetails.cardAddress.telephoneNumber |
|
822
|
|
|
|
|
|
|
%] |
|
823
|
|
|
|
|
|
|
|
|
824
|
|
|
|
|
|
|
|
|
825
|
|
|
|
|
|
|
[%- IF paymentDetails.cardAddress.firstName %] |
|
826
|
|
|
|
|
|
|
[% paymentDetails.cardAddress.firstName %] |
|
827
|
|
|
|
|
|
|
[%- END -%] |
|
828
|
|
|
|
|
|
|
[%- IF paymentDetails.cardAddress.lastName %] |
|
829
|
|
|
|
|
|
|
[% paymentDetails.cardAddress.lastName %] |
|
830
|
|
|
|
|
|
|
[%- END -%] |
|
831
|
|
|
|
|
|
|
[%- IF paymentDetails.cardAddress.street %] |
|
832
|
|
|
|
|
|
|
[% paymentDetails.cardAddress.street %] |
|
833
|
|
|
|
|
|
|
[%- END -%] |
|
834
|
|
|
|
|
|
|
[%- IF paymentDetails.cardAddress.postalCode.defined %] |
|
835
|
|
|
|
|
|
|
[% paymentDetails.cardAddress.postalCode %] |
|
836
|
|
|
|
|
|
|
[%- END -%] |
|
837
|
|
|
|
|
|
|
[%- IF paymentDetails.cardAddress.countryCode %] |
|
838
|
|
|
|
|
|
|
[% paymentDetails.cardAddress.countryCode %] |
|
839
|
|
|
|
|
|
|
[%- END -%] |
|
840
|
|
|
|
|
|
|
[%- IF paymentDetails.cardAddress.telephoneNumber %] |
|
841
|
|
|
|
|
|
|
[% paymentDetails.cardAddress.telephoneNumber %] |
|
842
|
|
|
|
|
|
|
[%- END %] |
|
843
|
|
|
|
|
|
|
|
|
844
|
|
|
|
|
|
|
|
|
845
|
|
|
|
|
|
|
[%- END %] |
|
846
|
|
|
|
|
|
|
[% paymentDetails.paymentType %]> |
|
847
|
|
|
|
|
|
|
[%- IF session.shopperIPAddress && session.id %] |
|
848
|
|
|
|
|
|
|
|
|
849
|
|
|
|
|
|
|
[%- END %] |
|
850
|
|
|
|
|
|
|
|
|
851
|
|
|
|
|
|
|
[%- IF shopper.acceptHeader && shopper.userAgentHeader %] |
|
852
|
|
|
|
|
|
|
|
|
853
|
|
|
|
|
|
|
|
|
854
|
|
|
|
|
|
|
[% shopper.acceptHeader %] |
|
855
|
|
|
|
|
|
|
[% shopper.userAgentHeader %] |
|
856
|
|
|
|
|
|
|
|
|
857
|
|
|
|
|
|
|
|
|
858
|
|
|
|
|
|
|
[%- END %] |
|
859
|
|
|
|
|
|
|
|
|
860
|
|
|
|
|
|
|
|
|
861
|
|
|
|
|
|
|
|
|
862
|
|
|
|
|
|
|
EOS |
|
863
|
|
|
|
|
|
|
|
|
864
|
|
|
|
|
|
|
$elv_payment_xml = <<'EOS'; |
|
865
|
|
|
|
|
|
|
|
|
866
|
|
|
|
|
|
|
|
|
867
|
|
|
|
|
|
|
"-//WorldPay/DTD WorldPay PaymentService v1//EN" |
|
868
|
|
|
|
|
|
|
"http://dtd.wp3.rbsworldpay.com/paymentService_v1.dtd"> |
|
869
|
|
|
|
|
|
|
|
|
870
|
|
|
|
|
|
|
|
|
871
|
|
|
|
|
|
|
|
|
872
|
|
|
|
|
|
|
|
|
873
|
|
|
|
|
|
|
[% description %] |
|
874
|
|
|
|
|
|
|
|
|
875
|
|
|
|
|
|
|
[%- IF orderContent -%] |
|
876
|
|
|
|
|
|
|
|
|
877
|
|
|
|
|
|
|
[%- END %] |
|
878
|
|
|
|
|
|
|
|
|
879
|
|
|
|
|
|
|
|
|
880
|
|
|
|
|
|
|
[% paymentDetails.accountHolderName %] |
|
881
|
|
|
|
|
|
|
[% paymentDetails.bankAccountNr %] |
|
882
|
|
|
|
|
|
|
[% paymentDetails.bankName %] |
|
883
|
|
|
|
|
|
|
[% paymentDetails.bankLocation %] |
|
884
|
|
|
|
|
|
|
[% paymentDetails.bankLocationId %] |
|
885
|
|
|
|
|
|
|
|
|
886
|
|
|
|
|
|
|
|
|
887
|
|
|
|
|
|
|
|
|
888
|
|
|
|
|
|
|
|
|
889
|
|
|
|
|
|
|
|
|
890
|
|
|
|
|
|
|
EOS |
|
891
|
|
|
|
|
|
|
|
|
892
|
|
|
|
|
|
|
$inquiry_xml = <<'EOS'; |
|
893
|
|
|
|
|
|
|
|
|
894
|
|
|
|
|
|
|
|
|
895
|
|
|
|
|
|
|
"-//WorldPay/DTD WorldPay PaymentService v1//EN" |
|
896
|
|
|
|
|
|
|
"http://dtd.wp3.rbsworldpay.com/paymentService_v1.dtd"> |
|
897
|
|
|
|
|
|
|
|
|
898
|
|
|
|
|
|
|
|
|
899
|
|
|
|
|
|
|
|
|
900
|
|
|
|
|
|
|
|
|
901
|
|
|
|
|
|
|
|
|
902
|
|
|
|
|
|
|
|
|
903
|
|
|
|
|
|
|
EOS |
|
904
|
|
|
|
|
|
|
|
|
905
|
|
|
|
|
|
|
$cancel_xml = <<'EOS'; |
|
906
|
|
|
|
|
|
|
|
|
907
|
|
|
|
|
|
|
|
|
908
|
|
|
|
|
|
|
"-//WorldPay/DTD WorldPay PaymentService v1//EN" |
|
909
|
|
|
|
|
|
|
"http://dtd.wp3.rbsworldpay.com/paymentService_v1.dtd"> |
|
910
|
|
|
|
|
|
|
|
|
911
|
|
|
|
|
|
|
|
|
912
|
|
|
|
|
|
|
|
|
913
|
|
|
|
|
|
|
|
|
914
|
|
|
|
|
|
|
|
|
915
|
|
|
|
|
|
|
|
|
916
|
|
|
|
|
|
|
|
|
917
|
|
|
|
|
|
|
|
|
918
|
|
|
|
|
|
|
EOS |
|
919
|
|
|
|
|
|
|
|
|
920
|
|
|
|
|
|
|
$refund_xml = <<'EOS'; |
|
921
|
|
|
|
|
|
|
|
|
922
|
|
|
|
|
|
|
|
|
923
|
|
|
|
|
|
|
"-//WorldPay/DTD WorldPay PaymentService v1//EN" |
|
924
|
|
|
|
|
|
|
"http://dtd.wp3.rbsworldpay.com/paymentService_v1.dtd"> |
|
925
|
|
|
|
|
|
|
|
|
926
|
|
|
|
|
|
|
|
|
927
|
|
|
|
|
|
|
|
|
928
|
|
|
|
|
|
|
|
|
929
|
|
|
|
|
|
|
|
|
930
|
|
|
|
|
|
|
|
|
931
|
|
|
|
|
|
|
value="[% amount.value %]" |
|
932
|
|
|
|
|
|
|
currencyCode="[% amount.currencyCode %]" |
|
933
|
|
|
|
|
|
|
exponent="[% amount.exponent %]" |
|
934
|
|
|
|
|
|
|
debitCreditIndicator="credit" |
|
935
|
|
|
|
|
|
|
/> |
|
936
|
|
|
|
|
|
|
|
|
937
|
|
|
|
|
|
|
|
|
938
|
|
|
|
|
|
|
|
|
939
|
|
|
|
|
|
|
|
|
940
|
|
|
|
|
|
|
EOS |
|
941
|
|
|
|
|
|
|
|
|
942
|
|
|
|
|
|
|
$cancel_or_refund_xml = <<'EOS'; |
|
943
|
|
|
|
|
|
|
|
|
944
|
|
|
|
|
|
|
|
|
945
|
|
|
|
|
|
|
"-//WorldPay/DTD WorldPay PaymentService v1//EN" |
|
946
|
|
|
|
|
|
|
"http://dtd.wp3.rbsworldpay.com/paymentService_v1.dtd"> |
|
947
|
|
|
|
|
|
|
|
|
948
|
|
|
|
|
|
|
|
|
949
|
|
|
|
|
|
|
|
|
950
|
|
|
|
|
|
|
|
|
951
|
|
|
|
|
|
|
|
|
952
|
|
|
|
|
|
|
|
|
953
|
|
|
|
|
|
|
|
|
954
|
|
|
|
|
|
|
|
|
955
|
|
|
|
|
|
|
EOS |
|
956
|
|
|
|
|
|
|
|
|
957
|
|
|
|
|
|
|
if ($action eq 'status') { |
|
958
|
|
|
|
|
|
|
return $inquiry_xml; |
|
959
|
|
|
|
|
|
|
} |
|
960
|
|
|
|
|
|
|
elsif ($action eq 'cancel') { |
|
961
|
|
|
|
|
|
|
return $cancel_xml; |
|
962
|
|
|
|
|
|
|
} |
|
963
|
|
|
|
|
|
|
elsif ($action eq 'refund') { |
|
964
|
|
|
|
|
|
|
return $refund_xml; |
|
965
|
|
|
|
|
|
|
} |
|
966
|
|
|
|
|
|
|
elsif ($action eq 'cancel_or_refund') { |
|
967
|
|
|
|
|
|
|
return $cancel_or_refund_xml; |
|
968
|
|
|
|
|
|
|
} |
|
969
|
|
|
|
|
|
|
elsif (defined $payment_type && $payment_type eq 'ELV-SSL') { |
|
970
|
|
|
|
|
|
|
return $elv_payment_xml; |
|
971
|
|
|
|
|
|
|
} |
|
972
|
|
|
|
|
|
|
else { |
|
973
|
|
|
|
|
|
|
return $payment_xml; |
|
974
|
|
|
|
|
|
|
} |
|
975
|
|
|
|
|
|
|
} |
|
976
|
|
|
|
|
|
|
|
|
977
|
|
|
|
|
|
|
1; |
|
978
|
|
|
|
|
|
|
|
|
979
|
|
|
|
|
|
|
=head1 NAME |
|
980
|
|
|
|
|
|
|
|
|
981
|
|
|
|
|
|
|
Business::OnlinePayment::WorldPay - RBS WorldPay interface for Business::OnlinePayment |
|
982
|
|
|
|
|
|
|
|
|
983
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
984
|
|
|
|
|
|
|
|
|
985
|
|
|
|
|
|
|
use Business::OnlinePayment; |
|
986
|
|
|
|
|
|
|
|
|
987
|
|
|
|
|
|
|
my $tx = Business::OnlinePayment->new("WorldPay"); |
|
988
|
|
|
|
|
|
|
|
|
989
|
|
|
|
|
|
|
$tx->content( |
|
990
|
|
|
|
|
|
|
installation => '12345', |
|
991
|
|
|
|
|
|
|
login => 'testdrive', |
|
992
|
|
|
|
|
|
|
password => 'xyzzy', |
|
993
|
|
|
|
|
|
|
|
|
994
|
|
|
|
|
|
|
type => 'Visa', |
|
995
|
|
|
|
|
|
|
action => 'payment', # 'status', 'cancel', 'refund', 'cancel_or_refund' |
|
996
|
|
|
|
|
|
|
description => '20 English Roses', |
|
997
|
|
|
|
|
|
|
amount => '49.95', |
|
998
|
|
|
|
|
|
|
currency => 'GBP', |
|
999
|
|
|
|
|
|
|
order_number => 'A00100', |
|
1000
|
|
|
|
|
|
|
|
|
1001
|
|
|
|
|
|
|
name => 'Claire Voyance', |
|
1002
|
|
|
|
|
|
|
address => '123 Disk Drive', |
|
1003
|
|
|
|
|
|
|
city => 'Anywhere', |
|
1004
|
|
|
|
|
|
|
state => 'DE', |
|
1005
|
|
|
|
|
|
|
zipcode => '19808', |
|
1006
|
|
|
|
|
|
|
country => 'US', |
|
1007
|
|
|
|
|
|
|
phone => '201-555-1212', |
|
1008
|
|
|
|
|
|
|
|
|
1009
|
|
|
|
|
|
|
card_number => '4007000000027', |
|
1010
|
|
|
|
|
|
|
exp_date => '09/10', |
|
1011
|
|
|
|
|
|
|
start_date => '01/03', |
|
1012
|
|
|
|
|
|
|
issue_number => '002', |
|
1013
|
|
|
|
|
|
|
cvc => '377', |
|
1014
|
|
|
|
|
|
|
); |
|
1015
|
|
|
|
|
|
|
|
|
1016
|
|
|
|
|
|
|
# ELV payment: |
|
1017
|
|
|
|
|
|
|
$tx->content( |
|
1018
|
|
|
|
|
|
|
installation => '12345', |
|
1019
|
|
|
|
|
|
|
login => 'testdrive', |
|
1020
|
|
|
|
|
|
|
password => 'xyzzy', |
|
1021
|
|
|
|
|
|
|
|
|
1022
|
|
|
|
|
|
|
type => 'ELV', |
|
1023
|
|
|
|
|
|
|
action => 'payment', |
|
1024
|
|
|
|
|
|
|
description => '20 English Roses', |
|
1025
|
|
|
|
|
|
|
amount => '49.95', |
|
1026
|
|
|
|
|
|
|
currency => 'GBP', |
|
1027
|
|
|
|
|
|
|
order_number => 'A00100', |
|
1028
|
|
|
|
|
|
|
|
|
1029
|
|
|
|
|
|
|
###################################### |
|
1030
|
|
|
|
|
|
|
account_holder_name => 'Claire Voyance', |
|
1031
|
|
|
|
|
|
|
bank_account_number => '92441196', |
|
1032
|
|
|
|
|
|
|
bank_name => 'Bundesbank', |
|
1033
|
|
|
|
|
|
|
bank_location => 'Berline', |
|
1034
|
|
|
|
|
|
|
bank_location_id => '20030000', |
|
1035
|
|
|
|
|
|
|
###################################### |
|
1036
|
|
|
|
|
|
|
); |
|
1037
|
|
|
|
|
|
|
|
|
1038
|
|
|
|
|
|
|
$tx->set_server('test'); # 'live' (default) or 'test' |
|
1039
|
|
|
|
|
|
|
|
|
1040
|
|
|
|
|
|
|
$tx->debug(1); # debugging to STDERR |
|
1041
|
|
|
|
|
|
|
$tx->debug(1, "/tmp/debugging.outfile"); # debugging output to file |
|
1042
|
|
|
|
|
|
|
|
|
1043
|
|
|
|
|
|
|
$tx->test_transaction(1); # another way to set server to test |
|
1044
|
|
|
|
|
|
|
|
|
1045
|
|
|
|
|
|
|
$tx->submit(); |
|
1046
|
|
|
|
|
|
|
|
|
1047
|
|
|
|
|
|
|
print "server_response = ", $tx->server_response, "\n"; |
|
1048
|
|
|
|
|
|
|
|
|
1049
|
|
|
|
|
|
|
print "is_success = ", $tx->is_success, "\n"; |
|
1050
|
|
|
|
|
|
|
|
|
1051
|
|
|
|
|
|
|
print "authorization = ", $tx->authorization, "\n"; |
|
1052
|
|
|
|
|
|
|
print "error_message = ", $tx->error_message, "\n\n"; |
|
1053
|
|
|
|
|
|
|
|
|
1054
|
|
|
|
|
|
|
print "result_code = ", $tx->result_code, "\n"; |
|
1055
|
|
|
|
|
|
|
|
|
1056
|
|
|
|
|
|
|
print "cvv_response = ", $tx->cvv_response, "\n"; |
|
1057
|
|
|
|
|
|
|
print "avs_response = ", $tx->avs_response, "\n"; |
|
1058
|
|
|
|
|
|
|
print "risk_score = ", $tx->risk_score, "\n"; |
|
1059
|
|
|
|
|
|
|
|
|
1060
|
|
|
|
|
|
|
if ($tx->is_success) { |
|
1061
|
|
|
|
|
|
|
print "Card processed successfully: " . $tx->authorization . "\n"; |
|
1062
|
|
|
|
|
|
|
} |
|
1063
|
|
|
|
|
|
|
else { |
|
1064
|
|
|
|
|
|
|
print "Card was rejected: " . $tx->error_message . "\n"; |
|
1065
|
|
|
|
|
|
|
} |
|
1066
|
|
|
|
|
|
|
|
|
1067
|
|
|
|
|
|
|
=cut |
|
1068
|
|
|
|
|
|
|
|
|
1069
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
1070
|
|
|
|
|
|
|
|
|
1071
|
|
|
|
|
|
|
This module subclasses Business::OnlinePayment to provide a basic merchant |
|
1072
|
|
|
|
|
|
|
processing interface for submitting transactions as XML requests in the |
|
1073
|
|
|
|
|
|
|
direct model provided by RBS WorldPay. |
|
1074
|
|
|
|
|
|
|
|
|
1075
|
|
|
|
|
|
|
L |
|
1076
|
|
|
|
|
|
|
|
|
1077
|
|
|
|
|
|
|
L |
|
1078
|
|
|
|
|
|
|
|
|
1079
|
|
|
|
|
|
|
It currently implements payments, cancellations, refunds, and payment status inquiries. |
|
1080
|
|
|
|
|
|
|
|
|
1081
|
|
|
|
|
|
|
Orders submitted to the RBS WorldPay system are required to be valid XML |
|
1082
|
|
|
|
|
|
|
files as specified in their Document Type Definition (DTD): |
|
1083
|
|
|
|
|
|
|
|
|
1084
|
|
|
|
|
|
|
L |
|
1085
|
|
|
|
|
|
|
|
|
1086
|
|
|
|
|
|
|
=head1 BUGS |
|
1087
|
|
|
|
|
|
|
|
|
1088
|
|
|
|
|
|
|
Please report any bugs or feature requests to |
|
1089
|
|
|
|
|
|
|
C, or through the web interface at |
|
1090
|
|
|
|
|
|
|
L. |
|
1091
|
|
|
|
|
|
|
I will be notified, and then you'll automatically be notified of progress on your |
|
1092
|
|
|
|
|
|
|
bug as I make changes. |
|
1093
|
|
|
|
|
|
|
|
|
1094
|
|
|
|
|
|
|
=head1 SUPPORT |
|
1095
|
|
|
|
|
|
|
|
|
1096
|
|
|
|
|
|
|
You can find documentation for this module with the perldoc command. |
|
1097
|
|
|
|
|
|
|
|
|
1098
|
|
|
|
|
|
|
perldoc Business::OnlinePayment::WorldPay |
|
1099
|
|
|
|
|
|
|
|
|
1100
|
|
|
|
|
|
|
You can also look for information at: |
|
1101
|
|
|
|
|
|
|
|
|
1102
|
|
|
|
|
|
|
=over 4 |
|
1103
|
|
|
|
|
|
|
|
|
1104
|
|
|
|
|
|
|
=item * RT: CPAN's request tracker |
|
1105
|
|
|
|
|
|
|
|
|
1106
|
|
|
|
|
|
|
L |
|
1107
|
|
|
|
|
|
|
|
|
1108
|
|
|
|
|
|
|
=item * AnnoCPAN: Annotated CPAN documentation |
|
1109
|
|
|
|
|
|
|
|
|
1110
|
|
|
|
|
|
|
L |
|
1111
|
|
|
|
|
|
|
|
|
1112
|
|
|
|
|
|
|
=item * CPAN Ratings |
|
1113
|
|
|
|
|
|
|
|
|
1114
|
|
|
|
|
|
|
L |
|
1115
|
|
|
|
|
|
|
|
|
1116
|
|
|
|
|
|
|
=item * Search CPAN |
|
1117
|
|
|
|
|
|
|
|
|
1118
|
|
|
|
|
|
|
L |
|
1119
|
|
|
|
|
|
|
|
|
1120
|
|
|
|
|
|
|
=back |
|
1121
|
|
|
|
|
|
|
|
|
1122
|
|
|
|
|
|
|
=head1 SEE ALSO |
|
1123
|
|
|
|
|
|
|
|
|
1124
|
|
|
|
|
|
|
L |
|
1125
|
|
|
|
|
|
|
|
|
1126
|
|
|
|
|
|
|
=head1 AUTHOR |
|
1127
|
|
|
|
|
|
|
|
|
1128
|
|
|
|
|
|
|
Paul Grassie, Epaul.grassie@ardishealth.comE |
|
1129
|
|
|
|
|
|
|
|
|
1130
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
|
1131
|
|
|
|
|
|
|
|
|
1132
|
|
|
|
|
|
|
Copyright (C) 2010 Paul Grassie, Ardis Health, http://www.ardishealth.com. All Rights Reserved. |
|
1133
|
|
|
|
|
|
|
|
|
1134
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or modify |
|
1135
|
|
|
|
|
|
|
it under the same terms as Perl itself, either Perl version 5.8.8 or, |
|
1136
|
|
|
|
|
|
|
at your option, any later version of Perl 5 you may have available. |