File Coverage

blib/lib/WebService/PayPal/PaymentsAdvanced/Response/SecureToken.pm
Criterion Covered Total %
statement 57 57 100.0
branch 14 14 100.0
condition n/a
subroutine 13 13 100.0
pod n/a
total 84 84 100.0


line stmt bran cond sub pod time code
1             package WebService::PayPal::PaymentsAdvanced::Response::SecureToken;
2              
3 7     7   178932 use Moo;
  7         7453  
  7         48  
4              
5 7     7   3982 use namespace::autoclean;
  7         1667  
  7         63  
6              
7             our $VERSION = '0.000028';
8              
9 7     7   751 use feature qw( state );
  7         28  
  7         877  
10              
11             extends 'WebService::PayPal::PaymentsAdvanced::Response';
12              
13 7     7   604 use HTTP::Status qw( is_server_error );
  7         4843  
  7         1322  
14 7     7   619 use Types::Standard qw( Bool CodeRef Enum InstanceOf Int );
  7         75851  
  7         85  
15 7     7   10034 use Types::URI qw( Uri );
  7         98460  
  7         67  
16 7     7   2954 use URI::QueryParam;
  7         885  
  7         213  
17 7     7   3997 use Web::Scraper qw( process scraper );
  7         522892  
  7         68  
18 7     7   1155 use WebService::PayPal::PaymentsAdvanced::Error::HostedForm ();
  7         24  
  7         134  
19 7     7   469 use WebService::PayPal::PaymentsAdvanced::Error::HTTP ();
  7         29  
  7         4785  
20              
21             has hosted_form_mode => (
22             is => 'ro',
23             isa => Enum [qw( LIVE TEST )],
24             predicate => '_has_hosted_form_mode',
25             );
26              
27             has hosted_form_uri => (
28             is => 'lazy',
29             isa => Uri,
30             init_arg => undef,
31             builder => '_build_hosted_form_uri',
32             );
33              
34             has payflow_link_uri => (
35             is => 'ro',
36             isa => Uri,
37             required => 1,
38             coerce => 1,
39             );
40              
41             has validate_hosted_form_uri => (
42             is => 'ro',
43             isa => Bool,
44             required => 1,
45             );
46              
47             # Retry up to this number of times when encountering 5xx HTTP errors.
48             has retry_attempts => (
49             is => 'ro',
50             isa => Int,
51             default => 0,
52             );
53              
54             # Callback to call after encountering a 5xx HTTP error. We call this prior to
55             # retrying the request.
56             # Parameters to the callback: HTTP::Response from the request that generated a
57             # 5xx response.
58             has retry_callback => (
59             is => 'ro',
60             isa => CodeRef,
61             default => sub {
62             sub { }
63             },
64             );
65              
66             with(
67             'WebService::PayPal::PaymentsAdvanced::Role::ClassFor',
68             'WebService::PayPal::PaymentsAdvanced::Role::HasTokens',
69             'WebService::PayPal::PaymentsAdvanced::Role::HasUA',
70             );
71              
72             sub _build_hosted_form_uri {
73 9     9   1915 my $self = shift;
74              
75 9         57 my $uri = $self->payflow_link_uri->clone;
76 9         277 $uri->query_param( SECURETOKEN => $self->secure_token, );
77 9         1814 $uri->query_param( SECURETOKENID => $self->secure_token_id, );
78              
79 9 100       1802 $uri->query_param( MODE => $self->hosted_form_mode, )
80             if $self->_has_hosted_form_mode;
81              
82 9 100       411 return $uri unless $self->validate_hosted_form_uri;
83              
84 8         32 my $res = $self->_make_http_request_with_retries($uri);
85              
86 7 100       26 unless ( $res->is_success ) {
87 1         11 $self->_class_for('Error::HTTP')->throw_from_http_response(
88             message_prefix => "hosted_form URI does not validate ($uri):",
89             http_response => $res,
90             request_uri => $uri,
91             );
92             }
93              
94             my $error_scraper = scraper {
95 6     6   103665 process( '.error', error => 'TEXT' );
96 6         74 };
97              
98 6         72 my $scraped_text = $error_scraper->scrape($res);
99              
100 6 100       173493 return $uri unless exists $scraped_text->{error};
101              
102 1         10 $self->_class_for('Error::HostedForm')->throw(
103             message =>
104             "hosted_form contains error message: $scraped_text->{error}",
105             http_response => $res,
106             http_status => $res->code,
107             request_uri => $uri,
108             );
109             }
110              
111             # Make an HTTP request to the given URI.
112             #
113             # Return the response if the request is successful. If the request fails with a
114             # 5xx error, retry. We retry up to the configured number of times. On
115             # encountering a non-5xx and non-success response, return the response
116             # immediately.
117             #
118             # Throw an error if we exhaust our retries.
119             sub _make_http_request_with_retries {
120 8     8   19 my $self = shift;
121 8         18 my $uri = shift;
122              
123 8         15 my $res;
124              
125             # +1 so we always try at least once.
126 8         43 for my $attempt ( 1 .. $self->retry_attempts + 1 ) {
127              
128             # For whatever reason on the PayPal side, HEAD isn't useful here.
129 9         1403 $res = $self->ua->get($uri);
130              
131 9 100       19788 if ( $res->is_success ) {
132 6         66 return $res;
133             }
134              
135             # We want to support retries only if there is a 5xx error.
136 3 100       34 if ( !is_server_error( $res->code ) ) {
137 1         15 return $res;
138             }
139              
140             # Don't call our callback if we won't be retrying. We'll throw an error.
141 2 100       39 last if $attempt == $self->retry_attempts + 1;
142              
143 1         14 my $cb = $self->retry_callback;
144 1         5 $cb->($res);
145             }
146              
147 1         7 $self->_class_for('Error::HTTP')->throw_from_http_response(
148             message_prefix => 'Made maximum number of HTTP requests. Tried '
149             . ( $self->retry_attempts + 1 )
150             . ' requests.',
151             http_response => $res,
152             request_uri => $uri,
153             );
154             }
155              
156             1;
157              
158             =pod
159              
160             =encoding UTF-8
161              
162             =head1 NAME
163              
164             WebService::PayPal::PaymentsAdvanced::Response::SecureToken - Response class for creating secure tokens
165              
166             =head1 VERSION
167              
168             version 0.000028
169              
170             =head1 SYNOPSIS
171              
172             my $ppa = WebService::PayPal::PaymentsAdvanced->new( ... );
173             my $response = $ppa->create_secure_token( ... );
174              
175             =head1 DESCRIPTION
176              
177             You should not create this response object directly. It will be provided to
178             you via L<WebService::PayPal::PaymentsAdvanced/<create_secure_token>.
179              
180             =head1 OPTIONS
181              
182             =head2 hosted_form_mode
183              
184             Sets the C<MODE> query parameter on C<hosted_form_uri>. This can be C<LIVE>
185             or C<TEST>.
186              
187             =head2 payflow_link_uri
188              
189             The URL for the PayflowLink web service. Can be a mocked URL.
190              
191             =head2 validate_hosted_form_uri
192              
193             C<Bool> which indicates whether we should pre-fetch the hosted form and do some
194             error checking (recommended).
195              
196             =head2 retry_attempts
197              
198             The number of HTTP retries to attempt if we encounter an error response. We
199             retry only when encountering HTTP 5xx responses.
200              
201             =head2 retry_callback
202              
203             A callback function we call prior to retrying the HTTP request to PayPal. We
204             call this function only when a retry will take place afterwards. Note we retry
205             only when there are retry attempts remaining, and only when encountering HTTP
206             5xx errors.
207              
208             This callback is useful if you want to know about each request failure.
209             Consider a case where the first request failed, and then a retry request
210             succeeded. If you want to know about the first failure, you can provide a
211             callback that we call prior to the retry. In this scenario, you may want your
212             callback function to write a message to a log.
213              
214             The callback will receive a single parameter, an HTTP::Response object. This is
215             the response to the request that failed.
216              
217             =head1 METHODS
218              
219             This module inherits from L<WebService::PayPal::PaymentsAdvanced::Response>,
220             please see its documentation for a list of the methods which it provides..
221              
222             =head2 hosted_form_uri
223              
224             Returns a L<URI> object which you can use either to insert an iframe into your
225             pages or redirect the user to PayPal directly in order to make a payment.
226              
227             use WebService::PayPal::PaymentsAdvanced;
228             my $payments = WebService::PayPal::PaymentsAdvanced->new(
229             validate_hosted_form_uri => 1, ... );
230              
231             my $response = $payments->create_secure_token(...);
232             my $uri = $response->hosted_form_uri;
233              
234             =head3 params
235              
236             A C<HashRef> of parameters which have been returned by PayPal.
237              
238             =head2 secure_token
239              
240             Returns the PayPal SECURETOKEN param.
241              
242             =head2 secure_token_id
243              
244             Returns the PayPal SECURETOKENID param.
245              
246             =head1 SUPPORT
247              
248             Bugs may be submitted through L<https://github.com/maxmind/webservice-paypal-paymentsadvanced/issues>.
249              
250             =head1 AUTHOR
251              
252             Olaf Alders <olaf@wundercounter.com>
253              
254             =head1 COPYRIGHT AND LICENSE
255              
256             This software is copyright (c) 2022 by MaxMind, Inc.
257              
258             This is free software; you can redistribute it and/or modify it under
259             the same terms as the Perl 5 programming language system itself.
260              
261             =cut
262              
263             __END__
264              
265             #ABSTRACT: Response class for creating secure tokens
266