paykit_lib/
delivery_confirmation.rs1use serde::{Deserialize, Serialize};
4
5use crate::{
6 validation::{invalid_plaintext_json, validate_wire_version_kind},
7 EventId, PaykitAppId, PaykitError, PrivateMessageKind, Result,
8};
9
10#[derive(Clone, Debug, PartialEq, Eq)]
19pub struct DeliveryConfirmation {
20 app_id: PaykitAppId,
21 event_id: EventId,
22 payload_hash: String,
23}
24
25impl DeliveryConfirmation {
26 pub fn new(app_id: PaykitAppId, event_id: EventId, payload_hash: String) -> Result<Self> {
34 let valid_hash = payload_hash.strip_prefix("sha256:").is_some_and(|hex| {
35 hex.len() == 64
36 && hex
37 .bytes()
38 .all(|byte| byte.is_ascii_digit() || (b'a'..=b'f').contains(&byte))
39 });
40 if !valid_hash {
41 return Err(PaykitError::Validation(
42 "Delivery Confirmation payload_hash must be sha256: followed by 64 lowercase hex digits"
43 .into(),
44 ));
45 }
46 Ok(Self {
47 app_id,
48 event_id,
49 payload_hash,
50 })
51 }
52
53 pub fn app_id(&self) -> &PaykitAppId {
55 &self.app_id
56 }
57
58 pub fn event_id(&self) -> &EventId {
60 &self.event_id
61 }
62
63 pub fn payload_hash(&self) -> &str {
65 &self.payload_hash
66 }
67}
68
69#[derive(Serialize, Deserialize)]
70#[serde(deny_unknown_fields)]
71struct DeliveryConfirmationWire {
72 version: u8,
73 kind: String,
74 app_id: String,
75 event_id: String,
76 payload_hash: String,
77}
78
79pub fn parse_delivery_confirmation_json(json: &str) -> Result<DeliveryConfirmation> {
84 let invalid = || invalid_plaintext_json("invalid Delivery Confirmation JSON");
87 let wire: DeliveryConfirmationWire = serde_json::from_str(json).map_err(|_| invalid())?;
88 validate_wire_version_kind(
89 wire.version,
90 &wire.kind,
91 PrivateMessageKind::DeliveryConfirmation,
92 "Delivery Confirmation",
93 )?;
94 DeliveryConfirmation::new(
95 PaykitAppId::new(wire.app_id).map_err(|_| invalid())?,
96 EventId::new(wire.event_id).map_err(|_| invalid())?,
97 wire.payload_hash,
98 )
99 .map_err(|_| invalid())
100}
101
102pub fn serialize_delivery_confirmation(confirmation: &DeliveryConfirmation) -> Result<String> {
104 let wire = DeliveryConfirmationWire {
105 version: 1,
106 kind: PrivateMessageKind::DeliveryConfirmation.as_str().to_owned(),
107 app_id: confirmation.app_id.as_str().to_owned(),
108 event_id: confirmation.event_id.as_str().to_owned(),
109 payload_hash: confirmation.payload_hash.clone(),
110 };
111 serde_json::to_string(&wire)
112 .map_err(|_| invalid_plaintext_json("failed to serialize Delivery Confirmation JSON"))
113}
114
115#[cfg(test)]
116mod tests;