use axum::routing::{get, post};
use axum::Router;
use crate::custom_handlers::{
cache_post_batch_mint, cache_post_melt_custom, cache_post_mint_custom,
get_check_melt_custom_quote, get_check_mint_custom_quote, post_batch_check_mint_quote,
post_melt_custom_quote, post_mint_custom_quote,
};
use crate::MintState;
pub fn create_custom_routers(state: MintState, custom_methods: Vec<String>) -> Router<MintState> {
tracing::info!(
"Creating routes for {} custom payment methods: {:?}",
custom_methods.len(),
custom_methods
);
Router::new()
.route("/mint/quote/{method}", post(post_mint_custom_quote))
.route(
"/mint/quote/{method}/{quote_id}",
get(get_check_mint_custom_quote),
)
.route(
"/mint/quote/{method}/check",
post(post_batch_check_mint_quote),
)
.route("/mint/{method}", post(cache_post_mint_custom))
.route("/mint/{method}/batch", post(cache_post_batch_mint))
.route("/melt/quote/{method}", post(post_melt_custom_quote))
.route(
"/melt/quote/{method}/{quote_id}",
get(get_check_melt_custom_quote),
)
.route("/melt/{method}", post(cache_post_melt_custom))
.with_state(state)
}
pub fn validate_custom_method_names(methods: &[String]) -> Result<(), String> {
for method in methods {
if !method
.chars()
.all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_')
{
return Err(format!(
"Custom payment method name '{}' contains invalid characters. Only alphanumeric, '-', and '_' are allowed.",
method
));
}
if method.is_empty() {
return Err("Custom payment method name cannot be empty".to_string());
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use cdk::nuts::nut00::KnownMethod;
use cdk::nuts::PaymentMethod;
use super::*;
#[test]
fn test_validate_custom_method_names_valid() {
assert!(validate_custom_method_names(&["paypal".to_string()]).is_ok());
assert!(
validate_custom_method_names(&["venmo".to_string(), "cashapp".to_string()]).is_ok()
);
assert!(validate_custom_method_names(&["my-method".to_string()]).is_ok());
assert!(validate_custom_method_names(&["my_method".to_string()]).is_ok());
assert!(validate_custom_method_names(&["method123".to_string()]).is_ok());
}
#[test]
fn test_validate_custom_method_names_bolt11_bolt12_allowed() {
assert!(validate_custom_method_names(&[
PaymentMethod::Known(KnownMethod::Bolt11).to_string()
])
.is_ok());
assert!(validate_custom_method_names(&[
PaymentMethod::Known(KnownMethod::Bolt12).to_string()
])
.is_ok());
assert!(validate_custom_method_names(&[
"paypal".to_string(),
PaymentMethod::Known(KnownMethod::Bolt11).to_string()
])
.is_ok());
}
#[test]
fn test_validate_custom_method_names_invalid_chars() {
assert!(validate_custom_method_names(&["pay/pal".to_string()]).is_err());
assert!(validate_custom_method_names(&["pay pal".to_string()]).is_err());
assert!(validate_custom_method_names(&["pay@pal".to_string()]).is_err());
}
#[test]
fn test_validate_custom_method_names_empty() {
assert!(validate_custom_method_names(&["".to_string()]).is_err());
}
#[test]
fn test_validate_custom_method_names_multiple_invalid() {
assert!(validate_custom_method_names(&[
"valid".to_string(),
"in valid".to_string(),
"also-valid".to_string()
])
.is_err());
}
#[test]
fn test_validate_custom_method_names_special_chars() {
assert!(validate_custom_method_names(&["pay.pal".to_string()]).is_err());
assert!(validate_custom_method_names(&["pay+pal".to_string()]).is_err());
assert!(validate_custom_method_names(&["pay$pal".to_string()]).is_err());
assert!(validate_custom_method_names(&["pay%pal".to_string()]).is_err());
assert!(validate_custom_method_names(&["pay&pal".to_string()]).is_err());
assert!(validate_custom_method_names(&["pay*pal".to_string()]).is_err());
assert!(validate_custom_method_names(&["pay(pal".to_string()]).is_err());
assert!(validate_custom_method_names(&["pay)pal".to_string()]).is_err());
assert!(validate_custom_method_names(&["pay=pal".to_string()]).is_err());
assert!(validate_custom_method_names(&["pay#pal".to_string()]).is_err());
}
#[test]
fn test_validate_custom_method_names_edge_cases() {
assert!(validate_custom_method_names(&["a".to_string()]).is_ok());
assert!(validate_custom_method_names(&["1".to_string()]).is_ok());
assert!(validate_custom_method_names(&["-".to_string()]).is_ok());
assert!(validate_custom_method_names(&["_".to_string()]).is_ok());
assert!(validate_custom_method_names(&["---".to_string()]).is_ok());
assert!(validate_custom_method_names(&["___".to_string()]).is_ok());
let long_name = "a".repeat(100);
assert!(validate_custom_method_names(&[long_name]).is_ok());
}
#[test]
fn test_validate_custom_method_names_mixed_valid() {
assert!(validate_custom_method_names(&[
"paypal".to_string(),
"cash-app".to_string(),
"venmo_pay".to_string(),
"method123".to_string(),
"UPPERCASE".to_string(),
])
.is_ok());
}
#[test]
fn test_validate_custom_method_names_error_messages() {
let result = validate_custom_method_names(&["pay/pal".to_string()]);
assert!(result.is_err());
let err = result.unwrap_err();
assert!(err.contains("pay/pal"));
assert!(err.contains("invalid characters"));
let result = validate_custom_method_names(&["".to_string()]);
assert!(result.is_err());
let err = result.unwrap_err();
assert!(err.contains("empty"));
}
#[test]
fn test_validate_custom_method_names_unicode() {
assert!(validate_custom_method_names(&["café".to_string()]).is_err());
assert!(validate_custom_method_names(&["北京".to_string()]).is_err());
assert!(validate_custom_method_names(&["🚀".to_string()]).is_err());
}
#[test]
fn test_validate_custom_method_names_empty_list() {
assert!(validate_custom_method_names(&[]).is_ok());
}
#[test]
fn test_create_custom_routers_method_list() {
let custom_methods = ["paypal".to_string(), "venmo".to_string()];
let methods_str = custom_methods
.iter()
.map(|m| m.as_str())
.collect::<Vec<_>>()
.join(", ");
assert!(methods_str.contains("paypal"));
assert!(methods_str.contains("venmo"));
assert_eq!(methods_str, "paypal, venmo");
}
}