mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-04 17:38:10 +00:00
rework handlers to add credits
This commit is contained in:
1 parent
65f36704cf
commit
c8651fa2bc
28 files changed
+558
-56
No files matched your search
+18
@@ -0,0 +1,18 @@
|
||||
use crate::credentials::storyteller_credential_set::StorytellerCredentialSet;
|
||||
use crate::error::storyteller_error::StorytellerError;
|
||||
use crate::utils::api_host::ApiHost;
|
||||
use crate::utils::basic_json_post_request::basic_json_post_request;
|
||||
use artcraft_api_defs::stripe_artcraft::create_credits_pack_checkout::{StripeArtcraftCreateCreditsPackCheckoutRequest, StripeArtcraftCreateCreditsPackCheckoutResponse, CREATE_CREDITS_PACK_CHECKOUT_URL_PATH};
|
||||
|
||||
pub async fn create_credits_pack_checkout(
|
||||
api_host: &ApiHost,
|
||||
maybe_creds: Option<&StorytellerCredentialSet>,
|
||||
request: StripeArtcraftCreateCreditsPackCheckoutRequest,
|
||||
) -> Result<StripeArtcraftCreateCreditsPackCheckoutResponse, StorytellerError> {
|
||||
Ok(basic_json_post_request(
|
||||
api_host,
|
||||
CREATE_CREDITS_PACK_CHECKOUT_URL_PATH,
|
||||
maybe_creds,
|
||||
request,
|
||||
).await?)
|
||||
}
|
||||
+3
-3
@@ -2,13 +2,13 @@ use crate::credentials::storyteller_credential_set::StorytellerCredentialSet;
|
||||
use crate::error::storyteller_error::StorytellerError;
|
||||
use crate::utils::api_host::ApiHost;
|
||||
use crate::utils::basic_json_post_request::basic_json_post_request;
|
||||
use artcraft_api_defs::stripe_artcraft::create_subscription_checkout::{StripeArtcraftCreateCheckoutSessionRequest, StripeArtcraftCreateCheckoutSessionResponse, CREATE_SUBSCRIPTION_CHECKOUT_URL_PATH};
|
||||
use artcraft_api_defs::stripe_artcraft::create_subscription_checkout::{StripeArtcraftCreateSubscriptionCheckoutRequest, StripeArtcraftCreateSubscriptionCheckoutResponse, CREATE_SUBSCRIPTION_CHECKOUT_URL_PATH};
|
||||
|
||||
pub async fn create_subscription_checkout(
|
||||
api_host: &ApiHost,
|
||||
maybe_creds: Option<&StorytellerCredentialSet>,
|
||||
request: StripeArtcraftCreateCheckoutSessionRequest,
|
||||
) -> Result<StripeArtcraftCreateCheckoutSessionResponse, StorytellerError> {
|
||||
request: StripeArtcraftCreateSubscriptionCheckoutRequest,
|
||||
) -> Result<StripeArtcraftCreateSubscriptionCheckoutResponse, StorytellerError> {
|
||||
Ok(basic_json_post_request(
|
||||
api_host,
|
||||
CREATE_SUBSCRIPTION_CHECKOUT_URL_PATH,
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
pub mod create_subscription_checkout;
|
||||
pub mod create_credits_pack_checkout;
|
||||
pub mod create_subscription_checkout;
|
||||
@@ -1 +1,2 @@
|
||||
pub mod storyteller_open_billing_command;
|
||||
pub mod storyteller_open_billing_command;
|
||||
pub mod storyteller_open_credits_purchase_command;
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::{ContextualImageEditModel, EditImageQuality, EditImageSize};
|
||||
use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::core::state::data_dir::app_data_root::AppDataRoot;
|
||||
use crate::services::midjourney::state::midjourney_credential_manager::MidjourneyCredentialManager;
|
||||
use crate::services::midjourney::windows::open_midjourney_login_window::open_midjourney_login_window;
|
||||
use crate::services::sora::windows::sora_login_window::open_sora_login_window::open_sora_login_window;
|
||||
use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager;
|
||||
use crate::services::storyteller::windows::open_storyteller_billing_window::open_storyteller_billing_window;
|
||||
use artcraft_api_defs::stripe_artcraft::create_subscription_checkout::PlanBillingCadence;
|
||||
use enums::common::artcraft_subscription_slug::ArtcraftSubscriptionSlug;
|
||||
use enums::tauri::ux::tauri_command_caller::TauriCommandCaller;
|
||||
use errors::AnyhowResult;
|
||||
use log::{error, info};
|
||||
use serde_derive::Deserialize;
|
||||
use tauri::{AppHandle, State};
|
||||
use enums::common::artcraft_credits_pack_slug::ArtcraftCreditsPackSlug;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct StorytellerOpenCreditsPurchaseCommand {
|
||||
pub credits_pack: Option<ArtcraftCreditsPackSlug>,
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn storyteller_credits_purchase_command(
|
||||
app: AppHandle,
|
||||
request: StorytellerOpenCreditsPurchaseCommand,
|
||||
app_data_root: State<'_, AppDataRoot>,
|
||||
app_env_configs: State<'_, AppEnvConfigs>,
|
||||
storyteller_creds_manager: State<'_, StorytellerCredentialManager>,
|
||||
) -> Result<String, String> {
|
||||
info!("storyteller_open_billing_command called");
|
||||
|
||||
do_open_billing(
|
||||
&app,
|
||||
&app_data_root,
|
||||
&app_env_configs,
|
||||
&storyteller_creds_manager,
|
||||
request.credits_pack,
|
||||
)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error opening login: {:?}", err);
|
||||
format!("Error opening login: {:?}", err)
|
||||
})?;
|
||||
|
||||
Ok("result".to_string())
|
||||
}
|
||||
|
||||
async fn do_open_billing(
|
||||
app: &AppHandle,
|
||||
app_data_root: &AppDataRoot,
|
||||
app_env_configs: &AppEnvConfigs,
|
||||
storyteller_creds_manager: &StorytellerCredentialManager,
|
||||
credits_pack: Option<ArtcraftCreditsPackSlug>,
|
||||
) -> AnyhowResult<()> {
|
||||
info!("Building billing window...");
|
||||
|
||||
open_storyteller_billing_window(
|
||||
app,
|
||||
app_env_configs,
|
||||
app_data_root,
|
||||
storyteller_creds_manager,
|
||||
plan,
|
||||
cadence,
|
||||
).await?;
|
||||
|
||||
info!("Done.");
|
||||
Ok(())
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
use enums::common::artcraft_credits_pack_slug::ArtcraftCreditsPackSlug;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use utoipa::ToSchema;
|
||||
|
||||
pub const CREATE_CREDITS_PACK_CHECKOUT_URL_PATH: &str = "/v1/stripe_artcraft/checkout/credits_pack";
|
||||
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct StripeArtcraftCreateCreditsPackCheckoutRequest {
|
||||
pub credits_pack: Option<ArtcraftCreditsPackSlug>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct StripeArtcraftCreateCreditsPackCheckoutResponse {
|
||||
pub success: bool,
|
||||
pub stripe_checkout_redirect_url: String,
|
||||
}
|
||||
|
||||
+3
-3
@@ -2,10 +2,10 @@ use enums::common::artcraft_subscription_slug::ArtcraftSubscriptionSlug;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use utoipa::ToSchema;
|
||||
|
||||
pub const CREATE_SUBSCRIPTION_CHECKOUT_URL_PATH: &str = "/v1/stripe_artcraft/checkout/create_subscription";
|
||||
pub const CREATE_SUBSCRIPTION_CHECKOUT_URL_PATH: &str = "/v1/stripe_artcraft/checkout/subscription";
|
||||
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct StripeArtcraftCreateCheckoutSessionRequest {
|
||||
pub struct StripeArtcraftCreateSubscriptionCheckoutRequest {
|
||||
/// The (non-Stripe) internal identifier for the product or subscription.
|
||||
/// This will be translated into a Stripe identifier.
|
||||
pub plan: Option<ArtcraftSubscriptionSlug>,
|
||||
@@ -23,7 +23,7 @@ pub enum PlanBillingCadence {
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct StripeArtcraftCreateCheckoutSessionResponse {
|
||||
pub struct StripeArtcraftCreateSubscriptionCheckoutResponse {
|
||||
pub success: bool,
|
||||
pub stripe_checkout_redirect_url: String,
|
||||
}
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
pub mod create_credits_pack_checkout;
|
||||
pub mod create_subscription_checkout;
|
||||
@@ -0,0 +1,118 @@
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
#[cfg(test)]
|
||||
use strum::EnumCount;
|
||||
#[cfg(test)]
|
||||
use strum::EnumIter;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
/// NB: This will be used by a variety of tables (MySQL and sqlite)!
|
||||
/// Keep the max length to 16 characters.
|
||||
#[cfg_attr(test, derive(EnumIter, EnumCount))]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd, Serialize, Deserialize, ToSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ArtcraftCreditsPackSlug {
|
||||
#[serde(rename= "artcraft_1000")]
|
||||
Artcraft1000,
|
||||
#[serde(rename= "artcraft_2500")]
|
||||
Artcraft2500,
|
||||
}
|
||||
|
||||
impl_enum_display_and_debug_using_to_str!(ArtcraftCreditsPackSlug);
|
||||
impl_mysql_enum_coders!(ArtcraftCreditsPackSlug);
|
||||
impl_mysql_from_row!(ArtcraftCreditsPackSlug);
|
||||
|
||||
// NB: We can derive `sqlx::Type` instead of using `impl_mysql_enum_coders`
|
||||
|
||||
impl ArtcraftCreditsPackSlug {
|
||||
pub fn to_str(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Artcraft1000 => "artcraft_1000",
|
||||
Self::Artcraft2500 => "artcraft_2500",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_str(s: &str) -> Result<Self, String> {
|
||||
match s {
|
||||
"artcraft_1000" => Ok(Self::Artcraft1000),
|
||||
"artcraft_2500" => Ok(Self::Artcraft2500),
|
||||
_ => Err(format!("invalid artcraft_credits_pack_slug: {:?}", s)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn all_variants() -> BTreeSet<Self> {
|
||||
// NB: BTreeSet is sorted
|
||||
// NB: BTreeSet::from() isn't const, but not worth using LazyStatic, etc.
|
||||
BTreeSet::from([
|
||||
Self::Artcraft1000,
|
||||
Self::Artcraft2500,
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::test_helpers::assert_serialization;
|
||||
use crate::common::artcraft_credits_pack_slug::ArtcraftCreditsPackSlug;
|
||||
|
||||
mod explicit_checks {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_serialization() {
|
||||
assert_serialization(ArtcraftCreditsPackSlug::Artcraft1000, "artcraft_1000");
|
||||
assert_serialization(ArtcraftCreditsPackSlug::Artcraft2500, "artcraft_2500");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn to_str() {
|
||||
assert_eq!(ArtcraftCreditsPackSlug::Artcraft1000.to_str(), "artcraft_1000");
|
||||
assert_eq!(ArtcraftCreditsPackSlug::Artcraft2500.to_str(), "artcraft_2500");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_str() {
|
||||
assert_eq!(ArtcraftCreditsPackSlug::from_str("artcraft_1000").unwrap(), ArtcraftCreditsPackSlug::Artcraft1000);
|
||||
assert_eq!(ArtcraftCreditsPackSlug::from_str("artcraft_2500").unwrap(), ArtcraftCreditsPackSlug::Artcraft2500);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn all_variants() {
|
||||
let mut variants = ArtcraftCreditsPackSlug::all_variants();
|
||||
assert_eq!(variants.len(), 2);
|
||||
assert_eq!(variants.pop_first(), Some(ArtcraftCreditsPackSlug::Artcraft1000));
|
||||
assert_eq!(variants.pop_first(), Some(ArtcraftCreditsPackSlug::Artcraft2500));
|
||||
assert_eq!(variants.pop_first(), None);
|
||||
}
|
||||
}
|
||||
|
||||
mod mechanical_checks {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn variant_length() {
|
||||
use strum::IntoEnumIterator;
|
||||
assert_eq!(ArtcraftCreditsPackSlug::all_variants().len(), ArtcraftCreditsPackSlug::iter().len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn round_trip() {
|
||||
for variant in ArtcraftCreditsPackSlug::all_variants() {
|
||||
// Test to_str(), from_str(), Display, and Debug.
|
||||
assert_eq!(variant, ArtcraftCreditsPackSlug::from_str(variant.to_str()).unwrap());
|
||||
assert_eq!(variant, ArtcraftCreditsPackSlug::from_str(&format!("{}", variant)).unwrap());
|
||||
assert_eq!(variant, ArtcraftCreditsPackSlug::from_str(&format!("{:?}", variant)).unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialized_length_ok_for_database() {
|
||||
const MAX_LENGTH : usize = 16;
|
||||
for variant in ArtcraftCreditsPackSlug::all_variants() {
|
||||
let serialized = variant.to_str();
|
||||
assert!(serialized.len() > 0, "variant {:?} is too short", variant);
|
||||
assert!(serialized.len() <= MAX_LENGTH, "variant {:?} is too long", variant);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod artcraft_credits_pack_slug;
|
||||
pub mod artcraft_subscription_slug;
|
||||
pub mod generation_provider;
|
||||
pub mod job_status;
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
use crate::configs::credits_packs::stripe_artcraft_credits_pack_info::StripeArtcraftCreditsPackInfo;
|
||||
use crate::configs::credits_packs::stripe_artcraft_credits_pack_info_list::{ARTCRAFT_1000_PRODUCTION, ARTCRAFT_1000_SANDBOX, ARTCRAFT_2500_PRODUCTION, ARTCRAFT_2500_SANDBOX};
|
||||
use enums::common::artcraft_credits_pack_slug::ArtcraftCreditsPackSlug;
|
||||
use reusable_types::server_environment::ServerEnvironment;
|
||||
|
||||
pub fn get_artcraft_credits_pack_by_slug_and_env(slug: ArtcraftCreditsPackSlug, env: ServerEnvironment) -> StripeArtcraftCreditsPackInfo {
|
||||
match (env, slug) {
|
||||
// Development sandbox
|
||||
(ServerEnvironment::Development, ArtcraftCreditsPackSlug::Artcraft1000) => ARTCRAFT_1000_SANDBOX,
|
||||
(ServerEnvironment::Development, ArtcraftCreditsPackSlug::Artcraft2500) => ARTCRAFT_2500_SANDBOX,
|
||||
// Production
|
||||
(ServerEnvironment::Production, ArtcraftCreditsPackSlug::Artcraft1000) => ARTCRAFT_1000_PRODUCTION,
|
||||
(ServerEnvironment::Production, ArtcraftCreditsPackSlug::Artcraft2500) => ARTCRAFT_2500_PRODUCTION,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
pub mod get_artcraft_credits_pack_by_slug_and_env;
|
||||
pub mod stripe_artcraft_credits_pack_info;
|
||||
pub mod stripe_artcraft_credits_pack_info_list;
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
use enums::common::artcraft_credits_pack_slug::ArtcraftCreditsPackSlug;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct StripeArtcraftCreditsPackInfo {
|
||||
pub slug: ArtcraftCreditsPackSlug,
|
||||
pub product_id: &'static str,
|
||||
pub price_id: &'static str,
|
||||
}
|
||||
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
use crate::configs::credits_packs::stripe_artcraft_credits_pack_info::StripeArtcraftCreditsPackInfo;
|
||||
use enums::common::artcraft_credits_pack_slug::ArtcraftCreditsPackSlug;
|
||||
|
||||
//
|
||||
// SANDBOX
|
||||
//
|
||||
|
||||
pub const ARTCRAFT_1000_SANDBOX : StripeArtcraftCreditsPackInfo = StripeArtcraftCreditsPackInfo {
|
||||
slug: ArtcraftCreditsPackSlug::Artcraft1000,
|
||||
product_id: "",
|
||||
price_id: "",
|
||||
};
|
||||
|
||||
pub const ARTCRAFT_2500_SANDBOX : StripeArtcraftCreditsPackInfo = StripeArtcraftCreditsPackInfo {
|
||||
slug: ArtcraftCreditsPackSlug::Artcraft2500,
|
||||
product_id: "",
|
||||
price_id: "",
|
||||
};
|
||||
|
||||
//
|
||||
// PRODUCTION
|
||||
//
|
||||
|
||||
pub const ARTCRAFT_1000_PRODUCTION : StripeArtcraftCreditsPackInfo = StripeArtcraftCreditsPackInfo {
|
||||
slug: ArtcraftCreditsPackSlug::Artcraft1000,
|
||||
product_id: "FIXME_1",
|
||||
price_id: "FIXME_2",
|
||||
};
|
||||
|
||||
pub const ARTCRAFT_2500_PRODUCTION : StripeArtcraftCreditsPackInfo = StripeArtcraftCreditsPackInfo {
|
||||
slug: ArtcraftCreditsPackSlug::Artcraft2500,
|
||||
product_id: "FIXME_3",
|
||||
price_id: "FIXME_4",
|
||||
};
|
||||
+28
-15
@@ -1,41 +1,54 @@
|
||||
use crate::configs::stripe_artcraft_product_info::StripeArtcraftProductInfo;
|
||||
use crate::configs::stripe_artcraft_product_info_list::{ARTCRAFT_BASIC_PRODUCTION, ARTCRAFT_BASIC_SANDBOX, ARTCRAFT_MAX_PRODUCTION, ARTCRAFT_MAX_SANDBOX, ARTCRAFT_PRO_PRODUCTION, ARTCRAFT_PRO_SANDBOX};
|
||||
use crate::configs::credits_packs::stripe_artcraft_credits_pack_info::StripeArtcraftCreditsPackInfo;
|
||||
use crate::configs::credits_packs::stripe_artcraft_credits_pack_info_list::{ARTCRAFT_1000_PRODUCTION, ARTCRAFT_1000_SANDBOX, ARTCRAFT_2500_PRODUCTION, ARTCRAFT_2500_SANDBOX};
|
||||
use crate::configs::stripe_artcraft_generic_product_info::StripeArtcraftGenericProductInfo;
|
||||
use crate::configs::subscriptions::stripe_artcraft_subscription_info::StripeArtcraftSubscriptionInfo;
|
||||
use crate::configs::subscriptions::stripe_artcraft_subscription_info_list::{ARTCRAFT_BASIC_PRODUCTION, ARTCRAFT_BASIC_SANDBOX, ARTCRAFT_MAX_PRODUCTION, ARTCRAFT_MAX_SANDBOX, ARTCRAFT_PRO_PRODUCTION, ARTCRAFT_PRO_SANDBOX};
|
||||
use once_cell::sync::Lazy;
|
||||
use reusable_types::server_environment::ServerEnvironment;
|
||||
use std::collections::HashMap;
|
||||
|
||||
|
||||
static PRODUCTION_PRODUCTS_BY_STRIPE_ID : Lazy<HashMap<String, StripeArtcraftProductInfo>> = Lazy::new(|| {
|
||||
static PRODUCTION_PRODUCTS_BY_STRIPE_ID : Lazy<HashMap<String, StripeArtcraftGenericProductInfo>> = Lazy::new(|| {
|
||||
let mut plans = HashMap::new();
|
||||
|
||||
fn add_plan(plans: &mut HashMap<String, StripeArtcraftProductInfo>, product: &StripeArtcraftProductInfo) {
|
||||
plans.insert(product.product_id.to_string(), product.clone());
|
||||
}
|
||||
|
||||
|
||||
// Subscriptions
|
||||
add_plan(&mut plans, &ARTCRAFT_BASIC_PRODUCTION);
|
||||
add_plan(&mut plans, &ARTCRAFT_PRO_PRODUCTION);
|
||||
add_plan(&mut plans, &ARTCRAFT_MAX_PRODUCTION);
|
||||
|
||||
// One-off payments
|
||||
add_credits(&mut plans, &ARTCRAFT_1000_PRODUCTION);
|
||||
add_credits(&mut plans, &ARTCRAFT_2500_PRODUCTION);
|
||||
|
||||
plans
|
||||
});
|
||||
|
||||
static SANDBOX_PRODUCTS_BY_STRIPE_ID : Lazy<HashMap<String, StripeArtcraftProductInfo>> = Lazy::new(|| {
|
||||
static SANDBOX_PRODUCTS_BY_STRIPE_ID : Lazy<HashMap<String, StripeArtcraftGenericProductInfo>> = Lazy::new(|| {
|
||||
let mut plans = HashMap::new();
|
||||
|
||||
fn add_plan(plans: &mut HashMap<String, StripeArtcraftProductInfo>, product: &StripeArtcraftProductInfo) {
|
||||
plans.insert(product.product_id.to_string(), product.clone());
|
||||
}
|
||||
|
||||
// Subscriptions
|
||||
add_plan(&mut plans, &ARTCRAFT_BASIC_SANDBOX);
|
||||
add_plan(&mut plans, &ARTCRAFT_PRO_SANDBOX);
|
||||
add_plan(&mut plans, &ARTCRAFT_MAX_SANDBOX);
|
||||
|
||||
// One-off payments
|
||||
add_credits(&mut plans, &ARTCRAFT_1000_SANDBOX);
|
||||
add_credits(&mut plans, &ARTCRAFT_2500_SANDBOX);
|
||||
|
||||
plans
|
||||
});
|
||||
|
||||
pub fn get_artcraft_product_by_stripe_id_and_env(stripe_id: &str, env: ServerEnvironment) -> Option<&StripeArtcraftProductInfo> {
|
||||
pub fn get_artcraft_product_by_stripe_id_and_env(stripe_id: &str, env: ServerEnvironment) -> Option<&StripeArtcraftGenericProductInfo> {
|
||||
match env {
|
||||
ServerEnvironment::Development => SANDBOX_PRODUCTS_BY_STRIPE_ID.get(stripe_id),
|
||||
ServerEnvironment::Production => PRODUCTION_PRODUCTS_BY_STRIPE_ID.get(stripe_id),
|
||||
}
|
||||
}
|
||||
|
||||
fn add_plan(plans: &mut HashMap<String, StripeArtcraftGenericProductInfo>, product: &StripeArtcraftSubscriptionInfo) {
|
||||
plans.insert(product.product_id.to_string(), StripeArtcraftGenericProductInfo::Subscription(product.clone()));
|
||||
}
|
||||
|
||||
fn add_credits(plans: &mut HashMap<String, StripeArtcraftGenericProductInfo>, product: &StripeArtcraftCreditsPackInfo) {
|
||||
plans.insert(product.product_id.to_string(), StripeArtcraftGenericProductInfo::CreditsPack(product.clone()));
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
pub mod get_artcraft_product_by_slug_and_env;
|
||||
pub mod credits_packs;
|
||||
pub mod get_artcraft_product_by_stripe_id_and_env;
|
||||
pub mod stripe_artcraft_generic_product_info;
|
||||
pub mod stripe_artcraft_metadata_keys;
|
||||
pub mod stripe_artcraft_product_info;
|
||||
pub mod stripe_artcraft_product_info_list;
|
||||
pub mod subscriptions;
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
use crate::configs::credits_packs::stripe_artcraft_credits_pack_info::StripeArtcraftCreditsPackInfo;
|
||||
use crate::configs::subscriptions::stripe_artcraft_subscription_info::StripeArtcraftSubscriptionInfo;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum StripeArtcraftGenericProductInfo {
|
||||
CreditsPack(StripeArtcraftCreditsPackInfo),
|
||||
Subscription(StripeArtcraftSubscriptionInfo),
|
||||
}
|
||||
+3
-3
@@ -1,9 +1,9 @@
|
||||
use crate::configs::stripe_artcraft_product_info::StripeArtcraftProductInfo;
|
||||
use crate::configs::stripe_artcraft_product_info_list::{ARTCRAFT_BASIC_PRODUCTION, ARTCRAFT_BASIC_SANDBOX, ARTCRAFT_MAX_PRODUCTION, ARTCRAFT_MAX_SANDBOX, ARTCRAFT_PRO_PRODUCTION, ARTCRAFT_PRO_SANDBOX};
|
||||
use crate::configs::subscriptions::stripe_artcraft_subscription_info::StripeArtcraftSubscriptionInfo;
|
||||
use crate::configs::subscriptions::stripe_artcraft_subscription_info_list::{ARTCRAFT_BASIC_PRODUCTION, ARTCRAFT_BASIC_SANDBOX, ARTCRAFT_MAX_PRODUCTION, ARTCRAFT_MAX_SANDBOX, ARTCRAFT_PRO_PRODUCTION, ARTCRAFT_PRO_SANDBOX};
|
||||
use enums::common::artcraft_subscription_slug::ArtcraftSubscriptionSlug;
|
||||
use reusable_types::server_environment::ServerEnvironment;
|
||||
|
||||
pub fn get_artcraft_product_by_slug_and_env(slug: ArtcraftSubscriptionSlug, env: ServerEnvironment) -> StripeArtcraftProductInfo {
|
||||
pub fn get_artcraft_subscription_by_slug_and_env(slug: ArtcraftSubscriptionSlug, env: ServerEnvironment) -> StripeArtcraftSubscriptionInfo {
|
||||
match (env, slug) {
|
||||
// Development sandbox
|
||||
(ServerEnvironment::Development, ArtcraftSubscriptionSlug::ArtcraftBasic) => ARTCRAFT_BASIC_SANDBOX,
|
||||
@@ -0,0 +1,3 @@
|
||||
pub mod get_artcraft_subscription_by_slug_and_env;
|
||||
pub mod stripe_artcraft_subscription_info;
|
||||
pub mod stripe_artcraft_subscription_info_list;
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
use enums::common::artcraft_subscription_slug::ArtcraftSubscriptionSlug;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct StripeArtcraftProductInfo {
|
||||
pub struct StripeArtcraftSubscriptionInfo {
|
||||
pub slug: ArtcraftSubscriptionSlug,
|
||||
pub product_id: &'static str,
|
||||
pub monthly_price_id: &'static str,
|
||||
+7
-7
@@ -1,25 +1,25 @@
|
||||
use crate::configs::stripe_artcraft_product_info::StripeArtcraftProductInfo;
|
||||
use crate::configs::subscriptions::stripe_artcraft_subscription_info::StripeArtcraftSubscriptionInfo;
|
||||
use enums::common::artcraft_subscription_slug::ArtcraftSubscriptionSlug;
|
||||
|
||||
//
|
||||
// SANDBOX
|
||||
//
|
||||
|
||||
pub const ARTCRAFT_BASIC_SANDBOX : StripeArtcraftProductInfo = StripeArtcraftProductInfo {
|
||||
pub const ARTCRAFT_BASIC_SANDBOX : StripeArtcraftSubscriptionInfo = StripeArtcraftSubscriptionInfo {
|
||||
slug: ArtcraftSubscriptionSlug::ArtcraftBasic,
|
||||
product_id: "prod_SyJURfbu2ixP7M",
|
||||
monthly_price_id: "price_1S2MrSEobp4xy4Tlwit8aeNE",
|
||||
yearly_price_id: "price_1S2MrsEobp4xy4TlDG2skTkc",
|
||||
};
|
||||
|
||||
pub const ARTCRAFT_PRO_SANDBOX : StripeArtcraftProductInfo = StripeArtcraftProductInfo {
|
||||
pub const ARTCRAFT_PRO_SANDBOX : StripeArtcraftSubscriptionInfo = StripeArtcraftSubscriptionInfo {
|
||||
slug: ArtcraftSubscriptionSlug::ArtcraftPro,
|
||||
product_id: "prod_SyJTy5arqKmaxT",
|
||||
monthly_price_id: "price_1S2MqZEobp4xy4TlR9KLyitD",
|
||||
yearly_price_id: "price_1S2MqyEobp4xy4Tl0ZXOK8Z0",
|
||||
};
|
||||
|
||||
pub const ARTCRAFT_MAX_SANDBOX : StripeArtcraftProductInfo = StripeArtcraftProductInfo {
|
||||
pub const ARTCRAFT_MAX_SANDBOX : StripeArtcraftSubscriptionInfo = StripeArtcraftSubscriptionInfo {
|
||||
slug: ArtcraftSubscriptionSlug::ArtcraftMax,
|
||||
product_id: "prod_SyJSoHNUU81BjU",
|
||||
monthly_price_id: "price_1S2MpGEobp4xy4TldyO9lAwd",
|
||||
@@ -30,21 +30,21 @@ pub const ARTCRAFT_MAX_SANDBOX : StripeArtcraftProductInfo = StripeArtcraftProdu
|
||||
// PRODUCTION
|
||||
//
|
||||
|
||||
pub const ARTCRAFT_BASIC_PRODUCTION : StripeArtcraftProductInfo = StripeArtcraftProductInfo {
|
||||
pub const ARTCRAFT_BASIC_PRODUCTION : StripeArtcraftSubscriptionInfo = StripeArtcraftSubscriptionInfo {
|
||||
slug: ArtcraftSubscriptionSlug::ArtcraftBasic,
|
||||
product_id: "prod_SyIXACAGqPbsoG",
|
||||
monthly_price_id: "price_1S2LwbIaZEzwFveeYqYxgrV7",
|
||||
yearly_price_id: "price_1S2Lz8IaZEzwFvee40D7PFoa",
|
||||
};
|
||||
|
||||
pub const ARTCRAFT_PRO_PRODUCTION : StripeArtcraftProductInfo = StripeArtcraftProductInfo {
|
||||
pub const ARTCRAFT_PRO_PRODUCTION : StripeArtcraftSubscriptionInfo = StripeArtcraftSubscriptionInfo {
|
||||
slug: ArtcraftSubscriptionSlug::ArtcraftPro,
|
||||
product_id: "prod_SyIfqs9Rmv3Fdu",
|
||||
monthly_price_id: "price_1S2M4FIaZEzwFveeRoqYDbDw",
|
||||
yearly_price_id: "price_1S2M5VIaZEzwFveeLAXk3hL8",
|
||||
};
|
||||
|
||||
pub const ARTCRAFT_MAX_PRODUCTION : StripeArtcraftProductInfo = StripeArtcraftProductInfo {
|
||||
pub const ARTCRAFT_MAX_PRODUCTION : StripeArtcraftSubscriptionInfo = StripeArtcraftSubscriptionInfo {
|
||||
slug: ArtcraftSubscriptionSlug::ArtcraftMax,
|
||||
product_id: "prod_SyJ6VIFnc3riPp",
|
||||
monthly_price_id: "price_1S2MUSIaZEzwFveesCPf2XjP",
|
||||
@@ -1 +1,2 @@
|
||||
pub mod stripe_artcraft_create_subscription_checkout_session_handler;
|
||||
pub mod stripe_artcraft_create_credits_pack_checkout_handler;
|
||||
pub mod stripe_artcraft_create_subscription_checkout_handler;
|
||||
+165
@@ -0,0 +1,165 @@
|
||||
use crate::configs::credits_packs::get_artcraft_credits_pack_by_slug_and_env::get_artcraft_credits_pack_by_slug_and_env;
|
||||
use crate::configs::stripe_artcraft_metadata_keys::{STRIPE_ARTCRAFT_METADATA_EMAIL, STRIPE_ARTCRAFT_METADATA_USERNAME, STRIPE_ARTCRAFT_METADATA_USER_TOKEN};
|
||||
use crate::configs::subscriptions::get_artcraft_subscription_by_slug_and_env::get_artcraft_subscription_by_slug_and_env;
|
||||
use crate::utils::artcraft_stripe_config::ArtcraftStripeConfigWithClient;
|
||||
use crate::utils::common_web_error::CommonWebError;
|
||||
use actix_web::web::{Data, Json};
|
||||
use actix_web::{web, HttpRequest};
|
||||
use artcraft_api_defs::stripe_artcraft::create_credits_pack_checkout::{StripeArtcraftCreateCreditsPackCheckoutRequest, StripeArtcraftCreateCreditsPackCheckoutResponse};
|
||||
use artcraft_api_defs::stripe_artcraft::create_subscription_checkout::{PlanBillingCadence, StripeArtcraftCreateSubscriptionCheckoutRequest, StripeArtcraftCreateSubscriptionCheckoutResponse};
|
||||
use component_traits::traits::internal_user_lookup::InternalUserLookup;
|
||||
use enums::common::artcraft_subscription_slug::ArtcraftSubscriptionSlug;
|
||||
use enums::common::subscription_namespace::SubscriptionNamespace;
|
||||
use log::{error, info, warn};
|
||||
use reusable_types::server_environment::ServerEnvironment;
|
||||
use std::collections::HashMap;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
use stripe_checkout::checkout_session::{CreateCheckoutSession, CreateCheckoutSessionAutomaticTax, CreateCheckoutSessionLineItems, CreateCheckoutSessionPaymentIntentData, CreateCheckoutSessionSubscriptionData};
|
||||
use stripe_checkout::CheckoutSessionMode;
|
||||
use stripe_core::CustomerId;
|
||||
use user_traits_component::traits::internal_session_cache_purge::InternalSessionCachePurge;
|
||||
|
||||
//use utoipa::ToSchema;
|
||||
|
||||
// /// Create a Stripe Checkout session and return the redirect URL in Json.
|
||||
// #[utoipa::path(
|
||||
// get,
|
||||
// tag = "Stripe (Artcraft)",
|
||||
// path = "/v1/stripe_artcraft/checkout/credits_pack",
|
||||
// params(
|
||||
// ("request" = CreateCheckoutSessionRequest, description = "Payload for Request"),
|
||||
// ),
|
||||
// responses(
|
||||
// (status = 200, description = "Success Delete", body = CreateCheckoutSessionSuccessResponse),
|
||||
// ),
|
||||
// )]
|
||||
pub async fn stripe_artcraft_create_credits_pack_checkout_handler(
|
||||
http_request: HttpRequest,
|
||||
request: Json<StripeArtcraftCreateCreditsPackCheckoutRequest>,
|
||||
stripe_config: Data<ArtcraftStripeConfigWithClient>,
|
||||
server_environment: Data<ServerEnvironment>,
|
||||
internal_user_lookup: Data<dyn InternalUserLookup>,
|
||||
internal_session_cache_purge: Data<dyn InternalSessionCachePurge>,
|
||||
) -> Result<Json<StripeArtcraftCreateCreditsPackCheckoutResponse>, CommonWebError>
|
||||
{
|
||||
let slug = match request.credits_pack {
|
||||
None => return Err(CommonWebError::BadInputWithSimpleMessage("no credits pack supplied".to_string())),
|
||||
Some(slug) => slug,
|
||||
};
|
||||
|
||||
let credits_pack = get_artcraft_credits_pack_by_slug_and_env(slug, **server_environment);
|
||||
|
||||
let maybe_user_metadata = internal_user_lookup
|
||||
.lookup_user_from_http_request(&http_request)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error looking up user: {:?}", err);
|
||||
CommonWebError::ServerError // NB: This was probably *our* fault.
|
||||
})?;
|
||||
|
||||
// NB: Our integration relies on an internal user token being present.
|
||||
let user_metadata = match maybe_user_metadata {
|
||||
None => return Err(CommonWebError::NotAuthorized),
|
||||
Some(user_metadata) => user_metadata,
|
||||
};
|
||||
|
||||
let success_url = stripe_config.checkout_success_url.clone();
|
||||
let cancel_url = stripe_config.checkout_cancel_url.clone();
|
||||
|
||||
let checkout_session = {
|
||||
// `client_reference_id`
|
||||
// Stripe Docs:
|
||||
// A unique string to reference the Checkout Session.
|
||||
// This can be a customer ID, a cart ID, or similar, and can be used to reconcile the session
|
||||
// with your internal systems.
|
||||
//
|
||||
// Our Notes:
|
||||
// This gets reported back in the Checkout Session (and related webhooks) as
|
||||
// `client_reference_id`. Passing the same ID on multiple checkouts does not unify or
|
||||
// cross-correlate customers and only seems to be metadata for the checkout session itself.
|
||||
// This is probably only useful for tracking checkout session engagement.
|
||||
//params.client_reference_id = Some("SOME_INTERNAL_ID");
|
||||
|
||||
// `customer_email`
|
||||
// Stripe Docs:
|
||||
// If provided, this value will be used when the Customer object is created. If not provided,
|
||||
// customers will be asked to enter their email address. Use this parameter to prefill
|
||||
// customer data if you already have an email on file. To access information about the
|
||||
// customer once a session is complete, use the customer field.
|
||||
//
|
||||
// Our Notes:
|
||||
// This does not look up previous customers with the same email and will not unify or
|
||||
// cross-correlate customers. By default the field will be un-editable in the checkout flow
|
||||
// if this is specified.
|
||||
//params.customer_email = Some("email@example.com");
|
||||
|
||||
let mut metadata = HashMap::new();
|
||||
|
||||
metadata.insert(STRIPE_ARTCRAFT_METADATA_USER_TOKEN.to_string(), user_metadata.user_token.to_string());
|
||||
|
||||
if let Some(username) = user_metadata.username.as_deref() {
|
||||
metadata.insert(STRIPE_ARTCRAFT_METADATA_USERNAME.to_string(), username.to_string());
|
||||
}
|
||||
|
||||
if let Some(user_email) = user_metadata.user_email.as_deref() {
|
||||
metadata.insert(STRIPE_ARTCRAFT_METADATA_EMAIL.to_string(), user_email.to_string());
|
||||
}
|
||||
|
||||
let mut checkout_builder = CreateCheckoutSession::new()
|
||||
.success_url(&success_url)
|
||||
.cancel_url(&cancel_url)
|
||||
.mode(CheckoutSessionMode::Payment)
|
||||
.line_items(vec![
|
||||
CreateCheckoutSessionLineItems {
|
||||
price: Some(credits_pack.price_id.to_string()),
|
||||
quantity: None,
|
||||
..Default::default()
|
||||
}
|
||||
])
|
||||
.allow_promotion_codes(true)
|
||||
.automatic_tax(CreateCheckoutSessionAutomaticTax {
|
||||
enabled: true,
|
||||
liability: None,
|
||||
})
|
||||
.metadata(metadata.clone())
|
||||
.payment_intent_data(CreateCheckoutSessionPaymentIntentData {
|
||||
metadata: Some(metadata),
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
if let Some(existing_stripe_customer_id) = user_metadata.maybe_existing_stripe_customer_id.as_deref() {
|
||||
match CustomerId::from_str(existing_stripe_customer_id) {
|
||||
Ok(customer_id) => {
|
||||
checkout_builder = checkout_builder.customer(customer_id);
|
||||
}
|
||||
Err(err) => {
|
||||
// NB: Don't block checkout.
|
||||
warn!("Error parsing user's ({}) supposed existing stripe customer id: {:?}",
|
||||
&user_metadata.user_token,
|
||||
err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let checkout_session = checkout_builder
|
||||
.send(&stripe_config.client)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Stripe Error: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
})?;
|
||||
|
||||
checkout_session
|
||||
};
|
||||
|
||||
let url = checkout_session.url.ok_or(CommonWebError::ServerError)?;
|
||||
|
||||
// Best effort to delete Redis session cache
|
||||
// internal_session_cache_purge.best_effort_purge_session_cache(&http_request);
|
||||
|
||||
Ok(Json(StripeArtcraftCreateCreditsPackCheckoutResponse{
|
||||
success: true,
|
||||
stripe_checkout_redirect_url: url,
|
||||
}))
|
||||
}
|
||||
+7
-7
@@ -1,10 +1,10 @@
|
||||
use crate::configs::get_artcraft_product_by_slug_and_env::get_artcraft_product_by_slug_and_env;
|
||||
use crate::configs::stripe_artcraft_metadata_keys::{STRIPE_ARTCRAFT_METADATA_EMAIL, STRIPE_ARTCRAFT_METADATA_USERNAME, STRIPE_ARTCRAFT_METADATA_USER_TOKEN};
|
||||
use crate::configs::subscriptions::get_artcraft_subscription_by_slug_and_env::get_artcraft_subscription_by_slug_and_env;
|
||||
use crate::utils::artcraft_stripe_config::ArtcraftStripeConfigWithClient;
|
||||
use crate::utils::common_web_error::CommonWebError;
|
||||
use actix_web::web::{Data, Json};
|
||||
use actix_web::{web, HttpRequest};
|
||||
use artcraft_api_defs::stripe_artcraft::create_subscription_checkout::{PlanBillingCadence, StripeArtcraftCreateCheckoutSessionRequest, StripeArtcraftCreateCheckoutSessionResponse};
|
||||
use artcraft_api_defs::stripe_artcraft::create_subscription_checkout::{PlanBillingCadence, StripeArtcraftCreateSubscriptionCheckoutRequest, StripeArtcraftCreateSubscriptionCheckoutResponse};
|
||||
use component_traits::traits::internal_user_lookup::InternalUserLookup;
|
||||
use enums::common::artcraft_subscription_slug::ArtcraftSubscriptionSlug;
|
||||
use enums::common::subscription_namespace::SubscriptionNamespace;
|
||||
@@ -32,14 +32,14 @@ use user_traits_component::traits::internal_session_cache_purge::InternalSession
|
||||
// (status = 200, description = "Success Delete", body = CreateCheckoutSessionSuccessResponse),
|
||||
// ),
|
||||
// )]
|
||||
pub async fn stripe_artcraft_create_checkout_session_handler(
|
||||
pub async fn stripe_artcraft_create_subscription_session_handler(
|
||||
http_request: HttpRequest,
|
||||
request: Json<StripeArtcraftCreateCheckoutSessionRequest>,
|
||||
request: Json<StripeArtcraftCreateSubscriptionCheckoutRequest>,
|
||||
stripe_config: Data<ArtcraftStripeConfigWithClient>,
|
||||
server_environment: Data<ServerEnvironment>,
|
||||
internal_user_lookup: Data<dyn InternalUserLookup>,
|
||||
internal_session_cache_purge: Data<dyn InternalSessionCachePurge>,
|
||||
) -> Result<Json<StripeArtcraftCreateCheckoutSessionResponse>, CommonWebError>
|
||||
) -> Result<Json<StripeArtcraftCreateSubscriptionCheckoutResponse>, CommonWebError>
|
||||
{
|
||||
let slug = match request.plan {
|
||||
None => return Err(CommonWebError::BadInputWithSimpleMessage("no plan supplied".to_string())),
|
||||
@@ -51,7 +51,7 @@ pub async fn stripe_artcraft_create_checkout_session_handler(
|
||||
Some(cadence) => cadence,
|
||||
};
|
||||
|
||||
let plan = get_artcraft_product_by_slug_and_env(slug, **server_environment);
|
||||
let plan = get_artcraft_subscription_by_slug_and_env(slug, **server_environment);
|
||||
|
||||
let price_id = match cadence {
|
||||
PlanBillingCadence::Monthly => plan.monthly_price_id.clone(),
|
||||
@@ -180,7 +180,7 @@ pub async fn stripe_artcraft_create_checkout_session_handler(
|
||||
// Best effort to delete Redis session cache
|
||||
internal_session_cache_purge.best_effort_purge_session_cache(&http_request);
|
||||
|
||||
Ok(Json(StripeArtcraftCreateCheckoutSessionResponse {
|
||||
Ok(Json(StripeArtcraftCreateSubscriptionCheckoutResponse {
|
||||
success: true,
|
||||
stripe_checkout_redirect_url: url,
|
||||
}))
|
||||
+10
-5
@@ -1,5 +1,5 @@
|
||||
use crate::configs::get_artcraft_product_by_slug_and_env::get_artcraft_product_by_slug_and_env;
|
||||
use crate::configs::get_artcraft_product_by_stripe_id_and_env::get_artcraft_product_by_stripe_id_and_env;
|
||||
use crate::configs::stripe_artcraft_generic_product_info::StripeArtcraftGenericProductInfo;
|
||||
use crate::endpoints::webhook::webhook_event_handlers::customer_subscription::calculate_subscription_end_date::calculate_subscription_end_date;
|
||||
use crate::endpoints::webhook::webhook_event_handlers::customer_subscription::subscription_event_extractor::subscription_summary_extractor;
|
||||
use crate::endpoints::webhook::webhook_event_handlers::stripe_artcraft_webhook_error::StripeArtcraftWebhookError;
|
||||
@@ -59,12 +59,17 @@ pub async fn customer_subscription_created_handler(
|
||||
&summary.stripe_product_id, server_environment);
|
||||
|
||||
let product = match maybe_product {
|
||||
Some(StripeArtcraftGenericProductInfo::Subscription(subscription)) => subscription,
|
||||
Some(StripeArtcraftGenericProductInfo::CreditsPack(credits_pack)) => {
|
||||
error!("Received 'customer.subscription.updated' for a credits pack product ({}). This should not happen.", &credits_pack.slug.to_str());
|
||||
result.should_ignore_retry = true;
|
||||
return Ok(result);
|
||||
}
|
||||
None => {
|
||||
return Err(StripeArtcraftWebhookError::BadRequest(
|
||||
format!("No matching product for stripe product ID: {}", &summary.stripe_product_id)
|
||||
));
|
||||
error!("No matching product for stripe product ID: {}", &summary.stripe_product_id);
|
||||
result.should_ignore_retry = true;
|
||||
return Ok(result);
|
||||
}
|
||||
Some(product) => product,
|
||||
};
|
||||
|
||||
if let Some(user_token) = summary.user_token.as_deref() {
|
||||
|
||||
+9
-2
@@ -1,4 +1,5 @@
|
||||
use crate::configs::get_artcraft_product_by_stripe_id_and_env::get_artcraft_product_by_stripe_id_and_env;
|
||||
use crate::configs::stripe_artcraft_generic_product_info::StripeArtcraftGenericProductInfo;
|
||||
use crate::endpoints::webhook::webhook_event_handlers::customer_subscription::calculate_subscription_end_date::calculate_subscription_end_date;
|
||||
use crate::endpoints::webhook::webhook_event_handlers::customer_subscription::subscription_event_extractor::subscription_summary_extractor;
|
||||
use crate::endpoints::webhook::webhook_event_handlers::stripe_artcraft_webhook_error::StripeArtcraftWebhookError;
|
||||
@@ -9,8 +10,8 @@ use mysql_queries::queries::users::user_subscriptions::get_user_subscription_by_
|
||||
use mysql_queries::queries::users::user_subscriptions::upsert_user_subscription_by_stripe_id::UpsertUserSubscription;
|
||||
use reusable_types::server_environment::ServerEnvironment;
|
||||
use reusable_types::stripe::stripe_subscription_status::StripeSubscriptionStatus;
|
||||
use sqlx::{MySql, MySqlPool};
|
||||
use sqlx::pool::PoolConnection;
|
||||
use sqlx::{MySql, MySqlPool};
|
||||
use stripe_shared::Subscription;
|
||||
|
||||
/// Handle event type: 'customer.subscription.deleted'
|
||||
@@ -20,6 +21,7 @@ pub async fn customer_subscription_deleted_handler(
|
||||
server_environment: ServerEnvironment,
|
||||
mysql_connection: &mut PoolConnection<MySql>,
|
||||
) -> Result<StripeArtcraftWebhookSummary, StripeArtcraftWebhookError> {
|
||||
|
||||
let summary = subscription_summary_extractor(subscription)
|
||||
.map_err(|err| {
|
||||
let reason = format!("Error extracting subscription from 'customer.subscription.deleted' payload: {:?}", err);
|
||||
@@ -41,12 +43,17 @@ pub async fn customer_subscription_deleted_handler(
|
||||
&summary.stripe_product_id, server_environment);
|
||||
|
||||
let product = match maybe_product {
|
||||
Some(StripeArtcraftGenericProductInfo::Subscription(subscription)) => subscription,
|
||||
Some(StripeArtcraftGenericProductInfo::CreditsPack(credits_pack)) => {
|
||||
error!("Received 'customer.subscription.updated' for a credits pack product ({}). This should not happen.", &credits_pack.slug.to_str());
|
||||
result.should_ignore_retry = true;
|
||||
return Ok(result);
|
||||
}
|
||||
None => {
|
||||
error!("No matching product for stripe product ID: {}", &summary.stripe_product_id);
|
||||
result.should_ignore_retry = true;
|
||||
return Ok(result);
|
||||
}
|
||||
Some(product) => product,
|
||||
};
|
||||
|
||||
// NB: It's possible to receive events out of order.
|
||||
|
||||
+8
-1
@@ -13,6 +13,8 @@ use reusable_types::stripe::stripe_subscription_status::StripeSubscriptionStatus
|
||||
use sqlx::pool::PoolConnection;
|
||||
use sqlx::{MySql, MySqlPool};
|
||||
use stripe_shared::Subscription;
|
||||
use crate::configs::stripe_artcraft_generic_product_info::StripeArtcraftGenericProductInfo;
|
||||
use crate::configs::subscriptions::get_artcraft_subscription_by_slug_and_env::get_artcraft_subscription_by_slug_and_env;
|
||||
|
||||
/// Handle event type: 'customer.subscription.updated'
|
||||
pub async fn customer_subscription_updated_handler(
|
||||
@@ -42,12 +44,17 @@ pub async fn customer_subscription_updated_handler(
|
||||
&summary.stripe_product_id, server_environment);
|
||||
|
||||
let product = match maybe_product {
|
||||
Some(StripeArtcraftGenericProductInfo::Subscription(subscription)) => subscription,
|
||||
Some(StripeArtcraftGenericProductInfo::CreditsPack(credits_pack)) => {
|
||||
error!("Received 'customer.subscription.updated' for a credits pack product ({}). This should not happen.", &credits_pack.slug.to_str());
|
||||
result.should_ignore_retry = true;
|
||||
return Ok(result);
|
||||
}
|
||||
None => {
|
||||
error!("No matching product for stripe product ID: {}", &summary.stripe_product_id);
|
||||
result.should_ignore_retry = true;
|
||||
return Ok(result);
|
||||
}
|
||||
Some(product) => product,
|
||||
};
|
||||
|
||||
// NB: It's possible to receive events out of order.
|
||||
|
||||
+8
-3
@@ -2,7 +2,8 @@ use actix_http::body::MessageBody;
|
||||
use actix_service::ServiceFactory;
|
||||
use actix_web::dev::{ServiceRequest, ServiceResponse};
|
||||
use actix_web::{web, App, Error, HttpResponse};
|
||||
use billing_artcraft_component::endpoints::checkout::stripe_artcraft_create_subscription_checkout_session_handler::stripe_artcraft_create_checkout_session_handler;
|
||||
use billing_artcraft_component::endpoints::checkout::stripe_artcraft_create_credits_pack_checkout_handler::stripe_artcraft_create_credits_pack_checkout_handler;
|
||||
use billing_artcraft_component::endpoints::checkout::stripe_artcraft_create_subscription_checkout_handler::stripe_artcraft_create_subscription_session_handler;
|
||||
use billing_artcraft_component::endpoints::webhook::stripe_artcraft_webhook_handler::stripe_artcraft_webhook_handler;
|
||||
|
||||
pub fn add_stripe_artcraft_routes<T, B> (app: App<T>) -> App<T>
|
||||
@@ -21,8 +22,12 @@ where
|
||||
.route(web::post().to(stripe_artcraft_webhook_handler))
|
||||
.route(web::head().to(|| HttpResponse::Ok()))
|
||||
)
|
||||
.service(web::resource("/checkout/create_subscription")
|
||||
.route(web::post().to(stripe_artcraft_create_checkout_session_handler))
|
||||
.service(web::resource("/checkout/credits_pack")
|
||||
.route(web::post().to(stripe_artcraft_create_credits_pack_checkout_handler))
|
||||
.route(web::head().to(|| HttpResponse::Ok()))
|
||||
)
|
||||
.service(web::resource("/checkout/subscription")
|
||||
.route(web::post().to(stripe_artcraft_create_subscription_session_handler))
|
||||
.route(web::head().to(|| HttpResponse::Ok()))
|
||||
)
|
||||
)
|
||||
|
||||
Reference in new issue
Block a user