1use std::sync::Arc;
2
3use anyhow::{Result, anyhow};
4use async_trait::async_trait;
5use collections::HashMap;
6use parking_lot::Mutex;
7use uuid::Uuid;
8
9use crate::stripe_client::{
10 CreateCustomerParams, StripeCheckoutSession, StripeCheckoutSessionMode,
11 StripeCheckoutSessionPaymentMethodCollection, StripeClient,
12 StripeCreateCheckoutSessionLineItems, StripeCreateCheckoutSessionParams,
13 StripeCreateCheckoutSessionSubscriptionData, StripeCreateMeterEventParams, StripeCustomer,
14 StripeCustomerId, StripeMeter, StripeMeterId, StripePrice, StripePriceId, StripeSubscription,
15 StripeSubscriptionId, UpdateSubscriptionParams,
16};
17
18#[derive(Debug, Clone)]
19pub struct StripeCreateMeterEventCall {
20 pub identifier: Arc<str>,
21 pub event_name: Arc<str>,
22 pub value: u64,
23 pub stripe_customer_id: StripeCustomerId,
24 pub timestamp: Option<i64>,
25}
26
27#[derive(Debug, Clone)]
28pub struct StripeCreateCheckoutSessionCall {
29 pub customer: Option<StripeCustomerId>,
30 pub client_reference_id: Option<String>,
31 pub mode: Option<StripeCheckoutSessionMode>,
32 pub line_items: Option<Vec<StripeCreateCheckoutSessionLineItems>>,
33 pub payment_method_collection: Option<StripeCheckoutSessionPaymentMethodCollection>,
34 pub subscription_data: Option<StripeCreateCheckoutSessionSubscriptionData>,
35 pub success_url: Option<String>,
36}
37
38pub struct FakeStripeClient {
39 pub customers: Arc<Mutex<HashMap<StripeCustomerId, StripeCustomer>>>,
40 pub subscriptions: Arc<Mutex<HashMap<StripeSubscriptionId, StripeSubscription>>>,
41 pub update_subscription_calls:
42 Arc<Mutex<Vec<(StripeSubscriptionId, UpdateSubscriptionParams)>>>,
43 pub prices: Arc<Mutex<HashMap<StripePriceId, StripePrice>>>,
44 pub meters: Arc<Mutex<HashMap<StripeMeterId, StripeMeter>>>,
45 pub create_meter_event_calls: Arc<Mutex<Vec<StripeCreateMeterEventCall>>>,
46 pub create_checkout_session_calls: Arc<Mutex<Vec<StripeCreateCheckoutSessionCall>>>,
47}
48
49impl FakeStripeClient {
50 pub fn new() -> Self {
51 Self {
52 customers: Arc::new(Mutex::new(HashMap::default())),
53 subscriptions: Arc::new(Mutex::new(HashMap::default())),
54 update_subscription_calls: Arc::new(Mutex::new(Vec::new())),
55 prices: Arc::new(Mutex::new(HashMap::default())),
56 meters: Arc::new(Mutex::new(HashMap::default())),
57 create_meter_event_calls: Arc::new(Mutex::new(Vec::new())),
58 create_checkout_session_calls: Arc::new(Mutex::new(Vec::new())),
59 }
60 }
61}
62
63#[async_trait]
64impl StripeClient for FakeStripeClient {
65 async fn list_customers_by_email(&self, email: &str) -> Result<Vec<StripeCustomer>> {
66 Ok(self
67 .customers
68 .lock()
69 .values()
70 .filter(|customer| customer.email.as_deref() == Some(email))
71 .cloned()
72 .collect())
73 }
74
75 async fn create_customer(&self, params: CreateCustomerParams<'_>) -> Result<StripeCustomer> {
76 let customer = StripeCustomer {
77 id: StripeCustomerId(format!("cus_{}", Uuid::new_v4()).into()),
78 email: params.email.map(|email| email.to_string()),
79 };
80
81 self.customers
82 .lock()
83 .insert(customer.id.clone(), customer.clone());
84
85 Ok(customer)
86 }
87
88 async fn get_subscription(
89 &self,
90 subscription_id: &StripeSubscriptionId,
91 ) -> Result<StripeSubscription> {
92 self.subscriptions
93 .lock()
94 .get(subscription_id)
95 .cloned()
96 .ok_or_else(|| anyhow!("no subscription found for {subscription_id:?}"))
97 }
98
99 async fn update_subscription(
100 &self,
101 subscription_id: &StripeSubscriptionId,
102 params: UpdateSubscriptionParams,
103 ) -> Result<()> {
104 let subscription = self.get_subscription(subscription_id).await?;
105
106 self.update_subscription_calls
107 .lock()
108 .push((subscription.id, params));
109
110 Ok(())
111 }
112
113 async fn list_prices(&self) -> Result<Vec<StripePrice>> {
114 let prices = self.prices.lock().values().cloned().collect();
115
116 Ok(prices)
117 }
118
119 async fn list_meters(&self) -> Result<Vec<StripeMeter>> {
120 let meters = self.meters.lock().values().cloned().collect();
121
122 Ok(meters)
123 }
124
125 async fn create_meter_event(&self, params: StripeCreateMeterEventParams<'_>) -> Result<()> {
126 self.create_meter_event_calls
127 .lock()
128 .push(StripeCreateMeterEventCall {
129 identifier: params.identifier.into(),
130 event_name: params.event_name.into(),
131 value: params.payload.value,
132 stripe_customer_id: params.payload.stripe_customer_id.clone(),
133 timestamp: params.timestamp,
134 });
135
136 Ok(())
137 }
138
139 async fn create_checkout_session(
140 &self,
141 params: StripeCreateCheckoutSessionParams<'_>,
142 ) -> Result<StripeCheckoutSession> {
143 self.create_checkout_session_calls
144 .lock()
145 .push(StripeCreateCheckoutSessionCall {
146 customer: params.customer.cloned(),
147 client_reference_id: params.client_reference_id.map(|id| id.to_string()),
148 mode: params.mode,
149 line_items: params.line_items,
150 payment_method_collection: params.payment_method_collection,
151 subscription_data: params.subscription_data,
152 success_url: params.success_url.map(|url| url.to_string()),
153 });
154
155 Ok(StripeCheckoutSession {
156 url: Some("https://checkout.stripe.com/c/pay/cs_test_1".to_string()),
157 })
158 }
159}