diff --git a/crates/service/web/storyteller_web/src/http_server/endpoint_helpers/mod.rs b/crates/service/web/storyteller_web/src/http_server/endpoint_helpers/mod.rs new file mode 100644 index 0000000000..e21c42741e --- /dev/null +++ b/crates/service/web/storyteller_web/src/http_server/endpoint_helpers/mod.rs @@ -0,0 +1 @@ +pub mod refund_wallet_after_api_failure; diff --git a/crates/service/web/storyteller_web/src/http_server/endpoint_helpers/refund_wallet_after_api_failure.rs b/crates/service/web/storyteller_web/src/http_server/endpoint_helpers/refund_wallet_after_api_failure.rs new file mode 100644 index 0000000000..69e286f575 --- /dev/null +++ b/crates/service/web/storyteller_web/src/http_server/endpoint_helpers/refund_wallet_after_api_failure.rs @@ -0,0 +1,51 @@ +use log::{error, info}; +use mysql_queries::queries::wallets::refund::try_to_refund_ledger_entry::{try_to_refund_ledger_entry, WalletRefundOutcome}; +use sqlx::Acquire; +use tokens::tokens::wallet_ledger_entries::WalletLedgerEntryToken; + +pub async fn refund_wallet_after_api_failure( + ledger_entry_token: &WalletLedgerEntryToken, + connection: &mut sqlx::pool::PoolConnection, +) { + let mut transaction = match connection.begin().await { + Ok(tx) => tx, + Err(err) => { + error!( + "Failed to begin refund transaction after API failure (ledger {}): {:?}", + ledger_entry_token.as_str(), err + ); + return; + } + }; + + match try_to_refund_ledger_entry(ledger_entry_token, &mut transaction).await { + Ok(WalletRefundOutcome::Refunded(summary)) => { + info!( + "Refunded {} credits after API failure (ledger {} → refund ledger {}).", + summary.refund_amount, + ledger_entry_token.as_str(), + summary.refund_ledger_entry_token.as_str(), + ); + if let Err(err) = transaction.commit().await { + error!( + "Failed to commit refund after API failure (ledger {}): {:?}", + ledger_entry_token.as_str(), err + ); + } + } + Ok(WalletRefundOutcome::AlreadyRefunded) => { + info!( + "Ledger entry {} was already refunded; no action needed.", + ledger_entry_token.as_str() + ); + let _ = transaction.rollback().await; + } + Err(err) => { + error!( + "Failed to refund ledger entry {} after API failure: {:?}", + ledger_entry_token.as_str(), err + ); + let _ = transaction.rollback().await; + } + } +} diff --git a/crates/service/web/storyteller_web/src/http_server/endpoints/generate/splat/generate_worldlabs_marble_0p1_mini_splat_handler.rs b/crates/service/web/storyteller_web/src/http_server/endpoints/generate/splat/generate_worldlabs_marble_0p1_mini_splat_handler.rs index 33cfc835a4..5a331d32e3 100644 --- a/crates/service/web/storyteller_web/src/http_server/endpoints/generate/splat/generate_worldlabs_marble_0p1_mini_splat_handler.rs +++ b/crates/service/web/storyteller_web/src/http_server/endpoints/generate/splat/generate_worldlabs_marble_0p1_mini_splat_handler.rs @@ -2,6 +2,7 @@ use std::sync::Arc; use crate::billing::wallets::attempt_wallet_deduction::attempt_wallet_deduction_else_common_web_error; use crate::http_server::common_responses::common_web_error::CommonWebError; +use crate::http_server::endpoint_helpers::refund_wallet_after_api_failure::refund_wallet_after_api_failure; use crate::http_server::endpoints::generate::common::payments_error_test::payments_error_test; use crate::http_server::validations::validate_idempotency_token_format::validate_idempotency_token_format; use crate::state::server_state::ServerState; @@ -20,7 +21,6 @@ use mysql_queries::queries::generic_inference::worldlabs::insert_generic_inferen use mysql_queries::queries::idepotency_tokens::insert_idempotency_token::insert_idempotency_token; use mysql_queries::queries::prompt_context_items::insert_batch_prompt_context_items::{insert_batch_prompt_context_items, InsertBatchArgs, PromptContextItem}; use mysql_queries::queries::prompts::insert_prompt::{insert_prompt, InsertPromptArgs}; -use mysql_queries::queries::wallets::refund::try_to_refund_ledger_entry::{try_to_refund_ledger_entry, WalletRefundOutcome}; use sqlx::Acquire; use tokens::tokens::generic_inference_jobs::InferenceJobToken; use world_labs_api::api::api_types::world_labs_model::WorldLabsModel; @@ -155,7 +155,7 @@ pub async fn generate_worldlabs_marble_0p1_mini_splat_handler( Ok(result) => result, Err(err) => { warn!("World Labs generate_world error: {:?}", err); - refund_after_api_failure(&wallet_deduction.ledger_entry_token, &mut mysql_connection).await; + refund_wallet_after_api_failure(&wallet_deduction.ledger_entry_token, &mut mysql_connection).await; return Err(CommonWebError::ServerError); } }; @@ -247,50 +247,3 @@ pub async fn generate_worldlabs_marble_0p1_mini_splat_handler( inference_job_token: job_token, })) } - -async fn refund_after_api_failure( - ledger_entry_token: &tokens::tokens::wallet_ledger_entries::WalletLedgerEntryToken, - connection: &mut sqlx::pool::PoolConnection, -) { - let mut transaction = match connection.begin().await { - Ok(tx) => tx, - Err(err) => { - error!( - "Failed to begin refund transaction after API failure (ledger {}): {:?}", - ledger_entry_token.as_str(), err - ); - return; - } - }; - - match try_to_refund_ledger_entry(ledger_entry_token, &mut transaction).await { - Ok(WalletRefundOutcome::Refunded(summary)) => { - info!( - "Refunded {} credits after API failure (ledger {} → refund ledger {}).", - summary.refund_amount, - ledger_entry_token.as_str(), - summary.refund_ledger_entry_token.as_str(), - ); - if let Err(err) = transaction.commit().await { - error!( - "Failed to commit refund after API failure (ledger {}): {:?}", - ledger_entry_token.as_str(), err - ); - } - } - Ok(WalletRefundOutcome::AlreadyRefunded) => { - info!( - "Ledger entry {} was already refunded; no action needed.", - ledger_entry_token.as_str() - ); - let _ = transaction.rollback().await; - } - Err(err) => { - error!( - "Failed to refund ledger entry {} after API failure: {:?}", - ledger_entry_token.as_str(), err - ); - let _ = transaction.rollback().await; - } - } -} diff --git a/crates/service/web/storyteller_web/src/http_server/endpoints/generate/splat/generate_worldlabs_marble_0p1_plus_splat_handler.rs b/crates/service/web/storyteller_web/src/http_server/endpoints/generate/splat/generate_worldlabs_marble_0p1_plus_splat_handler.rs index 843459c620..caf17069b3 100644 --- a/crates/service/web/storyteller_web/src/http_server/endpoints/generate/splat/generate_worldlabs_marble_0p1_plus_splat_handler.rs +++ b/crates/service/web/storyteller_web/src/http_server/endpoints/generate/splat/generate_worldlabs_marble_0p1_plus_splat_handler.rs @@ -2,6 +2,7 @@ use std::sync::Arc; use crate::billing::wallets::attempt_wallet_deduction::attempt_wallet_deduction_else_common_web_error; use crate::http_server::common_responses::common_web_error::CommonWebError; +use crate::http_server::endpoint_helpers::refund_wallet_after_api_failure::refund_wallet_after_api_failure; use crate::http_server::endpoints::generate::common::payments_error_test::payments_error_test; use crate::http_server::validations::validate_idempotency_token_format::validate_idempotency_token_format; use crate::state::server_state::ServerState; @@ -20,7 +21,6 @@ use mysql_queries::queries::generic_inference::worldlabs::insert_generic_inferen use mysql_queries::queries::idepotency_tokens::insert_idempotency_token::insert_idempotency_token; use mysql_queries::queries::prompt_context_items::insert_batch_prompt_context_items::{insert_batch_prompt_context_items, InsertBatchArgs, PromptContextItem}; use mysql_queries::queries::prompts::insert_prompt::{insert_prompt, InsertPromptArgs}; -use mysql_queries::queries::wallets::refund::try_to_refund_ledger_entry::{try_to_refund_ledger_entry, WalletRefundOutcome}; use sqlx::Acquire; use tokens::tokens::generic_inference_jobs::InferenceJobToken; use world_labs_api::api::api_types::world_labs_model::WorldLabsModel; @@ -155,7 +155,7 @@ pub async fn generate_worldlabs_marble_0p1_plus_splat_handler( Ok(result) => result, Err(err) => { warn!("World Labs generate_world error: {:?}", err); - refund_after_api_failure(&wallet_deduction.ledger_entry_token, &mut mysql_connection).await; + refund_wallet_after_api_failure(&wallet_deduction.ledger_entry_token, &mut mysql_connection).await; return Err(CommonWebError::ServerError); } }; @@ -247,50 +247,3 @@ pub async fn generate_worldlabs_marble_0p1_plus_splat_handler( inference_job_token: job_token, })) } - -async fn refund_after_api_failure( - ledger_entry_token: &tokens::tokens::wallet_ledger_entries::WalletLedgerEntryToken, - connection: &mut sqlx::pool::PoolConnection, -) { - let mut transaction = match connection.begin().await { - Ok(tx) => tx, - Err(err) => { - error!( - "Failed to begin refund transaction after API failure (ledger {}): {:?}", - ledger_entry_token.as_str(), err - ); - return; - } - }; - - match try_to_refund_ledger_entry(ledger_entry_token, &mut transaction).await { - Ok(WalletRefundOutcome::Refunded(summary)) => { - info!( - "Refunded {} credits after API failure (ledger {} → refund ledger {}).", - summary.refund_amount, - ledger_entry_token.as_str(), - summary.refund_ledger_entry_token.as_str(), - ); - if let Err(err) = transaction.commit().await { - error!( - "Failed to commit refund after API failure (ledger {}): {:?}", - ledger_entry_token.as_str(), err - ); - } - } - Ok(WalletRefundOutcome::AlreadyRefunded) => { - info!( - "Ledger entry {} was already refunded; no action needed.", - ledger_entry_token.as_str() - ); - let _ = transaction.rollback().await; - } - Err(err) => { - error!( - "Failed to refund ledger entry {} after API failure: {:?}", - ledger_entry_token.as_str(), err - ); - let _ = transaction.rollback().await; - } - } -} diff --git a/crates/service/web/storyteller_web/src/http_server/endpoints/generate/video/multi_function/seedance_2p0_multi_function_video_gen_handler.rs b/crates/service/web/storyteller_web/src/http_server/endpoints/generate/video/multi_function/seedance_2p0_multi_function_video_gen_handler.rs index d8752c358a..e06bec8ae3 100644 --- a/crates/service/web/storyteller_web/src/http_server/endpoints/generate/video/multi_function/seedance_2p0_multi_function_video_gen_handler.rs +++ b/crates/service/web/storyteller_web/src/http_server/endpoints/generate/video/multi_function/seedance_2p0_multi_function_video_gen_handler.rs @@ -2,6 +2,7 @@ use std::sync::Arc; use crate::billing::wallets::attempt_wallet_deduction::attempt_wallet_deduction_else_common_web_error; use crate::http_server::common_responses::common_web_error::CommonWebError; +use crate::http_server::endpoint_helpers::refund_wallet_after_api_failure::refund_wallet_after_api_failure; use crate::http_server::endpoints::generate::common::payments_error_test::payments_error_test; use crate::http_server::validations::validate_idempotency_token_format::validate_idempotency_token_format; use crate::state::server_state::ServerState; @@ -29,7 +30,6 @@ use mysql_queries::queries::prompt_context_items::insert_batch_prompt_context_it insert_batch_prompt_context_items, InsertBatchArgs, PromptContextItem, }; use mysql_queries::queries::prompts::insert_prompt::{insert_prompt, InsertPromptArgs}; -use mysql_queries::queries::wallets::refund::try_to_refund_ledger_entry::{try_to_refund_ledger_entry, WalletRefundOutcome}; use seedance2pro::creds::seedance2pro_session::Seedance2ProSession; use seedance2pro::requests::generate_video::generate_video::{ generate_video, BatchCount, GenerateVideoArgs, Resolution, @@ -221,7 +221,7 @@ pub async fn seedance_2p0_multi_function_video_gen_handler( Ok(resp) => resp, Err(err) => { warn!("Error calling seedance2pro generate_video: {:?}", err); - refund_after_api_failure(&deduction_result.ledger_entry_token, &mut mysql_connection).await; + refund_wallet_after_api_failure(&deduction_result.ledger_entry_token, &mut mysql_connection).await; // TODO: We need to start returning robust failure messages to the caller. return Err(CommonWebError::ServerError); @@ -375,53 +375,6 @@ pub async fn seedance_2p0_multi_function_video_gen_handler( })) } -async fn refund_after_api_failure( - ledger_entry_token: &tokens::tokens::wallet_ledger_entries::WalletLedgerEntryToken, - connection: &mut sqlx::pool::PoolConnection, -) { - let mut transaction = match connection.begin().await { - Ok(tx) => tx, - Err(err) => { - error!( - "Failed to begin refund transaction after API failure (ledger {}): {:?}", - ledger_entry_token.as_str(), err - ); - return; - } - }; - - match try_to_refund_ledger_entry(ledger_entry_token, &mut transaction).await { - Ok(WalletRefundOutcome::Refunded(summary)) => { - info!( - "Refunded {} credits after API failure (ledger {} → refund ledger {}).", - summary.refund_amount, - ledger_entry_token.as_str(), - summary.refund_ledger_entry_token.as_str(), - ); - if let Err(err) = transaction.commit().await { - error!( - "Failed to commit refund after API failure (ledger {}): {:?}", - ledger_entry_token.as_str(), err - ); - } - } - Ok(WalletRefundOutcome::AlreadyRefunded) => { - info!( - "Ledger entry {} was already refunded; no action needed.", - ledger_entry_token.as_str() - ); - let _ = transaction.rollback().await; - } - Err(err) => { - error!( - "Failed to refund ledger entry {} after API failure: {:?}", - ledger_entry_token.as_str(), err - ); - let _ = transaction.rollback().await; - } - } -} - async fn upload_to_seedance2pro( session: &Seedance2ProSession, our_cdn_url: &str, diff --git a/crates/service/web/storyteller_web/src/http_server/mod.rs b/crates/service/web/storyteller_web/src/http_server/mod.rs index 672a4343c9..b3bef446c3 100644 --- a/crates/service/web/storyteller_web/src/http_server/mod.rs +++ b/crates/service/web/storyteller_web/src/http_server/mod.rs @@ -1,5 +1,6 @@ pub mod common_requests; pub mod common_responses; +pub mod endpoint_helpers; pub mod cookies; pub mod deprecated_endpoints; pub mod endpoints;