tap_msg/message/
revert.rs

1//! Revert message type for the Transaction Authorization Protocol.
2//!
3//! This module defines the Revert message type, which is used
4//! for requesting reversal of settled transactions in the TAP protocol.
5
6use chrono::Utc;
7use serde::{Deserialize, Serialize};
8
9use crate::didcomm::PlainMessage;
10use crate::error::{Error, Result};
11use crate::impl_tap_message;
12use crate::message::tap_message_trait::TapMessageBody;
13
14/// Revert message body (TAIP-4).
15#[derive(Debug, Clone, Serialize, Deserialize)]
16pub struct Revert {
17    /// ID of the transfer being reverted.
18    pub transaction_id: String,
19
20    /// Settlement address in CAIP-10 format to return the funds to.
21    pub settlement_address: String,
22
23    /// Reason for the reversal request.
24    pub reason: String,
25
26    /// Optional note.
27    #[serde(skip_serializing_if = "Option::is_none")]
28    pub note: Option<String>,
29}
30
31impl Revert {
32    /// Create a new Revert message
33    pub fn new(transaction_id: &str, settlement_address: &str, reason: &str) -> Self {
34        Self {
35            transaction_id: transaction_id.to_string(),
36            settlement_address: settlement_address.to_string(),
37            reason: reason.to_string(),
38            note: None,
39        }
40    }
41
42    /// Create a new Revert message with a note
43    pub fn with_note(
44        transaction_id: &str,
45        settlement_address: &str,
46        reason: &str,
47        note: &str,
48    ) -> Self {
49        Self {
50            transaction_id: transaction_id.to_string(),
51            settlement_address: settlement_address.to_string(),
52            reason: reason.to_string(),
53            note: Some(note.to_string()),
54        }
55    }
56}
57
58impl TapMessageBody for Revert {
59    fn message_type() -> &'static str {
60        "https://tap.rsvp/schema/1.0#revert"
61    }
62
63    fn validate(&self) -> Result<()> {
64        if self.transaction_id.is_empty() {
65            return Err(Error::Validation(
66                "Transaction ID is required in Revert".to_string(),
67            ));
68        }
69
70        if self.settlement_address.is_empty() {
71            return Err(Error::Validation(
72                "Settlement address is required in Revert".to_string(),
73            ));
74        }
75
76        if self.reason.is_empty() {
77            return Err(Error::Validation(
78                "Reason is required in Revert".to_string(),
79            ));
80        }
81
82        Ok(())
83    }
84
85    fn to_didcomm(&self, from_did: &str) -> Result<PlainMessage> {
86        // Create a JSON representation of self with explicit type field
87        let mut body_json =
88            serde_json::to_value(self).map_err(|e| Error::SerializationError(e.to_string()))?;
89
90        // Ensure the @type field is correctly set in the body
91        if let Some(body_obj) = body_json.as_object_mut() {
92            body_obj.insert(
93                "@type".to_string(),
94                serde_json::Value::String(Self::message_type().to_string()),
95            );
96        }
97
98        // Create a new message with a random ID
99        let id = uuid::Uuid::new_v4().to_string();
100        let created_time = Utc::now().timestamp() as u64;
101
102        // The from field is required in our PlainMessage
103        let from = from_did.to_string();
104
105        // Create the message
106        let message = PlainMessage {
107            id,
108            typ: "application/didcomm-plain+json".to_string(),
109            type_: Self::message_type().to_string(),
110            from,
111            to: Vec::new(), // Empty recipients
112            thid: Some(self.transaction_id.clone()),
113            pthid: None,
114            created_time: Some(created_time),
115            expires_time: None,
116            extra_headers: std::collections::HashMap::new(),
117            from_prior: None,
118            body: body_json,
119            attachments: None,
120        };
121
122        Ok(message)
123    }
124}
125
126impl_tap_message!(Revert);