mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-04 09:28:11 +00:00
update payment method
This commit is contained in:
1 parent
01053b7ccc
commit
68e77fd7d3
11 files changed
+251
-11
No files matched your search
+17
@@ -0,0 +1,17 @@
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use utoipa::ToSchema;
|
||||
|
||||
pub const CUSTOMER_PORTAL_UPDATE_PAYMENT_METHOD_URL_PATH: &str = "/v1/stripe_artcraft/portal/update_payment_method";
|
||||
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct StripeArtcraftCustomerPortalUpdatePaymentMethodRequest {
|
||||
// TODO: Not sure if this is needed
|
||||
pub portal_config_id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct StripeArtcraftCustomerPortalUpdatePaymentMethodResponse {
|
||||
pub success: bool,
|
||||
pub stripe_portal_url: String,
|
||||
}
|
||||
|
||||
@@ -2,4 +2,5 @@ pub mod create_credits_pack_checkout;
|
||||
pub mod create_subscription_checkout;
|
||||
pub mod customer_portal_cancel_plan;
|
||||
pub mod customer_portal_manage_plan;
|
||||
pub mod customer_portal_switch_plan;
|
||||
pub mod customer_portal_switch_plan;
|
||||
pub mod customer_portal_update_payment_method;
|
||||
+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::customer_portal_update_payment_method::{StripeArtcraftCustomerPortalUpdatePaymentMethodRequest, StripeArtcraftCustomerPortalUpdatePaymentMethodResponse, CUSTOMER_PORTAL_UPDATE_PAYMENT_METHOD_URL_PATH};
|
||||
|
||||
pub async fn customer_portal_update_payment_method(
|
||||
api_host: &ApiHost,
|
||||
maybe_creds: Option<&StorytellerCredentialSet>,
|
||||
request: StripeArtcraftCustomerPortalUpdatePaymentMethodRequest,
|
||||
) -> Result<StripeArtcraftCustomerPortalUpdatePaymentMethodResponse, StorytellerError> {
|
||||
Ok(basic_json_post_request(
|
||||
api_host,
|
||||
CUSTOMER_PORTAL_UPDATE_PAYMENT_METHOD_URL_PATH,
|
||||
maybe_creds,
|
||||
request,
|
||||
).await?)
|
||||
}
|
||||
@@ -3,3 +3,4 @@ pub mod create_subscription_checkout;
|
||||
pub mod customer_portal_cancel_plan;
|
||||
pub mod customer_portal_manage_plan;
|
||||
pub mod customer_portal_switch_plan;
|
||||
pub mod customer_portal_update_payment_method;
|
||||
+2
-1
@@ -1,3 +1,4 @@
|
||||
pub mod storyteller_open_customer_portal_cancel_plan_command;
|
||||
pub mod storyteller_open_customer_portal_manage_plan_command;
|
||||
pub mod storyteller_open_customer_portal_switch_plan_command;
|
||||
pub mod storyteller_open_customer_portal_switch_plan_command;
|
||||
pub mod storyteller_open_customer_portal_update_payment_method_command;
|
||||
-7
@@ -23,13 +23,6 @@ pub struct StorytellerOpenCustomerPortalSwitchPlanCommand {
|
||||
pub cadence: Option<PlanBillingCadenceConfirmation>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Clone, Copy)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum PortalReason {
|
||||
UpdateSubscription,
|
||||
CancelSubscription,
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn storyteller_open_customer_portal_switch_plan_command(
|
||||
app: AppHandle,
|
||||
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
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, BillingWindowCase, OpenStorytellerBillingWindowArgs};
|
||||
use artcraft_api_defs::stripe_artcraft::create_subscription_checkout::PlanBillingCadence;
|
||||
use artcraft_api_defs::stripe_artcraft::customer_portal_switch_plan::PlanBillingCadenceConfirmation;
|
||||
use enums::common::artcraft_credits_pack_slug::ArtcraftCreditsPackSlug;
|
||||
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 tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn storyteller_open_customer_portal_update_payment_method_command(
|
||||
app: AppHandle,
|
||||
app_data_root: State<'_, AppDataRoot>,
|
||||
app_env_configs: State<'_, AppEnvConfigs>,
|
||||
storyteller_creds_manager: State<'_, StorytellerCredentialManager>,
|
||||
) -> Result<String, String> {
|
||||
info!("storyteller_open_customer_portal_update_payment_method_command called");
|
||||
|
||||
do_open_portal(
|
||||
&app,
|
||||
&app_data_root,
|
||||
&app_env_configs,
|
||||
&storyteller_creds_manager,
|
||||
)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error opening customer portal window: {:?}", err);
|
||||
format!("Error opening customer portal window: {:?}", err)
|
||||
})?;
|
||||
|
||||
Ok("result".to_string())
|
||||
}
|
||||
|
||||
async fn do_open_portal(
|
||||
app: &AppHandle,
|
||||
app_data_root: &AppDataRoot,
|
||||
app_env_configs: &AppEnvConfigs,
|
||||
storyteller_creds_manager: &StorytellerCredentialManager,
|
||||
) -> AnyhowResult<()> {
|
||||
info!("Building billing window...");
|
||||
|
||||
open_storyteller_billing_window(OpenStorytellerBillingWindowArgs {
|
||||
app,
|
||||
app_data_root,
|
||||
app_env_configs,
|
||||
storyteller_creds_manager,
|
||||
billing_window_case: BillingWindowCase::CustomerPortalUpdatePaymentMethod,
|
||||
}).await?;
|
||||
|
||||
info!("Done.");
|
||||
Ok(())
|
||||
}
|
||||
+32
-1
@@ -9,6 +9,7 @@ use artcraft_api_defs::stripe_artcraft::create_subscription_checkout::{PlanBilli
|
||||
use artcraft_api_defs::stripe_artcraft::customer_portal_cancel_plan::StripeArtcraftCustomerPortalCancelPlanRequest;
|
||||
use artcraft_api_defs::stripe_artcraft::customer_portal_manage_plan::StripeArtcraftCustomerPortalManagePlanRequest;
|
||||
use artcraft_api_defs::stripe_artcraft::customer_portal_switch_plan::{PlanBillingCadenceConfirmation, StripeArtcraftCustomerPortalSwitchPlanRequest};
|
||||
use artcraft_api_defs::stripe_artcraft::customer_portal_update_payment_method::StripeArtcraftCustomerPortalUpdatePaymentMethodRequest;
|
||||
use enums::common::artcraft_credits_pack_slug::ArtcraftCreditsPackSlug;
|
||||
use enums::common::artcraft_subscription_slug::ArtcraftSubscriptionSlug;
|
||||
use errors::AnyhowResult;
|
||||
@@ -20,6 +21,7 @@ use storyteller_client::stripe_artcraft::create_subscription_checkout::create_su
|
||||
use storyteller_client::stripe_artcraft::customer_portal_cancel_plan::customer_portal_cancel_plan;
|
||||
use storyteller_client::stripe_artcraft::customer_portal_manage_plan::customer_portal_manage_plan;
|
||||
use storyteller_client::stripe_artcraft::customer_portal_switch_plan::customer_portal_switch_plan;
|
||||
use storyteller_client::stripe_artcraft::customer_portal_update_payment_method::customer_portal_update_payment_method;
|
||||
use tauri::{AppHandle, Manager, WebviewUrl, WebviewWindowBuilder};
|
||||
|
||||
pub const BILLING_WINDOW_NAME: &str = "artcraft_billing_window";
|
||||
@@ -58,6 +60,9 @@ pub enum BillingWindowCase {
|
||||
plan: ArtcraftSubscriptionSlug,
|
||||
cadence: PlanBillingCadenceConfirmation,
|
||||
},
|
||||
|
||||
/// Change the payment method attached to the customer / subscription.
|
||||
CustomerPortalUpdatePaymentMethod,
|
||||
}
|
||||
|
||||
pub async fn open_storyteller_billing_window(
|
||||
@@ -108,6 +113,12 @@ pub async fn open_storyteller_billing_window(
|
||||
cadence,
|
||||
).await?
|
||||
},
|
||||
BillingWindowCase::CustomerPortalUpdatePaymentMethod => {
|
||||
get_customer_portal_update_payment_method_url(
|
||||
args.app_env_configs,
|
||||
&creds,
|
||||
).await?
|
||||
}
|
||||
};
|
||||
|
||||
do_open_window(args.app, args.app_data_root, checkout_url).await?;
|
||||
@@ -205,7 +216,6 @@ async fn get_customer_portal_manage_plan_url(
|
||||
Ok(Url::parse(&result.stripe_portal_url)?)
|
||||
}
|
||||
|
||||
|
||||
async fn get_customer_portal_switch_plan_url(
|
||||
app_env_configs: &AppEnvConfigs,
|
||||
storyteller_creds: &StorytellerCredentialSet,
|
||||
@@ -231,6 +241,27 @@ async fn get_customer_portal_switch_plan_url(
|
||||
Ok(Url::parse(&result.stripe_portal_url)?)
|
||||
}
|
||||
|
||||
async fn get_customer_portal_update_payment_method_url(
|
||||
app_env_configs: &AppEnvConfigs,
|
||||
storyteller_creds: &StorytellerCredentialSet,
|
||||
) -> AnyhowResult<Url> {
|
||||
|
||||
info!("Getting customer portal update payment method session...");
|
||||
|
||||
let request = StripeArtcraftCustomerPortalUpdatePaymentMethodRequest {
|
||||
portal_config_id: None,
|
||||
};
|
||||
|
||||
let result = customer_portal_update_payment_method(
|
||||
&app_env_configs.storyteller_host,
|
||||
Some(&storyteller_creds),
|
||||
request,
|
||||
).await?;
|
||||
|
||||
info!("Customer portal update payment method created...");
|
||||
Ok(Url::parse(&result.stripe_portal_url)?)
|
||||
}
|
||||
|
||||
async fn do_open_window(
|
||||
app: &AppHandle,
|
||||
app_data_root: &AppDataRoot,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod stripe_artcraft_customer_portal_cancel_plan_handler;
|
||||
pub mod stripe_artcraft_customer_portal_manage_plan_handler;
|
||||
pub mod stripe_artcraft_customer_portal_switch_plan_handler;
|
||||
pub mod stripe_artcraft_customer_portal_switch_plan_handler;
|
||||
pub mod stripe_artcraft_customer_portal_update_payment_method_handler;
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
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::endpoints::webhook::common::stripe_artcraft_webhook_error::StripeArtcraftWebhookError;
|
||||
use crate::requests::lookup_subscription_from_subscription_id::lookup_subscription_from_subscription_id;
|
||||
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, StripeArtcraftCreateSubscriptionCheckoutRequest, StripeArtcraftCreateSubscriptionCheckoutResponse};
|
||||
use artcraft_api_defs::stripe_artcraft::customer_portal_switch_plan::{PlanBillingCadenceConfirmation, StripeArtcraftCustomerPortalSwitchPlanRequest, StripeArtcraftCustomerPortalSwitchPlanResponse};
|
||||
use artcraft_api_defs::stripe_artcraft::customer_portal_update_payment_method::{StripeArtcraftCustomerPortalUpdatePaymentMethodRequest, StripeArtcraftCustomerPortalUpdatePaymentMethodResponse};
|
||||
use component_traits::traits::internal_user_lookup::InternalUserLookup;
|
||||
use enums::common::artcraft_subscription_slug::ArtcraftSubscriptionSlug;
|
||||
use enums::common::payments_namespace::PaymentsNamespace;
|
||||
use log::{error, info, warn};
|
||||
use mysql_queries::queries::users::user_subscriptions::find_subscription_for_owner_user::{find_subscription_for_owner_user_using_connection, UserSubscription};
|
||||
use reusable_types::server_environment::ServerEnvironment;
|
||||
use sqlx::MySqlPool;
|
||||
use std::collections::HashMap;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
use stripe_billing::billing_portal_session::{CreateBillingPortalSession, CreateBillingPortalSessionFlowData, CreateBillingPortalSessionFlowDataAfterCompletion, CreateBillingPortalSessionFlowDataAfterCompletionRedirect, CreateBillingPortalSessionFlowDataAfterCompletionType, CreateBillingPortalSessionFlowDataSubscriptionCancel, CreateBillingPortalSessionFlowDataSubscriptionCancelRetention, CreateBillingPortalSessionFlowDataSubscriptionCancelRetentionCouponOffer, CreateBillingPortalSessionFlowDataSubscriptionCancelRetentionType, CreateBillingPortalSessionFlowDataSubscriptionUpdate, CreateBillingPortalSessionFlowDataSubscriptionUpdateConfirm, CreateBillingPortalSessionFlowDataSubscriptionUpdateConfirmItems, CreateBillingPortalSessionFlowDataType};
|
||||
use stripe_billing::BillingPortalSession;
|
||||
use stripe_checkout::checkout_session::{CreateCheckoutSession, CreateCheckoutSessionAutomaticTax, CreateCheckoutSessionLineItems, CreateCheckoutSessionSubscriptionData};
|
||||
use stripe_checkout::CheckoutSessionMode;
|
||||
use stripe_core::CustomerId;
|
||||
use tokens::tokens::users::UserToken;
|
||||
use user_traits_component::traits::internal_session_cache_purge::InternalSessionCachePurge;
|
||||
|
||||
pub async fn stripe_artcraft_customer_portal_update_payment_method_handler(
|
||||
http_request: HttpRequest,
|
||||
request: Json<StripeArtcraftCustomerPortalUpdatePaymentMethodRequest>,
|
||||
stripe_config: Data<ArtcraftStripeConfigWithClient>,
|
||||
server_environment: Data<ServerEnvironment>,
|
||||
internal_user_lookup: Data<dyn InternalUserLookup>,
|
||||
mysql_pool: Data<MySqlPool>,
|
||||
) -> Result<Json<StripeArtcraftCustomerPortalUpdatePaymentMethodResponse>, CommonWebError>
|
||||
{
|
||||
let mut mysql_connection = mysql_pool
|
||||
.acquire()
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Could not acquire mysql connection: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
})?;
|
||||
|
||||
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 user_token = UserToken::new_from_str(&user_metadata.user_token);
|
||||
|
||||
let result = find_subscription_for_owner_user_using_connection(
|
||||
&user_token,
|
||||
PaymentsNamespace::Artcraft,
|
||||
&mut mysql_connection
|
||||
).await;
|
||||
|
||||
let subscription = match result {
|
||||
Err(err) => {
|
||||
error!("Error looking up user's ({}) existing subscription: {:?}", &user_metadata.user_token, err);
|
||||
return Err(CommonWebError::ServerError); // NB: This was probably *our* fault.
|
||||
}
|
||||
Ok(None) => {
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage("user has no active subscription".to_string()))
|
||||
}
|
||||
Ok(Some(subscription)) => subscription,
|
||||
};
|
||||
|
||||
// TODO: Set the configuration id.
|
||||
|
||||
let mut portal_builder = CreateBillingPortalSession::new(subscription.stripe_customer_id.clone())
|
||||
.return_url(stripe_config.portal_return_url.clone()) // TODO: This can be a different URL.
|
||||
.flow_data(CreateBillingPortalSessionFlowData {
|
||||
type_: CreateBillingPortalSessionFlowDataType::PaymentMethodUpdate,
|
||||
after_completion: Some(CreateBillingPortalSessionFlowDataAfterCompletion {
|
||||
type_: CreateBillingPortalSessionFlowDataAfterCompletionType::Redirect,
|
||||
redirect: Some(CreateBillingPortalSessionFlowDataAfterCompletionRedirect {
|
||||
return_url: stripe_config.portal_return_url.clone(), // TODO: This can be a different URL.
|
||||
}),
|
||||
hosted_confirmation: None,
|
||||
}),
|
||||
subscription_cancel: None,
|
||||
subscription_update: None,
|
||||
subscription_update_confirm: None,
|
||||
});
|
||||
|
||||
let portal_session = portal_builder
|
||||
.send(&stripe_config.client)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Stripe Error: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
})?;
|
||||
|
||||
Ok(Json(StripeArtcraftCustomerPortalUpdatePaymentMethodResponse {
|
||||
success: true,
|
||||
stripe_portal_url: portal_session.url,
|
||||
}))
|
||||
}
|
||||
+5
@@ -7,6 +7,7 @@ use billing_artcraft_component::endpoints::checkout::stripe_artcraft_create_subs
|
||||
use billing_artcraft_component::endpoints::customer_portal::stripe_artcraft_customer_portal_cancel_plan_handler::stripe_artcraft_customer_portal_cancel_plan_handler;
|
||||
use billing_artcraft_component::endpoints::customer_portal::stripe_artcraft_customer_portal_manage_plan_handler::stripe_artcraft_customer_portal_manage_plan_handler;
|
||||
use billing_artcraft_component::endpoints::customer_portal::stripe_artcraft_customer_portal_switch_plan_handler::stripe_artcraft_customer_portal_switch_plan_handler;
|
||||
use billing_artcraft_component::endpoints::customer_portal::stripe_artcraft_customer_portal_update_payment_method_handler::stripe_artcraft_customer_portal_update_payment_method_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>
|
||||
@@ -45,5 +46,9 @@ where
|
||||
.route(web::post().to(stripe_artcraft_customer_portal_switch_plan_handler))
|
||||
.route(web::head().to(|| HttpResponse::Ok()))
|
||||
)
|
||||
.service(web::resource("/portal/update_payment_method")
|
||||
.route(web::post().to(stripe_artcraft_customer_portal_update_payment_method_handler))
|
||||
.route(web::head().to(|| HttpResponse::Ok()))
|
||||
)
|
||||
)
|
||||
}
|
||||
Reference in new issue
Block a user