use crate::client::BscScanClient;
use crate::error::Result;
use crate::payment::models::{PaymentRequest, PaymentStatus};
use crate::payment::verification::{PaymentVerifier, VerificationResult};
use std::sync::Arc;
use std::time::Duration;
use tokio::time::sleep;
pub struct PaymentMonitor {
verifier: PaymentVerifier,
poll_interval: Duration,
}
impl PaymentMonitor {
pub fn new(client: BscScanClient, poll_interval: Duration) -> Self {
Self {
verifier: PaymentVerifier::new(client),
poll_interval,
}
}
pub fn builder() -> PaymentMonitorBuilder {
PaymentMonitorBuilder::default()
}
pub async fn start_monitoring<F>(&self, request: PaymentRequest, callback: F) -> Result<()>
where
F: Fn(PaymentStatus) + Send + Sync,
{
let callback = Arc::new(callback);
let mut last_status: Option<PaymentStatus> = None;
loop {
let result = self.verifier.verify_payment(&request).await?;
let current_status = match result {
VerificationResult::NotFound => PaymentStatus::Pending,
VerificationResult::Pending {
tx_hash,
confirmations,
} => PaymentStatus::Detected {
tx_hash,
confirmations,
},
VerificationResult::Confirmed {
tx_hash,
confirmations,
} => PaymentStatus::Confirmed {
tx_hash,
confirmations,
},
VerificationResult::Failed { reason } => PaymentStatus::Failed { reason },
};
if last_status.as_ref() != Some(¤t_status) {
callback(current_status.clone());
last_status = Some(current_status.clone());
}
if current_status.is_finalized() {
break;
}
sleep(self.poll_interval).await;
}
Ok(())
}
pub async fn check_payment_status(&self, request: &PaymentRequest) -> Result<PaymentStatus> {
let result = self.verifier.verify_payment(request).await?;
Ok(match result {
VerificationResult::NotFound => PaymentStatus::Pending,
VerificationResult::Pending {
tx_hash,
confirmations,
} => PaymentStatus::Detected {
tx_hash,
confirmations,
},
VerificationResult::Confirmed {
tx_hash,
confirmations,
} => PaymentStatus::Confirmed {
tx_hash,
confirmations,
},
VerificationResult::Failed { reason } => PaymentStatus::Failed { reason },
})
}
}
#[derive(Default)]
pub struct PaymentMonitorBuilder {
client: Option<BscScanClient>,
poll_interval: Option<Duration>,
}
impl PaymentMonitorBuilder {
pub fn client(mut self, client: BscScanClient) -> Self {
self.client = Some(client);
self
}
pub fn poll_interval(mut self, interval: Duration) -> Self {
self.poll_interval = Some(interval);
self
}
pub fn build(self) -> PaymentMonitor {
PaymentMonitor::new(
self.client.expect("BscScanClient is required"),
self.poll_interval.unwrap_or(Duration::from_secs(10)),
)
}
}