Detailed changes
@@ -1262,26 +1262,6 @@ dependencies = [
"syn 2.0.101",
]
-[[package]]
-name = "async-stripe"
-version = "0.40.0"
-source = "git+https://github.com/zed-industries/async-stripe?rev=3672dd4efb7181aa597bf580bf5a2f5d23db6735#3672dd4efb7181aa597bf580bf5a2f5d23db6735"
-dependencies = [
- "chrono",
- "futures-util",
- "http-types",
- "hyper 0.14.32",
- "hyper-rustls 0.24.2",
- "serde",
- "serde_json",
- "serde_path_to_error",
- "serde_qs 0.10.1",
- "smart-default 0.6.0",
- "smol_str 0.1.24",
- "thiserror 1.0.69",
- "tokio",
-]
-
[[package]]
name = "async-tar"
version = "0.5.0"
@@ -2083,12 +2063,6 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce"
-[[package]]
-name = "base64"
-version = "0.13.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
-
[[package]]
name = "base64"
version = "0.21.7"
@@ -3281,7 +3255,6 @@ dependencies = [
"anyhow",
"assistant_context",
"assistant_slash_command",
- "async-stripe",
"async-trait",
"async-tungstenite",
"audio",
@@ -3308,7 +3281,6 @@ dependencies = [
"dap_adapters",
"dashmap 6.1.0",
"debugger_ui",
- "derive_more 0.99.19",
"editor",
"envy",
"extension",
@@ -3870,7 +3842,7 @@ dependencies = [
"rustc-hash 1.1.0",
"rustybuzz 0.14.1",
"self_cell",
- "smol_str 0.2.2",
+ "smol_str",
"swash",
"sys-locale",
"ttf-parser 0.21.1",
@@ -6374,17 +6346,6 @@ dependencies = [
"windows-targets 0.48.5",
]
-[[package]]
-name = "getrandom"
-version = "0.1.16"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
-dependencies = [
- "cfg-if",
- "libc",
- "wasi 0.9.0+wasi-snapshot-preview1",
-]
-
[[package]]
name = "getrandom"
version = "0.2.15"
@@ -7988,27 +7949,6 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "add0ab9360ddbd88cfeb3bd9574a1d85cfdfa14db10b3e21d3700dbc4328758f"
-[[package]]
-name = "http-types"
-version = "2.12.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6e9b187a72d63adbfba487f48095306ac823049cb504ee195541e91c7775f5ad"
-dependencies = [
- "anyhow",
- "async-channel 1.9.0",
- "base64 0.13.1",
- "futures-lite 1.13.0",
- "http 0.2.12",
- "infer",
- "pin-project-lite",
- "rand 0.7.3",
- "serde",
- "serde_json",
- "serde_qs 0.8.5",
- "serde_urlencoded",
- "url",
-]
-
[[package]]
name = "http_client"
version = "0.1.0"
@@ -8487,12 +8427,6 @@ version = "2.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd"
-[[package]]
-name = "infer"
-version = "0.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "64e9829a50b42bb782c1df523f78d332fe371b10c661e78b7a3c34b0198e9fac"
-
[[package]]
name = "inherent"
version = "1.0.12"
@@ -10269,7 +10203,7 @@ dependencies = [
"num-traits",
"range-map",
"scroll",
- "smart-default 0.7.1",
+ "smart-default",
]
[[package]]
@@ -13143,19 +13077,6 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
-[[package]]
-name = "rand"
-version = "0.7.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
-dependencies = [
- "getrandom 0.1.16",
- "libc",
- "rand_chacha 0.2.2",
- "rand_core 0.5.1",
- "rand_hc",
-]
-
[[package]]
name = "rand"
version = "0.8.5"
@@ -13177,16 +13098,6 @@ dependencies = [
"rand_core 0.9.3",
]
-[[package]]
-name = "rand_chacha"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
-dependencies = [
- "ppv-lite86",
- "rand_core 0.5.1",
-]
-
[[package]]
name = "rand_chacha"
version = "0.3.1"
@@ -13207,15 +13118,6 @@ dependencies = [
"rand_core 0.9.3",
]
-[[package]]
-name = "rand_core"
-version = "0.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
-dependencies = [
- "getrandom 0.1.16",
-]
-
[[package]]
name = "rand_core"
version = "0.6.4"
@@ -13234,15 +13136,6 @@ dependencies = [
"getrandom 0.3.2",
]
-[[package]]
-name = "rand_hc"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
-dependencies = [
- "rand_core 0.5.1",
-]
-
[[package]]
name = "range-map"
version = "0.2.0"
@@ -14897,28 +14790,6 @@ dependencies = [
"serde",
]
-[[package]]
-name = "serde_qs"
-version = "0.8.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c7715380eec75f029a4ef7de39a9200e0a63823176b759d055b613f5a87df6a6"
-dependencies = [
- "percent-encoding",
- "serde",
- "thiserror 1.0.69",
-]
-
-[[package]]
-name = "serde_qs"
-version = "0.10.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8cac3f1e2ca2fe333923a1ae72caca910b98ed0630bb35ef6f8c8517d6e81afa"
-dependencies = [
- "percent-encoding",
- "serde",
- "thiserror 1.0.69",
-]
-
[[package]]
name = "serde_repr"
version = "0.1.20"
@@ -15295,17 +15166,6 @@ dependencies = [
"serde",
]
-[[package]]
-name = "smart-default"
-version = "0.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "133659a15339456eeeb07572eb02a91c91e9815e9cbc89566944d2c8d3efdbf6"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 1.0.109",
-]
-
[[package]]
name = "smart-default"
version = "0.7.1"
@@ -15334,15 +15194,6 @@ dependencies = [
"futures-lite 2.6.0",
]
-[[package]]
-name = "smol_str"
-version = "0.1.24"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fad6c857cbab2627dcf01ec85a623ca4e7dcb5691cbaa3d7fb7653671f0d09c9"
-dependencies = [
- "serde",
-]
-
[[package]]
name = "smol_str"
version = "0.2.2"
@@ -18191,12 +18042,6 @@ dependencies = [
"tracing",
]
-[[package]]
-name = "wasi"
-version = "0.9.0+wasi-snapshot-preview1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
-
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
@@ -667,20 +667,6 @@ workspace-hack = "0.1.0"
yawc = { git = "https://github.com/deviant-forks/yawc", rev = "1899688f3e69ace4545aceb97b2a13881cf26142" }
zstd = "0.11"
-[workspace.dependencies.async-stripe]
-git = "https://github.com/zed-industries/async-stripe"
-rev = "3672dd4efb7181aa597bf580bf5a2f5d23db6735"
-default-features = false
-features = [
- "runtime-tokio-hyper-rustls",
- "billing",
- "checkout",
- "events",
- # The features below are only enabled to get the `events` feature to build.
- "chrono",
- "connect",
-]
-
[workspace.dependencies.windows]
version = "0.61"
features = [
@@ -19,7 +19,6 @@ test-support = ["sqlite"]
[dependencies]
anyhow.workspace = true
-async-stripe.workspace = true
async-trait.workspace = true
async-tungstenite.workspace = true
aws-config = { version = "1.1.5" }
@@ -33,7 +32,6 @@ clock.workspace = true
cloud_llm_client.workspace = true
collections.workspace = true
dashmap.workspace = true
-derive_more.workspace = true
envy = "0.4.2"
futures.workspace = true
gpui.workspace = true
@@ -134,6 +132,3 @@ util.workspace = true
workspace = { workspace = true, features = ["test-support"] }
worktree = { workspace = true, features = ["test-support"] }
zlog.workspace = true
-
-[package.metadata.cargo-machete]
-ignored = ["async-stripe"]
@@ -219,12 +219,6 @@ spec:
secretKeyRef:
name: slack
key: panics_webhook
- - name: STRIPE_API_KEY
- valueFrom:
- secretKeyRef:
- name: stripe
- key: api_key
- optional: true
- name: COMPLETE_WITH_LANGUAGE_MODEL_RATE_LIMIT_PER_HOUR
value: "1000"
- name: SUPERMAVEN_ADMIN_API_KEY
@@ -1,4 +1,3 @@
-pub mod billing;
pub mod contributors;
pub mod events;
pub mod extensions;
@@ -1,59 +0,0 @@
-use std::sync::Arc;
-use stripe::SubscriptionStatus;
-
-use crate::AppState;
-use crate::db::billing_subscription::StripeSubscriptionStatus;
-use crate::db::{CreateBillingCustomerParams, billing_customer};
-use crate::stripe_client::{StripeClient, StripeCustomerId};
-
-impl From<SubscriptionStatus> for StripeSubscriptionStatus {
- fn from(value: SubscriptionStatus) -> Self {
- match value {
- SubscriptionStatus::Incomplete => Self::Incomplete,
- SubscriptionStatus::IncompleteExpired => Self::IncompleteExpired,
- SubscriptionStatus::Trialing => Self::Trialing,
- SubscriptionStatus::Active => Self::Active,
- SubscriptionStatus::PastDue => Self::PastDue,
- SubscriptionStatus::Canceled => Self::Canceled,
- SubscriptionStatus::Unpaid => Self::Unpaid,
- SubscriptionStatus::Paused => Self::Paused,
- }
- }
-}
-
-/// Finds or creates a billing customer using the provided customer.
-pub async fn find_or_create_billing_customer(
- app: &Arc<AppState>,
- stripe_client: &dyn StripeClient,
- customer_id: &StripeCustomerId,
-) -> anyhow::Result<Option<billing_customer::Model>> {
- // If we already have a billing customer record associated with the Stripe customer,
- // there's nothing more we need to do.
- if let Some(billing_customer) = app
- .db
- .get_billing_customer_by_stripe_customer_id(customer_id.0.as_ref())
- .await?
- {
- return Ok(Some(billing_customer));
- }
-
- let customer = stripe_client.get_customer(customer_id).await?;
-
- let Some(email) = customer.email else {
- return Ok(None);
- };
-
- let Some(user) = app.db.get_user_by_email(&email).await? else {
- return Ok(None);
- };
-
- let billing_customer = app
- .db
- .create_billing_customer(&CreateBillingCustomerParams {
- user_id: user.id,
- stripe_customer_id: customer.id.to_string(),
- })
- .await?;
-
- Ok(Some(billing_customer))
-}
@@ -1,5 +1,4 @@
use crate::db::{BillingCustomerId, BillingSubscriptionId};
-use crate::stripe_client;
use chrono::{Datelike as _, NaiveDate, Utc};
use sea_orm::entity::prelude::*;
use serde::Serialize;
@@ -160,17 +159,3 @@ pub enum StripeCancellationReason {
#[sea_orm(string_value = "payment_failed")]
PaymentFailed,
}
-
-impl From<stripe_client::StripeCancellationDetailsReason> for StripeCancellationReason {
- fn from(value: stripe_client::StripeCancellationDetailsReason) -> Self {
- match value {
- stripe_client::StripeCancellationDetailsReason::CancellationRequested => {
- Self::CancellationRequested
- }
- stripe_client::StripeCancellationDetailsReason::PaymentDisputed => {
- Self::PaymentDisputed
- }
- stripe_client::StripeCancellationDetailsReason::PaymentFailed => Self::PaymentFailed,
- }
- }
-}
@@ -7,8 +7,6 @@ pub mod llm;
pub mod migrations;
pub mod rpc;
pub mod seed;
-pub mod stripe_billing;
-pub mod stripe_client;
pub mod user_backfiller;
#[cfg(test)]
@@ -27,16 +25,12 @@ use serde::Deserialize;
use std::{path::PathBuf, sync::Arc};
use util::ResultExt;
-use crate::stripe_billing::StripeBilling;
-use crate::stripe_client::{RealStripeClient, StripeClient};
-
pub type Result<T, E = Error> = std::result::Result<T, E>;
pub enum Error {
Http(StatusCode, String, HeaderMap),
Database(sea_orm::error::DbErr),
Internal(anyhow::Error),
- Stripe(stripe::StripeError),
}
impl From<anyhow::Error> for Error {
@@ -51,12 +45,6 @@ impl From<sea_orm::error::DbErr> for Error {
}
}
-impl From<stripe::StripeError> for Error {
- fn from(error: stripe::StripeError) -> Self {
- Self::Stripe(error)
- }
-}
-
impl From<axum::Error> for Error {
fn from(error: axum::Error) -> Self {
Self::Internal(error.into())
@@ -104,14 +92,6 @@ impl IntoResponse for Error {
);
(StatusCode::INTERNAL_SERVER_ERROR, format!("{}", &error)).into_response()
}
- Error::Stripe(error) => {
- log::error!(
- "HTTP error {}: {:?}",
- StatusCode::INTERNAL_SERVER_ERROR,
- &error
- );
- (StatusCode::INTERNAL_SERVER_ERROR, format!("{}", &error)).into_response()
- }
}
}
}
@@ -122,7 +102,6 @@ impl std::fmt::Debug for Error {
Error::Http(code, message, _headers) => (code, message).fmt(f),
Error::Database(error) => error.fmt(f),
Error::Internal(error) => error.fmt(f),
- Error::Stripe(error) => error.fmt(f),
}
}
}
@@ -133,7 +112,6 @@ impl std::fmt::Display for Error {
Error::Http(code, message, _) => write!(f, "{code}: {message}"),
Error::Database(error) => error.fmt(f),
Error::Internal(error) => error.fmt(f),
- Error::Stripe(error) => error.fmt(f),
}
}
}
@@ -179,7 +157,6 @@ pub struct Config {
pub zed_client_checksum_seed: Option<String>,
pub slack_panics_webhook: Option<String>,
pub auto_join_channel_id: Option<ChannelId>,
- pub stripe_api_key: Option<String>,
pub supermaven_admin_api_key: Option<Arc<str>>,
pub user_backfiller_github_access_token: Option<Arc<str>>,
}
@@ -234,7 +211,6 @@ impl Config {
auto_join_channel_id: None,
migrations_path: None,
seed_path: None,
- stripe_api_key: None,
supermaven_admin_api_key: None,
user_backfiller_github_access_token: None,
kinesis_region: None,
@@ -269,11 +245,6 @@ pub struct AppState {
pub llm_db: Option<Arc<LlmDatabase>>,
pub livekit_client: Option<Arc<dyn livekit_api::Client>>,
pub blob_store_client: Option<aws_sdk_s3::Client>,
- /// This is a real instance of the Stripe client; we're working to replace references to this with the
- /// [`StripeClient`] trait.
- pub real_stripe_client: Option<Arc<stripe::Client>>,
- pub stripe_client: Option<Arc<dyn StripeClient>>,
- pub stripe_billing: Option<Arc<StripeBilling>>,
pub executor: Executor,
pub kinesis_client: Option<::aws_sdk_kinesis::Client>,
pub config: Config,
@@ -316,18 +287,11 @@ impl AppState {
};
let db = Arc::new(db);
- let stripe_client = build_stripe_client(&config).map(Arc::new).log_err();
let this = Self {
db: db.clone(),
llm_db,
livekit_client,
blob_store_client: build_blob_store_client(&config).await.log_err(),
- stripe_billing: stripe_client
- .clone()
- .map(|stripe_client| Arc::new(StripeBilling::new(stripe_client))),
- real_stripe_client: stripe_client.clone(),
- stripe_client: stripe_client
- .map(|stripe_client| Arc::new(RealStripeClient::new(stripe_client)) as _),
executor,
kinesis_client: if config.kinesis_access_key.is_some() {
build_kinesis_client(&config).await.log_err()
@@ -340,14 +304,6 @@ impl AppState {
}
}
-fn build_stripe_client(config: &Config) -> anyhow::Result<stripe::Client> {
- let api_key = config
- .stripe_api_key
- .as_ref()
- .context("missing stripe_api_key")?;
- Ok(stripe::Client::new(api_key))
-}
-
async fn build_blob_store_client(config: &Config) -> anyhow::Result<aws_sdk_s3::Client> {
let keys = aws_sdk_s3::config::Credentials::new(
config
@@ -102,13 +102,6 @@ async fn main() -> Result<()> {
let state = AppState::new(config, Executor::Production).await?;
- if let Some(stripe_billing) = state.stripe_billing.clone() {
- let executor = state.executor.clone();
- executor.spawn_detached(async move {
- stripe_billing.initialize().await.trace_err();
- });
- }
-
if mode.is_collab() {
state.db.purge_old_embeddings().await.trace_err();
@@ -1,156 +0,0 @@
-use std::sync::Arc;
-
-use anyhow::anyhow;
-use collections::HashMap;
-use stripe::SubscriptionStatus;
-use tokio::sync::RwLock;
-
-use crate::Result;
-use crate::stripe_client::{
- RealStripeClient, StripeAutomaticTax, StripeClient, StripeCreateSubscriptionItems,
- StripeCreateSubscriptionParams, StripeCustomerId, StripePrice, StripePriceId,
- StripeSubscription,
-};
-
-pub struct StripeBilling {
- state: RwLock<StripeBillingState>,
- client: Arc<dyn StripeClient>,
-}
-
-#[derive(Default)]
-struct StripeBillingState {
- prices_by_lookup_key: HashMap<String, StripePrice>,
-}
-
-impl StripeBilling {
- pub fn new(client: Arc<stripe::Client>) -> Self {
- Self {
- client: Arc::new(RealStripeClient::new(client.clone())),
- state: RwLock::default(),
- }
- }
-
- #[cfg(test)]
- pub fn test(client: Arc<crate::stripe_client::FakeStripeClient>) -> Self {
- Self {
- client,
- state: RwLock::default(),
- }
- }
-
- pub fn client(&self) -> &Arc<dyn StripeClient> {
- &self.client
- }
-
- pub async fn initialize(&self) -> Result<()> {
- log::info!("StripeBilling: initializing");
-
- let mut state = self.state.write().await;
-
- let prices = self.client.list_prices().await?;
-
- for price in prices {
- if let Some(lookup_key) = price.lookup_key.clone() {
- state.prices_by_lookup_key.insert(lookup_key, price);
- }
- }
-
- log::info!("StripeBilling: initialized");
-
- Ok(())
- }
-
- pub async fn zed_pro_price_id(&self) -> Result<StripePriceId> {
- self.find_price_id_by_lookup_key("zed-pro").await
- }
-
- pub async fn zed_free_price_id(&self) -> Result<StripePriceId> {
- self.find_price_id_by_lookup_key("zed-free").await
- }
-
- pub async fn find_price_id_by_lookup_key(&self, lookup_key: &str) -> Result<StripePriceId> {
- self.state
- .read()
- .await
- .prices_by_lookup_key
- .get(lookup_key)
- .map(|price| price.id.clone())
- .ok_or_else(|| crate::Error::Internal(anyhow!("no price ID found for {lookup_key:?}")))
- }
-
- pub async fn find_price_by_lookup_key(&self, lookup_key: &str) -> Result<StripePrice> {
- self.state
- .read()
- .await
- .prices_by_lookup_key
- .get(lookup_key)
- .cloned()
- .ok_or_else(|| crate::Error::Internal(anyhow!("no price found for {lookup_key:?}")))
- }
-
- /// Returns the Stripe customer associated with the provided email address, or creates a new customer, if one does
- /// not already exist.
- ///
- /// Always returns a new Stripe customer if the email address is `None`.
- pub async fn find_or_create_customer_by_email(
- &self,
- email_address: Option<&str>,
- ) -> Result<StripeCustomerId> {
- let existing_customer = if let Some(email) = email_address {
- let customers = self.client.list_customers_by_email(email).await?;
-
- customers.first().cloned()
- } else {
- None
- };
-
- let customer_id = if let Some(existing_customer) = existing_customer {
- existing_customer.id
- } else {
- let customer = self
- .client
- .create_customer(crate::stripe_client::CreateCustomerParams {
- email: email_address,
- })
- .await?;
-
- customer.id
- };
-
- Ok(customer_id)
- }
-
- pub async fn subscribe_to_zed_free(
- &self,
- customer_id: StripeCustomerId,
- ) -> Result<StripeSubscription> {
- let zed_free_price_id = self.zed_free_price_id().await?;
-
- let existing_subscriptions = self
- .client
- .list_subscriptions_for_customer(&customer_id)
- .await?;
-
- let existing_active_subscription =
- existing_subscriptions.into_iter().find(|subscription| {
- subscription.status == SubscriptionStatus::Active
- || subscription.status == SubscriptionStatus::Trialing
- });
- if let Some(subscription) = existing_active_subscription {
- return Ok(subscription);
- }
-
- let params = StripeCreateSubscriptionParams {
- customer: customer_id,
- items: vec![StripeCreateSubscriptionItems {
- price: Some(zed_free_price_id),
- quantity: Some(1),
- }],
- automatic_tax: Some(StripeAutomaticTax { enabled: true }),
- };
-
- let subscription = self.client.create_subscription(params).await?;
-
- Ok(subscription)
- }
-}
@@ -1,285 +0,0 @@
-#[cfg(test)]
-mod fake_stripe_client;
-mod real_stripe_client;
-
-use std::collections::HashMap;
-use std::sync::Arc;
-
-use anyhow::Result;
-use async_trait::async_trait;
-
-#[cfg(test)]
-pub use fake_stripe_client::*;
-pub use real_stripe_client::*;
-use serde::{Deserialize, Serialize};
-
-#[derive(Debug, PartialEq, Eq, Hash, Clone, derive_more::Display, Serialize)]
-pub struct StripeCustomerId(pub Arc<str>);
-
-#[derive(Debug, Clone)]
-pub struct StripeCustomer {
- pub id: StripeCustomerId,
- pub email: Option<String>,
-}
-
-#[derive(Debug)]
-pub struct CreateCustomerParams<'a> {
- pub email: Option<&'a str>,
-}
-
-#[derive(Debug)]
-pub struct UpdateCustomerParams<'a> {
- pub email: Option<&'a str>,
-}
-
-#[derive(Debug, PartialEq, Eq, Hash, Clone, derive_more::Display)]
-pub struct StripeSubscriptionId(pub Arc<str>);
-
-#[derive(Debug, PartialEq, Clone)]
-pub struct StripeSubscription {
- pub id: StripeSubscriptionId,
- pub customer: StripeCustomerId,
- // TODO: Create our own version of this enum.
- pub status: stripe::SubscriptionStatus,
- pub current_period_end: i64,
- pub current_period_start: i64,
- pub items: Vec<StripeSubscriptionItem>,
- pub cancel_at: Option<i64>,
- pub cancellation_details: Option<StripeCancellationDetails>,
-}
-
-#[derive(Debug, PartialEq, Eq, Hash, Clone, derive_more::Display)]
-pub struct StripeSubscriptionItemId(pub Arc<str>);
-
-#[derive(Debug, PartialEq, Clone)]
-pub struct StripeSubscriptionItem {
- pub id: StripeSubscriptionItemId,
- pub price: Option<StripePrice>,
-}
-
-#[derive(Debug, Clone, PartialEq)]
-pub struct StripeCancellationDetails {
- pub reason: Option<StripeCancellationDetailsReason>,
-}
-
-#[derive(Debug, PartialEq, Eq, Clone, Copy)]
-pub enum StripeCancellationDetailsReason {
- CancellationRequested,
- PaymentDisputed,
- PaymentFailed,
-}
-
-#[derive(Debug)]
-pub struct StripeCreateSubscriptionParams {
- pub customer: StripeCustomerId,
- pub items: Vec<StripeCreateSubscriptionItems>,
- pub automatic_tax: Option<StripeAutomaticTax>,
-}
-
-#[derive(Debug)]
-pub struct StripeCreateSubscriptionItems {
- pub price: Option<StripePriceId>,
- pub quantity: Option<u64>,
-}
-
-#[derive(Debug, Clone)]
-pub struct UpdateSubscriptionParams {
- pub items: Option<Vec<UpdateSubscriptionItems>>,
- pub trial_settings: Option<StripeSubscriptionTrialSettings>,
-}
-
-#[derive(Debug, PartialEq, Clone)]
-pub struct UpdateSubscriptionItems {
- pub price: Option<StripePriceId>,
-}
-
-#[derive(Debug, PartialEq, Clone)]
-pub struct StripeSubscriptionTrialSettings {
- pub end_behavior: StripeSubscriptionTrialSettingsEndBehavior,
-}
-
-#[derive(Debug, PartialEq, Clone)]
-pub struct StripeSubscriptionTrialSettingsEndBehavior {
- pub missing_payment_method: StripeSubscriptionTrialSettingsEndBehaviorMissingPaymentMethod,
-}
-
-#[derive(Debug, PartialEq, Eq, Clone, Copy)]
-pub enum StripeSubscriptionTrialSettingsEndBehaviorMissingPaymentMethod {
- Cancel,
- CreateInvoice,
- Pause,
-}
-
-#[derive(Debug, PartialEq, Eq, Hash, Clone, derive_more::Display)]
-pub struct StripePriceId(pub Arc<str>);
-
-#[derive(Debug, PartialEq, Clone)]
-pub struct StripePrice {
- pub id: StripePriceId,
- pub unit_amount: Option<i64>,
- pub lookup_key: Option<String>,
- pub recurring: Option<StripePriceRecurring>,
-}
-
-#[derive(Debug, PartialEq, Clone)]
-pub struct StripePriceRecurring {
- pub meter: Option<String>,
-}
-
-#[derive(Debug, PartialEq, Eq, Hash, Clone, derive_more::Display, Deserialize)]
-pub struct StripeMeterId(pub Arc<str>);
-
-#[derive(Debug, Clone, Deserialize)]
-pub struct StripeMeter {
- pub id: StripeMeterId,
- pub event_name: String,
-}
-
-#[derive(Debug, Serialize)]
-pub struct StripeCreateMeterEventParams<'a> {
- pub identifier: &'a str,
- pub event_name: &'a str,
- pub payload: StripeCreateMeterEventPayload<'a>,
- pub timestamp: Option<i64>,
-}
-
-#[derive(Debug, Serialize)]
-pub struct StripeCreateMeterEventPayload<'a> {
- pub value: u64,
- pub stripe_customer_id: &'a StripeCustomerId,
-}
-
-#[derive(Debug, PartialEq, Eq, Clone, Copy)]
-pub enum StripeBillingAddressCollection {
- Auto,
- Required,
-}
-
-#[derive(Debug, PartialEq, Clone)]
-pub struct StripeCustomerUpdate {
- pub address: Option<StripeCustomerUpdateAddress>,
- pub name: Option<StripeCustomerUpdateName>,
- pub shipping: Option<StripeCustomerUpdateShipping>,
-}
-
-#[derive(Debug, PartialEq, Eq, Clone, Copy)]
-pub enum StripeCustomerUpdateAddress {
- Auto,
- Never,
-}
-
-#[derive(Debug, PartialEq, Eq, Clone, Copy)]
-pub enum StripeCustomerUpdateName {
- Auto,
- Never,
-}
-
-#[derive(Debug, PartialEq, Eq, Clone, Copy)]
-pub enum StripeCustomerUpdateShipping {
- Auto,
- Never,
-}
-
-#[derive(Debug, Default)]
-pub struct StripeCreateCheckoutSessionParams<'a> {
- pub customer: Option<&'a StripeCustomerId>,
- pub client_reference_id: Option<&'a str>,
- pub mode: Option<StripeCheckoutSessionMode>,
- pub line_items: Option<Vec<StripeCreateCheckoutSessionLineItems>>,
- pub payment_method_collection: Option<StripeCheckoutSessionPaymentMethodCollection>,
- pub subscription_data: Option<StripeCreateCheckoutSessionSubscriptionData>,
- pub success_url: Option<&'a str>,
- pub billing_address_collection: Option<StripeBillingAddressCollection>,
- pub customer_update: Option<StripeCustomerUpdate>,
- pub tax_id_collection: Option<StripeTaxIdCollection>,
-}
-
-#[derive(Debug, PartialEq, Eq, Clone, Copy)]
-pub enum StripeCheckoutSessionMode {
- Payment,
- Setup,
- Subscription,
-}
-
-#[derive(Debug, PartialEq, Clone)]
-pub struct StripeCreateCheckoutSessionLineItems {
- pub price: Option<String>,
- pub quantity: Option<u64>,
-}
-
-#[derive(Debug, PartialEq, Eq, Clone, Copy)]
-pub enum StripeCheckoutSessionPaymentMethodCollection {
- Always,
- IfRequired,
-}
-
-#[derive(Debug, PartialEq, Clone)]
-pub struct StripeCreateCheckoutSessionSubscriptionData {
- pub metadata: Option<HashMap<String, String>>,
- pub trial_period_days: Option<u32>,
- pub trial_settings: Option<StripeSubscriptionTrialSettings>,
-}
-
-#[derive(Debug, PartialEq, Clone)]
-pub struct StripeTaxIdCollection {
- pub enabled: bool,
-}
-
-#[derive(Debug, Clone)]
-pub struct StripeAutomaticTax {
- pub enabled: bool,
-}
-
-#[derive(Debug)]
-pub struct StripeCheckoutSession {
- pub url: Option<String>,
-}
-
-#[async_trait]
-pub trait StripeClient: Send + Sync {
- async fn list_customers_by_email(&self, email: &str) -> Result<Vec<StripeCustomer>>;
-
- async fn get_customer(&self, customer_id: &StripeCustomerId) -> Result<StripeCustomer>;
-
- async fn create_customer(&self, params: CreateCustomerParams<'_>) -> Result<StripeCustomer>;
-
- async fn update_customer(
- &self,
- customer_id: &StripeCustomerId,
- params: UpdateCustomerParams<'_>,
- ) -> Result<StripeCustomer>;
-
- async fn list_subscriptions_for_customer(
- &self,
- customer_id: &StripeCustomerId,
- ) -> Result<Vec<StripeSubscription>>;
-
- async fn get_subscription(
- &self,
- subscription_id: &StripeSubscriptionId,
- ) -> Result<StripeSubscription>;
-
- async fn create_subscription(
- &self,
- params: StripeCreateSubscriptionParams,
- ) -> Result<StripeSubscription>;
-
- async fn update_subscription(
- &self,
- subscription_id: &StripeSubscriptionId,
- params: UpdateSubscriptionParams,
- ) -> Result<()>;
-
- async fn cancel_subscription(&self, subscription_id: &StripeSubscriptionId) -> Result<()>;
-
- async fn list_prices(&self) -> Result<Vec<StripePrice>>;
-
- async fn list_meters(&self) -> Result<Vec<StripeMeter>>;
-
- async fn create_meter_event(&self, params: StripeCreateMeterEventParams<'_>) -> Result<()>;
-
- async fn create_checkout_session(
- &self,
- params: StripeCreateCheckoutSessionParams<'_>,
- ) -> Result<StripeCheckoutSession>;
-}
@@ -1,247 +0,0 @@
-use std::sync::Arc;
-
-use anyhow::{Result, anyhow};
-use async_trait::async_trait;
-use chrono::{Duration, Utc};
-use collections::HashMap;
-use parking_lot::Mutex;
-use uuid::Uuid;
-
-use crate::stripe_client::{
- CreateCustomerParams, StripeBillingAddressCollection, StripeCheckoutSession,
- StripeCheckoutSessionMode, StripeCheckoutSessionPaymentMethodCollection, StripeClient,
- StripeCreateCheckoutSessionLineItems, StripeCreateCheckoutSessionParams,
- StripeCreateCheckoutSessionSubscriptionData, StripeCreateMeterEventParams,
- StripeCreateSubscriptionParams, StripeCustomer, StripeCustomerId, StripeCustomerUpdate,
- StripeMeter, StripeMeterId, StripePrice, StripePriceId, StripeSubscription,
- StripeSubscriptionId, StripeSubscriptionItem, StripeSubscriptionItemId, StripeTaxIdCollection,
- UpdateCustomerParams, UpdateSubscriptionParams,
-};
-
-#[derive(Debug, Clone)]
-pub struct StripeCreateMeterEventCall {
- pub identifier: Arc<str>,
- pub event_name: Arc<str>,
- pub value: u64,
- pub stripe_customer_id: StripeCustomerId,
- pub timestamp: Option<i64>,
-}
-
-#[derive(Debug, Clone)]
-pub struct StripeCreateCheckoutSessionCall {
- pub customer: Option<StripeCustomerId>,
- pub client_reference_id: Option<String>,
- pub mode: Option<StripeCheckoutSessionMode>,
- pub line_items: Option<Vec<StripeCreateCheckoutSessionLineItems>>,
- pub payment_method_collection: Option<StripeCheckoutSessionPaymentMethodCollection>,
- pub subscription_data: Option<StripeCreateCheckoutSessionSubscriptionData>,
- pub success_url: Option<String>,
- pub billing_address_collection: Option<StripeBillingAddressCollection>,
- pub customer_update: Option<StripeCustomerUpdate>,
- pub tax_id_collection: Option<StripeTaxIdCollection>,
-}
-
-pub struct FakeStripeClient {
- pub customers: Arc<Mutex<HashMap<StripeCustomerId, StripeCustomer>>>,
- pub subscriptions: Arc<Mutex<HashMap<StripeSubscriptionId, StripeSubscription>>>,
- pub update_subscription_calls:
- Arc<Mutex<Vec<(StripeSubscriptionId, UpdateSubscriptionParams)>>>,
- pub prices: Arc<Mutex<HashMap<StripePriceId, StripePrice>>>,
- pub meters: Arc<Mutex<HashMap<StripeMeterId, StripeMeter>>>,
- pub create_meter_event_calls: Arc<Mutex<Vec<StripeCreateMeterEventCall>>>,
- pub create_checkout_session_calls: Arc<Mutex<Vec<StripeCreateCheckoutSessionCall>>>,
-}
-
-impl FakeStripeClient {
- pub fn new() -> Self {
- Self {
- customers: Arc::new(Mutex::new(HashMap::default())),
- subscriptions: Arc::new(Mutex::new(HashMap::default())),
- update_subscription_calls: Arc::new(Mutex::new(Vec::new())),
- prices: Arc::new(Mutex::new(HashMap::default())),
- meters: Arc::new(Mutex::new(HashMap::default())),
- create_meter_event_calls: Arc::new(Mutex::new(Vec::new())),
- create_checkout_session_calls: Arc::new(Mutex::new(Vec::new())),
- }
- }
-}
-
-#[async_trait]
-impl StripeClient for FakeStripeClient {
- async fn list_customers_by_email(&self, email: &str) -> Result<Vec<StripeCustomer>> {
- Ok(self
- .customers
- .lock()
- .values()
- .filter(|customer| customer.email.as_deref() == Some(email))
- .cloned()
- .collect())
- }
-
- async fn get_customer(&self, customer_id: &StripeCustomerId) -> Result<StripeCustomer> {
- self.customers
- .lock()
- .get(customer_id)
- .cloned()
- .ok_or_else(|| anyhow!("no customer found for {customer_id:?}"))
- }
-
- async fn create_customer(&self, params: CreateCustomerParams<'_>) -> Result<StripeCustomer> {
- let customer = StripeCustomer {
- id: StripeCustomerId(format!("cus_{}", Uuid::new_v4()).into()),
- email: params.email.map(|email| email.to_string()),
- };
-
- self.customers
- .lock()
- .insert(customer.id.clone(), customer.clone());
-
- Ok(customer)
- }
-
- async fn update_customer(
- &self,
- customer_id: &StripeCustomerId,
- params: UpdateCustomerParams<'_>,
- ) -> Result<StripeCustomer> {
- let mut customers = self.customers.lock();
- if let Some(customer) = customers.get_mut(customer_id) {
- if let Some(email) = params.email {
- customer.email = Some(email.to_string());
- }
- Ok(customer.clone())
- } else {
- Err(anyhow!("no customer found for {customer_id:?}"))
- }
- }
-
- async fn list_subscriptions_for_customer(
- &self,
- customer_id: &StripeCustomerId,
- ) -> Result<Vec<StripeSubscription>> {
- let subscriptions = self
- .subscriptions
- .lock()
- .values()
- .filter(|subscription| subscription.customer == *customer_id)
- .cloned()
- .collect();
-
- Ok(subscriptions)
- }
-
- async fn get_subscription(
- &self,
- subscription_id: &StripeSubscriptionId,
- ) -> Result<StripeSubscription> {
- self.subscriptions
- .lock()
- .get(subscription_id)
- .cloned()
- .ok_or_else(|| anyhow!("no subscription found for {subscription_id:?}"))
- }
-
- async fn create_subscription(
- &self,
- params: StripeCreateSubscriptionParams,
- ) -> Result<StripeSubscription> {
- let now = Utc::now();
-
- let subscription = StripeSubscription {
- id: StripeSubscriptionId(format!("sub_{}", Uuid::new_v4()).into()),
- customer: params.customer,
- status: stripe::SubscriptionStatus::Active,
- current_period_start: now.timestamp(),
- current_period_end: (now + Duration::days(30)).timestamp(),
- items: params
- .items
- .into_iter()
- .map(|item| StripeSubscriptionItem {
- id: StripeSubscriptionItemId(format!("si_{}", Uuid::new_v4()).into()),
- price: item
- .price
- .and_then(|price_id| self.prices.lock().get(&price_id).cloned()),
- })
- .collect(),
- cancel_at: None,
- cancellation_details: None,
- };
-
- self.subscriptions
- .lock()
- .insert(subscription.id.clone(), subscription.clone());
-
- Ok(subscription)
- }
-
- async fn update_subscription(
- &self,
- subscription_id: &StripeSubscriptionId,
- params: UpdateSubscriptionParams,
- ) -> Result<()> {
- let subscription = self.get_subscription(subscription_id).await?;
-
- self.update_subscription_calls
- .lock()
- .push((subscription.id, params));
-
- Ok(())
- }
-
- async fn cancel_subscription(&self, subscription_id: &StripeSubscriptionId) -> Result<()> {
- // TODO: Implement fake subscription cancellation.
- let _ = subscription_id;
-
- Ok(())
- }
-
- async fn list_prices(&self) -> Result<Vec<StripePrice>> {
- let prices = self.prices.lock().values().cloned().collect();
-
- Ok(prices)
- }
-
- async fn list_meters(&self) -> Result<Vec<StripeMeter>> {
- let meters = self.meters.lock().values().cloned().collect();
-
- Ok(meters)
- }
-
- async fn create_meter_event(&self, params: StripeCreateMeterEventParams<'_>) -> Result<()> {
- self.create_meter_event_calls
- .lock()
- .push(StripeCreateMeterEventCall {
- identifier: params.identifier.into(),
- event_name: params.event_name.into(),
- value: params.payload.value,
- stripe_customer_id: params.payload.stripe_customer_id.clone(),
- timestamp: params.timestamp,
- });
-
- Ok(())
- }
-
- async fn create_checkout_session(
- &self,
- params: StripeCreateCheckoutSessionParams<'_>,
- ) -> Result<StripeCheckoutSession> {
- self.create_checkout_session_calls
- .lock()
- .push(StripeCreateCheckoutSessionCall {
- customer: params.customer.cloned(),
- client_reference_id: params.client_reference_id.map(|id| id.to_string()),
- mode: params.mode,
- line_items: params.line_items,
- payment_method_collection: params.payment_method_collection,
- subscription_data: params.subscription_data,
- success_url: params.success_url.map(|url| url.to_string()),
- billing_address_collection: params.billing_address_collection,
- customer_update: params.customer_update,
- tax_id_collection: params.tax_id_collection,
- });
-
- Ok(StripeCheckoutSession {
- url: Some("https://checkout.stripe.com/c/pay/cs_test_1".to_string()),
- })
- }
-}
@@ -1,612 +0,0 @@
-use std::str::FromStr as _;
-use std::sync::Arc;
-
-use anyhow::{Context as _, Result, anyhow};
-use async_trait::async_trait;
-use serde::{Deserialize, Serialize};
-use stripe::{
- CancellationDetails, CancellationDetailsReason, CheckoutSession, CheckoutSessionMode,
- CheckoutSessionPaymentMethodCollection, CreateCheckoutSession, CreateCheckoutSessionLineItems,
- CreateCheckoutSessionSubscriptionData, CreateCheckoutSessionSubscriptionDataTrialSettings,
- CreateCheckoutSessionSubscriptionDataTrialSettingsEndBehavior,
- CreateCheckoutSessionSubscriptionDataTrialSettingsEndBehaviorMissingPaymentMethod,
- CreateCustomer, CreateSubscriptionAutomaticTax, Customer, CustomerId, ListCustomers, Price,
- PriceId, Recurring, Subscription, SubscriptionId, SubscriptionItem, SubscriptionItemId,
- UpdateCustomer, UpdateSubscriptionItems, UpdateSubscriptionTrialSettings,
- UpdateSubscriptionTrialSettingsEndBehavior,
- UpdateSubscriptionTrialSettingsEndBehaviorMissingPaymentMethod,
-};
-
-use crate::stripe_client::{
- CreateCustomerParams, StripeAutomaticTax, StripeBillingAddressCollection,
- StripeCancellationDetails, StripeCancellationDetailsReason, StripeCheckoutSession,
- StripeCheckoutSessionMode, StripeCheckoutSessionPaymentMethodCollection, StripeClient,
- StripeCreateCheckoutSessionLineItems, StripeCreateCheckoutSessionParams,
- StripeCreateCheckoutSessionSubscriptionData, StripeCreateMeterEventParams,
- StripeCreateSubscriptionParams, StripeCustomer, StripeCustomerId, StripeCustomerUpdate,
- StripeCustomerUpdateAddress, StripeCustomerUpdateName, StripeCustomerUpdateShipping,
- StripeMeter, StripePrice, StripePriceId, StripePriceRecurring, StripeSubscription,
- StripeSubscriptionId, StripeSubscriptionItem, StripeSubscriptionItemId,
- StripeSubscriptionTrialSettings, StripeSubscriptionTrialSettingsEndBehavior,
- StripeSubscriptionTrialSettingsEndBehaviorMissingPaymentMethod, StripeTaxIdCollection,
- UpdateCustomerParams, UpdateSubscriptionParams,
-};
-
-pub struct RealStripeClient {
- client: Arc<stripe::Client>,
-}
-
-impl RealStripeClient {
- pub fn new(client: Arc<stripe::Client>) -> Self {
- Self { client }
- }
-}
-
-#[async_trait]
-impl StripeClient for RealStripeClient {
- async fn list_customers_by_email(&self, email: &str) -> Result<Vec<StripeCustomer>> {
- let response = Customer::list(
- &self.client,
- &ListCustomers {
- email: Some(email),
- ..Default::default()
- },
- )
- .await?;
-
- Ok(response
- .data
- .into_iter()
- .map(StripeCustomer::from)
- .collect())
- }
-
- async fn get_customer(&self, customer_id: &StripeCustomerId) -> Result<StripeCustomer> {
- let customer_id = customer_id.try_into()?;
-
- let customer = Customer::retrieve(&self.client, &customer_id, &[]).await?;
-
- Ok(StripeCustomer::from(customer))
- }
-
- async fn create_customer(&self, params: CreateCustomerParams<'_>) -> Result<StripeCustomer> {
- let customer = Customer::create(
- &self.client,
- CreateCustomer {
- email: params.email,
- ..Default::default()
- },
- )
- .await?;
-
- Ok(StripeCustomer::from(customer))
- }
-
- async fn update_customer(
- &self,
- customer_id: &StripeCustomerId,
- params: UpdateCustomerParams<'_>,
- ) -> Result<StripeCustomer> {
- let customer = Customer::update(
- &self.client,
- &customer_id.try_into()?,
- UpdateCustomer {
- email: params.email,
- ..Default::default()
- },
- )
- .await?;
-
- Ok(StripeCustomer::from(customer))
- }
-
- async fn list_subscriptions_for_customer(
- &self,
- customer_id: &StripeCustomerId,
- ) -> Result<Vec<StripeSubscription>> {
- let customer_id = customer_id.try_into()?;
-
- let subscriptions = stripe::Subscription::list(
- &self.client,
- &stripe::ListSubscriptions {
- customer: Some(customer_id),
- status: None,
- ..Default::default()
- },
- )
- .await?;
-
- Ok(subscriptions
- .data
- .into_iter()
- .map(StripeSubscription::from)
- .collect())
- }
-
- async fn get_subscription(
- &self,
- subscription_id: &StripeSubscriptionId,
- ) -> Result<StripeSubscription> {
- let subscription_id = subscription_id.try_into()?;
-
- let subscription = Subscription::retrieve(&self.client, &subscription_id, &[]).await?;
-
- Ok(StripeSubscription::from(subscription))
- }
-
- async fn create_subscription(
- &self,
- params: StripeCreateSubscriptionParams,
- ) -> Result<StripeSubscription> {
- let customer_id = params.customer.try_into()?;
-
- let mut create_subscription = stripe::CreateSubscription::new(customer_id);
- create_subscription.items = Some(
- params
- .items
- .into_iter()
- .map(|item| stripe::CreateSubscriptionItems {
- price: item.price.map(|price| price.to_string()),
- quantity: item.quantity,
- ..Default::default()
- })
- .collect(),
- );
- create_subscription.automatic_tax = params.automatic_tax.map(Into::into);
-
- let subscription = Subscription::create(&self.client, create_subscription).await?;
-
- Ok(StripeSubscription::from(subscription))
- }
-
- async fn update_subscription(
- &self,
- subscription_id: &StripeSubscriptionId,
- params: UpdateSubscriptionParams,
- ) -> Result<()> {
- let subscription_id = subscription_id.try_into()?;
-
- stripe::Subscription::update(
- &self.client,
- &subscription_id,
- stripe::UpdateSubscription {
- items: params.items.map(|items| {
- items
- .into_iter()
- .map(|item| UpdateSubscriptionItems {
- price: item.price.map(|price| price.to_string()),
- ..Default::default()
- })
- .collect()
- }),
- trial_settings: params.trial_settings.map(Into::into),
- ..Default::default()
- },
- )
- .await?;
-
- Ok(())
- }
-
- async fn cancel_subscription(&self, subscription_id: &StripeSubscriptionId) -> Result<()> {
- let subscription_id = subscription_id.try_into()?;
-
- Subscription::cancel(
- &self.client,
- &subscription_id,
- stripe::CancelSubscription {
- invoice_now: None,
- ..Default::default()
- },
- )
- .await?;
-
- Ok(())
- }
-
- async fn list_prices(&self) -> Result<Vec<StripePrice>> {
- let response = stripe::Price::list(
- &self.client,
- &stripe::ListPrices {
- limit: Some(100),
- ..Default::default()
- },
- )
- .await?;
-
- Ok(response.data.into_iter().map(StripePrice::from).collect())
- }
-
- async fn list_meters(&self) -> Result<Vec<StripeMeter>> {
- #[derive(Serialize)]
- struct Params {
- #[serde(skip_serializing_if = "Option::is_none")]
- limit: Option<u64>,
- }
-
- let response = self
- .client
- .get_query::<stripe::List<StripeMeter>, _>(
- "/billing/meters",
- Params { limit: Some(100) },
- )
- .await?;
-
- Ok(response.data)
- }
-
- async fn create_meter_event(&self, params: StripeCreateMeterEventParams<'_>) -> Result<()> {
- #[derive(Deserialize)]
- struct StripeMeterEvent {
- pub identifier: String,
- }
-
- let identifier = params.identifier;
- match self
- .client
- .post_form::<StripeMeterEvent, _>("/billing/meter_events", params)
- .await
- {
- Ok(_event) => Ok(()),
- Err(stripe::StripeError::Stripe(error)) => {
- if error.http_status == 400
- && error
- .message
- .as_ref()
- .map_or(false, |message| message.contains(identifier))
- {
- Ok(())
- } else {
- Err(anyhow!(stripe::StripeError::Stripe(error)))
- }
- }
- Err(error) => Err(anyhow!("failed to create meter event: {error:?}")),
- }
- }
-
- async fn create_checkout_session(
- &self,
- params: StripeCreateCheckoutSessionParams<'_>,
- ) -> Result<StripeCheckoutSession> {
- let params = params.try_into()?;
- let session = CheckoutSession::create(&self.client, params).await?;
-
- Ok(session.into())
- }
-}
-
-impl From<CustomerId> for StripeCustomerId {
- fn from(value: CustomerId) -> Self {
- Self(value.as_str().into())
- }
-}
-
-impl TryFrom<StripeCustomerId> for CustomerId {
- type Error = anyhow::Error;
-
- fn try_from(value: StripeCustomerId) -> Result<Self, Self::Error> {
- Self::from_str(value.0.as_ref()).context("failed to parse Stripe customer ID")
- }
-}
-
-impl TryFrom<&StripeCustomerId> for CustomerId {
- type Error = anyhow::Error;
-
- fn try_from(value: &StripeCustomerId) -> Result<Self, Self::Error> {
- Self::from_str(value.0.as_ref()).context("failed to parse Stripe customer ID")
- }
-}
-
-impl From<Customer> for StripeCustomer {
- fn from(value: Customer) -> Self {
- StripeCustomer {
- id: value.id.into(),
- email: value.email,
- }
- }
-}
-
-impl From<SubscriptionId> for StripeSubscriptionId {
- fn from(value: SubscriptionId) -> Self {
- Self(value.as_str().into())
- }
-}
-
-impl TryFrom<&StripeSubscriptionId> for SubscriptionId {
- type Error = anyhow::Error;
-
- fn try_from(value: &StripeSubscriptionId) -> Result<Self, Self::Error> {
- Self::from_str(value.0.as_ref()).context("failed to parse Stripe subscription ID")
- }
-}
-
-impl From<Subscription> for StripeSubscription {
- fn from(value: Subscription) -> Self {
- Self {
- id: value.id.into(),
- customer: value.customer.id().into(),
- status: value.status,
- current_period_start: value.current_period_start,
- current_period_end: value.current_period_end,
- items: value.items.data.into_iter().map(Into::into).collect(),
- cancel_at: value.cancel_at,
- cancellation_details: value.cancellation_details.map(Into::into),
- }
- }
-}
-
-impl From<CancellationDetails> for StripeCancellationDetails {
- fn from(value: CancellationDetails) -> Self {
- Self {
- reason: value.reason.map(Into::into),
- }
- }
-}
-
-impl From<CancellationDetailsReason> for StripeCancellationDetailsReason {
- fn from(value: CancellationDetailsReason) -> Self {
- match value {
- CancellationDetailsReason::CancellationRequested => Self::CancellationRequested,
- CancellationDetailsReason::PaymentDisputed => Self::PaymentDisputed,
- CancellationDetailsReason::PaymentFailed => Self::PaymentFailed,
- }
- }
-}
-
-impl From<SubscriptionItemId> for StripeSubscriptionItemId {
- fn from(value: SubscriptionItemId) -> Self {
- Self(value.as_str().into())
- }
-}
-
-impl From<SubscriptionItem> for StripeSubscriptionItem {
- fn from(value: SubscriptionItem) -> Self {
- Self {
- id: value.id.into(),
- price: value.price.map(Into::into),
- }
- }
-}
-
-impl From<StripeAutomaticTax> for CreateSubscriptionAutomaticTax {
- fn from(value: StripeAutomaticTax) -> Self {
- Self {
- enabled: value.enabled,
- liability: None,
- }
- }
-}
-
-impl From<StripeSubscriptionTrialSettings> for UpdateSubscriptionTrialSettings {
- fn from(value: StripeSubscriptionTrialSettings) -> Self {
- Self {
- end_behavior: value.end_behavior.into(),
- }
- }
-}
-
-impl From<StripeSubscriptionTrialSettingsEndBehavior>
- for UpdateSubscriptionTrialSettingsEndBehavior
-{
- fn from(value: StripeSubscriptionTrialSettingsEndBehavior) -> Self {
- Self {
- missing_payment_method: value.missing_payment_method.into(),
- }
- }
-}
-
-impl From<StripeSubscriptionTrialSettingsEndBehaviorMissingPaymentMethod>
- for UpdateSubscriptionTrialSettingsEndBehaviorMissingPaymentMethod
-{
- fn from(value: StripeSubscriptionTrialSettingsEndBehaviorMissingPaymentMethod) -> Self {
- match value {
- StripeSubscriptionTrialSettingsEndBehaviorMissingPaymentMethod::Cancel => Self::Cancel,
- StripeSubscriptionTrialSettingsEndBehaviorMissingPaymentMethod::CreateInvoice => {
- Self::CreateInvoice
- }
- StripeSubscriptionTrialSettingsEndBehaviorMissingPaymentMethod::Pause => Self::Pause,
- }
- }
-}
-
-impl From<PriceId> for StripePriceId {
- fn from(value: PriceId) -> Self {
- Self(value.as_str().into())
- }
-}
-
-impl TryFrom<StripePriceId> for PriceId {
- type Error = anyhow::Error;
-
- fn try_from(value: StripePriceId) -> Result<Self, Self::Error> {
- Self::from_str(value.0.as_ref()).context("failed to parse Stripe price ID")
- }
-}
-
-impl From<Price> for StripePrice {
- fn from(value: Price) -> Self {
- Self {
- id: value.id.into(),
- unit_amount: value.unit_amount,
- lookup_key: value.lookup_key,
- recurring: value.recurring.map(StripePriceRecurring::from),
- }
- }
-}
-
-impl From<Recurring> for StripePriceRecurring {
- fn from(value: Recurring) -> Self {
- Self { meter: value.meter }
- }
-}
-
-impl<'a> TryFrom<StripeCreateCheckoutSessionParams<'a>> for CreateCheckoutSession<'a> {
- type Error = anyhow::Error;
-
- fn try_from(value: StripeCreateCheckoutSessionParams<'a>) -> Result<Self, Self::Error> {
- Ok(Self {
- customer: value
- .customer
- .map(|customer_id| customer_id.try_into())
- .transpose()?,
- client_reference_id: value.client_reference_id,
- mode: value.mode.map(Into::into),
- line_items: value
- .line_items
- .map(|line_items| line_items.into_iter().map(Into::into).collect()),
- payment_method_collection: value.payment_method_collection.map(Into::into),
- subscription_data: value.subscription_data.map(Into::into),
- success_url: value.success_url,
- billing_address_collection: value.billing_address_collection.map(Into::into),
- customer_update: value.customer_update.map(Into::into),
- tax_id_collection: value.tax_id_collection.map(Into::into),
- ..Default::default()
- })
- }
-}
-
-impl From<StripeCheckoutSessionMode> for CheckoutSessionMode {
- fn from(value: StripeCheckoutSessionMode) -> Self {
- match value {
- StripeCheckoutSessionMode::Payment => Self::Payment,
- StripeCheckoutSessionMode::Setup => Self::Setup,
- StripeCheckoutSessionMode::Subscription => Self::Subscription,
- }
- }
-}
-
-impl From<StripeCreateCheckoutSessionLineItems> for CreateCheckoutSessionLineItems {
- fn from(value: StripeCreateCheckoutSessionLineItems) -> Self {
- Self {
- price: value.price,
- quantity: value.quantity,
- ..Default::default()
- }
- }
-}
-
-impl From<StripeCheckoutSessionPaymentMethodCollection> for CheckoutSessionPaymentMethodCollection {
- fn from(value: StripeCheckoutSessionPaymentMethodCollection) -> Self {
- match value {
- StripeCheckoutSessionPaymentMethodCollection::Always => Self::Always,
- StripeCheckoutSessionPaymentMethodCollection::IfRequired => Self::IfRequired,
- }
- }
-}
-
-impl From<StripeCreateCheckoutSessionSubscriptionData> for CreateCheckoutSessionSubscriptionData {
- fn from(value: StripeCreateCheckoutSessionSubscriptionData) -> Self {
- Self {
- trial_period_days: value.trial_period_days,
- trial_settings: value.trial_settings.map(Into::into),
- metadata: value.metadata,
- ..Default::default()
- }
- }
-}
-
-impl From<StripeSubscriptionTrialSettings> for CreateCheckoutSessionSubscriptionDataTrialSettings {
- fn from(value: StripeSubscriptionTrialSettings) -> Self {
- Self {
- end_behavior: value.end_behavior.into(),
- }
- }
-}
-
-impl From<StripeSubscriptionTrialSettingsEndBehavior>
- for CreateCheckoutSessionSubscriptionDataTrialSettingsEndBehavior
-{
- fn from(value: StripeSubscriptionTrialSettingsEndBehavior) -> Self {
- Self {
- missing_payment_method: value.missing_payment_method.into(),
- }
- }
-}
-
-impl From<StripeSubscriptionTrialSettingsEndBehaviorMissingPaymentMethod>
- for CreateCheckoutSessionSubscriptionDataTrialSettingsEndBehaviorMissingPaymentMethod
-{
- fn from(value: StripeSubscriptionTrialSettingsEndBehaviorMissingPaymentMethod) -> Self {
- match value {
- StripeSubscriptionTrialSettingsEndBehaviorMissingPaymentMethod::Cancel => Self::Cancel,
- StripeSubscriptionTrialSettingsEndBehaviorMissingPaymentMethod::CreateInvoice => {
- Self::CreateInvoice
- }
- StripeSubscriptionTrialSettingsEndBehaviorMissingPaymentMethod::Pause => Self::Pause,
- }
- }
-}
-
-impl From<CheckoutSession> for StripeCheckoutSession {
- fn from(value: CheckoutSession) -> Self {
- Self { url: value.url }
- }
-}
-
-impl From<StripeBillingAddressCollection> for stripe::CheckoutSessionBillingAddressCollection {
- fn from(value: StripeBillingAddressCollection) -> Self {
- match value {
- StripeBillingAddressCollection::Auto => {
- stripe::CheckoutSessionBillingAddressCollection::Auto
- }
- StripeBillingAddressCollection::Required => {
- stripe::CheckoutSessionBillingAddressCollection::Required
- }
- }
- }
-}
-
-impl From<StripeCustomerUpdateAddress> for stripe::CreateCheckoutSessionCustomerUpdateAddress {
- fn from(value: StripeCustomerUpdateAddress) -> Self {
- match value {
- StripeCustomerUpdateAddress::Auto => {
- stripe::CreateCheckoutSessionCustomerUpdateAddress::Auto
- }
- StripeCustomerUpdateAddress::Never => {
- stripe::CreateCheckoutSessionCustomerUpdateAddress::Never
- }
- }
- }
-}
-
-impl From<StripeCustomerUpdateName> for stripe::CreateCheckoutSessionCustomerUpdateName {
- fn from(value: StripeCustomerUpdateName) -> Self {
- match value {
- StripeCustomerUpdateName::Auto => stripe::CreateCheckoutSessionCustomerUpdateName::Auto,
- StripeCustomerUpdateName::Never => {
- stripe::CreateCheckoutSessionCustomerUpdateName::Never
- }
- }
- }
-}
-
-impl From<StripeCustomerUpdateShipping> for stripe::CreateCheckoutSessionCustomerUpdateShipping {
- fn from(value: StripeCustomerUpdateShipping) -> Self {
- match value {
- StripeCustomerUpdateShipping::Auto => {
- stripe::CreateCheckoutSessionCustomerUpdateShipping::Auto
- }
- StripeCustomerUpdateShipping::Never => {
- stripe::CreateCheckoutSessionCustomerUpdateShipping::Never
- }
- }
- }
-}
-
-impl From<StripeCustomerUpdate> for stripe::CreateCheckoutSessionCustomerUpdate {
- fn from(value: StripeCustomerUpdate) -> Self {
- stripe::CreateCheckoutSessionCustomerUpdate {
- address: value.address.map(Into::into),
- name: value.name.map(Into::into),
- shipping: value.shipping.map(Into::into),
- }
- }
-}
-
-impl From<StripeTaxIdCollection> for stripe::CreateCheckoutSessionTaxIdCollection {
- fn from(value: StripeTaxIdCollection) -> Self {
- stripe::CreateCheckoutSessionTaxIdCollection {
- enabled: value.enabled,
- }
- }
-}
@@ -8,7 +8,6 @@ mod channel_buffer_tests;
mod channel_guest_tests;
mod channel_message_tests;
mod channel_tests;
-// mod debug_panel_tests;
mod editor_tests;
mod following_tests;
mod git_tests;
@@ -18,7 +17,6 @@ mod random_channel_buffer_tests;
mod random_project_collaboration_tests;
mod randomized_test_helpers;
mod remote_editing_collaboration_tests;
-mod stripe_billing_tests;
mod test_server;
use language::{Language, LanguageConfig, LanguageMatcher, tree_sitter_rust};
@@ -1,123 +0,0 @@
-use std::sync::Arc;
-
-use pretty_assertions::assert_eq;
-
-use crate::stripe_billing::StripeBilling;
-use crate::stripe_client::{FakeStripeClient, StripePrice, StripePriceId, StripePriceRecurring};
-
-fn make_stripe_billing() -> (StripeBilling, Arc<FakeStripeClient>) {
- let stripe_client = Arc::new(FakeStripeClient::new());
- let stripe_billing = StripeBilling::test(stripe_client.clone());
-
- (stripe_billing, stripe_client)
-}
-
-#[gpui::test]
-async fn test_initialize() {
- let (stripe_billing, stripe_client) = make_stripe_billing();
-
- // Add test prices
- let price1 = StripePrice {
- id: StripePriceId("price_1".into()),
- unit_amount: Some(1_000),
- lookup_key: Some("zed-pro".to_string()),
- recurring: None,
- };
- let price2 = StripePrice {
- id: StripePriceId("price_2".into()),
- unit_amount: Some(0),
- lookup_key: Some("zed-free".to_string()),
- recurring: None,
- };
- let price3 = StripePrice {
- id: StripePriceId("price_3".into()),
- unit_amount: Some(500),
- lookup_key: None,
- recurring: Some(StripePriceRecurring {
- meter: Some("meter_1".to_string()),
- }),
- };
- stripe_client
- .prices
- .lock()
- .insert(price1.id.clone(), price1);
- stripe_client
- .prices
- .lock()
- .insert(price2.id.clone(), price2);
- stripe_client
- .prices
- .lock()
- .insert(price3.id.clone(), price3);
-
- // Initialize the billing system
- stripe_billing.initialize().await.unwrap();
-
- // Verify that prices can be found by lookup key
- let zed_pro_price_id = stripe_billing.zed_pro_price_id().await.unwrap();
- assert_eq!(zed_pro_price_id.to_string(), "price_1");
-
- let zed_free_price_id = stripe_billing.zed_free_price_id().await.unwrap();
- assert_eq!(zed_free_price_id.to_string(), "price_2");
-
- // Verify that a price can be found by lookup key
- let zed_pro_price = stripe_billing
- .find_price_by_lookup_key("zed-pro")
- .await
- .unwrap();
- assert_eq!(zed_pro_price.id.to_string(), "price_1");
- assert_eq!(zed_pro_price.unit_amount, Some(1_000));
-
- // Verify that finding a non-existent lookup key returns an error
- let result = stripe_billing
- .find_price_by_lookup_key("non-existent")
- .await;
- assert!(result.is_err());
-}
-
-#[gpui::test]
-async fn test_find_or_create_customer_by_email() {
- let (stripe_billing, stripe_client) = make_stripe_billing();
-
- // Create a customer with an email that doesn't yet correspond to a customer.
- {
- let email = "user@example.com";
-
- let customer_id = stripe_billing
- .find_or_create_customer_by_email(Some(email))
- .await
- .unwrap();
-
- let customer = stripe_client
- .customers
- .lock()
- .get(&customer_id)
- .unwrap()
- .clone();
- assert_eq!(customer.email.as_deref(), Some(email));
- }
-
- // Create a customer with an email that corresponds to an existing customer.
- {
- let email = "user2@example.com";
-
- let existing_customer_id = stripe_billing
- .find_or_create_customer_by_email(Some(email))
- .await
- .unwrap();
-
- let customer_id = stripe_billing
- .find_or_create_customer_by_email(Some(email))
- .await
- .unwrap();
- assert_eq!(customer_id, existing_customer_id);
-
- let customer = stripe_client
- .customers
- .lock()
- .get(&customer_id)
- .unwrap()
- .clone();
- assert_eq!(customer.email.as_deref(), Some(email));
- }
-}
@@ -1,4 +1,3 @@
-use crate::stripe_client::FakeStripeClient;
use crate::{
AppState, Config,
db::{NewUserParams, UserId, tests::TestDb},
@@ -569,9 +568,6 @@ impl TestServer {
llm_db: None,
livekit_client: Some(Arc::new(livekit_test_server.create_api_client())),
blob_store_client: None,
- real_stripe_client: None,
- stripe_client: Some(Arc::new(FakeStripeClient::new())),
- stripe_billing: None,
executor,
kinesis_client: None,
config: Config {
@@ -608,7 +604,6 @@ impl TestServer {
auto_join_channel_id: None,
migrations_path: None,
seed_path: None,
- stripe_api_key: None,
supermaven_admin_api_key: None,
user_backfiller_github_access_token: None,
kinesis_region: None,