Issue:
Bug: [FEATURE] mandate type to be sent by the `merchant`



### Feature Description

In the current scenario, the `mandate type` object has to be sent in the `confirm` call. But this decision has to be taken by the `Merchant` during the `payments_create` call. So we should allow the merchant to pass mandate type and then mandate type in the `confirm` call.

### Possible Implementation

Remove the validation that `confirm` should be `true` in case of mandates because this will not allow a payment to be created without confirming. Make necessary changes internally to accomodate the required changes.

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

- [X] I checked and didn't find 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!

Pull request description:


## Type of Change
<!-- Put an `x` in the boxes that apply -->

- [ ] Bugfix
- [ ] New feature
- [x] Enhancement
- [x] Refactoring
- [ ] Dependency updates
- [ ] Documentation
- [ ] CI/CD

## Description

**Current Implementation**
In order to create a mandate, the merchant has to provide both `mandate_type` and `customer_acceptance` together and is only allowed during the confirm call.
**Requirements**
As per the SDK requirements and the data availability. Both of these data fields won't be available together on either the merchant side or the SDK side. This compels us to make a mechanism which will provide a way for the merchant to provide the `mandate_type` and the SDK to provide `customer_acceptance`.
**Change**
The PR aims to solve the current limitations and provide a way for the merchant to provide/update the mandate type, and they allow the customer during the confirm call to provide the customer_acceptance to create & activate the mandate.
The PR also provides a entry flag in the `payment_method_list` to let the customer know, that this payment is a create mandate payment. This is done by looking for any mandate details in the payment_attempt when the customer tries to get the list of the payment methods

<!-- Describe your changes in detail -->


### Additional Changes

- [x] This PR modifies the API contract
adding an extra field in the payment_method_list response
```json
{
    mandate_payment: "new_mandate_txn",
}
```
Currently, this field will only be visible if it's a new mandate payment with the above mention addition in the payment_method_list API

- [x] This PR modifies the database schema
the following migration is done
```sql
ALTER TABLE payment_attempt ADD COLUMN mandate_details JSONB;
```

<!-- 
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
Customer critical 
<!--
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 its an obvious bug or documentation fix
that will have little conversation).
-->


## How did you test it?
![Screenshot 2023-05-17 at 7 08 08 PM](https://github.com/juspay/hyperswitch/assets/51093026/ddb9e158-3093-4747-8d19-6953e05403c9)
This is the initial payment with `confirm: false`
![Screenshot 2023-05-17 at 7 09 04 PM](https://github.com/juspay/hyperswitch/assets/51093026/6f6d69e8-f0a7-4ef6-9970-eeaf4aabadff)
This is the following confirm call, which creates the mandate
![Screenshot 2023-05-17 at 7 09 35 PM](https://github.com/juspay/hyperswitch/assets/51093026/2dcc1c07-cd2d-489b-aef8-b47eca93b672)
And this is the recurring payment, which provides a proof, that the mandate was successfully created

<!--
Did you write an integration/unit/API test to verify the code changes?
Or did you test this change manually (provide relevant screenshots)?
-->


## 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 submitted code
- [ ] I added unit tests for my changes where possible
- [ ] I added a [CHANGELOG](/CHANGELOG.md) entry if applicable


Testing:
![Screenshot 2023-05-17 at 7 08 08 PM](https://github.com/juspay/hyperswitch/assets/51093026/ddb9e158-3093-4747-8d19-6953e05403c9)
This is the initial payment with `confirm: false`
![Screenshot 2023-05-17 at 7 09 04 PM](https://github.com/juspay/hyperswitch/assets/51093026/6f6d69e8-f0a7-4ef6-9970-eeaf4aabadff)
This is the following confirm call, which creates the mandate
![Screenshot 2023-05-17 at 7 09 35 PM](https://github.com/juspay/hyperswitch/assets/51093026/2dcc1c07-cd2d-489b-aef8-b47eca93b672)
And this is the recurring payment, which provides a proof, that the mandate was successfully created



Patch:
diff --git a/Cargo.lock b/Cargo.lock
index fb22127838b..da0033c8336 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3000,7 +3000,7 @@ dependencies = [
 [[package]]
 name = "opentelemetry"
 version = "0.18.0"
-source = "git+https://github.com/open-telemetry/opentelemetry-rust/?rev=44b90202fd744598db8b0ace5b8f0bad7ec45658#44b90202fd744598db8b0ace5b8f0bad7ec45658"
+source = "git+https://github.com/open-telemetry/opentelemetry-rust?rev=44b90202fd744598db8b0ace5b8f0bad7ec45658#44b90202fd744598db8b0ace5b8f0bad7ec45658"
 dependencies = [
  "opentelemetry_api",
  "opentelemetry_sdk",
@@ -3009,7 +3009,7 @@ dependencies = [
 [[package]]
 name = "opentelemetry-otlp"
 version = "0.11.0"
-source = "git+https://github.com/open-telemetry/opentelemetry-rust/?rev=44b90202fd744598db8b0ace5b8f0bad7ec45658#44b90202fd744598db8b0ace5b8f0bad7ec45658"
+source = "git+https://github.com/open-telemetry/opentelemetry-rust?rev=44b90202fd744598db8b0ace5b8f0bad7ec45658#44b90202fd744598db8b0ace5b8f0bad7ec45658"
 dependencies = [
  "async-trait",
  "futures",
@@ -3026,7 +3026,7 @@ dependencies = [
 [[package]]
 name = "opentelemetry-proto"
 version = "0.1.0"
-source = "git+https://github.com/open-telemetry/opentelemetry-rust/?rev=44b90202fd744598db8b0ace5b8f0bad7ec45658#44b90202fd744598db8b0ace5b8f0bad7ec45658"
+source = "git+https://github.com/open-telemetry/opentelemetry-rust?rev=44b90202fd744598db8b0ace5b8f0bad7ec45658#44b90202fd744598db8b0ace5b8f0bad7ec45658"
 dependencies = [
  "futures",
  "futures-util",
@@ -3038,7 +3038,7 @@ dependencies = [
 [[package]]
 name = "opentelemetry_api"
 version = "0.18.0"
-source = "git+https://github.com/open-telemetry/opentelemetry-rust/?rev=44b90202fd744598db8b0ace5b8f0bad7ec45658#44b90202fd744598db8b0ace5b8f0bad7ec45658"
+source = "git+https://github.com/open-telemetry/opentelemetry-rust?rev=44b90202fd744598db8b0ace5b8f0bad7ec45658#44b90202fd744598db8b0ace5b8f0bad7ec45658"
 dependencies = [
  "fnv",
  "futures-channel",
@@ -3053,7 +3053,7 @@ dependencies = [
 [[package]]
 name = "opentelemetry_sdk"
 version = "0.18.0"
-source = "git+https://github.com/open-telemetry/opentelemetry-rust/?rev=44b90202fd744598db8b0ace5b8f0bad7ec45658#44b90202fd744598db8b0ace5b8f0bad7ec45658"
+source = "git+https://github.com/open-telemetry/opentelemetry-rust?rev=44b90202fd744598db8b0ace5b8f0bad7ec45658#44b90202fd744598db8b0ace5b8f0bad7ec45658"
 dependencies = [
  "async-trait",
  "crossbeam-channel",
diff --git a/crates/api_models/src/payment_methods.rs b/crates/api_models/src/payment_methods.rs
index d110f0192a1..4d53336eb08 100644
--- a/crates/api_models/src/payment_methods.rs
+++ b/crates/api_models/src/payment_methods.rs
@@ -429,6 +429,9 @@ pub struct PaymentMethodListResponse {
     ]
     ))]
     pub payment_methods: Vec<ResponsePaymentMethodsEnabled>,
+    /// Value indicating if the current payment is a mandate payment
+    #[schema(example = "new_mandate_txn")]
+    pub mandate_payment: Option<payments::MandateTxnType>,
 }
 
 #[derive(Eq, PartialEq, Hash, Debug, serde::Deserialize, ToSchema)]
diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs
index 0a6ec07349e..48bd3aa9265 100644
--- a/crates/api_models/src/payments.rs
+++ b/crates/api_models/src/payments.rs
@@ -298,7 +298,8 @@ impl From<PaymentsRequest> for VerifyRequest {
     }
 }
 
-#[derive(Clone)]
+#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
+#[serde(rename_all = "snake_case")]
 pub enum MandateTxnType {
     NewMandateTxn,
     RecurringMandateTxn,
@@ -331,13 +332,15 @@ impl MandateIds {
     }
 }
 
+// The fields on this struct are optional, as we want to allow the merchant to provide partial
+// information about creating mandates
 #[derive(Default, Eq, PartialEq, Debug, serde::Deserialize, serde::Serialize, Clone, ToSchema)]
 #[serde(deny_unknown_fields)]
 pub struct MandateData {
     /// A concent from the customer to store the payment method
-    pub customer_acceptance: CustomerAcceptance,
+    pub customer_acceptance: Option<CustomerAcceptance>,
     /// A way to select the type of mandate used
-    pub mandate_type: MandateType,
+    pub mandate_type: Option<MandateType>,
 }
 
 #[derive(Clone, Eq, PartialEq, Copy, Debug, Default, serde::Serialize, serde::Deserialize)]
diff --git a/crates/router/src/compatibility/stripe/payment_intents/types.rs b/crates/router/src/compatibility/stripe/payment_intents/types.rs
index 0206cf96008..752244440fa 100644
--- a/crates/router/src/compatibility/stripe/payment_intents/types.rs
+++ b/crates/router/src/compatibility/stripe/payment_intents/types.rs
@@ -561,27 +561,27 @@ impl ForeignTryFrom<(Option<MandateOption>, Option<String>)> for Option<payments
         let mandate_data = mandate_options.map(|mandate| payments::MandateData {
             mandate_type: match mandate.mandate_type {
                 Some(item) => match item {
-                    StripeMandateType::SingleUse => {
-                        payments::MandateType::SingleUse(payments::MandateAmountData {
+                    StripeMandateType::SingleUse => Some(payments::MandateType::SingleUse(
+                        payments::MandateAmountData {
                             amount: mandate.amount.unwrap_or_default(),
                             currency,
                             start_date: mandate.start_date,
                             end_date: mandate.end_date,
                             metadata: None,
-                        })
-                    }
-                    StripeMandateType::MultiUse => payments::MandateType::MultiUse(None),
+                        },
+                    )),
+                    StripeMandateType::MultiUse => Some(payments::MandateType::MultiUse(None)),
                 },
-                None => api_models::payments::MandateType::MultiUse(None),
+                None => Some(api_models::payments::MandateType::MultiUse(None)),
             },
-            customer_acceptance: payments::CustomerAcceptance {
+            customer_acceptance: Some(payments::CustomerAcceptance {
                 acceptance_type: payments::AcceptanceType::Online,
                 accepted_at: mandate.accepted_at,
                 online: Some(payments::OnlineMandate {
                     ip_address: mandate.ip_address.unwrap_or_default(),
                     user_agent: mandate.user_agent.unwrap_or_default(),
                 }),
-            },
+            }),
         });
         Ok(mandate_data)
     }
diff --git a/crates/router/src/connector/nuvei/transformers.rs b/crates/router/src/connector/nuvei/transformers.rs
index 917be015784..73ce1eac4a4 100644
--- a/crates/router/src/connector/nuvei/transformers.rs
+++ b/crates/router/src/connector/nuvei/transformers.rs
@@ -18,6 +18,7 @@ use crate::{
     core::errors,
     services,
     types::{self, api, storage::enums, transformers::ForeignTryFrom},
+    utils::OptionExt,
 };
 
 #[derive(Debug, Serialize, Default, Deserialize)]
@@ -712,7 +713,12 @@ fn get_card_info<F>(
         let (is_rebilling, additional_params, user_token_id) =
             match item.request.setup_mandate_details.clone() {
                 Some(mandate_data) => {
-                    let details = match mandate_data.mandate_type {
+                    let details = match mandate_data
+                        .mandate_type
+                        .get_required_value("mandate_type")
+                        .change_context(errors::ConnectorError::MissingRequiredField {
+                            field_name: "mandate_type",
+                        })? {
                         payments::MandateType::SingleUse(details) => details,
                         payments::MandateType::MultiUse(details) => {
                             details.ok_or(errors::ConnectorError::MissingRequiredField {
diff --git a/crates/router/src/connector/stripe/transformers.rs b/crates/router/src/connector/stripe/transformers.rs
index d21381c560b..ea155fe612c 100644
--- a/crates/router/src/connector/stripe/transformers.rs
+++ b/crates/router/src/connector/stripe/transformers.rs
@@ -1096,6 +1096,7 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for PaymentIntentRequest {
                 .and_then(|mandate_details| {
                     mandate_details
                         .customer_acceptance
+                        .as_ref()?
                         .online
                         .as_ref()
                         .map(|online_details| StripeMandateRequest {
diff --git a/crates/router/src/core/mandate.rs b/crates/router/src/core/mandate.rs
index 3273c5a579e..81502477006 100644
--- a/crates/router/src/core/mandate.rs
+++ b/crates/router/src/core/mandate.rs
@@ -210,7 +210,7 @@ where
                         pm_id.get_required_value("payment_method_id")?,
                         mandate_ids,
                         network_txn_id,
-                    ) {
+                    )? {
                         let connector = new_mandate_data.connector.clone();
                         logger::debug!("{:?}", new_mandate_data);
                         resp.request
diff --git a/crates/router/src/core/payment_methods/cards.rs b/crates/router/src/core/payment_methods/cards.rs
index cde48cc4a9a..a8682829b01 100644
--- a/crates/router/src/core/payment_methods/cards.rs
+++ b/crates/router/src/core/payment_methods/cards.rs
@@ -1093,6 +1093,10 @@ pub async fn list_payment_methods(
             api::PaymentMethodListResponse {
                 redirect_url: merchant_account.return_url,
                 payment_methods: payment_method_responses,
+                mandate_payment: payment_attempt
+                    .and_then(|inner| inner.mandate_details)
+                    // The data stored in the payment attempt only corresponds to a setup mandate.
+                    .map(|_mandate_data| api_models::payments::MandateTxnType::NewMandateTxn),
             },
         )))
 }
diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs
index 2890bf26614..06524d89ccb 100644
--- a/crates/router/src/core/payments/helpers.rs
+++ b/crates/router/src/core/payments/helpers.rs
@@ -296,14 +296,6 @@ pub fn validate_mandate(
 }
 
 fn validate_new_mandate_request(req: api::MandateValidationFields) -> RouterResult<()> {
-    let confirm = req.confirm.get_required_value("confirm")?;
-
-    if !confirm {
-        Err(report!(errors::ApiErrorResponse::PreconditionFailed {
-            message: "`confirm` must be `true` for mandates".into()
-        }))?
-    }
-
     let _ = req.customer_id.as_ref().get_required_value("customer_id")?;
 
     let mandate_data = req
@@ -321,8 +313,11 @@ fn validate_new_mandate_request(req: api::MandateValidationFields) -> RouterResu
         }))?
     };
 
-    if (mandate_data.customer_acceptance.acceptance_type == api::AcceptanceType::Online)
-        && mandate_data.customer_acceptance.online.is_none()
+    // Only use this validation if the customer_acceptance is present
+    if mandate_data
+        .customer_acceptance
+        .map(|inner| inner.acceptance_type == api::AcceptanceType::Online && inner.online.is_none())
+        .unwrap_or(false)
     {
         Err(report!(errors::ApiErrorResponse::PreconditionFailed {
             message: "`mandate_data.customer_acceptance.online` is required when \
@@ -332,8 +327,9 @@ fn validate_new_mandate_request(req: api::MandateValidationFields) -> RouterResu
     }
 
     let mandate_details = match mandate_data.mandate_type {
-        api_models::payments::MandateType::SingleUse(details) => Some(details),
-        api_models::payments::MandateType::MultiUse(details) => details,
+        Some(api_models::payments::MandateType::SingleUse(details)) => Some(details),
+        Some(api_models::payments::MandateType::MultiUse(details)) => details,
+        None => None,
     };
     mandate_details.and_then(|md| md.start_date.zip(md.end_date)).map(|(start_date, end_date)|
         utils::when (start_date >= end_date, || {
@@ -1167,7 +1163,7 @@ pub fn generate_mandate(
     payment_method_id: String,
     connector_mandate_id: Option<pii::SecretSerdeValue>,
     network_txn_id: Option<String>,
-) -> Option<storage::MandateNew> {
+) -> CustomResult<Option<storage::MandateNew>, errors::ApiErrorResponse> {
     match (setup_mandate_details, customer) {
         (Some(data), Some(cus)) => {
             let mandate_id = utils::generate_id(consts::ID_LENGTH, "man");
@@ -1175,6 +1171,9 @@ pub fn generate_mandate(
             // The construction of the mandate new must be visible
             let mut new_mandate = storage::MandateNew::default();
 
+            let customer_acceptance = data
+                .customer_acceptance
+                .get_required_value("customer_acceptance")?;
             new_mandate
                 .set_mandate_id(mandate_id)
                 .set_customer_id(cus.customer_id.clone())
@@ -1185,34 +1184,36 @@ pub fn generate_mandate(
                 .set_connector_mandate_ids(connector_mandate_id)
                 .set_network_transaction_id(network_txn_id)
                 .set_customer_ip_address(
-                    data.customer_acceptance
+                    customer_acceptance
                         .get_ip_address()
                         .map(masking::Secret::new),
                 )
-                .set_customer_user_agent(data.customer_acceptance.get_user_agent())
-                .set_customer_accepted_at(Some(data.customer_acceptance.get_accepted_at()));
-
-            Some(match data.mandate_type {
-                api::MandateType::SingleUse(data) => new_mandate
-                    .set_mandate_amount(Some(data.amount))
-                    .set_mandate_currency(Some(data.currency.foreign_into()))
-                    .set_mandate_type(storage_enums::MandateType::SingleUse)
-                    .to_owned(),
+                .set_customer_user_agent(customer_acceptance.get_user_agent())
+                .set_customer_accepted_at(Some(customer_acceptance.get_accepted_at()));
 
-                api::MandateType::MultiUse(op_data) => match op_data {
-                    Some(data) => new_mandate
+            Ok(Some(
+                match data.mandate_type.get_required_value("mandate_type")? {
+                    api::MandateType::SingleUse(data) => new_mandate
                         .set_mandate_amount(Some(data.amount))
                         .set_mandate_currency(Some(data.currency.foreign_into()))
-                        .set_start_date(data.start_date)
-                        .set_end_date(data.end_date)
-                        .set_metadata(data.metadata),
-                    None => &mut new_mandate,
-                }
-                .set_mandate_type(storage_enums::MandateType::MultiUse)
-                .to_owned(),
-            })
+                        .set_mandate_type(storage_enums::MandateType::SingleUse)
+                        .to_owned(),
+
+                    api::MandateType::MultiUse(op_data) => match op_data {
+                        Some(data) => new_mandate
+                            .set_mandate_amount(Some(data.amount))
+                            .set_mandate_currency(Some(data.currency.foreign_into()))
+                            .set_start_date(data.start_date)
+                            .set_end_date(data.end_date)
+                            .set_metadata(data.metadata),
+                        None => &mut new_mandate,
+                    }
+                    .set_mandate_type(storage_enums::MandateType::MultiUse)
+                    .to_owned(),
+                },
+            ))
         }
-        (_, _) => None,
+        (_, _) => Ok(None),
     }
 }
 
@@ -1821,6 +1822,7 @@ impl AttemptType {
             business_sub_label: old_payment_attempt.business_sub_label,
             // If the algorithm is entered in Create call from server side, it needs to be populated here, however it could be overridden from the request.
             straight_through_algorithm: old_payment_attempt.straight_through_algorithm,
+            mandate_details: old_payment_attempt.mandate_details,
         }
     }
 
diff --git a/crates/router/src/core/payments/operations/payment_complete_authorize.rs b/crates/router/src/core/payments/operations/payment_complete_authorize.rs
index ef0b3c215cf..335b620ec93 100644
--- a/crates/router/src/core/payments/operations/payment_complete_authorize.rs
+++ b/crates/router/src/core/payments/operations/payment_complete_authorize.rs
@@ -167,6 +167,16 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsRequest> for Co
         payment_intent.billing_address_id = billing_address.clone().map(|i| i.address_id);
         payment_intent.return_url = request.return_url.as_ref().map(|a| a.to_string());
 
+        // The operation merges mandate data from both request and payment_attempt
+        let setup_mandate = setup_mandate.map(|mandate_data| api_models::payments::MandateData {
+            customer_acceptance: mandate_data.customer_acceptance,
+            mandate_type: payment_attempt
+                .mandate_details
+                .clone()
+                .map(ForeignInto::foreign_into)
+                .or(mandate_data.mandate_type),
+        });
+
         Ok((
             Box::new(self),
             PaymentData {
diff --git a/crates/router/src/core/payments/operations/payment_confirm.rs b/crates/router/src/core/payments/operations/payment_confirm.rs
index 9b9ddc80b7c..b612c88540d 100644
--- a/crates/router/src/core/payments/operations/payment_confirm.rs
+++ b/crates/router/src/core/payments/operations/payment_confirm.rs
@@ -201,6 +201,16 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsRequest> for Pa
             .await
             .transpose()?;
 
+        // The operation merges mandate data from both request and payment_attempt
+        let setup_mandate = setup_mandate.map(|mandate_data| api_models::payments::MandateData {
+            customer_acceptance: mandate_data.customer_acceptance,
+            mandate_type: payment_attempt
+                .mandate_details
+                .clone()
+                .map(ForeignInto::foreign_into)
+                .or(mandate_data.mandate_type),
+        });
+
         Ok((
             Box::new(self),
             PaymentData {
diff --git a/crates/router/src/core/payments/operations/payment_create.rs b/crates/router/src/core/payments/operations/payment_create.rs
index 9746768c19c..c7635871476 100644
--- a/crates/router/src/core/payments/operations/payment_create.rs
+++ b/crates/router/src/core/payments/operations/payment_create.rs
@@ -211,6 +211,15 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsRequest> for Pa
             .await
             .transpose()?;
 
+        // The operation merges mandate data from both request and payment_attempt
+        let setup_mandate = setup_mandate.map(|mandate_data| api_models::payments::MandateData {
+            customer_acceptance: mandate_data.customer_acceptance,
+            mandate_type: mandate_data.mandate_type.or(payment_attempt
+                .mandate_details
+                .clone()
+                .map(ForeignInto::foreign_into)),
+        });
+
         Ok((
             operation,
             PaymentData {
@@ -495,6 +504,10 @@ impl PaymentCreate {
             payment_experience: request.payment_experience.map(ForeignInto::foreign_into),
             payment_method_type: request.payment_method_type.map(ForeignInto::foreign_into),
             payment_method_data: additional_pm_data,
+            mandate_details: request
+                .mandate_data
+                .as_ref()
+                .and_then(|inner| inner.mandate_type.clone().map(ForeignInto::foreign_into)),
             ..storage::PaymentAttemptNew::default()
         })
     }
diff --git a/crates/router/src/core/payments/operations/payment_update.rs b/crates/router/src/core/payments/operations/payment_update.rs
index 6f41bf646d4..7ce1d26881f 100644
--- a/crates/router/src/core/payments/operations/payment_update.rs
+++ b/crates/router/src/core/payments/operations/payment_update.rs
@@ -267,6 +267,15 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsRequest> for Pa
             .await
             .transpose()?;
 
+        // The operation merges mandate data from both request and payment_attempt
+        let setup_mandate = setup_mandate.map(|mandate_data| api_models::payments::MandateData {
+            customer_acceptance: mandate_data.customer_acceptance,
+            mandate_type: mandate_data.mandate_type.or(payment_attempt
+                .mandate_details
+                .clone()
+                .map(ForeignInto::foreign_into)),
+        });
+
         Ok((
             next_operation,
             PaymentData {
diff --git a/crates/router/src/db/payment_attempt.rs b/crates/router/src/db/payment_attempt.rs
index ffb4492f812..331152afce9 100644
--- a/crates/router/src/db/payment_attempt.rs
+++ b/crates/router/src/db/payment_attempt.rs
@@ -265,6 +265,7 @@ impl PaymentAttemptInterface for MockDb {
             payment_method_data: payment_attempt.payment_method_data,
             business_sub_label: payment_attempt.business_sub_label,
             straight_through_algorithm: payment_attempt.straight_through_algorithm,
+            mandate_details: payment_attempt.mandate_details,
         };
         payment_attempts.push(payment_attempt.clone());
         Ok(payment_attempt)
@@ -400,6 +401,7 @@ mod storage {
                         straight_through_algorithm: payment_attempt
                             .straight_through_algorithm
                             .clone(),
+                        mandate_details: payment_attempt.mandate_details.clone(),
                     };
 
                     let field = format!("pa_{}", created_attempt.attempt_id);
diff --git a/crates/router/src/types/transformers.rs b/crates/router/src/types/transformers.rs
index 822121803ab..777af030c57 100644
--- a/crates/router/src/types/transformers.rs
+++ b/crates/router/src/types/transformers.rs
@@ -161,6 +161,55 @@ impl ForeignFrom<storage_enums::AttemptStatus> for storage_enums::IntentStatus {
     }
 }
 
+impl ForeignFrom<api_models::payments::MandateType> for storage_enums::MandateDataType {
+    fn foreign_from(from: api_models::payments::MandateType) -> Self {
+        match from {
+            api_models::payments::MandateType::SingleUse(inner) => {
+                Self::SingleUse(inner.foreign_into())
+            }
+            api_models::payments::MandateType::MultiUse(inner) => {
+                Self::MultiUse(inner.map(ForeignInto::foreign_into))
+            }
+        }
+    }
+}
+impl ForeignFrom<storage_enums::MandateDataType> for api_models::payments::MandateType {
+    fn foreign_from(from: storage_enums::MandateDataType) -> Self {
+        match from {
+            storage_enums::MandateDataType::SingleUse(inner) => {
+                Self::SingleUse(inner.foreign_into())
+            }
+            storage_enums::MandateDataType::MultiUse(inner) => {
+                Self::MultiUse(inner.map(ForeignInto::foreign_into))
+            }
+        }
+    }
+}
+
+impl ForeignFrom<storage_enums::MandateAmountData> for api_models::payments::MandateAmountData {
+    fn foreign_from(from: storage_enums::MandateAmountData) -> Self {
+        Self {
+            amount: from.amount,
+            currency: from.currency.foreign_into(),
+            start_date: from.start_date,
+            end_date: from.end_date,
+            metadata: from.metadata,
+        }
+    }
+}
+
+impl ForeignFrom<api_models::payments::MandateAmountData> for storage_enums::MandateAmountData {
+    fn foreign_from(from: api_models::payments::MandateAmountData) -> Self {
+        Self {
+            amount: from.amount,
+            currency: from.currency.foreign_into(),
+            start_date: from.start_date,
+            end_date: from.end_date,
+            metadata: from.metadata,
+        }
+    }
+}
+
 impl ForeignTryFrom<api_enums::IntentStatus> for storage_enums::EventType {
     type Error = errors::ValidationError;
 
diff --git a/crates/router/src/utils/ext_traits.rs b/crates/router/src/utils/ext_traits.rs
index b79f4bf34b0..a04eec290c6 100644
--- a/crates/router/src/utils/ext_traits.rs
+++ b/crates/router/src/utils/ext_traits.rs
@@ -39,6 +39,8 @@ where
         })
     }
 
+    // This will allow the error message that was generated in this function to point to the call site
+    #[track_caller]
     fn get_required_value(self, field_name: &'static str) -> RouterResult<T> {
         match self {
             Some(v) => Ok(v),
diff --git a/crates/storage_models/src/enums.rs b/crates/storage_models/src/enums.rs
index 1a3eacf9abf..582afcaa82f 100644
--- a/crates/storage_models/src/enums.rs
+++ b/crates/storage_models/src/enums.rs
@@ -16,6 +16,9 @@ pub mod diesel_exports {
 }
 
 pub use common_enums::*;
+use common_utils::pii;
+use diesel::serialize::{Output, ToSql};
+use time::PrimitiveDateTime;
 
 #[derive(
     Clone,
@@ -574,6 +577,54 @@ pub enum MandateType {
     MultiUse,
 }
 
+use diesel::{
+    backend::Backend,
+    deserialize::{FromSql, FromSqlRow},
+    expression::AsExpression,
+    sql_types::Jsonb,
+};
+#[derive(
+    serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq, FromSqlRow, AsExpression,
+)]
+#[diesel(sql_type = Jsonb)]
+#[serde(rename_all = "snake_case")]
+pub enum MandateDataType {
+    SingleUse(MandateAmountData),
+    MultiUse(Option<MandateAmountData>),
+}
+
+impl<DB: Backend> FromSql<Jsonb, DB> for MandateDataType
+where
+    serde_json::Value: FromSql<Jsonb, DB>,
+{
+    fn from_sql(bytes: diesel::backend::RawValue<'_, DB>) -> diesel::deserialize::Result<Self> {
+        let value = <serde_json::Value as FromSql<Jsonb, DB>>::from_sql(bytes)?;
+        Ok(serde_json::from_value(value)?)
+    }
+}
+impl ToSql<Jsonb, diesel::pg::Pg> for MandateDataType
+where
+    serde_json::Value: ToSql<Jsonb, diesel::pg::Pg>,
+{
+    fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, diesel::pg::Pg>) -> diesel::serialize::Result {
+        let value = serde_json::to_value(self)?;
+
+        // the function `reborrow` only works in case of `Pg` backend. But, in case of other backends
+        // please refer to the diesel migration blog:
+        // https://github.com/Diesel-rs/Diesel/blob/master/guide_drafts/migration_guide.md#changed-tosql-implementations
+        <serde_json::Value as ToSql<Jsonb, diesel::pg::Pg>>::to_sql(&value, &mut out.reborrow())
+    }
+}
+
+#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
+pub struct MandateAmountData {
+    pub amount: i64,
+    pub currency: Currency,
+    pub start_date: Option<PrimitiveDateTime>,
+    pub end_date: Option<PrimitiveDateTime>,
+    pub metadata: Option<pii::SecretSerdeValue>,
+}
+
 #[derive(
     Clone,
     Copy,
diff --git a/crates/storage_models/src/payment_attempt.rs b/crates/storage_models/src/payment_attempt.rs
index d6210c554c9..e4154860ec1 100644
--- a/crates/storage_models/src/payment_attempt.rs
+++ b/crates/storage_models/src/payment_attempt.rs
@@ -46,6 +46,8 @@ pub struct PaymentAttempt {
     pub payment_method_data: Option<serde_json::Value>,
     pub business_sub_label: Option<String>,
     pub straight_through_algorithm: Option<serde_json::Value>,
+    // providing a location to store mandate details intermediately for transaction
+    pub mandate_details: Option<storage_enums::MandateDataType>,
 }
 
 #[derive(
@@ -91,6 +93,7 @@ pub struct PaymentAttemptNew {
     pub payment_method_data: Option<serde_json::Value>,
     pub business_sub_label: Option<String>,
     pub straight_through_algorithm: Option<serde_json::Value>,
+    pub mandate_details: Option<storage_enums::MandateDataType>,
 }
 
 #[derive(Debug, Clone, Serialize, Deserialize)]
diff --git a/crates/storage_models/src/schema.rs b/crates/storage_models/src/schema.rs
index 062b787c9bb..eaf872f1c96 100644
--- a/crates/storage_models/src/schema.rs
+++ b/crates/storage_models/src/schema.rs
@@ -321,6 +321,7 @@ diesel::table! {
         payment_method_data -> Nullable<Jsonb>,
         business_sub_label -> Nullable<Varchar>,
         straight_through_algorithm -> Nullable<Jsonb>,
+        mandate_details -> Nullable<Jsonb>,
     }
 }
 
diff --git a/migrations/2023-05-16-145008_mandate_data_in_pa/down.sql b/migrations/2023-05-16-145008_mandate_data_in_pa/down.sql
new file mode 100644
index 00000000000..610f84431bc
--- /dev/null
+++ b/migrations/2023-05-16-145008_mandate_data_in_pa/down.sql
@@ -0,0 +1,2 @@
+-- This file should undo anything in `up.sql`
+ALTER TABLE payment_attempt DROP COLUMN mandate_details;
diff --git a/migrations/2023-05-16-145008_mandate_data_in_pa/up.sql b/migrations/2023-05-16-145008_mandate_data_in_pa/up.sql
new file mode 100644
index 00000000000..b72efc7d9eb
--- /dev/null
+++ b/migrations/2023-05-16-145008_mandate_data_in_pa/up.sql
@@ -0,0 +1,2 @@
+-- Your SQL goes here
+ALTER TABLE payment_attempt ADD COLUMN mandate_details JSONB;