Issue:
Bug: [REFACTOR] accept customer details in customer object



Currently, for a payment, the `CustomerDetails` such as email, phone, name are accepted in the `PaymentsRequest` struct 
https://github.com/juspay/hyperswitch/blob/795500797d1061630b5ca493187a4e19d98d26c0/crates/api_models/src/payments.rs#L59

It is not clear that these fields are related to the customer. One may think that the fields are for a payment and one time use only. But these are stored in the `Customers` table.

A better api design would be to accept the customer details in a `customers` field in `PaymentsRequest`.

It would look something like this
```json
{
  "customer" : {
    "id": "cus_1235",
    "email" : "example@example.com"
  }
}
```

Pull request description:
## Type of Change
<!-- Put an `x` in the boxes that apply -->

- [x] Bugfix

## Description
<!-- Describe your changes in detail -->
This PR will support sending the customer details in the `customers` object in `PaymentsRequest` in all three endpoints 
- payments-create
- payments-update
- payments-confirm

The changes are added in a backwards compatible manner, the older fields will be deprecated soon.

### Additional Changes

- [x] This PR modifies the API contract

<!--
Provide links to the files with corresponding changes.

## 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 its an obvious bug or documentation fix
that will have little conversation).
-->
Fixes #1446  

## 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 a payment with customer details outside.
<img width="754" alt="Screenshot 2023-06-15 at 3 28 27 PM" src="https://github.com/juspay/hyperswitch/assets/48803246/e6495cd9-1bd6-4f1f-a800-73847ee6cb72">

- Create a payment with customer details in customers field.
<img width="738" alt="Screenshot 2023-06-15 at 3 27 19 PM" src="https://github.com/juspay/hyperswitch/assets/48803246/f470f949-0390-46f0-8219-310d9c93d957">

- Create ambiguity by passing the same field in both the places
<img width="1030" alt="Screenshot 2023-06-16 at 4 13 48 PM" src="https://github.com/juspay/hyperswitch/assets/48803246/985dcf41-0706-4685-afa1-f9d90a018b1e">


## 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

Testing:

- Create a payment with customer details outside.
<img width="754" alt="Screenshot 2023-06-15 at 3 28 27 PM" src="https://github.com/juspay/hyperswitch/assets/48803246/e6495cd9-1bd6-4f1f-a800-73847ee6cb72">

- Create a payment with customer details in customers field.
<img width="738" alt="Screenshot 2023-06-15 at 3 27 19 PM" src="https://github.com/juspay/hyperswitch/assets/48803246/f470f949-0390-46f0-8219-310d9c93d957">

- Create ambiguity by passing the same field in both the places
<img width="1030" alt="Screenshot 2023-06-16 at 4 13 48 PM" src="https://github.com/juspay/hyperswitch/assets/48803246/985dcf41-0706-4685-afa1-f9d90a018b1e">

Patch:
diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs
index 173b795efa3..53ce196737a 100644
--- a/crates/api_models/src/payments.rs
+++ b/crates/api_models/src/payments.rs
@@ -45,6 +45,28 @@ pub struct BankCodeResponse {
     pub eligible_connectors: Vec<String>,
 }
 
+#[derive(Default, Debug, serde::Deserialize, serde::Serialize, Clone, ToSchema)]
+pub struct CustomerDetails {
+    /// The identifier for the customer.
+    pub id: String,
+
+    /// The customer's name
+    #[schema(max_length = 255, value_type = Option<String>, example = "John Doe")]
+    pub name: Option<Secret<String>>,
+
+    /// The customer's email address
+    #[schema(max_length = 255, value_type = Option<String>, example = "johntest@test.com")]
+    pub email: Option<Email>,
+
+    /// The customer's phone number
+    #[schema(value_type = Option<String>, max_length = 10, example = "3141592653")]
+    pub phone: Option<Secret<String>>,
+
+    /// The country code for the customer's phone number
+    #[schema(max_length = 2, example = "+1")]
+    pub phone_country_code: Option<String>,
+}
+
 #[derive(
     Default,
     Debug,
@@ -114,23 +136,33 @@ pub struct PaymentsRequest {
     #[schema(default = false, example = true)]
     pub confirm: Option<bool>,
 
-    /// The identifier for the customer object. If not provided the customer ID will be autogenerated.
+    /// The details of a customer for this payment
+    /// This will create the customer if `customer.id` does not exist
+    /// If customer id already exists, it will update the details of the customer
+    pub customer: Option<CustomerDetails>,
+
+    /// The identifier for the customer object.
+    /// This field will be deprecated soon, use the customer object instead
     #[schema(max_length = 255, example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")]
     pub customer_id: Option<String>,
 
-    /// description: The customer's email address
+    /// The customer's email address
+    /// This field will be deprecated soon, use the customer object instead
     #[schema(max_length = 255, value_type = Option<String>, example = "johntest@test.com")]
     pub email: Option<Email>,
 
     /// description: The customer's name
+    /// This field will be deprecated soon, use the customer object instead
     #[schema(value_type = Option<String>, max_length = 255, example = "John Test")]
     pub name: Option<Secret<String>>,
 
     /// The customer's phone number
+    /// This field will be deprecated soon, use the customer object instead
     #[schema(value_type = Option<String>, max_length = 255, example = "3141592653")]
     pub phone: Option<Secret<String>>,
 
     /// The country code for the customer phone number
+    /// This field will be deprecated soon, use the customer object instead
     #[schema(max_length = 255, example = "+1")]
     pub phone_country_code: Option<String>,
 
@@ -303,27 +335,6 @@ pub struct VerifyRequest {
     pub merchant_connector_details: Option<admin::MerchantConnectorDetailsWrap>,
 }
 
-impl From<PaymentsRequest> for VerifyRequest {
-    fn from(item: PaymentsRequest) -> Self {
-        Self {
-            client_secret: item.client_secret,
-            merchant_id: item.merchant_id,
-            customer_id: item.customer_id,
-            email: item.email,
-            name: item.name,
-            phone: item.phone,
-            phone_country_code: item.phone_country_code,
-            payment_method: item.payment_method,
-            payment_method_data: item.payment_method_data,
-            payment_token: item.payment_token,
-            mandate_data: item.mandate_data,
-            setup_future_usage: item.setup_future_usage,
-            off_session: item.off_session,
-            merchant_connector_details: item.merchant_connector_details,
-        }
-    }
-}
-
 #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
 #[serde(rename_all = "snake_case")]
 pub enum MandateTxnType {
@@ -1478,7 +1489,12 @@ impl From<&PaymentsRequest> for MandateValidationFields {
         Self {
             mandate_id: req.mandate_id.clone(),
             confirm: req.confirm,
-            customer_id: req.customer_id.clone(),
+            customer_id: req
+                .customer
+                .as_ref()
+                .map(|customer_details| &customer_details.id)
+                .or(req.customer_id.as_ref())
+                .map(ToOwned::to_owned),
             mandate_data: req.mandate_data.clone(),
             setup_future_usage: req.setup_future_usage,
             off_session: req.off_session,
diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs
index 235ef14e7c1..badb8d948bf 100644
--- a/crates/router/src/core/payments/helpers.rs
+++ b/crates/router/src/core/payments/helpers.rs
@@ -96,7 +96,7 @@ pub async fn get_address_for_payment_request(
     req_address: Option<&api::Address>,
     address_id: Option<&str>,
     merchant_id: &str,
-    customer_id: &Option<String>,
+    customer_id: Option<&String>,
 ) -> CustomResult<Option<domain::Address>, errors::ApiErrorResponse> {
     let key = types::get_merchant_enc_key(db, merchant_id.to_string())
         .await
@@ -179,7 +179,7 @@ pub async fn get_address_for_payment_request(
                 }
                 None => {
                     // generate a new address here
-                    let customer_id = customer_id.as_deref().get_required_value("customer_id")?;
+                    let customer_id = customer_id.get_required_value("customer_id")?;
 
                     let address_details = address.address.clone().unwrap_or_default();
                     Some(
@@ -460,7 +460,7 @@ fn validate_new_mandate_request(
     is_confirm_operation: bool,
 ) -> RouterResult<()> {
     // We need not check for customer_id in the confirm request if it is already passed
-    //in create request
+    // in create request
 
     fp_utils::when(!is_confirm_operation && req.customer_id.is_none(), || {
         Err(report!(errors::ApiErrorResponse::PreconditionFailed {
@@ -810,6 +810,109 @@ pub async fn get_customer_from_details<F: Clone>(
     }
 }
 
+// Checks if the inner values of two options are not equal and throws appropriate error
+fn validate_options_for_inequality<T: PartialEq>(
+    first_option: Option<&T>,
+    second_option: Option<&T>,
+    field_name: &str,
+) -> Result<(), errors::ApiErrorResponse> {
+    fp_utils::when(
+        first_option
+            .zip(second_option)
+            .map(|(value1, value2)| value1 != value2)
+            .unwrap_or(false),
+        || {
+            Err(errors::ApiErrorResponse::PreconditionFailed {
+                message: format!("The field name `{field_name}` sent in both places is ambiguous"),
+            })
+        },
+    )
+}
+
+// Checks if the customer details are passed in both places
+// If so, raise an error
+pub fn validate_customer_details_in_request(
+    request: &api_models::payments::PaymentsRequest,
+) -> Result<(), errors::ApiErrorResponse> {
+    if let Some(customer_details) = request.customer.as_ref() {
+        validate_options_for_inequality(
+            request.customer_id.as_ref(),
+            Some(&customer_details.id),
+            "customer_id",
+        )?;
+
+        validate_options_for_inequality(
+            request.email.as_ref(),
+            customer_details.email.as_ref(),
+            "email",
+        )?;
+
+        validate_options_for_inequality(
+            request.name.as_ref(),
+            customer_details.name.as_ref(),
+            "name",
+        )?;
+
+        validate_options_for_inequality(
+            request.phone.as_ref(),
+            customer_details.phone.as_ref(),
+            "phone",
+        )?;
+
+        validate_options_for_inequality(
+            request.phone_country_code.as_ref(),
+            customer_details.phone_country_code.as_ref(),
+            "phone_country_code",
+        )?;
+    }
+
+    Ok(())
+}
+
+/// Get the customer details from customer field if present
+/// or from the individual fields in `PaymentsRequest`
+pub fn get_customer_details_from_request(
+    request: &api_models::payments::PaymentsRequest,
+) -> CustomerDetails {
+    let customer_id = request
+        .customer
+        .as_ref()
+        .map(|customer_details| customer_details.id.clone())
+        .or(request.customer_id.clone());
+
+    let customer_name = request
+        .customer
+        .as_ref()
+        .and_then(|customer_details| customer_details.name.clone())
+        .or(request.name.clone());
+
+    let customer_email = request
+        .customer
+        .as_ref()
+        .and_then(|customer_details| customer_details.email.clone())
+        .or(request.email.clone());
+
+    let customer_phone = request
+        .customer
+        .as_ref()
+        .and_then(|customer_details| customer_details.phone.clone())
+        .or(request.phone.clone());
+
+    let customer_phone_code = request
+        .customer
+        .as_ref()
+        .and_then(|customer_details| customer_details.phone_country_code.clone())
+        .or(request.phone_country_code.clone());
+
+    CustomerDetails {
+        customer_id,
+        name: customer_name,
+        email: customer_email,
+        phone: customer_phone,
+        phone_country_code: customer_phone_code,
+    }
+}
+
 pub async fn get_connector_default(
     _state: &AppState,
     request_connector: Option<serde_json::Value>,
diff --git a/crates/router/src/core/payments/operations/payment_cancel.rs b/crates/router/src/core/payments/operations/payment_cancel.rs
index 54cc83581ae..f06bf9e252a 100644
--- a/crates/router/src/core/payments/operations/payment_cancel.rs
+++ b/crates/router/src/core/payments/operations/payment_cancel.rs
@@ -80,7 +80,7 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsCancelRequest>
             None,
             payment_intent.shipping_address_id.as_deref(),
             merchant_id,
-            &payment_intent.customer_id,
+            payment_intent.customer_id.as_ref(),
         )
         .await?;
         let billing_address = helpers::get_address_for_payment_request(
@@ -88,7 +88,7 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsCancelRequest>
             None,
             payment_intent.billing_address_id.as_deref(),
             merchant_id,
-            &payment_intent.customer_id,
+            payment_intent.customer_id.as_ref(),
         )
         .await?;
 
diff --git a/crates/router/src/core/payments/operations/payment_capture.rs b/crates/router/src/core/payments/operations/payment_capture.rs
index 37f2ccb9d1b..cedc9d7a6d3 100644
--- a/crates/router/src/core/payments/operations/payment_capture.rs
+++ b/crates/router/src/core/payments/operations/payment_capture.rs
@@ -99,7 +99,7 @@ impl<F: Send + Clone> GetTracker<F, payments::PaymentData<F>, api::PaymentsCaptu
             None,
             payment_intent.shipping_address_id.as_deref(),
             merchant_id,
-            &payment_intent.customer_id,
+            payment_intent.customer_id.as_ref(),
         )
         .await?;
 
@@ -108,7 +108,7 @@ impl<F: Send + Clone> GetTracker<F, payments::PaymentData<F>, api::PaymentsCaptu
             None,
             payment_intent.billing_address_id.as_deref(),
             merchant_id,
-            &payment_intent.customer_id,
+            payment_intent.customer_id.as_ref(),
         )
         .await?;
 
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 fd06d9a6727..6f7bb3494ec 100644
--- a/crates/router/src/core/payments/operations/payment_complete_authorize.rs
+++ b/crates/router/src/core/payments/operations/payment_complete_authorize.rs
@@ -135,7 +135,7 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsRequest> for Co
             request.shipping.as_ref(),
             payment_intent.shipping_address_id.as_deref(),
             merchant_id,
-            &payment_intent.customer_id,
+            payment_intent.customer_id.as_ref(),
         )
         .await?;
         let billing_address = helpers::get_address_for_payment_request(
@@ -143,7 +143,7 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsRequest> for Co
             request.billing.as_ref(),
             payment_intent.billing_address_id.as_deref(),
             merchant_id,
-            &payment_intent.customer_id,
+            payment_intent.customer_id.as_ref(),
         )
         .await?;
 
diff --git a/crates/router/src/core/payments/operations/payment_confirm.rs b/crates/router/src/core/payments/operations/payment_confirm.rs
index b136610f726..00951faee3f 100644
--- a/crates/router/src/core/payments/operations/payment_confirm.rs
+++ b/crates/router/src/core/payments/operations/payment_confirm.rs
@@ -122,6 +122,8 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsRequest> for Pa
                 field_name: "browser_info",
             })?;
 
+        let customer_details = helpers::get_customer_details_from_request(request);
+
         let token = token.or_else(|| payment_attempt.payment_token.clone());
 
         helpers::validate_pm_or_token_given(
@@ -159,7 +161,7 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsRequest> for Pa
             &payment_intent
                 .customer_id
                 .clone()
-                .or_else(|| request.customer_id.clone()),
+                .or_else(|| customer_details.customer_id.clone()),
         )?;
 
         let shipping_address = helpers::get_address_for_payment_request(
@@ -167,7 +169,10 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsRequest> for Pa
             request.shipping.as_ref(),
             payment_intent.shipping_address_id.as_deref(),
             merchant_id,
-            &payment_intent.customer_id,
+            payment_intent
+                .customer_id
+                .as_ref()
+                .or(customer_details.customer_id.as_ref()),
         )
         .await?;
         let billing_address = helpers::get_address_for_payment_request(
@@ -175,7 +180,10 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsRequest> for Pa
             request.billing.as_ref(),
             payment_intent.billing_address_id.as_deref(),
             merchant_id,
-            &payment_intent.customer_id,
+            payment_intent
+                .customer_id
+                .as_ref()
+                .or(customer_details.customer_id.as_ref()),
         )
         .await?;
 
@@ -255,13 +263,7 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsRequest> for Pa
                 ephemeral_key: None,
                 redirect_response: None,
             },
-            Some(CustomerDetails {
-                customer_id: request.customer_id.clone(),
-                name: request.name.clone(),
-                email: request.email.clone(),
-                phone: request.phone.clone(),
-                phone_country_code: request.phone_country_code.clone(),
-            }),
+            Some(customer_details),
         ))
     }
 }
@@ -472,6 +474,9 @@ impl<F: Send + Clone> ValidateRequest<F, api::PaymentsRequest> for PaymentConfir
         {
             Err(errors::ApiErrorResponse::NotSupported { message: "order_details cannot be present both inside and outside metadata in payments request".to_string() })?
         }
+
+        helpers::validate_customer_details_in_request(request)?;
+
         let given_payment_id = match &request.payment_id {
             Some(id_type) => Some(
                 id_type
diff --git a/crates/router/src/core/payments/operations/payment_create.rs b/crates/router/src/core/payments/operations/payment_create.rs
index ee4af6244b0..b4b75cd815c 100644
--- a/crates/router/src/core/payments/operations/payment_create.rs
+++ b/crates/router/src/core/payments/operations/payment_create.rs
@@ -74,12 +74,14 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsRequest> for Pa
             )
             .await?;
 
+        let customer_details = helpers::get_customer_details_from_request(request);
+
         let shipping_address = helpers::get_address_for_payment_request(
             db,
             request.shipping.as_ref(),
             None,
             merchant_id,
-            &request.customer_id,
+            customer_details.customer_id.as_ref(),
         )
         .await?;
 
@@ -88,7 +90,7 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsRequest> for Pa
             request.billing.as_ref(),
             None,
             merchant_id,
-            &request.customer_id,
+            customer_details.customer_id.as_ref(),
         )
         .await?;
 
@@ -256,13 +258,7 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsRequest> for Pa
                 ephemeral_key,
                 redirect_response: None,
             },
-            Some(CustomerDetails {
-                customer_id: request.customer_id.clone(),
-                name: request.name.clone(),
-                email: request.email.clone(),
-                phone: request.phone.clone(),
-                phone_country_code: request.phone_country_code.clone(),
-            }),
+            Some(customer_details),
         ))
     }
 }
@@ -423,6 +419,9 @@ impl<F: Send + Clone> ValidateRequest<F, api::PaymentsRequest> for PaymentCreate
         {
             Err(errors::ApiErrorResponse::NotSupported { message: "order_details cannot be present both inside and outside metadata in payments request".to_string() })?
         }
+
+        helpers::validate_customer_details_in_request(request)?;
+
         let given_payment_id = match &request.payment_id {
             Some(id_type) => Some(
                 id_type
diff --git a/crates/router/src/core/payments/operations/payment_session.rs b/crates/router/src/core/payments/operations/payment_session.rs
index 3c1e9529113..5fe496aa281 100644
--- a/crates/router/src/core/payments/operations/payment_session.rs
+++ b/crates/router/src/core/payments/operations/payment_session.rs
@@ -87,7 +87,7 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsSessionRequest>
             None,
             payment_intent.shipping_address_id.as_deref(),
             merchant_id,
-            &payment_intent.customer_id,
+            payment_intent.customer_id.as_ref(),
         )
         .await?;
 
@@ -96,7 +96,7 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsSessionRequest>
             None,
             payment_intent.billing_address_id.as_deref(),
             merchant_id,
-            &payment_intent.customer_id,
+            payment_intent.customer_id.as_ref(),
         )
         .await?;
 
diff --git a/crates/router/src/core/payments/operations/payment_start.rs b/crates/router/src/core/payments/operations/payment_start.rs
index c197097e7ac..5cb6613a28b 100644
--- a/crates/router/src/core/payments/operations/payment_start.rs
+++ b/crates/router/src/core/payments/operations/payment_start.rs
@@ -81,7 +81,7 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsStartRequest> f
             None,
             payment_intent.shipping_address_id.as_deref(),
             merchant_id,
-            &payment_intent.customer_id,
+            payment_intent.customer_id.as_ref(),
         )
         .await?;
         let billing_address = helpers::get_address_for_payment_request(
@@ -89,7 +89,7 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsStartRequest> f
             None,
             payment_intent.billing_address_id.as_deref(),
             merchant_id,
-            &payment_intent.customer_id,
+            payment_intent.customer_id.as_ref(),
         )
         .await?;
 
diff --git a/crates/router/src/core/payments/operations/payment_update.rs b/crates/router/src/core/payments/operations/payment_update.rs
index b36fe6ea966..15ef40f34e5 100644
--- a/crates/router/src/core/payments/operations/payment_update.rs
+++ b/crates/router/src/core/payments/operations/payment_update.rs
@@ -107,6 +107,7 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsRequest> for Pa
         };
 
         payment_attempt.payment_method = payment_method_type.or(payment_attempt.payment_method);
+        let customer_details = helpers::get_customer_details_from_request(request);
 
         let amount = request
             .amount
@@ -120,7 +121,7 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsRequest> for Pa
                 &payment_intent
                     .customer_id
                     .clone()
-                    .or_else(|| request.customer_id.clone()),
+                    .or_else(|| customer_details.customer_id.clone()),
             )?;
         }
 
@@ -129,7 +130,10 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsRequest> for Pa
             request.shipping.as_ref(),
             payment_intent.shipping_address_id.as_deref(),
             merchant_id,
-            &payment_intent.customer_id,
+            payment_intent
+                .customer_id
+                .as_ref()
+                .or(customer_details.customer_id.as_ref()),
         )
         .await?;
         let billing_address = helpers::get_address_for_payment_request(
@@ -137,7 +141,10 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsRequest> for Pa
             request.billing.as_ref(),
             payment_intent.billing_address_id.as_deref(),
             merchant_id,
-            &payment_intent.customer_id,
+            payment_intent
+                .customer_id
+                .as_ref()
+                .or(customer_details.customer_id.as_ref()),
         )
         .await?;
 
@@ -281,6 +288,7 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsRequest> for Pa
                 .clone()
                 .map(ForeignInto::foreign_into)),
         });
+
         Ok((
             next_operation,
             PaymentData {
@@ -312,13 +320,7 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsRequest> for Pa
                 ephemeral_key: None,
                 redirect_response: None,
             },
-            Some(CustomerDetails {
-                customer_id: request.customer_id.clone(),
-                name: request.name.clone(),
-                email: request.email.clone(),
-                phone: request.phone.clone(),
-                phone_country_code: request.phone_country_code.clone(),
-            }),
+            Some(customer_details),
         ))
     }
 }
@@ -526,6 +528,9 @@ impl<F: Send + Clone> ValidateRequest<F, api::PaymentsRequest> for PaymentUpdate
         {
             Err(errors::ApiErrorResponse::NotSupported { message: "order_details cannot be present both inside and outside metadata in payments request".to_string() })?
         }
+
+        helpers::validate_customer_details_in_request(request)?;
+
         let given_payment_id = match &request.payment_id {
             Some(id_type) => Some(
                 id_type
diff --git a/crates/router/src/openapi.rs b/crates/router/src/openapi.rs
index 1ae4cce0334..9e870c5cd25 100644
--- a/crates/router/src/openapi.rs
+++ b/crates/router/src/openapi.rs
@@ -241,6 +241,7 @@ Never share your secret api keys. Keep them guarded and secure.
         api_models::mandates::MandateResponse,
         api_models::mandates::MandateCardDetails,
         api_models::ephemeral_key::EphemeralKeyCreateResponse,
+        api_models::payments::CustomerDetails,
         crate::types::api::admin::MerchantAccountResponse,
         crate::types::api::admin::MerchantConnectorId,
         crate::types::api::admin::MerchantDetails,