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 | |
|---|---|
| Stripe | HMAC-SHA256 over timestamp.body, with a tolerance window |
| PayTR | HMAC-SHA256 over three of the notice’s fields |
| Mollie | nothing is signed — the delivery carries an identifier, and the payment is read back over the merchant’s own authenticated connection |
| PayPal | PayPal 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§
Sourcefn provider(&self) -> ProviderId
fn provider(&self) -> ProviderId
Which provider this verifies deliveries from.
Sourcefn 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,
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".