Bug: refactor(gsm): introduce `feature` and `feature_data` to gsm



Refactor gsm to add `feature` and `feature_data` columns so that it case used to introduce new features
diff --git a/crates/api_models/src/gsm.rs b/crates/api_models/src/gsm.rs
index 7ffc9c69413..1b2800d11ea 100644
--- a/crates/api_models/src/gsm.rs
+++ b/crates/api_models/src/gsm.rs
@@ -1,12 +1,12 @@
-use common_enums::ErrorCategory;
 use utoipa::ToSchema;
 
-use crate::enums::Connector;
+use crate::enums as api_enums;
 
 #[derive(Debug, serde::Deserialize, serde::Serialize, ToSchema)]
 pub struct GsmCreateRequest {
     /// The connector through which payment has gone through
-    pub connector: Connector,
+    #[schema(value_type = Connector)]
+    pub connector: api_enums::Connector,
     /// The flow in which the code and message occurred for a connector
     pub flow: String,
     /// The sub_flow in which the code and message occurred  for a connector
@@ -20,23 +20,41 @@ pub struct GsmCreateRequest {
     /// optional error provided by the router
     pub router_error: Option<String>,
     /// decision to be taken for auto retries flow
-    pub decision: GsmDecision,
+    /// **Deprecated**: This field is now included as part of `feature_data` under the `Retry` variant.
+    #[schema(deprecated)]
+    #[schema(value_type = GsmDecision)]
+    pub decision: api_enums::GsmDecision,
     /// indicates if step_up retry is possible
+    /// **Deprecated**: This field is now included as part of `feature_data` under the `Retry` variant.
+    #[schema(deprecated)]
     pub step_up_possible: bool,
     /// error code unified across the connectors
     pub unified_code: Option<String>,
     /// error message unified across the connectors
     pub unified_message: Option<String>,
     /// category in which error belongs to
-    pub error_category: Option<ErrorCategory>,
+    #[schema(value_type = Option<ErrorCategory>)]
+    pub error_category: Option<api_enums::ErrorCategory>,
     /// indicates if retry with pan is possible
+    /// **Deprecated**: This field is now included as part of `feature_data` under the `Retry` variant.
+    #[schema(deprecated)]
     pub clear_pan_possible: bool,
+    /// Indicates the GSM feature associated with the request,
+    /// such as retry mechanisms or other specific functionalities provided by the system.
+    #[schema(value_type = Option<GsmFeature>)]
+    pub feature: Option<api_enums::GsmFeature>,
+    /// Contains the data relevant to the specified GSM feature, if applicable.
+    /// For example, if the `feature` is `Retry`, this will include configuration
+    /// details specific to the retry behavior.
+    #[schema(value_type = Option<GsmFeatureData>)]
+    pub feature_data: Option<common_types::domain::GsmFeatureData>,
 }
 
 #[derive(Debug, serde::Deserialize, serde::Serialize, ToSchema)]
 pub struct GsmRetrieveRequest {
     /// The connector through which payment has gone through
-    pub connector: Connector,
+    #[schema(value_type = Connector)]
+    pub connector: api_enums::Connector,
     /// The flow in which the code and message occurred for a connector
     pub flow: String,
     /// The sub_flow in which the code and message occurred  for a connector
@@ -47,28 +65,6 @@ pub struct GsmRetrieveRequest {
     pub message: String,
 }
 
-#[derive(
-    Default,
-    Clone,
-    Copy,
-    Debug,
-    strum::Display,
-    PartialEq,
-    Eq,
-    serde::Serialize,
-    serde::Deserialize,
-    strum::EnumString,
-    ToSchema,
-)]
-#[serde(rename_all = "snake_case")]
-#[strum(serialize_all = "snake_case")]
-pub enum GsmDecision {
-    Retry,
-    Requeue,
-    #[default]
-    DoDefault,
-}
-
 #[derive(Debug, serde::Deserialize, serde::Serialize, ToSchema)]
 pub struct GsmUpdateRequest {
     /// The connector through which payment has gone through
@@ -86,17 +82,34 @@ pub struct GsmUpdateRequest {
     /// optional error provided by the router
     pub router_error: Option<String>,
     /// decision to be taken for auto retries flow
-    pub decision: Option<GsmDecision>,
+    /// **Deprecated**: This field is now included as part of `feature_data` under the `Retry` variant.
+    #[schema(deprecated)]
+    #[schema(value_type = Option<GsmDecision>)]
+    pub decision: Option<api_enums::GsmDecision>,
     /// indicates if step_up retry is possible
+    /// **Deprecated**: This field is now included as part of `feature_data` under the `Retry` variant.
+    #[schema(deprecated)]
     pub step_up_possible: Option<bool>,
     /// error code unified across the connectors
     pub unified_code: Option<String>,
     /// error message unified across the connectors
     pub unified_message: Option<String>,
     /// category in which error belongs to
-    pub error_category: Option<ErrorCategory>,
+    #[schema(value_type = Option<ErrorCategory>)]
+    pub error_category: Option<api_enums::ErrorCategory>,
     /// indicates if retry with pan is possible
+    /// **Deprecated**: This field is now included as part of `feature_data` under the `Retry` variant.
+    #[schema(deprecated)]
     pub clear_pan_possible: Option<bool>,
+    /// Indicates the GSM feature associated with the request,
+    /// such as retry mechanisms or other specific functionalities provided by the system.
+    #[schema(value_type = Option<GsmFeature>)]
+    pub feature: Option<api_enums::GsmFeature>,
+    /// Contains the data relevant to the specified GSM feature, if applicable.
+    /// For example, if the `feature` is `Retry`, this will include configuration
+    /// details specific to the retry behavior.
+    #[schema(value_type = Option<GsmFeatureData>)]
+    pub feature_data: Option<common_types::domain::GsmFeatureData>,
 }
 
 #[derive(Debug, serde::Deserialize, serde::Serialize, ToSchema)]
@@ -143,15 +156,32 @@ pub struct GsmResponse {
     /// optional error provided by the router
     pub router_error: Option<String>,
     /// decision to be taken for auto retries flow
-    pub decision: String,
+    /// **Deprecated**: This field is now included as part of `feature_data` under the `Retry` variant.
+    #[schema(deprecated)]
+    #[schema(value_type = GsmDecision)]
+    pub decision: api_enums::GsmDecision,
     /// indicates if step_up retry is possible
+    /// **Deprecated**: This field is now included as part of `feature_data` under the `Retry` variant.
+    #[schema(deprecated)]
     pub step_up_possible: bool,
     /// error code unified across the connectors
     pub unified_code: Option<String>,
     /// error message unified across the connectors
     pub unified_message: Option<String>,
     /// category in which error belongs to
-    pub error_category: Option<ErrorCategory>,
+    #[schema(value_type = Option<ErrorCategory>)]
+    pub error_category: Option<api_enums::ErrorCategory>,
     /// indicates if retry with pan is possible
+    /// **Deprecated**: This field is now included as part of `feature_data` under the `Retry` variant.
+    #[schema(deprecated)]
     pub clear_pan_possible: bool,
+    /// Indicates the GSM feature associated with the request,
+    /// such as retry mechanisms or other specific functionalities provided by the system.
+    #[schema(value_type = GsmFeature)]
+    pub feature: api_enums::GsmFeature,
+    /// Contains the data relevant to the specified GSM feature, if applicable.
+    /// For example, if the `feature` is `Retry`, this will include configuration
+    /// details specific to the retry behavior.
+    #[schema(value_type = GsmFeatureData)]
+    pub feature_data: Option<common_types::domain::GsmFeatureData>,
 }
diff --git a/crates/common_enums/src/enums.rs b/crates/common_enums/src/enums.rs
index 4daa373c016..6b35bbaf9ce 100644
--- a/crates/common_enums/src/enums.rs
+++ b/crates/common_enums/src/enums.rs
@@ -267,6 +267,46 @@ pub enum RevenueRecoveryAlgorithmType {
     Cascading,
 }
 
+#[derive(
+    Default,
+    Clone,
+    Copy,
+    Debug,
+    strum::Display,
+    PartialEq,
+    Eq,
+    serde::Serialize,
+    serde::Deserialize,
+    strum::EnumString,
+    ToSchema,
+)]
+#[serde(rename_all = "snake_case")]
+#[strum(serialize_all = "snake_case")]
+pub enum GsmDecision {
+    Retry,
+    #[default]
+    DoDefault,
+}
+
+#[derive(
+    Clone,
+    Copy,
+    Debug,
+    strum::Display,
+    PartialEq,
+    Eq,
+    serde::Serialize,
+    serde::Deserialize,
+    strum::EnumString,
+    ToSchema,
+)]
+#[serde(rename_all = "snake_case")]
+#[strum(serialize_all = "snake_case")]
+#[router_derive::diesel_enum(storage_type = "text")]
+pub enum GsmFeature {
+    Retry,
+}
+
 /// Specifies the type of cardholder authentication to be applied for a payment.
 ///
 /// - `ThreeDs`: Requests 3D Secure (3DS) authentication. If the card is enrolled, 3DS authentication will be activated, potentially shifting chargeback liability to the issuer.
diff --git a/crates/common_types/src/domain.rs b/crates/common_types/src/domain.rs
index e800cb9cb61..3836aaabe75 100644
--- a/crates/common_types/src/domain.rs
+++ b/crates/common_types/src/domain.rs
@@ -113,3 +113,71 @@ pub struct ConnectorResponseData {
     /// Stringified connector raw response body
     pub raw_connector_response: Option<Secret<String>>,
 }
+
+#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, AsExpression, ToSchema)]
+#[diesel(sql_type = Jsonb)]
+#[serde(rename_all = "snake_case")]
+/// Contains the data relevant to the specified GSM feature, if applicable.
+/// For example, if the `feature` is `Retry`, this will include configuration
+/// details specific to the retry behavior.
+pub enum GsmFeatureData {
+    /// Represents the data associated with a retry feature in GSM.
+    Retry(RetryFeatureData),
+}
+
+/// Represents the data associated with a retry feature in GSM.
+#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, AsExpression, ToSchema)]
+#[diesel(sql_type = Jsonb)]
+pub struct RetryFeatureData {
+    /// indicates if step_up retry is possible
+    pub step_up_possible: bool,
+    /// indicates if retry with pan is possible
+    pub clear_pan_possible: bool,
+    /// indicates if retry with alternate network possible
+    pub alternate_network_possible: bool,
+    /// decision to be taken for auto retries flow
+    #[schema(value_type = GsmDecision)]
+    pub decision: common_enums::GsmDecision,
+}
+
+impl_to_sql_from_sql_json!(GsmFeatureData);
+impl_to_sql_from_sql_json!(RetryFeatureData);
+
+impl GsmFeatureData {
+    /// Retrieves the retry feature data if it exists.
+    pub fn get_retry_feature_data(&self) -> Option<RetryFeatureData> {
+        match self {
+            Self::Retry(data) => Some(data.clone()),
+        }
+    }
+
+    /// Retrieves the decision from the retry feature data.
+    pub fn get_decision(&self) -> common_enums::GsmDecision {
+        match self {
+            Self::Retry(data) => data.decision,
+        }
+    }
+}
+
+/// Implementation of methods for `RetryFeatureData`
+impl RetryFeatureData {
+    /// Checks if step-up retry is possible.
+    pub fn is_step_up_possible(&self) -> bool {
+        self.step_up_possible
+    }
+
+    /// Checks if retry with PAN is possible.
+    pub fn is_clear_pan_possible(&self) -> bool {
+        self.clear_pan_possible
+    }
+
+    /// Checks if retry with alternate network is possible.
+    pub fn is_alternate_network_possible(&self) -> bool {
+        self.alternate_network_possible
+    }
+
+    /// Retrieves the decision to be taken for auto retries flow.
+    pub fn get_decision(&self) -> common_enums::GsmDecision {
+        self.decision
+    }
+}
diff --git a/crates/diesel_models/src/gsm.rs b/crates/diesel_models/src/gsm.rs
index 46583a5b22c..b75b0e6ce86 100644
--- a/crates/diesel_models/src/gsm.rs
+++ b/crates/diesel_models/src/gsm.rs
@@ -1,5 +1,4 @@
 //! Gateway status mapping
-
 use common_enums::ErrorCategory;
 use common_utils::{
     custom_serde,
@@ -15,8 +14,6 @@ use crate::schema::gateway_status_map;
     Debug,
     Eq,
     PartialEq,
-    Ord,
-    PartialOrd,
     router_derive::DebugAsDisplay,
     Identifiable,
     Queryable,
@@ -42,6 +39,8 @@ pub struct GatewayStatusMap {
     pub unified_message: Option<String>,
     pub error_category: Option<ErrorCategory>,
     pub clear_pan_possible: bool,
+    pub feature_data: Option<common_types::domain::GsmFeatureData>,
+    pub feature: Option<common_enums::GsmFeature>,
 }
 
 #[derive(Clone, Debug, Eq, PartialEq, Insertable)]
@@ -60,6 +59,8 @@ pub struct GatewayStatusMappingNew {
     pub unified_message: Option<String>,
     pub error_category: Option<ErrorCategory>,
     pub clear_pan_possible: bool,
+    pub feature_data: Option<common_types::domain::GsmFeatureData>,
+    pub feature: Option<common_enums::GsmFeature>,
 }
 
 #[derive(
@@ -81,6 +82,8 @@ pub struct GatewayStatusMapperUpdateInternal {
     pub error_category: Option<ErrorCategory>,
     pub last_modified: PrimitiveDateTime,
     pub clear_pan_possible: Option<bool>,
+    pub feature_data: Option<common_types::domain::GsmFeatureData>,
+    pub feature: Option<common_enums::GsmFeature>,
 }
 
 #[derive(Debug)]
@@ -93,6 +96,8 @@ pub struct GatewayStatusMappingUpdate {
     pub unified_message: Option<String>,
     pub error_category: Option<ErrorCategory>,
     pub clear_pan_possible: Option<bool>,
+    pub feature_data: Option<common_types::domain::GsmFeatureData>,
+    pub feature: Option<common_enums::GsmFeature>,
 }
 
 impl From<GatewayStatusMappingUpdate> for GatewayStatusMapperUpdateInternal {
@@ -106,6 +111,8 @@ impl From<GatewayStatusMappingUpdate> for GatewayStatusMapperUpdateInternal {
             unified_message,
             error_category,
             clear_pan_possible,
+            feature_data,
+            feature,
         } = value;
         Self {
             status,
@@ -122,6 +129,8 @@ impl From<GatewayStatusMappingUpdate> for GatewayStatusMapperUpdateInternal {
             code: None,
             message: None,
             clear_pan_possible,
+            feature_data,
+            feature,
         }
     }
 }
diff --git a/crates/diesel_models/src/schema.rs b/crates/diesel_models/src/schema.rs
index 6788f478274..c141f765536 100644
--- a/crates/diesel_models/src/schema.rs
+++ b/crates/diesel_models/src/schema.rs
@@ -595,6 +595,9 @@ diesel::table! {
         #[max_length = 64]
         error_category -> Nullable<Varchar>,
         clear_pan_possible -> Bool,
+        feature_data -> Nullable<Jsonb>,
+        #[max_length = 64]
+        feature -> Nullable<Varchar>,
     }
 }
 
diff --git a/crates/diesel_models/src/schema_v2.rs b/crates/diesel_models/src/schema_v2.rs
index c5579b6c67b..e434e43b2c6 100644
--- a/crates/diesel_models/src/schema_v2.rs
+++ b/crates/diesel_models/src/schema_v2.rs
@@ -607,6 +607,9 @@ diesel::table! {
         #[max_length = 64]
         error_category -> Nullable<Varchar>,
         clear_pan_possible -> Bool,
+        feature_data -> Nullable<Jsonb>,
+        #[max_length = 64]
+        feature -> Nullable<Varchar>,
     }
 }
 
diff --git a/crates/hyperswitch_domain_models/src/gsm.rs b/crates/hyperswitch_domain_models/src/gsm.rs
new file mode 100644
index 00000000000..59319c89e5d
--- /dev/null
+++ b/crates/hyperswitch_domain_models/src/gsm.rs
@@ -0,0 +1,129 @@
+use common_utils::{errors::ValidationError, ext_traits::StringExt};
+use serde::{self, Deserialize, Serialize};
+
+#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
+pub struct GatewayStatusMap {
+    pub connector: String,
+    pub flow: String,
+    pub sub_flow: String,
+    pub code: String,
+    pub message: String,
+    pub status: String,
+    pub router_error: Option<String>,
+    pub unified_code: Option<String>,
+    pub unified_message: Option<String>,
+    pub error_category: Option<common_enums::ErrorCategory>,
+    pub feature_data: common_types::domain::GsmFeatureData,
+    pub feature: common_enums::GsmFeature,
+}
+
+#[derive(Debug, Clone, Deserialize, Serialize)]
+pub struct GatewayStatusMappingUpdate {
+    pub status: Option<String>,
+    pub router_error: Option<Option<String>>,
+    pub decision: Option<common_enums::GsmDecision>,
+    pub step_up_possible: Option<bool>,
+    pub unified_code: Option<String>,
+    pub unified_message: Option<String>,
+    pub error_category: Option<common_enums::ErrorCategory>,
+    pub clear_pan_possible: Option<bool>,
+    pub feature_data: Option<common_types::domain::GsmFeatureData>,
+    pub feature: Option<common_enums::GsmFeature>,
+}
+
+impl TryFrom<GatewayStatusMap> for diesel_models::gsm::GatewayStatusMappingNew {
+    type Error = error_stack::Report<ValidationError>;
+
+    fn try_from(value: GatewayStatusMap) -> Result<Self, Self::Error> {
+        Ok(Self {
+            connector: value.connector.to_string(),
+            flow: value.flow,
+            sub_flow: value.sub_flow,
+            code: value.code,
+            message: value.message,
+            status: value.status.clone(),
+            router_error: value.router_error,
+            decision: value.feature_data.get_decision().to_string(),
+            step_up_possible: value
+                .feature_data
+                .get_retry_feature_data()
+                .map(|retry_feature_data| retry_feature_data.is_step_up_possible())
+                .unwrap_or(false),
+            unified_code: value.unified_code,
+            unified_message: value.unified_message,
+            error_category: value.error_category,
+            clear_pan_possible: value
+                .feature_data
+                .get_retry_feature_data()
+                .map(|retry_feature_data| retry_feature_data.is_clear_pan_possible())
+                .unwrap_or(false),
+            feature_data: Some(value.feature_data),
+            feature: Some(value.feature),
+        })
+    }
+}
+
+impl TryFrom<GatewayStatusMappingUpdate> for diesel_models::gsm::GatewayStatusMappingUpdate {
+    type Error = error_stack::Report<ValidationError>;
+
+    fn try_from(value: GatewayStatusMappingUpdate) -> Result<Self, Self::Error> {
+        Ok(Self {
+            status: value.status,
+            router_error: value.router_error,
+            decision: value.decision.map(|gsm_decision| gsm_decision.to_string()),
+            step_up_possible: value.step_up_possible,
+            unified_code: value.unified_code,
+            unified_message: value.unified_message,
+            error_category: value.error_category,
+            clear_pan_possible: value.clear_pan_possible,
+            feature_data: value.feature_data,
+            feature: value.feature,
+        })
+    }
+}
+
+impl TryFrom<diesel_models::gsm::GatewayStatusMap> for GatewayStatusMap {
+    type Error = ValidationError;
+
+    fn try_from(item: diesel_models::gsm::GatewayStatusMap) -> Result<Self, Self::Error> {
+        let decision =
+            StringExt::<common_enums::GsmDecision>::parse_enum(item.decision, "GsmDecision")
+                .map_err(|_| ValidationError::InvalidValue {
+                    message: "Failed to parse GsmDecision".to_string(),
+                })?;
+        let db_feature_data = item.feature_data;
+
+        // The only case where `FeatureData` can be null is for legacy records
+        // (i.e., records created before `FeatureData` and related features were introduced).
+        // At that time, the only supported feature was `Retry`, so it's safe to default to it.
+        let feature_data = match db_feature_data {
+            Some(common_types::domain::GsmFeatureData::Retry(data)) => {
+                common_types::domain::GsmFeatureData::Retry(data)
+            }
+            None => common_types::domain::GsmFeatureData::Retry(
+                common_types::domain::RetryFeatureData {
+                    step_up_possible: item.step_up_possible,
+                    clear_pan_possible: item.clear_pan_possible,
+                    alternate_network_possible: false,
+                    decision,
+                },
+            ),
+        };
+
+        let feature = item.feature.unwrap_or(common_enums::GsmFeature::Retry);
+        Ok(Self {
+            connector: item.connector,
+            flow: item.flow,
+            sub_flow: item.sub_flow,
+            code: item.code,
+            message: item.message,
+            status: item.status,
+            router_error: item.router_error,
+            unified_code: item.unified_code,
+            unified_message: item.unified_message,
+            error_category: item.error_category,
+            feature_data,
+            feature,
+        })
+    }
+}
diff --git a/crates/hyperswitch_domain_models/src/lib.rs b/crates/hyperswitch_domain_models/src/lib.rs
index c7df1e3749b..30d0913a716 100644
--- a/crates/hyperswitch_domain_models/src/lib.rs
+++ b/crates/hyperswitch_domain_models/src/lib.rs
@@ -14,6 +14,7 @@ pub mod customer;
 pub mod disputes;
 pub mod errors;
 pub mod ext_traits;
+pub mod gsm;
 pub mod mandates;
 pub mod merchant_account;
 pub mod merchant_connector_account;
diff --git a/crates/openapi/src/openapi.rs b/crates/openapi/src/openapi.rs
index efa92af978a..bb2937a653e 100644
--- a/crates/openapi/src/openapi.rs
+++ b/crates/openapi/src/openapi.rs
@@ -400,7 +400,10 @@ Never share your secret api keys. Keep them guarded and secure.
         api_models::gsm::GsmDeleteRequest,
         api_models::gsm::GsmDeleteResponse,
         api_models::gsm::GsmResponse,
-        api_models::gsm::GsmDecision,
+        api_models::enums::GsmDecision,
+        api_models::enums::GsmFeature,
+        common_types::domain::GsmFeatureData,
+        common_types::domain::RetryFeatureData,
         api_models::payments::AddressDetails,
         api_models::payments::BankDebitData,
         api_models::payments::AliPayQr,
diff --git a/crates/openapi/src/openapi_v2.rs b/crates/openapi/src/openapi_v2.rs
index 81e8f10dcdc..23f0fa2d0d2 100644
--- a/crates/openapi/src/openapi_v2.rs
+++ b/crates/openapi/src/openapi_v2.rs
@@ -352,7 +352,10 @@ Never share your secret api keys. Keep them guarded and secure.
         api_models::gsm::GsmDeleteRequest,
         api_models::gsm::GsmDeleteResponse,
         api_models::gsm::GsmResponse,
-        api_models::gsm::GsmDecision,
+        api_models::enums::GsmDecision,
+        api_models::enums::GsmFeature,
+        common_types::domain::GsmFeatureData,
+        common_types::domain::RetryFeatureData,
         api_models::payments::NullObject,
         api_models::payments::AddressDetails,
         api_models::payments::BankDebitData,
diff --git a/crates/router/src/core/gsm.rs b/crates/router/src/core/gsm.rs
index 6c544791a18..4bd93a4e0e0 100644
--- a/crates/router/src/core/gsm.rs
+++ b/crates/router/src/core/gsm.rs
@@ -1,16 +1,12 @@
 use api_models::gsm as gsm_api_types;
-use diesel_models::gsm as storage;
 use error_stack::ResultExt;
 use router_env::{instrument, tracing};
 
 use crate::{
-    core::{
-        errors,
-        errors::{RouterResponse, StorageErrorExt},
-    },
+    core::errors::{self, RouterResponse, StorageErrorExt},
     db::gsm::GsmInterface,
     services,
-    types::transformers::ForeignInto,
+    types::transformers::{ForeignFrom, ForeignInto},
     SessionState,
 };
 
@@ -55,6 +51,24 @@ pub async fn update_gsm_rule(
     gsm_request: gsm_api_types::GsmUpdateRequest,
 ) -> RouterResponse<gsm_api_types::GsmResponse> {
     let db = state.store.as_ref();
+    let connector = gsm_request.connector.clone();
+    let flow = gsm_request.flow.clone();
+    let code = gsm_request.code.clone();
+    let sub_flow = gsm_request.sub_flow.clone();
+    let message = gsm_request.message.clone();
+
+    let gsm_db_record =
+        GsmInterface::find_gsm_rule(db, connector.to_string(), flow, sub_flow, code, message)
+            .await
+            .to_not_found_response(errors::ApiErrorResponse::GenericNotFoundError {
+                message: "GSM with given key does not exist in our records".to_string(),
+            })?;
+
+    let inferred_feature_info = <(
+        common_enums::GsmFeature,
+        common_types::domain::GsmFeatureData,
+    )>::foreign_from((&gsm_request, gsm_db_record));
+
     let gsm_api_types::GsmUpdateRequest {
         connector,
         flow,
@@ -69,6 +83,8 @@ pub async fn update_gsm_rule(
         unified_message,
         error_category,
         clear_pan_possible,
+        feature,
+        feature_data,
     } = gsm_request;
     GsmInterface::update_gsm_rule(
         db,
@@ -77,15 +93,25 @@ pub async fn update_gsm_rule(
         sub_flow,
         code,
         message,
-        storage::GatewayStatusMappingUpdate {
-            decision: decision.map(|d| d.to_string()),
+        hyperswitch_domain_models::gsm::GatewayStatusMappingUpdate {
+            decision,
             status,
             router_error: Some(router_error),
-            step_up_possible,
+            step_up_possible: feature_data
+                .as_ref()
+                .and_then(|feature_data| feature_data.get_retry_feature_data())
+                .map(|retry_feature_data| retry_feature_data.is_step_up_possible())
+                .or(step_up_possible),
             unified_code,
             unified_message,
             error_category,
-            clear_pan_possible,
+            clear_pan_possible: feature_data
+                .as_ref()
+                .and_then(|feature_data| feature_data.get_retry_feature_data())
+                .map(|retry_feature_data| retry_feature_data.is_clear_pan_possible())
+                .or(clear_pan_possible),
+            feature_data: feature_data.or(Some(inferred_feature_info.1)),
+            feature: feature.or(Some(inferred_feature_info.0)),
         },
     )
     .await
diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs
index 458fe4bc5b2..ca8a9708651 100644
--- a/crates/router/src/core/payments/helpers.rs
+++ b/crates/router/src/core/payments/helpers.rs
@@ -6441,7 +6441,7 @@ pub async fn get_gsm_record(
     error_message: Option<String>,
     connector_name: String,
     flow: String,
-) -> Option<storage::gsm::GatewayStatusMap> {
+) -> Option<hyperswitch_domain_models::gsm::GatewayStatusMap> {
     let get_gsm = || async {
         state.store.find_gsm_rule(
                 connector_name.clone(),
diff --git a/crates/router/src/core/payments/retry.rs b/crates/router/src/core/payments/retry.rs
index aafe25aea67..b435bd191ef 100644
--- a/crates/router/src/core/payments/retry.rs
+++ b/crates/router/src/core/payments/retry.rs
@@ -1,8 +1,8 @@
-use std::{str::FromStr, vec::IntoIter};
+use std::vec::IntoIter;
 
 use common_utils::{ext_traits::Encode, types::MinorUnit};
 use diesel_models::enums as storage_enums;
-use error_stack::{report, ResultExt};
+use error_stack::ResultExt;
 use router_env::{
     logger,
     tracing::{self, instrument},
@@ -65,10 +65,10 @@ where
 
     let mut initial_gsm = get_gsm(state, &router_data).await?;
 
-    //Check if step-up to threeDS is possible and merchant has enabled
     let step_up_possible = initial_gsm
-        .clone()
-        .map(|gsm| gsm.step_up_possible)
+        .as_ref()
+        .and_then(|data| data.feature_data.get_retry_feature_data())
+        .map(|data| data.is_step_up_possible())
         .unwrap_or(false);
 
     #[cfg(feature = "v1")]
@@ -124,7 +124,7 @@ where
             };
 
             match get_gsm_decision(gsm) {
-                api_models::gsm::GsmDecision::Retry => {
+                storage_enums::GsmDecision::Retry => {
                     retries = get_retries(
                         state,
                         retries,
@@ -150,11 +150,13 @@ where
                         .map(|pmd| pmd.is_network_token_payment_method_data())
                         .unwrap_or(false);
 
+                    let clear_pan_possible = initial_gsm
+                        .and_then(|data| data.feature_data.get_retry_feature_data())
+                        .map(|data| data.is_clear_pan_possible())
+                        .unwrap_or(false);
+
                     let should_retry_with_pan = is_network_token
-                        && initial_gsm
-                            .as_ref()
-                            .map(|gsm| gsm.clear_pan_possible)
-                            .unwrap_or(false)
+                        && clear_pan_possible
                         && business_profile.is_clear_pan_retries_enabled;
 
                     let (connector, routing_decision) = if should_retry_with_pan {
@@ -195,14 +197,7 @@ where
 
                     retries = retries.map(|i| i - 1);
                 }
-                api_models::gsm::GsmDecision::Requeue => {
-                    Err(report!(errors::ApiErrorResponse::NotImplemented {
-                        message: errors::NotImplementedMessage::Reason(
-                            "Requeue not implemented".to_string(),
-                        ),
-                    }))?
-                }
-                api_models::gsm::GsmDecision::DoDefault => break,
+                storage_enums::GsmDecision::DoDefault => break,
             }
             initial_gsm = None;
         }
@@ -278,7 +273,7 @@ pub async fn get_retries(
 pub async fn get_gsm<F, FData>(
     state: &app::SessionState,
     router_data: &types::RouterData<F, FData, types::PaymentsResponseData>,
-) -> RouterResult<Option<storage::gsm::GatewayStatusMap>> {
+) -> RouterResult<Option<hyperswitch_domain_models::gsm::GatewayStatusMap>> {
     let error_response = router_data.response.as_ref().err();
     let error_code = error_response.map(|err| err.code.to_owned());
     let error_message = error_response.map(|err| err.message.to_owned());
@@ -292,19 +287,11 @@ pub async fn get_gsm<F, FData>(
 
 #[instrument(skip_all)]
 pub fn get_gsm_decision(
-    option_gsm: Option<storage::gsm::GatewayStatusMap>,
-) -> api_models::gsm::GsmDecision {
+    option_gsm: Option<hyperswitch_domain_models::gsm::GatewayStatusMap>,
+) -> storage_enums::GsmDecision {
     let option_gsm_decision = option_gsm
-            .and_then(|gsm| {
-                api_models::gsm::GsmDecision::from_str(gsm.decision.as_str())
-                    .map_err(|err| {
-                        let api_error = report!(err).change_context(errors::ApiErrorResponse::InternalServerError)
-                            .attach_printable("gsm decision parsing failed");
-                        logger::warn!(get_gsm_decision_parse_error=?api_error, "error fetching gsm decision");
-                        api_error
-                    })
-                    .ok()
-            });
+        .as_ref()
+        .map(|gsm| gsm.feature_data.get_decision());
 
     if option_gsm_decision.is_some() {
         metrics::AUTO_RETRY_GSM_MATCH_COUNT.add(1, &[]);
diff --git a/crates/router/src/core/payouts/helpers.rs b/crates/router/src/core/payouts/helpers.rs
index f1f8dad3a05..8a7f87fa60d 100644
--- a/crates/router/src/core/payouts/helpers.rs
+++ b/crates/router/src/core/payouts/helpers.rs
@@ -1060,7 +1060,7 @@ pub async fn get_gsm_record(
     error_message: Option<String>,
     connector_name: Option<String>,
     flow: &str,
-) -> Option<storage::gsm::GatewayStatusMap> {
+) -> Option<hyperswitch_domain_models::gsm::GatewayStatusMap> {
     let connector_name = connector_name.unwrap_or_default();
     let get_gsm = || async {
         state.store.find_gsm_rule(
diff --git a/crates/router/src/core/payouts/retry.rs b/crates/router/src/core/payouts/retry.rs
index 30100a580d4..e4579613605 100644
--- a/crates/router/src/core/payouts/retry.rs
+++ b/crates/router/src/core/payouts/retry.rs
@@ -1,7 +1,7 @@
-use std::{cmp::Ordering, str::FromStr, vec::IntoIter};
+use std::vec::IntoIter;
 
 use common_enums::PayoutRetryType;
-use error_stack::{report, ResultExt};
+use error_stack::ResultExt;
 use router_env::{
     logger,
     tracing::{self, instrument},
@@ -38,7 +38,7 @@ pub async fn do_gsm_multiple_connector_actions(
         let gsm = get_gsm(state, &connector, payout_data).await?;
 
         match get_gsm_decision(gsm) {
-            api_models::gsm::GsmDecision::Retry => {
+            common_enums::GsmDecision::Retry => {
                 retries = get_retries(
                     state,
                     retries,
@@ -71,14 +71,7 @@ pub async fn do_gsm_multiple_connector_actions(
 
                 retries = retries.map(|i| i - 1);
             }
-            api_models::gsm::GsmDecision::Requeue => {
-                Err(report!(errors::ApiErrorResponse::NotImplemented {
-                    message: errors::NotImplementedMessage::Reason(
-                        "Requeue not implemented".to_string(),
-                    ),
-                }))?
-            }
-            api_models::gsm::GsmDecision::DoDefault => break,
+            common_enums::GsmDecision::DoDefault => break,
         }
     }
     Ok(())
@@ -102,13 +95,13 @@ pub async fn do_gsm_single_connector_actions(
         let gsm = get_gsm(state, &original_connector_data, payout_data).await?;
 
         // if the error config is same as previous, we break out of the loop
-        if let Ordering::Equal = gsm.cmp(&previous_gsm) {
+        if gsm == previous_gsm {
             break;
         }
         previous_gsm.clone_from(&gsm);
 
         match get_gsm_decision(gsm) {
-            api_models::gsm::GsmDecision::Retry => {
+            common_enums::GsmDecision::Retry => {
                 retries = get_retries(
                     state,
                     retries,
@@ -133,14 +126,7 @@ pub async fn do_gsm_single_connector_actions(
 
                 retries = retries.map(|i| i - 1);
             }
-            api_models::gsm::GsmDecision::Requeue => {
-                Err(report!(errors::ApiErrorResponse::NotImplemented {
-                    message: errors::NotImplementedMessage::Reason(
-                        "Requeue not implemented".to_string(),
-                    ),
-                }))?
-            }
-            api_models::gsm::GsmDecision::DoDefault => break,
+            common_enums::GsmDecision::DoDefault => break,
         }
     }
     Ok(())
@@ -182,7 +168,7 @@ pub async fn get_gsm(
     state: &app::SessionState,
     original_connector_data: &api::ConnectorData,
     payout_data: &PayoutData,
-) -> RouterResult<Option<storage::gsm::GatewayStatusMap>> {
+) -> RouterResult<Option<hyperswitch_domain_models::gsm::GatewayStatusMap>> {
     let error_code = payout_data.payout_attempt.error_code.to_owned();
     let error_message = payout_data.payout_attempt.error_message.to_owned();
     let connector_name = Some(original_connector_data.connector_name.to_string());
@@ -199,19 +185,9 @@ pub async fn get_gsm(
 
 #[instrument(skip_all)]
 pub fn get_gsm_decision(
-    option_gsm: Option<storage::gsm::GatewayStatusMap>,
-) -> api_models::gsm::GsmDecision {
-    let option_gsm_decision = option_gsm
-            .and_then(|gsm| {
-                api_models::gsm::GsmDecision::from_str(gsm.decision.as_str())
-                    .map_err(|err| {
-                        let api_error = report!(err).change_context(errors::ApiErrorResponse::InternalServerError)
-                            .attach_printable("gsm decision parsing failed");
-                        logger::warn!(get_gsm_decision_parse_error=?api_error, "error fetching gsm decision");
-                        api_error
-                    })
-                    .ok()
-            });
+    option_gsm: Option<hyperswitch_domain_models::gsm::GatewayStatusMap>,
+) -> common_enums::GsmDecision {
+    let option_gsm_decision = option_gsm.map(|gsm| gsm.feature_data.get_decision());
 
     if option_gsm_decision.is_some() {
         metrics::AUTO_PAYOUT_RETRY_GSM_MATCH_COUNT.add(1, &[]);
diff --git a/crates/router/src/db/gsm.rs b/crates/router/src/db/gsm.rs
index 1aa96e14c18..eb88eecd4c4 100644
--- a/crates/router/src/db/gsm.rs
+++ b/crates/router/src/db/gsm.rs
@@ -1,5 +1,5 @@
 use diesel_models::gsm as storage;
-use error_stack::report;
+use error_stack::{report, ResultExt};
 use router_env::{instrument, tracing};
 
 use super::MockDb;
@@ -13,8 +13,8 @@ use crate::{
 pub trait GsmInterface {
     async fn add_gsm_rule(
         &self,
-        rule: storage::GatewayStatusMappingNew,
-    ) -> CustomResult<storage::GatewayStatusMap, errors::StorageError>;
+        rule: hyperswitch_domain_models::gsm::GatewayStatusMap,
+    ) -> CustomResult<hyperswitch_domain_models::gsm::GatewayStatusMap, errors::StorageError>;
     async fn find_gsm_decision(
         &self,
         connector: String,
@@ -30,7 +30,7 @@ pub trait GsmInterface {
         sub_flow: String,
         code: String,
         message: String,
-    ) -> CustomResult<storage::GatewayStatusMap, errors::StorageError>;
+    ) -> CustomResult<hyperswitch_domain_models::gsm::GatewayStatusMap, errors::StorageError>;
     async fn update_gsm_rule(
         &self,
         connector: String,
@@ -38,8 +38,8 @@ pub trait GsmInterface {
         sub_flow: String,
         code: String,
         message: String,
-        data: storage::GatewayStatusMappingUpdate,
-    ) -> CustomResult<storage::GatewayStatusMap, errors::StorageError>;
+        data: hyperswitch_domain_models::gsm::GatewayStatusMappingUpdate,
+    ) -> CustomResult<hyperswitch_domain_models::gsm::GatewayStatusMap, errors::StorageError>;
 
     async fn delete_gsm_rule(
         &self,
@@ -56,12 +56,19 @@ impl GsmInterface for Store {
     #[instrument(skip_all)]
     async fn add_gsm_rule(
         &self,
-        rule: storage::GatewayStatusMappingNew,
-    ) -> CustomResult<storage::GatewayStatusMap, errors::StorageError> {
+        rule: hyperswitch_domain_models::gsm::GatewayStatusMap,
+    ) -> CustomResult<hyperswitch_domain_models::gsm::GatewayStatusMap, errors::StorageError> {
         let conn = connection::pg_connection_write(self).await?;
-        rule.insert(&conn)
+        let gsm_db_record = diesel_models::gsm::GatewayStatusMappingNew::try_from(rule)
+            .change_context(errors::StorageError::SerializationFailed)
+            .attach_printable("Failed to convert gsm domain models to diesel models")?
+            .insert(&conn)
             .await
-            .map_err(|error| report!(errors::StorageError::from(error)))
+            .map_err(|error| report!(errors::StorageError::from(error)))?;
+
+        hyperswitch_domain_models::gsm::GatewayStatusMap::try_from(gsm_db_record)
+            .change_context(errors::StorageError::DeserializationFailed)
+            .attach_printable("Failed to convert gsm diesel models to domain models")
     }
 
     #[instrument(skip_all)]
@@ -89,11 +96,16 @@ impl GsmInterface for Store {
         sub_flow: String,
         code: String,
         message: String,
-    ) -> CustomResult<storage::GatewayStatusMap, errors::StorageError> {
+    ) -> CustomResult<hyperswitch_domain_models::gsm::GatewayStatusMap, errors::StorageError> {
         let conn = connection::pg_connection_read(self).await?;
-        storage::GatewayStatusMap::find(&conn, connector, flow, sub_flow, code, message)
-            .await
-            .map_err(|error| report!(errors::StorageError::from(error)))
+        let gsm_db_record =
+            storage::GatewayStatusMap::find(&conn, connector, flow, sub_flow, code, message)
+                .await
+                .map_err(|error| report!(errors::StorageError::from(error)))?;
+
+        hyperswitch_domain_models::gsm::GatewayStatusMap::try_from(gsm_db_record)
+            .change_context(errors::StorageError::DeserializationFailed)
+            .attach_printable("Failed to convert gsm diesel models to domain models")
     }
 
     #[instrument(skip_all)]
@@ -104,12 +116,26 @@ impl GsmInterface for Store {
         sub_flow: String,
         code: String,
         message: String,
-        data: storage::GatewayStatusMappingUpdate,
-    ) -> CustomResult<storage::GatewayStatusMap, errors::StorageError> {
+        data: hyperswitch_domain_models::gsm::GatewayStatusMappingUpdate,
+    ) -> CustomResult<hyperswitch_domain_models::gsm::GatewayStatusMap, errors::StorageError> {
         let conn = connection::pg_connection_write(self).await?;
-        storage::GatewayStatusMap::update(&conn, connector, flow, sub_flow, code, message, data)
-            .await
-            .map_err(|error| report!(errors::StorageError::from(error)))
+        let gsm_update_data = diesel_models::gsm::GatewayStatusMappingUpdate::try_from(data)
+            .change_context(errors::StorageError::SerializationFailed)?;
+        let gsm_db_record = storage::GatewayStatusMap::update(
+            &conn,
+            connector,
+            flow,
+            sub_flow,
+            code,
+            message,
+            gsm_update_data,
+        )
+        .await
+        .map_err(|error| report!(errors::StorageError::from(error)))?;
+
+        hyperswitch_domain_models::gsm::GatewayStatusMap::try_from(gsm_db_record)
+            .change_context(errors::StorageError::DeserializationFailed)
+            .attach_printable("Failed to convert gsm diesel models to domain models")
     }
 
     #[instrument(skip_all)]
@@ -132,8 +158,8 @@ impl GsmInterface for Store {
 impl GsmInterface for MockDb {
     async fn add_gsm_rule(
         &self,
-        _rule: storage::GatewayStatusMappingNew,
-    ) -> CustomResult<storage::GatewayStatusMap, errors::StorageError> {
+        _rule: hyperswitch_domain_models::gsm::GatewayStatusMap,
+    ) -> CustomResult<hyperswitch_domain_models::gsm::GatewayStatusMap, errors::StorageError> {
         Err(errors::StorageError::MockDbError)?
     }
 
@@ -155,7 +181,7 @@ impl GsmInterface for MockDb {
         _sub_flow: String,
         _code: String,
         _message: String,
-    ) -> CustomResult<storage::GatewayStatusMap, errors::StorageError> {
+    ) -> CustomResult<hyperswitch_domain_models::gsm::GatewayStatusMap, errors::StorageError> {
         Err(errors::StorageError::MockDbError)?
     }
 
@@ -166,8 +192,8 @@ impl GsmInterface for MockDb {
         _sub_flow: String,
         _code: String,
         _message: String,
-        _data: storage::GatewayStatusMappingUpdate,
-    ) -> CustomResult<storage::GatewayStatusMap, errors::StorageError> {
+        _data: hyperswitch_domain_models::gsm::GatewayStatusMappingUpdate,
+    ) -> CustomResult<hyperswitch_domain_models::gsm::GatewayStatusMap, errors::StorageError> {
         Err(errors::StorageError::MockDbError)?
     }
 
diff --git a/crates/router/src/db/kafka_store.rs b/crates/router/src/db/kafka_store.rs
index b81e5c1f71c..412cc0a4a10 100644
--- a/crates/router/src/db/kafka_store.rs
+++ b/crates/router/src/db/kafka_store.rs
@@ -3185,8 +3185,8 @@ impl RoutingAlgorithmInterface for KafkaStore {
 impl GsmInterface for KafkaStore {
     async fn add_gsm_rule(
         &self,
-        rule: storage::GatewayStatusMappingNew,
-    ) -> CustomResult<storage::GatewayStatusMap, errors::StorageError> {
+        rule: hyperswitch_domain_models::gsm::GatewayStatusMap,
+    ) -> CustomResult<hyperswitch_domain_models::gsm::GatewayStatusMap, errors::StorageError> {
         self.diesel_store.add_gsm_rule(rule).await
     }
 
@@ -3210,7 +3210,7 @@ impl GsmInterface for KafkaStore {
         sub_flow: String,
         code: String,
         message: String,
-    ) -> CustomResult<storage::GatewayStatusMap, errors::StorageError> {
+    ) -> CustomResult<hyperswitch_domain_models::gsm::GatewayStatusMap, errors::StorageError> {
         self.diesel_store
             .find_gsm_rule(connector, flow, sub_flow, code, message)
             .await
@@ -3223,8 +3223,8 @@ impl GsmInterface for KafkaStore {
         sub_flow: String,
         code: String,
         message: String,
-        data: storage::GatewayStatusMappingUpdate,
-    ) -> CustomResult<storage::GatewayStatusMap, errors::StorageError> {
+        data: hyperswitch_domain_models::gsm::GatewayStatusMappingUpdate,
+    ) -> CustomResult<hyperswitch_domain_models::gsm::GatewayStatusMap, errors::StorageError> {
         self.diesel_store
             .update_gsm_rule(connector, flow, sub_flow, code, message, data)
             .await
diff --git a/crates/router/src/types/transformers.rs b/crates/router/src/types/transformers.rs
index 7b38342a743..90f2a423595 100644
--- a/crates/router/src/types/transformers.rs
+++ b/crates/router/src/types/transformers.rs
@@ -1586,42 +1586,129 @@ impl ForeignFrom<api_models::organization::OrganizationCreateRequest>
     }
 }
 
-impl ForeignFrom<gsm_api_types::GsmCreateRequest> for storage::GatewayStatusMappingNew {
+impl ForeignFrom<gsm_api_types::GsmCreateRequest>
+    for hyperswitch_domain_models::gsm::GatewayStatusMap
+{
     fn foreign_from(value: gsm_api_types::GsmCreateRequest) -> Self {
+        let inferred_feature_data = common_types::domain::GsmFeatureData::foreign_from(&value);
         Self {
             connector: value.connector.to_string(),
             flow: value.flow,
             sub_flow: value.sub_flow,
             code: value.code,
             message: value.message,
-            decision: value.decision.to_string(),
             status: value.status,
             router_error: value.router_error,
-            step_up_possible: value.step_up_possible,
             unified_code: value.unified_code,
             unified_message: value.unified_message,
             error_category: value.error_category,
-            clear_pan_possible: value.clear_pan_possible,
+            feature_data: value.feature_data.unwrap_or(inferred_feature_data),
+            feature: value.feature.unwrap_or(api_enums::GsmFeature::Retry),
+        }
+    }
+}
+
+impl ForeignFrom<&gsm_api_types::GsmCreateRequest> for common_types::domain::GsmFeatureData {
+    fn foreign_from(value: &gsm_api_types::GsmCreateRequest) -> Self {
+        // Defaulting alternate_network_possible to false as it is provided only in the Retry feature
+        // If the retry feature is not used, we assume alternate network as false
+        let alternate_network_possible = false;
+
+        match value.feature {
+            Some(api_enums::GsmFeature::Retry) | None => {
+                Self::Retry(common_types::domain::RetryFeatureData {
+                    step_up_possible: value.step_up_possible,
+                    clear_pan_possible: value.clear_pan_possible,
+                    alternate_network_possible,
+                    decision: value.decision,
+                })
+            }
+        }
+    }
+}
+
+impl
+    ForeignFrom<(
+        &gsm_api_types::GsmUpdateRequest,
+        hyperswitch_domain_models::gsm::GatewayStatusMap,
+    )> for (api_enums::GsmFeature, common_types::domain::GsmFeatureData)
+{
+    fn foreign_from(
+        (gsm_update_request, gsm_db_record): (
+            &gsm_api_types::GsmUpdateRequest,
+            hyperswitch_domain_models::gsm::GatewayStatusMap,
+        ),
+    ) -> Self {
+        let gsm_db_record_infered_feature = match gsm_db_record.feature_data.get_decision() {
+            api_enums::GsmDecision::Retry | api_enums::GsmDecision::DoDefault => {
+                api_enums::GsmFeature::Retry
+            }
+        };
+
+        let gsm_feature = gsm_update_request
+            .feature
+            .unwrap_or(gsm_db_record_infered_feature);
+
+        match gsm_feature {
+            api_enums::GsmFeature::Retry => {
+                let gsm_db_record_retry_feature_data =
+                    gsm_db_record.feature_data.get_retry_feature_data();
+
+                let retry_feature_data = common_types::domain::GsmFeatureData::Retry(
+                    common_types::domain::RetryFeatureData {
+                        step_up_possible: gsm_update_request
+                            .step_up_possible
+                            .or(gsm_db_record_retry_feature_data
+                                .clone()
+                                .map(|data| data.step_up_possible))
+                            .unwrap_or_default(),
+                        clear_pan_possible: gsm_update_request
+                            .clear_pan_possible
+                            .or(gsm_db_record_retry_feature_data
+                                .clone()
+                                .map(|data| data.clear_pan_possible))
+                            .unwrap_or_default(),
+                        alternate_network_possible: gsm_db_record_retry_feature_data
+                            .map(|data| data.alternate_network_possible)
+                            .unwrap_or_default(),
+                        decision: gsm_update_request
+                            .decision
+                            .or(Some(gsm_db_record.feature_data.get_decision()))
+                            .unwrap_or_default(),
+                    },
+                );
+                (api_enums::GsmFeature::Retry, retry_feature_data)
+            }
         }
     }
 }
 
-impl ForeignFrom<storage::GatewayStatusMap> for gsm_api_types::GsmResponse {
-    fn foreign_from(value: storage::GatewayStatusMap) -> Self {
+impl ForeignFrom<hyperswitch_domain_models::gsm::GatewayStatusMap> for gsm_api_types::GsmResponse {
+    fn foreign_from(value: hyperswitch_domain_models::gsm::GatewayStatusMap) -> Self {
         Self {
             connector: value.connector.to_string(),
             flow: value.flow,
             sub_flow: value.sub_flow,
             code: value.code,
             message: value.message,
-            decision: value.decision.to_string(),
+            decision: value.feature_data.get_decision(),
             status: value.status,
             router_error: value.router_error,
-            step_up_possible: value.step_up_possible,
+            step_up_possible: value
+                .feature_data
+                .get_retry_feature_data()
+                .map(|data| data.is_step_up_possible())
+                .unwrap_or(false),
             unified_code: value.unified_code,
             unified_message: value.unified_message,
             error_category: value.error_category,
-            clear_pan_possible: value.clear_pan_possible,
+            clear_pan_possible: value
+                .feature_data
+                .get_retry_feature_data()
+                .map(|data| data.is_clear_pan_possible())
+                .unwrap_or(false),
+            feature_data: Some(value.feature_data),
+            feature: value.feature,
         }
     }
 }
diff --git a/migrations/2025-04-07-133030_add_feature_data_column_to_gsm/down.sql b/migrations/2025-04-07-133030_add_feature_data_column_to_gsm/down.sql
new file mode 100644
index 00000000000..35bcb7d37f1
--- /dev/null
+++ b/migrations/2025-04-07-133030_add_feature_data_column_to_gsm/down.sql
@@ -0,0 +1,3 @@
+-- This file should undo anything in `up.sql`
+ALTER TABLE gateway_status_map DROP COLUMN IF EXISTS feature_data,
+    DROP COLUMN IF EXISTS feature;
\ No newline at end of file
diff --git a/migrations/2025-04-07-133030_add_feature_data_column_to_gsm/up.sql b/migrations/2025-04-07-133030_add_feature_data_column_to_gsm/up.sql
new file mode 100644
index 00000000000..1e53a532e8b
--- /dev/null
+++ b/migrations/2025-04-07-133030_add_feature_data_column_to_gsm/up.sql
@@ -0,0 +1,4 @@
+-- Your SQL goes here
+ALTER TABLE gateway_status_map
+ADD COLUMN IF NOT EXISTS feature_data JSONB DEFAULT NULL,
+    ADD COLUMN IF NOT EXISTS feature VARCHAR(64) DEFAULT NULL;
\ No newline at end of file

## Type of Change
<!-- Put an `x` in the boxes that apply -->

- [ ] Bugfix
- [x] New feature
- [ ] Enhancement
- [ ] Refactoring
- [ ] Dependency updates
- [ ] Documentation
- [ ] CI/CD

## Description
<!-- Describe your changes in detail -->
This pull request includes a number of changes to the `gsm` module across multiple files to introduce new features and improve the existing code structure. The most important changes include the addition of new fields and enums, refactoring of existing enums, and introducing domain models for gsm.

### Changes to `gsm` module:

* `crates/api_models/src/gsm.rs`:
  * Added new fields `alternate_network_possible` and `feature` to `GsmCreateRequest`, `GsmUpdateRequest`, and `GsmResponse` structs.
  * `step_up_possible` and `clear_pan_possible` are made as optional in api contract these are retry feature specific data 
and might not be present for other features.

* `crates/common_enums/src/enums.rs`:
  * Added new enum`GsmFeature` to handle GSM decision and feature data.
  * Remove `Requeue` as feature us not yet implemented

### Additional Changes

- [ ] This PR modifies the API contract
- [ ] This PR modifies the database schema
- [ ] This PR modifies application configuration/environment variables

<!--
Provide links to the files with corresponding changes.

Following are the paths where you can find config files:
1. `config`
2. `crates/router/src/configs`
3. `loadtest/config`
-->


## Motivation and Context
<!--
Why is this change required? What problem does it solve?
If it fixes an open issue, please link to the issue here.

If you don't have an issue, we'd recommend starting with one first so the PR
can focus on the implementation (unless it is an obvious bug or documentation fix
that will have little conversation).
-->


## How did you test it?
<!--
Did you write an integration/unit/API test to verify the code changes?
Or did you test this change manually (provide relevant screenshots)?
-->
### Create gsm rule without passing feature and feature data
```
curl --location 'http://localhost:8080/gsm' \
--header 'Content-Type: application/json' \
--header 'api-key: test_admin' \
--data '{
  "connector": "adyenplatform",
  "flow": "auth",
  "sub_flow": "sub_auth",
  "code": "501",
  "message": "Server Error",
  "status": "failed",
  "router_error": "Internal Server Error",
  "decision": "retry",
  "step_up_possible": true,
  "unified_code": "5xx",
  "unified_message": "Error",
  "error_category": "frm_decline",
  "clear_pan_possible": true,
  "alternate_network_possible": false
}'
```
```
{
    "connector": "adyenplatform",
    "flow": "auth",
    "sub_flow": "sub_auth",
    "code": "501",
    "message": "Server Error",
    "status": "failed",
    "router_error": "Internal Server Error",
    "decision": "retry",
    "step_up_possible": true,
    "unified_code": "5xx",
    "unified_message": "Error",
    "error_category": "frm_decline",
    "clear_pan_possible": true,
    "feature": "retry",
    "feature_data": {
        "retry": {
            "step_up_possible": true,
            "clear_pan_possible": true,
            "alternate_network_possible": false,
            "decision": "retry"
        }
    }
}
```
```
curl --location 'http://localhost:8080/gsm' \
--header 'Content-Type: application/json' \
--header 'api-key: test_admin' \
--data '{
  "connector": "stripe",
  "flow": "auth",
  "sub_flow": "sub_auth",
  "code": "501",
  "message": "Server Error",
  "status": "failed",
  "router_error": "Internal Server Error",
  "decision": "do_default",
  "step_up_possible": false,
  "unified_code": "5xx",
  "unified_message": "Error",
  "error_category": "frm_decline",
  "clear_pan_possible": true
}'
```
```
curl --location 'http://localhost:8080/gsm' \
--header 'Content-Type: application/json' \
--header 'api-key: test_admin' \
--data '{
  "connector": "stripe",
  "flow": "auth",
  "sub_flow": "sub_auth",
  "code": "501",
  "message": "Server Error",
  "status": "failed",
  "router_error": "Internal Server Error",
  "decision": "do_default",
  "step_up_possible": false,
  "unified_code": "5xx",
  "unified_message": "Error",
  "error_category": "frm_decline",
  "clear_pan_possible": true
}'
```
Db entry
<img width="2334" height="239" alt="image" src="https://github.com/user-attachments/assets/fd177675-dd2f-41d5-abf4-3b6c28411cda" />

### Update rule `"clear_pan_possible": false`
```
curl --location 'http://localhost:8080/gsm/update' \
--header 'Content-Type: application/json' \
--header 'api-key: test_admin' \
--data '{
  "connector": "stripe",
  "flow": "auth",
  "sub_flow": "sub_auth",
  "code": "501",
  "message": "Server Error",
  "status": "failed",
  "router_error": "Internal Server Error",
  "decision": "do_default",
  "step_up_possible": false,
  "unified_code": "5xx",
  "unified_message": "Error",
  "error_category": "frm_decline",
  "clear_pan_possible": false
}'
```
```
{
    "connector": "stripe",
    "flow": "auth",
    "sub_flow": "sub_auth",
    "code": "501",
    "message": "Server Error",
    "status": "failed",
    "router_error": "Internal Server Error",
    "decision": "do_default",
    "step_up_possible": false,
    "unified_code": "5xx",
    "unified_message": "Error",
    "error_category": "frm_decline",
    "clear_pan_possible": false,
    "feature": "retry",
    "feature_data": {
        "retry": {
            "step_up_possible": false,
            "clear_pan_possible": false,
            "alternate_network_possible": false,
            "decision": "do_default"
        }
    }
}
```

### Test case of existing records
(manually create db entries like the existing ones and try to update it)
db entry showing feature and feature_data to be null
<img width="1310" height="312" alt="image" src="https://github.com/user-attachments/assets/8a1af508-2899-4c56-a112-af37363542c5" />
-> update call will `"clear_pan_possible": true`
```
curl --location 'http://localhost:8080/gsm/update' \
--header 'Content-Type: application/json' \
--header 'api-key: test_admin' \
--data '{
  "connector": "novalnet",
  "flow": "Authorize",
  "sub_flow": "sub_flow",
  "code": "525002",
  "message": "Personal Identity Card Number not correct",
  "status": "Failure",
  "router_error": "Internal Server Error",
  "decision": "retry",
  "step_up_possible": false,
  "unified_code": "5xx",
  "unified_message": "Error",
  "error_category": "frm_decline",
  "clear_pan_possible": true
}'
```
```
{
    "connector": "novalnet",
    "flow": "Authorize",
    "sub_flow": "sub_flow",
    "code": "525002",
    "message": "Personal Identity Card Number not correct",
    "status": "Failure",
    "router_error": "Internal Server Error",
    "decision": "retry",
    "step_up_possible": false,
    "unified_code": "5xx",
    "unified_message": "Error",
    "error_category": "frm_decline",
    "clear_pan_possible": true,
    "feature": "retry",
    "feature_data": {
        "retry": {
            "step_up_possible": false,
            "clear_pan_possible": true,
            "alternate_network_possible": false,
            "decision": "retry"
        }
    }
}
```
DB entry after update
<img width="1267" height="326" alt="image" src="https://github.com/user-attachments/assets/421c4fe9-eb73-409b-9810-e7101be63bba" />

### Test case for the new change where in which we pass feature and feature data in the request
```
curl --location 'http://localhost:8080/gsm' \
--header 'Content-Type: application/json' \
--header 'api-key: test_admin' \
--data '{
    "connector": "stripe",
    "flow": "auth",
    "sub_flow": "sub_auth",
    "code": "501",
    "message": "Server Error",
    "status": "failed",
    "router_error": "Internal Server Error",
    "decision": "do_default",
    "step_up_possible": true,
    "clear_pan_possible": true,
    "unified_code": "5xx",
    "unified_message": "Error",
    "error_category": "frm_decline",
    "feature": "retry",
    "feature_data": {
        "retry": {
            "step_up_possible": false,
            "clear_pan_possible": false,
            "alternate_network_possible": false,
            "decision": "do_default"
        }
    }
}'
```
```
{
    "connector": "stripe",
    "flow": "auth",
    "sub_flow": "sub_auth",
    "code": "501",
    "message": "Server Error",
    "status": "failed",
    "router_error": "Internal Server Error",
    "decision": "do_default",
    "step_up_possible": false,
    "unified_code": "5xx",
    "unified_message": "Error",
    "error_category": "frm_decline",
    "clear_pan_possible": false,
    "feature": "retry",
    "feature_data": {
        "retry": {
            "step_up_possible": false,
            "clear_pan_possible": false,
            "alternate_network_possible": false,
            "decision": "do_default"
        }
    }
}
```
<img width="1221" height="336" alt="image" src="https://github.com/user-attachments/assets/8bd9e48b-4f7b-492a-aacc-9d6d8fd969c4" />
We can see that step_up_possible and clear_pan_possible has been passed as true in the root level whereas it has been passed as false in the feature_data. And in the application response clear_pan_possible and step_up_possible is false in both the places as the values in the feature_data takes precedence.

Note :-
Same goes for db state and update gsm call

-> Update call
```
curl --location 'http://localhost:8080/gsm/update' \
--header 'Content-Type: application/json' \
--header 'api-key: test_admin' \
--data '{
    "connector": "stripe",
    "flow": "auth",
    "sub_flow": "sub_auth",
    "code": "501",
    "message": "Server Error",
    "status": "failed",
    "router_error": "Internal Server Error",
    "decision": "do_default",
    "step_up_possible": false,
    "clear_pan_possible": false,
    "unified_code": "5xx",
    "unified_message": "Error",
    "error_category": "frm_decline",
    "feature": "retry",
    "feature_data": {
        "retry": {
            "step_up_possible": true,
            "clear_pan_possible": true,
            "alternate_network_possible": false,
            "decision": "do_default"
        }
    }
}'
``` 
```
{
    "connector": "stripe",
    "flow": "auth",
    "sub_flow": "sub_auth",
    "code": "501",
    "message": "Server Error",
    "status": "failed",
    "router_error": "Internal Server Error",
    "decision": "do_default",
    "step_up_possible": true,
    "unified_code": "5xx",
    "unified_message": "Error",
    "error_category": "frm_decline",
    "clear_pan_possible": true,
    "feature": "retry",
    "feature_data": {
        "retry": {
            "step_up_possible": true,
            "clear_pan_possible": true,
            "alternate_network_possible": false,
            "decision": "do_default"
        }
    }
}
```
<img width="1359" height="333" alt="image" src="https://github.com/user-attachments/assets/9e3252ab-4905-4113-a963-999dd6157932" />

-> Test a retry payment
```
curl --location 'http://localhost:8080/payments' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'x-feature: router-custom' \
--header 'x-tenant-id: hyperswitch' \
--header 'api-key: dev_NZClQhIklvWYnqEzfkfdzGOynCohMCTnkbYrPN0HPkQbm6rTnngnthX51kLWDIn2' \
--data-raw '{
    "amount": 1000000,
    "amount_to_capture": 1000000,
    "currency": "USD",
    "confirm": true,
    "capture_method": "automatic",
    "setup_future_usage": "on_session",
    "capture_on": "2022-09-10T10:11:12Z",
    "email": "guest@example.com",
    "name": "John Doe",
    "phone": "999999999",
    "phone_country_code": "+1",
    "description": "Its my first payment request",
    "authentication_type": "no_three_ds",
    "customer_id": "cu_1753886032",
    "return_url": "http://127.0.0.1:4040",
    "payment_method": "card",
    "payment_method_type": "debit",
    "payment_method_data": {
        "card": {
            "card_number": "4111111111111111",
            "card_exp_month": "03",
            "card_exp_year": "2030",
            "card_holder_name": "joseph Doe",
            "card_cvc": "123"
        }
    },
    "billing": {
        "address": {
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "city": "San",
            "state": "California",
            "zip": "94122",
            "country": "US",
            "first_name": "PiX",
            "last_name": "ss"
        },
        "email": "raj@gmail.com"
    },
    "shipping": {
        "address": {
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "city": "San Fransico",
            "state": "California",
            "zip": "94122",
            "country": "US",
            "first_name": "PiX"
        }
    },
    "customer_acceptance": {
        "acceptance_type": "offline",
        "accepted_at": "1963-05-03T04:07:52.723Z",
        "online": {
            "ip_address": "in sit",
            "user_agent": "amet irure esse"
        }
    },
    "browser_info": {
        "user_agent": "Mozilla\/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/70.0.3538.110 Safari\/537.36",
        "accept_header": "text\/html,application\/xhtml+xml,application\/xml;q=0.9,image\/webp,image\/apng,*\/*;q=0.8",
        "language": "nl-NL",
        "color_depth": 24,
        "screen_height": 723,
        "screen_width": 1536,
        "time_zone": 0,
        "java_enabled": true,
        "java_script_enabled": true,
        "ip_address": "125.0.0.1"
    },
    "statement_descriptor_name": "joseph",
    "statement_descriptor_suffix": "JS",
    "metadata": {
        "udf1": "value1",
        "new_customer": "true",
        "login_date": "2019-09-10T10:11:12Z"
    }
}'
```
```
{
    "payment_id": "pay_cLWcRqZDJcOaiN3oyxJh",
    "merchant_id": "merchant_1753885328",
    "status": "succeeded",
    "amount": 1000000,
    "net_amount": 1000000,
    "shipping_cost": null,
    "amount_capturable": 0,
    "amount_received": 1000000,
    "connector": "cybersource",
    "client_secret": "pay_cLWcRqZDJcOaiN3oyxJh_secret_CCZupjoT0zDE6m4s1OCu",
    "created": "2025-07-30T14:32:41.449Z",
    "currency": "USD",
    "customer_id": "cu_1753885961",
    "customer": {
        "id": "cu_1753885961",
        "name": "John Doe",
        "email": "guest@example.com",
        "phone": "999999999",
        "phone_country_code": "+1"
    },
    "description": "Its my first payment request",
    "refunds": null,
    "disputes": null,
    "mandate_id": null,
    "mandate_data": null,
    "setup_future_usage": "on_session",
    "off_session": null,
    "capture_on": null,
    "capture_method": "automatic",
    "payment_method": "card",
    "payment_method_data": {
        "card": {
            "last4": "1111",
            "card_type": null,
            "card_network": null,
            "card_issuer": null,
            "card_issuing_country": null,
            "card_isin": "411111",
            "card_extended_bin": null,
            "card_exp_month": "03",
            "card_exp_year": "2030",
            "card_holder_name": "joseph Doe",
            "payment_checks": {
                "avs_response": {
                    "code": "X",
                    "codeRaw": "I1"
                },
                "card_verification": null
            },
            "authentication_data": null
        },
        "billing": null
    },
    "payment_token": "token_eWHrgeRsB1CJo79hTJVt",
    "shipping": {
        "address": {
            "city": "San Fransico",
            "country": "US",
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "zip": "94122",
            "state": "California",
            "first_name": "PiX",
            "last_name": null
        },
        "phone": null,
        "email": null
    },
    "billing": {
        "address": {
            "city": "San",
            "country": "US",
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "zip": "94122",
            "state": "California",
            "first_name": "PiX",
            "last_name": "ss"
        },
        "phone": null,
        "email": "raj@gmail.com"
    },
    "order_details": null,
    "email": "guest@example.com",
    "name": "John Doe",
    "phone": "999999999",
    "return_url": "http://127.0.0.1:4040/",
    "authentication_type": "no_three_ds",
    "statement_descriptor_name": "joseph",
    "statement_descriptor_suffix": "JS",
    "next_action": null,
    "cancellation_reason": null,
    "error_code": null,
    "error_message": null,
    "unified_code": null,
    "unified_message": null,
    "payment_experience": null,
    "payment_method_type": "debit",
    "connector_label": null,
    "business_country": null,
    "business_label": "default",
    "business_sub_label": null,
    "allowed_payment_method_types": null,
    "ephemeral_key": {
        "customer_id": "cu_1753885961",
        "created_at": 1753885961,
        "expires": 1753889561,
        "secret": "epk_fa14f011aa934fb29ae59ecdcf6cdd86"
    },
    "manual_retry_allowed": false,
    "connector_transaction_id": "7538859630876495904805",
    "frm_message": null,
    "metadata": {
        "udf1": "value1",
        "login_date": "2019-09-10T10:11:12Z",
        "new_customer": "true"
    },
    "connector_metadata": null,
    "feature_metadata": null,
    "reference_id": "pay_cLWcRqZDJcOaiN3oyxJh_2",
    "payment_link": null,
    "profile_id": "pro_OMlZtENVSWshV7Mh9NPv",
    "surcharge_details": null,
    "attempt_count": 2,
    "merchant_decision": null,
    "merchant_connector_id": "mca_jAKVTh39vFjuD4LN7ZuX",
    "incremental_authorization_allowed": false,
    "authorization_count": null,
    "incremental_authorizations": null,
    "external_authentication_details": null,
    "external_3ds_authentication_attempted": false,
    "expires_on": "2025-07-30T14:47:41.449Z",
    "fingerprint": null,
    "browser_info": {
        "language": "nl-NL",
        "time_zone": 0,
        "ip_address": "125.0.0.1",
        "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36",
        "color_depth": 24,
        "java_enabled": true,
        "screen_width": 1536,
        "accept_header": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
        "screen_height": 723,
        "java_script_enabled": true
    },
    "payment_method_id": null,
    "payment_method_status": null,
    "updated": "2025-07-30T14:32:43.450Z",
    "split_payments": null,
    "frm_metadata": null,
    "extended_authorization_applied": null,
    "capture_before": null,
    "merchant_order_reference_id": null,
    "order_tax_amount": null,
    "connector_mandate_id": null,
    "card_discovery": "manual",
    "force_3ds_challenge": false,
    "force_3ds_challenge_trigger": false,
    "issuer_error_code": null,
    "issuer_error_message": null,
    "is_iframe_redirection_enabled": null,
    "whole_connector_response": null
}
```




## Checklist
<!-- Put an `x` in the boxes that apply -->

- [x] I formatted the code `cargo +nightly fmt --all`
- [x] I addressed lints thrown by `cargo clippy`
- [x] I reviewed the submitted code
- [ ] I added unit tests for my changes where possible
