File Coverage

blib/lib/Business/OnlinePayment/SagePay.pm
Criterion Covered Total %
statement 30 259 11.5
branch 0 104 0.0
condition 0 30 0.0
subroutine 10 25 40.0
pod 2 14 14.2
total 42 432 9.7


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.16';
4 1     1   15205 use strict;
  1         2  
  1         34  
5 1     1   4 use warnings;
  1         1  
  1         27  
6              
7 1     1   4 use base qw(Business::OnlinePayment);
  1         5  
  1         561  
8              
9 1     1   2493 use Carp;
  1         2  
  1         45  
10 1     1   574 use Net::SSLeay qw(make_form post_https);
  1         12254  
  1         373  
11 1     1   4306 use Devel::Dwarn;
  1         8839  
  1         6  
12 1     1   137 use Exporter 'import';
  1         2  
  1         33  
13              
14             use constant {
15 1         389 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   4 };
  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         1501  
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         unless($self->is_success($rf->{'Status'} eq SAGEPAY_STATUS_OK ? 1 : 0)) {
    0          
468 0 0         if($ENV{'SAGEPAY_DEBUG_ERROR_ONLY'}) {
469 0           Dwarn $rf;
470             }
471 0           my $code = substr $rf->{'StatusDetail'}, 0 ,4;
472 0           $self->error_code($code);
473 0   0       $self->error_message($status->{$code} || $status->{UNKNOWN});
474             }
475              
476             }
477              
478             sub sanitised_content {
479 0     0 0   my ($self,$content) = @_;
480 0           my %content = $self->content();
481              
482             {
483 1     1   8 no warnings 'uninitialized';
  1         2  
  1         936  
  0            
484              
485 0           $content{'expiration'} =~ s#/##g;
486 0 0         $content{'startdate'} =~ s#/##g if $content{'startdate'};
487              
488 0   0       $content{'card_name'} =
489             $content{'name_on_card'}
490             || $content{'first_name'} . ' ' . ($content{'last_name'}||"");
491 0 0 0       $content{'customer_name'} =
492             $content{'customer_name'}
493             || $content{'first_name'} ?
494             $content{'first_name'} . ' ' . $content{'last_name'} : undef;
495             # new protocol requires first and last name - do some people even have both!?
496 0   0       $content{'last_name'} ||= $content{'first_name'};
497 0           $content{'action'} = $action{ lc $content{'action'} };
498 0           $content{'card_type'} = $card_type{lc $content{'type'}};
499 0 0         $content{'amount'} = format_amount($content{'amount'})
500             if $content{'amount'};
501             }
502              
503 0           return \%content;
504             }
505              
506             sub post_request {
507 0     0 0   my ($self,$type,$data) = @_;
508              
509 0           $self->path($servers{$self->{'_server'}}->{$type});
510              
511 0 0         if($ENV{'SAGEPAY_DEBUG'}) {
512 0           warn sprintf("Posting to %s:%s%s",
513             $self->server, $self->port, $self->path);
514 0           Dwarn $data;
515             }
516              
517 0           my ($page, $response, %headers) = post_https(
518             $self->server,
519             $self->port,
520             $self->path,
521             undef,
522             make_form( %$data )
523             );
524              
525 0 0         unless ($page) {
526 0           $self->error_message($status->{TIMEOUT});
527 0           $self->is_success(0);
528 0           return;
529             }
530              
531 0           my $rf = $self->_parse_response($page);
532 0           $self->server_response($rf);
533 0           $self->result_code($rf->{'Status'});
534 0           $self->authorization($rf->{'VPSTxId'});
535 0           $self->authentication_key($rf->{'SecurityKey'});
536             }
537              
538             sub submit {
539 0     0 1   my $self = shift;
540 0           $self->initialise;
541 0           my $content = $self->sanitised_content;
542              
543 0           my %field_mapping = (
544             VpsProtocol => \($self->protocol),
545             Vendor => \($self->vendor),
546             TxType => 'action',
547             VendorTxCode=> 'invoice_number',
548             Description => 'description',
549             Currency => \($self->currency),
550             CardHolder => 'card_name',
551             CardNumber => 'card_number',
552             CV2 => 'cvv2',
553             ExpiryDate => 'expiration',
554             StartDate => 'startdate',
555             Amount => 'amount',
556             IssueNumber => 'issue_number',
557             CardType => 'card_type',
558             ApplyAVSCV2 => 0,
559             BillingSurname => 'last_name',
560             BillingFirstnames => 'first_name',
561             BillingAddress1 => 'address',
562             BillingPostCode => 'zip',
563             BillingCity => 'city',
564             BillingState => 'state',
565             BillingCountry => 'country',
566              
567             DeliverySurname => 'last_name',
568             DeliveryFirstnames => 'first_name',
569             DeliveryAddress1 => 'address',
570             DeliveryPostCode => 'zip',
571             DeliveryCity => 'city',
572             DeliveryCountry => 'country',
573             DeliveryState => 'state',
574              
575             CustomerName => 'customer_name',
576             ContactNumber => 'telephone',
577             ContactFax => 'fax',
578             CustomerEmail => 'email',
579              
580             PayPalCallbackURL => 'paypal_callback_uri',
581             );
582              
583 0           my %post_data = $self->do_remap($content,%field_mapping);
584              
585 0 0         if($ENV{'SAGEPAY_DEBUG'}) {
586 0           warn "Authentication Form:";
587 0           Dwarn {
588             %post_data,
589             CV2 => "XXX",
590             CardNumber => "XXXX XXXX XXXX XXXX"
591             };
592             }
593              
594 0 0         $self->path($servers{$self->{'_server'}}->{'authorise'})
595             if $post_data{'TxType'} eq 'AUTHORISE';
596 0           my ($page, $response, %headers) = post_https(
597             $self->server,
598             $self->port,
599             $self->path,
600             undef,
601             make_form(
602             %post_data
603             )
604             );
605 0 0         unless ($page) {
606 0           $self->error_message($status->{TIMEOUT});
607 0           $self->is_success(0);
608 0           return;
609             }
610              
611 0           my $rf = $self->_parse_response($page);
612 0           $self->server_response($rf);
613 0           $self->result_code($rf->{'Status'});
614 0           $self->authorization($rf->{'VPSTxId'});
615 0           $self->authentication_key($rf->{'SecurityKey'});
616 0           $self->authorization_code($rf->{'TxAuthNo'});
617              
618 0 0 0       if (
619             ($self->result_code eq SAGEPAY_STATUS_3DSECURE) &&
620             ($rf->{'3DSecureStatus'} eq SAGEPAY_STATUS_OK)
621             ) {
622 0           $self->require_3d(1);
623 0           $self->forward_to($rf->{'ACSURL'});
624 0           $self->pareq($rf->{'PAReq'});
625 0           $self->cross_reference($rf->{'MD'});
626             }
627              
628 0 0         if($self->result_code eq SAGEPAY_STATUS_PAYPAL_REDIRECT) {
629 0           $self->require_paypal(1);
630 0           $self->forward_to($rf->{'PayPalRedirectURL'});
631             }
632              
633 0           $self->cvv2_response($rf->{'CV2Result'});
634 0           $self->postcode_response($rf->{'PostCodeResult'});
635 0           $self->address_response($rf->{'AddressResult'});
636              
637 0 0         if($ENV{'SAGEPAY_DEBUG'}) {
638 0           warn "Authentication Response:";
639 0           Dwarn $rf;
640             }
641              
642 0 0 0       unless($self->is_success(
    0          
643             $rf->{'Status'} eq SAGEPAY_STATUS_3DSECURE ||
644             $rf->{'Status'} eq SAGEPAY_STATUS_PAYPAL_REDIRECT ||
645             $rf->{'Status'} eq SAGEPAY_STATUS_OK ||
646             $rf->{'Status'} eq SAGEPAY_STATUS_AUTHENTICATED ||
647             $rf->{'Status'} eq SAGEPAY_STATUS_REGISTERED
648             ? 1 : 0)) {
649 0           my $code = substr $rf->{'StatusDetail'}, 0 ,4;
650              
651 0 0         if($ENV{'SAGEPAY_DEBUG_ERROR_ONLY'}) {
652 0           Dwarn $rf;
653             }
654              
655 0           $self->error_code($code);
656 0   0       $self->error_message($status->{$code} || $status->{UNKNOWN});
657             }
658             }
659              
660             sub _parse_response {
661 0     0     my ($self,$response) = @_;
662 0           my $crlfpattern = qq{[\015\012\n\r]};
663 0           my %values = map { split(/=/,$_, 2) } grep(/=.+$/,split (/$crlfpattern/,$response));
  0            
664 0           return \%values;
665             }
666              
667             =head1 NAME
668              
669             Business::OnlinePayment::SagePay - SagePay backend for Business::OnlinePayment
670              
671             =head1 VERSION
672              
673             version 0.16
674              
675             =head1 SYNOPSIS
676              
677             use Business::OnlinePayment;
678              
679             my $tx = Business::OnlinePayment->new(
680             "SagePay",
681             "username" => "abc",
682             );
683              
684             $tx->content(
685             type => 'VISA',
686             login => 'testdrive',
687             password => '',
688             action => 'Normal Authorization',
689             description => 'Business::OnlinePayment test',
690             amount => '49.95',
691             invoice_number => '100100',
692             customer_id => 'jsk',
693             first_name => 'Jason',
694             last_name => 'Kohles',
695             address => '123 Anystreet',
696             city => 'Anywhere',
697             state => 'UT',
698             zip => '84058',
699             card_number => '4007000000027',
700             expiration => '09/02',
701             cvv2 => '1234', #optional
702             referer => 'http://valid.referer.url/',
703             );
704              
705             $tx->set_server('simulator'); #live, simulator or test(default)
706              
707             $tx->submit();
708              
709             if ($tx->is_success) {
710             print "Card processed successfully: " . $tx->authorization . "\n";
711             } else {
712             print "Card was rejected: " . $tx->error_message . "\n";
713             }
714              
715             =cut
716              
717             =head1 DESCRIPTION
718              
719             This perl module provides integration with the SagePay VSP payments system.
720              
721             =head1 PAYPAL
722              
723             If the card type is set to C then when submitted the transaction will use SagePay's PayPal integration (see
724             L). The URI to redirect the customer to after the page at
725             PayPal has been completed should be set in the C atrribute in C.
726              
727             If C then user should be redirected to
728             the uri provided in method C.
729              
730             =head1 METHODS
731              
732             =head2 submit_paypal
733              
734             This method submits a COMPLETE transaction to SagePay to complete a PayPal transaction. C
735             amd C should be set in C.
736              
737             =head2 SAGEPAY_STATUS_PAYPAL_REDIRECT
738              
739             if ($tx->status_code eq $tx->SAGEPAY_STATUS_PAYPAL_REDIRECT) {
740             # redirct to $tx->forward_to ...
741             }
742              
743             Status to check if transaction result_code requires a redirect to PayPal. Can be called as a class or
744             object method for convenience.
745              
746             =head1 BUGS
747              
748             Please report any bugs or feature requests to C, or through
749             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.
750              
751             =head1 SUPPORT
752              
753             You can find documentation for this module with the perldoc command.
754              
755             perldoc Business::OnlinePayment::SagePay
756              
757             You can also look for information at:
758              
759             =over 4
760              
761             =item * RT: CPAN's request tracker
762              
763             L
764              
765             =item * AnnoCPAN: Annotated CPAN documentation
766              
767             L
768              
769             =item * CPAN Ratings
770              
771             L
772              
773             =item * Search CPAN
774              
775             L
776              
777             =back
778              
779             =head1 SEE ALSO
780              
781             L
782              
783             =head1 AUTHOR
784              
785             purge: Simon Elliott
786              
787             cubabit: Pete Smith
788              
789             =head1 ACKNOWLEDGEMENTS
790              
791             To Airspace Software Ltd , for the sponsorship.
792              
793             To Wallace Reis, for comments and patches.
794              
795             =head1 LICENSE
796              
797             This library is free software under the same license as perl itself.
798              
799             =cut
800              
801             1;