diff --git a/crates/api_models/src/subscription.rs b/crates/api_models/src/subscription.rs index 37c1ea2bc4..08f6781bcd 100644 --- a/crates/api_models/src/subscription.rs +++ b/crates/api_models/src/subscription.rs @@ -1,7 +1,9 @@ use common_enums::connector_enums::InvoiceStatus; use common_types::payments::CustomerAcceptance; use common_utils::{ + errors::ValidationError, events::ApiEventMetric, + fp_utils, id_type::{ CustomerId, InvoiceId, MerchantConnectorAccountId, MerchantId, PaymentId, ProfileId, SubscriptionId, @@ -238,6 +240,22 @@ pub struct ConfirmSubscriptionPaymentDetails { pub payment_token: Option, } +impl ConfirmSubscriptionPaymentDetails { + pub fn validate(&self) -> Result<(), error_stack::Report> { + fp_utils::when( + self.payment_method_data.is_none() && self.payment_token.is_none(), + || { + Err(ValidationError::MissingRequiredField { + field_name: String::from( + "either of payment_method_data or payment_token must be present", + ), + } + .into()) + }, + ) + } +} + #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)] pub struct CreateSubscriptionPaymentDetails { /// The url to which user must be redirected to after completion of the purchase @@ -265,6 +283,22 @@ pub struct PaymentDetails { pub payment_token: Option, } +impl PaymentDetails { + pub fn validate(&self) -> Result<(), error_stack::Report> { + fp_utils::when( + self.payment_method_data.is_none() && self.payment_token.is_none(), + || { + Err(ValidationError::MissingRequiredField { + field_name: String::from( + "either of payment_method_data or payment_token must be present", + ), + } + .into()) + }, + ) + } +} + // Creating new type for PaymentRequest API call as usage of api_models::PaymentsRequest will result in invalid payment request during serialization // Eg: Amount will be serialized as { amount: {Value: 100 }} #[derive(Debug, Clone, serde::Serialize, ToSchema)] @@ -376,6 +410,11 @@ impl ConfirmSubscriptionRequest { .and_then(|data| data.billing.clone()) .or(self.payment_details.billing.clone()) } + + // Perform validation on ConfirmSubscriptionRequest fields + pub fn validate(&self) -> Result<(), error_stack::Report> { + self.payment_details.validate() + } } impl ApiEventMetric for ConfirmSubscriptionRequest {} @@ -415,6 +454,10 @@ impl CreateAndConfirmSubscriptionRequest { .and_then(|data| data.billing.clone()) .or(self.billing.clone()) } + + pub fn validate(&self) -> Result<(), error_stack::Report> { + self.payment_details.validate() + } } impl ApiEventMetric for CreateAndConfirmSubscriptionRequest {} diff --git a/crates/subscriptions/src/core.rs b/crates/subscriptions/src/core.rs index c2b79869a5..dd2c7df85d 100644 --- a/crates/subscriptions/src/core.rs +++ b/crates/subscriptions/src/core.rs @@ -178,6 +178,12 @@ pub async fn create_and_confirm_subscription( profile_id: common_utils::id_type::ProfileId, request: subscription_types::CreateAndConfirmSubscriptionRequest, ) -> RouterResponse { + request + .validate() + .map_err(|message| errors::ApiErrorResponse::InvalidRequestData { + message: message.to_string(), + })?; + let subscription_id = common_utils::id_type::SubscriptionId::generate(); let profile = SubscriptionHandler::find_business_profile(&state, &merchant_context, &profile_id) @@ -310,6 +316,12 @@ pub async fn confirm_subscription( request: subscription_types::ConfirmSubscriptionRequest, subscription_id: common_utils::id_type::SubscriptionId, ) -> RouterResponse { + // Validate request + request + .validate() + .map_err(|message| errors::ApiErrorResponse::InvalidRequestData { + message: message.to_string(), + })?; // Find the subscription from database let profile = SubscriptionHandler::find_business_profile(&state, &merchant_context, &profile_id)