1use bytes::Bytes;
2use hmac::{Hmac, Mac};
3use sha2::Sha256;
4
5use crate::{error::WiError, types::Event};
6
7type HmacSha256 = Hmac<Sha256>;
8
9const SIGNATURE_HEADER: &str = "x-wi-signature";
10
11pub fn verify_signature(body: &[u8], signature: &str, secret: &[u8]) -> bool {
19 let hex_part = match signature.strip_prefix("sha256=") {
20 Some(h) => h,
21 None => return false,
22 };
23
24 let expected = match hex::decode(hex_part) {
25 Ok(b) => b,
26 Err(_) => return false,
27 };
28
29 let mut mac = HmacSha256::new_from_slice(secret).expect("HMAC accepts any key length");
30 mac.update(body);
31 mac.verify_slice(&expected).is_ok()
32}
33
34pub fn parse_event(body: &[u8]) -> Result<Event, WiError> {
36 serde_json::from_slice(body).map_err(WiError::Serialize)
37}
38
39pub struct WebhookPayload {
41 pub event: Event,
42 pub raw: Bytes,
43}
44
45impl WebhookPayload {
46 pub fn from_parts(body: Bytes, signature: &str, secret: &[u8]) -> Option<Result<Self, WiError>> {
50 if !verify_signature(&body, signature, secret) {
51 return None;
52 }
53 Some(parse_event(&body).map(|event| WebhookPayload { event, raw: body }))
54 }
55
56 pub const HEADER: &'static str = SIGNATURE_HEADER;
58}