Business-Payr
view release on metacpan or search on metacpan
lib/Business/Payr/Request.pm view on Meta::CPAN
$body,
$headers = undef,
$method = 'POST',
) {
my $ua = $self->_ua;
my $res = $ua->start( $ua->build_tx(
$method,
$url,
{
'Accept' => 'application/json; charset=UTF-8',
'Content-Type' => 'application/json; charset=UTF-8',
@{ $headers // [] },
},
# Mojo::UserAgent::Transactor::tx calls $self->generators and then the
# callbacks based on count of @_, and does not expect undef here
( defined $body ? ($body) : () ),
) )->result;
# Possibly a redirect loop - this should be very rare
if ( $res->code == 301 ) {
croak( "Payr $method $url failed > $MAX_REDIRECTS levels of redirect" );
}
my $code = $res->code;
# No content
return if $code == 204;
my $type = $res->headers->content_type;
croak( "Payr $method $url returned $code with no MIME type" )
unless defined $type;
$body = $res->body;
croak( "Payr $method $url returned $code $type not JSON, status line: "
. $res->message )
unless $type =~ m!\Aapplication/json\b!i;
croak( "Payr $method $url returned $code with an empty body" )
unless length $body;
my $res_content = try sub {
JSON->new->canonical->decode( $body );
},
catch_default sub {
croak( "Payr $method $url returned $code with malformed JSON"
. " length @{[ length $body ]}: $_" );
};
croak( "Payr $method $url returned $code JSON $res_content" )
unless ref $res_content eq 'HASH';
return $res_content
if $res->is_success;
# --- Error handling ---
# Payr has several documented error response formats, try each in turn.
# Authentication / authorisation errors:
# { "detail": "Invalid token." }
# { "detail": "Authentication credentials were not provided." }
if ( my $detail = $res_content->{detail} ) {
croak( "Payr $method $url returned $code: $detail" );
}
# Processing errors:
# { "error": "Failed to download agreement document from provided URL" }
if ( my $error = $res_content->{error} ) {
croak( "Payr $method $url returned $code: $error" );
}
# Field-level validation errors:
# { "email": ["This field is required."], "tenant": [...] }
# or nested within tenant / installment arrays:
# { "tenant": [ { "frequency": ["\"weekly\" is not a valid choice."] } ] }
my @messages;
for my $field ( sort keys %$res_content ) {
my $errors = $res_content->{$field};
# Only treat array values as field-level validation errors.
# Scalar values do not match any documented Payr validation error
# format, so skip them here and let them fall through to the generic
# "unknown JSON keys" fallback below.
next unless ref $errors eq 'ARRAY';
for my $err ( @$errors ) {
if ( ref $err eq 'HASH' ) {
# Nested validation error (e.g. inside a tenant object)
for my $nested_field ( sort keys %$err ) {
my $nested_errors = $err->{$nested_field};
my $msgs = ref $nested_errors
? join( ', ', @$nested_errors )
: $nested_errors;
push @messages, "$field.$nested_field: $msgs";
}
}
else {
push @messages, "$field: $err";
}
}
}
if ( @messages ) {
croak( "Payr $method $url returned $code: " . join( "; ", @messages ) );
}
# Fallback for any undocumented response shape
croak( "Payr $method $url returned $code with JSON keys "
. join( ', ', map { "'$_'" } sort keys %$res_content )
. ' and status line: ' . $res->message );
}
1;
# vim: ts=4:sw=4:et
( run in 1.101 second using v1.01-cache-2.11-cpan-007c89162af )