1use std::str::FromStr as _;
2use std::sync::Arc;
3
4use anyhow::{Context as _, Result, anyhow};
5use async_trait::async_trait;
6use serde::Serialize;
7use stripe::{
8 CreateCustomer, Customer, CustomerId, ListCustomers, Price, PriceId, Recurring, Subscription,
9 SubscriptionId, SubscriptionItem, SubscriptionItemId, UpdateSubscriptionItems,
10 UpdateSubscriptionTrialSettings, UpdateSubscriptionTrialSettingsEndBehavior,
11 UpdateSubscriptionTrialSettingsEndBehaviorMissingPaymentMethod,
12};
13
14use crate::stripe_client::{
15 CreateCustomerParams, StripeClient, StripeCreateMeterEventParams, StripeCustomer,
16 StripeCustomerId, StripeMeter, StripePrice, StripePriceId, StripePriceRecurring,
17 StripeSubscription, StripeSubscriptionId, StripeSubscriptionItem, StripeSubscriptionItemId,
18 UpdateSubscriptionParams,
19};
20
21pub struct RealStripeClient {
22 client: Arc<stripe::Client>,
23}
24
25impl RealStripeClient {
26 pub fn new(client: Arc<stripe::Client>) -> Self {
27 Self { client }
28 }
29}
30
31#[async_trait]
32impl StripeClient for RealStripeClient {
33 async fn list_customers_by_email(&self, email: &str) -> Result<Vec<StripeCustomer>> {
34 let response = Customer::list(
35 &self.client,
36 &ListCustomers {
37 email: Some(email),
38 ..Default::default()
39 },
40 )
41 .await?;
42
43 Ok(response
44 .data
45 .into_iter()
46 .map(StripeCustomer::from)
47 .collect())
48 }
49
50 async fn create_customer(&self, params: CreateCustomerParams<'_>) -> Result<StripeCustomer> {
51 let customer = Customer::create(
52 &self.client,
53 CreateCustomer {
54 email: params.email,
55 ..Default::default()
56 },
57 )
58 .await?;
59
60 Ok(StripeCustomer::from(customer))
61 }
62
63 async fn get_subscription(
64 &self,
65 subscription_id: &StripeSubscriptionId,
66 ) -> Result<StripeSubscription> {
67 let subscription_id = subscription_id.try_into()?;
68
69 let subscription = Subscription::retrieve(&self.client, &subscription_id, &[]).await?;
70
71 Ok(StripeSubscription::from(subscription))
72 }
73
74 async fn update_subscription(
75 &self,
76 subscription_id: &StripeSubscriptionId,
77 params: UpdateSubscriptionParams,
78 ) -> Result<()> {
79 let subscription_id = subscription_id.try_into()?;
80
81 stripe::Subscription::update(
82 &self.client,
83 &subscription_id,
84 stripe::UpdateSubscription {
85 items: params.items.map(|items| {
86 items
87 .into_iter()
88 .map(|item| UpdateSubscriptionItems {
89 price: item.price.map(|price| price.to_string()),
90 ..Default::default()
91 })
92 .collect()
93 }),
94 trial_settings: params.trial_settings.map(Into::into),
95 ..Default::default()
96 },
97 )
98 .await?;
99
100 Ok(())
101 }
102
103 async fn list_prices(&self) -> Result<Vec<StripePrice>> {
104 let response = stripe::Price::list(
105 &self.client,
106 &stripe::ListPrices {
107 limit: Some(100),
108 ..Default::default()
109 },
110 )
111 .await?;
112
113 Ok(response.data.into_iter().map(StripePrice::from).collect())
114 }
115
116 async fn list_meters(&self) -> Result<Vec<StripeMeter>> {
117 #[derive(Serialize)]
118 struct Params {
119 #[serde(skip_serializing_if = "Option::is_none")]
120 limit: Option<u64>,
121 }
122
123 let response = self
124 .client
125 .get_query::<stripe::List<StripeMeter>, _>(
126 "/billing/meters",
127 Params { limit: Some(100) },
128 )
129 .await?;
130
131 Ok(response.data)
132 }
133
134 async fn create_meter_event(&self, params: StripeCreateMeterEventParams<'_>) -> Result<()> {
135 let identifier = params.identifier;
136 match self.client.post_form("/billing/meter_events", params).await {
137 Ok(event) => Ok(event),
138 Err(stripe::StripeError::Stripe(error)) => {
139 if error.http_status == 400
140 && error
141 .message
142 .as_ref()
143 .map_or(false, |message| message.contains(identifier))
144 {
145 Ok(())
146 } else {
147 Err(anyhow!(stripe::StripeError::Stripe(error)))
148 }
149 }
150 Err(error) => Err(anyhow!(error)),
151 }
152 }
153}
154
155impl From<CustomerId> for StripeCustomerId {
156 fn from(value: CustomerId) -> Self {
157 Self(value.as_str().into())
158 }
159}
160
161impl TryFrom<StripeCustomerId> for CustomerId {
162 type Error = anyhow::Error;
163
164 fn try_from(value: StripeCustomerId) -> Result<Self, Self::Error> {
165 Self::from_str(value.0.as_ref()).context("failed to parse Stripe customer ID")
166 }
167}
168
169impl From<Customer> for StripeCustomer {
170 fn from(value: Customer) -> Self {
171 StripeCustomer {
172 id: value.id.into(),
173 email: value.email,
174 }
175 }
176}
177
178impl From<SubscriptionId> for StripeSubscriptionId {
179 fn from(value: SubscriptionId) -> Self {
180 Self(value.as_str().into())
181 }
182}
183
184impl TryFrom<&StripeSubscriptionId> for SubscriptionId {
185 type Error = anyhow::Error;
186
187 fn try_from(value: &StripeSubscriptionId) -> Result<Self, Self::Error> {
188 Self::from_str(value.0.as_ref()).context("failed to parse Stripe subscription ID")
189 }
190}
191
192impl From<Subscription> for StripeSubscription {
193 fn from(value: Subscription) -> Self {
194 Self {
195 id: value.id.into(),
196 items: value.items.data.into_iter().map(Into::into).collect(),
197 }
198 }
199}
200
201impl From<SubscriptionItemId> for StripeSubscriptionItemId {
202 fn from(value: SubscriptionItemId) -> Self {
203 Self(value.as_str().into())
204 }
205}
206
207impl From<SubscriptionItem> for StripeSubscriptionItem {
208 fn from(value: SubscriptionItem) -> Self {
209 Self {
210 id: value.id.into(),
211 price: value.price.map(Into::into),
212 }
213 }
214}
215
216impl From<crate::stripe_client::UpdateSubscriptionTrialSettings>
217 for UpdateSubscriptionTrialSettings
218{
219 fn from(value: crate::stripe_client::UpdateSubscriptionTrialSettings) -> Self {
220 Self {
221 end_behavior: value.end_behavior.into(),
222 }
223 }
224}
225
226impl From<crate::stripe_client::UpdateSubscriptionTrialSettingsEndBehavior>
227 for UpdateSubscriptionTrialSettingsEndBehavior
228{
229 fn from(value: crate::stripe_client::UpdateSubscriptionTrialSettingsEndBehavior) -> Self {
230 Self {
231 missing_payment_method: value.missing_payment_method.into(),
232 }
233 }
234}
235
236impl From<crate::stripe_client::UpdateSubscriptionTrialSettingsEndBehaviorMissingPaymentMethod>
237 for UpdateSubscriptionTrialSettingsEndBehaviorMissingPaymentMethod
238{
239 fn from(
240 value: crate::stripe_client::UpdateSubscriptionTrialSettingsEndBehaviorMissingPaymentMethod,
241 ) -> Self {
242 match value {
243 crate::stripe_client::UpdateSubscriptionTrialSettingsEndBehaviorMissingPaymentMethod::Cancel => Self::Cancel,
244 crate::stripe_client::UpdateSubscriptionTrialSettingsEndBehaviorMissingPaymentMethod::CreateInvoice => {
245 Self::CreateInvoice
246 }
247 crate::stripe_client::UpdateSubscriptionTrialSettingsEndBehaviorMissingPaymentMethod::Pause => Self::Pause,
248 }
249 }
250}
251
252impl From<PriceId> for StripePriceId {
253 fn from(value: PriceId) -> Self {
254 Self(value.as_str().into())
255 }
256}
257
258impl TryFrom<StripePriceId> for PriceId {
259 type Error = anyhow::Error;
260
261 fn try_from(value: StripePriceId) -> Result<Self, Self::Error> {
262 Self::from_str(value.0.as_ref()).context("failed to parse Stripe price ID")
263 }
264}
265
266impl From<Price> for StripePrice {
267 fn from(value: Price) -> Self {
268 Self {
269 id: value.id.into(),
270 unit_amount: value.unit_amount,
271 lookup_key: value.lookup_key,
272 recurring: value.recurring.map(StripePriceRecurring::from),
273 }
274 }
275}
276
277impl From<Recurring> for StripePriceRecurring {
278 fn from(value: Recurring) -> Self {
279 Self { meter: value.meter }
280 }
281}