File Coverage

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


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