Business-Payr

 view release on metacpan or  search on metacpan

lib/Business/Payr/Webhook.pm  view on Meta::CPAN


=head1 DESCRIPTION

C<Business::Payr::Webhook> handles the receipt, cryptographic verification,
and parsing of webhook notifications sent by the Payr platform.

All webhooks are signed with HMAC-SHA256 using a secret shared between Payr
and your platform. The signature is carried in the C<X-Payr-Signature> HTTP
header. Verification is performed automatically during object construction; an
exception is thrown if the signature is missing, incorrect, or the payload
cannot be parsed.

B<Important:> You should always pass the B<raw request body> string as the
C<body> argument, before any deserialisation. Re-serialising a parsed
structure may produce different byte sequences and will cause verification to
fail.

Contact B<support@mypayr.co.uk> to configure your webhook endpoint URL and
receive your webhook secret.

=head1 SIGNATURE VERIFICATION DETAILS

Payr signs webhook payloads as follows:

=over

=item 1.

The JSON payload is serialised with compact separators (no spaces) and
lexicographically sorted keys.

=item 2.

An HMAC-SHA256 digest of the serialised payload is computed using the shared
webhook secret.

=item 3.

The hex-encoded digest is placed in the C<X-Payr-Signature> HTTP header.

=back

This module computes the same digest over the raw request body and compares
it against the header value using a constant-time comparison to prevent
timing attacks.

=head1 DEBUGGING

Set C<MOJO_CLIENT_DEBUG=1> for user agent and transport debug output when
fetching JWKS or other remote resources.

=cut

use strict;
use warnings;
use feature qw/ signatures /;

use Moose;
no warnings qw/ experimental::signatures /;

use namespace::autoclean;

use Carp                          qw/ croak confess /;
use Digest::SHA                   qw/ hmac_sha256_hex /;
use String::Compare::ConstantTime qw/ equals /;
use JSON;

use Business::Payr::Webhook::Payment;

=head1 ATTRIBUTES

=over

=item body (Str, required)

The raw (undecoded) HTTP request body string exactly as received from Payr.
Do B<not> deserialise and re-serialise this value before passing it in; the
signature is computed over the original byte sequence.

=item signature (Str, required)

The value of the C<X-Payr-Signature> HTTP header included in the webhook
request. This is a hex-encoded HMAC-SHA256 digest.

=item secret (Str, required)

Your Payr webhook signing secret. Keep this value secure and never expose it
in client-side code or version control. Contact B<support@mypayr.co.uk> to
obtain or rotate your secret.

=back

=cut

has [ qw/ body signature secret / ] => (
    is       => 'ro',
    isa      => 'Str',
    required => 1,
);

# Parsed payload hashref, populated during BUILD.
# Exposed as rw so that tests can inject a pre-baked payload without
# going through full JSON parsing (mirrors the TrueLayer Webhook pattern).
has '_payload' => (
    is       => 'rw',
    isa      => 'HashRef',
    required => 0,
);

=head1 METHODS

=head2 BUILD

Called automatically by Moose after construction. Verifies the HMAC-SHA256
signature and decodes the JSON payload. Throws an exception if either step
fails.

You do not need to call this method directly.

=cut



( run in 1.298 second using v1.01-cache-2.11-cpan-4ac696b4eb4 )