fix billing pages (#1342)

This commit is contained in:
Brandon Thomas
2026-04-11 19:45:58 -04:00
committed by GitHub
parent e77b946d57
commit adda7483e8
13 changed files with 104 additions and 116 deletions
@@ -10,7 +10,6 @@ use crate::stripe::http_endpoints::checkout::create::stripe_create_checkout_sess
use crate::stripe::http_endpoints::customer_portal::stripe_create_customer_portal_session_json_handler::stripe_create_customer_portal_session_json_handler;
use crate::stripe::http_endpoints::customer_portal::stripe_create_customer_portal_session_redirect_handler::stripe_create_customer_portal_session_redirect_handler;
use crate::stripe::http_endpoints::webhook::stripe_webhook_handler::stripe_webhook_handler;
use crate::users::http_endpoints::list_active_user_subscriptions_handler::list_active_user_subscriptions_handler;
pub fn add_suggested_stripe_billing_routes<T, B> (app: App<T>) -> App<T>
where
@@ -23,13 +22,9 @@ pub fn add_suggested_stripe_billing_routes<T, B> (app: App<T>) -> App<T>
InitError = (),
>,
{
// NB: The /v1/billing/active_subscriptions route has been moved to
// storyteller-web (billing_fakeyou_routes). Only Stripe routes remain here.
app.service(web::scope("/v1")
.service(web::scope("/billing")
.service(web::resource("/active_subscriptions")
.route(web::get().to(list_active_user_subscriptions_handler))
.route(web::head().to(|| HttpResponse::Ok()))
)
)
.service(web::scope("/stripe")
.service(web::resource("/webhook")
.route(web::post().to(stripe_webhook_handler))
@@ -19,4 +19,3 @@
pub mod default_routes;
pub mod paypal;
pub mod stripe;
pub mod users;
@@ -1,101 +0,0 @@
use actix_http::StatusCode;
use actix_web::{web, HttpRequest, HttpResponse, ResponseError};
use component_traits::traits::internal_user_lookup::InternalUserLookup;
use enums::common::payments_namespace::PaymentsNamespace;
use http_server_common::response::serialize_as_json_error::serialize_as_json_error;
use log::error;
use utoipa::ToSchema;
// =============== Success Response ===============
#[derive(Serialize, ToSchema)]
pub struct ListActiveUserSubscriptionsResponse {
pub success: bool,
pub maybe_loyalty_program: Option<String>,
pub active_subscriptions: Vec<SubscriptionProductKey>,
}
#[derive(Serialize, ToSchema)]
pub struct SubscriptionProductKey {
/// This should always be "fakeyou".
pub namespace: PaymentsNamespace,
/// Possible values: fakeyou_plus, fakeyou_pro, fakeyou_elite, etc.
pub product_slug: String,
}
// =============== Error Response ===============
#[derive(Debug, Serialize, Eq, PartialEq, Copy, Clone, ToSchema)]
pub enum ListActiveUserSubscriptionsError {
InvalidSession,
ServerError,
}
impl ResponseError for ListActiveUserSubscriptionsError {
fn status_code(&self) -> StatusCode {
match *self {
ListActiveUserSubscriptionsError::InvalidSession => StatusCode::UNAUTHORIZED,
ListActiveUserSubscriptionsError::ServerError => StatusCode::INTERNAL_SERVER_ERROR,
}
}
fn error_response(&self) -> HttpResponse {
serialize_as_json_error(self)
}
}
// NB: Not using derive_more::Display since Clion doesn't understand it.
impl std::fmt::Display for ListActiveUserSubscriptionsError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}
#[utoipa::path(
get,
tag = "Billing",
path = "/v1/billing/active_subscriptions",
responses(
(status = 200, description = "Success response", body = ListActiveUserSubscriptionsResponse),
(status = 500, description = "Server error", body = ListActiveUserSubscriptionsError),
),
)]
pub async fn list_active_user_subscriptions_handler(
http_request: HttpRequest,
internal_user_lookup: web::Data<dyn InternalUserLookup>,
) -> Result<HttpResponse, ListActiveUserSubscriptionsError>
{
let maybe_user_metadata = internal_user_lookup
.lookup_user_from_http_request(&http_request)
.await
.map_err(|err| {
error!("Error looking up user: {:?}", err);
ListActiveUserSubscriptionsError::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(ListActiveUserSubscriptionsError::InvalidSession),
Some(user_metadata) => user_metadata,
};
let response = ListActiveUserSubscriptionsResponse {
success: true,
maybe_loyalty_program: user_metadata.maybe_loyalty_program_key,
active_subscriptions: user_metadata.existing_subscription_keys
.into_iter()
.map(|sub| SubscriptionProductKey {
namespace: sub.internal_subscription_namespace,
product_slug: sub.internal_subscription_product_slug,
})
.collect(),
};
let body = serde_json::to_string(&response)
.map_err(|_e| ListActiveUserSubscriptionsError::ServerError)?;
Ok(HttpResponse::Ok()
.content_type("application/json")
.body(body))
}
@@ -1 +0,0 @@
pub mod list_active_user_subscriptions_handler;
@@ -1 +0,0 @@
pub mod http_endpoints;
@@ -154,7 +154,7 @@ use artcraft_api_defs::users::edit_email::{EditEmailRequest, EditEmailResponse};
use artcraft_api_defs::users::edit_username::{EditUsernameRequest, EditUsernameResponse};
use billing_component::stripe::http_endpoints::checkout::create::stripe_create_checkout_session_error::CreateCheckoutSessionError;
use billing_component::stripe::http_endpoints::checkout::create::stripe_create_checkout_session_json_handler::*;
use billing_component::users::http_endpoints::list_active_user_subscriptions_handler::*;
use crate::http_server::endpoints::billing_fakeyou::list_active_user_subscriptions_handler::*;
use crate::http_server::endpoints::service::status_alert_handler::*;
use crate::http_server::endpoints::stats::get_unified_queue_stats_handler::*;
use crate::http_server::endpoints::studio_gen2::enqueue_studio_gen2_handler::*;
@@ -318,7 +318,7 @@ use crate::http_server::endpoints::media_files::list::list_batch_generated_redux
#[openapi(
paths(
billing_component::stripe::http_endpoints::checkout::create::stripe_create_checkout_session_json_handler::stripe_create_checkout_session_json_handler,
billing_component::users::http_endpoints::list_active_user_subscriptions_handler::list_active_user_subscriptions_handler,
crate::http_server::endpoints::billing_fakeyou::list_active_user_subscriptions_handler::list_active_user_subscriptions_handler,
crate::http_server::deprecated_endpoints::conversion::enqueue_fbx_to_gltf_handler::enqueue_fbx_to_gltf_handler,
crate::http_server::deprecated_endpoints::engine::create_scene_handler::create_scene_handler,
crate::http_server::deprecated_endpoints::workflows::enqueue::enqueue_face_fusion_workflow_handler::enqueue_face_fusion_workflow_handler,
@@ -871,7 +871,6 @@ use crate::http_server::endpoints::media_files::list::list_batch_generated_redux
InferenceJobStatusResponsePayload,
InferenceJobTokenType,
LegacyQueueDetails,
ListActiveUserSubscriptionsError,
ListActiveUserSubscriptionsResponse,
ListAvailableWeightsQuery,
ListAvailableWeightsSuccessResponse,
@@ -0,0 +1,66 @@
use actix_web::web::Json;
use actix_web::{web, HttpRequest};
use component_traits::traits::internal_user_lookup::InternalUserLookup;
use enums::common::payments_namespace::PaymentsNamespace;
use log::warn;
use utoipa::ToSchema;
use crate::http_server::common_responses::advanced_common_web_error::AdvancedCommonWebError;
// =============== Response ===============
#[derive(Serialize, ToSchema)]
pub struct ListActiveUserSubscriptionsResponse {
pub success: bool,
pub maybe_loyalty_program: Option<String>,
pub active_subscriptions: Vec<SubscriptionProductKey>,
}
#[derive(Serialize, ToSchema)]
pub struct SubscriptionProductKey {
/// This should always be "fakeyou".
pub namespace: PaymentsNamespace,
/// Possible values: fakeyou_plus, fakeyou_pro, fakeyou_elite, etc.
pub product_slug: String,
}
#[utoipa::path(
get,
tag = "Billing",
path = "/v1/billing/active_subscriptions",
responses(
(status = 200, description = "Success response", body = ListActiveUserSubscriptionsResponse),
(status = 401, description = "Not authorized"),
(status = 500, description = "Server error"),
),
)]
pub async fn list_active_user_subscriptions_handler(
http_request: HttpRequest,
internal_user_lookup: web::Data<dyn InternalUserLookup>,
) -> Result<Json<ListActiveUserSubscriptionsResponse>, AdvancedCommonWebError> {
let maybe_user_metadata = internal_user_lookup
.lookup_user_from_http_request(&http_request)
.await
.map_err(|err| {
warn!("Error looking up user: {:?}", err);
AdvancedCommonWebError::from_error
})?;
let user_metadata = match maybe_user_metadata {
None => return Err(AdvancedCommonWebError::NotAuthorized),
Some(user_metadata) => user_metadata,
};
Ok(Json(ListActiveUserSubscriptionsResponse {
success: true,
maybe_loyalty_program: user_metadata.maybe_loyalty_program_key,
active_subscriptions: user_metadata.existing_subscription_keys
.into_iter()
.map(|sub| SubscriptionProductKey {
namespace: sub.internal_subscription_namespace,
product_slug: sub.internal_subscription_product_slug,
})
.collect(),
}))
}
@@ -0,0 +1 @@
pub mod list_active_user_subscriptions_handler;
@@ -1,6 +1,7 @@
pub mod analytics;
pub mod app_state;
pub mod beta_keys;
pub mod billing_fakeyou;
pub mod characters;
pub mod comments;
pub mod credits;
@@ -44,6 +44,7 @@ mod tests {
reference_image_media_tokens: None,
reference_video_media_tokens: None,
reference_audio_media_tokens: None,
reference_character_tokens: None,
resolution,
aspect_ratio,
quality: None,
@@ -1,4 +1,5 @@
use crate::http_server::routes::application_routes::analytics_routes::add_analytics_routes;
use crate::http_server::routes::application_routes::billing_fakeyou_routes::add_billing_fakeyou_routes;
use crate::http_server::routes::application_routes::character_routes::add_character_routes;
use crate::http_server::routes::application_routes::comments_routes::add_comments_routes;
use crate::http_server::routes::application_routes::credits_routes::add_credits_routes;
@@ -74,8 +75,9 @@ where
app = add_stripe_artcraft_routes(app); // /v1/stripe_artcraft/...
app = add_subscription_routes(app); // /v1/subscriptions/...
// FakeYou Billing component - still necessary for FakeYou monetization
app = add_suggested_stripe_billing_routes(app); // /stripe, billing, webhooks, etc.
// FakeYou Billing
app = add_billing_fakeyou_routes(app); // /v1/billing/active_subscriptions
app = add_suggested_stripe_billing_routes(app); // /stripe, webhooks, etc.
app
}
@@ -0,0 +1,26 @@
use crate::http_server::endpoints::billing_fakeyou::list_active_user_subscriptions_handler::list_active_user_subscriptions_handler;
use actix_http::body::MessageBody;
use actix_service::ServiceFactory;
use actix_web::dev::{ServiceRequest, ServiceResponse};
use actix_web::{web, App, Error, HttpResponse};
pub fn add_billing_fakeyou_routes<T, B>(app: App<T>) -> App<T>
where
B: MessageBody,
T: ServiceFactory<
ServiceRequest,
Config = (),
Response = ServiceResponse<B>,
Error = Error,
InitError = (),
>,
{
app.service(web::scope("/v1")
.service(web::scope("/billing")
.service(web::resource("/active_subscriptions")
.route(web::get().to(list_active_user_subscriptions_handler))
.route(web::head().to(|| HttpResponse::Ok()))
)
)
)
}
@@ -1,4 +1,5 @@
mod analytics_routes;
mod billing_fakeyou_routes;
mod character_routes;
mod comments_routes;
mod credits_routes;