use crate::{EntityType, PayrixClient, Result, SearchBuilder};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct Address {
pub address1: Option<String>,
pub address2: Option<String>,
pub city: Option<String>,
pub state: Option<String>,
pub zip: Option<String>,
pub country: Option<String>,
}
#[derive(Debug, Clone, Serialize)]
pub struct CustomerData {
pub first: Option<String>,
pub middle: Option<String>,
pub last: Option<String>,
pub email: String,
pub phone: String,
#[serde(flatten)]
pub address: Address,
pub custom: Option<String>,
}
pub async fn find_customers_by_custom_field(
client: &PayrixClient,
custom_value: &str,
) -> Result<Vec<serde_json::Value>> {
let search = SearchBuilder::new().field("custom", custom_value).build();
let customers = client
.search::<serde_json::Value>(EntityType::Customers, &search)
.await?;
Ok(customers)
}
pub async fn create_customer(
client: &PayrixClient,
merchant_id: &str,
login_id: &str,
customer_data: &CustomerData,
) -> Result<serde_json::Value> {
let clean_phone = customer_data
.phone
.chars()
.filter(|c| c.is_numeric())
.collect::<String>();
let data = serde_json::json!({
"login": login_id,
"merchant": merchant_id,
"first": customer_data.first,
"middle": customer_data.middle,
"last": customer_data.last,
"email": customer_data.email,
"custom": customer_data.custom,
"address1": customer_data.address.address1,
"address2": customer_data.address.address2,
"city": customer_data.address.city,
"state": customer_data.address.state,
"zip": customer_data.address.zip,
"country": customer_data.address.country,
"phone": clean_phone,
"inactive": 0,
"frozen": 0,
});
let customer = client
.create::<_, serde_json::Value>(EntityType::Customers, &data)
.await?;
tracing::info!(
"Created Payrix customer {}",
customer
.get("id")
.and_then(|i| i.as_str())
.unwrap_or("unknown")
);
Ok(customer)
}
pub async fn get_customer(
client: &PayrixClient,
customer_id: &str,
) -> Result<Option<serde_json::Value>> {
client
.get_one::<serde_json::Value>(EntityType::Customers, customer_id)
.await
}
pub async fn update_customer(
client: &PayrixClient,
customer_id: &str,
updates: &serde_json::Value,
) -> Result<serde_json::Value> {
let customer = client
.update::<_, serde_json::Value>(EntityType::Customers, customer_id, updates)
.await?;
tracing::info!("Updated Payrix customer {}", customer_id);
Ok(customer)
}
pub async fn delete_customer(client: &PayrixClient, customer_id: &str) -> Result<()> {
let _: serde_json::Value = client.remove(EntityType::Customers, customer_id).await?;
tracing::info!("Deleted Payrix customer {}", customer_id);
Ok(())
}
pub async fn delete_customers_by_custom_field(
client: &PayrixClient,
custom_value: &str,
) -> Result<()> {
let customers = find_customers_by_custom_field(client, custom_value).await?;
for customer in customers {
if let Some(id) = customer.get("id").and_then(|i| i.as_str()) {
let _: serde_json::Value = client.remove(EntityType::Customers, id).await?;
tracing::info!("Deleted Payrix customer {}", id);
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_customer_data_creation() {
let customer = CustomerData {
first: Some("John".to_string()),
middle: Some("Q".to_string()),
last: Some("Doe".to_string()),
email: "john@example.com".to_string(),
phone: "555-123-4567".to_string(),
address: Address {
address1: Some("123 Main St".to_string()),
address2: None,
city: Some("Boston".to_string()),
state: Some("MA".to_string()),
zip: Some("02101".to_string()),
country: Some("US".to_string()),
},
custom: Some("my-custom-id".to_string()),
};
assert_eq!(customer.first.unwrap(), "John");
assert_eq!(customer.custom.unwrap(), "my-custom-id");
}
#[test]
fn test_address_creation() {
let address = Address {
address1: Some("123 Main St".to_string()),
address2: None,
city: Some("Boston".to_string()),
state: Some("MA".to_string()),
zip: Some("02101".to_string()),
country: Some("US".to_string()),
};
assert_eq!(address.zip.unwrap(), "02101");
}
}