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