Bug:  [Fix] 3DS mandates, for the connector _mandate_details to be stored in the payment_methods table



### Feature Description

After complete_authorize, or Psync the connector_mandate_details wasn't getting stored in the PaymentMethods table. Hence the recurringMandate would fail with the error Payment Methods not found
In this PR , we fix the 3ds mandate payments

### Possible Implementation

Add validations for such

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

None
diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs
index 6e8e4e11fdd..48a6d411780 100644
--- a/crates/router/src/core/payments/helpers.rs
+++ b/crates/router/src/core/payments/helpers.rs
@@ -417,6 +417,7 @@ pub async fn get_token_pm_type_mandate_details(
     mandate_type: Option<api::MandateTransactionType>,
     merchant_account: &domain::MerchantAccount,
     merchant_key_store: &domain::MerchantKeyStore,
+    payment_method_id: Option<String>,
 ) -> RouterResult<MandateGenericData> {
     let mandate_data = request.mandate_data.clone().map(MandateData::foreign_from);
     let (
@@ -529,15 +530,27 @@ pub async fn get_token_pm_type_mandate_details(
                 }
             }
         }
-        None => (
-            request.payment_token.to_owned(),
-            request.payment_method,
-            request.payment_method_type,
-            mandate_data,
-            None,
-            None,
-            None,
-        ),
+        None => {
+            let payment_method_info = payment_method_id
+                .async_map(|payment_method_id| async move {
+                    state
+                        .store
+                        .find_payment_method(&payment_method_id, merchant_account.storage_scheme)
+                        .await
+                        .to_not_found_response(errors::ApiErrorResponse::PaymentMethodNotFound)
+                })
+                .await
+                .transpose()?;
+            (
+                request.payment_token.to_owned(),
+                request.payment_method,
+                request.payment_method_type,
+                mandate_data,
+                None,
+                None,
+                payment_method_info,
+            )
+        }
     };
     Ok(MandateGenericData {
         token: payment_token,
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 e9e0ec36a73..89bdd0b84cc 100644
--- a/crates/router/src/core/payments/operations/payment_complete_authorize.rs
+++ b/crates/router/src/core/payments/operations/payment_complete_authorize.rs
@@ -119,6 +119,7 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve>
             mandate_type.to_owned(),
             merchant_account,
             key_store,
+            payment_attempt.payment_method_id.clone(),
         )
         .await?;
         let token = token.or_else(|| payment_attempt.payment_token.clone());
diff --git a/crates/router/src/core/payments/operations/payment_confirm.rs b/crates/router/src/core/payments/operations/payment_confirm.rs
index 7af7e13b9fc..de0e6604098 100644
--- a/crates/router/src/core/payments/operations/payment_confirm.rs
+++ b/crates/router/src/core/payments/operations/payment_confirm.rs
@@ -484,6 +484,7 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve>
                     m_mandate_type,
                     &m_merchant_account,
                     &m_key_store,
+                    None,
                 )
                 .await
             }
diff --git a/crates/router/src/core/payments/operations/payment_create.rs b/crates/router/src/core/payments/operations/payment_create.rs
index 99a387fc8d1..5d510f2afd4 100644
--- a/crates/router/src/core/payments/operations/payment_create.rs
+++ b/crates/router/src/core/payments/operations/payment_create.rs
@@ -134,6 +134,7 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve>
             mandate_type.clone(),
             merchant_account,
             merchant_key_store,
+            None,
         )
         .await?;
 
diff --git a/crates/router/src/core/payments/operations/payment_response.rs b/crates/router/src/core/payments/operations/payment_response.rs
index 9bb8119d40c..b96cf483236 100644
--- a/crates/router/src/core/payments/operations/payment_response.rs
+++ b/crates/router/src/core/payments/operations/payment_response.rs
@@ -17,7 +17,7 @@ use crate::{
     core::{
         errors::{self, RouterResult, StorageErrorExt},
         mandate,
-        payment_methods::PaymentMethodRetrieve,
+        payment_methods::{self, PaymentMethodRetrieve},
         payments::{
             self,
             helpers::{
@@ -886,6 +886,8 @@ async fn payment_response_update_tracker<F: Clone, T: types::Capturable>(
         .in_current_span(),
     );
 
+    // When connector requires redirection for mandate creation it can update the connector mandate_id in payment_methods during Psync and CompleteAuthorize
+
     let flow_name = core_utils::get_flow_name::<F>()?;
     if flow_name == "PSync" || flow_name == "CompleteAuthorize" {
         let connector_mandate_id = match router_data.response.clone() {
@@ -904,23 +906,31 @@ async fn payment_response_update_tracker<F: Clone, T: types::Capturable>(
             },
             Err(_) => None,
         };
-        if let Some(ref payment_method) = payment_data.payment_method_info {
-            payments::tokenization::update_connector_mandate_details_in_payment_method(
+        if let Some(payment_method) = payment_data.payment_method_info.clone() {
+            let connector_mandate_details =
+                payments::tokenization::update_connector_mandate_details_in_payment_method(
+                    payment_method.clone(),
+                    payment_method.payment_method_type,
+                    Some(payment_data.payment_attempt.amount),
+                    payment_data.payment_attempt.currency,
+                    payment_data.payment_attempt.merchant_connector_id.clone(),
+                    connector_mandate_id,
+                )?;
+            payment_methods::cards::update_payment_method_connector_mandate_details(
+                &*state.store,
                 payment_method.clone(),
-                payment_method.payment_method_type,
-                Some(payment_data.payment_attempt.amount),
-                payment_data.payment_attempt.currency,
-                payment_data.payment_attempt.merchant_connector_id.clone(),
-                connector_mandate_id,
+                connector_mandate_details,
+                storage_scheme,
             )
-            .await?;
+            .await
+            .change_context(errors::ApiErrorResponse::InternalServerError)
+            .attach_printable("Failed to update payment method in db")?
         }
     }
-
     // When connector requires redirection for mandate creation it can update the connector mandate_id during Psync and CompleteAuthorize
     let m_db = state.clone().store;
-    let m_payment_method_id = payment_data.payment_attempt.payment_method_id.clone();
     let m_router_data_merchant_id = router_data.merchant_id.clone();
+    let m_payment_method_id = payment_data.payment_attempt.payment_method_id.clone();
     let m_payment_data_mandate_id =
         payment_data
             .payment_attempt
diff --git a/crates/router/src/core/payments/operations/payment_update.rs b/crates/router/src/core/payments/operations/payment_update.rs
index bfd056e8f97..b201453068b 100644
--- a/crates/router/src/core/payments/operations/payment_update.rs
+++ b/crates/router/src/core/payments/operations/payment_update.rs
@@ -146,6 +146,7 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve>
             mandate_type.to_owned(),
             merchant_account,
             key_store,
+            None,
         )
         .await?;
         helpers::validate_amount_to_capture_and_capture_method(Some(&payment_attempt), request)?;
diff --git a/crates/router/src/core/payments/tokenization.rs b/crates/router/src/core/payments/tokenization.rs
index c6f6a23d0a2..62acfb449eb 100644
--- a/crates/router/src/core/payments/tokenization.rs
+++ b/crates/router/src/core/payments/tokenization.rs
@@ -282,8 +282,7 @@ where
                                                 currency,
                                                 connector.merchant_connector_id.clone(),
                                                 connector_mandate_id.clone(),
-                                            )
-                                            .await?;
+                                            )?;
 
                                         payment_methods::cards::update_payment_method_connector_mandate_details(db, pm, connector_mandate_details, merchant_account.storage_scheme).await.change_context(
                                         errors::ApiErrorResponse::InternalServerError,
@@ -373,8 +372,7 @@ where
                                                     currency,
                                                     connector.merchant_connector_id.clone(),
                                                     connector_mandate_id.clone(),
-                                                )
-                                                .await?;
+                                                )?;
 
                                             payment_methods::cards::update_payment_method_connector_mandate_details(db, pm.clone(), connector_mandate_details, merchant_account.storage_scheme).await.change_context(
                                             errors::ApiErrorResponse::InternalServerError,
@@ -809,7 +807,7 @@ pub fn add_connector_mandate_details_in_payment_method(
     }
 }
 
-pub async fn update_connector_mandate_details_in_payment_method(
+pub fn update_connector_mandate_details_in_payment_method(
     payment_method: diesel_models::PaymentMethod,
     payment_method_type: Option<storage_enums::PaymentMethodType>,
     authorized_amount: Option<i64>,
@@ -866,5 +864,6 @@ pub async fn update_connector_mandate_details_in_payment_method(
         .transpose()
         .change_context(errors::ApiErrorResponse::InternalServerError)
         .attach_printable("Unable to serialize customer acceptance to value")?;
+
     Ok(connector_mandate_details)
 }

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

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

## Description
After complete_authorize, or Psync the connector_mandate_details wasn't getting stored in the PaymentMethods table. Hence the recurringMandate would fail with the error `Payment Methods not found`
In this PR , we fix the 3ds mandate payments

### 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 a 3DS off-session payment/
- > `Create`
```
curl --location 'http://localhost:8080/payments' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: dev_njRmxwKH6ktMs37HQLB07DhgK1sFgROrpNiUiUgiSKyIzpoz3Wa5vKDCWc6PQlLe' \
--data-raw '{
    "amount": 6777,
    "currency": "USD",
    "confirm": false,
    
    "customer_id": "new-c77",
    "capture_method": "automatic",
    "capture_on": "2022-09-10T10:11:12Z",
    "authentication_type": "three_ds",
    "return_url": "https://google.com",
     "email": "something@gmail.com",
    "name": "Joseph Doe",
    "phone": "999999999",
    "phone_country_code": "+65",
    "description": "Its my first payment request",   
"setup_future_usage":"off_session",
    "billing": {
        "address": {
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "city": "San Fransico",
            "state": "California",
            "zip": "94122",
            "country": "US",
            "first_name": "joseph",
            "last_name": "Doe"
        },
        "phone": {
            "number": "8056594427",
            "country_code": "+91"
        }
    },

    "shipping": {
        "address": {
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "city": "San Fransico",
            "state": "California",
            "zip": "94122",
            "country": "US",
            "first_name": "joseph",
            "last_name": "Doe"
        },
        "phone": {
            "number": "8056594427",
            "country_code": "+91"
        }
    },
    "connector_metadata": {
        "noon": {
            "order_category": "pay"
        }
    }
}'
```
-> `Confirm`
```
curl --location 'http://localhost:8080/payments/pay_UvTYFgVG2EelieqJlHZu/confirm' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: dev_njRmxwKH6ktMs37HQLB07DhgK1sFgROrpNiUiUgiSKyIzpoz3Wa5vKDCWc6PQlLe' \
--data '{
    "confirm": true,
    
    
    
        
        
        
        
        
        
        
        
          "payment_method": "card",
    "payment_method_type": "credit",
    
    "payment_method_data": {
        "card": {
             "card_number": "4242424242424242",
            "card_exp_month": "12",
            "card_exp_year": "2043",
            "card_holder_name": "Cy1",
            "card_cvc": "123"
        }
    },
        
      "customer_acceptance": {
            "acceptance_type": "offline",
            "accepted_at": "1963-05-03T04:07:52.723Z",
            "online": {
                "ip_address": "in sit",
                "user_agent": "amet irure esse"
            }}

}'
```
- Make a recurring `off_Session:true` payment
- > `Create`
```
curl --location 'http://localhost:8080/payments' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: dev_njRmxwKH6ktMs37HQLB07DhgK1sFgROrpNiUiUgiSKyIzpoz3Wa5vKDCWc6PQlLe' \
--data-raw '{
    "amount": 1000,
    "currency": "USD",
    "confirm": true,
    
    "customer_id": "new-c77",
    "capture_method": "automatic",
    "capture_on": "2022-09-10T10:11:12Z",
    "authentication_type": "three_ds",
    "return_url": "https://google.com",
    "email": "something@gmail.com",
    "name": "Joseph Doe",
    "phone": "999999999",
    "phone_country_code": "+65",
    "description": "Its my first payment request",
    
   
    "billing": {
        "address": {
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "city": "San Fransico",
            "state": "California",
            "zip": "94122",
            "country": "US",
            "first_name": "joseph",
            "last_name": "Doe"
        },
        "phone": {
            "number": "8056594427",
            "country_code": "+91"
        }
    },
    
    "off_session":true,
      "recurring_details":{
        "type":"payment_method_id",
        "data":"pm_SrFlrDpRmqb9ObWTB4E6"
    },
    "shipping": {
        "address": {
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "city": "San Fransico",
            "state": "California",
            "zip": "94122",
            "country": "US",
            "first_name": "joseph",
            "last_name": "Doe"
        },
        "phone": {
            "number": "8056594427",
            "country_code": "+91"
        }
    },
    "connector_metadata": {
        "noon": {
            "order_category": "pay"
        }
    }
}'
```
> `Response`
```
{
    "payment_id": "pay_Kr0zqnLF7YvlfGambRdv",
    "merchant_id": "merchant_1712557617",
    "status": "succeeded",
    "amount": 1000,
    "net_amount": 1000,
    "amount_capturable": 0,
    "amount_received": 1000,
    "connector": "cybersource",
    "client_secret": "pay_Kr0zqnLF7YvlfGambRdv_secret_1g5i4yaC24q3cryBXRaH",
    "created": "2024-04-08T06:33:18.853Z",
    "currency": "USD",
    "customer_id": "new-c77",
    "customer": {
        "id": "new-c77",
        "name": "Joseph Doe",
        "email": "something@gmail.com",
        "phone": "999999999",
        "phone_country_code": "+65"
    },
    "description": "Its my first payment request",
    "refunds": null,
    "disputes": null,
    "mandate_id": null,
    "mandate_data": null,
    "setup_future_usage": null,
    "off_session": null,
    "capture_on": null,
    "capture_method": "automatic",
    "payment_method": "card",
    "payment_method_data": {
        "card": {
            "last4": "4242",
            "card_type": null,
            "card_network": null,
            "card_issuer": null,
            "card_issuing_country": null,
            "card_isin": "424242",
            "card_extended_bin": null,
            "card_exp_month": "12",
            "card_exp_year": "2043",
            "card_holder_name": "Cy1",
            "payment_checks": null,
            "authentication_data": null
        },
        "billing": null
    },
    "payment_token": null,
    "shipping": {
        "address": {
            "city": "San Fransico",
            "country": "US",
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "zip": "94122",
            "state": "California",
            "first_name": "joseph",
            "last_name": "Doe"
        },
        "phone": {
            "number": "8056594427",
            "country_code": "+91"
        },
        "email": null
    },
    "billing": {
        "address": {
            "city": "San Fransico",
            "country": "US",
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "zip": "94122",
            "state": "California",
            "first_name": "joseph",
            "last_name": "Doe"
        },
        "phone": {
            "number": "8056594427",
            "country_code": "+91"
        },
        "email": null
    },
    "order_details": null,
    "email": "something@gmail.com",
    "name": "Joseph Doe",
    "phone": "999999999",
    "return_url": "https://google.com/",
    "authentication_type": "three_ds",
    "statement_descriptor_name": null,
    "statement_descriptor_suffix": null,
    "next_action": null,
    "cancellation_reason": null,
    "error_code": null,
    "error_message": null,
    "unified_code": null,
    "unified_message": null,
    "payment_experience": null,
    "payment_method_type": "credit",
    "connector_label": null,
    "business_country": null,
    "business_label": "default",
    "business_sub_label": null,
    "allowed_payment_method_types": null,
    "ephemeral_key": {
        "customer_id": "new-c77",
        "created_at": 1712557998,
        "expires": 1712561598,
        "secret": "epk_f0f3118f76554bc0862a93601aa6b628"
    },
    "manual_retry_allowed": false,
    "connector_transaction_id": "7125580011196185103955",
    "frm_message": null,
    "metadata": null,
    "connector_metadata": {
        "apple_pay": null,
        "airwallex": null,
        "noon": {
            "order_category": "pay"
        }
    },
    "feature_metadata": null,
    "reference_id": "pay_Kr0zqnLF7YvlfGambRdv_1",
    "payment_link": null,
    "profile_id": "pro_WiTyPlqeoWjRO22EiiNb",
    "surcharge_details": null,
    "attempt_count": 1,
    "merchant_decision": null,
    "merchant_connector_id": "mca_cwp0jm7FkjSYWpeXVkG0",
    "incremental_authorization_allowed": false,
    "authorization_count": null,
    "incremental_authorizations": null,
    "external_authentication_details": null,
    "external_3ds_authentication_attempted": false,
    "expires_on": "2024-04-08T06:48:18.853Z",
    "fingerprint": null,
    "browser_info": null,
    "payment_method_id": "pm_SrFlrDpRmqb9ObWTB4E6",
    "payment_method_status": "active"
}
```

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