File Coverage

blib/lib/Business/OnlinePayment/SagePay.pm
Criterion Covered Total %
statement 30 261 11.4
branch 0 108 0.0
condition 0 30 0.0
subroutine 10 25 40.0
pod 2 14 14.2
total 42 438 9.5


line stmt bran cond sub pod time code
1             package Business::OnlinePayment::SagePay;
2             # vim: ts=2 sts=2 sw=2 :
3             $Business::OnlinePayment::SagePay::VERSION = '0.17';
4 1     1   16510 use strict;
  1         3  
  1         35  
5 1     1   3 use warnings;
  1         1  
  1         33  
6              
7 1     1   4 use base qw(Business::OnlinePayment);
  1         9  
  1         580  
8              
9 1     1   2691 use Carp;
  1         2  
  1         52  
10 1     1   591 use Net::SSLeay qw(make_form post_https);
  1         12324  
  1         377  
11 1     1   490 use Devel::Dwarn;
  1         9048  
  1         6  
12 1     1   119 use Exporter 'import';
  1         1  
  1         32  
13              
14             use constant {
15 1         423 SAGEPAY_STATUS_OK => 'OK',
16             SAGEPAY_STATUS_AUTHENTICATED => 'AUTHENTICATED',
17             SAGEPAY_STATUS_REGISTERED => 'REGISTERED',
18             SAGEPAY_STATUS_3DSECURE => '3DAUTH',
19             SAGEPAY_STATUS_PAYPAL_REDIRECT => 'PPREDIRECT',
20 1     1   3 };
  1         2  
21              
22             # CARD TYPE MAP
23             my %card_type = (
24             'american express' => 'AMEX',
25             'amex' => 'AMEX',
26             'visa' => 'VISA',
27             'visa electron' => 'UKE',
28             'visa debit' => 'DELTA',
29             'mastercard' => 'MC',
30             'mastercard debit' => 'MC',
31             'maestro' => 'MAESTRO',
32             'international maestro' => 'MAESTRO',
33             'switch' => 'MAESTRO',
34             'switch solo' => 'SOLO',
35             'solo' => 'SOLO',
36             'diners club' => 'DINERS',
37             'jcb' => 'JCB',
38             'paypal' => 'PAYPAL',
39             );
40              
41             my $status = {
42             TIMEOUT => 'There was a problem communicating with the payment server, please try later',
43             UNKNOWN => 'There was an unknown problem taking your payment. Please try again',
44             '3D_PASS' => 'Your card failed the password check.',
45             2000 => 'Your card was declined by the bank.',
46             5013 => 'Your card has expired.',
47             3078 => 'Your e-mail was invalid.',
48             4023 => 'The card issue number is invalid.',
49             4024 => 'The card issue number is required.',
50             2000 => 'Your card was declined by the issuer',
51             2001 => 'Your card was declined by the merchant',
52             5995 => 'Please ensure you have entered the correct digits off the back of your card and your billing address is correct',
53             5027 => 'Card start date is invalid',
54             5028 => 'Card expiry date is invalid',
55             3107 => 'Please ensure you have entered your full name, not just your surname',
56             3069 => 'Your card type is not supported by this vendor. Please try a different card',
57             3057 => 'Your card security number was incorrect. This is normally the last 3 digits on the back of your card',
58             4021 => 'Your card number was incorrect',
59             5018 => "Your card security number was the incorrect length. This is normally the last 3 digits on the back of your card",
60             3130 => "Your state was incorrect. Please use the standard two character state code",
61             3068 => "Your card type is not supported by this vendor. Please try a different card",
62             5055 => "Your postcode had incorrect characters. Please re-enter",
63             3055 => "Your card was not recognised. Please try another",
64             };
65              
66             #ACTION MAP
67             my %action = (
68             'normal authorization' => 'PAYMENT',
69             'authorization only' => 'AUTHENTICATE',
70             'post authorization' => 'AUTHORISE',
71             'refund' => 'REFUND',
72             'repeat' => 'REPEAT',
73             );
74              
75             my %servers = (
76             live => {
77             url => 'live.sagepay.com',
78             path => '/gateway/service/vspdirect-register.vsp',
79             callback => '/gateway/service/direct3dcallback.vsp',
80             authorise => '/gateway/service/authorise.vsp',
81             refund => '/gateway/service/refund.vsp',
82             repeat => '/gateway/service/repeat.vsp',
83             cancel => '/gateway/service/cancel.vsp',
84             complete => '/gateway/service/complete.vsp',
85             void => '/gateway/service/void.vsp',
86             port => 443,
87             },
88             test => {
89             url => 'test.sagepay.com',
90             path => '/gateway/service/vspdirect-register.vsp',
91             callback => '/gateway/service/direct3dcallback.vsp',
92             authorise => '/gateway/service/authorise.vsp',
93             refund => '/gateway/service/refund.vsp',
94             repeat => '/gateway/service/repeat.vsp',
95             cancel => '/gateway/service/cancel.vsp',
96             complete => '/gateway/service/complete.vsp',
97             void => '/gateway/service/void.vsp',
98             port => 443,
99             },
100             simulator => {
101             url => 'test.sagepay.com',
102             path => '/Simulator/VSPDirectGateway.asp',
103             callback => '/Simulator/VSPDirectCallback.asp',
104             authorise => '/Simulator/VSPServerGateway.asp?service=VendorAuthoriseTx',
105             refund => '/Simulator/VSPServerGateway.asp?service=VendorRefundTx',
106             repeat => '/Simulator/VSPServerGateway.asp?service=VendorRepeatTx',
107             cancel => '/Simulator/VSPServerGateway.asp?service=VendorCancelTx',
108             void => '/Simulator/VSPServerGateway.asp?service=VendorVoidTx',
109             complete => '/Simulator/paypalcomplete.asp',
110             port => 443,
111             },
112             timeout => {
113             url => 'localhost',
114             path => '/timeout',
115             callback => '/Simulator/VSPDirectCallback.asp',
116             authorise => '/Simulator/VSPServerGateway.asp?service=VendorAuthoriseTx',
117             refund => '/Simulator/VSPServerGateway.asp?service=VendorRefundTx',
118             repeat => '/Simulator/VSPServerGateway.asp?service=VendorRepeatTx',
119             cancel => '/Simulator/VSPServerGateway.asp?service=VendorCancelTx',
120             void => '/Simulator/VSPServerGateway.asp?service=VendorVoidTx',
121             port => 3000,
122             }
123             );
124              
125             sub callback {
126 0     0 0   my ($self, $value) = @_;
127 0 0         $self->{'callback'} = $value if $value;
128 0           return $self->{'callback'};
129             }
130              
131             sub set_server {
132 0     0 0   my ($self, $type) = @_;
133 0           $self->{'_server'} = $type;
134 0           $self->server($servers{$type}->{'url'});
135 0           $self->path($servers{$type}->{'path'});
136 0           $self->callback($servers{$type}->{'callback'});
137 0           $self->port($servers{$type}->{'port'});
138             }
139              
140             sub set_defaults {
141 0     0 0   my $self = shift;
142 0           $self->set_server('live');
143 0           $self->build_subs(qw/
144             protocol currency cvv2_response postcode_response address_response error_code require_3d require_paypal
145             forward_to invoice_number authentication_key authorization_code pareq cross_reference callback encoded_3d_result
146             /);
147 0           $self->protocol('3.00');
148 0           $self->currency('GBP');
149 0           $self->require_3d(0);
150 0           $self->require_paypal(0);
151             }
152              
153             sub do_remap {
154 0     0 0   my ($self, $content, %map) = @_;
155 0           my %remapped = ();
156 0           while (my ($k, $v) = each %map) {
157 1     1   5 no strict 'refs';
  1         1  
  1         1460  
158 0           $remapped{$k} = ref( $map{$k} ) ?
159 0 0         ${ $map{$k} }
160             :
161             $content->{$v};
162             }
163 0           return %remapped;
164             }
165              
166             sub format_amount {
167 0     0 0   my $amount = shift;
168 0           return sprintf("%.2f",$amount);
169             }
170              
171             sub submit_3d {
172 0     0 0   my $self = shift;
173 0           my %content = $self->content;
174 0           my %post_data = (
175 0           ( map { $_ => $content{$_} } qw(login password) ),
176             MD => $content{'cross_reference'},
177             PaRes => $content{'pares'},
178             );
179 0 0         $self->set_server($ENV{'SAGEPAY_F_SIMULATOR'} ? 'simulator' : 'test')
    0          
180             if $self->test_transaction;
181 0           my ($page, $response, %headers) =
182             post_https(
183             $self->server,
184             $self->port,
185             $self->callback,
186             undef,
187             make_form(%post_data)
188             );
189 0 0         unless ($page) {
190 0           $self->error_message($status->{TIMEOUT});
191 0           return;
192             }
193              
194 0           my $rf = $self->_parse_response($page);
195              
196 0 0         if($ENV{'SAGEPAY_DEBUG'}) {
197 0           warn "3DSecure:";
198 0           Dwarn $rf;
199             }
200              
201 0           $self->server_response($rf);
202 0           $self->result_code($rf->{'Status'});
203 0           $self->authentication_key($rf->{'SecurityKey'});
204 0           $self->authorization($rf->{'VPSTxId'});
205 0           $self->authorization_code($rf->{'TxAuthNo'});
206 0           $self->encoded_3d_result($rf->{'CAVV'});
207              
208 0 0 0       unless(
    0          
209             ($self->is_success($rf->{'Status'} eq SAGEPAY_STATUS_OK) ||
210             ($rf->{'Status'} eq SAGEPAY_STATUS_AUTHENTICATED)
211             ? 1 : 0)) {
212 0           $self->error_message($status->{'3D_PASS'});
213 0 0         if($ENV{'SAGEPAY_DEBUG_ERROR_ONLY'}) {
214 0           Dwarn $rf;
215             }
216 0           return 0;
217             } else{
218 0           return 1;
219             }
220             }
221              
222             sub submit_paypal {
223 0     0 1   my $self = shift;
224              
225 0           my $content = $self->sanitised_content;
226              
227 0           my %field_mapping = (
228             VpsProtocol => \($self->protocol),
229             Vendor => \($self->vendor),
230             VPSTxId => 'authentication_id',
231             Amount => 'amount',
232             );
233              
234 0           my %post_data = (
235             $self->do_remap($content, %field_mapping),
236             TxType => 'COMPLETE',
237             Accept => 'YES',
238             );
239              
240 0 0         if($ENV{'SAGEPAY_DEBUG'}) {
241 0           warn "PayPal complete Form:";
242 0           Dwarn {
243             %post_data,
244             };
245             }
246              
247 0 0         $self->set_server($ENV{'SAGEPAY_F_SIMULATOR'} ? 'simulator' : 'test')
    0          
248             if $self->test_transaction;
249              
250 0           $self->path( $servers{ $self->{'_server'} }->{'complete'} );
251              
252 0           my ($page, $response, %headers) =
253             post_https(
254             $self->server,
255             $self->port,
256             $self->path,
257             undef,
258             make_form(%post_data)
259             );
260              
261 0 0         unless ($page) {
262 0           $self->error_message($status->{TIMEOUT});
263 0           $self->is_success(0);
264 0           return;
265             }
266              
267 0           my $rf = $self->_parse_response($page);
268              
269 0 0         if($ENV{'SAGEPAY_DEBUG'}) {
270 0           warn "PayPal:";
271 0           Dwarn $rf;
272             }
273              
274 0           $self->server_response($rf);
275 0           $self->result_code($rf->{'Status'});
276 0           $self->authorization($rf->{'VPSTxId'});
277 0           $self->authentication_key($rf->{'SecurityKey'});
278 0           $self->authorization_code($rf->{'TxAuthNo'});
279              
280 0 0 0       unless($self->is_success(
    0          
281             $rf->{'Status'} eq SAGEPAY_STATUS_OK ||
282             $rf->{'Status'} eq SAGEPAY_STATUS_AUTHENTICATED
283             ? 1 : 0)
284             ) {
285 0           my $code = substr $rf->{'StatusDetail'}, 0 ,4;
286              
287 0 0         if($ENV{'SAGEPAY_DEBUG_ERROR_ONLY'}) {
288 0           Dwarn $rf;
289             }
290              
291 0           $self->error_code($code);
292 0   0       $self->error_message($status->{$code} || $status->{UNKNOWN});
293              
294 0           return 0;
295             }
296             else {
297 0           return 1;
298             }
299             }
300              
301              
302             sub void_action { #void authorization
303 0     0 0   my $self = shift;
304 0           $self->initialise;
305 0           my %content = $self->content();
306 0           my %field_mapping = (
307             VpsProtocol => \($self->protocol),
308             Vendor => \($self->vendor),
309             VendorTxCode => 'invoice_number',
310             VPSTxId => 'authentication_id',
311             SecurityKey => 'authentication_key',
312             TxAuthNo => 'authentication_number'
313             );
314 0           my %post_data = $self->do_remap(\%content,%field_mapping);
315 0           $post_data{'TxType'} = 'VOID';
316              
317 0 0         if($ENV{'SAGEPAY_DEBUG'}) {
318 0           Dwarn %post_data;
319             }
320              
321 0           $self->path($servers{$self->{'_server'}}->{'void'});
322 0           my ($page, $response, %headers) =
323             post_https(
324             $self->server,
325             $self->port,
326             $self->path,
327             undef,
328             make_form(
329             %post_data
330             )
331             );
332 0 0         unless ($page) {
333 0           $self->error_message($status->{TIMEOUT});
334 0           $self->is_success(0);
335 0           return;
336             }
337              
338 0           my $rf = $self->_parse_response($page);
339              
340 0 0         if($ENV{'SAGEPAY_DEBUG'}) {
341 0           warn "Cancellation:";
342 0           Dwarn $rf;
343             }
344              
345 0           $self->server_response($rf);
346 0           $self->result_code($rf->{'Status'});
347 0 0         unless($self->is_success($rf->{'Status'} eq SAGEPAY_STATUS_OK ? 1 : 0)) {
    0          
348 0 0         if($ENV{'SAGEPAY_DEBUG_ERROR_ONLY'}) {
349 0           Dwarn $rf;
350             }
351 0           $self->error_message($rf->{'StatusDetail'});
352             }
353             }
354              
355             sub cancel_action { #cancel authentication
356 0     0 0   my $self = shift;
357 0           $self->initialise;
358 0           my %content = $self->content();
359 0           my %field_mapping = (
360             VpsProtocol => \($self->protocol),
361             Vendor => \($self->vendor),
362             VendorTxCode => 'parent_invoice_number',
363             TxAuthNo => 'invoice_number',
364             VPSTxId => 'authentication_id',
365             SecurityKey => 'authentication_key',
366             );
367 0           my %post_data = $self->do_remap(\%content,%field_mapping);
368 0           $post_data{'TxType'} = 'CANCEL';
369              
370 0 0         if($ENV{'SAGEPAY_DEBUG'}) {
371 0           Dwarn %post_data;
372             }
373              
374 0           $self->path($servers{$self->{'_server'}}->{'cancel'});
375 0           my ($page, $response, %headers) =
376             post_https(
377             $self->server,
378             $self->port,
379             $self->path,
380             undef,
381             make_form(
382             %post_data
383             )
384             );
385 0 0         unless ($page) {
386 0           $self->error_message($status->{TIMEOUT});
387 0           $self->is_success(0);
388 0           return;
389             }
390              
391 0           my $rf = $self->_parse_response($page);
392              
393 0 0         if($ENV{'SAGEPAY_DEBUG'}) {
394 0           warn "Cancellation:";
395 0           Dwarn $rf;
396             }
397              
398 0           $self->server_response($rf);
399 0           $self->result_code($rf->{'Status'});
400 0 0         unless($self->is_success($rf->{'Status'} eq SAGEPAY_STATUS_OK ? 1 : 0)) {
    0          
401 0 0         if($ENV{'SAGEPAY_DEBUG_ERROR_ONLY'}) {
402 0           Dwarn $rf;
403             }
404 0           $self->error_message($rf->{'StatusDetail'});
405             }
406             }
407              
408             sub initialise {
409 0     0 0   my $self = shift;
410 0 0         croak "Need vendor ID"
411             unless defined $self->vendor;
412 0 0         $self->set_server($ENV{'SAGEPAY_F_SIMULATOR'} ? 'simulator' : 'test')
    0          
413             if $self->test_transaction;
414             }
415              
416             sub auth_action {
417 0     0 0   my ($self, $action) = @_;
418 0           $self->initialise;
419              
420 0           my %content = $self->content();
421 0           my %field_mapping = (
422             VpsProtocol => \($self->protocol),
423             Vendor => \($self->vendor),
424             TxType => \($action{lc $content{'action'}}),
425             VendorTxCode=> 'invoice_number',
426             Description => 'description',
427             Currency => \($self->currency),
428             Amount => \(format_amount($content{'amount'})),
429             RelatedVPSTxId => 'parent_auth',
430             RelatedVendorTxCode => 'parent_invoice_number',
431             RelatedSecurityKey => 'authentication_key',
432             RelatedTxAuthNo => 'authentication_number'
433             );
434 0           my %post_data = $self->do_remap(\%content,%field_mapping);
435              
436 0 0         if($ENV{'SAGEPAY_DEBUG'}) {
437 0           Dwarn {%post_data};
438             }
439              
440 0           $self->path($servers{$self->{'_server'}}->{lc $post_data{'TxType'}});
441 0           my ($page, $response, %headers) =
442             post_https(
443             $self->server,
444             $self->port,
445             $self->path,
446             undef,
447             make_form(
448             %post_data
449             )
450             );
451 0 0         unless ($page) {
452 0           $self->error_message($status->{TIMEOUT});
453 0           $self->is_success(0);
454 0           return;
455             }
456              
457 0           my $rf = $self->_parse_response($page);
458              
459 0 0         if($ENV{'SAGEPAY_DEBUG'}) {
460 0           warn "Authorization:";
461 0           Dwarn $rf;
462             }
463              
464 0           $self->server_response($rf);
465 0           $self->result_code($rf->{'Status'});
466 0           $self->authorization($rf->{'VPSTxId'});
467 0 0         $self->authorization_code($rf->{'TxAuthNo'}) if defined $rf->{'TxAuthNo'};
468 0 0         $self->authentication_key($rf->{'SecurityKey'}) if defined $rf->{'SecurityKey'};
469 0 0         unless($self->is_success($rf->{'Status'} eq SAGEPAY_STATUS_OK ? 1 : 0)) {
    0          
470 0 0         if($ENV{'SAGEPAY_DEBUG_ERROR_ONLY'}) {
471 0           Dwarn $rf;
472             }
473 0           my $code = substr $rf->{'StatusDetail'}, 0 ,4;
474 0           $self->error_code($code);
475 0   0       $self->error_message($status->{$code} || $status->{UNKNOWN});
476             }
477              
478             }
479              
480             sub sanitised_content {
481 0     0 0   my ($self,$content) = @_;
482 0           my %content = $self->content();
483              
484             {
485 1     1   7 no warnings 'uninitialized';
  1         2  
  1         862  
  0            
486              
487 0           $content{'expiration'} =~ s#/##g;
488 0 0         $content{'startdate'} =~ s#/##g if $content{'startdate'};
489              
490 0   0       $content{'card_name'} =
491             $content{'name_on_card'}
492             || $content{'first_name'} . ' ' . ($content{'last_name'}||"");
493 0 0 0       $content{'customer_name'} =
494             $content{'customer_name'}
495             || $content{'first_name'} ?
496             $content{'first_name'} . ' ' . $content{'last_name'} : undef;
497             # new protocol requires first and last name - do some people even have both!?
498 0   0       $content{'last_name'} ||= $content{'first_name'};
499 0           $content{'action'} = $action{ lc $content{'action'} };
500 0           $content{'card_type'} = $card_type{lc $content{'type'}};
501 0 0         $content{'amount'} = format_amount($content{'amount'})
502             if $content{'amount'};
503             }
504              
505 0           return \%content;
506             }
507              
508             sub post_request {
509 0     0 0   my ($self,$type,$data) = @_;
510              
511 0           $self->path($servers{$self->{'_server'}}->{$type});
512              
513 0 0         if($ENV{'SAGEPAY_DEBUG'}) {
514 0           warn sprintf("Posting to %s:%s%s",
515             $self->server, $self->port, $self->path);
516 0           Dwarn $data;
517             }
518              
519 0           my ($page, $response, %headers) = post_https(
520             $self->server,
521             $self->port,
522             $self->path,
523             undef,
524             make_form( %$data )
525             );
526              
527 0 0         unless ($page) {
528 0           $self->error_message($status->{TIMEOUT});
529 0           $self->is_success(0);
530 0           return;
531             }
532              
533 0           my $rf = $self->_parse_response($page);
534 0           $self->server_response($rf);
535 0           $self->result_code($rf->{'Status'});
536 0           $self->authorization($rf->{'VPSTxId'});
537 0           $self->authentication_key($rf->{'SecurityKey'});
538             }
539              
540             sub submit {
541 0     0 1   my $self = shift;
542 0           $self->initialise;
543 0           my $content = $self->sanitised_content;
544              
545 0           my %field_mapping = (
546             VpsProtocol => \($self->protocol),
547             Vendor => \($self->vendor),
548             TxType => 'action',
549             VendorTxCode=> 'invoice_number',
550             Description => 'description',
551             Currency => \($self->currency),
552             CardHolder => 'card_name',
553             CardNumber => 'card_number',
554             CV2 => 'cvv2',
555             ExpiryDate => 'expiration',
556             StartDate => 'startdate',
557             Amount => 'amount',
558             IssueNumber => 'issue_number',
559             CardType => 'card_type',
560             ApplyAVSCV2 => 0,
561             BillingSurname => 'last_name',
562             BillingFirstnames => 'first_name',
563             BillingAddress1 => 'address',
564             BillingPostCode => 'zip',
565             BillingCity => 'city',
566             BillingState => 'state',
567             BillingCountry => 'country',
568              
569             DeliverySurname => 'last_name',
570             DeliveryFirstnames => 'first_name',
571             DeliveryAddress1 => 'address',
572             DeliveryPostCode => 'zip',
573             DeliveryCity => 'city',
574             DeliveryCountry => 'country',
575             DeliveryState => 'state',
576              
577             CustomerName => 'customer_name',
578             ContactNumber => 'telephone',
579             ContactFax => 'fax',
580             CustomerEmail => 'email',
581              
582             PayPalCallbackURL => 'paypal_callback_uri',
583             BillingAgreement => 'billing_agreement',
584             );
585              
586 0           my %post_data = $self->do_remap($content,%field_mapping);
587              
588 0 0         if($ENV{'SAGEPAY_DEBUG'}) {
589 0           warn "Authentication Form:";
590 0           Dwarn {
591             %post_data,
592             CV2 => "XXX",
593             CardNumber => "XXXX XXXX XXXX XXXX"
594             };
595             }
596              
597 0 0         $self->path($servers{$self->{'_server'}}->{'authorise'})
598             if $post_data{'TxType'} eq 'AUTHORISE';
599 0           my ($page, $response, %headers) = post_https(
600             $self->server,
601             $self->port,
602             $self->path,
603             undef,
604             make_form(
605             %post_data
606             )
607             );
608 0 0         unless ($page) {
609 0           $self->error_message($status->{TIMEOUT});
610 0           $self->is_success(0);
611 0           return;
612             }
613              
614 0           my $rf = $self->_parse_response($page);
615 0           $self->server_response($rf);
616 0           $self->result_code($rf->{'Status'});
617 0           $self->authorization($rf->{'VPSTxId'});
618 0           $self->authentication_key($rf->{'SecurityKey'});
619 0           $self->authorization_code($rf->{'TxAuthNo'});
620              
621 0 0 0       if (
622             ($self->result_code eq SAGEPAY_STATUS_3DSECURE) &&
623             ($rf->{'3DSecureStatus'} eq SAGEPAY_STATUS_OK)
624             ) {
625 0           $self->require_3d(1);
626 0           $self->forward_to($rf->{'ACSURL'});
627 0           $self->pareq($rf->{'PAReq'});
628 0           $self->cross_reference($rf->{'MD'});
629             }
630              
631 0 0         if($self->result_code eq SAGEPAY_STATUS_PAYPAL_REDIRECT) {
632 0           $self->require_paypal(1);
633 0           $self->forward_to($rf->{'PayPalRedirectURL'});
634             }
635              
636 0           $self->cvv2_response($rf->{'CV2Result'});
637 0           $self->postcode_response($rf->{'PostCodeResult'});
638 0           $self->address_response($rf->{'AddressResult'});
639              
640 0 0         if($ENV{'SAGEPAY_DEBUG'}) {
641 0           warn "Authentication Response:";
642 0           Dwarn $rf;
643             }
644              
645 0 0 0       unless($self->is_success(
    0          
646             $rf->{'Status'} eq SAGEPAY_STATUS_3DSECURE ||
647             $rf->{'Status'} eq SAGEPAY_STATUS_PAYPAL_REDIRECT ||
648             $rf->{'Status'} eq SAGEPAY_STATUS_OK ||
649             $rf->{'Status'} eq SAGEPAY_STATUS_AUTHENTICATED ||
650             $rf->{'Status'} eq SAGEPAY_STATUS_REGISTERED
651             ? 1 : 0)) {
652 0           my $code = substr $rf->{'StatusDetail'}, 0 ,4;
653              
654 0 0         if($ENV{'SAGEPAY_DEBUG_ERROR_ONLY'}) {
655 0           Dwarn $rf;
656             }
657              
658 0           $self->error_code($code);
659 0   0       $self->error_message($status->{$code} || $status->{UNKNOWN});
660             }
661             }
662              
663             sub _parse_response {
664 0     0     my ($self,$response) = @_;
665 0           my $crlfpattern = qq{[\015\012\n\r]};
666 0           my %values = map { split(/=/,$_, 2) } grep(/=.+$/,split (/$crlfpattern/,$response));
  0            
667 0           return \%values;
668             }
669              
670             =head1 NAME
671              
672             Business::OnlinePayment::SagePay - SagePay backend for Business::OnlinePayment
673              
674             =head1 VERSION
675              
676             version 0.17
677              
678             =head1 SYNOPSIS
679              
680             use Business::OnlinePayment;
681              
682             my $tx = Business::OnlinePayment->new(
683             "SagePay",
684             "username" => "abc",
685             );
686              
687             $tx->content(
688             type => 'VISA',
689             login => 'testdrive',
690             password => '',
691             action => 'Normal Authorization',
692             description => 'Business::OnlinePayment test',
693             amount => '49.95',
694             invoice_number => '100100',
695             customer_id => 'jsk',
696             first_name => 'Jason',
697             last_name => 'Kohles',
698             address => '123 Anystreet',
699             city => 'Anywhere',
700             state => 'UT',
701             zip => '84058',
702             card_number => '4007000000027',
703             expiration => '09/02',
704             cvv2 => '1234', #optional
705             referer => 'http://valid.referer.url/',
706             );
707              
708             $tx->set_server('simulator'); #live, simulator or test(default)
709              
710             $tx->submit();
711              
712             if ($tx->is_success) {
713             print "Card processed successfully: " . $tx->authorization . "\n";
714             } else {
715             print "Card was rejected: " . $tx->error_message . "\n";
716             }
717              
718             =cut
719              
720             =head1 DESCRIPTION
721              
722             This perl module provides integration with the SagePay VSP payments system.
723              
724             =head1 PAYPAL
725              
726             If the card type is set to C then when submitted the transaction will use SagePay's PayPal integration (see
727             L). The URI to redirect the customer to after the page at
728             PayPal has been completed should be set in the C atrribute in C.
729              
730             If C then user should be redirected to
731             the uri provided in method C.
732              
733             =head1 METHODS
734              
735             =head2 submit_paypal
736              
737             This method submits a COMPLETE transaction to SagePay to complete a PayPal transaction. C
738             amd C should be set in C.
739              
740             =head2 SAGEPAY_STATUS_PAYPAL_REDIRECT
741              
742             if ($tx->status_code eq $tx->SAGEPAY_STATUS_PAYPAL_REDIRECT) {
743             # redirct to $tx->forward_to ...
744             }
745              
746             Status to check if transaction result_code requires a redirect to PayPal. Can be called as a class or
747             object method for convenience.
748              
749             =head1 BUGS
750              
751             Please report any bugs or feature requests to C, or through
752             the web interface at L. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.
753              
754             =head1 SUPPORT
755              
756             You can find documentation for this module with the perldoc command.
757              
758             perldoc Business::OnlinePayment::SagePay
759              
760             You can also look for information at:
761              
762             =over 4
763              
764             =item * RT: CPAN's request tracker
765              
766             L
767              
768             =item * AnnoCPAN: Annotated CPAN documentation
769              
770             L
771              
772             =item * CPAN Ratings
773              
774             L
775              
776             =item * Search CPAN
777              
778             L
779              
780             =back
781              
782             =head1 SEE ALSO
783              
784             L
785              
786             =head1 AUTHOR
787              
788             purge: Simon Elliott
789              
790             cubabit: Pete Smith
791              
792             =head1 ACKNOWLEDGEMENTS
793              
794             To Airspace Software Ltd , for the sponsorship.
795              
796             To Wallace Reis, for comments and patches.
797              
798             =head1 LICENSE
799              
800             This library is free software under the same license as perl itself.
801              
802             =cut
803              
804             1;