real_stripe_client.rs

  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}