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   186257 use Moo;
  7         7636  
  7         46  
4              
5 7     7   3963 use namespace::autoclean;
  7         1645  
  7         51  
6              
7             our $VERSION = '0.000027';
8              
9 7     7   712 use feature qw( state );
  7         17  
  7         906  
10              
11             extends 'WebService::PayPal::PaymentsAdvanced::Response';
12              
13 7     7   63 use HTTP::Status qw( is_server_error );
  7         15  
  7         1419  
14 7     7   702 use Type::Params qw( compile );
  7         92602  
  7         93  
15 7     7   2151 use Types::Standard qw( Bool CodeRef Enum InstanceOf Int );
  7         19  
  7         57  
16 7     7   9134 use Types::URI qw( Uri );
  7         103249  
  7         76  
17 7     7   2993 use URI::QueryParam;
  7         911  
  7         211  
18 7     7   3887 use Web::Scraper;
  7         515888  
  7         64  
19 7     7   1165 use WebService::PayPal::PaymentsAdvanced::Error::HTTP;
  7         22  
  7         151  
20 7     7   671 use WebService::PayPal::PaymentsAdvanced::Error::HostedForm;
  7         28  
  7         79  
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   1902 my $self = shift;
75              
76 9         73 my $uri = $self->payflow_link_uri->clone;
77 9         289 $uri->query_param( SECURETOKEN => $self->secure_token, );
78 9         1845 $uri->query_param( SECURETOKENID => $self->secure_token_id, );
79              
80 9 100       1783 $uri->query_param( MODE => $self->hosted_form_mode, )
81             if $self->_has_hosted_form_mode;
82              
83 9 100       431 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         13 $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   86330 process( '.error', error => 'TEXT' );
97 6         68 };
98              
99 6         77 my $scraped_text = $error_scraper->scrape($res);
100              
101 6 100       134708 return $uri unless exists $scraped_text->{error};
102              
103 1         5 $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   20 my $self = shift;
122 8         17 my $uri = shift;
123              
124 8         15 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         1564 $res = $self->ua->get($uri);
131              
132 9 100       19917 if ( $res->is_success ) {
133 6         73 return $res;
134             }
135              
136             # We want to support retries only if there is a 5xx error.
137 3 100       37 if ( !is_server_error( $res->code ) ) {
138 1         16 return $res;
139             }
140              
141             # Don't call our callback if we won't be retrying. We'll throw an error.
142 2 100       48 last if $attempt == $self->retry_attempts + 1;
143              
144 1         5 my $cb = $self->retry_callback;
145 1         4 $cb->($res);
146             }
147              
148 1         9 $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             =encoding UTF-8
162              
163             =head1 NAME
164              
165             WebService::PayPal::PaymentsAdvanced::Response::SecureToken - Response class for creating secure tokens
166              
167             =head1 VERSION
168              
169             version 0.000027
170              
171             =head1 SYNOPSIS
172              
173             my $ppa = WebService::PayPal::PaymentsAdvanced->new( ... );
174             my $response = $ppa->create_secure_token( ... );
175              
176             =head1 DESCRIPTION
177              
178             You should not create this response object directly. It will be provided to
179             you via L<WebService::PayPal::PaymentsAdvanced/<create_secure_token>.
180              
181             =head1 OPTIONS
182              
183             =head2 hosted_form_mode
184              
185             Sets the C<MODE> query parameter on C<hosted_form_uri>. This can be C<LIVE>
186             or C<TEST>.
187              
188             =head2 payflow_link_uri
189              
190             The URL for the PayflowLink web service. Can be a mocked URL.
191              
192             =head2 validate_hosted_form_uri
193              
194             C<Bool> which indicates whether we should pre-fetch the hosted form and do some
195             error checking (recommended).
196              
197             =head2 retry_attempts
198              
199             The number of HTTP retries to attempt if we encounter an error response. We
200             retry only when encountering HTTP 5xx responses.
201              
202             =head2 retry_callback
203              
204             A callback function we call prior to retrying the HTTP request to PayPal. We
205             call this function only when a retry will take place afterwards. Note we retry
206             only when there are retry attempts remaining, and only when encountering HTTP
207             5xx errors.
208              
209             This callback is useful if you want to know about each request failure.
210             Consider a case where the first request failed, and then a retry request
211             succeeded. If you want to know about the first failure, you can provide a
212             callback that we call prior to the retry. In this scenario, you may want your
213             callback function to write a message to a log.
214              
215             The callback will receive a single parameter, an HTTP::Response object. This is
216             the response to the request that failed.
217              
218             =head1 METHODS
219              
220             This module inherits from L<WebService::PayPal::PaymentsAdvanced::Response>,
221             please see its documentation for a list of the methods which it provides..
222              
223             =head2 hosted_form_uri
224              
225             Returns a L<URI> object which you can use either to insert an iframe into your
226             pages or redirect the user to PayPal directly in order to make a payment.
227              
228             use WebService::PayPal::PaymentsAdvanced;
229             my $payments = WebService::PayPal::PaymentsAdvanced->new(
230             validate_hosted_form_uri => 1, ... );
231              
232             my $response = $payments->create_secure_token(...);
233             my $uri = $response->hosted_form_uri;
234              
235             =head3 params
236              
237             A C<HashRef> of parameters which have been returned by PayPal.
238              
239             =head2 secure_token
240              
241             Returns the PayPal SECURETOKEN param.
242              
243             =head2 secure_token_id
244              
245             Returns the PayPal SECURETOKENID param.
246              
247             =head1 SUPPORT
248              
249             Bugs may be submitted through L<https://github.com/maxmind/webservice-paypal-paymentsadvanced/issues>.
250              
251             =head1 AUTHOR
252              
253             Olaf Alders <olaf@wundercounter.com>
254              
255             =head1 COPYRIGHT AND LICENSE
256              
257             This software is copyright (c) 2021 by MaxMind, Inc.
258              
259             This is free software; you can redistribute it and/or modify it under
260             the same terms as the Perl 5 programming language system itself.
261              
262             =cut
263              
264             __END__
265              
266             #ABSTRACT: Response class for creating secure tokens
267