use crate::network::protocol::{
GetPaymentRequestMessage, PaymentACKMessage, PaymentMessage, PaymentRequestMessage,
};
use crate::payment::processor::PaymentProcessor;
use anyhow::Result;
use blvm_protocol::payment::Bip70Error;
use hex;
use std::sync::Arc;
pub async fn handle_get_payment_request(
request: &GetPaymentRequestMessage,
processor: Option<Arc<PaymentProcessor>>,
) -> Result<PaymentRequestMessage> {
let processor = processor.ok_or_else(|| anyhow::anyhow!("Payment processor not available"))?;
let payment_id = hex::encode(&request.payment_id);
let payment_request = processor
.get_payment_request(&payment_id)
.await
.map_err(|e| anyhow::anyhow!("Failed to get payment request: {}", e))?;
let merchant_signature = match payment_request.signature.clone() {
Some(sig) if !sig.is_empty() => sig,
_ => {
return Err(anyhow::anyhow!(
"PaymentRequest for {} is unsigned; refusing to forward unsigned BIP70 payload over P2P",
payment_id
));
}
};
let merchant_pubkey = payment_request
.merchant_pubkey
.clone()
.unwrap_or_else(|| request.merchant_pubkey.clone());
Ok(PaymentRequestMessage {
payment_request,
payment_id: request.payment_id.clone(),
merchant_pubkey,
merchant_signature,
#[cfg(feature = "ctv")]
covenant_proof: None, })
}
pub async fn handle_payment(
payment_msg: &PaymentMessage,
processor: Option<Arc<PaymentProcessor>>,
merchant_private_key: Option<&[u8; 32]>,
) -> Result<PaymentACKMessage> {
let processor = processor.ok_or_else(|| anyhow::anyhow!("Payment processor not available"))?;
let payment_id = hex::encode(&payment_msg.payment_id);
let payment_ack = processor
.process_payment(
payment_msg.payment.clone(),
payment_id,
merchant_private_key,
)
.await
.map_err(|e| anyhow::anyhow!("Payment processing failed: {}", e))?;
let merchant_signature = match payment_ack.signature.clone() {
Some(sig) if !sig.is_empty() => sig,
_ => {
return Err(anyhow::anyhow!(
"PaymentACK is unsigned (no merchant_key provided?); refusing to forward unsigned BIP70 payload over P2P"
));
}
};
Ok(PaymentACKMessage {
payment_ack,
payment_id: payment_msg.payment_id.clone(),
merchant_signature,
})
}
pub fn validate_payment_request_message(msg: &PaymentRequestMessage) -> Result<(), Bip70Error> {
use blvm_protocol::payment::PaymentProtocolClient;
PaymentProtocolClient::validate_payment_request(
&msg.payment_request,
Some(&msg.merchant_pubkey),
)
}
pub fn validate_payment_ack_message(
ack: &PaymentACKMessage,
merchant_pubkey: &[u8],
) -> Result<(), Bip70Error> {
use blvm_protocol::payment::PaymentProtocolClient;
PaymentProtocolClient::validate_payment_ack(
&ack.payment_ack,
&ack.merchant_signature,
merchant_pubkey,
)
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn get_payment_request_requires_processor() {
let request = GetPaymentRequestMessage {
merchant_pubkey: vec![0x02; 33],
payment_id: vec![0xab; 32],
network: "regtest".into(),
};
assert!(handle_get_payment_request(&request, None).await.is_err());
}
#[tokio::test]
async fn handle_payment_requires_processor() {
let payment = PaymentMessage {
payment_id: vec![0xcd; 32],
payment: blvm_protocol::payment::Payment {
merchant_data: None,
transactions: vec![],
refund_to: None,
memo: None,
},
customer_signature: None,
};
assert!(handle_payment(&payment, None, None).await.is_err());
}
}