tap_msg/message/
settle.rs

1//! Settle message type for the Transaction Authorization Protocol.
2//!
3//! This module defines the Settle message type, which is used
4//! for settling transactions in the TAP protocol.
5
6use crate::error::{Error, Result};
7use crate::TapMessage;
8use serde::{Deserialize, Serialize};
9
10/// Settle message body (TAIP-4).
11#[derive(Debug, Clone, Serialize, Deserialize, TapMessage)]
12#[tap(message_type = "https://tap.rsvp/schema/1.0#Settle")]
13pub struct Settle {
14    /// ID of the transaction being settled.
15    #[tap(thread_id)]
16    pub transaction_id: String,
17
18    /// Settlement ID (CAIP-220 identifier of the underlying settlement transaction).
19    pub settlement_id: String,
20
21    /// Optional amount settled. If specified, must be less than or equal to the original amount.
22    #[serde(skip_serializing_if = "Option::is_none")]
23    pub amount: Option<String>,
24}
25
26impl Settle {
27    /// Create a new Settle message
28    pub fn new(transaction_id: &str, settlement_id: &str) -> Self {
29        Self {
30            transaction_id: transaction_id.to_string(),
31            settlement_id: settlement_id.to_string(),
32            amount: None,
33        }
34    }
35
36    /// Create a new Settle message with an amount
37    pub fn with_amount(transaction_id: &str, settlement_id: &str, amount: &str) -> Self {
38        Self {
39            transaction_id: transaction_id.to_string(),
40            settlement_id: settlement_id.to_string(),
41            amount: Some(amount.to_string()),
42        }
43    }
44}
45
46impl Settle {
47    /// Custom validation for Settle messages
48    pub fn validate_settle(&self) -> Result<()> {
49        if self.transaction_id.is_empty() {
50            return Err(Error::Validation(
51                "Transaction ID is required in Settle".to_string(),
52            ));
53        }
54
55        if self.settlement_id.is_empty() {
56            return Err(Error::Validation(
57                "Settlement ID is required in Settle".to_string(),
58            ));
59        }
60
61        if let Some(amount) = &self.amount {
62            if amount.is_empty() {
63                return Err(Error::Validation(
64                    "Amount must be a valid number".to_string(),
65                ));
66            }
67
68            // Validate amount is a positive number if provided
69            match amount.parse::<f64>() {
70                Ok(amount) if amount <= 0.0 => {
71                    return Err(Error::Validation("Amount must be positive".to_string()));
72                }
73                Err(_) => {
74                    return Err(Error::Validation(
75                        "Amount must be a valid number".to_string(),
76                    ));
77                }
78                _ => {}
79            }
80        }
81
82        Ok(())
83    }
84}