File: crates/router/src/db/business_profile.rs

use common_utils::{ext_traits::AsyncExt, types::keymanager::KeyManagerState};
use error_stack::{report, ResultExt};
use router_env::{instrument, tracing};

use super::Store;
use crate::{
    connection,
    core::errors::{self, CustomResult},
    db::MockDb,
    types::{
        domain::{
            self,
            behaviour::{Conversion, ReverseConversion},
        },
        storage,
    },
};

#[async_trait::async_trait]
pub trait ProfileInterface
where
    domain::Profile: Conversion<DstType = storage::Profile, NewDstType = storage::ProfileNew>,
{
    async fn insert_business_profile(
        &self,
        key_manager_state: &KeyManagerState,
        merchant_key_store: &domain::MerchantKeyStore,
        business_profile: domain::Profile,
    ) -> CustomResult<domain::Profile, errors::StorageError>;

    async fn find_business_profile_by_profile_id(
        &self,
        key_manager_state: &KeyManagerState,
        merchant_key_store: &domain::MerchantKeyStore,
        profile_id: &common_utils::id_type::ProfileId,
    ) -> CustomResult<domain::Profile, errors::StorageError>;

    async fn find_business_profile_by_merchant_id_profile_id(
        &self,
        key_manager_state: &KeyManagerState,
        merchant_key_store: &domain::MerchantKeyStore,
        merchant_id: &common_utils::id_type::MerchantId,
        profile_id: &common_utils::id_type::ProfileId,
    ) -> CustomResult<domain::Profile, errors::StorageError>;

    async fn find_business_profile_by_profile_name_merchant_id(
        &self,
        key_manager_state: &KeyManagerState,
        merchant_key_store: &domain::MerchantKeyStore,
        profile_name: &str,
        merchant_id: &common_utils::id_type::MerchantId,
    ) -> CustomResult<domain::Profile, errors::StorageError>;

    async fn update_profile_by_profile_id(
        &self,
        key_manager_state: &KeyManagerState,
        merchant_key_store: &domain::MerchantKeyStore,
        current_state: domain::Profile,
        profile_update: domain::ProfileUpdate,
    ) -> CustomResult<domain::Profile, errors::StorageError>;

    async fn delete_profile_by_profile_id_merchant_id(
        &self,
        profile_id: &common_utils::id_type::ProfileId,
        merchant_id: &common_utils::id_type::MerchantId,
    ) -> CustomResult<bool, errors::StorageError>;

    async fn list_profile_by_merchant_id(
        &self,
        key_manager_state: &KeyManagerState,
        merchant_key_store: &domain::MerchantKeyStore,
        merchant_id: &common_utils::id_type::MerchantId,
    ) -> CustomResult<Vec<domain::Profile>, errors::StorageError>;
}

#[async_trait::async_trait]
impl ProfileInterface for Store {
    #[instrument(skip_all)]
    async fn insert_business_profile(
        &self,
        key_manager_state: &KeyManagerState,
        merchant_key_store: &domain::MerchantKeyStore,
        business_profile: domain::Profile,
    ) -> CustomResult<domain::Profile, errors::StorageError> {
        let conn = connection::pg_accounts_connection_write(self).await?;
        business_profile
            .construct_new()
            .await
            .change_context(errors::StorageError::EncryptionError)?
            .insert(&conn)
            .await
            .map_err(|error| report!(errors::StorageError::from(error)))?
            .convert(
                key_manager_state,
                merchant_key_store.key.get_inner(),
                merchant_key_store.merchant_id.clone().into(),
            )
            .await
            .change_context(errors::StorageError::DecryptionError)
    }

    #[instrument(skip_all)]
    async fn find_business_profile_by_profile_id(
        &self,
        key_manager_state: &KeyManagerState,
        merchant_key_store: &domain::MerchantKeyStore,
        profile_id: &common_utils::id_type::ProfileId,
    ) -> CustomResult<domain::Profile, errors::StorageError> {
        let conn = connection::pg_accounts_connection_read(self).await?;
        storage::Profile::find_by_profile_id(&conn, profile_id)
            .await
            .map_err(|error| report!(errors::StorageError::from(error)))?
            .convert(
                key_manager_state,
                merchant_key_store.key.get_inner(),
                merchant_key_store.merchant_id.clone().into(),
            )
            .await
            .change_context(errors::StorageError::DecryptionError)
    }

    async fn find_business_profile_by_merchant_id_profile_id(
        &self,
        key_manager_state: &KeyManagerState,
        merchant_key_store: &domain::MerchantKeyStore,
        merchant_id: &common_utils::id_type::MerchantId,
        profile_id: &common_utils::id_type::ProfileId,
    ) -> CustomResult<domain::Profile, errors::StorageError> {
        let conn = connection::pg_accounts_connection_read(self).await?;
        storage::Profile::find_by_merchant_id_profile_id(&conn, merchant_id, profile_id)
            .await
            .map_err(|error| report!(errors::StorageError::from(error)))?
            .convert(
                key_manager_state,
                merchant_key_store.key.get_inner(),
                merchant_key_store.merchant_id.clone().into(),
            )
            .await
            .change_context(errors::StorageError::DecryptionError)
    }

    #[instrument(skip_all)]
    async fn find_business_profile_by_profile_name_merchant_id(
        &self,
        key_manager_state: &KeyManagerState,
        merchant_key_store: &domain::MerchantKeyStore,
        profile_name: &str,
        merchant_id: &common_utils::id_type::MerchantId,
    ) -> CustomResult<domain::Profile, errors::StorageError> {
        let conn = connection::pg_accounts_connection_read(self).await?;
        storage::Profile::find_by_profile_name_merchant_id(&conn, profile_name, merchant_id)
            .await
            .map_err(|error| report!(errors::StorageError::from(error)))?
            .convert(
                key_manager_state,
                merchant_key_store.key.get_inner(),
                merchant_key_store.merchant_id.clone().into(),
            )
            .await
            .change_context(errors::StorageError::DecryptionError)
    }

    #[instrument(skip_all)]
    async fn update_profile_by_profile_id(
        &self,
        key_manager_state: &KeyManagerState,
        merchant_key_store: &domain::MerchantKeyStore,
        current_state: domain::Profile,
        profile_update: domain::ProfileUpdate,
    ) -> CustomResult<domain::Profile, errors::StorageError> {
        let conn = connection::pg_accounts_connection_write(self).await?;
        Conversion::convert(current_state)
            .await
            .change_context(errors::StorageError::EncryptionError)?
            .update_by_profile_id(&conn, storage::ProfileUpdateInternal::from(profile_update))
            .await
            .map_err(|error| report!(errors::StorageError::from(error)))?
            .convert(
                key_manager_state,
                merchant_key_store.key.get_inner(),
                merchant_key_store.merchant_id.clone().into(),
            )
            .await
            .change_context(errors::StorageError::DecryptionError)
    }

    #[instrument(skip_all)]
    async fn delete_profile_by_profile_id_merchant_id(
        &self,
        profile_id: &common_utils::id_type::ProfileId,
        merchant_id: &common_utils::id_type::MerchantId,
    ) -> CustomResult<bool, errors::StorageError> {
        let conn = connection::pg_accounts_connection_write(self).await?;
        storage::Profile::delete_by_profile_id_merchant_id(&conn, profile_id, merchant_id)
            .await
            .map_err(|error| report!(errors::StorageError::from(error)))
    }

    #[instrument(skip_all)]
    async fn list_profile_by_merchant_id(
        &self,
        key_manager_state: &KeyManagerState,
        merchant_key_store: &domain::MerchantKeyStore,
        merchant_id: &common_utils::id_type::MerchantId,
    ) -> CustomResult<Vec<domain::Profile>, errors::StorageError> {
        let conn = connection::pg_accounts_connection_read(self).await?;
        storage::Profile::list_profile_by_merchant_id(&conn, merchant_id)
            .await
            .map_err(|error| report!(errors::StorageError::from(error)))
            .async_and_then(|business_profiles| async {
                let mut domain_business_profiles = Vec::with_capacity(business_profiles.len());
                for business_profile in business_profiles.into_iter() {
                    domain_business_profiles.push(
                        business_profile
                            .convert(
                                key_manager_state,
                                merchant_key_store.key.get_inner(),
                                merchant_key_store.merchant_id.clone().into(),
                            )
                            .await
                            .change_context(errors::StorageError::DecryptionError)?,
                    );
                }
                Ok(domain_business_profiles)
            })
            .await
    }
}

#[async_trait::async_trait]
impl ProfileInterface for MockDb {
    async fn insert_business_profile(
        &self,
        key_manager_state: &KeyManagerState,
        merchant_key_store: &domain::MerchantKeyStore,
        business_profile: domain::Profile,
    ) -> CustomResult<domain::Profile, errors::StorageError> {
        let stored_business_profile = Conversion::convert(business_profile)
            .await
            .change_context(errors::StorageError::EncryptionError)?;

        self.business_profiles
            .lock()
            .await
            .push(stored_business_profile.clone());

        stored_business_profile
            .convert(
                key_manager_state,
                merchant_key_store.key.get_inner(),
                merchant_key_store.merchant_id.clone().into(),
            )
            .await
            .change_context(errors::StorageError::DecryptionError)
    }

    async fn find_business_profile_by_profile_id(
        &self,
        key_manager_state: &KeyManagerState,
        merchant_key_store: &domain::MerchantKeyStore,
        profile_id: &common_utils::id_type::ProfileId,
    ) -> CustomResult<domain::Profile, errors::StorageError> {
        self.business_profiles
            .lock()
            .await
            .iter()
            .find(|business_profile| business_profile.get_id() == profile_id)
            .cloned()
            .async_map(|business_profile| async {
                business_profile
                    .convert(
                        key_manager_state,
                        merchant_key_store.key.get_inner(),
                        merchant_key_store.merchant_id.clone().into(),
                    )
                    .await
                    .change_context(errors::StorageError::DecryptionError)
            })
            .await
            .transpose()?
            .ok_or(
                errors::StorageError::ValueNotFound(format!(
                    "No business profile found for profile_id = {profile_id:?}"
                ))
                .into(),
            )
    }

    async fn find_business_profile_by_merchant_id_profile_id(
        &self,
        key_manager_state: &KeyManagerState,
        merchant_key_store: &domain::MerchantKeyStore,
        merchant_id: &common_utils::id_type::MerchantId,
        profile_id: &common_utils::id_type::ProfileId,
    ) -> CustomResult<domain::Profile, errors::StorageError> {
        self.business_profiles
            .lock()
            .await
            .iter()
            .find(|business_profile| {
                business_profile.merchant_id == *merchant_id
                    && business_profile.get_id() == profile_id
            })
            .cloned()
            .async_map(|business_profile| async {
                business_profile
                    .convert(
                        key_manager_state,
                        merchant_key_store.key.get_inner(),
                        merchant_key_store.merchant_id.clone().into(),
                    )
                    .await
                    .change_context(errors::StorageError::DecryptionError)
            })
            .await
            .transpose()?
            .ok_or(
                errors::StorageError::ValueNotFound(format!(
                    "No business profile found for merchant_id = {merchant_id:?} and profile_id = {profile_id:?}"
                ))
                .into(),
            )
    }

    async fn update_profile_by_profile_id(
        &self,
        key_manager_state: &KeyManagerState,
        merchant_key_store: &domain::MerchantKeyStore,
        current_state: domain::Profile,
        profile_update: domain::ProfileUpdate,
    ) -> CustomResult<domain::Profile, errors::StorageError> {
        let profile_id = current_state.get_id().to_owned();
        self.business_profiles
            .lock()
            .await
            .iter_mut()
            .find(|business_profile| business_profile.get_id() == current_state.get_id())
            .async_map(|business_profile| async {
                let profile_updated = storage::ProfileUpdateInternal::from(profile_update)
                    .apply_changeset(
                        Conversion::convert(current_state)
                            .await
                            .change_context(errors::StorageError::EncryptionError)?,
                    );
                *business_profile = profile_updated.clone();

                profile_updated
                    .convert(
                        key_manager_state,
                        merchant_key_store.key.get_inner(),
                        merchant_key_store.merchant_id.clone().into(),
                    )
                    .await
                    .change_context(errors::StorageError::DecryptionError)
            })
            .await
            .transpose()?
            .ok_or(
                errors::StorageError::ValueNotFound(format!(
                    "No business profile found for profile_id = {profile_id:?}",
                ))
                .into(),
            )
    }

    async fn delete_profile_by_profile_id_merchant_id(
        &self,
        profile_id: &common_utils::id_type::ProfileId,
        merchant_id: &common_utils::id_type::MerchantId,
    ) -> CustomResult<bool, errors::StorageError> {
        let mut business_profiles = self.business_profiles.lock().await;
        let index = business_profiles
            .iter()
            .position(|business_profile| {
                business_profile.get_id() == profile_id
                    && business_profile.merchant_id == *merchant_id
            })
            .ok_or::<errors::StorageError>(errors::StorageError::ValueNotFound(format!(
                "No business profile found for profile_id = {profile_id:?} and merchant_id = {merchant_id:?}"
            )))?;
        business_profiles.remove(index);
        Ok(true)
    }

    async fn list_profile_by_merchant_id(
        &self,
        key_manager_state: &KeyManagerState,
        merchant_key_store: &domain::MerchantKeyStore,
        merchant_id: &common_utils::id_type::MerchantId,
    ) -> CustomResult<Vec<domain::Profile>, errors::StorageError> {
        let business_profiles = self
            .business_profiles
            .lock()
            .await
            .iter()
            .filter(|business_profile| business_profile.merchant_id == *merchant_id)
            .cloned()
            .collect::<Vec<_>>();

        let mut domain_business_profiles = Vec::with_capacity(business_profiles.len());

        for business_profile in business_profiles {
            let domain_profile = business_profile
                .convert(
                    key_manager_state,
                    merchant_key_store.key.get_inner(),
                    merchant_key_store.merchant_id.clone().into(),
                )
                .await
                .change_context(errors::StorageError::DecryptionError)?;
            domain_business_profiles.push(domain_profile);
        }

        Ok(domain_business_profiles)
    }

    async fn find_business_profile_by_profile_name_merchant_id(
        &self,
        key_manager_state: &KeyManagerState,
        merchant_key_store: &domain::MerchantKeyStore,
        profile_name: &str,
        merchant_id: &common_utils::id_type::MerchantId,
    ) -> CustomResult<domain::Profile, errors::StorageError> {
        self.business_profiles
            .lock()
            .await
            .iter()
            .find(|business_profile| {
                business_profile.profile_name == profile_name
                    && business_profile.merchant_id == *merchant_id
            })
            .cloned()
            .async_map(|business_profile| async {
                business_profile
                    .convert(
                        key_manager_state,
                        merchant_key_store.key.get_inner(),
                        merchant_key_store.merchant_id.clone().into(),
                    )
                    .await
                    .change_context(errors::StorageError::DecryptionError)
            })
            .await
            .transpose()?
            .ok_or(
                errors::StorageError::ValueNotFound(format!(
                    "No business profile found for profile_name = {profile_name} and merchant_id = {merchant_id:?}"

                ))
                .into(),
            )
    }
}


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

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

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

impl ConnectorSpecifications for for Inespay


pub fn is_terminal_status(self) -> bool {
        match self {
            Self::Started | Self::Pending => false,
            Self::Success | Self::Failed => true,
        }
    }


pub struct PaysafeSettlementResponse {
    pub merchant_ref_num: Option<String>,
    pub id: String,
    pub status: PaysafeSettlementStatus,
}


pub struct PaymentMethodId {
    pub payment_method_id: String,
}


pub struct HyperswitchVaultCustomerCreateRequest {
    name: Option<Secret<String>>,
    email: Option<Email>,
}


pub async fn tokenize_card_api() {}


pub struct SingleUsePaymentMethodToken {
    pub token: Secret<String>,
    pub merchant_connector_id: id_type::MerchantConnectorAccountId,
}


pub async fn sdk_events_core(
    pool: &AnalyticsProvider,
    req: SdkEventsRequest,
    publishable_key: &String,
) -> AnalyticsResult<Vec<SdkEventsResult>> {
    match pool {
        AnalyticsProvider::Sqlx(_) => Err(FiltersError::NotImplemented(
            "SDK Events not implemented for SQLX",
        ))
        .attach_printable("SQL Analytics is not implemented for Sdk Events"),
        AnalyticsProvider::Clickhouse(pool) => get_sdk_event(publishable_key, req, pool).await,
        AnalyticsProvider::CombinedSqlx(_sqlx_pool, ckh_pool)
        | AnalyticsProvider::CombinedCkh(_sqlx_pool, ckh_pool) => {
            get_sdk_event(publishable_key, req, ckh_pool).await
        }
    }
    .switch()
}


pub async fn customers_list(
    state: web::Data<AppState>,
    req: HttpRequest,
    query: web::Query<customers::CustomerListRequest>,
) -> HttpResponse {
    let flow = Flow::CustomersList;
    let payload = query.into_inner();

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        payload,
        |state, auth: auth::AuthenticationData, request, _| {
            list_customers(
                state,
                auth.merchant_account.get_id().to_owned(),
                None,
                auth.key_store,
                request,
            )
        },
        auth::auth_type(
            &auth::HeaderAuth(auth::ApiKeyAuth {
                is_connected_allowed: false,
                is_platform_allowed: false,
            }),
            &auth::JWTAuth {
                permission: Permission::MerchantCustomerRead,
            },
            req.headers(),
        ),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


impl DynamicRoutingAlgoAccessor for ContractRoutingAlgorithm {
    fn get_algorithm_id_with_timestamp(
        self,
    ) -> DynamicAlgorithmWithTimestamp<common_utils::id_type::RoutingId> {
        self.algorithm_id_with_timestamp
    }
    fn get_enabled_features(&mut self) -> &mut DynamicRoutingFeatures {
        &mut self.enabled_feature
    }
}

impl DynamicRoutingAlgoAccessor for for ContractRoutingAlgorithm


File: crates/router/src/types/storage/reverse_lookup.rs

pub use diesel_models::reverse_lookup::{ReverseLookup, ReverseLookupNew};


pub struct RecoveryPaymentTuple(
    revenue_recovery::RecoveryPaymentIntent,
    revenue_recovery::RecoveryPaymentAttempt,
);


impl SchedulerInterface for KafkaStore {}

impl SchedulerInterface for for KafkaStore


pub fn get_connector_customer_id(
        &self,
        customer: Option<&customer::Customer>,
        merchant_connector_account: &MerchantConnectorAccountTypeDetails,
    ) -> Option<String> {
        match merchant_connector_account {
            MerchantConnectorAccountTypeDetails::MerchantConnectorAccount(_) => customer
                .and_then(|customer| customer.get_connector_customer_id(merchant_connector_account))
                .map(|id| id.to_string())
                .or_else(|| {
                    self.payment_intent
                        .get_connector_customer_id_from_feature_metadata()
                }),
            MerchantConnectorAccountTypeDetails::MerchantConnectorDetails(_) => None,
        }
    }


impl Deutschebank {
    pub fn new() -> &'static Self {
        &Self {
            amount_converter: &MinorUnitForConnector,
        }
    }
}


OpenAPI Block Path: components.schemas.RelayRefundRequestData
{
  "type": "object",
  "required": [
    "amount",
    "currency"
  ],
  "properties": {
    "amount": {
      "type": "integer",
      "format": "int64",
      "description": "The amount that is being refunded",
      "example": 6540
    },
    "currency": {
      "$ref": "#/components/schemas/Currency"
    },
    "reason": {
      "type": "string",
      "description": "The reason for the refund",
      "example": "Customer returned the product",
      "nullable": true,
      "maxLength": 255
    }
  }
}


impl Responder {
    let flow = Flow::DecisionEngineDecideGatewayCall;
    Box::pin(oss_api::server_wrap(
        flow,
        state,
        &req,
        payload.clone(),
        |state, _auth, payload, _| routing::decide_gateway_open_router(state.clone(), payload),
        &auth::ApiKeyAuth {
            is_connected_allowed: false,
            is_platform_allowed: false,
        },
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


pub fn is_disabled(&self) -> bool {
        self.flag == common_enums::TokenizationFlag::Disabled
    }


pub struct PayuErrorResponse {
    pub status: PayuErrorData,
}


impl api::Refund for Paytm {}


Documentation: crates/hyperswitch_interfaces/README.md
# Type: Doc File

# Hyperswitch Interfaces

This crate includes interfaces and its error types


pub async fn get_profile_sankey(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<TimeRange>,
    ) -> impl Responder {
        let flow = AnalyticsFlow::GetSankey;
        let payload = json_payload.into_inner();
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            payload,
            |state: crate::routes::SessionState, auth: AuthenticationData, req, _| async move {
                let org_id = auth.merchant_account.get_org_id();
                let merchant_id = auth.merchant_account.get_id();
                let profile_id = auth
                    .profile_id
                    .ok_or(report!(UserErrors::JwtProfileIdMissing))
                    .change_context(AnalyticsError::AccessForbiddenError)?;
                let auth: AuthInfo = AuthInfo::ProfileLevel {
                    org_id: org_id.clone(),
                    merchant_id: merchant_id.clone(),
                    profile_ids: vec![profile_id.clone()],
                };
                analytics::payment_intents::get_sankey(&state.pool, &auth, req)
                    .await
                    .map(ApplicationResponse::Json)
            },
            &auth::JWTAuth {
                permission: Permission::ProfileAnalyticsRead,
            },
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }


pub async fn validate_create_request(
    _state: &SessionState,
    _merchant_context: &domain::MerchantContext,
    _req: &payouts::PayoutCreateRequest,
) -> RouterResult<(
    String,
    Option<payouts::PayoutMethodData>,
    String,
    Option<domain::Customer>,
    Option<PaymentMethod>,
)> {
    todo!()
}


pub struct NomupayErrorResponse {
    pub status: Option<String>,
    pub code: Option<u64>,
    pub error: Option<NomupayInnerError>,
    pub status_code: Option<u16>,
    pub detail: Option<Vec<DetailsType>>,
}


pub struct SignatureKey {
    pub api_key: Secret<String>,
    pub key1: Secret<String>,
    pub api_secret: Secret<String>,
}


pub struct SessionFlowRoutingInput<'a> {
    pub country: Option<CountryAlpha2>,
    pub payment_intent: &'a oss_storage::PaymentIntent,
    pub chosen: api::SessionConnectorDatas,
}


pub trait LoadRow<T>
where
    Self: AnalyticsDataSource,
    T: Sized,
{
    fn load_row(row: Self::Row) -> CustomResult<T, QueryExecutionError>;
}


File: crates/hyperswitch_connectors/src/connectors/airwallex/transformers.rs
 Public functions: 3
 Public structs: 55

use common_enums::enums;
use common_utils::{
    errors::ParsingError,
    pii::{Email, IpAddress},
    request::Method,
    types::{MinorUnit, StringMajorUnit},
};
use error_stack::ResultExt;
use hyperswitch_domain_models::{
    payment_method_data::{
        BankRedirectData, BankTransferData, PayLaterData, PaymentMethodData, WalletData,
    },
    router_data::{AccessToken, ConnectorAuthType, RouterData},
    router_flow_types::{
        refunds::{Execute, RSync},
        PSync,
    },
    router_request_types::{PaymentsSyncData, ResponseId},
    router_response_types::{PaymentsResponseData, RedirectForm, RefundsResponseData},
    types,
};
use hyperswitch_interfaces::errors;
use masking::{ExposeInterface, PeekInterface, Secret};
use serde::{Deserialize, Serialize};
use time::PrimitiveDateTime;
use url::Url;
use uuid::Uuid;

use crate::{
    types::{RefundsResponseRouterData, ResponseRouterData},
    utils::{
        self, BrowserInformationData, CardData as _, ForeignTryFrom, PaymentsAuthorizeRequestData,
        PhoneDetailsData, RouterData as _,
    },
};

pub struct AirwallexAuthType {
    pub x_api_key: Secret<String>,
    pub x_client_id: Secret<String>,
}

impl TryFrom<&ConnectorAuthType> for AirwallexAuthType {
    type Error = error_stack::Report<errors::ConnectorError>;

    fn try_from(auth_type: &ConnectorAuthType) -> Result<Self, Self::Error> {
        if let ConnectorAuthType::BodyKey { api_key, key1 } = auth_type {
            Ok(Self {
                x_api_key: api_key.clone(),
                x_client_id: key1.clone(),
            })
        } else {
            Err(errors::ConnectorError::FailedToObtainAuthType)?
        }
    }
}

#[derive(Default, Debug, Serialize, Eq, PartialEq)]
pub struct ReferrerData {
    #[serde(rename = "type")]
    r_type: String,
    version: String,
}

#[derive(Default, Debug, Serialize, Eq, PartialEq)]
pub struct AirwallexIntentRequest {
    // Unique ID to be sent for each transaction/operation request to the connector
    request_id: String,
    amount: StringMajorUnit,
    currency: enums::Currency,
    //ID created in merchant's order system that corresponds to this PaymentIntent.
    merchant_order_id: String,
    // This data is required to whitelist Hyperswitch at Airwallex.
    referrer_data: ReferrerData,
    order: Option<AirwallexOrderData>,
}

impl TryFrom<&AirwallexRouterData<&types::PaymentsPreProcessingRouterData>>
    for AirwallexIntentRequest
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: &AirwallexRouterData<&types::PaymentsPreProcessingRouterData>,
    ) -> Result<Self, Self::Error> {
        let referrer_data = ReferrerData {
            r_type: "hyperswitch".to_string(),
            version: "1.0.0".to_string(),
        };
        let amount = item.amount.clone();
        let currency = item.router_data.request.currency.ok_or(
            errors::ConnectorError::MissingRequiredField {
                field_name: "currency",
            },
        )?;

        let order = match item.router_data.request.payment_method_data {
            Some(PaymentMethodData::PayLater(_)) => Some(
                item.router_data
                    .request
                    .order_details
                    .as_ref()
                    .map(|order_data| AirwallexOrderData {
                        products: order_data
                            .iter()
                            .map(|product| AirwallexProductData {
                                name: product.product_name.clone(),
                                quantity: product.quantity,
                                unit_price: product.amount,
                            })
                            .collect(),
                        shipping: Some(AirwallexShippingData {
                            first_name: item.router_data.get_optional_shipping_first_name(),
                            last_name: item.router_data.get_optional_shipping_last_name(),
                            phone_number: item.router_data.get_optional_shipping_phone_number(),
                            address: AirwallexPLShippingAddress {
                                country_code: item.router_data.get_optional_shipping_country(),
                                city: item.router_data.get_optional_shipping_city(),
                                street: item.router_data.get_optional_shipping_line1(),
                                postcode: item.router_data.get_optional_shipping_zip(),
                            },
                        }),
                    })
                    .ok_or(errors::ConnectorError::MissingRequiredField {
                        field_name: "order_details",
                    })?,
            ),
            _ => None,
        };

        Ok(Self {
            request_id: Uuid::new_v4().to_string(),
            amount,
            currency,
            merchant_order_id: item.router_data.connector_request_reference_id.clone(),
            referrer_data,
            order,
        })
    }
}

#[derive(Debug, Serialize)]
pub struct AirwallexRouterData<T> {
    pub amount: StringMajorUnit,
    pub router_data: T,
}

impl<T> TryFrom<(StringMajorUnit, T)> for AirwallexRouterData<T> {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from((amount, router_data): (StringMajorUnit, T)) -> Result<Self, Self::Error> {
        Ok(Self {
            amount,
            router_data,
        })
    }
}

#[derive(Debug, Serialize)]
pub struct AirwallexPaymentsRequest {
    // Unique ID to be sent for each transaction/operation request to the connector
    request_id: String,
    payment_method: AirwallexPaymentMethod,
    payment_method_options: Option<AirwallexPaymentOptions>,
    return_url: Option<String>,
    device_data: DeviceData,
}

#[derive(Debug, Serialize, Eq, PartialEq, Default)]
pub struct AirwallexOrderData {
    products: Vec<AirwallexProductData>,
    shipping: Option<AirwallexShippingData>,
}

#[derive(Debug, Serialize, Eq, PartialEq)]
pub struct AirwallexProductData {
    name: String,
    quantity: u16,
    unit_price: MinorUnit,
}

#[derive(Debug, Serialize, Eq, PartialEq)]
pub struct AirwallexShippingData {
    first_name: Option<Secret<String>>,
    last_name: Option<Secret<String>>,
    phone_number: Option<Secret<String>>,
    address: AirwallexPLShippingAddress,
}

#[derive(Debug, Serialize, Eq, PartialEq)]
pub struct AirwallexPLShippingAddress {
    country_code: Option<enums::CountryAlpha2>,
    city: Option<String>,
    street: Option<Secret<String>>,
    postcode: Option<Secret<String>>,
}

#[derive(Debug, Serialize)]
pub struct DeviceData {
    accept_header: String,
    browser: Browser,
    ip_address: Secret<String, IpAddress>,
    language: String,
    mobile: Option<Mobile>,
    screen_color_depth: u8,
    screen_height: u32,
    screen_width: u32,
    timezone: String,
}

#[derive(Debug, Serialize)]
pub struct Browser {
    java_enabled: bool,
    javascript_enabled: bool,
    user_agent: String,
}

#[derive(Debug, Serialize)]
pub struct Mobile {
    device_model: Option<String>,
    os_type: Option<String>,
    os_version: Option<String>,
}

#[derive(Debug, Serialize)]
#[serde(untagged)]
pub enum AirwallexPaymentMethod {
    Card(AirwallexCard),
    Wallets(AirwallexWalletData),
    PayLater(AirwallexPayLaterData),
    BankRedirect(AirwallexBankRedirectData),
    BankTransfer(AirwallexBankTransferData),
}

#[derive(Debug, Serialize)]
pub struct AirwallexCard {
    card: AirwallexCardDetails,
    #[serde(rename = "type")]
    payment_method_type: AirwallexPaymentType,
}
#[derive(Debug, Serialize)]
pub struct AirwallexCardDetails {
    expiry_month: Secret<String>,
    expiry_year: Secret<String>,
    number: cards::CardNumber,
    cvc: Secret<String>,
}

#[derive(Debug, Serialize)]
#[serde(untagged)]
pub enum AirwallexWalletData {
    GooglePay(GooglePayData),
    Paypal(PaypalData),
    Skrill(SkrillData),
}

#[derive(Debug, Serialize)]
pub struct GooglePayData {
    googlepay: GooglePayDetails,
    #[serde(rename = "type")]
    payment_method_type: AirwallexPaymentType,
}

#[derive(Debug, Serialize)]
pub struct PaypalData {
    paypal: PaypalDetails,
    #[serde(rename = "type")]
    payment_method_type: AirwallexPaymentType,
}

#[derive(Debug, Serialize)]
pub struct SkrillData {
    skrill: SkrillDetails,
    #[serde(rename = "type")]
    payment_method_type: AirwallexPaymentType,
}

#[derive(Debug, Serialize)]
pub struct GooglePayDetails {
    encrypted_payment_token: Secret<String>,
    payment_data_type: GpayPaymentDataType,
}

#[derive(Debug, Serialize)]
pub struct PaypalDetails {
    shopper_name: Secret<String>,
    country_code: enums::CountryAlpha2,
}

#[derive(Debug, Serialize)]
pub struct SkrillDetails {
    shopper_name: Secret<String>,
    shopper_email: Email,
    country_code: enums::CountryAlpha2,
}

#[derive(Debug, Serialize)]
#[serde(untagged)]
pub enum AirwallexPayLaterData {
    Klarna(Box<KlarnaData>),
    Atome(AtomeData),
}

#[derive(Debug, Serialize)]
pub struct KlarnaData {
    klarna: KlarnaDetails,
    #[serde(rename = "type")]
    payment_method_type: AirwallexPaymentType,
}

#[derive(Debug, Serialize)]
pub struct KlarnaDetails {
    country_code: enums::CountryAlpha2,
    billing: Option<Billing>,
}

#[derive(Debug, Serialize)]
pub struct Billing {
    date_of_birth: Option<Secret<String>>,
    email: Option<Email>,
    first_name: Option<Secret<String>>,
    last_name: Option<Secret<String>>,
    phone_number: Option<Secret<String>>,
    address: Option<AddressAirwallex>,
}

#[derive(Debug, Serialize)]
pub struct AddressAirwallex {
    country_code: Option<enums::CountryAlpha2>,
    city: Option<String>,
    street: Option<Secret<String>>,
    postcode: Option<Secret<String>>,
}

#[derive(Debug, Serialize)]
pub struct AtomeData {
    atome: AtomeDetails,
    #[serde(rename = "type")]
    payment_method_type: AirwallexPaymentType,
}

#[derive(Debug, Serialize)]
pub struct AtomeDetails {
    shopper_phone: Secret<String>,
}

#[derive(Debug, Serialize)]
#[serde(untagged)]
pub enum AirwallexBankTransferData {
    IndonesianBankTransfer(IndonesianBankTransferData),
}

#[derive(Debug, Serialize)]
pub struct IndonesianBankTransferData {
    bank_transfer: IndonesianBankTransferDetails,
    #[serde(rename = "type")]
    payment_method_type: AirwallexPaymentType,
}

#[derive(Debug, Serialize)]
pub struct IndonesianBankTransferDetails {
    shopper_name: Secret<String>,
    shopper_email: Email,
    bank_name: common_enums::BankNames,
    country_code: enums::CountryAlpha2,
}

#[derive(Debug, Serialize)]
#[serde(untagged)]
pub enum AirwallexBankRedirectData {
    Trustly(TrustlyData),
    Blik(BlikData),
    Ideal(IdealData),
}

#[derive(Debug, Serialize)]
pub struct TrustlyData {
    trustly: TrustlyDetails,
    #[serde(rename = "type")]
    payment_method_type: AirwallexPaymentType,
}

#[derive(Debug, Serialize)]
pub struct TrustlyDetails {
    shopper_name: Secret<String>,
    country_code: enums::CountryAlpha2,
}

#[derive(Debug, Serialize)]
pub struct BlikData {
    blik: BlikDetails,
    #[serde(rename = "type")]
    payment_method_type: AirwallexPaymentType,
}

#[derive(Debug, Serialize)]
pub struct BlikDetails {
    shopper_name: Secret<String>,
}

#[derive(Debug, Serialize)]
pub struct IdealData {
    ideal: IdealDetails,
    #[serde(rename = "type")]
    payment_method_type: AirwallexPaymentType,
}

#[derive(Debug, Serialize)]
pub struct IdealDetails {
    bank_name: Option<common_enums::BankNames>,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum AirwallexPaymentType {
    Card,
    Googlepay,
    Paypal,
    Klarna,
    Atome,
    Trustly,
    Blik,
    Ideal,
    Skrill,
    BankTransfer,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum GpayPaymentDataType {
    EncryptedPaymentToken,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum AirwallexPaymentOptions {
    Card(AirwallexCardPaymentOptions),
    Klarna(AirwallexPayLaterPaymentOptions),
    Atome(AirwallexPayLaterPaymentOptions),
}

#[derive(Debug, Serialize)]
pub struct AirwallexCardPaymentOptions {
    auto_capture: bool,
}

#[derive(Debug, Serialize)]
pub struct AirwallexPayLaterPaymentOptions {
    auto_capture: bool,
}

impl TryFrom<&AirwallexRouterData<&types::PaymentsAuthorizeRouterData>>
    for AirwallexPaymentsRequest
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: &AirwallexRouterData<&types::PaymentsAuthorizeRouterData>,
    ) -> Result<Self, Self::Error> {
        let mut payment_method_options = None;
        let request = &item.router_data.request;
        let payment_method = match request.payment_method_data.clone() {
            PaymentMethodData::Card(ccard) => {
                payment_method_options =
                    Some(AirwallexPaymentOptions::Card(AirwallexCardPaymentOptions {
                        auto_capture: matches!(
                            request.capture_method,
                            Some(enums::CaptureMethod::Automatic)
                                | Some(enums::CaptureMethod::SequentialAutomatic)
                                | None
                        ),
                    }));
                Ok(AirwallexPaymentMethod::Card(AirwallexCard {
                    card: AirwallexCardDetails {
                        number: ccard.card_number.clone(),
                        expiry_month: ccard.card_exp_month.clone(),
                        expiry_year: ccard.get_expiry_year_4_digit(),
                        cvc: ccard.card_cvc,
                    },
                    payment_method_type: AirwallexPaymentType::Card,
                }))
            }
            PaymentMethodData::Wallet(ref wallet_data) => get_wallet_details(wallet_data, item),
            PaymentMethodData::PayLater(ref paylater_data) => {
                let paylater_options = AirwallexPayLaterPaymentOptions {
                    auto_capture: item.router_data.request.is_auto_capture()?,
                };

                payment_method_options = match paylater_data {
                    PayLaterData::KlarnaRedirect { .. } => {
                        Some(AirwallexPaymentOptions::Klarna(paylater_options))
                    }
                    PayLaterData::AtomeRedirect { .. } => {
                        Some(AirwallexPaymentOptions::Atome(paylater_options))
                    }
                    _ => None,
                };

                get_paylater_details(paylater_data, item)
            }
            PaymentMethodData::BankTransfer(ref banktransfer_data) => {
                get_banktransfer_details(banktransfer_data, item)
            }
            PaymentMethodData::BankRedirect(ref bankredirect_data) => {
                get_bankredirect_details(bankredirect_data, item)
            }
            PaymentMethodData::BankDebit(_)
            | PaymentMethodData::CardRedirect(_)
            | PaymentMethodData::Crypto(_)
            | PaymentMethodData::MandatePayment
            | PaymentMethodData::Reward
            | PaymentMethodData::RealTimePayment(_)
            | PaymentMethodData::MobilePayment(_)
            | PaymentMethodData::Upi(_)
            | PaymentMethodData::Voucher(_)
            | PaymentMethodData::GiftCard(_)
            | PaymentMethodData::OpenBanking(_)
            | PaymentMethodData::CardToken(_)
            | PaymentMethodData::NetworkToken(_)
            | PaymentMethodData::CardDetailsForNetworkTransactionId(_) => {
                Err(errors::ConnectorError::NotImplemented(
                    utils::get_unimplemented_payment_method_error_message("airwallex"),
                ))
            }
        }?;
        let device_data = get_device_data(item.router_data)?;

        let return_url = match &request.payment_method_data {
            PaymentMethodData::Wallet(wallet_data) => match wallet_data {
                WalletData::PaypalRedirect(_paypal_details) => {
                    item.router_data.request.router_return_url.clone()
                }
                WalletData::Skrill(_) => item.router_data.request.router_return_url.clone(),
                _ => request.complete_authorize_url.clone(),
            },
            PaymentMethodData::BankRedirect(_bankredirect_data) => {
                item.router_data.request.router_return_url.clone()
            }
            PaymentMethodData::PayLater(_paylater_data) => {
                item.router_data.request.router_return_url.clone()
            }
            _ => request.complete_authorize_url.clone(),
        };

        Ok(Self {
            request_id: Uuid::new_v4().to_string(),
            payment_method,
            payment_method_options,
            return_url,
            device_data,
        })
    }
}

fn get_device_data(
    item: &types::PaymentsAuthorizeRouterData,
) -> Result<DeviceData, error_stack::Report<errors::ConnectorError>> {
    let info = item.request.get_browser_info()?;
    let browser = Browser {
        java_enabled: info.get_java_enabled()?,
        javascript_enabled: info.get_java_script_enabled()?,
        user_agent: info.get_user_agent()?,
    };
    let mobile = {
        let device_model = info.get_device_model().ok();
        let os_type = info.get_os_type().ok();
        let os_version = info.get_os_version().ok();

        if device_model.is_some() || os_type.is_some() || os_version.is_some() {
            Some(Mobile {
                device_model,
                os_type,
                os_version,
            })
        } else {
            None
        }
    };
    Ok(DeviceData {
        accept_header: info.get_accept_header()?,
        browser,
        ip_address: info.get_ip_address()?,
        mobile,
        screen_color_depth: info.get_color_depth()?,
        screen_height: info.get_screen_height()?,
        screen_width: info.get_screen_width()?,
        timezone: info.get_time_zone()?.to_string(),
        language: info.get_language()?,
    })
}

fn get_banktransfer_details(
    banktransfer_data: &BankTransferData,
    item: &AirwallexRouterData<&types::PaymentsAuthorizeRouterData>,
) -> Result<AirwallexPaymentMethod, errors::ConnectorError> {
    let _bank_transfer_details = match banktransfer_data {
        BankTransferData::IndonesianBankTransfer { bank_name } => {
            AirwallexPaymentMethod::BankTransfer(AirwallexBankTransferData::IndonesianBankTransfer(
                IndonesianBankTransferData {
                    bank_transfer: IndonesianBankTransferDetails {
                        shopper_name: item.router_data.get_billing_full_name().map_err(|_| {
                            errors::ConnectorError::MissingRequiredField {
                                field_name: "shopper_name",
                            }
                        })?,
                        shopper_email: item.router_data.get_billing_email().map_err(|_| {
                            errors::ConnectorError::MissingRequiredField {
                                field_name: "shopper_email",
                            }
                        })?,
                        bank_name: bank_name.ok_or(
                            errors::ConnectorError::MissingRequiredField {
                                field_name: "bank_name",
                            },
                        )?,
                        country_code: item.router_data.get_billing_country().map_err(|_| {
                            errors::ConnectorError::MissingRequiredField {
                                field_name: "country_code",
                            }
                        })?,
                    },
                    payment_method_type: AirwallexPaymentType::BankTransfer,
                },
            ))
        }
        _ => Err(errors::ConnectorError::NotImplemented(
            utils::get_unimplemented_payment_method_error_message("airwallex"),
        ))?,
    };
    let not_implemented = Err(errors::ConnectorError::NotImplemented(
        utils::get_unimplemented_payment_method_error_message("airwallex"),
    ))?;
    Ok(not_implemented)
}

fn get_paylater_details(
    paylater_data: &PayLaterData,
    item: &AirwallexRouterData<&types::PaymentsAuthorizeRouterData>,
) -> Result<AirwallexPaymentMethod, errors::ConnectorError> {
    let paylater_details = match paylater_data {
        PayLaterData::KlarnaRedirect {} => {
            AirwallexPaymentMethod::PayLater(AirwallexPayLaterData::Klarna(Box::new(KlarnaData {
                klarna: KlarnaDetails {
                    country_code: item.router_data.get_billing_country().map_err(|_| {
                        errors::ConnectorError::MissingRequiredField {
                            field_name: "country_code",
                        }
                    })?,
                    billing: Some(Billing {
                        date_of_birth: None,
                        first_name: item.router_data.get_optional_billing_first_name(),
                        last_name: item.router_data.get_optional_billing_last_name(),
                        email: item.router_data.get_optional_billing_email(),
                        phone_number: item.router_data.get_optional_billing_phone_number(),
                        address: Some(AddressAirwallex {
                            country_code: item.router_data.get_optional_billing_country(),
                            city: item.router_data.get_optional_billing_city(),
                            street: item.router_data.get_optional_billing_line1(),
                            postcode: item.router_data.get_optional_billing_zip(),
                        }),
                    }),
                },
                payment_method_type: AirwallexPaymentType::Klarna,
            })))
        }
        PayLaterData::AtomeRedirect {} => {
            AirwallexPaymentMethod::PayLater(AirwallexPayLaterData::Atome(AtomeData {
                atome: AtomeDetails {
                    shopper_phone: item
                        .router_data
                        .get_billing_phone()
                        .map_err(|_| errors::ConnectorError::MissingRequiredField {
                            field_name: "shopper_phone",
                        })?
                        .get_number_with_country_code()
                        .map_err(|_| errors::ConnectorError::MissingRequiredField {
                            field_name: "country_code",
                        })?,
                },
                payment_method_type: AirwallexPaymentType::Atome,
            }))
        }
        _ => Err(errors::ConnectorError::NotImplemented(
            utils::get_unimplemented_payment_method_error_message("airwallex"),
        ))?,
    };
    Ok(paylater_details)
}

fn get_bankredirect_details(
    bankredirect_data: &BankRedirectData,
    item: &AirwallexRouterData<&types::PaymentsAuthorizeRouterData>,
) -> Result<AirwallexPaymentMethod, errors::ConnectorError> {
    let bank_redirect_details = match bankredirect_data {
        BankRedirectData::Trustly { .. } => {
            AirwallexPaymentMethod::BankRedirect(AirwallexBankRedirectData::Trustly(TrustlyData {
                trustly: TrustlyDetails {
                    shopper_name: item.router_data.get_billing_full_name().map_err(|_| {
                        errors::ConnectorError::MissingRequiredField {
                            field_name: "shopper_name",
                        }
                    })?,
                    country_code: item.router_data.get_billing_country().map_err(|_| {
                        errors::ConnectorError::MissingRequiredField {
                            field_name: "country_code",
                        }
                    })?,
                },
                payment_method_type: AirwallexPaymentType::Trustly,
            }))
        }
        BankRedirectData::Blik { .. } => {
            AirwallexPaymentMethod::BankRedirect(AirwallexBankRedirectData::Blik(BlikData {
                blik: BlikDetails {
                    shopper_name: item.router_data.get_billing_full_name().map_err(|_| {
                        errors::ConnectorError::MissingRequiredField {
                            field_name: "shopper_name",
                        }
                    })?,
                },
                payment_method_type: AirwallexPaymentType::Blik,
            }))
        }
        BankRedirectData::Ideal { .. } => {
            AirwallexPaymentMethod::BankRedirect(AirwallexBankRedirectData::Ideal(IdealData {
                ideal: IdealDetails { bank_name: None },
                payment_method_type: AirwallexPaymentType::Ideal,
            }))
        }
        _ => Err(errors::ConnectorError::NotImplemented(
            utils::get_unimplemented_payment_method_error_message("airwallex"),
        ))?,
    };
    Ok(bank_redirect_details)
}

fn get_wallet_details(
    wallet_data: &WalletData,
    item: &AirwallexRouterData<&types::PaymentsAuthorizeRouterData>,
) -> Result<AirwallexPaymentMethod, errors::ConnectorError> {
    let wallet_details: AirwallexPaymentMethod = match wallet_data {
        WalletData::GooglePay(gpay_details) => {
            let token = gpay_details
                .tokenization_data
                .get_encrypted_google_pay_token()
                .attach_printable("Failed to get gpay wallet token")
                .map_err(|_| errors::ConnectorError::MissingRequiredField {
                    field_name: "gpay wallet_token",
                })?;
            AirwallexPaymentMethod::Wallets(AirwallexWalletData::GooglePay(GooglePayData {
                googlepay: GooglePayDetails {
                    encrypted_payment_token: Secret::new(token.clone()),
                    payment_data_type: GpayPaymentDataType::EncryptedPaymentToken,
                },
                payment_method_type: AirwallexPaymentType::Googlepay,
            }))
        }
        WalletData::PaypalRedirect(_paypal_details) => {
            AirwallexPaymentMethod::Wallets(AirwallexWalletData::Paypal(PaypalData {
                paypal: PaypalDetails {
                    shopper_name: item
                        .router_data
                        .request
                        .customer_name
                        .as_ref()
                        .cloned()
                        .or_else(|| item.router_data.get_billing_full_name().ok())
                        .ok_or(errors::ConnectorError::MissingRequiredField {
                            field_name: "shopper_name",
                        })?,
                    country_code: item.router_data.get_billing_country().map_err(|_| {
                        errors::ConnectorError::MissingRequiredField {
                            field_name: "country_code",
                        }
                    })?,
                },
                payment_method_type: AirwallexPaymentType::Paypal,
            }))
        }
        WalletData::Skrill(_skrill_details) => {
            AirwallexPaymentMethod::Wallets(AirwallexWalletData::Skrill(SkrillData {
                skrill: SkrillDetails {
                    shopper_name: item
                        .router_data
                        .request
                        .customer_name
                        .as_ref()
                        .cloned()
                        .or_else(|| item.router_data.get_billing_full_name().ok())
                        .ok_or(errors::ConnectorError::MissingRequiredField {
                            field_name: "shopper_name",
                        })?,
                    shopper_email: item.router_data.get_billing_email().map_err(|_| {
                        errors::ConnectorError::MissingRequiredField {
                            field_name: "shopper_email",
                        }
                    })?,
                    country_code: item.router_data.get_billing_country().map_err(|_| {
                        errors::ConnectorError::MissingRequiredField {
                            field_name: "country_code",
                        }
                    })?,
                },
                payment_method_type: AirwallexPaymentType::Skrill,
            }))
        }
        WalletData::AliPayQr(_)
        | WalletData::AliPayRedirect(_)
        | WalletData::AliPayHkRedirect(_)
        | WalletData::AmazonPayRedirect(_)
        | WalletData::Paysera(_)
        | WalletData::MomoRedirect(_)
        | WalletData::KakaoPayRedirect(_)
        | WalletData::GoPayRedirect(_)
        | WalletData::GcashRedirect(_)
        | WalletData::AmazonPay(_)
        | WalletData::ApplePay(_)
        | WalletData::BluecodeRedirect {}
        | WalletData::ApplePayRedirect(_)
        | WalletData::ApplePayThirdPartySdk(_)
        | WalletData::DanaRedirect {}
        | WalletData::GooglePayRedirect(_)
        | WalletData::GooglePayThirdPartySdk(_)
        | WalletData::MbWayRedirect(_)
        | WalletData::MobilePayRedirect(_)
        | WalletData::PaypalSdk(_)
        | WalletData::Paze(_)
        | WalletData::SamsungPay(_)
        | WalletData::TwintRedirect {}
        | WalletData::VippsRedirect {}
        | WalletData::TouchNGoRedirect(_)
        | WalletData::WeChatPayRedirect(_)
        | WalletData::WeChatPayQr(_)
        | WalletData::CashappQr(_)
        | WalletData::SwishQr(_)
        | WalletData::Mifinity(_)
        | WalletData::RevolutPay(_) => Err(errors::ConnectorError::NotImplemented(
            utils::get_unimplemented_payment_method_error_message("airwallex"),
        ))?,
    };
    Ok(wallet_details)
}

#[derive(Deserialize, Debug, Serialize)]
pub struct AirwallexAuthUpdateResponse {
    #[serde(with = "common_utils::custom_serde::iso8601")]
    expires_at: PrimitiveDateTime,
    token: Secret<String>,
}

impl<F, T> TryFrom<ResponseRouterData<F, AirwallexAuthUpdateResponse, T, AccessToken>>
    for RouterData<F, T, AccessToken>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<F, AirwallexAuthUpdateResponse, T, AccessToken>,
    ) -> Result<Self, Self::Error> {
        let expires = (item.response.expires_at - common_utils::date_time::now()).whole_seconds();
        Ok(Self {
            response: Ok(AccessToken {
                token: item.response.token,
                expires,
            }),
            ..item.data
        })
    }
}

#[derive(Default, Debug, Serialize, Eq, PartialEq)]
pub struct AirwallexCompleteRequest {
    request_id: String,
    three_ds: AirwallexThreeDsData,
    #[serde(rename = "type")]
    three_ds_type: AirwallexThreeDsType,
}

#[derive(Default, Debug, Serialize, Eq, PartialEq)]
pub struct AirwallexThreeDsData {
    acs_response: Option<Secret<String>>,
}

#[derive(Default, Debug, Serialize, Eq, PartialEq)]
pub enum AirwallexThreeDsType {
    #[default]
    #[serde(rename = "3ds_continue")]
    ThreeDSContinue,
}

impl TryFrom<&types::PaymentsCompleteAuthorizeRouterData> for AirwallexCompleteRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &types::PaymentsCompleteAuthorizeRouterData) -> Result<Self, Self::Error> {
        Ok(Self {
            request_id: Uuid::new_v4().to_string(),
            three_ds: AirwallexThreeDsData {
                acs_response: item
                    .request
                    .redirect_response
                    .as_ref()
                    .map(|f| f.payload.to_owned())
                    .ok_or(errors::ConnectorError::MissingRequiredField {
                        field_name: "redirect_response.payload",
                    })?
                    .as_ref()
                    .map(|data| serde_json::to_string(data.peek()))
                    .transpose()
                    .change_context(errors::ConnectorError::ResponseDeserializationFailed)?
                    .map(Secret::new),
            },
            three_ds_type: AirwallexThreeDsType::ThreeDSContinue,
        })
    }
}

#[derive(Default, Debug, Serialize, Eq, PartialEq)]
pub struct AirwallexPaymentsCaptureRequest {
    // Unique ID to be sent for each transaction/operation request to the connector
    request_id: String,
    amount: Option<String>,
}

impl TryFrom<&types::PaymentsCaptureRouterData> for AirwallexPaymentsCaptureRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &types::PaymentsCaptureRouterData) -> Result<Self, Self::Error> {
        Ok(Self {
            request_id: Uuid::new_v4().to_string(),
            amount: Some(utils::to_currency_base_unit(
                item.request.amount_to_capture,
                item.request.currency,
            )?),
        })
    }
}

#[derive(Default, Debug, Serialize, Eq, PartialEq)]
pub struct AirwallexPaymentsCancelRequest {
    // Unique ID to be sent for each transaction/operation request to the connector
    request_id: String,
    cancellation_reason: Option<String>,
}

impl TryFrom<&types::PaymentsCancelRouterData> for AirwallexPaymentsCancelRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &types::PaymentsCancelRouterData) -> Result<Self, Self::Error> {
        Ok(Self {
            request_id: Uuid::new_v4().to_string(),
            cancellation_reason: item.request.cancellation_reason.clone(),
        })
    }
}

// PaymentsResponse
#[derive(Debug, Clone, Default, Deserialize, PartialEq, Serialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum AirwallexPaymentStatus {
    Succeeded,
    Failed,
    #[default]
    Pending,
    RequiresPaymentMethod,
    RequiresCustomerAction,
    RequiresCapture,
    Cancelled,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum AirwallexNextAction {
    Payments(AirwallexPaymentsNextAction),
    Redirect(AirwallexRedirectNextAction),
}

fn get_payment_status(
    status: &AirwallexPaymentStatus,
    next_action: &Option<AirwallexNextAction>,
) -> enums::AttemptStatus {
    match status.clone() {
        AirwallexPaymentStatus::Succeeded => enums::AttemptStatus::Charged,
        AirwallexPaymentStatus::Failed => enums::AttemptStatus::Failure,
        AirwallexPaymentStatus::Pending => enums::AttemptStatus::Pending,
        AirwallexPaymentStatus::RequiresPaymentMethod => enums::AttemptStatus::PaymentMethodAwaited,
        AirwallexPaymentStatus::RequiresCustomerAction => next_action.as_ref().map_or(
            enums::AttemptStatus::AuthenticationPending,
            |next_action| match next_action {
                AirwallexNextAction::Payments(payments_next_action) => {
                    match payments_next_action.stage {
                        AirwallexNextActionStage::WaitingDeviceDataCollection => {
                            enums::AttemptStatus::DeviceDataCollectionPending
                        }
                        AirwallexNextActionStage::WaitingUserInfoInput => {
                            enums::AttemptStatus::AuthenticationPending
                        }
                    }
                }
                AirwallexNextAction::Redirect(_) => enums::AttemptStatus::AuthenticationPending,
            },
        ),
        AirwallexPaymentStatus::RequiresCapture => enums::AttemptStatus::Authorized,
        AirwallexPaymentStatus::Cancelled => enums::AttemptStatus::Voided,
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum AirwallexNextActionStage {
    WaitingDeviceDataCollection,
    WaitingUserInfoInput,
}

#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
pub struct AirwallexRedirectFormData {
    #[serde(rename = "JWT")]
    jwt: Option<Secret<String>>,
    #[serde(rename = "threeDSMethodData")]
    three_ds_method_data: Option<Secret<String>>,
    token: Option<Secret<String>>,
    provider: Option<String>,
    version: Option<String>,
}

#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
pub struct AirwallexPaymentsNextAction {
    url: Url,
    method: Method,
    data: AirwallexRedirectFormData,
    stage: AirwallexNextActionStage,
}

#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
pub struct AirwallexRedirectNextAction {
    url: Url,
    method: Method,
}

#[derive(Default, Debug, Clone, Deserialize, PartialEq, Serialize)]
pub struct AirwallexPaymentsResponse {
    status: AirwallexPaymentStatus,
    //Unique identifier for the PaymentIntent
    id: String,
    amount: Option<f32>,
    //ID of the PaymentConsent related to this PaymentIntent
    payment_consent_id: Option<Secret<String>>,
    next_action: Option<AirwallexPaymentsNextAction>,
}

#[derive(Default, Debug, Clone, Deserialize, PartialEq, Serialize)]
pub struct AirwallexRedirectResponse {
    status: AirwallexPaymentStatus,
    id: String,
    amount: Option<f32>,
    payment_consent_id: Option<Secret<String>>,
    next_action: Option<AirwallexRedirectNextAction>,
}

#[derive(Default, Debug, Clone, Deserialize, PartialEq, Serialize)]
pub struct AirwallexPaymentsSyncResponse {
    status: AirwallexPaymentStatus,
    //Unique identifier for the PaymentIntent
    id: String,
    amount: Option<f32>,
    //ID of the PaymentConsent related to this PaymentIntent
    payment_consent_id: Option<Secret<String>>,
    next_action: Option<AirwallexPaymentsNextAction>,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(untagged)]
pub enum AirwallexAuthorizeResponse {
    AirwallexPaymentsResponse(AirwallexPaymentsResponse),
    AirwallexRedirectResponse(AirwallexRedirectResponse),
}

fn get_redirection_form(response_url_data: AirwallexPaymentsNextAction) -> Option<RedirectForm> {
    Some(RedirectForm::Form {
        endpoint: response_url_data.url.to_string(),
        method: response_url_data.method,
        form_fields: std::collections::HashMap::from([
            //Some form fields might be empty based on the authentication type by the connector
            (
                "JWT".to_string(),
                response_url_data
                    .data
                    .jwt
                    .map(|jwt| jwt.expose())
                    .unwrap_or_default(),
            ),
            (
                "threeDSMethodData".to_string(),
                response_url_data
                    .data
                    .three_ds_method_data
                    .map(|three_ds_method_data| three_ds_method_data.expose())
                    .unwrap_or_default(),
            ),
            (
                "token".to_string(),
                response_url_data
                    .data
                    .token
                    .map(|token: Secret<String>| token.expose())
                    .unwrap_or_default(),
            ),
            (
                "provider".to_string(),
                response_url_data.data.provider.unwrap_or_default(),
            ),
            (
                "version".to_string(),
                response_url_data.data.version.unwrap_or_default(),
            ),
        ]),


pub struct TokenCreation {
    #[serde(rename = "type")]
    pub token_type: TokenCreationType,
}


File: crates/hyperswitch_connectors/src/connectors/cryptopay/transformers.rs
 Public structs: 8

use common_enums::enums;
use common_utils::{
    pii,
    types::{MinorUnit, StringMajorUnit},
};
use hyperswitch_domain_models::{
    payment_method_data::PaymentMethodData,
    router_data::{ConnectorAuthType, ErrorResponse, RouterData},
    router_request_types::ResponseId,
    router_response_types::{PaymentsResponseData, RedirectForm},
    types,
};
use hyperswitch_interfaces::{consts, errors};
use masking::Secret;
use reqwest::Url;
use serde::{Deserialize, Serialize};

use crate::{
    types::ResponseRouterData,
    utils::{self, CryptoData, ForeignTryFrom, PaymentsAuthorizeRequestData},
};

#[derive(Debug, Serialize)]
pub struct CryptopayRouterData<T> {
    pub amount: StringMajorUnit,
    pub router_data: T,
}

impl<T> From<(StringMajorUnit, T)> for CryptopayRouterData<T> {
    fn from((amount, item): (StringMajorUnit, T)) -> Self {
        Self {
            amount,
            router_data: item,
        }
    }
}

#[derive(Default, Debug, Serialize)]
pub struct CryptopayPaymentsRequest {
    price_amount: StringMajorUnit,
    price_currency: enums::Currency,
    pay_currency: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    network: Option<String>,
    success_redirect_url: Option<String>,
    unsuccess_redirect_url: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    metadata: Option<pii::SecretSerdeValue>,
    custom_id: String,
}

impl TryFrom<&CryptopayRouterData<&types::PaymentsAuthorizeRouterData>>
    for CryptopayPaymentsRequest
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: &CryptopayRouterData<&types::PaymentsAuthorizeRouterData>,
    ) -> Result<Self, Self::Error> {
        let cryptopay_request = match item.router_data.request.payment_method_data {
            PaymentMethodData::Crypto(ref cryptodata) => {
                let pay_currency = cryptodata.get_pay_currency()?;
                Ok(Self {
                    price_amount: item.amount.clone(),
                    price_currency: item.router_data.request.currency,
                    pay_currency,
                    network: cryptodata.network.to_owned(),
                    success_redirect_url: item.router_data.request.router_return_url.clone(),
                    unsuccess_redirect_url: item.router_data.request.router_return_url.clone(),
                    //Cryptopay only accepts metadata as Object. If any other type, payment will fail with error.
                    metadata: item.router_data.request.get_metadata_as_object(),
                    custom_id: item.router_data.connector_request_reference_id.clone(),
                })
            }
            PaymentMethodData::Card(_)
            | PaymentMethodData::CardRedirect(_)
            | PaymentMethodData::Wallet(_)
            | PaymentMethodData::PayLater(_)
            | PaymentMethodData::BankRedirect(_)
            | PaymentMethodData::BankDebit(_)
            | PaymentMethodData::BankTransfer(_)
            | PaymentMethodData::MandatePayment
            | PaymentMethodData::Reward
            | PaymentMethodData::RealTimePayment(_)
            | PaymentMethodData::Upi(_)
            | PaymentMethodData::MobilePayment(_)
            | PaymentMethodData::Voucher(_)
            | PaymentMethodData::GiftCard(_)
            | PaymentMethodData::OpenBanking(_)
            | PaymentMethodData::CardToken(_)
            | PaymentMethodData::NetworkToken(_)
            | PaymentMethodData::CardDetailsForNetworkTransactionId(_) => {
                Err(errors::ConnectorError::NotImplemented(
                    utils::get_unimplemented_payment_method_error_message("CryptoPay"),
                ))
            }
        }?;
        Ok(cryptopay_request)
    }
}

// Auth Struct
pub struct CryptopayAuthType {
    pub(super) api_key: Secret<String>,
    pub(super) api_secret: Secret<String>,
}

impl TryFrom<&ConnectorAuthType> for CryptopayAuthType {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(auth_type: &ConnectorAuthType) -> Result<Self, Self::Error> {
        if let ConnectorAuthType::BodyKey { api_key, key1 } = auth_type {
            Ok(Self {
                api_key: api_key.to_owned(),
                api_secret: key1.to_owned(),
            })
        } else {
            Err(errors::ConnectorError::FailedToObtainAuthType.into())
        }
    }
}
// PaymentsResponse
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum CryptopayPaymentStatus {
    New,
    Completed,
    Unresolved,
    Refunded,
    Cancelled,
}

impl From<CryptopayPaymentStatus> for enums::AttemptStatus {
    fn from(item: CryptopayPaymentStatus) -> Self {
        match item {
            CryptopayPaymentStatus::New => Self::AuthenticationPending,
            CryptopayPaymentStatus::Completed => Self::Charged,
            CryptopayPaymentStatus::Cancelled => Self::Failure,
            CryptopayPaymentStatus::Unresolved | CryptopayPaymentStatus::Refunded => {
                Self::Unresolved
            } //mapped refunded to Unresolved because refund api is not available, also merchant has done the action on the connector dashboard.
        }
    }
}

#[derive(Debug, Serialize, Deserialize)]
pub struct CryptopayPaymentsResponse {
    pub data: CryptopayPaymentResponseData,
}

impl<F, T>
    ForeignTryFrom<(
        ResponseRouterData<F, CryptopayPaymentsResponse, T, PaymentsResponseData>,
        Option<MinorUnit>,
    )> for RouterData<F, T, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn foreign_try_from(
        (item, amount_captured_in_minor_units): (
            ResponseRouterData<F, CryptopayPaymentsResponse, T, PaymentsResponseData>,
            Option<MinorUnit>,
        ),
    ) -> Result<Self, Self::Error> {
        let status = enums::AttemptStatus::from(item.response.data.status.clone());
        let response = if utils::is_payment_failure(status) {
            let payment_response = &item.response.data;
            Err(ErrorResponse {
                code: payment_response
                    .name
                    .clone()
                    .unwrap_or(consts::NO_ERROR_CODE.to_string()),
                message: payment_response
                    .status_context
                    .clone()
                    .unwrap_or(consts::NO_ERROR_MESSAGE.to_string()),
                reason: payment_response.status_context.clone(),
                status_code: item.http_code,
                attempt_status: None,
                connector_transaction_id: Some(payment_response.id.clone()),
                network_advice_code: None,
                network_decline_code: None,
                network_error_message: None,
                connector_metadata: None,
            })
        } else {
            let redirection_data = item
                .response
                .data
                .hosted_page_url
                .map(|x| RedirectForm::from((x, common_utils::request::Method::Get)));
            Ok(PaymentsResponseData::TransactionResponse {
                resource_id: ResponseId::ConnectorTransactionId(item.response.data.id.clone()),
                redirection_data: Box::new(redirection_data),
                mandate_reference: Box::new(None),
                connector_metadata: None,
                network_txn_id: None,
                connector_response_reference_id: item
                    .response
                    .data
                    .custom_id
                    .or(Some(item.response.data.id)),
                incremental_authorization_allowed: None,
                charges: None,
            })
        };
        match amount_captured_in_minor_units {
            Some(minor_amount) => {
                let amount_captured = Some(minor_amount.get_amount_as_i64());
                Ok(Self {
                    status,
                    response,
                    amount_captured,
                    minor_amount_captured: amount_captured_in_minor_units,
                    ..item.data
                })
            }
            None => Ok(Self {
                status,
                response,
                ..item.data
            }),
        }
    }
}

#[derive(Default, Debug, Serialize, Deserialize)]
pub struct CryptopayErrorData {
    pub code: String,
    pub message: String,
    pub reason: Option<String>,
}

#[derive(Default, Debug, Serialize, Deserialize)]
pub struct CryptopayErrorResponse {
    pub error: CryptopayErrorData,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct CryptopayPaymentResponseData {
    pub id: String,
    pub custom_id: Option<String>,
    pub customer_id: Option<String>,
    pub status: CryptopayPaymentStatus,
    pub status_context: Option<String>,
    pub address: Option<Secret<String>>,
    pub network: Option<String>,
    pub uri: Option<String>,
    pub price_amount: Option<StringMajorUnit>,
    pub price_currency: Option<String>,
    pub pay_amount: Option<StringMajorUnit>,
    pub pay_currency: Option<String>,
    pub fee: Option<String>,
    pub fee_currency: Option<String>,
    pub paid_amount: Option<String>,
    pub name: Option<String>,
    pub description: Option<String>,
    pub success_redirect_url: Option<String>,
    pub unsuccess_redirect_url: Option<String>,
    pub hosted_page_url: Option<Url>,
    pub created_at: Option<String>,
    pub expires_at: Option<String>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct CryptopayWebhookDetails {
    #[serde(rename = "type")]
    pub service_type: String,
    pub event: WebhookEvent,
    pub data: CryptopayPaymentResponseData,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum WebhookEvent {
    TransactionCreated,
    TransactionConfirmed,
    StatusChanged,
}


pub struct PaymentMethodSession {
    pub id: common_utils::id_type::GlobalPaymentMethodSessionId,
    pub customer_id: common_utils::id_type::GlobalCustomerId,
    pub billing: Option<common_utils::encryption::Encryption>,
    pub psp_tokenization: Option<common_types::payment_methods::PspTokenization>,
    pub network_tokenization: Option<common_types::payment_methods::NetworkTokenization>,
    pub tokenization_data: Option<pii::SecretSerdeValue>,
    pub return_url: Option<common_utils::types::Url>,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub expires_at: time::PrimitiveDateTime,
    pub associated_payment_methods: Option<Vec<String>>,
    pub associated_payment: Option<common_utils::id_type::GlobalPaymentId>,
    pub associated_token_id: Option<common_utils::id_type::GlobalTokenId>,
}


pub fn is_db_not_found(&self) -> bool {
        match self {
            Self::DatabaseError(err) => matches!(err.current_context(), DatabaseError::NotFound),
            Self::ValueNotFound(_) => true,
            Self::RedisError(err) => matches!(err.current_context(), RedisError::NotFound),
            _ => false,
        }
    }


impl PaymentIntentFetchConstraints {
    pub fn get_profile_id(&self) -> Option<id_type::ProfileId> {
        let Self::List(pi_list_params) = self;
        pi_list_params.profile_id.clone()
    }
}


pub async fn retrieve_payment_method_with_token(
    state: &SessionState,
    merchant_key_store: &domain::MerchantKeyStore,
    token_data: &storage::PaymentTokenData,
    payment_intent: &PaymentIntent,
    payment_attempt: &PaymentAttempt,
    card_token_data: Option<&domain::CardToken>,
    customer: &Option<domain::Customer>,
    storage_scheme: common_enums::enums::MerchantStorageScheme,
    mandate_id: Option<api_models::payments::MandateIds>,
    payment_method_info: Option<domain::PaymentMethod>,
    business_profile: &domain::Profile,
    should_retry_with_pan: bool,
    vault_data: Option<&VaultData>,
) -> RouterResult<storage::PaymentMethodDataWithId> {
    let token = match token_data {
        storage::PaymentTokenData::TemporaryGeneric(generic_token) => {
            payment_helpers::retrieve_payment_method_with_temporary_token(
                state,
                &generic_token.token,
                payment_intent,
                payment_attempt,
                merchant_key_store,
                card_token_data,
            )
            .await?
            .map(
                |(payment_method_data, payment_method)| storage::PaymentMethodDataWithId {
                    payment_method_data: Some(payment_method_data),
                    payment_method: Some(payment_method),
                    payment_method_id: None,
                },
            )
            .unwrap_or_default()
        }

        storage::PaymentTokenData::Temporary(generic_token) => {
            payment_helpers::retrieve_payment_method_with_temporary_token(
                state,
                &generic_token.token,
                payment_intent,
                payment_attempt,
                merchant_key_store,
                card_token_data,
            )
            .await?
            .map(
                |(payment_method_data, payment_method)| storage::PaymentMethodDataWithId {
                    payment_method_data: Some(payment_method_data),
                    payment_method: Some(payment_method),
                    payment_method_id: None,
                },
            )
            .unwrap_or_default()
        }

        storage::PaymentTokenData::Permanent(card_token) => {
            payment_helpers::retrieve_payment_method_data_with_permanent_token(
                state,
                card_token.locker_id.as_ref().unwrap_or(&card_token.token),
                card_token
                    .payment_method_id
                    .as_ref()
                    .unwrap_or(&card_token.token),
                payment_intent,
                card_token_data,
                merchant_key_store,
                storage_scheme,
                mandate_id,
                payment_method_info
                    .get_required_value("PaymentMethod")
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("PaymentMethod not found")?,
                business_profile,
                payment_attempt.connector.clone(),
                should_retry_with_pan,
                vault_data,
            )
            .await
            .map(|card| Some((card, enums::PaymentMethod::Card)))?
            .map(
                |(payment_method_data, payment_method)| storage::PaymentMethodDataWithId {
                    payment_method_data: Some(payment_method_data),
                    payment_method: Some(payment_method),
                    payment_method_id: Some(
                        card_token
                            .payment_method_id
                            .as_ref()
                            .unwrap_or(&card_token.token)
                            .to_string(),
                    ),
                },
            )
            .unwrap_or_default()
        }

        storage::PaymentTokenData::PermanentCard(card_token) => {
            payment_helpers::retrieve_payment_method_data_with_permanent_token(
                state,
                card_token.locker_id.as_ref().unwrap_or(&card_token.token),
                card_token
                    .payment_method_id
                    .as_ref()
                    .unwrap_or(&card_token.token),
                payment_intent,
                card_token_data,
                merchant_key_store,
                storage_scheme,
                mandate_id,
                payment_method_info
                    .get_required_value("PaymentMethod")
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("PaymentMethod not found")?,
                business_profile,
                payment_attempt.connector.clone(),
                should_retry_with_pan,
                vault_data,
            )
            .await
            .map(|card| Some((card, enums::PaymentMethod::Card)))?
            .map(
                |(payment_method_data, payment_method)| storage::PaymentMethodDataWithId {
                    payment_method_data: Some(payment_method_data),
                    payment_method: Some(payment_method),
                    payment_method_id: Some(
                        card_token
                            .payment_method_id
                            .as_ref()
                            .unwrap_or(&card_token.token)
                            .to_string(),
                    ),
                },
            )
            .unwrap_or_default()
        }

        storage::PaymentTokenData::AuthBankDebit(auth_token) => {
            pm_auth::retrieve_payment_method_from_auth_service(
                state,
                merchant_key_store,
                auth_token,
                payment_intent,
                customer,
            )
            .await?
            .map(
                |(payment_method_data, payment_method)| storage::PaymentMethodDataWithId {
                    payment_method_data: Some(payment_method_data),
                    payment_method: Some(payment_method),
                    payment_method_id: None,
                },
            )
            .unwrap_or_default()
        }

        storage::PaymentTokenData::WalletToken(_) => storage::PaymentMethodDataWithId {
            payment_method: None,
            payment_method_data: None,
            payment_method_id: None,
        },
    };
    Ok(token)
}


File: crates/router/tests/connectors/celero.rs

use masking::Secret;
use router::types::{self, api, domain, storage::enums};
use test_utils::connector_auth;

use crate::utils::{self, ConnectorActions};

#[derive(Clone, Copy)]
struct CeleroTest;
impl ConnectorActions for CeleroTest {}
impl utils::Connector for CeleroTest {
    fn get_data(&self) -> api::ConnectorData {
        use router::connector::Celero;
        utils::construct_connector_data_old(
            Box::new(Celero::new()),
            types::Connector::Celero,
            api::GetToken::Connector,
            None,
        )
    }

    fn get_auth_token(&self) -> types::ConnectorAuthType {
        utils::to_connector_auth_type(
            connector_auth::ConnectorAuthentication::new()
                .celero
                .expect("Missing connector authentication configuration")
                .into(),
        )
    }

    fn get_name(&self) -> String {
        "celero".to_string()
    }
}

static CONNECTOR: CeleroTest = CeleroTest {};

fn get_default_payment_info() -> Option<utils::PaymentInfo> {
    None
}

fn payment_method_details() -> Option<types::PaymentsAuthorizeData> {
    None
}

// Cards Positive Tests
// Creates a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_only_authorize_payment() {
    let response = CONNECTOR
        .authorize_payment(payment_method_details(), get_default_payment_info())
        .await
        .expect("Authorize payment response");
    assert_eq!(response.status, enums::AttemptStatus::Authorized);
}

// Captures a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_capture_authorized_payment() {
    let response = CONNECTOR
        .authorize_and_capture_payment(payment_method_details(), None, get_default_payment_info())
        .await
        .expect("Capture payment response");
    assert_eq!(response.status, enums::AttemptStatus::Charged);
}

// Partially captures a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_partially_capture_authorized_payment() {
    let response = CONNECTOR
        .authorize_and_capture_payment(
            payment_method_details(),
            Some(types::PaymentsCaptureData {
                amount_to_capture: 50,
                ..utils::PaymentCaptureType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .expect("Capture payment response");
    assert_eq!(response.status, enums::AttemptStatus::Charged);
}

// Synchronizes a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_sync_authorized_payment() {
    let authorize_response = CONNECTOR
        .authorize_payment(payment_method_details(), get_default_payment_info())
        .await
        .expect("Authorize payment response");
    let txn_id = utils::get_connector_transaction_id(authorize_response.response);
    let response = CONNECTOR
        .psync_retry_till_status_matches(
            enums::AttemptStatus::Authorized,
            Some(types::PaymentsSyncData {
                connector_transaction_id: types::ResponseId::ConnectorTransactionId(
                    txn_id.unwrap(),
                ),
                ..Default::default()
            }),
            get_default_payment_info(),
        )
        .await
        .expect("PSync response");
    assert_eq!(response.status, enums::AttemptStatus::Authorized,);
}

// Voids a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_void_authorized_payment() {
    let response = CONNECTOR
        .authorize_and_void_payment(
            payment_method_details(),
            Some(types::PaymentsCancelData {
                connector_transaction_id: String::from(""),
                cancellation_reason: Some("requested_by_customer".to_string()),
                ..Default::default()
            }),
            get_default_payment_info(),
        )
        .await
        .expect("Void payment response");
    assert_eq!(response.status, enums::AttemptStatus::Voided);
}

// Refunds a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_refund_manually_captured_payment() {
    let response = CONNECTOR
        .capture_payment_and_refund(
            payment_method_details(),
            None,
            None,
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap().refund_status,
        enums::RefundStatus::Success,
    );
}

// Partially refunds a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_partially_refund_manually_captured_payment() {
    let response = CONNECTOR
        .capture_payment_and_refund(
            payment_method_details(),
            None,
            Some(types::RefundsData {
                refund_amount: 50,
                ..utils::PaymentRefundType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap().refund_status,
        enums::RefundStatus::Success,
    );
}

// Synchronizes a refund using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_sync_manually_captured_refund() {
    let refund_response = CONNECTOR
        .capture_payment_and_refund(
            payment_method_details(),
            None,
            None,
            get_default_payment_info(),
        )
        .await
        .unwrap();
    let response = CONNECTOR
        .rsync_retry_till_status_matches(
            enums::RefundStatus::Success,
            refund_response.response.unwrap().connector_refund_id,
            None,
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap().refund_status,
        enums::RefundStatus::Success,
    );
}

// Creates a payment using the automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_make_payment() {
    let authorize_response = CONNECTOR
        .make_payment(payment_method_details(), get_default_payment_info())
        .await
        .unwrap();
    assert_eq!(authorize_response.status, enums::AttemptStatus::Charged);
}

// Synchronizes a payment using the automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_sync_auto_captured_payment() {
    let authorize_response = CONNECTOR
        .make_payment(payment_method_details(), get_default_payment_info())
        .await
        .unwrap();
    assert_eq!(authorize_response.status, enums::AttemptStatus::Charged);
    let txn_id = utils::get_connector_transaction_id(authorize_response.response);
    assert_ne!(txn_id, None, "Empty connector transaction id");
    let response = CONNECTOR
        .psync_retry_till_status_matches(
            enums::AttemptStatus::Charged,
            Some(types::PaymentsSyncData {
                connector_transaction_id: types::ResponseId::ConnectorTransactionId(
                    txn_id.unwrap(),
                ),
                capture_method: Some(enums::CaptureMethod::Automatic),
                ..Default::default()
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(response.status, enums::AttemptStatus::Charged,);
}

// Refunds a payment using the automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_refund_auto_captured_payment() {
    let response = CONNECTOR
        .make_payment_and_refund(payment_method_details(), None, get_default_payment_info())
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap().refund_status,
        enums::RefundStatus::Success,
    );
}

// Partially refunds a payment using the automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_partially_refund_succeeded_payment() {
    let refund_response = CONNECTOR
        .make_payment_and_refund(
            payment_method_details(),
            Some(types::RefundsData {
                refund_amount: 50,
                ..utils::PaymentRefundType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        refund_response.response.unwrap().refund_status,
        enums::RefundStatus::Success,
    );
}

// Creates multiple refunds against a payment using the automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_refund_succeeded_payment_multiple_times() {
    CONNECTOR
        .make_payment_and_multiple_refund(
            payment_method_details(),
            Some(types::RefundsData {
                refund_amount: 50,
                ..utils::PaymentRefundType::default().0
            }),
            get_default_payment_info(),
        )
        .await;
}

// Synchronizes a refund using the automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_sync_refund() {
    let refund_response = CONNECTOR
        .make_payment_and_refund(payment_method_details(), None, get_default_payment_info())
        .await
        .unwrap();
    let response = CONNECTOR
        .rsync_retry_till_status_matches(
            enums::RefundStatus::Success,
            refund_response.response.unwrap().connector_refund_id,
            None,
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap().refund_status,
        enums::RefundStatus::Success,
    );
}

// Cards Negative scenarios
// Creates a payment with incorrect CVC.
#[actix_web::test]
async fn should_fail_payment_for_incorrect_cvc() {
    let response = CONNECTOR
        .make_payment(
            Some(types::PaymentsAuthorizeData {
                payment_method_data: domain::PaymentMethodData::Card(domain::Card {
                    card_cvc: Secret::new("12345".to_string()),
                    ..utils::CCardType::default().0
                }),
                ..utils::PaymentAuthorizeType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap_err().message,
        "Your card's security code is invalid.".to_string(),
    );
}

// Creates a payment with incorrect expiry month.
#[actix_web::test]
async fn should_fail_payment_for_invalid_exp_month() {
    let response = CONNECTOR
        .make_payment(
            Some(types::PaymentsAuthorizeData {
                payment_method_data: domain::PaymentMethodData::Card(domain::Card {
                    card_exp_month: Secret::new("20".to_string()),
                    ..utils::CCardType::default().0
                }),
                ..utils::PaymentAuthorizeType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap_err().message,
        "Your card's expiration month is invalid.".to_string(),
    );
}

// Creates a payment with incorrect expiry year.
#[actix_web::test]
async fn should_fail_payment_for_incorrect_expiry_year() {
    let response = CONNECTOR
        .make_payment(
            Some(types::PaymentsAuthorizeData {
                payment_method_data: domain::PaymentMethodData::Card(domain::Card {
                    card_exp_year: Secret::new("2000".to_string()),
                    ..utils::CCardType::default().0
                }),
                ..utils::PaymentAuthorizeType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap_err().message,
        "Your card's expiration year is invalid.".to_string(),
    );
}

// Voids a payment using automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_fail_void_payment_for_auto_capture() {
    let authorize_response = CONNECTOR
        .make_payment(payment_method_details(), get_default_payment_info())
        .await
        .unwrap();
    assert_eq!(authorize_response.status, enums::AttemptStatus::Charged);
    let txn_id = utils::get_connector_transaction_id(authorize_response.response);
    assert_ne!(txn_id, None, "Empty connector transaction id");
    let void_response = CONNECTOR
        .void_payment(txn_id.unwrap(), None, get_default_payment_info())
        .await
        .unwrap();
    assert_eq!(
        void_response.response.unwrap_err().message,
        "You cannot cancel this PaymentIntent because it has a status of succeeded."
    );
}

// Captures a payment using invalid connector payment id.
#[actix_web::test]
async fn should_fail_capture_for_invalid_payment() {
    let capture_response = CONNECTOR
        .capture_payment("123456789".to_string(), None, get_default_payment_info())
        .await
        .unwrap();
    assert_eq!(
        capture_response.response.unwrap_err().message,
        String::from("No such payment_intent: '123456789'")
    );
}

// Refunds a payment with refund amount higher than payment amount.
#[actix_web::test]
async fn should_fail_for_refund_amount_higher_than_payment_amount() {
    let response = CONNECTOR
        .make_payment_and_refund(
            payment_method_details(),
            Some(types::RefundsData {
                refund_amount: 150,
                ..utils::PaymentRefundType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap_err().message,
        "Refund amount (₹1.50) is greater than charge amount (₹1.00)",
    );
}

// Connector dependent test cases goes here

// [#478]: add unit tests for non 3DS, wallets & webhooks in connector tests