                    samsung_pay: None,
                }))
            }
            domain::WalletData::GooglePay(google_pay_pm_data) => {
                Ok(Some(api_models::payments::AdditionalPaymentData::Wallet {
                    apple_pay: None,
                    google_pay: Some(payment_additional_types::WalletAdditionalDataForCard {
                        last4: google_pay_pm_data.info.card_details.clone(),
                        card_network: google_pay_pm_data.info.card_network.clone(),
                        card_type: Some(google_pay_pm_data.pm_type.clone()),
                    }),
                    samsung_pay: None,
                }))
            }
            domain::WalletData::SamsungPay(samsung_pay_pm_data) => {
                Ok(Some(api_models::payments::AdditionalPaymentData::Wallet {
                    apple_pay: None,
                    google_pay: None,
                    samsung_pay: Some(payment_additional_types::WalletAdditionalDataForCard {
                        last4: samsung_pay_pm_data
                            .payment_credential
                            .card_last_four_digits
                            .clone(),
                        card_network: samsung_pay_pm_data
                            .payment_credential
                            .card_brand
                            .to_string(),
                        card_type: None,
                    }),
                }))
            }
            _ => Ok(Some(api_models::payments::AdditionalPaymentData::Wallet {
                apple_pay: None,
                google_pay: None,
                samsung_pay: None,
            })),
        },
        domain::PaymentMethodData::PayLater(_) => Ok(Some(
            api_models::payments::AdditionalPaymentData::PayLater { klarna_sdk: None },
        )),
        domain::PaymentMethodData::BankTransfer(bank_transfer) => Ok(Some(
            api_models::payments::AdditionalPaymentData::BankTransfer {
                details: Some((*(bank_transfer.to_owned())).into()),
            },
        )),
        domain::PaymentMethodData::Crypto(crypto) => {
            Ok(Some(api_models::payments::AdditionalPaymentData::Crypto {
                details: Some(crypto.to_owned().into()),
            }))
        }
        domain::PaymentMethodData::BankDebit(bank_debit) => Ok(Some(
            api_models::payments::AdditionalPaymentData::BankDebit {
                details: Some(bank_debit.to_owned().into()),
            },
        )),
        domain::PaymentMethodData::MandatePayment => Ok(Some(
            api_models::payments::AdditionalPaymentData::MandatePayment {},
        )),
        domain::PaymentMethodData::Reward => {
            Ok(Some(api_models::payments::AdditionalPaymentData::Reward {}))
        }
        domain::PaymentMethodData::RealTimePayment(realtime_payment) => Ok(Some(
            api_models::payments::AdditionalPaymentData::RealTimePayment {
                details: Some((*(realtime_payment.to_owned())).into()),
            },
        )),
        domain::PaymentMethodData::Upi(upi) => {
            Ok(Some(api_models::payments::AdditionalPaymentData::Upi {
                details: Some(upi.to_owned().into()),
            }))
        }
        domain::PaymentMethodData::CardRedirect(card_redirect) => Ok(Some(
            api_models::payments::AdditionalPaymentData::CardRedirect {
                details: Some(card_redirect.to_owned().into()),
            },
        )),
        domain::PaymentMethodData::Voucher(voucher) => {
            Ok(Some(api_models::payments::AdditionalPaymentData::Voucher {
                details: Some(voucher.to_owned().into()),
            }))
        }
        domain::PaymentMethodData::GiftCard(gift_card) => Ok(Some(
            api_models::payments::AdditionalPaymentData::GiftCard {
                details: Some((*(gift_card.to_owned())).into()),
            },
        )),
        domain::PaymentMethodData::CardToken(card_token) => Ok(Some(
            api_models::payments::AdditionalPaymentData::CardToken {
                details: Some(card_token.to_owned().into()),
            },
        )),
        domain::PaymentMethodData::OpenBanking(open_banking) => Ok(Some(
            api_models::payments::AdditionalPaymentData::OpenBanking {
                details: Some(open_banking.to_owned().into()),
            },
        )),
        domain::PaymentMethodData::CardDetailsForNetworkTransactionId(card_data) => {
            let card_isin = Some(card_data.card_number.get_card_isin());
            let enable_extended_bin =db
            .find_config_by_key_unwrap_or(
                format!("{}_enable_extended_card_bin", profile_id.get_string_repr()).as_str(),
             Some("false".to_string()))
            .await.map_err(|err| services::logger::error!(message="Failed to fetch the config", extended_card_bin_error=?err)).ok();

            let card_extended_bin = match enable_extended_bin {
                Some(config) if config.config == "true" => {
                    Some(card_data.card_number.get_extended_card_bin())
                }
                _ => None,
            };

            let card_network = match card_data
                .card_number
                .is_cobadged_card()
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable(
                    "Card cobadge check failed due to an invalid card network regex",
                )? {
                true => card_data.card_network.clone(),
                false => None,
            };

            let last4 = Some(card_data.card_number.get_last4());
            if card_data.card_issuer.is_some()
                && card_network.is_some()
                && card_data.card_type.is_some()
                && card_data.card_issuing_country.is_some()
                && card_data.bank_code.is_some()
            {
                Ok(Some(api_models::payments::AdditionalPaymentData::Card(
                    Box::new(api_models::payments::AdditionalCardInfo {
                        card_issuer: card_data.card_issuer.to_owned(),
                        card_network,
                        card_type: card_data.card_type.to_owned(),
                        card_issuing_country: card_data.card_issuing_country.to_owned(),
                        bank_code: card_data.bank_code.to_owned(),
                        card_exp_month: Some(card_data.card_exp_month.clone()),
                        card_exp_year: Some(card_data.card_exp_year.clone()),
                        card_holder_name: card_data.card_holder_name.clone(),
                        last4: last4.clone(),
                        card_isin: card_isin.clone(),
                        card_extended_bin: card_extended_bin.clone(),
                        // These are filled after calling the processor / connector
                        payment_checks: None,
                        authentication_data: None,
                        is_regulated: None,
                        signature_network: None,
                    }),
                )))
            } else {
                let card_info = card_isin
                    .clone()
                    .async_and_then(|card_isin| async move {
                        db.get_card_info(&card_isin)
                            .await
                            .map_err(|error| services::logger::warn!(card_info_error=?error))
                            .ok()
                    })
                    .await
                    .flatten()
                    .map(|card_info| {
                        api_models::payments::AdditionalPaymentData::Card(Box::new(
                            api_models::payments::AdditionalCardInfo {
                                card_issuer: card_info.card_issuer,
                                card_network: card_network.clone().or(card_info.card_network),
                                bank_code: card_info.bank_code,
                                card_type: card_info.card_type,
                                card_issuing_country: card_info.card_issuing_country,
                                last4: last4.clone(),
                                card_isin: card_isin.clone(),
                                card_extended_bin: card_extended_bin.clone(),
                                card_exp_month: Some(card_data.card_exp_month.clone()),
                                card_exp_year: Some(card_data.card_exp_year.clone()),
                                card_holder_name: card_data.card_holder_name.clone(),
                                // These are filled after calling the processor / connector
                                payment_checks: None,
                                authentication_data: None,
                                is_regulated: None,
                                signature_network: None,
                            },
                        ))
                    });
                Ok(Some(card_info.unwrap_or_else(|| {
                    api_models::payments::AdditionalPaymentData::Card(Box::new(
                        api_models::payments::AdditionalCardInfo {
                            card_issuer: None,
                            card_network,
                            bank_code: None,
                            card_type: None,
                            card_issuing_country: None,
                            last4,
                            card_isin,
                            card_extended_bin,
                            card_exp_month: Some(card_data.card_exp_month.clone()),
                            card_exp_year: Some(card_data.card_exp_year.clone()),
                            card_holder_name: card_data.card_holder_name.clone(),
                            // These are filled after calling the processor / connector
                            payment_checks: None,
                            authentication_data: None,
                            is_regulated: None,
                            signature_network: None,
                        },
                    ))
                })))
            }
        }
        domain::PaymentMethodData::MobilePayment(mobile_payment) => Ok(Some(
            api_models::payments::AdditionalPaymentData::MobilePayment {
                details: Some(mobile_payment.to_owned().into()),
            },
        )),
        domain::PaymentMethodData::NetworkToken(_) => Ok(None),
    }
}

#[cfg(feature = "v1")]
pub fn validate_customer_access(
    payment_intent: &PaymentIntent,
    auth_flow: services::AuthFlow,
    request: &api::PaymentsRequest,
) -> Result<(), errors::ApiErrorResponse> {
    if auth_flow == services::AuthFlow::Client && request.get_customer_id().is_some() {
        let is_same_customer = request.get_customer_id() == payment_intent.customer_id.as_ref();
        if !is_same_customer {
            Err(errors::ApiErrorResponse::GenericUnauthorized {
                message: "Unauthorised access to update customer".to_string(),
            })?;
        }
    }
    Ok(())
}

pub fn is_apple_pay_simplified_flow(
    connector_metadata: Option<pii::SecretSerdeValue>,
    connector_name: Option<&String>,
) -> CustomResult<bool, errors::ApiErrorResponse> {
    let option_apple_pay_metadata = get_applepay_metadata(connector_metadata)
        .map_err(|error| {
            logger::info!(
                "Apple pay metadata parsing for {:?} in is_apple_pay_simplified_flow {:?}",
                connector_name,
                error
            )
        })
        .ok();

    // return true only if the apple flow type is simplified
    Ok(matches!(
        option_apple_pay_metadata,
        Some(
            api_models::payments::ApplepaySessionTokenMetadata::ApplePayCombined(
                api_models::payments::ApplePayCombinedMetadata::Simplified { .. }
            )
        )
    ))
}

// This function will return the encrypted connector wallets details with Apple Pay certificates
// Currently apple pay certifiactes are stored in the metadata which is not encrypted.
// In future we want those certificates to be encrypted and stored in the connector_wallets_details.
// As part of migration fallback this function checks apple pay details are present in connector_wallets_details
// If yes, it will encrypt connector_wallets_details and store it in the database.
// If no, it will check if apple pay details are present in metadata and merge it with connector_wallets_details, encrypt and store it.
pub async fn get_connector_wallets_details_with_apple_pay_certificates(
    connector_metadata: &Option<masking::Secret<tera::Value>>,
    connector_wallets_details_optional: &Option<api_models::admin::ConnectorWalletDetails>,
) -> RouterResult<Option<masking::Secret<serde_json::Value>>> {
    let connector_wallet_details_with_apple_pay_metadata_optional =
        get_apple_pay_metadata_if_needed(connector_metadata, connector_wallets_details_optional)
            .await?;

    let connector_wallets_details = connector_wallet_details_with_apple_pay_metadata_optional
        .map(|details| {
            serde_json::to_value(details)
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Failed to serialize Apple Pay metadata as JSON")
        })
        .transpose()?
        .map(masking::Secret::new);

    Ok(connector_wallets_details)
}

async fn get_apple_pay_metadata_if_needed(
    connector_metadata: &Option<masking::Secret<tera::Value>>,
    connector_wallets_details_optional: &Option<api_models::admin::ConnectorWalletDetails>,
) -> RouterResult<Option<api_models::admin::ConnectorWalletDetails>> {
    if let Some(connector_wallets_details) = connector_wallets_details_optional {
        if connector_wallets_details.apple_pay_combined.is_some()
            || connector_wallets_details.apple_pay.is_some()
        {
            return Ok(Some(connector_wallets_details.clone()));
        }
        // Otherwise, merge Apple Pay metadata
        return get_and_merge_apple_pay_metadata(
            connector_metadata.clone(),
            Some(connector_wallets_details.clone()),
        )
        .await;
    }

    // If connector_wallets_details_optional is None, attempt to get Apple Pay metadata
    get_and_merge_apple_pay_metadata(connector_metadata.clone(), None).await
}

async fn get_and_merge_apple_pay_metadata(
    connector_metadata: Option<masking::Secret<tera::Value>>,
    connector_wallets_details_optional: Option<api_models::admin::ConnectorWalletDetails>,
) -> RouterResult<Option<api_models::admin::ConnectorWalletDetails>> {
    let apple_pay_metadata_optional = get_applepay_metadata(connector_metadata)
        .map_err(|error| {
            logger::error!(
                "Apple Pay metadata parsing failed in get_encrypted_connector_wallets_details_with_apple_pay_certificates {:?}",
                error
            );
        })
        .ok();

    if let Some(apple_pay_metadata) = apple_pay_metadata_optional {
        let updated_wallet_details = match apple_pay_metadata {
            api_models::payments::ApplepaySessionTokenMetadata::ApplePayCombined(
                apple_pay_combined_metadata,
            ) => {
                let combined_metadata_json = serde_json::to_value(apple_pay_combined_metadata)
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("Failed to serialize Apple Pay combined metadata as JSON")?;

                api_models::admin::ConnectorWalletDetails {
                    apple_pay_combined: Some(masking::Secret::new(combined_metadata_json)),
                    apple_pay: connector_wallets_details_optional
                        .as_ref()
                        .and_then(|d| d.apple_pay.clone()),
                    amazon_pay: connector_wallets_details_optional
                        .as_ref()
                        .and_then(|d| d.amazon_pay.clone()),
                    samsung_pay: connector_wallets_details_optional
                        .as_ref()
                        .and_then(|d| d.samsung_pay.clone()),
                    paze: connector_wallets_details_optional
                        .as_ref()
                        .and_then(|d| d.paze.clone()),
                    google_pay: connector_wallets_details_optional
                        .as_ref()
                        .and_then(|d| d.google_pay.clone()),
                }
            }
            api_models::payments::ApplepaySessionTokenMetadata::ApplePay(apple_pay_metadata) => {
                let metadata_json = serde_json::to_value(apple_pay_metadata)
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("Failed to serialize Apple Pay metadata as JSON")?;

                api_models::admin::ConnectorWalletDetails {
                    apple_pay: Some(masking::Secret::new(metadata_json)),
                    apple_pay_combined: connector_wallets_details_optional
                        .as_ref()
                        .and_then(|d| d.apple_pay_combined.clone()),
                    amazon_pay: connector_wallets_details_optional
                        .as_ref()
                        .and_then(|d| d.amazon_pay.clone()),
                    samsung_pay: connector_wallets_details_optional
                        .as_ref()
                        .and_then(|d| d.samsung_pay.clone()),
                    paze: connector_wallets_details_optional
                        .as_ref()
                        .and_then(|d| d.paze.clone()),
                    google_pay: connector_wallets_details_optional
                        .as_ref()
                        .and_then(|d| d.google_pay.clone()),
                }
            }
        };

        return Ok(Some(updated_wallet_details));
    }

    // Return connector_wallets_details if no Apple Pay metadata was found
    Ok(connector_wallets_details_optional)
}

pub fn get_applepay_metadata(
    connector_metadata: Option<pii::SecretSerdeValue>,
) -> RouterResult<api_models::payments::ApplepaySessionTokenMetadata> {
    connector_metadata
        .clone()
        .parse_value::<api_models::payments::ApplepayCombinedSessionTokenData>(
            "ApplepayCombinedSessionTokenData",
        )
        .map(|combined_metadata| {
            api_models::payments::ApplepaySessionTokenMetadata::ApplePayCombined(
                combined_metadata.apple_pay_combined,
            )
        })
        .or_else(|_| {
            connector_metadata
                .parse_value::<api_models::payments::ApplepaySessionTokenData>(
                    "ApplepaySessionTokenData",
                )
                .map(|old_metadata| {
                    api_models::payments::ApplepaySessionTokenMetadata::ApplePay(
                        old_metadata.apple_pay,
                    )
                })
        })
        .change_context(errors::ApiErrorResponse::InvalidDataFormat {
            field_name: "connector_metadata".to_string(),
            expected_format: "applepay_metadata_format".to_string(),
        })
}

pub fn calculate_debit_routing_savings(net_amount: i64, saving_percentage: f64) -> MinorUnit {
    logger::debug!(
        ?net_amount,
        ?saving_percentage,
        "Calculating debit routing saving amount"
    );

    let net_decimal = Decimal::from_i64(net_amount).unwrap_or_else(|| {
        logger::warn!(?net_amount, "Invalid net_amount, using 0");
        Decimal::ZERO
    });

    let percentage_decimal = Decimal::from_f64(saving_percentage).unwrap_or_else(|| {
        logger::warn!(?saving_percentage, "Invalid saving_percentage, using 0");
        Decimal::ZERO
    });

    let savings_decimal = net_decimal * percentage_decimal / Decimal::from(100);
    let rounded_savings = savings_decimal.round();

    let savings_int = rounded_savings.to_i64().unwrap_or_else(|| {
        logger::warn!(
            ?rounded_savings,
            "Debit routing savings calculation overflowed when converting to i64"
        );
        0
    });

    MinorUnit::new(savings_int)
}

pub fn get_debit_routing_savings_amount(
    payment_method_data: &domain::PaymentMethodData,
    payment_attempt: &PaymentAttempt,
) -> Option<MinorUnit> {
    let card_network = payment_attempt.extract_card_network()?;

    let saving_percentage =
        payment_method_data.extract_debit_routing_saving_percentage(&card_network)?;

    let net_amount = payment_attempt.get_total_amount().get_amount_as_i64();

    Some(calculate_debit_routing_savings(
        net_amount,
        saving_percentage,
    ))
}

#[cfg(all(feature = "retry", feature = "v1"))]
pub async fn get_apple_pay_retryable_connectors<F, D>(
    state: &SessionState,
    merchant_context: &domain::MerchantContext,
    payment_data: &D,
    pre_routing_connector_data_list: &[api::ConnectorRoutingData],
    merchant_connector_id: Option<&id_type::MerchantConnectorAccountId>,
    business_profile: domain::Profile,
) -> CustomResult<Option<Vec<api::ConnectorRoutingData>>, errors::ApiErrorResponse>
where
    F: Send + Clone,
    D: payments::OperationSessionGetters<F> + Send,
{
    let profile_id = business_profile.get_id();

    let pre_decided_connector_data_first = pre_routing_connector_data_list
        .first()
        .ok_or(errors::ApiErrorResponse::IncorrectPaymentMethodConfiguration)?;

    let merchant_connector_account_type = get_merchant_connector_account(
        state,
        merchant_context.get_merchant_account().get_id(),
        payment_data.get_creds_identifier(),
        merchant_context.get_merchant_key_store(),
        profile_id,
        &pre_decided_connector_data_first
            .connector_data
            .connector_name
            .to_string(),
        merchant_connector_id,
    )
    .await?;

    let connector_data_list = if is_apple_pay_simplified_flow(
        merchant_connector_account_type.get_metadata(),
        merchant_connector_account_type
            .get_connector_name()
            .as_ref(),
    )? {
        let merchant_connector_account_list = state
            .store
            .find_merchant_connector_account_by_merchant_id_and_disabled_list(
                &state.into(),
                merchant_context.get_merchant_account().get_id(),
                false,
                merchant_context.get_merchant_key_store(),
            )
            .await
            .to_not_found_response(errors::ApiErrorResponse::InternalServerError)?;

        let profile_specific_merchant_connector_account_list = merchant_connector_account_list
            .filter_based_on_profile_and_connector_type(
                profile_id,
                ConnectorType::PaymentProcessor,
            );

        let mut connector_data_list = vec![pre_decided_connector_data_first.clone()];

        for merchant_connector_account in profile_specific_merchant_connector_account_list {
            if is_apple_pay_simplified_flow(
                merchant_connector_account.metadata.clone(),
                Some(&merchant_connector_account.connector_name),
            )? {
                let routing_data: api::ConnectorRoutingData =
                    api::ConnectorData::get_connector_by_name(
                        &state.conf.connectors,
                        &merchant_connector_account.connector_name.to_string(),
                        api::GetToken::Connector,
                        Some(merchant_connector_account.get_id()),
                    )
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("Invalid connector name received")?
                    .into();

                if !connector_data_list.iter().any(|connector_details| {
                    connector_details.connector_data.merchant_connector_id
                        == routing_data.connector_data.merchant_connector_id
                }) {
                    connector_data_list.push(routing_data)
                }
            }
        }
        #[cfg(feature = "v1")]
        let fallback_connetors_list = crate::core::routing::helpers::get_merchant_default_config(
            &*state.clone().store,
            profile_id.get_string_repr(),
            &api_enums::TransactionType::Payment,
        )
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to get merchant default fallback connectors config")?;

        let mut routing_connector_data_list = Vec::new();

        pre_routing_connector_data_list.iter().for_each(|pre_val| {
            routing_connector_data_list.push(pre_val.connector_data.merchant_connector_id.clone())
        });

        fallback_connetors_list.iter().for_each(|fallback_val| {
            routing_connector_data_list
                .iter()
                .all(|val| *val != fallback_val.merchant_connector_id)
                .then(|| {
                    routing_connector_data_list.push(fallback_val.merchant_connector_id.clone())
                });
        });

        // connector_data_list is the list of connectors for which Apple Pay simplified flow is configured.
        // This list is arranged in the same order as the merchant's connectors routingconfiguration.

        let mut ordered_connector_data_list = Vec::new();

        routing_connector_data_list
            .iter()
            .for_each(|merchant_connector_id| {
                let connector_data = connector_data_list.iter().find(|connector_data| {
                    *merchant_connector_id == connector_data.connector_data.merchant_connector_id
                });
                if let Some(connector_data_details) = connector_data {
                    ordered_connector_data_list.push(connector_data_details.clone());
                }
            });

        Some(ordered_connector_data_list)
    } else {
        None
    };
    Ok(connector_data_list)
}

#[derive(Debug, Serialize, Deserialize)]
pub struct ApplePayData {
    version: masking::Secret<String>,
    data: masking::Secret<String>,
    signature: masking::Secret<String>,
    header: ApplePayHeader,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ApplePayHeader {
    ephemeral_public_key: masking::Secret<String>,
    public_key_hash: masking::Secret<String>,
    transaction_id: masking::Secret<String>,
}

impl ApplePayData {
    pub fn token_json(
        wallet_data: domain::WalletData,
    ) -> CustomResult<Self, errors::ConnectorError> {
        let json_wallet_data: Self = connector::utils::WalletData::get_wallet_token_as_json(
            &wallet_data,
            "Apple Pay".to_string(),
        )?;
        Ok(json_wallet_data)
    }

    pub async fn decrypt(
        &self,
        payment_processing_certificate: &masking::Secret<String>,
        payment_processing_certificate_key: &masking::Secret<String>,
    ) -> CustomResult<serde_json::Value, errors::ApplePayDecryptionError> {
        let merchant_id = self.merchant_id(payment_processing_certificate)?;
        let shared_secret = self.shared_secret(payment_processing_certificate_key)?;
        let symmetric_key = self.symmetric_key(&merchant_id, &shared_secret)?;
        let decrypted = self.decrypt_ciphertext(&symmetric_key)?;
        let parsed_decrypted: serde_json::Value = serde_json::from_str(&decrypted)
            .change_context(errors::ApplePayDecryptionError::DecryptionFailed)?;
        Ok(parsed_decrypted)
    }

    pub fn merchant_id(
        &self,
        payment_processing_certificate: &masking::Secret<String>,
    ) -> CustomResult<String, errors::ApplePayDecryptionError> {
        let cert_data = payment_processing_certificate.clone().expose();

        let base64_decode_cert_data = BASE64_ENGINE
            .decode(cert_data)
            .change_context(errors::ApplePayDecryptionError::Base64DecodingFailed)?;

        // Parsing the certificate using x509-parser
        let (_, certificate) = parse_x509_certificate(&base64_decode_cert_data)
            .change_context(errors::ApplePayDecryptionError::CertificateParsingFailed)
            .attach_printable("Error parsing apple pay PPC")?;

        // Finding the merchant ID extension
        let apple_pay_m_id = certificate
            .extensions()
            .iter()
            .find(|extension| {
                extension
                    .oid
                    .to_string()
                    .eq(consts::MERCHANT_ID_FIELD_EXTENSION_ID)
            })
            .map(|ext| {
                let merchant_id = String::from_utf8_lossy(ext.value)
                    .trim()
                    .trim_start_matches('@')
                    .to_string();

                merchant_id
            })
            .ok_or(errors::ApplePayDecryptionError::MissingMerchantId)
            .attach_printable("Unable to find merchant ID extension in the certificate")?;

        Ok(apple_pay_m_id)
    }

    pub fn shared_secret(
        &self,
        payment_processing_certificate_key: &masking::Secret<String>,
    ) -> CustomResult<Vec<u8>, errors::ApplePayDecryptionError> {
        let public_ec_bytes = BASE64_ENGINE
            .decode(self.header.ephemeral_public_key.peek().as_bytes())
            .change_context(errors::ApplePayDecryptionError::Base64DecodingFailed)?;

        let public_key = PKey::public_key_from_der(&public_ec_bytes)
            .change_context(errors::ApplePayDecryptionError::KeyDeserializationFailed)
            .attach_printable("Failed to deserialize the public key")?;

        let decrypted_apple_pay_ppc_key = payment_processing_certificate_key.clone().expose();

        // Create PKey objects from EcKey
        let private_key = PKey::private_key_from_pem(decrypted_apple_pay_ppc_key.as_bytes())
            .change_context(errors::ApplePayDecryptionError::KeyDeserializationFailed)
            .attach_printable("Failed to deserialize the private key")?;

        // Create the Deriver object and set the peer public key
        let mut deriver = Deriver::new(&private_key)
            .change_context(errors::ApplePayDecryptionError::DerivingSharedSecretKeyFailed)
            .attach_printable("Failed to create a deriver for the private key")?;

        deriver
            .set_peer(&public_key)
            .change_context(errors::ApplePayDecryptionError::DerivingSharedSecretKeyFailed)
            .attach_printable("Failed to set the peer key for the secret derivation")?;

        // Compute the shared secret
        let shared_secret = deriver
            .derive_to_vec()
            .change_context(errors::ApplePayDecryptionError::DerivingSharedSecretKeyFailed)
            .attach_printable("Final key derivation failed")?;
        Ok(shared_secret)
    }

    pub fn symmetric_key(
        &self,
        merchant_id: &str,
        shared_secret: &[u8],
    ) -> CustomResult<Vec<u8>, errors::ApplePayDecryptionError> {
        let kdf_algorithm = b"\x0did-aes256-GCM";
        let kdf_party_v = hex::decode(merchant_id)
            .change_context(errors::ApplePayDecryptionError::Base64DecodingFailed)?;
        let kdf_party_u = b"Apple";
        let kdf_info = [&kdf_algorithm[..], kdf_party_u, &kdf_party_v[..]].concat();

        let mut hash = openssl::sha::Sha256::new();
        hash.update(b"\x00\x00\x00");
        hash.update(b"\x01");
        hash.update(shared_secret);
        hash.update(&kdf_info[..]);
        let symmetric_key = hash.finish();
        Ok(symmetric_key.to_vec())
    }

    pub fn decrypt_ciphertext(
        &self,
        symmetric_key: &[u8],
    ) -> CustomResult<String, errors::ApplePayDecryptionError> {
        logger::info!("Decrypt apple pay token");

        let data = BASE64_ENGINE
            .decode(self.data.peek().as_bytes())
            .change_context(errors::ApplePayDecryptionError::Base64DecodingFailed)?;
        let iv = [0u8; 16]; //Initialization vector IV is typically used in AES-GCM (Galois/Counter Mode) encryption for randomizing the encryption process.
        let ciphertext = data
            .get(..data.len() - 16)
            .ok_or(errors::ApplePayDecryptionError::DecryptionFailed)?;
        let tag = data
            .get(data.len() - 16..)
            .ok_or(errors::ApplePayDecryptionError::DecryptionFailed)?;
        let cipher = Cipher::aes_256_gcm();
        let decrypted_data = decrypt_aead(cipher, symmetric_key, Some(&iv), &[], ciphertext, tag)
            .change_context(errors::ApplePayDecryptionError::DecryptionFailed)?;
        let decrypted = String::from_utf8(decrypted_data)
            .change_context(errors::ApplePayDecryptionError::DecryptionFailed)?;

        Ok(decrypted)
    }
}

// Structs for keys and the main decryptor
pub struct GooglePayTokenDecryptor {
    root_signing_keys: Vec<GooglePayRootSigningKey>,
    recipient_id: masking::Secret<String>,
    private_key: PKey<openssl::pkey::Private>,
}

#[derive(Debug, Clone, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct EncryptedData {
    signature: String,
    intermediate_signing_key: IntermediateSigningKey,
    protocol_version: GooglePayProtocolVersion,
    #[serde(with = "common_utils::custom_serde::json_string")]
    signed_message: GooglePaySignedMessage,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GooglePaySignedMessage {
    #[serde(with = "common_utils::Base64Serializer")]
    encrypted_message: masking::Secret<Vec<u8>>,
    #[serde(with = "common_utils::Base64Serializer")]
    ephemeral_public_key: masking::Secret<Vec<u8>>,
    #[serde(with = "common_utils::Base64Serializer")]
    tag: masking::Secret<Vec<u8>>,
}

#[derive(Debug, Clone, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct IntermediateSigningKey {
    signed_key: masking::Secret<String>,
    signatures: Vec<masking::Secret<String>>,
}

#[derive(Debug, Clone, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GooglePaySignedKey {
    key_value: masking::Secret<String>,
    key_expiration: String,
}

#[derive(Debug, Clone, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GooglePayRootSigningKey {
    key_value: masking::Secret<String>,
    key_expiration: String,
    protocol_version: GooglePayProtocolVersion,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Eq, PartialEq)]
pub enum GooglePayProtocolVersion {
    #[serde(rename = "ECv2")]
    EcProtocolVersion2,
}

// Check expiration date validity
fn check_expiration_date_is_valid(
    expiration: &str,
) -> CustomResult<bool, errors::GooglePayDecryptionError> {
    let expiration_ms = expiration
        .parse::<i128>()
        .change_context(errors::GooglePayDecryptionError::InvalidExpirationTime)?;
    // convert milliseconds to nanoseconds (1 millisecond = 1_000_000 nanoseconds) to create OffsetDateTime
    let expiration_time =
        time::OffsetDateTime::from_unix_timestamp_nanos(expiration_ms * 1_000_000)
            .change_context(errors::GooglePayDecryptionError::InvalidExpirationTime)?;
    let now = time::OffsetDateTime::now_utc();

    Ok(expiration_time > now)
}

// Construct little endian format of u32
fn get_little_endian_format(number: u32) -> Vec<u8> {
    number.to_le_bytes().to_vec()
}

// Filter and parse the root signing keys based on protocol version and expiration time
fn filter_root_signing_keys(
    root_signing_keys: Vec<GooglePayRootSigningKey>,
) -> CustomResult<Vec<GooglePayRootSigningKey>, errors::GooglePayDecryptionError> {
    let filtered_root_signing_keys = root_signing_keys
        .iter()
        .filter(|key| {
            key.protocol_version == GooglePayProtocolVersion::EcProtocolVersion2
                && matches!(
                    check_expiration_date_is_valid(&key.key_expiration).inspect_err(
                        |err| logger::warn!(
                            "Failed to check expirattion due to invalid format: {:?}",
                            err
                        )
                    ),
                    Ok(true)
                )
        })
        .cloned()
        .collect::<Vec<GooglePayRootSigningKey>>();

    logger::info!(
        "Filtered {} out of {} root signing keys",
        filtered_root_signing_keys.len(),
        root_signing_keys.len()
    );

    Ok(filtered_root_signing_keys)
}

impl GooglePayTokenDecryptor {
    pub fn new(
        root_keys: masking::Secret<String>,
        recipient_id: masking::Secret<String>,
        private_key: masking::Secret<String>,
    ) -> CustomResult<Self, errors::GooglePayDecryptionError> {
        // base64 decode the private key
        let decoded_key = BASE64_ENGINE
            .decode(private_key.expose())
            .change_context(errors::GooglePayDecryptionError::Base64DecodingFailed)?;
        // base64 decode the root signing keys
        let decoded_root_signing_keys = BASE64_ENGINE
            .decode(root_keys.expose())
            .change_context(errors::GooglePayDecryptionError::Base64DecodingFailed)?;
        // create a private key from the decoded key
        let private_key = PKey::private_key_from_pkcs8(&decoded_key)
            .change_context(errors::GooglePayDecryptionError::KeyDeserializationFailed)
            .attach_printable("cannot convert private key from decode_key")?;

        // parse the root signing keys
        let root_keys_vector: Vec<GooglePayRootSigningKey> = decoded_root_signing_keys
            .parse_struct("GooglePayRootSigningKey")
            .change_context(errors::GooglePayDecryptionError::DeserializationFailed)?;

        // parse and filter the root signing keys by protocol version
        let filtered_root_signing_keys = filter_root_signing_keys(root_keys_vector)?;

        Ok(Self {
            root_signing_keys: filtered_root_signing_keys,
            recipient_id,
            private_key,
        })
    }

    // Decrypt the Google pay token
    pub fn decrypt_token(
        &self,
        data: String,
        should_verify_signature: bool,
    ) -> CustomResult<
        hyperswitch_domain_models::router_data::GooglePayPredecryptDataInternal,
        errors::GooglePayDecryptionError,
    > {
        // parse the encrypted data
        let encrypted_data: EncryptedData = data
            .parse_struct("EncryptedData")
            .change_context(errors::GooglePayDecryptionError::DeserializationFailed)?;

        // verify the signature if required
        if should_verify_signature {
            self.verify_signature(&encrypted_data)?;
        }

        let ephemeral_public_key = encrypted_data.signed_message.ephemeral_public_key.peek();
        let tag = encrypted_data.signed_message.tag.peek();
        let encrypted_message = encrypted_data.signed_message.encrypted_message.peek();

        // derive the shared key
        let shared_key = self.get_shared_key(ephemeral_public_key)?;

        // derive the symmetric encryption key and MAC key
        let derived_key = self.derive_key(ephemeral_public_key, &shared_key)?;
        // First 32 bytes for AES-256 and Remaining bytes for HMAC
        let (symmetric_encryption_key, mac_key) = derived_key
            .split_at_checked(32)
            .ok_or(errors::GooglePayDecryptionError::ParsingFailed)?;

        // verify the HMAC of the message
        self.verify_hmac(mac_key, tag, encrypted_message)?;

        // decrypt the message
        let decrypted = self.decrypt_message(symmetric_encryption_key, encrypted_message)?;

        // parse the decrypted data
        let decrypted_data: hyperswitch_domain_models::router_data::GooglePayPredecryptDataInternal =
            decrypted
                .parse_struct("GooglePayPredecryptDataInternal")
                .change_context(errors::GooglePayDecryptionError::DeserializationFailed)?;

        // check the expiration date of the decrypted data
        if matches!(
            check_expiration_date_is_valid(&decrypted_data.message_expiration),
            Ok(true)
        ) {
            Ok(decrypted_data)
        } else {
            Err(errors::GooglePayDecryptionError::DecryptedTokenExpired.into())
        }
    }

    // Verify the signature of the token
    fn verify_signature(
        &self,
        encrypted_data: &EncryptedData,
    ) -> CustomResult<(), errors::GooglePayDecryptionError> {
        // check the protocol version
        if encrypted_data.protocol_version != GooglePayProtocolVersion::EcProtocolVersion2 {
            return Err(errors::GooglePayDecryptionError::InvalidProtocolVersion.into());
        }

        // verify the intermediate signing key
        self.verify_intermediate_signing_key(encrypted_data)?;
        // validate and fetch the signed key
        let signed_key = self.validate_signed_key(&encrypted_data.intermediate_signing_key)?;
        // verify the signature of the token
        self.verify_message_signature(encrypted_data, &signed_key)
    }


// File: crates/router/src/consts/user_role.rs
// Module: router

// User Roles
pub const ROLE_ID_MERCHANT_ADMIN: &str = "merchant_admin";
pub const ROLE_ID_MERCHANT_VIEW_ONLY: &str = "merchant_view_only";
pub const ROLE_ID_MERCHANT_IAM_ADMIN: &str = "merchant_iam_admin";
pub const ROLE_ID_MERCHANT_DEVELOPER: &str = "merchant_developer";
pub const ROLE_ID_MERCHANT_OPERATOR: &str = "merchant_operator";
pub const ROLE_ID_MERCHANT_CUSTOMER_SUPPORT: &str = "merchant_customer_support";

pub const ROLE_ID_PROFILE_ADMIN: &str = "profile_admin";
pub const ROLE_ID_PROFILE_VIEW_ONLY: &str = "profile_view_only";
pub const ROLE_ID_PROFILE_IAM_ADMIN: &str = "profile_iam_admin";
pub const ROLE_ID_PROFILE_DEVELOPER: &str = "profile_developer";
pub const ROLE_ID_PROFILE_OPERATOR: &str = "profile_operator";
pub const ROLE_ID_PROFILE_CUSTOMER_SUPPORT: &str = "profile_customer_support";

pub const INTERNAL_USER_MERCHANT_ID: &str = "juspay000";
pub const MAX_ROLE_NAME_LENGTH: usize = 64;


// Trait: SdkEventMetricAccumulator
// File: crates/analytics/src/sdk_events/accumulator.rs
// Module: analytics

pub trait SdkEventMetricAccumulator


// Function: add_payment_method_status_update_task
// File: crates/router/src/core/payment_methods.rs
// Module: router

pub fn add_payment_method_status_update_task(
    db: &dyn StorageInterface,
    payment_method: &domain::PaymentMethod,
    prev_status: enums::PaymentMethodStatus,
    curr_status: enums::PaymentMethodStatus,
    merchant_id: &id_type::MerchantId,
) -> Result<(), ProcessTrackerError>


// Implementation: impl api::PaymentSync for for Moneris
// File: crates/hyperswitch_connectors/src/connectors/moneris.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PaymentSync for for Moneris


// Struct: FeatureMatrixRequest
// File: crates/api_models/src/feature_matrix.rs
// Module: api_models
// Implementations: 1
// Traits: common_utils::events::ApiEventMetric

pub struct FeatureMatrixRequest


// Struct: EcommerceCardPaymentOnlyResponseData
// File: crates/hyperswitch_connectors/src/connectors/peachpayments/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct EcommerceCardPaymentOnlyResponseData


// Implementation: impl Responder
// File: crates/router/src/routes/payments.rs
// Module: router
// Methods: 0 total (0 public)

impl Responder


// Implementation: impl api::PaymentCapture for for Gigadat
// File: crates/hyperswitch_connectors/src/connectors/gigadat.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PaymentCapture for for Gigadat


// Struct: IatapayAuthUpdateResponse
// File: crates/hyperswitch_connectors/src/connectors/iatapay/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct IatapayAuthUpdateResponse


// Struct: MandateResponseBody
// File: crates/hyperswitch_connectors/src/connectors/bamboraapac/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct MandateResponseBody


// Function: validate
// File: crates/external_services/src/grpc_client/revenue_recovery/recovery_decider_client.rs
// Module: external_services

// Documentation: Validate the configuration

pub fn validate(&self) -> Result<(), RecoveryDeciderError>


// Struct: IatapayAccessTokenErrorResponse
// File: crates/hyperswitch_connectors/src/connectors/iatapay/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct IatapayAccessTokenErrorResponse


// Function: generate_digest
// File: crates/hyperswitch_connectors/src/connectors/wellsfargo.rs
// Module: hyperswitch_connectors

pub fn generate_digest(&self, payload: &[u8]) -> String


// Struct: FrmFulfillmentSignifydRequest
// File: crates/hyperswitch_connectors/src/connectors/signifyd/transformers/api.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct FrmFulfillmentSignifydRequest


// Implementation: impl api::RefundSync for for Datatrans
// File: crates/hyperswitch_connectors/src/connectors/datatrans.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::RefundSync for for Datatrans


// File: crates/router/src/core/currency.rs
// Module: router
// Public functions: 3

use analytics::errors::AnalyticsError;
use common_utils::errors::CustomResult;
use currency_conversion::types::ExchangeRates;
use error_stack::ResultExt;
use router_env::logger;

use crate::{
    consts::DEFAULT_ANALYTICS_FOREX_RETRY_ATTEMPTS,
    core::errors::ApiErrorResponse,
    services::ApplicationResponse,
    utils::currency::{self, convert_currency, get_forex_rates, ForexError as ForexCacheError},
    SessionState,
};

pub async fn retrieve_forex(
    state: SessionState,
) -> CustomResult<ApplicationResponse<currency::FxExchangeRatesCacheEntry>, ApiErrorResponse> {
    let forex_api = state.conf.forex_api.get_inner();
    Ok(ApplicationResponse::Json(
        get_forex_rates(&state, forex_api.data_expiration_delay_in_seconds)
            .await
            .change_context(ApiErrorResponse::GenericNotFoundError {
                message: "Unable to fetch forex rates".to_string(),
            })?,
    ))
}

pub async fn convert_forex(
    state: SessionState,
    amount: i64,
    to_currency: String,
    from_currency: String,
) -> CustomResult<
    ApplicationResponse<api_models::currency::CurrencyConversionResponse>,
    ApiErrorResponse,
> {
    Ok(ApplicationResponse::Json(
        Box::pin(convert_currency(
            state.clone(),
            amount,
            to_currency,
            from_currency,
        ))
        .await
        .change_context(ApiErrorResponse::InternalServerError)?,
    ))
}

pub async fn get_forex_exchange_rates(
    state: SessionState,
) -> CustomResult<ExchangeRates, AnalyticsError> {
    let forex_api = state.conf.forex_api.get_inner();
    let mut attempt = 1;

    logger::info!("Starting forex exchange rates fetch");
    loop {
        logger::info!("Attempting to fetch forex rates - Attempt {attempt} of {DEFAULT_ANALYTICS_FOREX_RETRY_ATTEMPTS}");

        match get_forex_rates(&state, forex_api.data_expiration_delay_in_seconds).await {
            Ok(rates) => {
                logger::info!("Successfully fetched forex rates");
                return Ok((*rates.data).clone());
            }
            Err(error) => {
                let is_retryable = matches!(
                    error.current_context(),
                    ForexCacheError::CouldNotAcquireLock
                        | ForexCacheError::EntryNotFound
                        | ForexCacheError::ForexDataUnavailable
                        | ForexCacheError::LocalReadError
                        | ForexCacheError::LocalWriteError
                        | ForexCacheError::RedisConnectionError
                        | ForexCacheError::RedisLockReleaseFailed
                        | ForexCacheError::RedisWriteError
                        | ForexCacheError::WriteLockNotAcquired
                );

                if !is_retryable {
                    return Err(error.change_context(AnalyticsError::ForexFetchFailed));
                }

                if attempt >= DEFAULT_ANALYTICS_FOREX_RETRY_ATTEMPTS {
                    logger::error!("Failed to fetch forex rates after {DEFAULT_ANALYTICS_FOREX_RETRY_ATTEMPTS} attempts");
                    return Err(error.change_context(AnalyticsError::ForexFetchFailed));
                }
                logger::warn!(
                    "Forex rates fetch failed with retryable error, retrying in {attempt} seconds"
                );
                tokio::time::sleep(std::time::Duration::from_secs(attempt * 2)).await;
                attempt += 1;
            }
        }
    }
}


// Implementation: impl PaymentMethod
// File: crates/diesel_models/src/payment_method.rs
// Module: diesel_models
// Methods: 2 total (2 public)

impl PaymentMethod


// Struct: ResetTrackingIdRequest
// File: crates/api_models/src/connector_onboarding.rs
// Module: api_models
// Implementations: 0

pub struct ResetTrackingIdRequest


// Module Structure
// File: crates/hyperswitch_connectors/src/connectors/paysafe.rs
// Module: hyperswitch_connectors

// Public submodules:
pub mod transformers;


// Struct: CustomerBody
// File: crates/hyperswitch_connectors/src/connectors/braintree/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct CustomerBody


// File: crates/router/src/compatibility/stripe/webhooks.rs
// Module: router
// Public structs: 4

#[cfg(feature = "payouts")]
use api_models::payouts as payout_models;
use api_models::{
    enums::{Currency, DisputeStatus, MandateStatus},
    webhooks::{self as api},
};
use common_utils::{crypto::SignMessage, date_time, ext_traits::Encode};
#[cfg(feature = "payouts")]
use common_utils::{
    id_type,
    pii::{self, Email},
};
use error_stack::ResultExt;
use router_env::logger;
use serde::Serialize;

use super::{
    payment_intents::types::StripePaymentIntentResponse, refunds::types::StripeRefundResponse,
};
use crate::{
    core::{
        errors,
        webhooks::types::{OutgoingWebhookPayloadWithSignature, OutgoingWebhookType},
    },
    headers,
    services::request::Maskable,
};

#[derive(Serialize, Debug)]
pub struct StripeOutgoingWebhook {
    id: String,
    #[serde(rename = "type")]
    stype: &'static str,
    object: &'static str,
    data: StripeWebhookObject,
    created: u64,
    // api_version: "2019-11-05", // not used
}

impl OutgoingWebhookType for StripeOutgoingWebhook {
    fn get_outgoing_webhooks_signature(
        &self,
        payment_response_hash_key: Option<impl AsRef<[u8]>>,
    ) -> errors::CustomResult<OutgoingWebhookPayloadWithSignature, errors::WebhooksFlowError> {
        let timestamp = self.created;

        let payment_response_hash_key = payment_response_hash_key
            .ok_or(errors::WebhooksFlowError::MerchantConfigNotFound)
            .attach_printable("For stripe compatibility payment_response_hash_key is mandatory")?;

        let webhook_signature_payload = self
            .encode_to_string_of_json()
            .change_context(errors::WebhooksFlowError::OutgoingWebhookEncodingFailed)
            .attach_printable("failed encoding outgoing webhook payload")?;

        let new_signature_payload = format!("{timestamp}.{webhook_signature_payload}");
        let v1 = hex::encode(
            common_utils::crypto::HmacSha256::sign_message(
                &common_utils::crypto::HmacSha256,
                payment_response_hash_key.as_ref(),
                new_signature_payload.as_bytes(),
            )
            .change_context(errors::WebhooksFlowError::OutgoingWebhookSigningFailed)
            .attach_printable("Failed to sign the message")?,
        );

        let t = timestamp;
        let signature = Some(format!("t={t},v1={v1}"));

        Ok(OutgoingWebhookPayloadWithSignature {
            payload: webhook_signature_payload.into(),
            signature,
        })
    }

    fn add_webhook_header(header: &mut Vec<(String, Maskable<String>)>, signature: String) {
        header.push((
            headers::STRIPE_COMPATIBLE_WEBHOOK_SIGNATURE.to_string(),
            signature.into(),
        ))
    }
}

#[derive(Serialize, Debug)]
#[serde(tag = "type", content = "object", rename_all = "snake_case")]
pub enum StripeWebhookObject {
    PaymentIntent(Box<StripePaymentIntentResponse>),
    Refund(StripeRefundResponse),
    Dispute(StripeDisputeResponse),
    Mandate(StripeMandateResponse),
    #[cfg(feature = "payouts")]
    Payout(StripePayoutResponse),
}

#[derive(Serialize, Debug)]
pub struct StripeDisputeResponse {
    pub id: String,
    pub amount: String,
    pub currency: Currency,
    pub payment_intent: id_type::PaymentId,
    pub reason: Option<String>,
    pub status: StripeDisputeStatus,
}

#[derive(Serialize, Debug)]
pub struct StripeMandateResponse {
    pub mandate_id: String,
    pub status: StripeMandateStatus,
    pub payment_method_id: String,
    pub payment_method: String,
}

#[cfg(feature = "payouts")]
#[derive(Clone, Serialize, Debug)]
pub struct StripePayoutResponse {
    pub id: id_type::PayoutId,
    pub amount: i64,
    pub currency: String,
    pub payout_type: Option<common_enums::PayoutType>,
    pub status: StripePayoutStatus,
    pub name: Option<masking::Secret<String>>,
    pub email: Option<Email>,
    pub phone: Option<masking::Secret<String>>,
    pub phone_country_code: Option<String>,
    pub created: Option<i64>,
    pub metadata: Option<pii::SecretSerdeValue>,
    pub entity_type: common_enums::PayoutEntityType,
    pub recurring: bool,
    pub error_message: Option<String>,
    pub error_code: Option<String>,
}

#[cfg(feature = "payouts")]
#[derive(Clone, Serialize, Debug)]
#[serde(rename_all = "snake_case")]
pub enum StripePayoutStatus {
    PayoutSuccess,
    PayoutFailure,
    PayoutProcessing,
    PayoutCancelled,
    PayoutInitiated,
    PayoutExpired,
    PayoutReversed,
}

#[cfg(feature = "payouts")]
impl From<common_enums::PayoutStatus> for StripePayoutStatus {
    fn from(status: common_enums::PayoutStatus) -> Self {
        match status {
            common_enums::PayoutStatus::Success => Self::PayoutSuccess,
            common_enums::PayoutStatus::Failed => Self::PayoutFailure,
            common_enums::PayoutStatus::Cancelled => Self::PayoutCancelled,
            common_enums::PayoutStatus::Initiated => Self::PayoutInitiated,
            common_enums::PayoutStatus::Expired => Self::PayoutExpired,
            common_enums::PayoutStatus::Reversed => Self::PayoutReversed,
            common_enums::PayoutStatus::Pending
            | common_enums::PayoutStatus::Ineligible
            | common_enums::PayoutStatus::RequiresCreation
            | common_enums::PayoutStatus::RequiresFulfillment
            | common_enums::PayoutStatus::RequiresPayoutMethodData
            | common_enums::PayoutStatus::RequiresVendorAccountCreation
            | common_enums::PayoutStatus::RequiresConfirmation => Self::PayoutProcessing,
        }
    }
}

#[cfg(feature = "payouts")]
impl From<payout_models::PayoutCreateResponse> for StripePayoutResponse {
    fn from(res: payout_models::PayoutCreateResponse) -> Self {
        let (name, email, phone, phone_country_code) = match res.customer {
            Some(customer) => (
                customer.name,
                customer.email,
                customer.phone,
                customer.phone_country_code,
            ),
            None => (None, None, None, None),
        };
        Self {
            id: res.payout_id,
            amount: res.amount.get_amount_as_i64(),
            currency: res.currency.to_string(),
            payout_type: res.payout_type,
            status: StripePayoutStatus::from(res.status),
            name,
            email,
            phone,
            phone_country_code,
            created: res.created.map(|t| t.assume_utc().unix_timestamp()),
            metadata: res.metadata,
            entity_type: res.entity_type,
            recurring: res.recurring,
            error_message: res.error_message,
            error_code: res.error_code,
        }
    }
}

#[derive(Serialize, Debug)]
#[serde(rename_all = "snake_case")]
pub enum StripeMandateStatus {
    Active,
    Inactive,
    Pending,
}

#[derive(Serialize, Debug)]
#[serde(rename_all = "snake_case")]
pub enum StripeDisputeStatus {
    WarningNeedsResponse,
    WarningUnderReview,
    WarningClosed,
    NeedsResponse,
    UnderReview,
    ChargeRefunded,
    Won,
    Lost,
}

impl From<api_models::disputes::DisputeResponse> for StripeDisputeResponse {
    fn from(res: api_models::disputes::DisputeResponse) -> Self {
        Self {
            id: res.dispute_id,
            amount: res.amount.to_string(),
            currency: res.currency,
            payment_intent: res.payment_id,
            reason: res.connector_reason,
            status: StripeDisputeStatus::from(res.dispute_status),
        }
    }
}

impl From<api_models::mandates::MandateResponse> for StripeMandateResponse {
    fn from(res: api_models::mandates::MandateResponse) -> Self {
        Self {
            mandate_id: res.mandate_id,
            payment_method: res.payment_method,
            payment_method_id: res.payment_method_id,
            status: StripeMandateStatus::from(res.status),
        }
    }
}

impl From<MandateStatus> for StripeMandateStatus {
    fn from(status: MandateStatus) -> Self {
        match status {
            MandateStatus::Active => Self::Active,
            MandateStatus::Inactive | MandateStatus::Revoked => Self::Inactive,
            MandateStatus::Pending => Self::Pending,
        }
    }
}

impl From<DisputeStatus> for StripeDisputeStatus {
    fn from(status: DisputeStatus) -> Self {
        match status {
            DisputeStatus::DisputeOpened => Self::WarningNeedsResponse,
            DisputeStatus::DisputeExpired => Self::Lost,
            DisputeStatus::DisputeAccepted => Self::Lost,
            DisputeStatus::DisputeCancelled => Self::WarningClosed,
            DisputeStatus::DisputeChallenged => Self::WarningUnderReview,
            DisputeStatus::DisputeWon => Self::Won,
            DisputeStatus::DisputeLost => Self::Lost,
        }
    }
}

fn get_stripe_event_type(event_type: api_models::enums::EventType) -> &'static str {
    match event_type {
        api_models::enums::EventType::PaymentSucceeded => "payment_intent.succeeded",
        api_models::enums::EventType::PaymentFailed => "payment_intent.payment_failed",
        api_models::enums::EventType::PaymentProcessing
        | api_models::enums::EventType::PaymentPartiallyAuthorized => "payment_intent.processing",
        api_models::enums::EventType::PaymentCancelled
        | api_models::enums::EventType::PaymentCancelledPostCapture
        | api_models::enums::EventType::PaymentExpired => "payment_intent.canceled",

        // the below are not really stripe compatible because stripe doesn't provide this
        api_models::enums::EventType::ActionRequired => "action.required",
        api_models::enums::EventType::RefundSucceeded => "refund.succeeded",
        api_models::enums::EventType::RefundFailed => "refund.failed",
        api_models::enums::EventType::DisputeOpened => "dispute.failed",
        api_models::enums::EventType::DisputeExpired => "dispute.expired",
        api_models::enums::EventType::DisputeAccepted => "dispute.accepted",
        api_models::enums::EventType::DisputeCancelled => "dispute.cancelled",
        api_models::enums::EventType::DisputeChallenged => "dispute.challenged",
        api_models::enums::EventType::DisputeWon => "dispute.won",
        api_models::enums::EventType::DisputeLost => "dispute.lost",
        api_models::enums::EventType::MandateActive => "mandate.active",
        api_models::enums::EventType::MandateRevoked => "mandate.revoked",

        // as per this doc https://stripe.com/docs/api/events/types#event_types-payment_intent.amount_capturable_updated
        api_models::enums::EventType::PaymentAuthorized => {
            "payment_intent.amount_capturable_updated"
        }
        // stripe treats partially captured payments as succeeded.
        api_models::enums::EventType::PaymentCaptured => "payment_intent.succeeded",
        api_models::enums::EventType::PayoutSuccess => "payout.paid",
        api_models::enums::EventType::PayoutFailed => "payout.failed",
        api_models::enums::EventType::PayoutInitiated => "payout.created",
        api_models::enums::EventType::PayoutCancelled => "payout.canceled",
        api_models::enums::EventType::PayoutProcessing => "payout.created",
        api_models::enums::EventType::PayoutExpired => "payout.failed",
        api_models::enums::EventType::PayoutReversed => "payout.reconciliation_completed",
    }
}

impl From<api::OutgoingWebhook> for StripeOutgoingWebhook {
    fn from(value: api::OutgoingWebhook) -> Self {
        Self {
            id: value.event_id,
            stype: get_stripe_event_type(value.event_type),
            data: StripeWebhookObject::from(value.content),
            object: "event",
            // put this conversion it into a function
            created: u64::try_from(value.timestamp.assume_utc().unix_timestamp()).unwrap_or_else(
                |error| {
                    logger::error!(
                        %error,
                        "incorrect value for `webhook.timestamp` provided {}", value.timestamp
                    );
                    // Current timestamp converted to Unix timestamp should have a positive value
                    // for many years to come
                    u64::try_from(date_time::now().assume_utc().unix_timestamp())
                        .unwrap_or_default()
                },
            ),
        }
    }
}

impl From<api::OutgoingWebhookContent> for StripeWebhookObject {
    fn from(value: api::OutgoingWebhookContent) -> Self {
        match value {
            api::OutgoingWebhookContent::PaymentDetails(payment) => {
                Self::PaymentIntent(Box::new((*payment).into()))
            }
            api::OutgoingWebhookContent::RefundDetails(refund) => Self::Refund((*refund).into()),
            api::OutgoingWebhookContent::DisputeDetails(dispute) => {
                Self::Dispute((*dispute).into())
            }
            api::OutgoingWebhookContent::MandateDetails(mandate) => {
                Self::Mandate((*mandate).into())
            }
            #[cfg(feature = "payouts")]
            api::OutgoingWebhookContent::PayoutDetails(payout) => Self::Payout((*payout).into()),
        }
    }
}


// Trait: StorageModel
// File: crates/storage_impl/src/kv_router_store.rs
// Module: storage_impl

// Documentation: Storage model with all required capabilities for KV operations

pub trait StorageModel<D: Conversion>: de::DeserializeOwned
    + serde::Serialize
    + Debug
    + KvStorePartition
    + UniqueConstraints
    + Sync
    + Send
    + ReverseConversion<D>


// Struct: GetSubscriptionEstimate
// File: crates/hyperswitch_domain_models/src/router_flow_types/subscriptions.rs
// Module: hyperswitch_domain_models
// Implementations: 0

pub struct GetSubscriptionEstimate


// Function: connector_create
// File: crates/router/src/routes/admin.rs
// Module: router

pub fn connector_create(
    state: web::Data<AppState>,
    req: HttpRequest,
    path: web::Path<common_utils::id_type::MerchantId>,
    json_payload: web::Json<admin::MerchantConnectorCreate>,
) -> HttpResponse


// Struct: ThreeDsSettings
// File: crates/hyperswitch_connectors/src/connectors/payme/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct ThreeDsSettings


// Function: get_forex_exchange_rates
// File: crates/router/src/core/currency.rs
// Module: router

pub fn get_forex_exchange_rates(
    state: SessionState,
) -> CustomResult<ExchangeRates, AnalyticsError>


// Function: get_token
// File: crates/hyperswitch_connectors/src/connectors/novalnet/transformers.rs
// Module: hyperswitch_connectors

pub fn get_token(transaction_data: Option<&Self>) -> Option<String>


// Function: perform_volume_split
// File: crates/router/src/core/payments/routing.rs
// Module: router

pub fn perform_volume_split(
    mut splits: Vec<routing_types::ConnectorVolumeSplit>,
) -> RoutingResult<Vec<routing_types::RoutableConnectorChoice>>


// Implementation: impl ExternalAuthentication for for Netcetera
// File: crates/hyperswitch_connectors/src/connectors/netcetera.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl ExternalAuthentication for for Netcetera


// Implementation: impl api::PaymentToken for for Forte
// File: crates/hyperswitch_connectors/src/connectors/forte.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PaymentToken for for Forte


// Function: insert
// File: crates/diesel_models/src/query/merchant_account.rs
// Module: diesel_models

pub fn insert(self, conn: &PgPooledConn) -> StorageResult<MerchantAccount>


// Implementation: impl ConnectorValidation for for Boku
// File: crates/hyperswitch_connectors/src/connectors/boku.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl ConnectorValidation for for Boku


// Struct: RecipientBankAccountRequest
// File: crates/hyperswitch_connectors/src/connectors/stripe/transformers/connect.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct RecipientBankAccountRequest


// File: crates/analytics/src/lambda_utils.rs
// Module: analytics
// Public functions: 1

use aws_config::{self, meta::region::RegionProviderChain, Region};
use aws_sdk_lambda::{types::InvocationType::Event, Client};
use aws_smithy_types::Blob;
use common_utils::errors::CustomResult;
use error_stack::{report, ResultExt};

use crate::errors::AnalyticsError;

async fn get_aws_client(region: String) -> Client {
    let region_provider = RegionProviderChain::first_try(Region::new(region));
    let sdk_config = aws_config::from_env().region(region_provider).load().await;
    Client::new(&sdk_config)
}

pub async fn invoke_lambda(
    function_name: &str,
    region: &str,
    json_bytes: &[u8],
) -> CustomResult<(), AnalyticsError> {
    get_aws_client(region.to_string())
        .await
        .invoke()
        .function_name(function_name)
        .invocation_type(Event)
        .payload(Blob::new(json_bytes.to_owned()))
        .send()
        .await
        .map_err(|er| {
            let er_rep = format!("{er:?}");
            report!(er).attach_printable(er_rep)
        })
        .change_context(AnalyticsError::UnknownError)
        .attach_printable("Lambda invocation failed")?;
    Ok(())
}


// Function: refunds_update
// File: crates/openapi/src/routes/refunds.rs
// Module: openapi

pub fn refunds_update()


// Struct: User
// File: crates/router_derive/src/lib.rs
// Module: router_derive
// Implementations: 0

pub struct User


// Implementation: impl Globepay
// File: crates/hyperswitch_connectors/src/connectors/globepay.rs
// Module: hyperswitch_connectors
// Methods: 1 total (1 public)

impl Globepay


// Function: get_payout_attempt_id
// File: crates/router/src/utils.rs
// Module: router

pub fn get_payout_attempt_id(payout_id: &str, attempt_count: i16) -> String


// Implementation: impl MerchantConnectorListResponse
// File: crates/api_models/src/admin.rs
// Module: api_models
// Methods: 2 total (2 public)

impl MerchantConnectorListResponse


// Struct: ShippingData
// File: crates/hyperswitch_connectors/src/connectors/redsys/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct ShippingData


// Struct: SyncResponse
// File: crates/hyperswitch_connectors/src/connectors/bamboraapac/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct SyncResponse


// Implementation: impl Context
// File: crates/euclid/src/backend/vir_interpreter/types.rs
// Module: euclid
// Methods: 2 total (2 public)

impl Context


// Struct: CardDetailFromLocker
// File: crates/api_models/src/payment_methods.rs
// Module: api_models
// Implementations: 0

pub struct CardDetailFromLocker


// File: crates/diesel_models/src/merchant_key_store.rs
// Module: diesel_models
// Public structs: 3

use common_utils::{custom_serde, encryption::Encryption};
use diesel::{AsChangeset, Identifiable, Insertable, Queryable, Selectable};
use time::PrimitiveDateTime;

use crate::schema::merchant_key_store;

#[derive(
    Clone,
    Debug,
    serde::Serialize,
    serde::Deserialize,
    Identifiable,
    Queryable,
    Selectable,
    router_derive::DebugAsDisplay,
)]
#[diesel(table_name = merchant_key_store, primary_key(merchant_id), check_for_backend(diesel::pg::Pg))]
pub struct MerchantKeyStore {
    pub merchant_id: common_utils::id_type::MerchantId,
    pub key: Encryption,
    #[serde(with = "custom_serde::iso8601")]
    pub created_at: PrimitiveDateTime,
}

#[derive(
    Clone, Debug, serde::Serialize, serde::Deserialize, Insertable, router_derive::DebugAsDisplay,
)]
#[diesel(table_name = merchant_key_store)]
pub struct MerchantKeyStoreNew {
    pub merchant_id: common_utils::id_type::MerchantId,
    pub key: Encryption,
    pub created_at: PrimitiveDateTime,
}

#[derive(
    Clone, Debug, serde::Serialize, serde::Deserialize, AsChangeset, router_derive::DebugAsDisplay,
)]
#[diesel(table_name = merchant_key_store)]
pub struct MerchantKeyStoreUpdateInternal {
    pub merchant_id: common_utils::id_type::MerchantId,
    pub key: Encryption,
}


// Function: generic_delete
// File: crates/diesel_models/src/query/generics.rs
// Module: diesel_models

pub fn generic_delete<T, P>(conn: &PgPooledConn, predicate: P) -> StorageResult<bool>
where
    T: FilterDsl<P> + HasTable<Table = T> + Table + 'static,
    Filter<T, P>: IntoUpdateTarget,
    DeleteStatement<
        <Filter<T, P> as HasTable>::Table,
        <Filter<T, P> as IntoUpdateTarget>::WhereClause,
    >: AsQuery + QueryFragment<Pg> + QueryId + Send + 'static,


// Struct: RequiredFieldFinal
// File: crates/payment_methods/src/configs/settings.rs
// Module: payment_methods
// Implementations: 0

pub struct RequiredFieldFinal


// Implementation: impl Responder
// File: crates/router/src/routes/api_keys.rs
// Module: router
// Methods: 0 total (0 public)

impl Responder


// Function: profile_create
// File: crates/router/src/routes/profiles.rs
// Module: router

pub fn profile_create(
    state: web::Data<AppState>,
    req: HttpRequest,
    json_payload: web::Json<admin::ProfileCreate>,
) -> HttpResponse


// Struct: SaleQueryResponse
// File: crates/hyperswitch_connectors/src/connectors/payme/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct SaleQueryResponse


// Implementation: impl Default for for Filter
// File: crates/analytics/src/query.rs
// Module: analytics
// Methods: 1 total (0 public)

impl Default for for Filter


// Function: add_refund_execute_task
// File: crates/router/src/core/refunds.rs
// Module: router

pub fn add_refund_execute_task(
    db: &dyn db::StorageInterface,
    refund: &diesel_refund::Refund,
    runner: storage::ProcessTrackerRunner,
) -> RouterResult<storage::ProcessTracker>


// Struct: Getnet
// File: crates/hyperswitch_connectors/src/connectors/getnet.rs
// Module: hyperswitch_connectors
// Implementations: 17
// Traits: api::Payment, api::PaymentSession, api::ConnectorAccessToken, api::MandateSetup, api::PaymentAuthorize, api::PaymentSync, api::PaymentCapture, api::PaymentVoid, api::Refund, api::RefundExecute, api::RefundSync, api::PaymentToken, ConnectorCommon, ConnectorValidation, webhooks::IncomingWebhook, ConnectorSpecifications

pub struct Getnet

api::Payment api::PaymentSession api::ConnectorAccessToken api::MandateSetup api::PaymentAuthorize api::PaymentSync api::PaymentCapture api::PaymentVoid api::Refund api::RefundExecute api::RefundSync api::PaymentToken ConnectorCommon ConnectorValidation webhooks::IncomingWebhook ConnectorSpecifications


// Function: is_dynamic_routing
// File: crates/api_models/src/routing.rs
// Module: api_models

pub fn is_dynamic_routing(self) -> bool