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   3373749 use Moo;
  6         11675  
  6         56  
4              
5 6     6   4280 use namespace::autoclean;
  6         11757  
  6         61  
6              
7             our $VERSION = '0.000027';
8              
9 6     6   875 use feature qw( say state );
  6         31  
  6         845  
10              
11 6     6   1538 use Data::GUID;
  6         29240  
  6         67  
12 6     6   2240 use List::AllUtils qw( any );
  6         10947  
  6         423  
13 6     6   770 use LWP::UserAgent;
  6         44418  
  6         193  
14 6     6   563 use MooX::StrictConstructor;
  6         13965  
  6         75  
15 6     6   34809 use Type::Params qw( compile );
  6         159890  
  6         67  
16 6     6   2029 use Types::Common::Numeric qw( PositiveNum );
  6         12873  
  6         57  
17 6     6   4045 use Types::Common::String qw( NonEmptyStr );
  6         85994  
  6         69  
18 6         42 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   2884 );
  6         18  
29 6     6   12366 use Types::URI qw( Uri );
  6         498481  
  6         79  
30 6     6   2531 use URI;
  6         17  
  6         156  
31 6     6   33 use URI::FromHash qw( uri uri_object );
  6         14  
  6         348  
32 6     6   525 use URI::QueryParam;
  6         841  
  6         214  
33              
34             #<<< don't perltidy
35 6     6   2668 use WebService::PayPal::PaymentsAdvanced::Error::Generic;
  6         22  
  6         217  
36 6     6   3260 use WebService::PayPal::PaymentsAdvanced::Error::HostedForm;
  6         22  
  6         247  
37 6     6   2698 use WebService::PayPal::PaymentsAdvanced::Response;
  6         23  
  6         218  
38 6     6   3310 use WebService::PayPal::PaymentsAdvanced::Response::Authorization;
  6         25  
  6         328  
39 6     6   3515 use WebService::PayPal::PaymentsAdvanced::Response::Authorization::CreditCard;
  6         21  
  6         243  
40 6     6   3437 use WebService::PayPal::PaymentsAdvanced::Response::Authorization::PayPal;
  6         32  
  6         243  
41 6     6   3437 use WebService::PayPal::PaymentsAdvanced::Response::Capture;
  6         22  
  6         235  
42 6     6   3189 use WebService::PayPal::PaymentsAdvanced::Response::Credit;
  6         23  
  6         233  
43 6     6   3242 use WebService::PayPal::PaymentsAdvanced::Response::FromHTTP;
  6         22  
  6         240  
44 6     6   3276 use WebService::PayPal::PaymentsAdvanced::Response::FromRedirect;
  6         23  
  6         234  
45 6     6   2883 use WebService::PayPal::PaymentsAdvanced::Response::FromSilentPOST;
  6         22  
  6         268  
46 6     6   3498 use WebService::PayPal::PaymentsAdvanced::Response::FromSilentPOST::CreditCard;
  6         23  
  6         238  
47 6     6   3412 use WebService::PayPal::PaymentsAdvanced::Response::FromSilentPOST::PayPal;
  6         22  
  6         232  
48 6     6   3321 use WebService::PayPal::PaymentsAdvanced::Response::Inquiry;
  6         22  
  6         252  
49 6     6   3260 use WebService::PayPal::PaymentsAdvanced::Response::Inquiry::CreditCard;
  6         24  
  6         253  
50 6     6   3375 use WebService::PayPal::PaymentsAdvanced::Response::Inquiry::PayPal;
  6         20  
  6         222  
51 6     6   3146 use WebService::PayPal::PaymentsAdvanced::Response::Sale;
  6         35  
  6         252  
52 6     6   3347 use WebService::PayPal::PaymentsAdvanced::Response::Sale::CreditCard;
  6         21  
  6         242  
53 6     6   3234 use WebService::PayPal::PaymentsAdvanced::Response::Sale::PayPal;
  6         25  
  6         239  
54 6     6   3258 use WebService::PayPal::PaymentsAdvanced::Response::SecureToken;
  6         31  
  6         55  
55 6     6   3232 use WebService::PayPal::PaymentsAdvanced::Response::Void;
  6         22  
  6         47  
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   284 my $self = shift;
126              
127 13 100       98 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   105 my $self = shift;
137              
138 4 100       41 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 578 my $self = shift;
148              
149 2         8 state $check = compile( NonEmptyStr, Optional [PositiveNum] );
150 2         2426 my ( $origid, $amt ) = $check->(@_);
151              
152             ## no critic (ValuesAndExpressions::ProhibitCommaSeparatedStatements)
153 2 100       107 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   114 my $self = shift;
165 54         134 my $class_suffix = shift;
166 54         231 my %args = @_;
167              
168 54         211 $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 2357 my $self = shift;
176              
177 4         20 state $check = compile( HashRef, Optional [HashRef] );
178 4         4186 my ( $args, $options ) = $check->(@_);
179 4   50     215 $options ||= {};
180              
181 4         18 my $post = $self->_force_upper_case($args);
182              
183 4         16 $post->{CREATESECURETOKEN} = 'Y';
184 4   66     38 $post->{SECURETOKENID} ||= Data::GUID->new->as_string;
185              
186 4         561 my $res = $self->post( $post, $options );
187              
188 2         10 $self->_validate_secure_token_id( $res, $post->{SECURETOKENID} );
189              
190 2         105 return $res;
191             }
192              
193             sub get_response_from_redirect {
194 2     2 1 1318 my $self = shift;
195              
196 2         10 state $check = compile(HashRef);
197 2         996 my ($args) = $check->(@_);
198              
199 2         28 my $response = $self->_class_for('Response::FromRedirect')->new($args);
200              
201 2         1655 return $self->_response_for( 'Response', params => $response->params );
202             }
203              
204             sub get_response_from_silent_post {
205 18     18 1 2816 my $self = shift;
206              
207 18         55 state $check = compile(HashRef);
208 18         3921 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       226 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         21 );
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         39 my $class_suffix = 'Response::FromSilentPOST';
227 17         35 my $response = $self->_response_for( $class_suffix, %{$args} );
  17         68  
228              
229 16 100       636 $class_suffix
230             .= '::'
231             . ( $response->is_credit_card_transaction ? 'CreditCard' : 'PayPal' );
232              
233 16         492 return $self->_response_for( $class_suffix, params => $response->params );
234             }
235              
236             sub inquiry_transaction {
237 1     1 1 229 my $self = shift;
238              
239 1         5 state $check = compile(HashRef);
240 1         805 my ($args) = $check->(@_);
241 1         11 $args->{TRXTYPE} = 'I';
242              
243 1         4 return $self->post($args);
244             }
245              
246             sub post {
247 14     14 1 34 my $self = shift;
248              
249 14         36 state $check = compile( HashRef, Optional [HashRef] );
250 14         3052 my ( $post, $options ) = $check->(@_);
251              
252 14         677 $post = $self->_force_upper_case($post);
253 14 50       86 $post->{VERBOSITY} = 'HIGH' if $self->verbose;
254              
255 14         43 my $content = join '&', $self->_encode_credentials,
256             $self->_pseudo_encode_args($post);
257              
258 14         320 my $http_response
259             = $self->ua->post( $self->payflow_pro_uri, Content => $content );
260              
261 14         1721308 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     3429 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       89 %{ $options || {} },
  4         1541  
274             );
275             }
276              
277 10         105 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         26 my $type = $post->{TRXTYPE};
287 10         18 my $response_class_suffix = 'Response';
288 10 50 33     53 if ( $type && exists $class_for_type{$type} ) {
289              
290 10         20 $response_class_suffix = $class_for_type{$type};
291              
292             # Get more specific response classes for CC and PayPal txns.
293 10 100   24   66 unless ( any { $type eq $_ } ( 'C', 'D', 'V' ) ) {
  24         64  
294 5         14 my $response = $self->_response_for(
295             $response_class_suffix,
296             params => $params
297             );
298              
299 5 100       100 $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         176 return $self->_response_for( $response_class_suffix, params => $params );
309             }
310              
311             sub refund_transaction {
312 2     2 1 504 my $self = shift;
313 2         10 state $check = compile( NonEmptyStr, Optional [NonEmptyStr] );
314 2         2334 my ( $origid, $amount ) = $check->(@_);
315              
316 2 100       118 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 201 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 197 my $self = shift;
332 1         4 return $self->_credit_card_reference_transaction( 'S', @_ );
333             }
334              
335             sub _credit_card_reference_transaction {
336 2     2   4 my $self = shift;
337 2         7 state $check
338             = compile( NonEmptyStr, NonEmptyStr, Num, Optional [HashRef] );
339 2         2286 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       112 $extra ? ( %{$extra} ) : (),
  2         14  
348             }
349             );
350             }
351              
352             sub auth_from_paypal_reference_transaction {
353 1     1 1 205 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 208 my $self = shift;
359 1         3 return $self->_paypal_reference_transaction( 'S', @_ );
360             }
361              
362             sub _paypal_reference_transaction {
363 2     2   5 my $self = shift;
364 2         7 state $check = compile( NonEmptyStr, NonEmptyStr, Num, NonEmptyStr,
365             Optional [HashRef]
366             );
367 2         2684 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       122 $extra ? ( %{$extra} ) : (),
  0         0  
378             }
379             );
380             }
381              
382             sub void_transaction {
383 1     1 1 237 my $self = shift;
384              
385 1         7 state $check = compile(NonEmptyStr);
386 1         1073 my ($pnref) = $check->(@_);
387              
388 1         19 return $self->post( { TRXTYPE => 'V', ORIGID => $pnref } );
389             }
390              
391             sub _validate_secure_token_id {
392 2     2   4 my $self = shift;
393 2         6 my $res = shift;
394 2         3 my $token_id = shift;
395              
396             # This should only happen if bad actors are involved.
397 2 50       37 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   1409 my $self = shift;
414              
415 16         151 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         74 my $pairs = join '&', map { $_ . '=' . $auth{$_} } sort keys %auth;
  64         216  
424 16         81 return $pairs;
425             }
426              
427             sub _force_upper_case {
428 20     20   43 my $self = shift;
429 20         35 my $args = shift;
430 20         36 my %post = map { uc $_ => $args->{$_} } keys %{$args};
  51         151  
  20         70  
431              
432 20         68 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   1440 my $self = shift;
439 16         38 my $args = shift;
440              
441             my $uri = join '&', map {
442 65         245 join '=', sprintf( '%s[%i]', $_, length( $args->{$_} ) ), $args->{$_}
443 16         34 } grep { defined $args->{$_} } sort keys %{$args};
  65         127  
  16         57  
444 16         55 return $uri;
445             }
446              
447             1;
448              
449             =pod
450              
451             =encoding UTF-8
452              
453             =head1 NAME
454              
455             WebService::PayPal::PaymentsAdvanced - A simple wrapper around the PayPal Payments Advanced web service
456              
457             =head1 VERSION
458              
459             version 0.000027
460              
461             =head1 SYNOPSIS
462              
463             use WebService::PayPal::PaymentsAdvanced;
464             my $payments = WebService::PayPal::PaymentsAdvanced->new(
465             {
466             password => 'seekrit',
467             user => 'username',
468             vendor => 'somevendor',
469             }
470             );
471              
472             my $response = $payments->create_secure_token(
473             {
474             AMT => 100,
475             TRXTYPE => 'S',
476             BILLINGTYPE => 'MerchantInitiatedBilling',
477             CANCELURL => 'https://example.com/cancel',
478             ERRORURL => 'https://example.com/error',
479             L_BILLINGTYPE0 => 'MerchantInitiatedBilling',
480             NAME => 'Chuck Norris',
481             RETURNURL => 'https://example.com/return',
482             }
483             );
484              
485             my $uri = $response->hosted_form_uri;
486              
487             # Store token data for later use. You'll need to implement this yourself.
488             $foo->freeze_token_data(
489             token => $response->secure_token,
490             token_id => $response->secure_token_id,
491             );
492              
493             # Later, when PayPal returns a silent POST or redirects the user to your
494             # return URL:
495              
496             my $redirect_response = $payments->get_response_from_redirect(
497             ip_address => $ip,
498             params => $params,
499             );
500              
501             # Fetch the tokens from the original request. You'll need to implement
502             # this yourself.
503              
504             my $thawed = $foo->get_thawed_tokens(...);
505              
506             # Don't do anything until you're sure the tokens are ok.
507             if ( $thawed->secure_token ne $redirect->secure_token
508             || $thawed->secure_token_id ne $response->secure_token_id ) {
509             die 'Fraud!';
510             }
511              
512             # Everything looks good. Carry on!
513              
514             print $response->secure_token;
515              
516             =head1 DESCRIPTION
517              
518             BETA BETA BETA. The interface is still subject to change.
519              
520             This is a wrapper around the "PayPal Payments Advanced" (AKA "PayPal Payflow
521             Link") hosted forms. This code does things like facilitating secure token
522             creation, providing an URL which you can use to insert an hosted_form into
523             your pages and processing the various kinds of response you can get from
524             PayPal.
525              
526             We also use various exception classes to make it easier for you to decide how
527             to handle the parts that go wrong.
528              
529             =head1 OBJECT INSTANTIATION
530              
531             The following parameters can be supplied to C<new()> when creating a new object.
532              
533             =head2 Required Parameters
534              
535             =head3 password
536              
537             The value of the C<password> field you use when logging in to the Payflow
538             Manager. (You'll probably want to create a specific user just for API calls).
539              
540             =head3 user
541              
542             The value of the C<user> field you use when logging in to the Payflow Manager.
543              
544             =head3 vendor
545              
546             The value of the C<vendor> field you use when logging in to the Payflow
547             Manager.
548              
549             =head2 Optional Parameters
550              
551             =head3 nonfatal_result_codes
552              
553             An arrayref of result codes that will be treated as non-fatal (i.e., that will
554             not cause an exception). By default, only 0 is considered non-fatal, but
555             depending on your integration, other codes such as 112 (failed AVS check) may
556             be considered non-fatal.
557              
558             =head3 partner
559              
560             The value of the C<partner> field you use when logging in to the Payflow
561             Manager. Defaults to C<PayPal>.
562              
563             =head3 payflow_pro_uri
564              
565             The hostname for the Payflow Pro API. This is where token creation requests
566             get directed. This already has a sensible (and correct) default, but it is
567             settable so that you can more easily mock API calls when testing.
568              
569             =head3 payflow_link_uri
570              
571             The hostname for the Payflow Link website. This is the hosted service where
572             users will enter their payment information. This already has a sensible (and
573             correct) default, but it is settable in case you want to mock it while testing.
574              
575             =head3 production_mode
576              
577             This is a C<Boolean>. Set this to C<true> if when you are ready to process
578             real transactions. Defaults to C<false>.
579              
580             =head3 ua
581              
582             You may provide your own UserAgent, but it must be of the L<LWP::UserAgent>
583             family. If you do provide a UserAgent, be sure to set a sensible timeout
584             value. Requests to the web service frequently run 20-30 seconds.
585              
586             This can be useful for debugging. You'll be able to get detailed information
587             about the network calls which are being made.
588              
589             use LWP::ConsoleLogger::Easy qw( debug_ua );
590             use LWP::UserAgent;
591             use WebService::PayPal::PaymentsAdvanced;
592              
593             my $ua = LWP::UserAgent;
594             debug_ua($ua);
595              
596             my $payments
597             = WebService::PayPal::PaymentsAdvanced->new( ua => $ua, ... );
598              
599             # Now fire up a console and watch your network activity.
600              
601             Check the tests which accompany this distribution for an example of how to mock
602             API calls using L<Test::LWP::UserAgent>.
603              
604             =head3 validate_hosted_form_uri
605              
606             C<Boolean>. If enabled, this module will attempt to GET the uri which you'll
607             be providing to the end user. This can help you identify issues on the PayPal
608             side. This is helpful because you'll be able to log exceptions thrown by this
609             method and deal with them accordingly. If you disable this option, you'll need
610             to rely on end users to report issues which may exist within PayPal's hosted
611             pages. Defaults to C<true>.
612              
613             =head3 verbose
614              
615             C<Boolean>. Sets C<VERBOSITY=HIGH> on all transactions if enabled. Defaults
616             to C<true>.
617              
618             =head2 Methods
619              
620             =head3 create_secure_token
621              
622             Create a secure token which you can use to create a hosted form uri. Returns a
623             L<WebService::PayPal::PaymentsAdvanced::Response::SecureToken> object.
624              
625             The first parameter holds the key/value parameters for the request. The second
626             parameter is optional and holds parameters to the underlying
627             L<WebService::PayPal::PaymentsAdvanced::Response::SecureToken> object, which is
628             useful to set attributes such as C<retry_attempts> and C<retry_callback>.
629              
630             use WebService::PayPal::PaymentsAdvanced;
631             my $payments = WebService::PayPal::PaymentsAdvanced->new(...);
632              
633             my $response = $payments->create_secure_token(
634             {
635             AMT => 100,
636             TRXTYPE => 'S',
637             BILLINGTYPE => 'MerchantInitiatedBilling',
638             CANCELURL => 'https://example.com/cancel',
639             ERRORURL => 'https://example.com/error',
640             L_BILLINGTYPE0 => 'MerchantInitiatedBilling',
641             NAME => 'Chuck Norris',
642             RETURNURL => 'https://example.com/return'
643             }
644             );
645              
646             print $response->secure_token;
647              
648             =head3 get_response_from_redirect
649              
650             This method can be used to parse responses from PayPal to your return URL.
651             It's essentially a wrapper around
652             L<WebService::PayPal::PaymentsAdvanced::Response::FromRedirect>. Returns a
653             L<WebService::PayPal::PaymentsAdvanced::Response> object.
654              
655             my $response = $payments->get_response_from_redirect(
656             params => $params,
657             );
658             print $response->message;
659              
660             =head3 get_response_from_silent_post
661              
662             This method can be used to validate responses from PayPal to your silent POST
663             url. If you provide an ip_address parameter, it will be validated against a
664             list of known IPs which PayPal provides. You're encouraged to provide an IP
665             address in order to prevent spoofing of payment responses. See
666             L<WebService::PayPal::PaymentsAdvanced::Response::FromSilentPOST> for more
667             information on this behaviour.
668              
669             This method returns a
670             L<WebService::PayPal::PaymentsAdvanced::Response::FromSilentPost::PayPal>
671             object for PayPal transactions. It returns a
672             L<WebService::PayPal::PaymentsAdvanced::Response::FromSilentPost::CreditCard>
673             object for credit card transactions. You can either inspect the class returned
674             to you or use the C<is_credit_card_transaction> or C<is_paypal_transaction>
675             methods to learn which method the customer paid with. Both methods return a
676             C<Boolean>.
677              
678             my $response = $payments->get_response_from_redirect(
679             ip_address => $ip,
680             params => $params,
681             );
682             print $response->message. "\n";
683             if ( $response->is_credit_card_transaction ) {
684             print $response->card_type, q{ }, $response->card_expiration;
685             }
686              
687             =head3 post
688              
689             Generic method to post arbitrary params to PayPal. Requires a C<HashRef> of
690             parameters and returns a L<WebService::PayPal::PaymentsAdvanced::Response>
691             object. Any lower case keys will be converted to upper case before this
692             response is sent. The second parameter is an optional C<HashRef>. If provided,
693             it defines attributes to pass to the
694             L<WebService::PayPal::PaymentsAdvanced::Response::SecureToken> object.
695              
696             use WebService::PayPal::PaymentsAdvanced;
697             my $payments = WebService::PayPal::PaymentsAdvanced->new(...);
698              
699             my $response = $payments->post( { TRXTYPE => 'V', ORIGID => $pnref, } );
700             say $response->message;
701              
702             # OR
703             my $response = $payments->post( { trxtype => 'V', origid => $pnref, } );
704              
705             =head3 capture_delayed_transaction( $ORIGID, [$AMT] )
706              
707             Captures a sale which you have previously authorized. Requires the ID of the
708             original transaction. If you wish to capture an amount which is not equal to
709             the original authorization amount, you'll need to pass an amount as the second
710             parameter. Returns a response object.
711              
712             =head3 auth_from_credit_card_reference_transaction( $ORIGID, $amount, $extra )
713              
714             Process a authorization based on a reference transaction from a credit card.
715             Requires 2 arguments: an ORIGID from a previous credit card transaction and an
716             amount. Any additional parameters can be passed via a HashRef as an optional
717             3rd argument.
718              
719             use WebService::PayPal::PaymentsAdvanced;
720             my $payments = WebService::PayPal::PaymentsAdvanced->new(...);
721              
722             my $response = $payments->auth_from_credit_card_reference_transaction(
723             'BFOOBAR', 1.50', { INVNUM => 'FOO123' }
724             );
725             say $response->message;
726              
727             =head3 sale_from_credit_card_reference_transaction( $ORIGID, $amount )
728              
729             Process a sale based on a reference transaction from a credit card. See
730             Requires 2 arguments: an ORIGID from a previous credit card transaction and an
731             amount. Any additional parameters can be passed via a HashRef as an optional
732             3rd argument.
733              
734             use WebService::PayPal::PaymentsAdvanced;
735             my $payments = WebService::PayPal::PaymentsAdvanced->new(...);
736              
737             my $response = $payments->sale_from_credit_card_reference_transaction(
738             'BFOOBAR', 1.50', { INVNUM => 'FOO123' }
739             );
740             say $response->message;
741              
742             =head3 auth_from_paypal_reference_transaction( $BAID, $amount, $currency, $extra )
743              
744             Process an authorization based on a reference transaction from PayPal.
745             Requires 3 arguments: a BAID from a previous PayPal transaction, an amount and
746             a currency. Any additional parameters can be passed via a HashRef as the
747             optional fourth argument.
748              
749             use WebService::PayPal::PaymentsAdvanced;
750             my $payments = WebService::PayPal::PaymentsAdvanced->new(...);
751              
752             my $response = $payments->auth_from_paypal_reference_transaction(
753             'B-FOOBAR', 1.50, 'USD', { INVNUM => 'FOO123' }
754             );
755             say $response->message;
756              
757             =head3 sale_from_paypal_reference_transaction( $BAID, $amount, $currency, $extra )
758              
759             Process a sale based on a reference transaction from PayPal. Requires 3
760             arguments: a BAID from a previous PayPal transaction, an amount and a currency.
761             Any additional parameters can be passed via a HashRef as an optional fourth
762             argument.
763              
764             use WebService::PayPal::PaymentsAdvanced;
765             my $payments = WebService::PayPal::PaymentsAdvanced->new(...);
766              
767             my $response = $payments->sale_from_paypal_reference_transaction(
768             'B-FOOBAR', 1.50, 'USD', { INVNUM => 'FOO123' }
769             );
770             say $response->message;
771              
772             =head3 refund_transaction( $origid, [$amount] )
773              
774             Refunds (credits) a previous transaction. Requires the C<ORIGID> and an
775             optional C<AMT>. If no amount is provided, the entire transaction will be
776             refunded.
777              
778             =head3 inquiry_transaction( $HashRef )
779              
780             Performs a transaction inquiry on a previously submitted transaction. Requires
781             the ID of the original transaction. Returns a response object.
782              
783             use WebService::PayPal::PaymentsAdvanced;
784             my $payments = WebService::PayPal::PaymentsAdvanced->new(...);
785              
786             my $inquiry = $payments->inquiry_transaction(
787             { ORIGID => 'FOO123', TENDER => 'C', }
788             );
789             say $response->message;
790              
791             =head3 void_transaction( $ORIGID )
792              
793             Voids a previous transaction. Requires the ID of the transaction to void.
794             Returns a response object.
795              
796             =head1 SEE ALSO
797              
798             The official L<Payflow Gateway Developer Guide and
799             Reference|https://developer.paypal.com/docs/classic/payflow/integration-guide/>
800              
801             =head1 SUPPORT
802              
803             Bugs may be submitted through L<https://github.com/maxmind/webservice-paypal-paymentsadvanced/issues>.
804              
805             =head1 AUTHOR
806              
807             Olaf Alders <olaf@wundercounter.com>
808              
809             =head1 CONTRIBUTORS
810              
811             =for stopwords Andy Jack Dave Rolsky Greg Oschwald Mark Fowler Mateu X Hunter Narsimham Chelluri Olaf Alders William Storey
812              
813             =over 4
814              
815             =item *
816              
817             Andy Jack <ajack@maxmind.com>
818              
819             =item *
820              
821             Dave Rolsky <drolsky@maxmind.com>
822              
823             =item *
824              
825             Greg Oschwald <goschwald@maxmind.com>
826              
827             =item *
828              
829             Mark Fowler <mark@twoshortplanks.com>
830              
831             =item *
832              
833             Mateu X Hunter <mhunter@maxmind.com>
834              
835             =item *
836              
837             Narsimham Chelluri <nchelluri@users.noreply.github.com>
838              
839             =item *
840              
841             Olaf Alders <oalders@maxmind.com>
842              
843             =item *
844              
845             William Storey <wstorey@maxmind.com>
846              
847             =back
848              
849             =head1 COPYRIGHT AND LICENSE
850              
851             This software is copyright (c) 2021 by MaxMind, Inc.
852              
853             This is free software; you can redistribute it and/or modify it under
854             the same terms as the Perl 5 programming language system itself.
855              
856             =cut
857              
858             __END__
859             #ABSTRACT: A simple wrapper around the PayPal Payments Advanced web service
860