File Coverage

blib/lib/WebService/PayPal/PaymentsAdvanced.pm
Criterion Covered Total %
statement 218 220 99.0
branch 24 30 80.0
condition 6 11 54.5
subroutine 58 58 100.0
pod 12 12 100.0
total 318 331 96.0


line stmt bran cond sub pod time code
1             package WebService::PayPal::PaymentsAdvanced;
2              
3 6     6   4335266 use Moo;
  6         11078  
  6         52  
4              
5 6     6   3889 use namespace::autoclean;
  6         11775  
  6         59  
6              
7             our $VERSION = '0.000026';
8              
9 6     6   684 use feature qw( say state );
  6         22  
  6         778  
10              
11 6     6   1559 use Data::GUID;
  6         27344  
  6         56  
12 6     6   1986 use List::AllUtils qw( any );
  6         10876  
  6         384  
13 6     6   761 use LWP::UserAgent;
  6         46011  
  6         176  
14 6     6   478 use MooX::StrictConstructor;
  6         13982  
  6         75  
15 6     6   32277 use Type::Params qw( compile );
  6         155419  
  6         62  
16 6     6   2003 use Types::Common::Numeric qw( PositiveNum );
  6         12847  
  6         54  
17 6     6   4517 use Types::Common::String qw( NonEmptyStr );
  6         113995  
  6         73  
18 6         40 use Types::Standard qw(
19             ArrayRef
20             Bool
21             CodeRef
22             Defined
23             HashRef
24             InstanceOf
25             Int
26             Num
27             Optional
28 6     6   3071 );
  6         16  
29 6     6   12409 use Types::URI qw( Uri );
  6         509161  
  6         77  
30 6     6   2435 use URI;
  6         16  
  6         161  
31 6     6   35 use URI::FromHash qw( uri uri_object );
  6         19  
  6         325  
32 6     6   537 use URI::QueryParam;
  6         901  
  6         212  
33              
34             #<<< don't perltidy
35 6     6   3052 use WebService::PayPal::PaymentsAdvanced::Error::Generic;
  6         19  
  6         212  
36 6     6   2994 use WebService::PayPal::PaymentsAdvanced::Error::HostedForm;
  6         21  
  6         220  
37 6     6   2919 use WebService::PayPal::PaymentsAdvanced::Response;
  6         24  
  6         253  
38 6     6   2872 use WebService::PayPal::PaymentsAdvanced::Response::Authorization;
  6         32  
  6         274  
39 6     6   3295 use WebService::PayPal::PaymentsAdvanced::Response::Authorization::CreditCard;
  6         23  
  6         222  
40 6     6   3032 use WebService::PayPal::PaymentsAdvanced::Response::Authorization::PayPal;
  6         28  
  6         277  
41 6     6   3079 use WebService::PayPal::PaymentsAdvanced::Response::Capture;
  6         22  
  6         209  
42 6     6   2836 use WebService::PayPal::PaymentsAdvanced::Response::Credit;
  6         21  
  6         206  
43 6     6   2978 use WebService::PayPal::PaymentsAdvanced::Response::FromHTTP;
  6         19  
  6         226  
44 6     6   3209 use WebService::PayPal::PaymentsAdvanced::Response::FromRedirect;
  6         20  
  6         228  
45 6     6   3006 use WebService::PayPal::PaymentsAdvanced::Response::FromSilentPOST;
  6         26  
  6         289  
46 6     6   3037 use WebService::PayPal::PaymentsAdvanced::Response::FromSilentPOST::CreditCard;
  6         21  
  6         256  
47 6     6   3097 use WebService::PayPal::PaymentsAdvanced::Response::FromSilentPOST::PayPal;
  6         23  
  6         212  
48 6     6   2889 use WebService::PayPal::PaymentsAdvanced::Response::Inquiry;
  6         19  
  6         226  
49 6     6   2965 use WebService::PayPal::PaymentsAdvanced::Response::Inquiry::CreditCard;
  6         23  
  6         220  
50 6     6   3044 use WebService::PayPal::PaymentsAdvanced::Response::Inquiry::PayPal;
  6         21  
  6         214  
51 6     6   2853 use WebService::PayPal::PaymentsAdvanced::Response::Sale;
  6         34  
  6         228  
52 6     6   3095 use WebService::PayPal::PaymentsAdvanced::Response::Sale::CreditCard;
  6         32  
  6         278  
53 6     6   3000 use WebService::PayPal::PaymentsAdvanced::Response::Sale::PayPal;
  6         28  
  6         208  
54 6     6   3071 use WebService::PayPal::PaymentsAdvanced::Response::SecureToken;
  6         27  
  6         51  
55 6     6   3008 use WebService::PayPal::PaymentsAdvanced::Response::Void;
  6         22  
  6         45  
56             #>>>
57              
58             has nonfatal_result_codes => (
59             is => 'ro',
60             isa => ArrayRef [Int],
61             default => sub { [0] },
62             );
63              
64             has partner => (
65             is => 'ro',
66             isa => NonEmptyStr,
67             required => 1,
68             default => 'PayPal',
69             );
70              
71             has password => (
72             is => 'ro',
73             isa => Defined,
74             required => 1,
75             );
76              
77             has payflow_pro_uri => (
78             is => 'lazy',
79             isa => Uri,
80             coerce => 1,
81             );
82              
83             has payflow_link_uri => (
84             is => 'lazy',
85             isa => Uri,
86             coerce => 1,
87             );
88              
89             has production_mode => (
90             is => 'ro',
91             isa => Bool,
92             default => 0,
93             );
94              
95             has user => (
96             is => 'ro',
97             isa => Defined,
98             required => 1,
99             );
100              
101             has validate_hosted_form_uri => (
102             is => 'ro',
103             isa => Bool,
104             default => 1,
105             );
106              
107             has vendor => (
108             is => 'ro',
109             isa => NonEmptyStr,
110             required => 1,
111             );
112              
113             has verbose => (
114             is => 'ro',
115             isa => Bool,
116             default => 1,
117             );
118              
119             with(
120             'WebService::PayPal::PaymentsAdvanced::Role::HasUA',
121             'WebService::PayPal::PaymentsAdvanced::Role::ClassFor'
122             );
123              
124             sub _build_payflow_pro_uri {
125 13     13   322 my $self = shift;
126              
127 13 100       149 return uri_object(
128             scheme => 'https',
129             host => $self->production_mode
130             ? 'payflowpro.paypal.com'
131             : 'pilot-payflowpro.paypal.com'
132             );
133             }
134              
135             sub _build_payflow_link_uri {
136 4     4   111 my $self = shift;
137              
138 4 100       43 return uri_object(
139             scheme => 'https',
140             host => $self->production_mode
141             ? 'payflowlink.paypal.com'
142             : 'pilot-payflowlink.paypal.com'
143             );
144             }
145              
146             sub capture_delayed_transaction {
147 2     2 1 715 my $self = shift;
148              
149 2         11 state $check = compile( NonEmptyStr, Optional [PositiveNum] );
150 2         2718 my ( $origid, $amt ) = $check->(@_);
151              
152             ## no critic (ValuesAndExpressions::ProhibitCommaSeparatedStatements)
153 2 100       137 return $self->post(
154             {
155             $amt ? ( AMT => $amt ) : (),
156             ORIGID => $origid,
157             TRXTYPE => 'D',
158             }
159             );
160             ## use critic
161             }
162              
163             sub _response_for {
164 54     54   120 my $self = shift;
165 54         111 my $class_suffix = shift;
166 54         170 my %args = @_;
167              
168 54         218 $self->_class_for($class_suffix)->new(
169             nonfatal_result_codes => $self->nonfatal_result_codes,
170             %args,
171             );
172             }
173              
174             sub create_secure_token {
175 4     4 1 2517 my $self = shift;
176              
177 4         23 state $check = compile( HashRef, Optional [HashRef] );
178 4         4490 my ( $args, $options ) = $check->(@_);
179 4   50     201 $options ||= {};
180              
181 4         20 my $post = $self->_force_upper_case($args);
182              
183 4         18 $post->{CREATESECURETOKEN} = 'Y';
184 4   66     39 $post->{SECURETOKENID} ||= Data::GUID->new->as_string;
185              
186 4         540 my $res = $self->post( $post, $options );
187              
188 2         15 $self->_validate_secure_token_id( $res, $post->{SECURETOKENID} );
189              
190 2         121 return $res;
191             }
192              
193             sub get_response_from_redirect {
194 2     2 1 1153 my $self = shift;
195              
196 2         9 state $check = compile(HashRef);
197 2         948 my ($args) = $check->(@_);
198              
199 2         27 my $response = $self->_class_for('Response::FromRedirect')->new($args);
200              
201 2         1636 return $self->_response_for( 'Response', params => $response->params );
202             }
203              
204             sub get_response_from_silent_post {
205 18     18 1 2412 my $self = shift;
206              
207 18         51 state $check = compile(HashRef);
208 18         3551 my ($args) = $check->(@_);
209              
210             # If the RESPMSG param is missing, then this may just be a garbage, random
211             # POST from a bot.
212              
213 18 100       220 unless ( $args->{params}->{RESPMSG} ) {
214             WebService::PayPal::PaymentsAdvanced::Error::Generic->throw(
215             message => 'Bad params supplied from silent POST',
216             params => $args->{params},
217 1         20 );
218             }
219              
220             # First we create a SilentPOST response, which may or may not validate the
221             # IP. If there's no exception we query the object to find out if this was a
222             # PayPal or CreditCard transaction and then return the appropriate class.
223             # IPs will only be validate once as the PayPal/CreditCard object
224             # instantiation will not provide an IP address.
225              
226 17         46 my $class_suffix = 'Response::FromSilentPOST';
227 17         34 my $response = $self->_response_for( $class_suffix, %{$args} );
  17         71  
228              
229 16 100       689 $class_suffix
230             .= '::'
231             . ( $response->is_credit_card_transaction ? 'CreditCard' : 'PayPal' );
232              
233 16         476 return $self->_response_for( $class_suffix, params => $response->params );
234             }
235              
236             sub inquiry_transaction {
237 1     1 1 270 my $self = shift;
238              
239 1         6 state $check = compile(HashRef);
240 1         978 my ($args) = $check->(@_);
241 1         11 $args->{TRXTYPE} = 'I';
242              
243 1         5 return $self->post($args);
244             }
245              
246             sub post {
247 14     14 1 51 my $self = shift;
248              
249 14         36 state $check = compile( HashRef, Optional [HashRef] );
250 14         3438 my ( $post, $options ) = $check->(@_);
251              
252 14         357 $post = $self->_force_upper_case($post);
253 14 50       95 $post->{VERBOSITY} = 'HIGH' if $self->verbose;
254              
255 14         53 my $content = join '&', $self->_encode_credentials,
256             $self->_pseudo_encode_args($post);
257              
258 14         379 my $http_response
259             = $self->ua->post( $self->payflow_pro_uri, Content => $content );
260              
261 14         2481149 my $params = $self->_class_for('Response::FromHTTP')->new(
262             http_response => $http_response,
263             request_uri => $self->payflow_pro_uri,
264             )->params;
265              
266 14 100 66     4040 if ( $post->{CREATESECURETOKEN} && $post->{CREATESECURETOKEN} eq 'Y' ) {
267             return $self->_response_for(
268             'Response::SecureToken',
269             params => $params,
270             payflow_link_uri => $self->payflow_link_uri,
271             ua => $self->ua,
272             validate_hosted_form_uri => $self->validate_hosted_form_uri,
273 4 50       93 %{ $options || {} },
  4         1647  
274             );
275             }
276              
277 10         74 my %class_for_type = (
278             A => 'Response::Authorization',
279             C => 'Response::Credit',
280             D => 'Response::Capture',
281             I => 'Response::Inquiry',
282             S => 'Response::Sale',
283             V => 'Response::Void',
284             );
285              
286 10         31 my $type = $post->{TRXTYPE};
287 10         21 my $response_class_suffix = 'Response';
288 10 50 33     79 if ( $type && exists $class_for_type{$type} ) {
289              
290 10         28 $response_class_suffix = $class_for_type{$type};
291              
292             # Get more specific response classes for CC and PayPal txns.
293 10 100   24   84 unless ( any { $type eq $_ } ( 'C', 'D', 'V' ) ) {
  24         71  
294 5         23 my $response = $self->_response_for(
295             $response_class_suffix,
296             params => $params
297             );
298              
299 5 100       127 $response_class_suffix = sprintf(
300             '%s::%s', $response_class_suffix,
301             $response->is_credit_card_transaction
302             ? 'CreditCard'
303             : 'PayPal'
304             );
305             }
306             }
307              
308 10         259 return $self->_response_for( $response_class_suffix, params => $params );
309             }
310              
311             sub refund_transaction {
312 2     2 1 614 my $self = shift;
313 2         11 state $check = compile( NonEmptyStr, Optional [NonEmptyStr] );
314 2         2633 my ( $origid, $amount ) = $check->(@_);
315              
316 2 100       132 return $self->post(
317             {
318             TRXTYPE => 'C',
319             ORIGID => $origid,
320             $amount ? ( AMT => $amount ) : ()
321             }
322             );
323             }
324              
325             sub auth_from_credit_card_reference_transaction {
326 1     1 1 264 my $self = shift;
327 1         5 return $self->_credit_card_reference_transaction( 'A', @_ );
328             }
329              
330             sub sale_from_credit_card_reference_transaction {
331 1     1 1 293 my $self = shift;
332 1         5 return $self->_credit_card_reference_transaction( 'S', @_ );
333             }
334              
335             sub _credit_card_reference_transaction {
336 2     2   6 my $self = shift;
337 2         9 state $check
338             = compile( NonEmptyStr, NonEmptyStr, Num, Optional [HashRef] );
339 2         2958 my ( $type, $origid, $amount, $extra ) = $check->(@_);
340              
341             return $self->post(
342             {
343             AMT => $amount,
344             ORIGID => $origid,
345             TENDER => 'C',
346             TRXTYPE => $type,
347 2 50       144 $extra ? ( %{$extra} ) : (),
  2         16  
348             }
349             );
350             }
351              
352             sub auth_from_paypal_reference_transaction {
353 1     1 1 290 my $self = shift;
354 1         6 return $self->_paypal_reference_transaction( 'A', @_ );
355             }
356              
357             sub sale_from_paypal_reference_transaction {
358 1     1 1 420 my $self = shift;
359 1         8 return $self->_paypal_reference_transaction( 'S', @_ );
360             }
361              
362             sub _paypal_reference_transaction {
363 2     2   6 my $self = shift;
364 2         10 state $check = compile( NonEmptyStr, NonEmptyStr, Num, NonEmptyStr,
365             Optional [HashRef]
366             );
367 2         3517 my ( $type, $baid, $amount, $currency, $extra ) = $check->(@_);
368              
369             return $self->post(
370             {
371             ACTION => 'D',
372             AMT => $amount,
373             BAID => $baid,
374             CURRENCY => $currency,
375             TENDER => 'P',
376             TRXTYPE => $type,
377 2 50       174 $extra ? ( %{$extra} ) : (),
  0         0  
378             }
379             );
380             }
381              
382             sub void_transaction {
383 1     1 1 262 my $self = shift;
384              
385 1         7 state $check = compile(NonEmptyStr);
386 1         1204 my ($pnref) = $check->(@_);
387              
388 1         29 return $self->post( { TRXTYPE => 'V', ORIGID => $pnref } );
389             }
390              
391             sub _validate_secure_token_id {
392 2     2   5 my $self = shift;
393 2         6 my $res = shift;
394 2         5 my $token_id = shift;
395              
396             # This should only happen if bad actors are involved.
397 2 50       50 if ( $res->secure_token_id ne $token_id ) {
398 0         0 WebService::PayPal::PaymentsAdvanced::Error::Generic->throw(
399             message => sprintf(
400             'Secure token ids do not match. Yours: %s. From response: %s.',
401             $token_id, $res->secure_token_id
402             ),
403             params => $res->params,
404             );
405             }
406             }
407              
408             # The authentication args will not contain characters which need to be handled
409             # specially. Also, I think adding the length to these keys actually just
410             # doesn't work.
411              
412             sub _encode_credentials {
413 16     16   1461 my $self = shift;
414              
415 16         172 my %auth = (
416             PARTNER => $self->partner,
417             PWD => $self->password,
418             USER => $self->user,
419             VENDOR => $self->vendor,
420             );
421              
422             # Create key/value pairs the way that PayPal::PaymentsAdvanced wants them.
423 16         88 my $pairs = join '&', map { $_ . '=' . $auth{$_} } sort keys %auth;
  64         231  
424 16         93 return $pairs;
425             }
426              
427             sub _force_upper_case {
428 20     20   55 my $self = shift;
429 20         44 my $args = shift;
430 20         66 my %post = map { uc $_ => $args->{$_} } keys %{$args};
  51         191  
  20         83  
431              
432 20         105 return \%post;
433             }
434              
435             # Payments Advanced treats encoding key/value pairs like a special snowflake.
436             # https://metacpan.org/source/PLOBBES/Business-OnlinePayment-PayflowPro-1.01/PayflowPro.pm#L276
437             sub _pseudo_encode_args {
438 16     16   1790 my $self = shift;
439 16         35 my $args = shift;
440              
441             my $uri = join '&', map {
442 65         327 join '=', sprintf( '%s[%i]', $_, length( $args->{$_} ) ), $args->{$_}
443 16         40 } grep { defined $args->{$_} } sort keys %{$args};
  65         155  
  16         70  
444 16         79 return $uri;
445             }
446              
447             1;
448              
449             =pod
450              
451             =head1 NAME
452              
453             WebService::PayPal::PaymentsAdvanced - A simple wrapper around the PayPal Payments Advanced web service
454              
455             =head1 VERSION
456              
457             version 0.000026
458              
459             =head1 SYNOPSIS
460              
461             use WebService::PayPal::PaymentsAdvanced;
462             my $payments = WebService::PayPal::PaymentsAdvanced->new(
463             {
464             password => 'seekrit',
465             user => 'username',
466             vendor => 'somevendor',
467             }
468             );
469              
470             my $response = $payments->create_secure_token(
471             {
472             AMT => 100,
473             TRXTYPE => 'S',
474             BILLINGTYPE => 'MerchantInitiatedBilling',
475             CANCELURL => 'https://example.com/cancel',
476             ERRORURL => 'https://example.com/error',
477             L_BILLINGTYPE0 => 'MerchantInitiatedBilling',
478             NAME => 'Chuck Norris',
479             RETURNURL => 'https://example.com/return',
480             }
481             );
482              
483             my $uri = $response->hosted_form_uri;
484              
485             # Store token data for later use. You'll need to implement this yourself.
486             $foo->freeze_token_data(
487             token => $response->secure_token,
488             token_id => $response->secure_token_id,
489             );
490              
491             # Later, when PayPal returns a silent POST or redirects the user to your
492             # return URL:
493              
494             my $redirect_response = $payments->get_response_from_redirect(
495             ip_address => $ip,
496             params => $params,
497             );
498              
499             # Fetch the tokens from the original request. You'll need to implement
500             # this yourself.
501              
502             my $thawed = $foo->get_thawed_tokens(...);
503              
504             # Don't do anything until you're sure the tokens are ok.
505             if ( $thawed->secure_token ne $redirect->secure_token
506             || $thawed->secure_token_id ne $response->secure_token_id ) {
507             die 'Fraud!';
508             }
509              
510             # Everything looks good. Carry on!
511              
512             print $response->secure_token;
513              
514             =head1 DESCRIPTION
515              
516             BETA BETA BETA. The interface is still subject to change.
517              
518             This is a wrapper around the "PayPal Payments Advanced" (AKA "PayPal Payflow
519             Link") hosted forms. This code does things like facilitating secure token
520             creation, providing an URL which you can use to insert an hosted_form into
521             your pages and processing the various kinds of response you can get from
522             PayPal.
523              
524             We also use various exception classes to make it easier for you to decide how
525             to handle the parts that go wrong.
526              
527             =head1 OBJECT INSTANTIATION
528              
529             The following parameters can be supplied to C<new()> when creating a new object.
530              
531             =head2 Required Parameters
532              
533             =head3 password
534              
535             The value of the C<password> field you use when logging in to the Payflow
536             Manager. (You'll probably want to create a specific user just for API calls).
537              
538             =head3 user
539              
540             The value of the C<user> field you use when logging in to the Payflow Manager.
541              
542             =head3 vendor
543              
544             The value of the C<vendor> field you use when logging in to the Payflow
545             Manager.
546              
547             =head2 Optional Parameters
548              
549             =head3 nonfatal_result_codes
550              
551             An arrayref of result codes that will be treated as non-fatal (i.e., that will
552             not cause an exception). By default, only 0 is considered non-fatal, but
553             depending on your integration, other codes such as 112 (failed AVS check) may
554             be considered non-fatal.
555              
556             =head3 partner
557              
558             The value of the C<partner> field you use when logging in to the Payflow
559             Manager. Defaults to C<PayPal>.
560              
561             =head3 payflow_pro_uri
562              
563             The hostname for the Payflow Pro API. This is where token creation requests
564             get directed. This already has a sensible (and correct) default, but it is
565             settable so that you can more easily mock API calls when testing.
566              
567             =head3 payflow_link_uri
568              
569             The hostname for the Payflow Link website. This is the hosted service where
570             users will enter their payment information. This already has a sensible (and
571             correct) default, but it is settable in case you want to mock it while testing.
572              
573             =head3 production_mode
574              
575             This is a C<Boolean>. Set this to C<true> if when you are ready to process
576             real transactions. Defaults to C<false>.
577              
578             =head3 ua
579              
580             You may provide your own UserAgent, but it must be of the L<LWP::UserAgent>
581             family. If you do provide a UserAgent, be sure to set a sensible timeout
582             value. Requests to the web service frequently run 20-30 seconds.
583              
584             This can be useful for debugging. You'll be able to get detailed information
585             about the network calls which are being made.
586              
587             use LWP::ConsoleLogger::Easy qw( debug_ua );
588             use LWP::UserAgent;
589             use WebService::PayPal::PaymentsAdvanced;
590              
591             my $ua = LWP::UserAgent;
592             debug_ua($ua);
593              
594             my $payments
595             = WebService::PayPal::PaymentsAdvanced->new( ua => $ua, ... );
596              
597             # Now fire up a console and watch your network activity.
598              
599             Check the tests which accompany this distribution for an example of how to mock
600             API calls using L<Test::LWP::UserAgent>.
601              
602             =head3 validate_hosted_form_uri
603              
604             C<Boolean>. If enabled, this module will attempt to GET the uri which you'll
605             be providing to the end user. This can help you identify issues on the PayPal
606             side. This is helpful because you'll be able to log exceptions thrown by this
607             method and deal with them accordingly. If you disable this option, you'll need
608             to rely on end users to report issues which may exist within PayPal's hosted
609             pages. Defaults to C<true>.
610              
611             =head3 verbose
612              
613             C<Boolean>. Sets C<VERBOSITY=HIGH> on all transactions if enabled. Defaults
614             to C<true>.
615              
616             =head2 Methods
617              
618             =head3 create_secure_token
619              
620             Create a secure token which you can use to create a hosted form uri. Returns a
621             L<WebService::PayPal::PaymentsAdvanced::Response::SecureToken> object.
622              
623             The first parameter holds the key/value parameters for the request. The second
624             parameter is optional and holds parameters to the underlying
625             L<WebService::PayPal::PaymentsAdvanced::Response::SecureToken> object, which is
626             useful to set attributes such as C<retry_attempts> and C<retry_callback>.
627              
628             use WebService::PayPal::PaymentsAdvanced;
629             my $payments = WebService::PayPal::PaymentsAdvanced->new(...);
630              
631             my $response = $payments->create_secure_token(
632             {
633             AMT => 100,
634             TRXTYPE => 'S',
635             BILLINGTYPE => 'MerchantInitiatedBilling',
636             CANCELURL => 'https://example.com/cancel',
637             ERRORURL => 'https://example.com/error',
638             L_BILLINGTYPE0 => 'MerchantInitiatedBilling',
639             NAME => 'Chuck Norris',
640             RETURNURL => 'https://example.com/return'
641             }
642             );
643              
644             print $response->secure_token;
645              
646             =head3 get_response_from_redirect
647              
648             This method can be used to parse responses from PayPal to your return URL.
649             It's essentially a wrapper around
650             L<WebService::PayPal::PaymentsAdvanced::Response::FromRedirect>. Returns a
651             L<WebService::PayPal::PaymentsAdvanced::Response> object.
652              
653             my $response = $payments->get_response_from_redirect(
654             params => $params,
655             );
656             print $response->message;
657              
658             =head3 get_response_from_silent_post
659              
660             This method can be used to validate responses from PayPal to your silent POST
661             url. If you provide an ip_address parameter, it will be validated against a
662             list of known IPs which PayPal provides. You're encouraged to provide an IP
663             address in order to prevent spoofing of payment responses. See
664             L<WebService::PayPal::PaymentsAdvanced::Response::FromSilentPOST> for more
665             information on this behaviour.
666              
667             This method returns a
668             L<WebService::PayPal::PaymentsAdvanced::Response::FromSilentPost::PayPal>
669             object for PayPal transactions. It returns a
670             L<WebService::PayPal::PaymentsAdvanced::Response::FromSilentPost::CreditCard>
671             object for credit card transactions. You can either inspect the class returned
672             to you or use the C<is_credit_card_transaction> or C<is_paypal_transaction>
673             methods to learn which method the customer paid with. Both methods return a
674             C<Boolean>.
675              
676             my $response = $payments->get_response_from_redirect(
677             ip_address => $ip,
678             params => $params,
679             );
680             print $response->message. "\n";
681             if ( $response->is_credit_card_transaction ) {
682             print $response->card_type, q{ }, $response->card_expiration;
683             }
684              
685             =head3 post
686              
687             Generic method to post arbitrary params to PayPal. Requires a C<HashRef> of
688             parameters and returns a L<WebService::PayPal::PaymentsAdvanced::Response>
689             object. Any lower case keys will be converted to upper case before this
690             response is sent. The second parameter is an optional C<HashRef>. If provided,
691             it defines attributes to pass to the
692             L<WebService::PayPal::PaymentsAdvanced::Response::SecureToken> object.
693              
694             use WebService::PayPal::PaymentsAdvanced;
695             my $payments = WebService::PayPal::PaymentsAdvanced->new(...);
696              
697             my $response = $payments->post( { TRXTYPE => 'V', ORIGID => $pnref, } );
698             say $response->message;
699              
700             # OR
701             my $response = $payments->post( { trxtype => 'V', origid => $pnref, } );
702              
703             =head3 capture_delayed_transaction( $ORIGID, [$AMT] )
704              
705             Captures a sale which you have previously authorized. Requires the ID of the
706             original transaction. If you wish to capture an amount which is not equal to
707             the original authorization amount, you'll need to pass an amount as the second
708             parameter. Returns a response object.
709              
710             =head3 auth_from_credit_card_reference_transaction( $ORIGID, $amount, $extra )
711              
712             Process a authorization based on a reference transaction from a credit card.
713             Requires 2 arguments: an ORIGID from a previous credit card transaction and an
714             amount. Any additional parameters can be passed via a HashRef as an optional
715             3rd argument.
716              
717             use WebService::PayPal::PaymentsAdvanced;
718             my $payments = WebService::PayPal::PaymentsAdvanced->new(...);
719              
720             my $response = $payments->auth_from_credit_card_reference_transaction(
721             'BFOOBAR', 1.50', { INVNUM => 'FOO123' }
722             );
723             say $response->message;
724              
725             =head3 sale_from_credit_card_reference_transaction( $ORIGID, $amount )
726              
727             Process a sale based on a reference transaction from a credit card. See
728             Requires 2 arguments: an ORIGID from a previous credit card transaction and an
729             amount. Any additional parameters can be passed via a HashRef as an optional
730             3rd argument.
731              
732             use WebService::PayPal::PaymentsAdvanced;
733             my $payments = WebService::PayPal::PaymentsAdvanced->new(...);
734              
735             my $response = $payments->sale_from_credit_card_reference_transaction(
736             'BFOOBAR', 1.50', { INVNUM => 'FOO123' }
737             );
738             say $response->message;
739              
740             =head3 auth_from_paypal_reference_transaction( $BAID, $amount, $currency, $extra )
741              
742             Process an authorization based on a reference transaction from PayPal.
743             Requires 3 arguments: a BAID from a previous PayPal transaction, an amount and
744             a currency. Any additional parameters can be passed via a HashRef as the
745             optional fourth argument.
746              
747             use WebService::PayPal::PaymentsAdvanced;
748             my $payments = WebService::PayPal::PaymentsAdvanced->new(...);
749              
750             my $response = $payments->auth_from_paypal_reference_transaction(
751             'B-FOOBAR', 1.50, 'USD', { INVNUM => 'FOO123' }
752             );
753             say $response->message;
754              
755             =head3 sale_from_paypal_reference_transaction( $BAID, $amount, $currency, $extra )
756              
757             Process a sale based on a reference transaction from PayPal. Requires 3
758             arguments: a BAID from a previous PayPal transaction, an amount and a currency.
759             Any additional parameters can be passed via a HashRef as an optional fourth
760             argument.
761              
762             use WebService::PayPal::PaymentsAdvanced;
763             my $payments = WebService::PayPal::PaymentsAdvanced->new(...);
764              
765             my $response = $payments->sale_from_paypal_reference_transaction(
766             'B-FOOBAR', 1.50, 'USD', { INVNUM => 'FOO123' }
767             );
768             say $response->message;
769              
770             =head3 refund_transaction( $origid, [$amount] )
771              
772             Refunds (credits) a previous transaction. Requires the C<ORIGID> and an
773             optional C<AMT>. If no amount is provided, the entire transaction will be
774             refunded.
775              
776             =head3 inquiry_transaction( $HashRef )
777              
778             Performs a transaction inquiry on a previously submitted transaction. Requires
779             the ID of the original transaction. Returns a response object.
780              
781             use WebService::PayPal::PaymentsAdvanced;
782             my $payments = WebService::PayPal::PaymentsAdvanced->new(...);
783              
784             my $inquiry = $payments->inquiry_transaction(
785             { ORIGID => 'FOO123', TENDER => 'C', }
786             );
787             say $response->message;
788              
789             =head3 void_transaction( $ORIGID )
790              
791             Voids a previous transaction. Requires the ID of the transaction to void.
792             Returns a response object.
793              
794             =head1 SEE ALSO
795              
796             The official L<Payflow Gateway Developer Guide and
797             Reference|https://developer.paypal.com/docs/classic/payflow/integration-guide/>
798              
799             =head1 AUTHOR
800              
801             Olaf Alders <olaf@wundercounter.com>
802              
803             =head1 CONTRIBUTORS
804              
805             =for stopwords Andy Jack Dave Rolsky Greg Oschwald Mark Fowler Mateu X Hunter Narsimham Chelluri Olaf Alders William Storey
806              
807             =over 4
808              
809             =item *
810              
811             Andy Jack <ajack@maxmind.com>
812              
813             =item *
814              
815             Dave Rolsky <drolsky@maxmind.com>
816              
817             =item *
818              
819             Greg Oschwald <goschwald@maxmind.com>
820              
821             =item *
822              
823             Mark Fowler <mark@twoshortplanks.com>
824              
825             =item *
826              
827             Mateu X Hunter <mhunter@maxmind.com>
828              
829             =item *
830              
831             Narsimham Chelluri <nchelluri@users.noreply.github.com>
832              
833             =item *
834              
835             Olaf Alders <oalders@maxmind.com>
836              
837             =item *
838              
839             William Storey <wstorey@maxmind.com>
840              
841             =back
842              
843             =head1 COPYRIGHT AND LICENSE
844              
845             This software is copyright (c) 2020 by MaxMind, Inc.
846              
847             This is free software; you can redistribute it and/or modify it under
848             the same terms as the Perl 5 programming language system itself.
849              
850             =cut
851              
852             __END__
853             #ABSTRACT: A simple wrapper around the PayPal Payments Advanced web service
854