pub async fn list_user_roles_details(
    state: SessionState,
    user_from_token: auth::UserFromToken,
    request: user_api::GetUserRoleDetailsRequest,
    _req_state: ReqState,
) -> UserResponse<Vec<user_api::GetUserRoleDetailsResponseV2>> {
    let required_user = utils::user::get_user_from_db_by_email(&state, request.email.try_into()?)
        .await
        .to_not_found_response(UserErrors::InvalidRoleOperation)?;

    let requestor_role_info = roles::RoleInfo::from_role_id_org_id_tenant_id(
        &state,
        &user_from_token.role_id,
        &user_from_token.org_id,
        user_from_token
            .tenant_id
            .as_ref()
            .unwrap_or(&state.tenant.tenant_id),
    )
    .await
    .to_not_found_response(UserErrors::InternalServerError)
    .attach_printable("Failed to fetch role info")?;

    if requestor_role_info.is_internal() {
        return Err(UserErrors::InvalidRoleOperationWithMessage(
            "Internal roles are not allowed for this operation".to_string(),
        )
        .into());
    }

    let user_roles_set = state
        .global_store
        .list_user_roles_by_user_id(ListUserRolesByUserIdPayload {
            user_id: required_user.get_user_id(),
            tenant_id: user_from_token
                .tenant_id
                .as_ref()
                .unwrap_or(&state.tenant.tenant_id),
            org_id: Some(&user_from_token.org_id),
            merchant_id: (requestor_role_info.get_entity_type() <= EntityType::Merchant)
                .then_some(&user_from_token.merchant_id),
            profile_id: (requestor_role_info.get_entity_type() <= EntityType::Profile)
                .then_some(&user_from_token.profile_id),
            entity_id: None,
            version: None,
            status: None,
            limit: None,
        })
        .await
        .change_context(UserErrors::InternalServerError)
        .attach_printable("Failed to fetch user roles")?
        .into_iter()
        .collect::<HashSet<_>>();

    let org_name = state
        .accounts_store
        .find_organization_by_org_id(&user_from_token.org_id)
        .await
        .change_context(UserErrors::InternalServerError)
        .attach_printable("Org id not found")?
        .get_organization_name();

    let org = NameIdUnit {
        id: user_from_token.org_id.clone(),
        name: org_name,
    };

    let (merchant_ids, merchant_profile_ids) = user_roles_set.iter().try_fold(
        (Vec::new(), Vec::new()),
        |(mut merchant, mut merchant_profile), user_role| {
            let (_, entity_type) = user_role
                .get_entity_id_and_type()
                .ok_or(UserErrors::InternalServerError)
                .attach_printable("Failed to compute entity id and type")?;

            match entity_type {
                EntityType::Merchant => {
                    let merchant_id = user_role
                        .merchant_id
                        .clone()
                        .ok_or(UserErrors::InternalServerError)
                        .attach_printable(
                            "Merchant id not found in user role for merchant level entity",
                        )?;
                    merchant.push(merchant_id)
                }
                EntityType::Profile => {
                    let merchant_id = user_role
                        .merchant_id
                        .clone()
                        .ok_or(UserErrors::InternalServerError)
                        .attach_printable(
                            "Merchant id not found in user role for merchant level entity",
                        )?;
                    let profile_id = user_role
                        .profile_id
                        .clone()
                        .ok_or(UserErrors::InternalServerError)
                        .attach_printable(
                            "Profile id not found in user role for profile level entity",
                        )?;

                    merchant.push(merchant_id.clone());
                    merchant_profile.push((merchant_id, profile_id))
                }
                EntityType::Tenant | EntityType::Organization => (),
            };

            Ok::<_, error_stack::Report<UserErrors>>((merchant, merchant_profile))
        },
    )?;

    let merchant_map = state
        .store
        .list_multiple_merchant_accounts(&(&state).into(), merchant_ids)
        .await
        .change_context(UserErrors::InternalServerError)
        .attach_printable("Error while listing merchant accounts")?
        .into_iter()
        .map(|merchant_account| {
            (
                merchant_account.get_id().to_owned(),
                merchant_account.merchant_name.clone(),
            )
        })
        .collect::<HashMap<_, _>>();

    let key_manager_state = &(&state).into();

    let profile_map = futures::future::try_join_all(merchant_profile_ids.iter().map(
        |merchant_profile_id| async {
            let merchant_key_store = state
                .store
                .get_merchant_key_store_by_merchant_id(
                    key_manager_state,
                    &merchant_profile_id.0,
                    &state.store.get_master_key().to_vec().into(),
                )
                .await
                .change_context(UserErrors::InternalServerError)
                .attach_printable("Failed to retrieve merchant key store by merchant_id")?;

            state
                .store
                .find_business_profile_by_profile_id(
                    key_manager_state,
                    &merchant_key_store,
                    &merchant_profile_id.1,
                )
                .await
                .change_context(UserErrors::InternalServerError)
                .attach_printable("Failed to retrieve business profile")
        },
    ))
    .await
    .change_context(UserErrors::InternalServerError)
    .attach_printable("Failed to construct profile map")?
    .into_iter()
    .map(|profile| (profile.get_id().to_owned(), profile.profile_name))
    .collect::<HashMap<_, _>>();

    let role_name_map = futures::future::try_join_all(
        user_roles_set
            .iter()
            .map(|user_role| user_role.role_id.clone())
            .collect::<HashSet<_>>()
            .into_iter()
            .map(|role_id| async {
                let role_info = roles::RoleInfo::from_role_id_org_id_tenant_id(
                    &state,
                    &role_id,
                    &user_from_token.org_id,
                    user_from_token
                        .tenant_id
                        .as_ref()
                        .unwrap_or(&state.tenant.tenant_id),
                )
                .await
                .change_context(UserErrors::InternalServerError)?;

                Ok::<_, error_stack::Report<_>>((role_id, role_info.get_role_name().to_string()))
            }),
    )
    .await?
    .into_iter()
    .collect::<HashMap<_, _>>();

    let role_details_list: Vec<_> = user_roles_set
        .iter()
        .map(|user_role| {
            let (_, entity_type) = user_role
                .get_entity_id_and_type()
                .ok_or(UserErrors::InternalServerError)?;

            let (merchant, profile) = match entity_type {
                EntityType::Tenant | EntityType::Organization => (None, None),
                EntityType::Merchant => {
                    let merchant_id = &user_role
                        .merchant_id
                        .clone()
                        .ok_or(UserErrors::InternalServerError)?;

                    (
                        Some(NameIdUnit {
                            id: merchant_id.clone(),
                            name: merchant_map
                                .get(merchant_id)
                                .ok_or(UserErrors::InternalServerError)?
                                .to_owned(),
                        }),
                        None,
                    )
                }
                EntityType::Profile => {
                    let merchant_id = &user_role
                        .merchant_id
                        .clone()
                        .ok_or(UserErrors::InternalServerError)?;
                    let profile_id = &user_role
                        .profile_id
                        .clone()
                        .ok_or(UserErrors::InternalServerError)?;

                    (
                        Some(NameIdUnit {
                            id: merchant_id.clone(),
                            name: merchant_map
                                .get(merchant_id)
                                .ok_or(UserErrors::InternalServerError)?
                                .to_owned(),
                        }),
                        Some(NameIdUnit {
                            id: profile_id.clone(),
                            name: profile_map
                                .get(profile_id)
                                .ok_or(UserErrors::InternalServerError)?
                                .to_owned(),
                        }),
                    )
                }
            };

            Ok(user_api::GetUserRoleDetailsResponseV2 {
                role_id: user_role.role_id.clone(),
                org: org.clone(),
                merchant,
                profile,
                status: user_role.status.foreign_into(),
                entity_type,
                role_name: role_name_map
                    .get(&user_role.role_id)
                    .ok_or(UserErrors::InternalServerError)
                    .cloned()?,
            })
        })
        .collect::<Result<Vec<_>, UserErrors>>()?;

    Ok(ApplicationResponse::Json(role_details_list))
}


pub struct HelcimVoidRequest {
    card_transaction_id: u64,
    ip_address: Secret<String, IpAddress>,
    #[serde(skip_serializing_if = "Option::is_none")]
    ecommerce: Option<bool>,
}


pub struct AuthorizationIsoFields {
    pub response_code: String,
    pub response_code_description: String,
    pub authorization_code: String,
    pub network_code: String,
    pub system_trace_audit_number: Secret<String>,
    pub retrieval_reference_number: String,
    pub eci: String,
    pub network_specific_fields: NetworkSpecificFields,
}


/// Retrieves the appropriate file storage client based on the file storage configuration.
    pub async fn get_file_storage_client(&self) -> Arc<dyn FileStorageInterface> {
        match self {
            #[cfg(feature = "aws_s3")]
            Self::AwsS3 { aws_s3 } => Arc::new(aws_s3::AwsFileStorageClient::new(aws_s3).await),
            Self::FileSystem => Arc::new(file_system::FileSystem),
        }
    }


pub fn is_confirm_operation(&self) -> bool {
        matches!(self, Self::ConfirmIntent { .. })
    }


pub struct TokeniseCreditCardResult {
    tokenise_credit_card_response: MandateResponseBody,
}


impl ConnectorValidation for Authorizedotnet {
    fn validate_mandate_payment(
        &self,
        pm_type: Option<enums::PaymentMethodType>,
        pm_data: PaymentMethodData,
    ) -> CustomResult<(), errors::ConnectorError> {
        let mandate_supported_pmd = std::collections::HashSet::from([
            PaymentMethodDataType::Card,
            PaymentMethodDataType::GooglePay,
            PaymentMethodDataType::ApplePay,
        ]);
        connector_utils::is_mandate_supported(pm_data, pm_type, mandate_supported_pmd, self.id())
    }
}

impl ConnectorValidation for for Authorizedotnet


pub async fn perform_dynamic_routing_with_intelligent_router<F, D>(
    state: &SessionState,
    routable_connectors: Vec<api_routing::RoutableConnectorChoice>,
    profile: &domain::Profile,
    dynamic_routing_config_params_interpolator: routing::helpers::DynamicRoutingConfigParamsInterpolator,
    payment_data: &mut D,
) -> RoutingResult<Vec<api_routing::RoutableConnectorChoice>>
where
    F: Send + Clone,
    D: OperationSessionGetters<F> + OperationSessionSetters<F> + Send + Sync + Clone,
{
    let dynamic_routing_algo_ref: api_routing::DynamicRoutingAlgorithmRef = profile
        .dynamic_routing_algorithm
        .clone()
        .map(|val| val.parse_value("DynamicRoutingAlgorithmRef"))
        .transpose()
        .change_context(errors::RoutingError::DeserializationError {
            from: "JSON".to_string(),
            to: "DynamicRoutingAlgorithmRef".to_string(),
        })
        .attach_printable("unable to deserialize DynamicRoutingAlgorithmRef from JSON")?
        .ok_or(errors::RoutingError::GenericNotFoundError {
            field: "dynamic_routing_algorithm".to_string(),
        })?;

    logger::debug!(
        "performing dynamic_routing for profile {}",
        profile.get_id().get_string_repr()
    );

    let payment_attempt = payment_data.get_payment_attempt().clone();

    let mut connector_list = match dynamic_routing_algo_ref
        .success_based_algorithm
        .as_ref()
        .async_map(|algorithm| {
            perform_success_based_routing(
                state,
                routable_connectors.clone(),
                profile.get_id(),
                &payment_attempt.merchant_id,
                &payment_attempt.payment_id,
                dynamic_routing_config_params_interpolator.clone(),
                algorithm.clone(),
                payment_data,
            )
        })
        .await
        .transpose()
        .inspect_err(|e| logger::error!(dynamic_routing_error=?e))
        .ok()
        .flatten()
    {
        Some(success_based_list) => success_based_list,
        None => {
            // Only run contract based if success based returns None
            dynamic_routing_algo_ref
                .contract_based_routing
                .as_ref()
                .async_map(|algorithm| {
                    perform_contract_based_routing(
                        state,
                        routable_connectors.clone(),
                        profile.get_id(),
                        &payment_attempt.merchant_id,
                        &payment_attempt.payment_id,
                        dynamic_routing_config_params_interpolator.clone(),
                        algorithm.clone(),
                        payment_data,
                    )
                })
                .await
                .transpose()
                .inspect_err(|e| logger::error!(dynamic_routing_error=?e))
                .ok()
                .flatten()
                .unwrap_or(routable_connectors.clone())
        }
    };

    connector_list = dynamic_routing_algo_ref
        .elimination_routing_algorithm
        .as_ref()
        .async_map(|algorithm| {
            perform_elimination_routing(
                state,
                connector_list.clone(),
                profile.get_id(),
                &payment_attempt.merchant_id,
                &payment_attempt.payment_id,
                dynamic_routing_config_params_interpolator.clone(),
                algorithm.clone(),
            )
        })
        .await
        .transpose()
        .inspect_err(|e| logger::error!(dynamic_routing_error=?e))
        .ok()
        .flatten()
        .unwrap_or(connector_list);

    Ok(connector_list)
}


- - -

## 2024.08.14.0

### Features

- **connector:** Create Taxjar connector ([#5597](https://github.com/juspay/hyperswitch/pull/5597)) ([`0ab0aa1`](https://github.com/juspay/hyperswitch/commit/0ab0aa1a94fe98719d51ff89d27935a30cb33721))

### Bug Fixes

- **router:** Return missing required field error when a domain is missing during apple pay session call ([#5596](https://github.com/juspay/hyperswitch/pull/5596)) ([`751ba15`](https://github.com/juspay/hyperswitch/commit/751ba15482f6e4aed403ff947eea01b75093087e))

### Refactors

- **connector:** Add amount conversion framework for ACI ([#5456](https://github.com/juspay/hyperswitch/pull/5456)) ([`93aa522`](https://github.com/juspay/hyperswitch/commit/93aa522929fc8538894bd4bc934cb17a793d2e24))
- **routing:** Refactor fallback routing apis for v2 ([#5592](https://github.com/juspay/hyperswitch/pull/5592)) ([`051086f`](https://github.com/juspay/hyperswitch/commit/051086f7b657600efe2d6a41023572610ef41ccb))

### Documentation

- **README:** Add social media links ([#5600](https://github.com/juspay/hyperswitch/pull/5600)) ([`e757605`](https://github.com/juspay/hyperswitch/commit/e757605fdc287685c573d0c1c461cea556e5a5ce))

### Miscellaneous Tasks

- Run pml independently ([#5604](https://github.com/juspay/hyperswitch/pull/5604)) ([`f5b2eec`](https://github.com/juspay/hyperswitch/commit/f5b2eec015bce088ed666960228769b40d76b2c0))

**Full Changelog:** [`2024.08.13.0...2024.08.14.0`](https://github.com/juspay/hyperswitch/compare/2024.08.13.0...2024.08.14.0)

- - -

## 2024.08.13.0

### Features

- **analytics:** Populate status_code, initial_attempt_id & delivery_attempt on clickhouse for outgoing webhook events ([#5383](https://github.com/juspay/hyperswitch/pull/5383)) ([`f9c29b0`](https://github.com/juspay/hyperswitch/commit/f9c29b084b68563c10f07599e0d789a105958592))
- **connector:**
  - [WELLSFARGO_PAYOUT] PR template code ([#5567](https://github.com/juspay/hyperswitch/pull/5567)) ([`6a5b493`](https://github.com/juspay/hyperswitch/commit/6a5b49397adc402f7ce50543c817df3f11ca46ea))
  - [FISERVEMEA] Add template code ([#5583](https://github.com/juspay/hyperswitch/pull/5583)) ([`74fcc91`](https://github.com/juspay/hyperswitch/commit/74fcc910e9f0b4487f5958f1872e71436fe8f40e))
- **cypress:** Generate test reports ([#5563](https://github.com/juspay/hyperswitch/pull/5563)) ([`116f31c`](https://github.com/juspay/hyperswitch/commit/116f31cf9b79104d0e5b38ce774555a9ae2f4b88))
- **payout_link:** Add localisation support for payout link's templates ([#5552](https://github.com/juspay/hyperswitch/pull/5552)) ([`b0346e0`](https://github.com/juspay/hyperswitch/commit/b0346e08f45c6739da22f370b657a41cf2a9cd67))
- Change admin api key auth to merchant api key auth in few connectors flow ([#5572](https://github.com/juspay/hyperswitch/pull/5572)) ([`7a23e66`](https://github.com/juspay/hyperswitch/commit/7a23e663c283333aaa4e45550e2a36f223ad5e3e))

### Bug Fixes

- **frm:** Restrict enabled mca for frm connectors ([#5499](https://github.com/juspay/hyperswitch/pull/5499)) ([`7718800`](https://github.com/juspay/hyperswitch/commit/7718800e1fc434a553a211b24bd48d2cddc06d1f))
- **payment_link:** Remove dynamic section if no fields are present ([#5579](https://github.com/juspay/hyperswitch/pull/5579)) ([`78d9906`](https://github.com/juspay/hyperswitch/commit/78d9906ebbedd9069a15b88f49b9348b663cbee8))

### Refactors

- **core:** Adapt the usage of routing_algorithm_id in routing and payments core for v2 ([#5533](https://github.com/juspay/hyperswitch/pull/5533)) ([`61de3e0`](https://github.com/juspay/hyperswitch/commit/61de3e025a21cc691852add8298573d5dd95388c))
- **openapi_v2:** Add merchant account v2 openapi ([#5588](https://github.com/juspay/hyperswitch/pull/5588)) ([`c8943eb`](https://github.com/juspay/hyperswitch/commit/c8943eb289664093f2a4d515bfacd804f86cd20a))

### Build System / Dependencies

- Bump MSRV to 1.76.0 ([#5586](https://github.com/juspay/hyperswitch/pull/5586)) ([`59b36a0`](https://github.com/juspay/hyperswitch/commit/59b36a054cfdd30daf810ba514dd4f495e36734a))

**Full Changelog:** [`2024.08.12.0...2024.08.13.0`](https://github.com/juspay/hyperswitch/compare/2024.08.12.0...2024.08.13.0)

- - -

## 2024.08.12.0

### Features

- **Connector:** Plaid connector configs ([#5545](https://github.com/juspay/hyperswitch/pull/5545)) ([`885428b`](https://github.com/juspay/hyperswitch/commit/885428bd1eee9fa2aad18eef1a1419702ebade75))
- **core:** Use profile_id passed from auth layer within core functions ([#5553](https://github.com/juspay/hyperswitch/pull/5553)) ([`9fa631d`](https://github.com/juspay/hyperswitch/commit/9fa631d2b9df9a2ba429e197b8785885edd97798))
- **events:** Add profile_id in payment_intents events and clickhouse ([#5573](https://github.com/juspay/hyperswitch/pull/5573)) ([`76b1460`](https://github.com/juspay/hyperswitch/commit/76b14601c843e328d168559840d8e22f78e59d3f))

### Bug Fixes

- **connector:** [Bambora Apac] failure on missing capture method and billing address requirement in mandates ([#5539](https://github.com/juspay/hyperswitch/pull/5539)) ([`3183a86`](https://github.com/juspay/hyperswitch/commit/3183a86ecde7192dcb3d4dd83105853344b04d61))
- **docker:** Currency enum fix for docker config ([#5577](https://github.com/juspay/hyperswitch/pull/5577)) ([`920243e`](https://github.com/juspay/hyperswitch/commit/920243e1d41fd103cf4f20495e966dfd86a29d0f))
- **payment_methods:** List cards on the basis of profiles ([#5584](https://github.com/juspay/hyperswitch/pull/5584)) ([`68574b2`](https://github.com/juspay/hyperswitch/commit/68574b28cd16c8f14f7a2fc9cb83836a509e174c))
- Cache on multitenancy ([#5561](https://github.com/juspay/hyperswitch/pull/5561)) ([`74632ae`](https://github.com/juspay/hyperswitch/commit/74632aebea28b9f1b67c258eec995f7c0f4c99d0))

### Refactors

- **connector:** Connector template generation ([#5568](https://github.com/juspay/hyperswitch/pull/5568)) ([`8fdcabd`](https://github.com/juspay/hyperswitch/commit/8fdcabda3f2278f867d0e3acacbd02bae4951781))
- **core:** Use hyperswitch_domain_models within the Payments Core instead of api_models ([#5511](https://github.com/juspay/hyperswitch/pull/5511)) ([`f81416e`](https://github.com/juspay/hyperswitch/commit/f81416e4df6fa430fd7f6eb910b55464cd72f3f0))
- **merchant_account_v2:** Remove routing algorithms from merchant account and add version column ([#5527](https://github.com/juspay/hyperswitch/pull/5527)) ([`f1196be`](https://github.com/juspay/hyperswitch/commit/f1196be9055699421d5ec1148a4f0646da4b8fc7))
- **openapi:** Add openapi support for generating v2 api-reference ([#5580](https://github.com/juspay/hyperswitch/pull/5580)) ([`92d76a3`](https://github.com/juspay/hyperswitch/commit/92d76a361a8babaa3433ead589f83268cb3d722f))
- **payouts:** OpenAPI schemas and mintlify docs ([#5284](https://github.com/juspay/hyperswitch/pull/5284)) ([`942e63d`](https://github.com/juspay/hyperswitch/commit/942e63d9cd607913d4ef7d5a01493a1615a783c1))

**Full Changelog:** [`2024.08.09.0...2024.08.12.0`](https://github.com/juspay/hyperswitch/compare/2024.08.09.0...2024.08.12.0)

- - -

## 2024.08.09.0

### Features

- **core:** [Payment Link] add dynamic merchant fields ([#5512](https://github.com/juspay/hyperswitch/pull/5512)) ([`03f0ea1`](https://github.com/juspay/hyperswitch/commit/03f0ea1582c76f1ed9dc4ff2215e80d659e5591a))
- Payment processor token for recurring payments ([#5508](https://github.com/juspay/hyperswitch/pull/5508)) ([`0cbbc92`](https://github.com/juspay/hyperswitch/commit/0cbbc92a4319cb51864f443462545ab6e1ec75b6))

### Refactors

- Use business profile domain models instead of diesel models ([#5566](https://github.com/juspay/hyperswitch/pull/5566)) ([`e56ad0d`](https://github.com/juspay/hyperswitch/commit/e56ad0d6884c6505d73df048e73d6210db3aae46))

**Full Changelog:** [`2024.08.08.0...2024.08.09.0`](https://github.com/juspay/hyperswitch/compare/2024.08.08.0...2024.08.09.0)

- - -

## 2024.08.08.0

### Bug Fixes

- **core:** Update pm_status accordingly for the respective attempt status ([#5557](https://github.com/juspay/hyperswitch/pull/5557)) ([`9d44eae`](https://github.com/juspay/hyperswitch/commit/9d44eaeb92bc110e2ce7e5d6fd69e175a6e25492))
- **opensearch:** Added @timestamp field for opensearch localhost ([#5556](https://github.com/juspay/hyperswitch/pull/5556)) ([`7324722`](https://github.com/juspay/hyperswitch/commit/732472204d835ef0ceb60bfec89f872c7098621e))

### Refactors

- **connector:** Added amount conversion framework for Gpayments ([#4978](https://github.com/juspay/hyperswitch/pull/4978)) ([`aa036ce`](https://github.com/juspay/hyperswitch/commit/aa036ce84d1ecef7665dd1f8832e569d50a2d7cd))
- **cypress:** Make `connector_label` configurable in cypress ([#5547](https://github.com/juspay/hyperswitch/pull/5547)) ([`8881e97`](https://github.com/juspay/hyperswitch/commit/8881e9774e59c956cb24995f55a9c8a92c20e24f))

**Full Changelog:** [`2024.08.07.1...2024.08.08.0`](https://github.com/juspay/hyperswitch/compare/2024.08.07.1...2024.08.08.0)

- - -

## 2024.08.07.1

### Features

- **core:** Add support for payment links localization ([#5530](https://github.com/juspay/hyperswitch/pull/5530)) ([`3604b4f`](https://github.com/juspay/hyperswitch/commit/3604b4ffac569aac5f776f563dc11544577e8467))
- **customer_v2:** Add customer create v2 endpoint ([#5444](https://github.com/juspay/hyperswitch/pull/5444)) ([`52cada0`](https://github.com/juspay/hyperswitch/commit/52cada015ef5f65128e4032da9cb3a07953fa8aa))
- **cypress:**
  - Add configs for not overriding screenshots ([#5524](https://github.com/juspay/hyperswitch/pull/5524)) ([`52154cb`](https://github.com/juspay/hyperswitch/commit/52154cbbe2fa83d2a51943934a86963d37c7ce1c))
  - Add mandates and refunds variation cases ([#5513](https://github.com/juspay/hyperswitch/pull/5513)) ([`29c5f8a`](https://github.com/juspay/hyperswitch/commit/29c5f8a7570513bd0a23e5d0a90d6c2f78c63f6f))
  - Add core flow tests ([#5546](https://github.com/juspay/hyperswitch/pull/5546)) ([`7e545e3`](https://github.com/juspay/hyperswitch/commit/7e545e36ebdcfe46a1fcb29687af542e34d00e93))
- Add a wrapper for encryption and decryption ([#5502](https://github.com/juspay/hyperswitch/pull/5502)) ([`f51b6c9`](https://github.com/juspay/hyperswitch/commit/f51b6c91a726cc7be8607b4b9b921d348d3fc5a3))

### Bug Fixes

- **filter_mca:** Update helper function for filtering MCAs for payments ([#5529](https://github.com/juspay/hyperswitch/pull/5529)) ([`1715cf0`](https://github.com/juspay/hyperswitch/commit/1715cf0ed4c67e87fe3ddf9090174fc70d6c9e8c))

### Refactors

- **business_profile:** Use concrete types for JSON fields ([#5531](https://github.com/juspay/hyperswitch/pull/5531)) ([`a8ba21c`](https://github.com/juspay/hyperswitch/commit/a8ba21c1d1f51bc362b3412a31160d5c73364492))
- **connector:**
  - Added amount conversion framework for Boku ([#5459](https://github.com/juspay/hyperswitch/pull/5459)) ([`69a9c14`](https://github.com/juspay/hyperswitch/commit/69a9c144bd3be4b90f34025d97d1b610a21b666d))
  - Added amount conversion framework for Mifinity ([#5460](https://github.com/juspay/hyperswitch/pull/5460)) ([`c6a9607`](https://github.com/juspay/hyperswitch/commit/c6a960766d3e68b7d5e3ddf10c2306fa3e9c4786))
- **router:** Refactor `merchant_connector_account` retrieve and delete v2 apis ([#5528](https://github.com/juspay/hyperswitch/pull/5528)) ([`253f1be`](https://github.com/juspay/hyperswitch/commit/253f1be3e18934de58b7a1876e406c22ae042557))

**Full Changelog:** [`2024.08.07.0...2024.08.07.1`](https://github.com/juspay/hyperswitch/compare/2024.08.07.0...2024.08.07.1)

- - -

## 2024.08.07.0

### Features

- **connector:**
  - Remove Braintree SDK Flow support ([#5264](https://github.com/juspay/hyperswitch/pull/5264)) ([`61a0cb3`](https://github.com/juspay/hyperswitch/commit/61a0cb3e1eeacd1a900e3b06bbde494dfc274b35))
  - [WELLSFARGO] Implement Payment Flows ([#5463](https://github.com/juspay/hyperswitch/pull/5463)) ([`a082759`](https://github.com/juspay/hyperswitch/commit/a0827596cb243f4187735e8559dd9a759bb51405))
  - Added configs for Plaid ([#5479](https://github.com/juspay/hyperswitch/pull/5479)) ([`18e328d`](https://github.com/juspay/hyperswitch/commit/18e328d3825847f41184fbda58ee390eeaa54381))
- **core:** Pass `profile_id` to core from auth layer ([#5532](https://github.com/juspay/hyperswitch/pull/5532)) ([`95e9c85`](https://github.com/juspay/hyperswitch/commit/95e9c8523544bad4a034e61f62f6a321a8990963))

### Bug Fixes

- [CYBERSOURCE] Update status handling for AuthorizedPendingReview ([#5534](https://github.com/juspay/hyperswitch/pull/5534)) ([`2f3a463`](https://github.com/juspay/hyperswitch/commit/2f3a463253c1704218a1bed06b1bec192a3e02b9))

### Refactors

- **core:** Refactor customer payment method list for v2 ([#4856](https://github.com/juspay/hyperswitch/pull/4856)) ([`8302272`](https://github.com/juspay/hyperswitch/commit/8302272460eee5ddfd56a89f280d0d18a04701f1))
- **merchant_account_v2:** Recreate id and remove deprecated fields from merchant account ([#5493](https://github.com/juspay/hyperswitch/pull/5493)) ([`49892b2`](https://github.com/juspay/hyperswitch/commit/49892b261ef9bd0a54b8e4568d40463fca26862b))

**Full Changelog:** [`2024.08.06.0...2024.08.07.0`](https://github.com/juspay/hyperswitch/compare/2024.08.06.0...2024.08.07.0)

- - -

## 2024.08.06.0

### Features

- **core:** Accept profile_id in merchant_account, connectors and customers core functions ([#5505](https://github.com/juspay/hyperswitch/pull/5505)) ([`a3e01bb`](https://github.com/juspay/hyperswitch/commit/a3e01bb4ae5893f639f3846ccb73adcca6b25ee0))
- **router:** Add accept language header middleware ([#5500](https://github.com/juspay/hyperswitch/pull/5500)) ([`ec5f9de`](https://github.com/juspay/hyperswitch/commit/ec5f9de0cbfecd584cc200e26b13a37aebe97f16))

### Bug Fixes

- **connector:** Fixed status mapping for Plaid ([#5525](https://github.com/juspay/hyperswitch/pull/5525)) ([`4364630`](https://github.com/juspay/hyperswitch/commit/4364630d6ffbce43bef0947a0150ce255a43751a))

### Refactors

- **auth:** Pass `profile_id` from the auth to core functions ([#5520](https://github.com/juspay/hyperswitch/pull/5520)) ([`53b5551`](https://github.com/juspay/hyperswitch/commit/53b5551df7e7a04bb26532591180608542c33c3a))
- **cypress:** Pass `connector_type` externally ([#5522](https://github.com/juspay/hyperswitch/pull/5522)) ([`7743255`](https://github.com/juspay/hyperswitch/commit/7743255cd8618ea290e714f2268c07bac4cfa88d))
- **router:** Refactor merchant_connector update v2 flow ([#5484](https://github.com/juspay/hyperswitch/pull/5484)) ([`9e358e4`](https://github.com/juspay/hyperswitch/commit/9e358e4f7bac86fbf433bbb727e41e4a9acf29ea))
- **routing:** Refactor api v2 routes for deactivating and retrieving the routing config ([#5478](https://github.com/juspay/hyperswitch/pull/5478)) ([`3fea00c`](https://github.com/juspay/hyperswitch/commit/3fea00c43ee597c9b786da6636e245cb848cdb97))

**Full Changelog:** [`2024.08.05.0...2024.08.06.0`](https://github.com/juspay/hyperswitch/compare/2024.08.05.0...2024.08.06.0)

- - -

## 2024.08.05.0

### Bug Fixes

- **pm_auth:** Added mca status check in pml ([#5421](https://github.com/juspay/hyperswitch/pull/5421)) ([`e9bd345`](https://github.com/juspay/hyperswitch/commit/e9bd345464f28133aeaab638c33b77f31dd1fcb5))
- **router:** [Iatapay] make error status and error message optional ([#5382](https://github.com/juspay/hyperswitch/pull/5382)) ([`37e34e3`](https://github.com/juspay/hyperswitch/commit/37e34e3bfde9281b3a69b0769c901a887dcf400f))

### Refactors

- **payment_methods:** List the Payment Methods for Merchant , based on the connector type ([#4909](https://github.com/juspay/hyperswitch/pull/4909)) ([`f3677f2`](https://github.com/juspay/hyperswitch/commit/f3677f268ca18879bc8a9e4c7ab8c96011eb56c3))

### Miscellaneous Tasks

- **postman:** Update Postman collection files ([`1737d74`](https://github.com/juspay/hyperswitch/commit/1737d74183a77910ea764c8cf2cdb148ba77ab74))

**Full Changelog:** [`2024.08.02.0...2024.08.05.0`](https://github.com/juspay/hyperswitch/compare/2024.08.02.0...2024.08.05.0)

- - -

## 2024.08.02.0

### Features

- **auth:**
  - Add support for partial-auth, by facilitating injection of authentication parameters in headers ([#4802](https://github.com/juspay/hyperswitch/pull/4802)) ([`1d4c87a`](https://github.com/juspay/hyperswitch/commit/1d4c87a9e37ab1fc05754208ba4fbbcf15ad895a))
  - Add `profile_id` in `AuthenticationData` ([#5492](https://github.com/juspay/hyperswitch/pull/5492)) ([`b4eb601`](https://github.com/juspay/hyperswitch/commit/b4eb6016a4e696acf155732592a6571363c24e64))
- **business_profile:** Introduce domain models for business profile v1 and v2 APIs ([#5497](https://github.com/juspay/hyperswitch/pull/5497)) ([`537630f`](https://github.com/juspay/hyperswitch/commit/537630f00482939d4c0b49c643dee3763fe0e046))
- **connector:** [Paybox] add connector template code ([#5485](https://github.com/juspay/hyperswitch/pull/5485)) ([`5e1eb4a`](https://github.com/juspay/hyperswitch/commit/5e1eb4af863265c94299189de983e02a255e7e62))
- **core:** Accept business profile in core functions for payments, refund, payout and disputes ([#5498](https://github.com/juspay/hyperswitch/pull/5498)) ([`fb32b61`](https://github.com/juspay/hyperswitch/commit/fb32b61edfa2b4190a5717850aeca6b3b0d7db54))
- **cypress:** Add corner cases ([#5481](https://github.com/juspay/hyperswitch/pull/5481)) ([`c0f4577`](https://github.com/juspay/hyperswitch/commit/c0f45771b0b4d7d60918ae03aca9f14162ff3218))
- **opensearch:** Updated status filter field name to match index and added time-range based search ([#5468](https://github.com/juspay/hyperswitch/pull/5468)) ([`625f5ae`](https://github.com/juspay/hyperswitch/commit/625f5ae289ca93a1a6d469d6a0f71d7492f22bc5))

### Bug Fixes


impl api::ConnectorAccessToken for Opennode {}

impl api::ConnectorAccessToken for for Opennode


impl MerchantConnectorAccount {
    pub fn server(state: AppState) -> Scope {
        let mut route = web::scope("/account").app_data(web::Data::new(state));

        #[cfg(feature = "olap")]
        {
            use super::admin::*;

            route = route
                .service(
                    web::resource("/connectors/verify")
                        .route(web::post().to(super::verify_connector::payment_connector_verify)),
                )
                .service(
                    web::resource("/{merchant_id}/connectors")
                        .route(web::post().to(connector_create))
                        .route(web::get().to(connector_list)),
                )
                .service(
                    web::resource("/{merchant_id}/connectors/{merchant_connector_id}")
                        .route(web::get().to(connector_retrieve))
                        .route(web::post().to(connector_update))
                        .route(web::delete().to(connector_delete)),
                );
        }
        #[cfg(feature = "oltp")]
        {
            route = route.service(
                web::resource("/payment_methods")
                    .route(web::get().to(payment_methods::list_payment_method_api)),
            );
        }
        route
    }
}


OpenAPI Block Path: components.schemas.MerchantAccountResponse
{
  "type": "object",
  "required": [
    "merchant_id",
    "enable_payment_response_hash",
    "redirect_to_merchant_with_http_post",
    "primary_business_details",
    "organization_id",
    "is_recon_enabled",
    "recon_status",
    "merchant_account_type"
  ],
  "properties": {
    "merchant_id": {
      "type": "string",
      "description": "The identifier for the Merchant Account",
      "example": "y3oqhf46pyzuxjbcn2giaqnb44",
      "maxLength": 64
    },
    "merchant_name": {
      "type": "string",
      "description": "Name of the Merchant Account",
      "example": "NewAge Retailer",
      "nullable": true
    },
    "return_url": {
      "type": "string",
      "description": "The URL to redirect after completion of the payment",
      "example": "https://www.example.com/success",
      "nullable": true,
      "maxLength": 255
    },
    "enable_payment_response_hash": {
      "type": "boolean",
      "description": "A boolean value to indicate if payment response hash needs to be enabled",
      "default": false,
      "example": true
    },
    "payment_response_hash_key": {
      "type": "string",
      "description": "Refers to the hash key used for calculating the signature for webhooks and redirect response. If the value is not provided, a value is automatically generated.",
      "example": "xkkdf909012sdjki2dkh5sdf",
      "nullable": true,
      "maxLength": 255
    },
    "redirect_to_merchant_with_http_post": {
      "type": "boolean",
      "description": "A boolean value to indicate if redirect to merchant with http post needs to be enabled",
      "default": false,
      "example": true
    },
    "merchant_details": {
      "allOf": [
        {
          "$ref": "#/components/schemas/MerchantDetails"
        }
      ],
      "nullable": true
    },
    "webhook_details": {
      "allOf": [
        {
          "$ref": "#/components/schemas/WebhookDetails"
        }
      ],
      "nullable": true
    },
    "payout_routing_algorithm": {
      "allOf": [
        {
          "$ref": "#/components/schemas/StaticRoutingAlgorithm"
        }
      ],
      "nullable": true
    },
    "sub_merchants_enabled": {
      "type": "boolean",
      "description": "A boolean value to indicate if the merchant is a sub-merchant under a master or a parent merchant. By default, its value is false.",
      "default": false,
      "example": false,
      "nullable": true
    },
    "parent_merchant_id": {
      "type": "string",
      "description": "Refers to the Parent Merchant ID if the merchant being created is a sub-merchant",
      "example": "xkkdf909012sdjki2dkh5sdf",
      "nullable": true,
      "maxLength": 255
    },
    "publishable_key": {
      "type": "string",
      "description": "API key that will be used for server side API access",
      "example": "AH3423bkjbkjdsfbkj",
      "nullable": true
    },
    "metadata": {
      "type": "object",
      "description": "Metadata is useful for storing additional, unstructured information on an object.",
      "nullable": true
    },
    "locker_id": {
      "type": "string",
      "description": "An identifier for the vault used to store payment method information.",
      "example": "locker_abc123",
      "nullable": true
    },
    "primary_business_details": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/PrimaryBusinessDetails"
      },
      "description": "Details about the primary business unit of the merchant account"
    },
    "frm_routing_algorithm": {
      "allOf": [
        {
          "$ref": "#/components/schemas/StaticRoutingAlgorithm"
        }
      ],
      "nullable": true
    },
    "organization_id": {
      "type": "string",
      "description": "The organization id merchant is associated with",
      "example": "org_q98uSGAYbjEwqs0mJwnz",
      "maxLength": 64,
      "minLength": 1
    },
    "is_recon_enabled": {
      "type": "boolean",
      "description": "A boolean value to indicate if the merchant has recon service is enabled or not, by default value is false"
    },
    "default_profile": {
      "type": "string",
      "description": "The default profile that must be used for creating merchant accounts and payments",
      "nullable": true,
      "maxLength": 64
    },
    "recon_status": {
      "$ref": "#/components/schemas/ReconStatus"
    },
    "pm_collect_link_config": {
      "allOf": [
        {
          "$ref": "#/components/schemas/BusinessCollectLinkConfig"
        }
      ],
      "nullable": true
    },
    "product_type": {
      "allOf": [
        {
          "$ref": "#/components/schemas/MerchantProductType"
        }
      ],
      "nullable": true
    },
    "merchant_account_type": {
      "$ref": "#/components/schemas/MerchantAccountType"
    }
  }
}


pub struct RoutingEvaluateResponse {
    pub status: String,
    pub output: serde_json::Value,
    #[serde(deserialize_with = "deserialize_connector_choices")]
    pub evaluated_output: Vec<RoutableConnectorChoice>,
    #[serde(deserialize_with = "deserialize_connector_choices")]
    pub eligible_connectors: Vec<RoutableConnectorChoice>,
}


File: crates/openapi/src/main.rs

#[cfg(feature = "v1")]
mod openapi;
#[cfg(feature = "v2")]
mod openapi_v2;
mod routes;

#[allow(clippy::print_stdout)] // Using a logger is not necessary here
fn main() {
    #[cfg(all(feature = "v1", feature = "v2"))]
    compile_error!("features v1 and v2 are mutually exclusive, please enable only one of them");

    #[cfg(feature = "v1")]
    let relative_file_path = "api-reference/v1/openapi_spec_v1.json";

    #[cfg(feature = "v2")]
    let relative_file_path = "api-reference/v2/openapi_spec_v2.json";

    #[cfg(any(feature = "v1", feature = "v2"))]
    let mut file_path = router_env::workspace_path();

    #[cfg(any(feature = "v1", feature = "v2"))]
    file_path.push(relative_file_path);

    #[cfg(feature = "v1")]
    let openapi = <openapi::ApiDoc as utoipa::OpenApi>::openapi();
    #[cfg(feature = "v2")]
    let openapi = <openapi_v2::ApiDoc as utoipa::OpenApi>::openapi();

    #[allow(clippy::expect_used)]
    #[cfg(any(feature = "v1", feature = "v2"))]
    std::fs::write(
        &file_path,
        openapi
            .to_pretty_json()
            .expect("Failed to serialize OpenAPI specification as JSON"),
    )
    .expect("Failed to write OpenAPI specification to file");

    #[allow(clippy::expect_used)]
    #[cfg(feature = "v1")]
    {
        // TODO: Do this using utoipa::extensions after we have upgraded to 5.x
        let file_content =
            std::fs::read_to_string(&file_path).expect("Failed to read OpenAPI specification file");

        let mut lines: Vec<&str> = file_content.lines().collect();

        // Insert the new text at line 3 (index 2)
        if lines.len() > 2 {
            let new_line = "  \"x-mcp\": {\n    \"enabled\": true\n  },";
            lines.insert(2, new_line);
        }

        let modified_content = lines.join("\n");
        std::fs::write(&file_path, modified_content)
            .expect("Failed to write modified OpenAPI specification to file");
    }

    #[cfg(any(feature = "v1", feature = "v2"))]
    println!("Successfully saved OpenAPI specification file at '{relative_file_path}'");

    #[cfg(not(any(feature = "v1", feature = "v2")))]
    println!("No feature enabled to generate OpenAPI specification, please enable either 'v1' or 'v2' feature");
}


impl RefundMetricAccumulator for RefundReasonAccumulator {
    type MetricOutput = Option<u64>;

    fn add_metrics_bucket(&mut self, metrics: &RefundMetricRow) {
        if let Some(count) = metrics.count {
            if let Ok(count_u64) = u64::try_from(count) {
                self.count += count_u64;
            }
        }
    }

    fn collect(self) -> Self::MetricOutput {
        Some(self.count)
    }
}

impl RefundMetricAccumulator for for RefundReasonAccumulator


pub struct RazorpayErrorMessage {
    pub message: String,
}


impl api::Refund for Deutschebank {}


pub struct ApiEventMetricRow {
    pub latency: Option<u64>,
    pub api_count: Option<u64>,
    pub status_code_count: Option<u64>,
    #[serde(with = "common_utils::custom_serde::iso8601::option")]
    pub start_bucket: Option<PrimitiveDateTime>,
    #[serde(with = "common_utils::custom_serde::iso8601::option")]
    pub end_bucket: Option<PrimitiveDateTime>,
}


impl ConnectorSpecifications for Fiservemea {
    fn get_connector_about(&self) -> Option<&'static ConnectorInfo> {
        Some(&FISERVEMEA_CONNECTOR_INFO)
    }

    fn get_supported_payment_methods(&self) -> Option<&'static SupportedPaymentMethods> {
        Some(&*FISERVEMEA_SUPPORTED_PAYMENT_METHODS)
    }

    fn get_supported_webhook_flows(&self) -> Option<&'static [enums::EventClass]> {
        Some(&FISERVEMEA_SUPPORTED_WEBHOOK_FLOWS)
    }
}

impl ConnectorSpecifications for for Fiservemea


pub async fn payments_manual_update(
    state: SessionState,
    req: api_models::payments::PaymentsManualUpdateRequest,
) -> RouterResponse<api_models::payments::PaymentsManualUpdateResponse> {
    let api_models::payments::PaymentsManualUpdateRequest {
        payment_id,
        attempt_id,
        merchant_id,
        attempt_status,
        error_code,
        error_message,
        error_reason,
        connector_transaction_id,
    } = req;
    let key_manager_state = &(&state).into();
    let key_store = state
        .store
        .get_merchant_key_store_by_merchant_id(
            key_manager_state,
            &merchant_id,
            &state.store.get_master_key().to_vec().into(),
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)
        .attach_printable("Error while fetching the key store by merchant_id")?;
    let merchant_account = state
        .store
        .find_merchant_account_by_merchant_id(key_manager_state, &merchant_id, &key_store)
        .await
        .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)
        .attach_printable("Error while fetching the merchant_account by merchant_id")?;
    let payment_attempt = state
        .store
        .find_payment_attempt_by_payment_id_merchant_id_attempt_id(
            &payment_id,
            &merchant_id,
            &attempt_id.clone(),
            merchant_account.storage_scheme,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)
        .attach_printable(
            "Error while fetching the payment_attempt by payment_id, merchant_id and attempt_id",
        )?;

    let payment_intent = state
        .store
        .find_payment_intent_by_payment_id_merchant_id(
            key_manager_state,
            &payment_id,
            merchant_account.get_id(),
            &key_store,
            merchant_account.storage_scheme,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)
        .attach_printable("Error while fetching the payment_intent by payment_id, merchant_id")?;

    let option_gsm = if let Some(((code, message), connector_name)) = error_code
        .as_ref()
        .zip(error_message.as_ref())
        .zip(payment_attempt.connector.as_ref())
    {
        helpers::get_gsm_record(
            &state,
            Some(code.to_string()),
            Some(message.to_string()),
            connector_name.to_string(),
            // We need to get the unified_code and unified_message of the Authorize flow
            "Authorize".to_string(),
        )
        .await
    } else {
        None
    };
    // Update the payment_attempt
    let attempt_update = storage::PaymentAttemptUpdate::ManualUpdate {
        status: attempt_status,
        error_code,
        error_message,
        error_reason,
        updated_by: merchant_account.storage_scheme.to_string(),
        unified_code: option_gsm.as_ref().and_then(|gsm| gsm.unified_code.clone()),
        unified_message: option_gsm.and_then(|gsm| gsm.unified_message),
        connector_transaction_id,
    };
    let updated_payment_attempt = state
        .store
        .update_payment_attempt_with_attempt_id(
            payment_attempt.clone(),
            attempt_update,
            merchant_account.storage_scheme,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)
        .attach_printable("Error while updating the payment_attempt")?;
    // If the payment_attempt is active attempt for an intent, update the intent status
    if payment_intent.active_attempt.get_id() == payment_attempt.attempt_id {
        let intent_status = enums::IntentStatus::foreign_from(updated_payment_attempt.status);
        let payment_intent_update = storage::PaymentIntentUpdate::ManualUpdate {
            status: Some(intent_status),
            updated_by: merchant_account.storage_scheme.to_string(),
        };
        state
            .store
            .update_payment_intent(
                key_manager_state,
                payment_intent,
                payment_intent_update,
                &key_store,
                merchant_account.storage_scheme,
            )
            .await
            .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)
            .attach_printable("Error while updating payment_intent")?;
    }
    Ok(services::ApplicationResponse::Json(
        api_models::payments::PaymentsManualUpdateResponse {
            payment_id: updated_payment_attempt.payment_id,
            attempt_id: updated_payment_attempt.attempt_id,
            merchant_id: updated_payment_attempt.merchant_id,
            attempt_status: updated_payment_attempt.status,
            error_code: updated_payment_attempt.error_code,
            error_message: updated_payment_attempt.error_message,
            error_reason: updated_payment_attempt.error_reason,
            connector_transaction_id: updated_payment_attempt.connector_transaction_id,
        },
    ))
}


impl Encryption {
    pub fn new(item: Secret<Vec<u8>, EncryptionStrategy>) -> Self {
        Self { inner: item }
    }

    #[inline]
    pub fn into_inner(self) -> Secret<Vec<u8>, EncryptionStrategy> {
        self.inner
    }

    #[inline]
    pub fn get_inner(&self) -> &Secret<Vec<u8>, EncryptionStrategy> {
        &self.inner
    }
}


impl api::MandateSetup for Payme {}


pub struct DisputeTransaction {
    pub amount: StringMajorUnit,
    pub id: String,
}


pub struct PhoneDetails {
    pub number: Option<Secret<String>>,
    pub country_code: Option<String>,
}


File: crates/hyperswitch_domain_models/src/payments/payment_intent.rs
 Public functions: 4
 Public structs: 6

#[cfg(feature = "v1")]
use common_utils::consts::PAYMENTS_LIST_MAX_LIMIT_V2;
#[cfg(feature = "v2")]
use common_utils::errors::ParsingError;
#[cfg(feature = "v2")]
use common_utils::ext_traits::{Encode, ValueExt};
use common_utils::{
    consts::PAYMENTS_LIST_MAX_LIMIT_V1,
    crypto::Encryptable,
    encryption::Encryption,
    errors::{CustomResult, ValidationError},
    id_type,
    pii::{self, Email},
    type_name,
    types::{
        keymanager::{self, KeyManagerState, ToEncryptable},
        CreatedBy, MinorUnit,
    },
};
use diesel_models::{
    PaymentIntent as DieselPaymentIntent, PaymentIntentNew as DieselPaymentIntentNew,
};
use error_stack::ResultExt;
#[cfg(feature = "v2")]
use masking::ExposeInterface;
use masking::{Deserialize, PeekInterface, Secret};
use serde::Serialize;
use time::PrimitiveDateTime;

#[cfg(all(feature = "v1", feature = "olap"))]
use super::payment_attempt::PaymentAttempt;
use super::PaymentIntent;
#[cfg(feature = "v2")]
use crate::address::Address;
#[cfg(feature = "v2")]
use crate::routing;
use crate::{
    behaviour,
    merchant_key_store::MerchantKeyStore,
    type_encryption::{crypto_operation, CryptoOperation},
};
#[cfg(feature = "v1")]
use crate::{errors, RemoteStorageObject};

#[async_trait::async_trait]
pub trait PaymentIntentInterface {
    type Error;
    async fn update_payment_intent(
        &self,
        state: &KeyManagerState,
        this: PaymentIntent,
        payment_intent: PaymentIntentUpdate,
        merchant_key_store: &MerchantKeyStore,
        storage_scheme: common_enums::MerchantStorageScheme,
    ) -> error_stack::Result<PaymentIntent, Self::Error>;

    async fn insert_payment_intent(
        &self,
        state: &KeyManagerState,
        new: PaymentIntent,
        merchant_key_store: &MerchantKeyStore,
        storage_scheme: common_enums::MerchantStorageScheme,
    ) -> error_stack::Result<PaymentIntent, Self::Error>;

    #[cfg(feature = "v1")]
    async fn find_payment_intent_by_payment_id_merchant_id(
        &self,
        state: &KeyManagerState,
        payment_id: &id_type::PaymentId,
        merchant_id: &id_type::MerchantId,
        merchant_key_store: &MerchantKeyStore,
        storage_scheme: common_enums::MerchantStorageScheme,
    ) -> error_stack::Result<PaymentIntent, Self::Error>;
    #[cfg(feature = "v2")]
    async fn find_payment_intent_by_merchant_reference_id_profile_id(
        &self,
        state: &KeyManagerState,
        merchant_reference_id: &id_type::PaymentReferenceId,
        profile_id: &id_type::ProfileId,
        merchant_key_store: &MerchantKeyStore,
        storage_scheme: &common_enums::MerchantStorageScheme,
    ) -> error_stack::Result<PaymentIntent, Self::Error>;

    #[cfg(feature = "v2")]
    async fn find_payment_intent_by_id(
        &self,
        state: &KeyManagerState,
        id: &id_type::GlobalPaymentId,
        merchant_key_store: &MerchantKeyStore,
        storage_scheme: common_enums::MerchantStorageScheme,
    ) -> error_stack::Result<PaymentIntent, Self::Error>;

    #[cfg(all(feature = "v1", feature = "olap"))]
    async fn filter_payment_intent_by_constraints(
        &self,
        state: &KeyManagerState,
        merchant_id: &id_type::MerchantId,
        filters: &PaymentIntentFetchConstraints,
        merchant_key_store: &MerchantKeyStore,
        storage_scheme: common_enums::MerchantStorageScheme,
    ) -> error_stack::Result<Vec<PaymentIntent>, Self::Error>;

    #[cfg(all(feature = "v1", feature = "olap"))]
    async fn filter_payment_intents_by_time_range_constraints(
        &self,
        state: &KeyManagerState,
        merchant_id: &id_type::MerchantId,
        time_range: &common_utils::types::TimeRange,
        merchant_key_store: &MerchantKeyStore,
        storage_scheme: common_enums::MerchantStorageScheme,
    ) -> error_stack::Result<Vec<PaymentIntent>, Self::Error>;

    #[cfg(feature = "olap")]
    async fn get_intent_status_with_count(
        &self,
        merchant_id: &id_type::MerchantId,
        profile_id_list: Option<Vec<id_type::ProfileId>>,
        constraints: &common_utils::types::TimeRange,
    ) -> error_stack::Result<Vec<(common_enums::IntentStatus, i64)>, Self::Error>;

    #[cfg(all(feature = "v1", feature = "olap"))]
    async fn get_filtered_payment_intents_attempt(
        &self,
        state: &KeyManagerState,
        merchant_id: &id_type::MerchantId,
        constraints: &PaymentIntentFetchConstraints,
        merchant_key_store: &MerchantKeyStore,
        storage_scheme: common_enums::MerchantStorageScheme,
    ) -> error_stack::Result<Vec<(PaymentIntent, PaymentAttempt)>, Self::Error>;

    #[cfg(all(feature = "v2", feature = "olap"))]
    async fn get_filtered_payment_intents_attempt(
        &self,
        state: &KeyManagerState,
        merchant_id: &id_type::MerchantId,
        constraints: &PaymentIntentFetchConstraints,
        merchant_key_store: &MerchantKeyStore,
        storage_scheme: common_enums::MerchantStorageScheme,
    ) -> error_stack::Result<
        Vec<(
            PaymentIntent,
            Option<super::payment_attempt::PaymentAttempt>,
        )>,
        Self::Error,
    >;

    #[cfg(all(feature = "v2", feature = "olap"))]
    async fn get_filtered_active_attempt_ids_for_total_count(
        &self,
        merchant_id: &id_type::MerchantId,
        constraints: &PaymentIntentFetchConstraints,
        storage_scheme: common_enums::MerchantStorageScheme,
    ) -> error_stack::Result<Vec<Option<String>>, Self::Error>;

    #[cfg(all(feature = "v1", feature = "olap"))]
    async fn get_filtered_active_attempt_ids_for_total_count(
        &self,
        merchant_id: &id_type::MerchantId,
        constraints: &PaymentIntentFetchConstraints,
        storage_scheme: common_enums::MerchantStorageScheme,
    ) -> error_stack::Result<Vec<String>, Self::Error>;
}

#[derive(Clone, Debug, PartialEq, router_derive::DebugAsDisplay, Serialize, Deserialize)]
pub struct CustomerData {
    pub name: Option<Secret<String>>,
    pub email: Option<Email>,
    pub phone: Option<Secret<String>>,
    pub phone_country_code: Option<String>,
    pub tax_registration_id: Option<Secret<String>>,
}

#[cfg(feature = "v2")]
#[derive(Debug, Clone, Serialize)]
pub struct PaymentIntentUpdateFields {
    pub amount: Option<MinorUnit>,
    pub currency: Option<common_enums::Currency>,
    pub shipping_cost: Option<MinorUnit>,
    pub tax_details: Option<diesel_models::TaxDetails>,
    pub skip_external_tax_calculation: Option<common_enums::TaxCalculationOverride>,
    pub skip_surcharge_calculation: Option<common_enums::SurchargeCalculationOverride>,
    pub surcharge_amount: Option<MinorUnit>,
    pub tax_on_surcharge: Option<MinorUnit>,
    pub routing_algorithm_id: Option<id_type::RoutingId>,
    pub capture_method: Option<common_enums::CaptureMethod>,
    pub authentication_type: Option<common_enums::AuthenticationType>,
    pub billing_address: Option<Encryptable<Address>>,
    pub shipping_address: Option<Encryptable<Address>>,
    pub customer_present: Option<common_enums::PresenceOfCustomerDuringPayment>,
    pub description: Option<common_utils::types::Description>,
    pub return_url: Option<common_utils::types::Url>,
    pub setup_future_usage: Option<common_enums::FutureUsage>,
    pub apply_mit_exemption: Option<common_enums::MitExemptionRequest>,
    pub statement_descriptor: Option<common_utils::types::StatementDescriptor>,
    pub order_details: Option<Vec<Secret<diesel_models::types::OrderDetailsWithAmount>>>,
    pub allowed_payment_method_types: Option<Vec<common_enums::PaymentMethodType>>,
    pub metadata: Option<pii::SecretSerdeValue>,
    pub connector_metadata: Option<pii::SecretSerdeValue>,
    pub feature_metadata: Option<diesel_models::types::FeatureMetadata>,
    pub payment_link_config: Option<diesel_models::PaymentLinkConfigRequestForPayments>,
    pub request_incremental_authorization: Option<common_enums::RequestIncrementalAuthorization>,
    pub session_expiry: Option<PrimitiveDateTime>,
    pub frm_metadata: Option<pii::SecretSerdeValue>,
    pub request_external_three_ds_authentication:
        Option<common_enums::External3dsAuthenticationRequest>,
    pub active_attempt_id: Option<Option<id_type::GlobalAttemptId>>,
    // updated_by is set internally, field not present in request
    pub updated_by: String,
    pub force_3ds_challenge: Option<bool>,
    pub is_iframe_redirection_enabled: Option<bool>,
}

#[cfg(feature = "v1")]
#[derive(Debug, Clone, Serialize)]
pub struct PaymentIntentUpdateFields {
    pub amount: MinorUnit,
    pub currency: common_enums::Currency,
    pub setup_future_usage: Option<common_enums::FutureUsage>,
    pub status: common_enums::IntentStatus,
    pub customer_id: Option<id_type::CustomerId>,
    pub shipping_address_id: Option<String>,
    pub billing_address_id: Option<String>,
    pub return_url: Option<String>,
    pub business_country: Option<common_enums::CountryAlpha2>,
    pub business_label: Option<String>,
    pub description: Option<String>,
    pub statement_descriptor_name: Option<String>,
    pub statement_descriptor_suffix: Option<String>,
    pub order_details: Option<Vec<pii::SecretSerdeValue>>,
    pub metadata: Option<serde_json::Value>,
    pub frm_metadata: Option<pii::SecretSerdeValue>,
    pub payment_confirm_source: Option<common_enums::PaymentSource>,
    pub updated_by: String,
    pub fingerprint_id: Option<String>,
    pub session_expiry: Option<PrimitiveDateTime>,
    pub request_external_three_ds_authentication: Option<bool>,
    pub customer_details: Option<Encryptable<Secret<serde_json::Value>>>,
    pub billing_details: Option<Encryptable<Secret<serde_json::Value>>>,
    pub merchant_order_reference_id: Option<String>,
    pub shipping_details: Option<Encryptable<Secret<serde_json::Value>>>,
    pub is_payment_processor_token_flow: Option<bool>,
    pub tax_details: Option<diesel_models::TaxDetails>,
    pub force_3ds_challenge: Option<bool>,
    pub is_iframe_redirection_enabled: Option<bool>,
    pub tax_status: Option<common_enums::TaxStatus>,
    pub discount_amount: Option<MinorUnit>,
    pub order_date: Option<PrimitiveDateTime>,
    pub shipping_amount_tax: Option<MinorUnit>,
    pub duty_amount: Option<MinorUnit>,
    pub is_confirm_operation: bool,
    pub payment_channel: Option<common_enums::PaymentChannel>,
    pub feature_metadata: Option<Secret<serde_json::Value>>,
    pub enable_partial_authorization: Option<bool>,
}

#[cfg(feature = "v1")]
#[derive(Debug, Clone, Serialize)]
pub enum PaymentIntentUpdate {
    ResponseUpdate {
        status: common_enums::IntentStatus,
        amount_captured: Option<MinorUnit>,
        updated_by: String,
        fingerprint_id: Option<String>,
        incremental_authorization_allowed: Option<bool>,
        feature_metadata: Option<Secret<serde_json::Value>>,
    },
    MetadataUpdate {
        metadata: serde_json::Value,
        updated_by: String,
    },
    Update(Box<PaymentIntentUpdateFields>),
    PaymentCreateUpdate {
        return_url: Option<String>,
        status: Option<common_enums::IntentStatus>,
        customer_id: Option<id_type::CustomerId>,
        shipping_address_id: Option<String>,
        billing_address_id: Option<String>,
        customer_details: Option<Encryptable<Secret<serde_json::Value>>>,
        updated_by: String,
    },
    MerchantStatusUpdate {
        status: common_enums::IntentStatus,
        shipping_address_id: Option<String>,
        billing_address_id: Option<String>,
        updated_by: String,
    },
    PGStatusUpdate {
        status: common_enums::IntentStatus,
        incremental_authorization_allowed: Option<bool>,
        updated_by: String,
        feature_metadata: Option<Secret<serde_json::Value>>,
    },
    PaymentAttemptAndAttemptCountUpdate {
        active_attempt_id: String,
        attempt_count: i16,
        updated_by: String,
    },
    StatusAndAttemptUpdate {
        status: common_enums::IntentStatus,
        active_attempt_id: String,
        attempt_count: i16,
        updated_by: String,
    },
    ApproveUpdate {
        status: common_enums::IntentStatus,
        merchant_decision: Option<String>,
        updated_by: String,
    },
    RejectUpdate {
        status: common_enums::IntentStatus,
        merchant_decision: Option<String>,
        updated_by: String,
    },
    SurchargeApplicableUpdate {
        surcharge_applicable: bool,
        updated_by: String,
    },
    IncrementalAuthorizationAmountUpdate {
        amount: MinorUnit,
    },
    AuthorizationCountUpdate {
        authorization_count: i32,
    },
    CompleteAuthorizeUpdate {
        shipping_address_id: Option<String>,
    },
    ManualUpdate {
        status: Option<common_enums::IntentStatus>,
        updated_by: String,
    },
    SessionResponseUpdate {
        tax_details: diesel_models::TaxDetails,
        shipping_address_id: Option<String>,
        updated_by: String,
        shipping_details: Option<Encryptable<Secret<serde_json::Value>>>,
    },
}

#[cfg(feature = "v1")]
impl PaymentIntentUpdate {
    pub fn is_confirm_operation(&self) -> bool {
        match self {
            Self::Update(value) => value.is_confirm_operation,
            _ => false,
        }
    }
}

#[cfg(feature = "v2")]
#[derive(Debug, Clone, Serialize)]
pub enum PaymentIntentUpdate {
    /// PreUpdate tracker of ConfirmIntent
    ConfirmIntent {
        status: common_enums::IntentStatus,
        active_attempt_id: Option<id_type::GlobalAttemptId>,
        updated_by: String,
    },
    /// PostUpdate tracker of ConfirmIntent
    ConfirmIntentPostUpdate {
        status: common_enums::IntentStatus,
        amount_captured: Option<MinorUnit>,
        updated_by: String,
        feature_metadata: Option<Box<diesel_models::types::FeatureMetadata>>,
    },
    /// SyncUpdate of ConfirmIntent in PostUpdateTrackers
    SyncUpdate {
        status: common_enums::IntentStatus,
        amount_captured: Option<MinorUnit>,
        updated_by: String,
    },
    CaptureUpdate {
        status: common_enums::IntentStatus,
        amount_captured: Option<MinorUnit>,
        updated_by: String,
    },
    /// Update the payment intent details on payment sdk session call, before calling the connector.
    SessionIntentUpdate {
        prerouting_algorithm: routing::PaymentRoutingInfo,
        updated_by: String,
    },
    RecordUpdate {
        status: common_enums::IntentStatus,
        feature_metadata: Box<Option<diesel_models::types::FeatureMetadata>>,
        updated_by: String,
        active_attempt_id: Option<id_type::GlobalAttemptId>,
    },
    /// UpdateIntent
    UpdateIntent(Box<PaymentIntentUpdateFields>),
}

#[cfg(feature = "v2")]
impl PaymentIntentUpdate {
    pub fn is_confirm_operation(&self) -> bool {
        matches!(self, Self::ConfirmIntent { .. })
    }
}

#[cfg(feature = "v1")]
#[derive(Clone, Debug, Default)]
pub struct PaymentIntentUpdateInternal {
    pub amount: Option<MinorUnit>,
    pub currency: Option<common_enums::Currency>,
    pub status: Option<common_enums::IntentStatus>,
    pub amount_captured: Option<MinorUnit>,
    pub customer_id: Option<id_type::CustomerId>,
    pub return_url: Option<String>,
    pub setup_future_usage: Option<common_enums::FutureUsage>,
    pub off_session: Option<bool>,
    pub metadata: Option<serde_json::Value>,
    pub billing_address_id: Option<String>,
    pub shipping_address_id: Option<String>,
    pub modified_at: Option<PrimitiveDateTime>,
    pub active_attempt_id: Option<String>,
    pub business_country: Option<common_enums::CountryAlpha2>,
    pub business_label: Option<String>,
    pub description: Option<String>,
    pub statement_descriptor_name: Option<String>,
    pub statement_descriptor_suffix: Option<String>,
    pub order_details: Option<Vec<pii::SecretSerdeValue>>,
    pub attempt_count: Option<i16>,
    // Denotes the action(approve or reject) taken by merchant in case of manual review.
    // Manual review can occur when the transaction is marked as risky by the frm_processor, payment processor or when there is underpayment/over payment incase of crypto payment
    pub merchant_decision: Option<String>,
    pub payment_confirm_source: Option<common_enums::PaymentSource>,

    pub updated_by: String,
    pub surcharge_applicable: Option<bool>,
    pub incremental_authorization_allowed: Option<bool>,
    pub authorization_count: Option<i32>,
    pub fingerprint_id: Option<String>,
    pub session_expiry: Option<PrimitiveDateTime>,
    pub request_external_three_ds_authentication: Option<bool>,
    pub frm_metadata: Option<pii::SecretSerdeValue>,
    pub customer_details: Option<Encryptable<Secret<serde_json::Value>>>,
    pub billing_details: Option<Encryptable<Secret<serde_json::Value>>>,
    pub merchant_order_reference_id: Option<String>,
    pub shipping_details: Option<Encryptable<Secret<serde_json::Value>>>,
    pub is_payment_processor_token_flow: Option<bool>,
    pub tax_details: Option<diesel_models::TaxDetails>,
    pub force_3ds_challenge: Option<bool>,
    pub is_iframe_redirection_enabled: Option<bool>,
    pub payment_channel: Option<common_enums::PaymentChannel>,
    pub feature_metadata: Option<Secret<serde_json::Value>>,
    pub tax_status: Option<common_enums::TaxStatus>,
    pub discount_amount: Option<MinorUnit>,
    pub order_date: Option<PrimitiveDateTime>,
    pub shipping_amount_tax: Option<MinorUnit>,
    pub duty_amount: Option<MinorUnit>,
    pub enable_partial_authorization: Option<bool>,
}

// This conversion is used in the `update_payment_intent` function
#[cfg(feature = "v2")]
impl TryFrom<PaymentIntentUpdate> for diesel_models::PaymentIntentUpdateInternal {
    type Error = error_stack::Report<ParsingError>;
    fn try_from(payment_intent_update: PaymentIntentUpdate) -> Result<Self, Self::Error> {
        match payment_intent_update {
            PaymentIntentUpdate::ConfirmIntent {
                status,
                active_attempt_id,
                updated_by,
            } => Ok(Self {
                status: Some(status),
                active_attempt_id: Some(active_attempt_id),
                prerouting_algorithm: None,
                modified_at: common_utils::date_time::now(),
                amount: None,
                amount_captured: None,
                currency: None,
                shipping_cost: None,
                tax_details: None,
                skip_external_tax_calculation: None,
                surcharge_applicable: None,
                surcharge_amount: None,
                tax_on_surcharge: None,
                routing_algorithm_id: None,
                capture_method: None,
                authentication_type: None,
                billing_address: None,
                shipping_address: None,
                customer_present: None,
                description: None,
                return_url: None,
                setup_future_usage: None,
                apply_mit_exemption: None,
                statement_descriptor: None,
                order_details: None,
                allowed_payment_method_types: None,
                metadata: None,
                connector_metadata: None,
                feature_metadata: None,
                payment_link_config: None,
                request_incremental_authorization: None,
                session_expiry: None,
                frm_metadata: None,
                request_external_three_ds_authentication: None,
                updated_by,
                force_3ds_challenge: None,
                is_iframe_redirection_enabled: None,
            }),

            PaymentIntentUpdate::ConfirmIntentPostUpdate {
                status,
                updated_by,
                amount_captured,
                feature_metadata,
            } => Ok(Self {
                status: Some(status),
                active_attempt_id: None,
                prerouting_algorithm: None,
                modified_at: common_utils::date_time::now(),
                amount_captured,
                amount: None,
                currency: None,
                shipping_cost: None,
                tax_details: None,
                skip_external_tax_calculation: None,
                surcharge_applicable: None,
                surcharge_amount: None,
                tax_on_surcharge: None,
                routing_algorithm_id: None,
                capture_method: None,
                authentication_type: None,
                billing_address: None,
                shipping_address: None,
                customer_present: None,
                description: None,
                return_url: None,
                setup_future_usage: None,
                apply_mit_exemption: None,
                statement_descriptor: None,
                order_details: None,
                allowed_payment_method_types: None,
                metadata: None,
                connector_metadata: None,
                feature_metadata: feature_metadata.map(|val| *val),
                payment_link_config: None,
                request_incremental_authorization: None,
                session_expiry: None,
                frm_metadata: None,
                request_external_three_ds_authentication: None,
                updated_by,
                force_3ds_challenge: None,
                is_iframe_redirection_enabled: None,
            }),
            PaymentIntentUpdate::SyncUpdate {
                status,
                amount_captured,
                updated_by,
            } => Ok(Self {
                status: Some(status),
                active_attempt_id: None,
                prerouting_algorithm: None,
                modified_at: common_utils::date_time::now(),
                amount: None,
                currency: None,
                amount_captured,
                shipping_cost: None,
                tax_details: None,
                skip_external_tax_calculation: None,
                surcharge_applicable: None,
                surcharge_amount: None,
                tax_on_surcharge: None,
                routing_algorithm_id: None,
                capture_method: None,
                authentication_type: None,
                billing_address: None,
                shipping_address: None,
                customer_present: None,
                description: None,
                return_url: None,
                setup_future_usage: None,
                apply_mit_exemption: None,
                statement_descriptor: None,
                order_details: None,
                allowed_payment_method_types: None,
                metadata: None,
                connector_metadata: None,
                feature_metadata: None,
                payment_link_config: None,
                request_incremental_authorization: None,
                session_expiry: None,
                frm_metadata: None,
                request_external_three_ds_authentication: None,
                updated_by,
                force_3ds_challenge: None,
                is_iframe_redirection_enabled: None,
            }),
            PaymentIntentUpdate::CaptureUpdate {
                status,
                amount_captured,
                updated_by,
            } => Ok(Self {
                status: Some(status),
                amount_captured,
                active_attempt_id: None,
                prerouting_algorithm: None,
                modified_at: common_utils::date_time::now(),
                amount: None,
                currency: None,
                shipping_cost: None,
                tax_details: None,
                skip_external_tax_calculation: None,
                surcharge_applicable: None,
                surcharge_amount: None,
                tax_on_surcharge: None,
                routing_algorithm_id: None,
                capture_method: None,
                authentication_type: None,
                billing_address: None,
                shipping_address: None,
                customer_present: None,
                description: None,
                return_url: None,
                setup_future_usage: None,
                apply_mit_exemption: None,
                statement_descriptor: None,
                order_details: None,
                allowed_payment_method_types: None,
                metadata: None,
                connector_metadata: None,
                feature_metadata: None,
                payment_link_config: None,
                request_incremental_authorization: None,
                session_expiry: None,
                frm_metadata: None,
                request_external_three_ds_authentication: None,
                updated_by,
                force_3ds_challenge: None,
                is_iframe_redirection_enabled: None,
            }),
            PaymentIntentUpdate::SessionIntentUpdate {
                prerouting_algorithm,
                updated_by,
            } => Ok(Self {
                status: None,
                active_attempt_id: None,
                modified_at: common_utils::date_time::now(),
                amount_captured: None,
                prerouting_algorithm: Some(
                    prerouting_algorithm
                        .encode_to_value()
                        .attach_printable("Failed to Serialize prerouting_algorithm")?,
                ),
                amount: None,
                currency: None,
                shipping_cost: None,
                tax_details: None,
                skip_external_tax_calculation: None,
                surcharge_applicable: None,
                surcharge_amount: None,
                tax_on_surcharge: None,
                routing_algorithm_id: None,
                capture_method: None,
                authentication_type: None,
                billing_address: None,
                shipping_address: None,
                customer_present: None,
                description: None,
                return_url: None,
                setup_future_usage: None,
                apply_mit_exemption: None,
                statement_descriptor: None,
                order_details: None,
                allowed_payment_method_types: None,
                metadata: None,
                connector_metadata: None,
                feature_metadata: None,
                payment_link_config: None,
                request_incremental_authorization: None,
                session_expiry: None,
                frm_metadata: None,
                request_external_three_ds_authentication: None,
                updated_by,
                force_3ds_challenge: None,
                is_iframe_redirection_enabled: None,
            }),
            PaymentIntentUpdate::UpdateIntent(boxed_intent) => {
                let PaymentIntentUpdateFields {
                    amount,
                    currency,
                    shipping_cost,
                    tax_details,
                    skip_external_tax_calculation,
                    skip_surcharge_calculation,
                    surcharge_amount,
                    tax_on_surcharge,
                    routing_algorithm_id,
                    capture_method,
                    authentication_type,
                    billing_address,
                    shipping_address,
                    customer_present,
                    description,
                    return_url,
                    setup_future_usage,
                    apply_mit_exemption,
                    statement_descriptor,
                    order_details,
                    allowed_payment_method_types,
                    metadata,
                    connector_metadata,
                    feature_metadata,
                    payment_link_config,
                    request_incremental_authorization,
                    session_expiry,
                    frm_metadata,
                    request_external_three_ds_authentication,
                    active_attempt_id,
                    updated_by,
                    force_3ds_challenge,
                    is_iframe_redirection_enabled,
                } = *boxed_intent;
                Ok(Self {
                    status: None,
                    active_attempt_id,
                    prerouting_algorithm: None,
                    modified_at: common_utils::date_time::now(),
                    amount_captured: None,
                    amount,
                    currency,
                    shipping_cost,
                    tax_details,
                    skip_external_tax_calculation: skip_external_tax_calculation
                        .map(|val| val.as_bool()),
                    surcharge_applicable: skip_surcharge_calculation.map(|val| val.as_bool()),
                    surcharge_amount,
                    tax_on_surcharge,
                    routing_algorithm_id,
                    capture_method,
                    authentication_type,
                    billing_address: billing_address.map(Encryption::from),
                    shipping_address: shipping_address.map(Encryption::from),
                    customer_present: customer_present.map(|val| val.as_bool()),
                    description,
                    return_url,
                    setup_future_usage,
                    apply_mit_exemption: apply_mit_exemption.map(|val| val.as_bool()),
                    statement_descriptor,
                    order_details,
                    allowed_payment_method_types: allowed_payment_method_types
                        .map(|allowed_payment_method_types| {
                            allowed_payment_method_types.encode_to_value()
                        })
                        .and_then(|r| r.ok().map(Secret::new)),
                    metadata,
                    connector_metadata,
                    feature_metadata,
                    payment_link_config,
                    request_incremental_authorization,
                    session_expiry,
                    frm_metadata,
                    request_external_three_ds_authentication:
                        request_external_three_ds_authentication.map(|val| val.as_bool()),
                    updated_by,
                    force_3ds_challenge,
                    is_iframe_redirection_enabled,
                })
            }
            PaymentIntentUpdate::RecordUpdate {
                status,
                feature_metadata,
                updated_by,
                active_attempt_id,
            } => Ok(Self {
                status: Some(status),
                amount_captured: None,
                active_attempt_id: Some(active_attempt_id),
                modified_at: common_utils::date_time::now(),
                amount: None,
                currency: None,
                shipping_cost: None,
                tax_details: None,
                skip_external_tax_calculation: None,
                surcharge_applicable: None,
                surcharge_amount: None,
                tax_on_surcharge: None,
                routing_algorithm_id: None,
                capture_method: None,
                authentication_type: None,
                billing_address: None,
                shipping_address: None,
                customer_present: None,
                description: None,
                return_url: None,
                setup_future_usage: None,
                apply_mit_exemption: None,
                statement_descriptor: None,
                order_details: None,
                allowed_payment_method_types: None,
                metadata: None,
                connector_metadata: None,
                feature_metadata: *feature_metadata,
                payment_link_config: None,
                request_incremental_authorization: None,
                prerouting_algorithm: None,
                session_expiry: None,
                frm_metadata: None,
                request_external_three_ds_authentication: None,
                updated_by,
                force_3ds_challenge: None,
                is_iframe_redirection_enabled: None,
            }),
        }
    }
}

#[cfg(feature = "v1")]
impl From<PaymentIntentUpdate> for PaymentIntentUpdateInternal {
    fn from(payment_intent_update: PaymentIntentUpdate) -> Self {
        match payment_intent_update {
            PaymentIntentUpdate::MetadataUpdate {
                metadata,
                updated_by,
            } => Self {
                metadata: Some(metadata),
                modified_at: Some(common_utils::date_time::now()),
                updated_by,
                ..Default::default()
            },
            PaymentIntentUpdate::Update(value) => Self {
                amount: Some(value.amount),
                currency: Some(value.currency),
                setup_future_usage: value.setup_future_usage,
                status: Some(value.status),
                customer_id: value.customer_id,
                shipping_address_id: value.shipping_address_id,
                billing_address_id: value.billing_address_id,
                return_url: value.return_url,
                business_country: value.business_country,
                business_label: value.business_label,
                description: value.description,
                statement_descriptor_name: value.statement_descriptor_name,
                statement_descriptor_suffix: value.statement_descriptor_suffix,
                order_details: value.order_details,
                metadata: value.metadata,
                payment_confirm_source: value.payment_confirm_source,
                updated_by: value.updated_by,
                session_expiry: value.session_expiry,
                fingerprint_id: value.fingerprint_id,
                request_external_three_ds_authentication: value
                    .request_external_three_ds_authentication,
                frm_metadata: value.frm_metadata,
                customer_details: value.customer_details,
                billing_details: value.billing_details,
                merchant_order_reference_id: value.merchant_order_reference_id,
                shipping_details: value.shipping_details,
                is_payment_processor_token_flow: value.is_payment_processor_token_flow,
                tax_details: value.tax_details,
                tax_status: value.tax_status,
                discount_amount: value.discount_amount,
                order_date: value.order_date,
                shipping_amount_tax: value.shipping_amount_tax,
                duty_amount: value.duty_amount,
                ..Default::default()
            },
            PaymentIntentUpdate::PaymentCreateUpdate {
                return_url,
                status,
                customer_id,
                shipping_address_id,
                billing_address_id,
                customer_details,
                updated_by,
            } => Self {
                return_url,
                status,
                customer_id,
                shipping_address_id,
                billing_address_id,
                customer_details,
                modified_at: Some(common_utils::date_time::now()),
                updated_by,
                ..Default::default()
            },
            PaymentIntentUpdate::PGStatusUpdate {
                status,
                updated_by,
                incremental_authorization_allowed,
                feature_metadata,
            } => Self {
                status: Some(status),
                modified_at: Some(common_utils::date_time::now()),
                updated_by,
                incremental_authorization_allowed,
                feature_metadata,
                ..Default::default()
            },
            PaymentIntentUpdate::MerchantStatusUpdate {
                status,
                shipping_address_id,
                billing_address_id,
                updated_by,
            } => Self {
                status: Some(status),
                shipping_address_id,
                billing_address_id,
                modified_at: Some(common_utils::date_time::now()),
                updated_by,
                ..Default::default()
            },
            PaymentIntentUpdate::ResponseUpdate {
                // amount,
                // currency,
                status,
                amount_captured,
                fingerprint_id,
                // customer_id,
                updated_by,
                incremental_authorization_allowed,
                feature_metadata,
            } => Self {
                // amount,
                // currency: Some(currency),
                status: Some(status),
                amount_captured,
                fingerprint_id,
                // customer_id,
                modified_at: Some(common_utils::date_time::now()),
                updated_by,
                incremental_authorization_allowed,
                feature_metadata,
                ..Default::default()
            },
            PaymentIntentUpdate::PaymentAttemptAndAttemptCountUpdate {
                active_attempt_id,
                attempt_count,
                updated_by,
            } => Self {
                active_attempt_id: Some(active_attempt_id),
                attempt_count: Some(attempt_count),
                updated_by,
                ..Default::default()
            },
            PaymentIntentUpdate::StatusAndAttemptUpdate {
                status,
                active_attempt_id,
                attempt_count,
                updated_by,
            } => Self {
                status: Some(status),
                active_attempt_id: Some(active_attempt_id),
                attempt_count: Some(attempt_count),
                updated_by,
                ..Default::default()
            },
            PaymentIntentUpdate::ApproveUpdate {
                status,
                merchant_decision,
                updated_by,
            } => Self {
                status: Some(status),
                merchant_decision,
                updated_by,
                ..Default::default()
            },
            PaymentIntentUpdate::RejectUpdate {
                status,
                merchant_decision,
                updated_by,
            } => Self {
                status: Some(status),
                merchant_decision,
                updated_by,
                ..Default::default()
            },
            PaymentIntentUpdate::SurchargeApplicableUpdate {
                surcharge_applicable,
                updated_by,
            } => Self {
                surcharge_applicable: Some(surcharge_applicable),
                updated_by,
                ..Default::default()
            },
            PaymentIntentUpdate::IncrementalAuthorizationAmountUpdate { amount } => Self {
                amount: Some(amount),
                ..Default::default()
            },
            PaymentIntentUpdate::AuthorizationCountUpdate {
                authorization_count,
            } => Self {
                authorization_count: Some(authorization_count),
                ..Default::default()
            },
            PaymentIntentUpdate::CompleteAuthorizeUpdate {
                shipping_address_id,
            } => Self {
                shipping_address_id,
                ..Default::default()
            },
            PaymentIntentUpdate::ManualUpdate { status, updated_by } => Self {
                status,
                modified_at: Some(common_utils::date_time::now()),
                updated_by,
                ..Default::default()
            },
            PaymentIntentUpdate::SessionResponseUpdate {
                tax_details,
                shipping_address_id,
                updated_by,
                shipping_details,
            } => Self {
                tax_details: Some(tax_details),
                shipping_address_id,
                updated_by,
                shipping_details,
                ..Default::default()
            },
        }
    }
}

#[cfg(feature = "v1")]
use diesel_models::{
    PaymentIntentUpdate as DieselPaymentIntentUpdate,
    PaymentIntentUpdateFields as DieselPaymentIntentUpdateFields,
};

// TODO: check where this conversion is used
// #[cfg(feature = "v2")]
// impl From<PaymentIntentUpdate> for DieselPaymentIntentUpdate {
//     fn from(value: PaymentIntentUpdate) -> Self {
//         match value {
//             PaymentIntentUpdate::ConfirmIntent { status, updated_by } => {
//                 Self::ConfirmIntent { status, updated_by }
//             }
//             PaymentIntentUpdate::ConfirmIntentPostUpdate { status, updated_by } => {
//                 Self::ConfirmIntentPostUpdate { status, updated_by }
//             }
//         }
//     }
// }

#[cfg(feature = "v1")]
impl From<PaymentIntentUpdate> for DieselPaymentIntentUpdate {
    fn from(value: PaymentIntentUpdate) -> Self {
        match value {
            PaymentIntentUpdate::ResponseUpdate {
                status,
                amount_captured,
                fingerprint_id,
                updated_by,
                incremental_authorization_allowed,
                feature_metadata,
            } => Self::ResponseUpdate {
                status,
                amount_captured,
                fingerprint_id,
                updated_by,
                incremental_authorization_allowed,
                feature_metadata,
            },
            PaymentIntentUpdate::MetadataUpdate {
                metadata,
                updated_by,
            } => Self::MetadataUpdate {
                metadata,
                updated_by,
            },
            PaymentIntentUpdate::Update(value) => {
                Self::Update(Box::new(DieselPaymentIntentUpdateFields {
                    amount: value.amount,
                    currency: value.currency,
                    setup_future_usage: value.setup_future_usage,
                    status: value.status,
                    customer_id: value.customer_id,
                    shipping_address_id: value.shipping_address_id,
                    billing_address_id: value.billing_address_id,
                    return_url: value.return_url,
                    business_country: value.business_country,
                    business_label: value.business_label,
                    description: value.description,
                    statement_descriptor_name: value.statement_descriptor_name,
                    statement_descriptor_suffix: value.statement_descriptor_suffix,
                    order_details: value.order_details,
                    metadata: value.metadata,
                    payment_confirm_source: value.payment_confirm_source,
                    updated_by: value.updated_by,
                    session_expiry: value.session_expiry,
                    fingerprint_id: value.fingerprint_id,
                    request_external_three_ds_authentication: value
                        .request_external_three_ds_authentication,
                    frm_metadata: value.frm_metadata,
                    customer_details: value.customer_details.map(Encryption::from),
                    billing_details: value.billing_details.map(Encryption::from),
                    merchant_order_reference_id: value.merchant_order_reference_id,
                    shipping_details: value.shipping_details.map(Encryption::from),
                    is_payment_processor_token_flow: value.is_payment_processor_token_flow,
                    tax_details: value.tax_details,
                    force_3ds_challenge: value.force_3ds_challenge,


impl IncomingWebhook for Worldpay {
    fn get_webhook_source_verification_algorithm(
        &self,
        _request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<Box<dyn crypto::VerifySignature + Send>, errors::ConnectorError> {
        Ok(Box::new(crypto::Sha256))
    }

    fn get_webhook_source_verification_signature(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
        _connector_webhook_secrets: &api_models::webhooks::ConnectorWebhookSecrets,
    ) -> CustomResult<Vec<u8>, errors::ConnectorError> {
        let mut event_signature =
            get_header_key_value("Event-Signature", request.headers)?.split(',');
        let sign_header = event_signature
            .next_back()
            .ok_or(errors::ConnectorError::WebhookSignatureNotFound)?;
        let signature = sign_header
            .split('/')
            .next_back()
            .ok_or(errors::ConnectorError::WebhookSignatureNotFound)?;
        hex::decode(signature).change_context(errors::ConnectorError::WebhookResponseEncodingFailed)
    }

    fn get_webhook_source_verification_message(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
        _merchant_id: &common_utils::id_type::MerchantId,
        _connector_webhook_secrets: &api_models::webhooks::ConnectorWebhookSecrets,
    ) -> CustomResult<Vec<u8>, errors::ConnectorError> {
        Ok(request.body.to_vec())
    }

    async fn verify_webhook_source(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
        merchant_id: &common_utils::id_type::MerchantId,
        connector_webhook_details: Option<common_utils::pii::SecretSerdeValue>,
        _connector_account_details: crypto::Encryptable<masking::Secret<serde_json::Value>>,
        connector_label: &str,
    ) -> CustomResult<bool, errors::ConnectorError> {
        let connector_webhook_secrets = self
            .get_webhook_source_verification_merchant_secret(
                merchant_id,
                connector_label,
                connector_webhook_details,
            )
            .await
            .change_context(errors::ConnectorError::WebhookSourceVerificationFailed)?;
        let signature = self
            .get_webhook_source_verification_signature(request, &connector_webhook_secrets)
            .change_context(errors::ConnectorError::WebhookSourceVerificationFailed)?;
        let message = self
            .get_webhook_source_verification_message(
                request,
                merchant_id,
                &connector_webhook_secrets,
            )
            .change_context(errors::ConnectorError::WebhookSourceVerificationFailed)?;
        let secret_key = hex::decode(connector_webhook_secrets.secret)
            .change_context(errors::ConnectorError::WebhookVerificationSecretInvalid)?;

        let signing_key = hmac::Key::new(hmac::HMAC_SHA256, &secret_key);
        let signed_message = hmac::sign(&signing_key, &message);
        let computed_signature = hex::encode(signed_message.as_ref());

        Ok(computed_signature.as_bytes() == hex::encode(signature).as_bytes())
    }

    fn get_webhook_object_reference_id(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<api_models::webhooks::ObjectReferenceId, errors::ConnectorError> {
        let body: WorldpayWebhookTransactionId = request
            .body
            .parse_struct("WorldpayWebhookTransactionId")
            .change_context(errors::ConnectorError::WebhookReferenceIdNotFound)?;
        Ok(api_models::webhooks::ObjectReferenceId::PaymentId(
            PaymentIdType::PaymentAttemptId(body.event_details.transaction_reference),
        ))
    }

    fn get_webhook_event_type(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<IncomingWebhookEvent, errors::ConnectorError> {
        let body: WorldpayWebhookEventType = request
            .body
            .parse_struct("WorldpayWebhookEventType")
            .change_context(errors::ConnectorError::WebhookReferenceIdNotFound)?;
        match body.event_details.event_type {
            EventType::Authorized => Ok(IncomingWebhookEvent::PaymentIntentAuthorizationSuccess),
            EventType::Settled => Ok(IncomingWebhookEvent::PaymentIntentSuccess),
            EventType::SentForSettlement | EventType::SentForAuthorization => {
                Ok(IncomingWebhookEvent::PaymentIntentProcessing)
            }
            EventType::Error | EventType::Expired | EventType::SettlementFailed => {
                Ok(IncomingWebhookEvent::PaymentIntentFailure)
            }
            EventType::Unknown
            | EventType::Cancelled
            | EventType::Refused
            | EventType::Refunded
            | EventType::SentForRefund
            | EventType::RefundFailed => Ok(IncomingWebhookEvent::EventNotSupported),
        }
    }

    fn get_webhook_resource_object(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<Box<dyn masking::ErasedMaskSerialize>, errors::ConnectorError> {
        let body: WorldpayWebhookEventType = request
            .body
            .parse_struct("WorldpayWebhookEventType")
            .change_context(errors::ConnectorError::WebhookResourceObjectNotFound)?;
        let psync_body = WorldpayEventResponse::try_from(body)?;
        Ok(Box::new(psync_body))
    }

    fn get_mandate_details(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<
        Option<hyperswitch_domain_models::router_flow_types::ConnectorMandateDetails>,
        errors::ConnectorError,
    > {
        let body: WorldpayWebhookTransactionId = request
            .body
            .parse_struct("WorldpayWebhookTransactionId")
            .change_context(errors::ConnectorError::WebhookReferenceIdNotFound)?;
        let mandate_reference = body.event_details.token.map(|mandate_token| {
            hyperswitch_domain_models::router_flow_types::ConnectorMandateDetails {
                connector_mandate_id: mandate_token.href,
            }
        });
        Ok(mandate_reference)
    }

    fn get_network_txn_id(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<
        Option<hyperswitch_domain_models::router_flow_types::ConnectorNetworkTxnId>,
        errors::ConnectorError,
    > {
        let body: WorldpayWebhookTransactionId = request
            .body
            .parse_struct("WorldpayWebhookTransactionId")
            .change_context(errors::ConnectorError::WebhookReferenceIdNotFound)?;
        let optional_network_txn_id = body.event_details.scheme_reference.map(|network_txn_id| {
            hyperswitch_domain_models::router_flow_types::ConnectorNetworkTxnId::new(network_txn_id)
        });
        Ok(optional_network_txn_id)
    }
}