//! Stripe billing helpers: fee estimation and timestamp conversion. /// Convert a Unix timestamp from Stripe into a UTC datetime, falling back to now. pub fn stripe_timestamp(ts: i64) -> chrono::DateTime { chrono::DateTime::from_timestamp(ts, 0).unwrap_or_else(chrono::Utc::now) } /// Estimate Stripe's processing fee and the net amount the creator receives. /// /// Returns `(fee_cents, creator_receives_cents)`. Uses the standard /// Stripe rate from [`constants`](crate::constants). pub fn estimate_stripe_fee(price_cents: i32) -> (i32, i32) { if price_cents <= 0 { return (0, 0); } let fee = (price_cents as f64 * crate::constants::STRIPE_FEE_PERCENTAGE + crate::constants::STRIPE_FEE_FIXED_CENTS) as i32; let creator_receives = (price_cents - fee).max(0); (fee.min(price_cents), creator_receives) } #[cfg(test)] mod tests { use super::*; // ── estimate_stripe_fee ── #[test] fn stripe_fee_standard_price() { let (fee, receives) = estimate_stripe_fee(1000); assert_eq!(fee, 59); assert_eq!(receives, 941); } #[test] fn stripe_fee_small_price() { let (fee, receives) = estimate_stripe_fee(100); assert_eq!(fee, 32); assert_eq!(receives, 68); } #[test] fn stripe_fee_zero_is_free() { let (fee, receives) = estimate_stripe_fee(0); assert_eq!(fee, 0); assert_eq!(receives, 0); } #[test] fn stripe_fee_negative_price() { let (fee, receives) = estimate_stripe_fee(-100); assert_eq!(fee, 0); assert_eq!(receives, 0); } #[test] fn stripe_fee_plus_receives_equals_price() { for price in [50, 100, 250, 500, 999, 1000, 2500, 5000, 10000, 50000] { let (fee, receives) = estimate_stripe_fee(price); assert_eq!( fee + receives, price, "fee + receives should equal price for {price} cents" ); } } // ── stripe_timestamp ── #[test] fn stripe_timestamp_zero() { assert_eq!(stripe_timestamp(0).timestamp(), 0); } #[test] fn stripe_timestamp_valid() { assert_eq!(stripe_timestamp(1_714_400_000).timestamp(), 1_714_400_000); } // ── Property-based tests ── proptest::proptest! { #[test] fn prop_stripe_fee_invariant(price in 1..=1_000_000i32) { let (fee, receives) = estimate_stripe_fee(price); proptest::prop_assert_eq!(fee + receives, price, "fee ({}) + receives ({}) must equal price ({})", fee, receives, price); proptest::prop_assert!(fee > 0, "Fee should be positive for price {}", price); proptest::prop_assert!(receives >= 0, "Receives should be non-negative"); } } }