Skip to main content

max / makenotwork

2.8 KB · 93 lines History Blame Raw
1 //! Stripe billing helpers: fee estimation and timestamp conversion.
2
3 /// Convert a Unix timestamp from Stripe into a UTC datetime, falling back to now.
4 pub fn stripe_timestamp(ts: i64) -> chrono::DateTime<chrono::Utc> {
5 chrono::DateTime::from_timestamp(ts, 0).unwrap_or_else(chrono::Utc::now)
6 }
7
8 /// Estimate Stripe's processing fee and the net amount the creator receives.
9 ///
10 /// Returns `(fee_cents, creator_receives_cents)`. Uses the standard
11 /// Stripe rate from [`constants`](crate::constants).
12 pub fn estimate_stripe_fee(price_cents: i32) -> (i32, i32) {
13 if price_cents <= 0 {
14 return (0, 0);
15 }
16 let fee = (price_cents as f64 * crate::constants::STRIPE_FEE_PERCENTAGE
17 + crate::constants::STRIPE_FEE_FIXED_CENTS) as i32;
18 let creator_receives = (price_cents - fee).max(0);
19 (fee.min(price_cents), creator_receives)
20 }
21
22 #[cfg(test)]
23 mod tests {
24 use super::*;
25
26 // ── estimate_stripe_fee ──
27
28 #[test]
29 fn stripe_fee_standard_price() {
30 let (fee, receives) = estimate_stripe_fee(1000);
31 assert_eq!(fee, 59);
32 assert_eq!(receives, 941);
33 }
34
35 #[test]
36 fn stripe_fee_small_price() {
37 let (fee, receives) = estimate_stripe_fee(100);
38 assert_eq!(fee, 32);
39 assert_eq!(receives, 68);
40 }
41
42 #[test]
43 fn stripe_fee_zero_is_free() {
44 let (fee, receives) = estimate_stripe_fee(0);
45 assert_eq!(fee, 0);
46 assert_eq!(receives, 0);
47 }
48
49 #[test]
50 fn stripe_fee_negative_price() {
51 let (fee, receives) = estimate_stripe_fee(-100);
52 assert_eq!(fee, 0);
53 assert_eq!(receives, 0);
54 }
55
56 #[test]
57 fn stripe_fee_plus_receives_equals_price() {
58 for price in [50, 100, 250, 500, 999, 1000, 2500, 5000, 10000, 50000] {
59 let (fee, receives) = estimate_stripe_fee(price);
60 assert_eq!(
61 fee + receives,
62 price,
63 "fee + receives should equal price for {price} cents"
64 );
65 }
66 }
67
68 // ── stripe_timestamp ──
69
70 #[test]
71 fn stripe_timestamp_zero() {
72 assert_eq!(stripe_timestamp(0).timestamp(), 0);
73 }
74
75 #[test]
76 fn stripe_timestamp_valid() {
77 assert_eq!(stripe_timestamp(1_714_400_000).timestamp(), 1_714_400_000);
78 }
79
80 // ── Property-based tests ──
81
82 proptest::proptest! {
83 #[test]
84 fn prop_stripe_fee_invariant(price in 1..=1_000_000i32) {
85 let (fee, receives) = estimate_stripe_fee(price);
86 proptest::prop_assert_eq!(fee + receives, price,
87 "fee ({}) + receives ({}) must equal price ({})", fee, receives, price);
88 proptest::prop_assert!(fee > 0, "Fee should be positive for price {}", price);
89 proptest::prop_assert!(receives >= 0, "Receives should be non-negative");
90 }
91 }
92 }
93