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