File Coverage

blib/lib/WebService/PayPal/PaymentsAdvanced.pm
Criterion Covered Total %
statement 215 217 99.0
branch 24 30 80.0
condition 6 11 54.5
subroutine 57 57 100.0
pod 12 12 100.0
total 314 327 96.0


line stmt bran cond sub pod time code
1             package WebService::PayPal::PaymentsAdvanced;
2              
3 6     6   3837596 use Moo;
  6         11190  
  6         68  
4              
5 6     6   4248 use namespace::autoclean;
  6         11885  
  6         75  
6              
7             our $VERSION = '0.000028';
8              
9 6     6   695 use feature qw( say state );
  6         27  
  6         787  
10              
11 6     6   1807 use Data::GUID ();
  6         31296  
  6         259  
12 6     6   706 use List::AllUtils qw( any );
  6         11476  
  6         475  
13 6     6   518 use MooX::StrictConstructor;
  6         14582  
  6         79  
14 6     6   35112 use Type::Params qw( compile );
  6         164951  
  6         82  
15 6     6   3803 use Types::Common::Numeric qw( PositiveNum );
  6         12920  
  6         64  
16 6     6   4922 use Types::Common::String qw( NonEmptyStr );
  6         96008  
  6         128  
17 6         43 use Types::Standard qw(
18             ArrayRef
19             Bool
20             CodeRef
21             Defined
22             HashRef
23             InstanceOf
24             Int
25             Num
26             Optional
27 6     6   3171 );
  6         17  
28 6     6   13465 use Types::URI qw( Uri );
  6         554200  
  6         97  
29 6     6   2842 use URI ();
  6         15  
  6         137  
30 6     6   37 use URI::FromHash qw( uri_object );
  6         14  
  6         382  
31 6     6   554 use URI::QueryParam;
  6         843  
  6         170  
32              
33             #<<< don't perltidy
34 6     6   2631 use WebService::PayPal::PaymentsAdvanced::Error::Generic ();
  6         58  
  6         206  
35 6     6   3582 use WebService::PayPal::PaymentsAdvanced::Error::HostedForm ();
  6         24  
  6         290  
36 6     6   3084 use WebService::PayPal::PaymentsAdvanced::Response ();
  6         24  
  6         203  
37 6     6   3683 use WebService::PayPal::PaymentsAdvanced::Response::Authorization ();
  6         32  
  6         296  
38 6     6   3931 use WebService::PayPal::PaymentsAdvanced::Response::Authorization::CreditCard ();
  6         28  
  6         248  
39 6     6   5425 use WebService::PayPal::PaymentsAdvanced::Response::Authorization::PayPal ();
  6         44  
  6         245  
40 6     6   3604 use WebService::PayPal::PaymentsAdvanced::Response::Capture ();
  6         26  
  6         239  
41 6     6   3490 use WebService::PayPal::PaymentsAdvanced::Response::Credit ();
  6         27  
  6         221  
42 6     6   3493 use WebService::PayPal::PaymentsAdvanced::Response::FromHTTP ();
  6         26  
  6         285  
43 6     6   3780 use WebService::PayPal::PaymentsAdvanced::Response::FromRedirect ();
  6         24  
  6         275  
44 6     6   3018 use WebService::PayPal::PaymentsAdvanced::Response::FromSilentPOST ();
  6         25  
  6         255  
45 6     6   3767 use WebService::PayPal::PaymentsAdvanced::Response::FromSilentPOST::CreditCard ();
  6         28  
  6         238  
46 6     6   3805 use WebService::PayPal::PaymentsAdvanced::Response::FromSilentPOST::PayPal ();
  6         31  
  6         328  
47 6     6   3639 use WebService::PayPal::PaymentsAdvanced::Response::Inquiry ();
  6         27  
  6         546  
48 6     6   3850 use WebService::PayPal::PaymentsAdvanced::Response::Inquiry::CreditCard ();
  6         25  
  6         240  
49 6     6   4230 use WebService::PayPal::PaymentsAdvanced::Response::Inquiry::PayPal ();
  6         22  
  6         261  
50 6     6   3842 use WebService::PayPal::PaymentsAdvanced::Response::Sale ();
  6         27  
  6         266  
51 6     6   3784 use WebService::PayPal::PaymentsAdvanced::Response::Sale::CreditCard ();
  6         25  
  6         246  
52 6     6   3614 use WebService::PayPal::PaymentsAdvanced::Response::Sale::PayPal ();
  6         27  
  6         239  
53 6     6   3650 use WebService::PayPal::PaymentsAdvanced::Response::SecureToken ();
  6         32  
  6         300  
54 6     6   3565 use WebService::PayPal::PaymentsAdvanced::Response::Void ();
  6         30  
  6         15153  
55             #>>>
56              
57             has nonfatal_result_codes => (
58             is => 'ro',
59             isa => ArrayRef [Int],
60             default => sub { [0] },
61             );
62              
63             has partner => (
64             is => 'ro',
65             isa => NonEmptyStr,
66             required => 1,
67             default => 'PayPal',
68             );
69              
70             has password => (
71             is => 'ro',
72             isa => Defined,
73             required => 1,
74             );
75              
76             has payflow_pro_uri => (
77             is => 'lazy',
78             isa => Uri,
79             coerce => 1,
80             );
81              
82             has payflow_link_uri => (
83             is => 'lazy',
84             isa => Uri,
85             coerce => 1,
86             );
87              
88             has production_mode => (
89             is => 'ro',
90             isa => Bool,
91             default => 0,
92             );
93              
94             has user => (
95             is => 'ro',
96             isa => Defined,
97             required => 1,
98             );
99              
100             has validate_hosted_form_uri => (
101             is => 'ro',
102             isa => Bool,
103             default => 1,
104             );
105              
106             has vendor => (
107             is => 'ro',
108             isa => NonEmptyStr,
109             required => 1,
110             );
111              
112             has verbose => (
113             is => 'ro',
114             isa => Bool,
115             default => 1,
116             );
117              
118             with(
119             'WebService::PayPal::PaymentsAdvanced::Role::HasUA',
120             'WebService::PayPal::PaymentsAdvanced::Role::ClassFor'
121             );
122              
123             sub _build_payflow_pro_uri {
124 13     13   357 my $self = shift;
125              
126 13 100       133 return uri_object(
127             scheme => 'https',
128             host => $self->production_mode
129             ? 'payflowpro.paypal.com'
130             : 'pilot-payflowpro.paypal.com'
131             );
132             }
133              
134             sub _build_payflow_link_uri {
135 4     4   121 my $self = shift;
136              
137 4 100       46 return uri_object(
138             scheme => 'https',
139             host => $self->production_mode
140             ? 'payflowlink.paypal.com'
141             : 'pilot-payflowlink.paypal.com'
142             );
143             }
144              
145             sub capture_delayed_transaction {
146 2     2 1 813 my $self = shift;
147              
148 2         11 state $check = compile( NonEmptyStr, Optional [PositiveNum] );
149 2         2911 my ( $origid, $amt ) = $check->(@_);
150              
151             ## no critic (ValuesAndExpressions::ProhibitCommaSeparatedStatements)
152 2 100       144 return $self->post(
153             {
154             $amt ? ( AMT => $amt ) : (),
155             ORIGID => $origid,
156             TRXTYPE => 'D',
157             }
158             );
159             ## use critic
160             }
161              
162             sub _response_for {
163 54     54   149 my $self = shift;
164 54         116 my $class_suffix = shift;
165 54         194 my %args = @_;
166              
167 54         304 $self->_class_for($class_suffix)->new(
168             nonfatal_result_codes => $self->nonfatal_result_codes,
169             %args,
170             );
171             }
172              
173             sub create_secure_token {
174 4     4 1 2796 my $self = shift;
175              
176 4         43 state $check = compile( HashRef, Optional [HashRef] );
177 4         4833 my ( $args, $options ) = $check->(@_);
178 4   50     224 $options ||= {};
179              
180 4         23 my $post = $self->_force_upper_case($args);
181              
182 4         18 $post->{CREATESECURETOKEN} = 'Y';
183 4   66     41 $post->{SECURETOKENID} ||= Data::GUID->new->as_string;
184              
185 4         752 my $res = $self->post( $post, $options );
186              
187 2         16 $self->_validate_secure_token_id( $res, $post->{SECURETOKENID} );
188              
189 2         120 return $res;
190             }
191              
192             sub get_response_from_redirect {
193 2     2 1 1295 my $self = shift;
194              
195 2         8 state $check = compile(HashRef);
196 2         938 my ($args) = $check->(@_);
197              
198 2         27 my $response = $self->_class_for('Response::FromRedirect')->new($args);
199              
200 2         1646 return $self->_response_for( 'Response', params => $response->params );
201             }
202              
203             sub get_response_from_silent_post {
204 18     18 1 2645 my $self = shift;
205              
206 18         62 state $check = compile(HashRef);
207 18         4091 my ($args) = $check->(@_);
208              
209             # If the RESPMSG param is missing, then this may just be a garbage, random
210             # POST from a bot.
211              
212 18 100       269 unless ( $args->{params}->{RESPMSG} ) {
213             WebService::PayPal::PaymentsAdvanced::Error::Generic->throw(
214             message => 'Bad params supplied from silent POST',
215             params => $args->{params},
216 1         23 );
217             }
218              
219             # First we create a SilentPOST response, which may or may not validate the
220             # IP. If there's no exception we query the object to find out if this was a
221             # PayPal or CreditCard transaction and then return the appropriate class.
222             # IPs will only be validate once as the PayPal/CreditCard object
223             # instantiation will not provide an IP address.
224              
225 17         52 my $class_suffix = 'Response::FromSilentPOST';
226 17         44 my $response = $self->_response_for( $class_suffix, %{$args} );
  17         90  
227              
228 16 100       623 $class_suffix
229             .= '::'
230             . ( $response->is_credit_card_transaction ? 'CreditCard' : 'PayPal' );
231              
232 16         564 return $self->_response_for( $class_suffix, params => $response->params );
233             }
234              
235             sub inquiry_transaction {
236 1     1 1 249 my $self = shift;
237              
238 1         7 state $check = compile(HashRef);
239 1         862 my ($args) = $check->(@_);
240 1         12 $args->{TRXTYPE} = 'I';
241              
242 1         5 return $self->post($args);
243             }
244              
245             sub post {
246 14     14 1 46 my $self = shift;
247              
248 14         46 state $check = compile( HashRef, Optional [HashRef] );
249 14         3447 my ( $post, $options ) = $check->(@_);
250              
251 14         342 $post = $self->_force_upper_case($post);
252 14 50       106 $post->{VERBOSITY} = 'HIGH' if $self->verbose;
253              
254 14         60 my $content = join '&', $self->_encode_credentials,
255             $self->_pseudo_encode_args($post);
256              
257 14         425 my $http_response
258             = $self->ua->post( $self->payflow_pro_uri, Content => $content );
259              
260 14         1875374 my $params = $self->_class_for('Response::FromHTTP')->new(
261             http_response => $http_response,
262             request_uri => $self->payflow_pro_uri,
263             )->params;
264              
265 14 100 66     4472 if ( $post->{CREATESECURETOKEN} && $post->{CREATESECURETOKEN} eq 'Y' ) {
266             return $self->_response_for(
267             'Response::SecureToken',
268             params => $params,
269             payflow_link_uri => $self->payflow_link_uri,
270             ua => $self->ua,
271             validate_hosted_form_uri => $self->validate_hosted_form_uri,
272 4 50       106 %{ $options || {} },
  4         1958  
273             );
274             }
275              
276 10         84 my %class_for_type = (
277             A => 'Response::Authorization',
278             C => 'Response::Credit',
279             D => 'Response::Capture',
280             I => 'Response::Inquiry',
281             S => 'Response::Sale',
282             V => 'Response::Void',
283             );
284              
285 10         33 my $type = $post->{TRXTYPE};
286 10         29 my $response_class_suffix = 'Response';
287 10 50 33     109 if ( $type && exists $class_for_type{$type} ) {
288              
289 10         31 $response_class_suffix = $class_for_type{$type};
290              
291             # Get more specific response classes for CC and PayPal txns.
292 10 100   24   81 unless ( any { $type eq $_ } ( 'C', 'D', 'V' ) ) {
  24         77  
293 5         262 my $response = $self->_response_for(
294             $response_class_suffix,
295             params => $params
296             );
297              
298 5 100       175 $response_class_suffix = sprintf(
299             '%s::%s', $response_class_suffix,
300             $response->is_credit_card_transaction
301             ? 'CreditCard'
302             : 'PayPal'
303             );
304             }
305             }
306              
307 10         294 return $self->_response_for( $response_class_suffix, params => $params );
308             }
309              
310             sub refund_transaction {
311 2     2 1 551 my $self = shift;
312 2         10 state $check = compile( NonEmptyStr, Optional [NonEmptyStr] );
313 2         2600 my ( $origid, $amount ) = $check->(@_);
314              
315 2 100       163 return $self->post(
316             {
317             TRXTYPE => 'C',
318             ORIGID => $origid,
319             $amount ? ( AMT => $amount ) : ()
320             }
321             );
322             }
323              
324             sub auth_from_credit_card_reference_transaction {
325 1     1 1 294 my $self = shift;
326 1         7 return $self->_credit_card_reference_transaction( 'A', @_ );
327             }
328              
329             sub sale_from_credit_card_reference_transaction {
330 1     1 1 326 my $self = shift;
331 1         6 return $self->_credit_card_reference_transaction( 'S', @_ );
332             }
333              
334             sub _credit_card_reference_transaction {
335 2     2   8 my $self = shift;
336 2         10 state $check
337             = compile( NonEmptyStr, NonEmptyStr, Num, Optional [HashRef] );
338 2         3270 my ( $type, $origid, $amount, $extra ) = $check->(@_);
339              
340             return $self->post(
341             {
342             AMT => $amount,
343             ORIGID => $origid,
344             TENDER => 'C',
345             TRXTYPE => $type,
346 2 50       188 $extra ? ( %{$extra} ) : (),
  2         26  
347             }
348             );
349             }
350              
351             sub auth_from_paypal_reference_transaction {
352 1     1 1 296 my $self = shift;
353 1         5 return $self->_paypal_reference_transaction( 'A', @_ );
354             }
355              
356             sub sale_from_paypal_reference_transaction {
357 1     1 1 285 my $self = shift;
358 1         5 return $self->_paypal_reference_transaction( 'S', @_ );
359             }
360              
361             sub _paypal_reference_transaction {
362 2     2   5 my $self = shift;
363 2         12 state $check = compile(
364             NonEmptyStr, NonEmptyStr, Num, NonEmptyStr,
365             Optional [HashRef]
366             );
367 2         3389 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       164 $extra ? ( %{$extra} ) : (),
  0         0  
378             }
379             );
380             }
381              
382             sub void_transaction {
383 1     1 1 293 my $self = shift;
384              
385 1         7 state $check = compile(NonEmptyStr);
386 1         1051 my ($pnref) = $check->(@_);
387              
388 1         21 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         4 my $token_id = shift;
395              
396             # This should only happen if bad actors are involved.
397 2 50       48 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   1659 my $self = shift;
414              
415 16         204 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         98 my $pairs = join '&', map { $_ . '=' . $auth{$_} } sort keys %auth;
  64         239  
424 16         95 return $pairs;
425             }
426              
427             sub _force_upper_case {
428 20     20   50 my $self = shift;
429 20         44 my $args = shift;
430 20         53 my %post = map { uc $_ => $args->{$_} } keys %{$args};
  51         194  
  20         79  
431              
432 20         78 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   1831 my $self = shift;
439 16         40 my $args = shift;
440              
441             my $uri = join '&', map {
442 65         319 join '=', sprintf( '%s[%i]', $_, length( $args->{$_} ) ), $args->{$_}
443 16         55 } grep { defined $args->{$_} } sort keys %{$args};
  65         157  
  16         75  
444 16         75 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.000028
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             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 SUPPORT
800              
801             Bugs may be submitted through L<https://github.com/maxmind/webservice-paypal-paymentsadvanced/issues>.
802              
803             =head1 AUTHOR
804              
805             Olaf Alders <olaf@wundercounter.com>
806              
807             =head1 CONTRIBUTORS
808              
809             =for stopwords Andy Jack Dave Rolsky Greg Oschwald Mark Fowler Mateu X Hunter Narsimham Chelluri Nick Logan Olaf Alders William Storey
810              
811             =over 4
812              
813             =item *
814              
815             Andy Jack <ajack@maxmind.com>
816              
817             =item *
818              
819             Dave Rolsky <drolsky@maxmind.com>
820              
821             =item *
822              
823             Greg Oschwald <goschwald@maxmind.com>
824              
825             =item *
826              
827             Mark Fowler <mark@twoshortplanks.com>
828              
829             =item *
830              
831             Mateu X Hunter <mhunter@maxmind.com>
832              
833             =item *
834              
835             Narsimham Chelluri <nchelluri@users.noreply.github.com>
836              
837             =item *
838              
839             Nick Logan <nlogan@maxmind.com>
840              
841             =item *
842              
843             Olaf Alders <oalders@maxmind.com>
844              
845             =item *
846              
847             William Storey <wstorey@maxmind.com>
848              
849             =back
850              
851             =head1 COPYRIGHT AND LICENSE
852              
853             This software is copyright (c) 2022 by MaxMind, Inc.
854              
855             This is free software; you can redistribute it and/or modify it under
856             the same terms as the Perl 5 programming language system itself.
857              
858             =cut
859              
860             __END__
861             #ABSTRACT: A simple wrapper around the PayPal Payments Advanced web service
862