Skip to main content

Webhook

Trait Webhook 

Source
pub trait Webhook:
    Debug
    + Send
    + Sync {
    // Required methods
    fn provider(&self) -> ProviderId;
    fn verify<'life0, 'life1, 'life2, 'async_trait>(
        &'life0 self,
        delivery: &'life1 Delivery<'life2>,
    ) -> Pin<Box<dyn Future<Output = Result<Event, Error>> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait,
             'life1: 'async_trait,
             'life2: 'async_trait;
}
Expand description

Checks that a delivery is the provider’s, and says what it means.

Separate from Provider because it is a separate thing to hold: verification needs a webhook secret the API credentials do not carry, and a process that takes payments does not always handle the callbacks for them.

§Verification is not one mechanism

verify is async because for two of the four providers that implement it here, checking a delivery is a network call rather than a hash:

how a delivery is shown to be theirs
StripeHMAC-SHA256 over timestamp.body, with a tolerance window
PayTRHMAC-SHA256 over three of the notice’s fields
Mollienothing is signed — the delivery carries an identifier, and the payment is read back over the merchant’s own authenticated connection
PayPalPayPal verifies it, at /v1/notifications/verify-webhook-signature

A trait that took only (headers, body) -> Result<Event, Error> synchronously would fit the first two and force the other two to lie.

§Answering the provider is not this trait’s business

An Err here says do not act on this. It does not say what to answer: PayTR retries any reply that is not exactly OK for days, so a handler that turns ErrorKind::Untrusted into a 500 has arranged for a forged notice to be delivered again every hour. Answer the provider what the provider documents, and act only on Ok.

Required Methods§

Source

fn provider(&self) -> ProviderId

Which provider this verifies deliveries from.

Source

fn verify<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, delivery: &'life1 Delivery<'life2>, ) -> Pin<Box<dyn Future<Output = Result<Event, Error>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

Shows that a delivery is the provider’s, and reads what it says.

§Errors

ErrorKind::Untrusted for a delivery that cannot be shown to be theirs — a signature that does not match, one that is missing where the provider always sends one, or a timestamp outside the tolerance a replay would fall outside. Nothing has been read out of the body at that point, and nothing should be.

ErrorKind::Malformed for one that verifies and is then not the shape the provider documents. An event type this crate does not know is not that: it is EventKind::Other, and it is Ok.

Dyn Compatibility§

This trait is dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementors§