use crate::types::{CustomerId, Metadata};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize)]
pub struct CreateCustomerRequest {
pub customer: CreateCustomer,
}
#[derive(Debug, Clone, Serialize)]
pub struct UpdateCustomerRequest {
pub customer: UpdateCustomer,
}
#[derive(Debug, Clone, Deserialize)]
pub struct CustomerResponse {
pub data: CustomerData,
}
#[derive(Debug, Clone, Deserialize)]
pub struct CustomerData {
pub customer: Customer,
}
#[derive(Debug, Clone, Deserialize)]
pub struct SingleCustomerResponse {
pub customer: Customer,
}
#[derive(Debug, Clone, Deserialize)]
pub struct ListCustomersResponse {
pub data: Vec<Customer>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Customer {
pub id: CustomerId,
pub external_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub email: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub phone: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub organization_id: Option<String>,
#[serde(default)]
pub livemode: bool,
#[serde(default)]
pub archived: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub metadata: Option<Metadata>,
#[serde(rename = "createdAt")]
pub created_at: i64,
#[serde(rename = "updatedAt")]
pub updated_at: i64,
}
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CreateCustomer {
pub external_id: String,
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub email: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub phone: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub metadata: Option<Metadata>,
}
impl CreateCustomer {
pub fn new(external_id: impl Into<String>, name: impl Into<String>) -> Self {
Self {
external_id: external_id.into(),
name: name.into(),
email: None,
phone: None,
metadata: None,
}
}
pub fn email(mut self, email: impl Into<String>) -> Self {
self.email = Some(email.into());
self
}
pub fn phone(mut self, phone: impl Into<String>) -> Self {
self.phone = Some(phone.into());
self
}
pub fn metadata(mut self, key: impl Into<String>, value: serde_json::Value) -> Self {
self.metadata
.get_or_insert_with(Metadata::new)
.insert(key.into(), value);
self
}
pub fn with_metadata(mut self, metadata: Metadata) -> Self {
self.metadata = Some(metadata);
self
}
}
#[derive(Debug, Clone, Default, Serialize)]
pub struct UpdateCustomer {
#[serde(skip_serializing_if = "Option::is_none")]
pub email: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub phone: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub metadata: Option<Metadata>,
}
impl UpdateCustomer {
pub fn new() -> Self {
Self::default()
}
pub fn email(mut self, email: impl Into<String>) -> Self {
self.email = Some(email.into());
self
}
pub fn name(mut self, name: impl Into<String>) -> Self {
self.name = Some(name.into());
self
}
pub fn phone(mut self, phone: impl Into<String>) -> Self {
self.phone = Some(phone.into());
self
}
pub fn metadata(mut self, key: impl Into<String>, value: serde_json::Value) -> Self {
self.metadata
.get_or_insert_with(Metadata::new)
.insert(key.into(), value);
self
}
pub fn with_metadata(mut self, metadata: Metadata) -> Self {
self.metadata = Some(metadata);
self
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BillingDetails {
pub customer: Customer,
#[serde(default)]
pub subscriptions: Vec<serde_json::Value>,
#[serde(default)]
pub current_subscriptions: Vec<serde_json::Value>,
#[serde(default)]
pub invoices: Vec<serde_json::Value>,
#[serde(default)]
pub payment_methods: Vec<serde_json::Value>,
#[serde(default)]
pub purchases: Vec<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub pricing_model: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub catalog: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub billing_portal_url: Option<String>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_create_customer_builder() {
let params = CreateCustomer::new("user_123", "Test User")
.email("test@example.com")
.phone("+1-555-0123");
assert_eq!(params.external_id, "user_123");
assert_eq!(params.name, "Test User");
assert_eq!(params.email.as_deref(), Some("test@example.com"));
assert_eq!(params.phone.as_deref(), Some("+1-555-0123"));
}
#[test]
fn test_create_customer_with_metadata() {
let params = CreateCustomer::new("user_456", "Metadata User")
.email("test@example.com")
.metadata("plan", serde_json::json!("premium"))
.metadata("tier", serde_json::json!(3));
assert_eq!(params.external_id, "user_456");
assert_eq!(params.name, "Metadata User");
assert!(params.metadata.is_some());
let metadata = params.metadata.unwrap();
assert_eq!(metadata.len(), 2);
assert_eq!(metadata["plan"], "premium");
assert_eq!(metadata["tier"], 3);
}
#[test]
fn test_update_customer_builder() {
let params = UpdateCustomer::new()
.name("Updated Name")
.email("updated@example.com");
assert_eq!(params.name.as_deref(), Some("Updated Name"));
assert_eq!(params.email.as_deref(), Some("updated@example.com"));
}
#[test]
fn test_customer_serialization() {
let customer_json = r#"{
"id": "cus_123",
"externalId": "user_123",
"email": "test@example.com",
"name": "Test User",
"metadata": {},
"createdAt": 1640995200000,
"updatedAt": 1640995200000
}"#;
let customer: Customer = serde_json::from_str(customer_json).unwrap();
assert_eq!(customer.id.as_str(), "cus_123");
assert_eq!(customer.external_id, "user_123");
assert_eq!(customer.email.as_deref(), Some("test@example.com"));
assert_eq!(customer.name.as_deref(), Some("Test User"));
assert_eq!(customer.created_at, 1640995200000);
assert_eq!(customer.updated_at, 1640995200000);
}
}