Bug: [FEATURE] Decide Flow based on setup_future_usage param



### Feature Description

The setup_future_usage has two fields on_session and off_session. The flow, i.e. either SaveCard/Mandate, will be decided on the basis of these fields.



### Possible Implementation

In our current flow there's no specific SaveCard Flow implementation based on the setup_future_usage. So to have specific implementation and distinction between different flows, there would be appropriate usage of the fields of setup_future_usage as:

- If the field is on_session, there would be a normal save card flow
- if the field is off_session and customer_acceptance field is passed ,  there would be a mandate flow
- If the field is  off_session and no customer_acceptance field is passed, there would be a One Time Payment

### Have you spent some time checking if this feature request has been raised before?

- [X] I checked and didn't find a similar issue

### Have you read the Contributing Guidelines?

- [X] I have read the [Contributing Guidelines](https://github.com/juspay/hyperswitch/blob/main/docs/CONTRIBUTING.md)

### Are you willing to submit a PR?

Yes, I am willing to submit a PR!
diff --git a/crates/api_models/src/mandates.rs b/crates/api_models/src/mandates.rs
index b29f4e0d0c3..7c20a902d28 100644
--- a/crates/api_models/src/mandates.rs
+++ b/crates/api_models/src/mandates.rs
@@ -77,6 +77,9 @@ pub struct MandateCardDetails {
     pub card_network: Option<api_enums::CardNetwork>,
     /// The type of the payment card
     pub card_type: Option<String>,
+    /// The nick_name of the card holder
+    #[schema(value_type = Option<String>)]
+    pub nick_name: Option<Secret<String>>,
 }
 
 #[derive(Clone, Debug, Deserialize, ToSchema, Serialize)]
diff --git a/crates/router/src/core/payments/flows/authorize_flow.rs b/crates/router/src/core/payments/flows/authorize_flow.rs
index c6de222f7d8..c64912195ed 100644
--- a/crates/router/src/core/payments/flows/authorize_flow.rs
+++ b/crates/router/src/core/payments/flows/authorize_flow.rs
@@ -103,6 +103,7 @@ impl Feature<api::Authorize, types::PaymentsAuthorizeData> for types::PaymentsAu
                     merchant_account,
                     self.request.payment_method_type,
                     key_store,
+                    is_mandate,
                 ))
                 .await?;
                 Ok(mandate::mandate_procedure(
@@ -134,6 +135,7 @@ impl Feature<api::Authorize, types::PaymentsAuthorizeData> for types::PaymentsAu
                         &merchant_account,
                         self.request.payment_method_type,
                         &key_store,
+                        is_mandate,
                     ))
                     .await;
 
diff --git a/crates/router/src/core/payments/flows/setup_mandate_flow.rs b/crates/router/src/core/payments/flows/setup_mandate_flow.rs
index 8b0b54158fd..712184308dd 100644
--- a/crates/router/src/core/payments/flows/setup_mandate_flow.rs
+++ b/crates/router/src/core/payments/flows/setup_mandate_flow.rs
@@ -76,6 +76,9 @@ impl Feature<api::SetupMandate, types::SetupMandateRequestData> for types::Setup
         )
         .await
         .to_setup_mandate_failed_response()?;
+
+        let is_mandate = resp.request.setup_mandate_details.is_some();
+
         let pm_id = Box::pin(tokenization::save_payment_method(
             state,
             connector,
@@ -84,6 +87,7 @@ impl Feature<api::SetupMandate, types::SetupMandateRequestData> for types::Setup
             merchant_account,
             self.request.payment_method_type,
             key_store,
+            is_mandate,
         ))
         .await?;
 
@@ -280,6 +284,8 @@ impl types::SetupMandateRouterData {
 
                 let payment_method_type = self.request.payment_method_type;
 
+                let is_mandate = resp.request.setup_mandate_details.is_some();
+
                 let pm_id = Box::pin(tokenization::save_payment_method(
                     state,
                     connector,
@@ -288,6 +294,7 @@ impl types::SetupMandateRouterData {
                     merchant_account,
                     payment_method_type,
                     key_store,
+                    is_mandate,
                 ))
                 .await?;
 
diff --git a/crates/router/src/core/payments/operations/payment_create.rs b/crates/router/src/core/payments/operations/payment_create.rs
index 378cbb25467..9117dcb9966 100644
--- a/crates/router/src/core/payments/operations/payment_create.rs
+++ b/crates/router/src/core/payments/operations/payment_create.rs
@@ -8,7 +8,7 @@ use data_models::{
     payments::payment_attempt::PaymentAttempt,
 };
 use diesel_models::ephemeral_key;
-use error_stack::{self, ResultExt};
+use error_stack::{self, report, ResultExt};
 use masking::PeekInterface;
 use router_derive::PaymentOperation;
 use router_env::{instrument, tracing};
@@ -619,6 +619,17 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve> ValidateRequest<F, api::Paymen
 
         helpers::validate_payment_method_fields_present(request)?;
 
+        if request.mandate_data.is_none()
+            && request
+                .setup_future_usage
+                .map(|fut_usage| fut_usage == enums::FutureUsage::OffSession)
+                .unwrap_or(false)
+        {
+            Err(report!(errors::ApiErrorResponse::PreconditionFailed {
+                message: "`setup_future_usage` cannot be `off_session` for normal payments".into()
+            }))?
+        }
+
         let mandate_type =
             helpers::validate_mandate(request, payments::is_operation_confirm(self))?;
 
diff --git a/crates/router/src/core/payments/operations/payment_update.rs b/crates/router/src/core/payments/operations/payment_update.rs
index 664fce820a0..b29696a2f10 100644
--- a/crates/router/src/core/payments/operations/payment_update.rs
+++ b/crates/router/src/core/payments/operations/payment_update.rs
@@ -672,6 +672,17 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve> ValidateRequest<F, api::Paymen
 
         helpers::validate_payment_method_fields_present(request)?;
 
+        if request.mandate_data.is_none()
+            && request
+                .setup_future_usage
+                .map(|fut_usage| fut_usage == storage_enums::FutureUsage::OffSession)
+                .unwrap_or(false)
+        {
+            Err(report!(errors::ApiErrorResponse::PreconditionFailed {
+                message: "`setup_future_usage` cannot be `off_session` for normal payments".into()
+            }))?
+        }
+
         let mandate_type = helpers::validate_mandate(request, false)?;
 
         Ok((
diff --git a/crates/router/src/core/payments/tokenization.rs b/crates/router/src/core/payments/tokenization.rs
index f52bce46d8e..1b9f512d842 100644
--- a/crates/router/src/core/payments/tokenization.rs
+++ b/crates/router/src/core/payments/tokenization.rs
@@ -22,6 +22,7 @@ use crate::{
 };
 
 #[instrument(skip_all)]
+#[allow(clippy::too_many_arguments)]
 pub async fn save_payment_method<F: Clone, FData>(
     state: &AppState,
     connector: &api::ConnectorData,
@@ -30,6 +31,7 @@ pub async fn save_payment_method<F: Clone, FData>(
     merchant_account: &domain::MerchantAccount,
     payment_method_type: Option<storage_enums::PaymentMethodType>,
     key_store: &domain::MerchantKeyStore,
+    is_mandate: bool,
 ) -> RouterResult<Option<String>>
 where
     FData: mandate::MandateBehaviour,
@@ -62,8 +64,16 @@ where
             } else {
                 None
             };
+            let future_usage_validation = resp
+                .request
+                .get_setup_future_usage()
+                .map(|future_usage| {
+                    (future_usage == storage_enums::FutureUsage::OffSession && is_mandate)
+                        || (future_usage == storage_enums::FutureUsage::OnSession && !is_mandate)
+                })
+                .unwrap_or(false);
 
-            let pm_id = if resp.request.get_setup_future_usage().is_some() {
+            let pm_id = if future_usage_validation {
                 let customer = maybe_customer.to_owned().get_required_value("customer")?;
                 let payment_method_create_request = helpers::get_payment_method_create_request(
                     Some(&resp.request.get_payment_method_data()),
diff --git a/crates/router/src/types/api/mandates.rs b/crates/router/src/types/api/mandates.rs
index f6b2d7bba93..051547dfa92 100644
--- a/crates/router/src/types/api/mandates.rs
+++ b/crates/router/src/types/api/mandates.rs
@@ -112,6 +112,7 @@ impl From<api::payment_methods::CardDetailFromLocker> for MandateCardDetails {
             card_issuer: card_details_from_locker.card_issuer,
             card_network: card_details_from_locker.card_network,
             card_type: card_details_from_locker.card_type,
+            nick_name: card_details_from_locker.nick_name,
         }
         .into()
     }
diff --git a/openapi/openapi_spec.json b/openapi/openapi_spec.json
index 98cfaecf6b8..9627d32ef9e 100644
--- a/openapi/openapi_spec.json
+++ b/openapi/openapi_spec.json
@@ -9059,6 +9059,11 @@
             "type": "string",
             "description": "The type of the payment card",
             "nullable": true
+          },
+          "nick_name": {
+            "type": "string",
+            "description": "The nick_name of the card holder",
+            "nullable": true
           }
         }
       },
diff --git a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario23- Update Amount/Payments - Confirm/request.json b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario23- Update Amount/Payments - Confirm/request.json
index 4d4486b1d43..5b0c090f2be 100644
--- a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario23- Update Amount/Payments - Confirm/request.json	
+++ b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario23- Update Amount/Payments - Confirm/request.json	
@@ -62,7 +62,7 @@
           "card_cvc": "737"
         }
       },
-      "setup_future_usage": "off_session",
+      "setup_future_usage": "on_session",
       "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",
@@ -79,14 +79,8 @@
   },
   "url": {
     "raw": "{{baseUrl}}/payments/:id/confirm",
-    "host": [
-      "{{baseUrl}}"
-    ],
-    "path": [
-      "payments",
-      ":id",
-      "confirm"
-    ],
+    "host": ["{{baseUrl}}"],
+    "path": ["payments", ":id", "confirm"],
     "variable": [
       {
         "key": "id",

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

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

## Description
decide flow based on setup_future_usage


### 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?

- Create an MA and MCA
- Change the setup future usage field on the basis of following cases :
> If the field is on_session, there would be a normal save card flow
> if the field is off_session and customer_acceptance field is passed , there would be a mandate flow
> If the field is off_session and no customer_acceptance field is passed, there would be a One Time Payment
> If the field is null it'll be a one Time Payment


## 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`
- [ ] I reviewed the submitted code
- [ ] I added unit tests for my changes where possible
- [ ] I added a [CHANGELOG](/CHANGELOG.md) entry if applicable
