OpenAPI Block Path: components.schemas.PaymentsCreateRequest
{
  "type": "object",
  "required": [
    "amount",
    "currency"
  ],
  "properties": {
    "amount": {
      "type": "integer",
      "format": "int64",
      "description": "The primary amount for the payment, provided in the lowest denomination of the specified currency (e.g., 6540 for $65.40 USD). This field is mandatory for creating a payment.",
      "minimum": 0
    },
    "order_tax_amount": {
      "type": "integer",
      "format": "int64",
      "description": "Total tax amount applicable to the order, in the lowest denomination of the currency.",
      "example": 6540,
      "nullable": true
    },
    "currency": {
      "$ref": "#/components/schemas/Currency"
    },
    "amount_to_capture": {
      "type": "integer",
      "format": "int64",
      "description": "The amount to be captured from the user's payment method, in the lowest denomination. If not provided, and `capture_method` is `automatic`, the full payment `amount` will be captured. If `capture_method` is `manual`, this can be specified in the `/capture` call. Must be less than or equal to the authorized amount.",
      "example": 6540,
      "nullable": true
    },
    "shipping_cost": {
      "type": "integer",
      "format": "int64",
      "description": "The shipping cost for the payment. This is required for tax calculation in some regions.",
      "example": 6540,
      "nullable": true
    },
    "payment_id": {
      "type": "string",
      "description": "Optional. A merchant-provided unique identifier for the payment, contains 30 characters long (e.g., \"pay_mbabizu24mvu3mela5njyhpit4\"). If provided, it ensures idempotency for the payment creation request. If omitted, Hyperswitch generates a unique ID for the payment.",
      "example": "pay_mbabizu24mvu3mela5njyhpit4",
      "nullable": true,
      "maxLength": 30,
      "minLength": 30
    },
    "routing": {
      "allOf": [
        {
          "$ref": "#/components/schemas/StraightThroughAlgorithm"
        }
      ],
      "nullable": true
    },
    "connector": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/Connector"
      },
      "description": "This allows to manually select a connector with which the payment can go through.",
      "example": [
        "stripe",
        "adyen"
      ],
      "nullable": true
    },
    "capture_method": {
      "allOf": [
        {
          "$ref": "#/components/schemas/CaptureMethod"
        }
      ],
      "nullable": true
    },
    "authentication_type": {
      "allOf": [
        {
          "$ref": "#/components/schemas/AuthenticationType"
        }
      ],
      "default": "three_ds",
      "nullable": true
    },
    "billing": {
      "allOf": [
        {
          "$ref": "#/components/schemas/Address"
        }
      ],
      "nullable": true
    },
    "confirm": {
      "type": "boolean",
      "description": "If set to `true`, Hyperswitch attempts to confirm and authorize the payment immediately after creation, provided sufficient payment method details are included. If `false` or omitted (default is `false`), the payment is created with a status such as `requires_payment_method` or `requires_confirmation`, and a separate `POST /payments/{payment_id}/confirm` call is necessary to proceed with authorization.",
      "default": false,
      "example": true,
      "nullable": true
    },
    "customer": {
      "allOf": [
        {
          "$ref": "#/components/schemas/CustomerDetails"
        }
      ],
      "nullable": true
    },
    "customer_id": {
      "type": "string",
      "description": "The identifier for the customer",
      "example": "cus_y3oqhf46pyzuxjbcn2giaqnb44",
      "nullable": true,
      "maxLength": 64,
      "minLength": 1
    },
    "off_session": {
      "type": "boolean",
      "description": "Set to true to indicate that the customer is not in your checkout flow during this payment, and therefore is unable to authenticate. This parameter is intended for scenarios where you collect card details and charge them later. When making a recurring payment by passing a mandate_id, this parameter is mandatory",
      "example": true,
      "nullable": true
    },
    "description": {
      "type": "string",
      "description": "An arbitrary string attached to the payment. Often useful for displaying to users or for your own internal record-keeping.",
      "example": "It's my first payment request",
      "nullable": true
    },
    "return_url": {
      "type": "string",
      "description": "The URL to redirect the customer to after they complete the payment process or authentication. This is crucial for flows that involve off-site redirection (e.g., 3DS, some bank redirects, wallet payments).",
      "example": "https://hyperswitch.io",
      "nullable": true,
      "maxLength": 2048
    },
    "setup_future_usage": {
      "allOf": [
        {
          "$ref": "#/components/schemas/FutureUsage"
        }
      ],
      "nullable": true
    },
    "payment_method_data": {
      "allOf": [
        {
          "$ref": "#/components/schemas/PaymentMethodDataRequest"
        }
      ],
      "nullable": true
    },
    "payment_method": {
      "allOf": [
        {
          "$ref": "#/components/schemas/PaymentMethod"
        }
      ],
      "nullable": true
    },
    "payment_token": {
      "type": "string",
      "description": "As Hyperswitch tokenises the sensitive details about the payments method, it provides the payment_token as a reference to a stored payment method, ensuring that the sensitive details are not exposed in any manner.",
      "example": "187282ab-40ef-47a9-9206-5099ba31e432",
      "nullable": true
    },
    "shipping": {
      "allOf": [
        {
          "$ref": "#/components/schemas/Address"
        }
      ],
      "nullable": true
    },
    "statement_descriptor_name": {
      "type": "string",
      "description": "For non-card charges, you can use this value as the complete description that appears on your customers’ statements. Must contain at least one letter, maximum 22 characters.",
      "example": "Hyperswitch Router",
      "nullable": true,
      "maxLength": 255
    },
    "statement_descriptor_suffix": {
      "type": "string",
      "description": "Provides information about a card payment that customers see on their statements. Concatenated with the prefix (shortened descriptor) or statement descriptor that’s set on the account to form the complete statement descriptor. Maximum 22 characters for the concatenated descriptor.",
      "example": "Payment for shoes purchase",
      "nullable": true,
      "maxLength": 255
    },
    "order_details": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/OrderDetailsWithAmount"
      },
      "description": "Use this object to capture the details about the different products for which the payment is being made. The sum of amount across different products here should be equal to the overall payment amount",
      "example": "[{\n        \"product_name\": \"Apple iPhone 16\",\n        \"quantity\": 1,\n        \"amount\" : 69000\n        \"product_img_link\" : \"https://dummy-img-link.com\"\n    }]",
      "nullable": true
    },
    "mandate_data": {
      "allOf": [
        {
          "$ref": "#/components/schemas/MandateData"
        }
      ],
      "nullable": true
    },
    "customer_acceptance": {
      "allOf": [
        {
          "$ref": "#/components/schemas/CustomerAcceptance"
        }
      ],
      "nullable": true
    },
    "mandate_id": {
      "type": "string",
      "description": "A unique identifier to link the payment to a mandate. To do Recurring payments after a mandate has been created, pass the mandate_id instead of payment_method_data",
      "example": "mandate_iwer89rnjef349dni3",
      "nullable": true,
      "maxLength": 64
    },
    "browser_info": {
      "allOf": [
        {
          "$ref": "#/components/schemas/BrowserInformation"
        }
      ],
      "nullable": true
    },
    "payment_experience": {
      "allOf": [
        {
          "$ref": "#/components/schemas/PaymentExperience"
        }
      ],
      "nullable": true
    },
    "payment_method_type": {
      "allOf": [
        {
          "$ref": "#/components/schemas/PaymentMethodType"
        }
      ],
      "nullable": true
    },
    "business_country": {
      "allOf": [
        {
          "$ref": "#/components/schemas/CountryAlpha2"
        }
      ],
      "nullable": true
    },
    "business_label": {
      "type": "string",
      "description": "Business label of the merchant for this payment.\nTo be deprecated soon. Pass the profile_id instead",
      "example": "food",
      "nullable": true
    },
    "merchant_connector_details": {
      "allOf": [
        {
          "$ref": "#/components/schemas/MerchantConnectorDetailsWrap"
        }
      ],
      "nullable": true
    },
    "allowed_payment_method_types": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/PaymentMethodType"
      },
      "description": "Use this parameter to restrict the Payment Method Types to show for a given PaymentIntent",
      "nullable": true
    },
    "metadata": {
      "type": "object",
      "description": "You can specify up to 50 keys, with key names up to 40 characters long and values up to 500 characters long. Metadata is useful for storing additional, structured information on an object.",
      "nullable": true
    },
    "connector_metadata": {
      "allOf": [
        {
          "$ref": "#/components/schemas/ConnectorMetadata"
        }
      ],
      "nullable": true
    },
    "payment_link": {
      "type": "boolean",
      "description": "Whether to generate the payment link for this payment or not (if applicable)",
      "default": false,
      "example": true,
      "nullable": true
    },
    "payment_link_config": {
      "allOf": [
        {
          "$ref": "#/components/schemas/PaymentCreatePaymentLinkConfig"
        }
      ],
      "nullable": true
    },
    "payment_link_config_id": {
      "type": "string",
      "description": "Custom payment link config id set at business profile, send only if business_specific_configs is configured",
      "nullable": true
    },
    "profile_id": {
      "type": "string",
      "description": "The business profile to be used for this payment, if not passed the default business profile associated with the merchant account will be used. It is mandatory in case multiple business profiles have been set up.",
      "nullable": true
    },
    "surcharge_details": {
      "allOf": [
        {
          "$ref": "#/components/schemas/RequestSurchargeDetails"
        }
      ],
      "nullable": true
    },
    "payment_type": {
      "allOf": [
        {
          "$ref": "#/components/schemas/PaymentType"
        }
      ],
      "nullable": true
    },
    "request_incremental_authorization": {
      "type": "boolean",
      "description": "Request an incremental authorization, i.e., increase the authorized amount on a confirmed payment before you capture it.",
      "nullable": true
    },
    "session_expiry": {
      "type": "integer",
      "format": "int32",
      "description": "Will be used to expire client secret after certain amount of time to be supplied in seconds\n(900) for 15 mins",
      "example": 900,
      "nullable": true,
      "minimum": 0
    },
    "frm_metadata": {
      "type": "object",
      "description": "Additional data related to some frm(Fraud Risk Management) connectors",
      "nullable": true
    },
    "request_external_three_ds_authentication": {
      "type": "boolean",
      "description": "Whether to perform external authentication (if applicable)",
      "example": true,
      "nullable": true
    },
    "recurring_details": {
      "allOf": [
        {
          "$ref": "#/components/schemas/RecurringDetails"
        }
      ],
      "nullable": true
    },
    "split_payments": {
      "allOf": [
        {
          "$ref": "#/components/schemas/SplitPaymentsRequest"
        }
      ],
      "nullable": true
    },
    "request_extended_authorization": {
      "type": "boolean",
      "description": "Optional boolean value to extent authorization period of this payment\n\ncapture method must be manual or manual_multiple",
      "default": false,
      "nullable": true
    },
    "merchant_order_reference_id": {
      "type": "string",
      "description": "Your unique identifier for this payment or order. This ID helps you reconcile payments on your system. If provided, it is passed to the connector if supported.",
      "example": "Custom_Order_id_123",
      "nullable": true,
      "maxLength": 255
    },
    "skip_external_tax_calculation": {
      "type": "boolean",
      "description": "Whether to calculate tax for this payment intent",
      "nullable": true
    },
    "psd2_sca_exemption_type": {
      "allOf": [
        {
          "$ref": "#/components/schemas/ScaExemptionType"
        }
      ],
      "nullable": true
    },
    "ctp_service_details": {
      "allOf": [
        {
          "$ref": "#/components/schemas/CtpServiceDetails"
        }
      ],
      "nullable": true
    },
    "force_3ds_challenge": {
      "type": "boolean",
      "description": "Indicates if 3ds challenge is forced",
      "nullable": true
    },
    "threeds_method_comp_ind": {
      "allOf": [
        {
          "$ref": "#/components/schemas/ThreeDsCompletionIndicator"
        }
      ],
      "nullable": true
    },
    "is_iframe_redirection_enabled": {
      "type": "boolean",
      "description": "Indicates if the redirection has to open in the iframe",
      "nullable": true
    },
    "all_keys_required": {
      "type": "boolean",
      "description": "If enabled, provides whole connector response",
      "nullable": true
    },
    "payment_channel": {
      "allOf": [
        {
          "$ref": "#/components/schemas/PaymentChannel"
        }
      ],
      "nullable": true
    },
    "tax_status": {
      "allOf": [
        {
          "$ref": "#/components/schemas/TaxStatus"
        }
      ],
      "nullable": true
    },
    "discount_amount": {
      "type": "integer",
      "format": "int64",
      "description": "Total amount of the discount you have applied to the order or transaction.",
      "example": 6540,
      "nullable": true
    },
    "shipping_amount_tax": {
      "allOf": [
        {
          "$ref": "#/components/schemas/MinorUnit"
        }
      ],
      "nullable": true
    },
    "duty_amount": {
      "allOf": [
        {
          "$ref": "#/components/schemas/MinorUnit"
        }
      ],
      "nullable": true
    },
    "order_date": {
      "type": "string",
      "format": "date-time",
      "description": "Date the payer placed the order.",
      "nullable": true
    },
    "enable_partial_authorization": {
      "type": "boolean",
      "description": "Allow partial authorization for this payment",
      "nullable": true
    }
  }
}


impl ApiEventMetric for FrmFiltersResponse {}

impl ApiEventMetric for for FrmFiltersResponse


impl api::RefundSync for Iatapay {}


File: crates/hyperswitch_domain_models/src/behaviour.rs

use common_utils::{
    errors::{CustomResult, ValidationError},
    types::keymanager::{Identifier, KeyManagerState},
};
use masking::Secret;

/// Trait for converting domain types to storage models
#[async_trait::async_trait]
pub trait Conversion {
    type DstType;
    type NewDstType;
    async fn convert(self) -> CustomResult<Self::DstType, ValidationError>;

    async fn convert_back(
        state: &KeyManagerState,
        item: Self::DstType,
        key: &Secret<Vec<u8>>,
        key_manager_identifier: Identifier,
    ) -> CustomResult<Self, ValidationError>
    where
        Self: Sized;

    async fn construct_new(self) -> CustomResult<Self::NewDstType, ValidationError>;
}

#[async_trait::async_trait]
pub trait ReverseConversion<SrcType: Conversion> {
    async fn convert(
        self,
        state: &KeyManagerState,
        key: &Secret<Vec<u8>>,
        key_manager_identifier: Identifier,
    ) -> CustomResult<SrcType, ValidationError>;
}

#[async_trait::async_trait]
impl<T: Send, U: Conversion<DstType = T>> ReverseConversion<U> for T {
    async fn convert(
        self,
        state: &KeyManagerState,
        key: &Secret<Vec<u8>>,
        key_manager_identifier: Identifier,
    ) -> CustomResult<U, ValidationError> {
        U::convert_back(state, self, key, key_manager_identifier).await
    }
}


pub async fn refresh_cgraph_cache(
    state: &SessionState,
    key_store: &domain::MerchantKeyStore,
    key: String,
    profile_id: &common_utils::id_type::ProfileId,
    transaction_type: &api_enums::TransactionType,
) -> RoutingResult<Arc<hyperswitch_constraint_graph::ConstraintGraph<euclid_dir::DirValue>>> {
    let mut merchant_connector_accounts = state
        .store
        .find_merchant_connector_account_by_merchant_id_and_disabled_list(
            &state.into(),
            &key_store.merchant_id,
            false,
            key_store,
        )
        .await
        .change_context(errors::RoutingError::KgraphCacheRefreshFailed)?;

    match transaction_type {
        api_enums::TransactionType::Payment => {
            merchant_connector_accounts.retain(|mca| {
                mca.connector_type != storage_enums::ConnectorType::PaymentVas
                    && mca.connector_type != storage_enums::ConnectorType::PaymentMethodAuth
                    && mca.connector_type != storage_enums::ConnectorType::PayoutProcessor
                    && mca.connector_type != storage_enums::ConnectorType::AuthenticationProcessor
            });
        }
        #[cfg(feature = "payouts")]
        api_enums::TransactionType::Payout => {
            merchant_connector_accounts
                .retain(|mca| mca.connector_type == storage_enums::ConnectorType::PayoutProcessor);
        }
        api_enums::TransactionType::ThreeDsAuthentication => {
            Err(errors::RoutingError::InvalidTransactionType)?
        }
    };

    let connector_type = match transaction_type {
        api_enums::TransactionType::Payment => common_enums::ConnectorType::PaymentProcessor,
        #[cfg(feature = "payouts")]
        api_enums::TransactionType::Payout => common_enums::ConnectorType::PayoutProcessor,
        api_enums::TransactionType::ThreeDsAuthentication => {
            Err(errors::RoutingError::InvalidTransactionType)?
        }
    };

    let merchant_connector_accounts = merchant_connector_accounts
        .filter_based_on_profile_and_connector_type(profile_id, connector_type);

    let api_mcas = merchant_connector_accounts
        .into_iter()
        .map(admin_api::MerchantConnectorResponse::foreign_try_from)
        .collect::<Result<Vec<_>, _>>()
        .change_context(errors::RoutingError::KgraphCacheRefreshFailed)?;
    let connector_configs = state
        .conf
        .pm_filters
        .0
        .clone()
        .into_iter()
        .filter(|(key, _)| key != "default")
        .map(|(key, value)| {
            let key = api_enums::RoutableConnectors::from_str(&key)
                .map_err(|_| errors::RoutingError::InvalidConnectorName(key))?;

            Ok((key, value.foreign_into()))
        })
        .collect::<Result<HashMap<_, _>, errors::RoutingError>>()?;
    let default_configs = state
        .conf
        .pm_filters
        .0
        .get("default")
        .cloned()
        .map(ForeignFrom::foreign_from);
    let config_pm_filters = CountryCurrencyFilter {
        connector_configs,
        default_configs,
    };
    let cgraph = Arc::new(
        mca_graph::make_mca_graph(api_mcas, &config_pm_filters)
            .change_context(errors::RoutingError::KgraphCacheRefreshFailed)
            .attach_printable("when construction cgraph")?,
    );

    CGRAPH_CACHE
        .push(
            CacheKey {
                key,
                prefix: state.tenant.redis_key_prefix.clone(),
            },
            Arc::clone(&cgraph),
        )
        .await;

    Ok(cgraph)
}


pub struct DummyConnector;


File: crates/hyperswitch_domain_models/src/payment_address.rs
 Public functions: 7
 Public structs: 1

use crate::address::Address;

#[derive(Clone, Default, Debug)]
pub struct PaymentAddress {
    shipping: Option<Address>,
    billing: Option<Address>,
    unified_payment_method_billing: Option<Address>,
    payment_method_billing: Option<Address>,
}

impl PaymentAddress {
    pub fn new(
        shipping: Option<Address>,
        billing: Option<Address>,
        payment_method_billing: Option<Address>,
        should_unify_address: Option<bool>,
    ) -> Self {
        // billing -> .billing, this is the billing details passed in the root of payments request
        // payment_method_billing -> .payment_method_data.billing

        let unified_payment_method_billing = if should_unify_address.unwrap_or(true) {
            // Merge the billing details field from both `payment.billing` and `payment.payment_method_data.billing`
            // The unified payment_method_billing will be used as billing address and passed to the connector module
            // This unification is required in order to provide backwards compatibility
            // so that if `payment.billing` is passed it should be sent to the connector module
            // Unify the billing details with `payment_method_data.billing`
            payment_method_billing
                .as_ref()
                .map(|payment_method_billing| {
                    payment_method_billing
                        .clone()
                        .unify_address(billing.as_ref())
                })
                .or(billing.clone())
        } else {
            payment_method_billing.clone()
        };

        Self {
            shipping,
            billing,
            unified_payment_method_billing,
            payment_method_billing,
        }
    }

    pub fn get_shipping(&self) -> Option<&Address> {
        self.shipping.as_ref()
    }

    pub fn get_payment_method_billing(&self) -> Option<&Address> {
        self.unified_payment_method_billing.as_ref()
    }

    /// Unify the billing details from `payment_method_data.[payment_method_data].billing details`.
    /// Here the fields passed in payment_method_data_billing takes precedence
    pub fn unify_with_payment_method_data_billing(
        self,
        payment_method_data_billing: Option<Address>,
    ) -> Self {
        // Unify the billing details with `payment_method_data.billing_details`
        let unified_payment_method_billing = payment_method_data_billing
            .map(|payment_method_data_billing| {
                payment_method_data_billing.unify_address(self.get_payment_method_billing())
            })
            .or(self.get_payment_method_billing().cloned());

        Self {
            shipping: self.shipping,
            billing: self.billing,
            unified_payment_method_billing,
            payment_method_billing: self.payment_method_billing,
        }
    }

    /// Unify the billing details from `payment_method_data.[payment_method_data].billing details`.
    /// Here the `self` takes precedence
    pub fn unify_with_payment_data_billing(
        self,
        other_payment_method_billing: Option<Address>,
    ) -> Self {
        let unified_payment_method_billing = self
            .get_payment_method_billing()
            .map(|payment_method_billing| {
                payment_method_billing
                    .clone()
                    .unify_address(other_payment_method_billing.as_ref())
            })
            .or(other_payment_method_billing);

        Self {
            shipping: self.shipping,
            billing: self.billing,
            unified_payment_method_billing,
            payment_method_billing: self.payment_method_billing,
        }
    }

    pub fn get_request_payment_method_billing(&self) -> Option<&Address> {
        self.payment_method_billing.as_ref()
    }

    pub fn get_payment_billing(&self) -> Option<&Address> {
        self.billing.as_ref()
    }
}


impl Responder {
    let api_auth = auth::ApiKeyAuth {
        is_connected_allowed: false,
        is_platform_allowed: true,
    };

    let (auth_type, _auth_flow) = match auth::get_auth_type_and_flow(req.headers(), api_auth) {
        Ok(auth) => auth,
        Err(err) => return api::log_and_return_error_response(report!(err)),
    };

    tracing::Span::current().record("payment_id", json_payload.payment_id.get_string_repr());

    let payload = payment_types::PaymentsRetrieveRequest {
        resource_id: payment_types::PaymentIdType::PaymentIntentId(json_payload.payment_id.clone()),
        merchant_id: json_payload.merchant_id.clone(),
        force_sync: json_payload.force_sync.unwrap_or(false),
        merchant_connector_details: json_payload.merchant_connector_details.clone(),
        ..Default::default()
    };

    let flow = match json_payload.force_sync {
        Some(true) => Flow::PaymentsRetrieveForceSync,
        _ => Flow::PaymentsRetrieve,
    };

    tracing::Span::current().record("flow", flow.to_string());

    let locking_action = payload.get_locking_input(flow.clone());

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        payload,
        |state, auth: auth::AuthenticationData, req, req_state| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            payments::payments_core::<
                api_types::PSync,
                payment_types::PaymentsResponse,
                _,
                _,
                _,
                payments::PaymentData<api_types::PSync>,
            >(
                state,
                req_state,
                merchant_context,
                auth.profile_id,
                payments::PaymentStatus,
                req,
                api::AuthFlow::Merchant,
                payments::CallConnectorAction::Trigger,
                None,
                HeaderPayload::default(),
            )
        },
        &*auth_type,
        locking_action,
    ))
    .await
}


                    bank_code: None,           // Bank code is not stored in external vault
                }),
            ))
        }
        payment_methods::PaymentMethodsData::BankDetails(_)
        | payment_methods::PaymentMethodsData::WalletDetails(_) => {
            Err(errors::ApiErrorResponse::UnprocessableEntity {
                message: "External vaulting is not supported for this payment method type"
                    .to_string(),
            }
            .into())
        }
    }
}

#[cfg(feature = "v2")]
/// Update the connector_mandate_details of the payment method with
/// new token details for the payment
fn create_connector_token_details_update(
    token_details: payment_methods::ConnectorTokenDetails,
    payment_method: &domain::PaymentMethod,
) -> hyperswitch_domain_models::mandates::CommonMandateReference {
    let connector_id = token_details.connector_id.clone();

    let reference_record =
        hyperswitch_domain_models::mandates::ConnectorTokenReferenceRecord::foreign_from(
            token_details,
        );

    let connector_token_details = payment_method.connector_mandate_details.clone();

    match connector_token_details {
        Some(mut connector_mandate_reference) => {
            connector_mandate_reference
                .insert_payment_token_reference_record(&connector_id, reference_record);

            connector_mandate_reference
        }
        None => {
            let reference_record_hash_map =
                std::collections::HashMap::from([(connector_id, reference_record)]);
            let payments_mandate_reference =
                hyperswitch_domain_models::mandates::PaymentsTokenReference(
                    reference_record_hash_map,
                );
            hyperswitch_domain_models::mandates::CommonMandateReference {
                payments: Some(payments_mandate_reference),
                payouts: None,
            }
        }
    }
}

#[cfg(feature = "v2")]
#[allow(clippy::too_many_arguments)]
pub async fn create_pm_additional_data_update(
    pmd: Option<&domain::PaymentMethodVaultingData>,
    state: &SessionState,
    key_store: &domain::MerchantKeyStore,
    vault_id: Option<String>,
    vault_fingerprint_id: Option<String>,
    payment_method: &domain::PaymentMethod,
    connector_token_details: Option<payment_methods::ConnectorTokenDetails>,
    nt_data: Option<NetworkTokenPaymentMethodDetails>,
    payment_method_type: Option<common_enums::PaymentMethod>,
    payment_method_subtype: Option<common_enums::PaymentMethodType>,
    external_vault_source: Option<id_type::MerchantConnectorAccountId>,
) -> RouterResult<storage::PaymentMethodUpdate> {
    let encrypted_payment_method_data = pmd
        .map(
            |payment_method_vaulting_data| match payment_method_vaulting_data {
                domain::PaymentMethodVaultingData::Card(card) => {
                    payment_method_data::PaymentMethodsData::Card(
                        payment_method_data::CardDetailsPaymentMethod::from(card.clone()),
                    )
                }
                domain::PaymentMethodVaultingData::NetworkToken(network_token) => {
                    payment_method_data::PaymentMethodsData::NetworkToken(
                        payment_method_data::NetworkTokenDetailsPaymentMethod::from(
                            network_token.clone(),
                        ),
                    )
                }
            },
        )
        .async_map(|payment_method_details| async {
            let key_manager_state = &(state).into();

            cards::create_encrypted_data(key_manager_state, key_store, payment_method_details)
                .await
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Unable to encrypt Payment method data")
        })
        .await
        .transpose()?
        .map(From::from);

    let connector_mandate_details_update = connector_token_details
        .map(|connector_token| {
            create_connector_token_details_update(connector_token, payment_method)
        })
        .map(From::from);

    let pm_update = storage::PaymentMethodUpdate::GenericUpdate {
        // A new payment method is created with inactive state
        // It will be marked active after payment succeeds
        status: Some(enums::PaymentMethodStatus::Inactive),
        locker_id: vault_id,
        payment_method_type_v2: payment_method_type,
        payment_method_subtype,
        payment_method_data: encrypted_payment_method_data,
        network_token_requestor_reference_id: nt_data
            .clone()
            .map(|data| data.network_token_requestor_reference_id),
        network_token_locker_id: nt_data.clone().map(|data| data.network_token_locker_id),
        network_token_payment_method_data: nt_data.map(|data| data.network_token_pmd.into()),
        connector_mandate_details: connector_mandate_details_update,
        locker_fingerprint_id: vault_fingerprint_id,
        external_vault_source,
    };

    Ok(pm_update)
}

#[cfg(feature = "v2")]
#[instrument(skip_all)]
pub async fn vault_payment_method_internal(
    state: &SessionState,
    pmd: &domain::PaymentMethodVaultingData,
    merchant_context: &domain::MerchantContext,
    existing_vault_id: Option<domain::VaultId>,
    customer_id: &id_type::GlobalCustomerId,
) -> RouterResult<pm_types::AddVaultResponse> {
    let db = &*state.store;

    // get fingerprint_id from vault
    let fingerprint_id_from_vault =
        vault::get_fingerprint_id_from_vault(state, pmd, customer_id.get_string_repr().to_owned())
            .await
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Failed to get fingerprint_id from vault")?;

    // throw back error if payment method is duplicated
    when(
        db.find_payment_method_by_fingerprint_id(
            &(state.into()),
            merchant_context.get_merchant_key_store(),
            &fingerprint_id_from_vault,
        )
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to find payment method by fingerprint_id")
        .inspect_err(|e| logger::error!("Vault Fingerprint_id error: {:?}", e))
        .is_ok(),
        || {
            Err(report!(errors::ApiErrorResponse::DuplicatePaymentMethod)
                .attach_printable("Cannot vault duplicate payment method"))
        },
    )?;

    let mut resp_from_vault = vault::add_payment_method_to_vault(
        state,
        merchant_context,
        pmd,
        existing_vault_id,
        customer_id,
    )
    .await
    .change_context(errors::ApiErrorResponse::InternalServerError)
    .attach_printable("Failed to add payment method in vault")?;

    // add fingerprint_id to the response
    resp_from_vault.fingerprint_id = Some(fingerprint_id_from_vault);

    Ok(resp_from_vault)
}

#[cfg(feature = "v2")]
#[instrument(skip_all)]
pub async fn vault_payment_method_external(
    state: &SessionState,
    pmd: &domain::PaymentMethodVaultingData,
    merchant_account: &domain::MerchantAccount,
    merchant_connector_account: domain::MerchantConnectorAccountTypeDetails,
) -> RouterResult<pm_types::AddVaultResponse> {
    let router_data = core_utils::construct_vault_router_data(
        state,
        merchant_account,
        &merchant_connector_account,
        Some(pmd.clone()),
        None,
        None,
    )
    .await?;

    let mut old_router_data = VaultConnectorFlowData::to_old_router_data(router_data)
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable(
            "Cannot construct router data for making the external vault insert api call",
        )?;

    let connector_name = merchant_connector_account
        .get_connector_name()
        .ok_or(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Connector name not present for external vault")?; // always get the connector name from this call

    let connector_data = api::ConnectorData::get_external_vault_connector_by_name(
        &state.conf.connectors,
        &connector_name,
        api::GetToken::Connector,
        merchant_connector_account.get_mca_id(),
    )
    .change_context(errors::ApiErrorResponse::InternalServerError)
    .attach_printable("Failed to get the connector data")?;

    let connector_integration: services::BoxedVaultConnectorIntegrationInterface<
        ExternalVaultInsertFlow,
        types::VaultRequestData,
        types::VaultResponseData,
    > = connector_data.connector.get_connector_integration();

    let router_data_resp = services::execute_connector_processing_step(
        state,
        connector_integration,
        &old_router_data,
        payments_core::CallConnectorAction::Trigger,
        None,
        None,
    )
    .await
    .to_vault_failed_response()?;

    get_vault_response_for_insert_payment_method_data(router_data_resp)
}

#[cfg(feature = "v2")]
pub fn get_vault_response_for_insert_payment_method_data<F>(
    router_data: VaultRouterData<F>,
) -> RouterResult<pm_types::AddVaultResponse> {
    match router_data.response {
        Ok(response) => match response {
            types::VaultResponseData::ExternalVaultInsertResponse {
                connector_vault_id,
                fingerprint_id,
            } => Ok(pm_types::AddVaultResponse {
                vault_id: domain::VaultId::generate(connector_vault_id),
                fingerprint_id: Some(fingerprint_id),
                entity_id: None,
            }),
            types::VaultResponseData::ExternalVaultRetrieveResponse { .. }
            | types::VaultResponseData::ExternalVaultDeleteResponse { .. }
            | types::VaultResponseData::ExternalVaultCreateResponse { .. } => {
                Err(report!(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("Invalid Vault Response"))
            }
        },
        Err(err) => {
            logger::error!("Error vaulting payment method: {:?}", err);
            Err(report!(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Failed to vault payment method"))
        }
    }
}

#[cfg(feature = "v2")]
#[instrument(skip_all)]
pub async fn vault_payment_method(
    state: &SessionState,
    pmd: &domain::PaymentMethodVaultingData,
    merchant_context: &domain::MerchantContext,
    profile: &domain::Profile,
    existing_vault_id: Option<domain::VaultId>,
    customer_id: &id_type::GlobalCustomerId,
) -> RouterResult<(
    pm_types::AddVaultResponse,
    Option<id_type::MerchantConnectorAccountId>,
)> {
    let is_external_vault_enabled = profile.is_external_vault_enabled();

    match is_external_vault_enabled {
        true => {
            let external_vault_source: id_type::MerchantConnectorAccountId = profile
                .external_vault_connector_details
                .clone()
                .map(|connector_details| connector_details.vault_connector_id.clone())
                .ok_or(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("mca_id not present for external vault")?;

            let merchant_connector_account =
                domain::MerchantConnectorAccountTypeDetails::MerchantConnectorAccount(Box::new(
                    payments_core::helpers::get_merchant_connector_account_v2(
                        state,
                        merchant_context.get_merchant_key_store(),
                        Some(&external_vault_source),
                    )
                    .await
                    .attach_printable(
                        "failed to fetch merchant connector account for external vault insert",
                    )?,
                ));

            vault_payment_method_external(
                state,
                pmd,
                merchant_context.get_merchant_account(),
                merchant_connector_account,
            )
            .await
            .map(|value| (value, Some(external_vault_source)))
        }
        false => vault_payment_method_internal(
            state,
            pmd,
            merchant_context,
            existing_vault_id,
            customer_id,
        )
        .await
        .map(|value| (value, None)),
    }
}

#[cfg(feature = "v2")]
fn get_pm_list_context(
    payment_method_type: enums::PaymentMethod,
    payment_method: &domain::PaymentMethod,
    is_payment_associated: bool,
) -> Result<Option<PaymentMethodListContext>, error_stack::Report<errors::ApiErrorResponse>> {
    let payment_method_data = payment_method
        .payment_method_data
        .clone()
        .map(|payment_method_data| payment_method_data.into_inner());

    let payment_method_retrieval_context = match payment_method_data {
        Some(payment_methods::PaymentMethodsData::Card(card)) => {
            Some(PaymentMethodListContext::Card {
                card_details: api::CardDetailFromLocker::from(card),
                token_data: is_payment_associated.then_some(
                    storage::PaymentTokenData::permanent_card(
                        payment_method.get_id().clone(),
                        payment_method
                            .locker_id
                            .as_ref()
                            .map(|id| id.get_string_repr().to_owned())
                            .or_else(|| Some(payment_method.get_id().get_string_repr().to_owned())),
                        payment_method
                            .locker_id
                            .as_ref()
                            .map(|id| id.get_string_repr().to_owned())
                            .unwrap_or_else(|| {
                                payment_method.get_id().get_string_repr().to_owned()
                            }),
                    ),
                ),
            })
        }
        Some(payment_methods::PaymentMethodsData::BankDetails(bank_details)) => {
            let get_bank_account_token_data =
                || -> CustomResult<payment_methods::BankAccountTokenData,errors::ApiErrorResponse> {
                    let connector_details = bank_details
                        .connector_details
                        .first()
                        .cloned()
                        .ok_or(errors::ApiErrorResponse::InternalServerError)
                        .attach_printable("Failed to obtain bank account connector details")?;

                    let payment_method_subtype = payment_method
                        .get_payment_method_subtype()
                        .get_required_value("payment_method_subtype")
                        .attach_printable("PaymentMethodType not found")?;

                    Ok(payment_methods::BankAccountTokenData {
                        payment_method_type: payment_method_subtype,
                        payment_method: payment_method_type,
                        connector_details,
                    })
                };

            // Retrieve the pm_auth connector details so that it can be tokenized
            let bank_account_token_data = get_bank_account_token_data()
                .inspect_err(|error| logger::error!(?error))
                .ok();
            bank_account_token_data.map(|data| {
                let token_data = storage::PaymentTokenData::AuthBankDebit(data);

                PaymentMethodListContext::Bank {
                    token_data: is_payment_associated.then_some(token_data),
                }
            })
        }
        Some(payment_methods::PaymentMethodsData::WalletDetails(_)) | None => {
            Some(PaymentMethodListContext::TemporaryToken {
                token_data: is_payment_associated.then_some(
                    storage::PaymentTokenData::temporary_generic(generate_id(
                        consts::ID_LENGTH,
                        "token",
                    )),
                ),
            })
        }
    };

    Ok(payment_method_retrieval_context)
}

#[cfg(feature = "v2")]
fn get_pm_list_token_data(
    payment_method_type: enums::PaymentMethod,
    payment_method: &domain::PaymentMethod,
) -> Result<Option<storage::PaymentTokenData>, error_stack::Report<errors::ApiErrorResponse>> {
    let pm_list_context = get_pm_list_context(payment_method_type, payment_method, true)?
        .get_required_value("PaymentMethodListContext")?;

    match pm_list_context {
        PaymentMethodListContext::Card {
            card_details: _,
            token_data,
        } => Ok(token_data),
        PaymentMethodListContext::Bank { token_data } => Ok(token_data),
        PaymentMethodListContext::BankTransfer {
            bank_transfer_details: _,
            token_data,
        } => Ok(token_data),
        PaymentMethodListContext::TemporaryToken { token_data } => Ok(token_data),
    }
}

#[cfg(all(feature = "v2", feature = "olap"))]
pub async fn list_payment_methods_core(
    state: &SessionState,
    merchant_context: &domain::MerchantContext,
    customer_id: &id_type::GlobalCustomerId,
) -> RouterResult<payment_methods::CustomerPaymentMethodsListResponse> {
    let db = &*state.store;
    let key_manager_state = &(state).into();

    let saved_payment_methods = db
        .find_payment_method_by_global_customer_id_merchant_id_status(
            key_manager_state,
            merchant_context.get_merchant_key_store(),
            customer_id,
            merchant_context.get_merchant_account().get_id(),
            common_enums::PaymentMethodStatus::Active,
            None,
            merchant_context.get_merchant_account().storage_scheme,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::PaymentMethodNotFound)?;

    let customer_payment_methods = saved_payment_methods
        .into_iter()
        .map(ForeignTryFrom::foreign_try_from)
        .collect::<Result<Vec<payment_methods::PaymentMethodResponseItem>, _>>()
        .change_context(errors::ApiErrorResponse::InternalServerError)?;

    let response = payment_methods::CustomerPaymentMethodsListResponse {
        customer_payment_methods,
    };

    Ok(response)
}

#[cfg(all(feature = "v2", feature = "oltp"))]
pub async fn list_customer_payment_methods_core(
    state: &SessionState,
    merchant_context: &domain::MerchantContext,
    customer_id: &id_type::GlobalCustomerId,
) -> RouterResult<Vec<payment_methods::CustomerPaymentMethodResponseItem>> {
    let db = &*state.store;
    let key_manager_state = &(state).into();

    let saved_payment_methods = db
        .find_payment_method_by_global_customer_id_merchant_id_status(
            key_manager_state,
            merchant_context.get_merchant_key_store(),
            customer_id,
            merchant_context.get_merchant_account().get_id(),
            common_enums::PaymentMethodStatus::Active,
            None,
            merchant_context.get_merchant_account().storage_scheme,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::PaymentMethodNotFound)?;

    let mut customer_payment_methods = Vec::new();

    let payment_method_results: Result<Vec<_>, error_stack::Report<errors::ApiErrorResponse>> =
        saved_payment_methods
            .into_iter()
            .map(|pm| async move {
                let parent_payment_method_token = generate_id(consts::ID_LENGTH, "token");

                // For payment methods that are active we should always have the payment method type
                let payment_method_type = pm
                    .payment_method_type
                    .get_required_value("payment_method_type")?;

                let intent_fulfillment_time = common_utils::consts::DEFAULT_INTENT_FULFILLMENT_TIME;

                let token_data = get_pm_list_token_data(payment_method_type, &pm)?;

                if let Some(token_data) = token_data {
                    pm_routes::ParentPaymentMethodToken::create_key_for_token((
                        &parent_payment_method_token,
                        payment_method_type,
                    ))
                    .insert(intent_fulfillment_time, token_data, state)
                    .await?;

                    let final_pm = api::CustomerPaymentMethodResponseItem::foreign_try_from((
                        pm,
                        parent_payment_method_token,
                    ))
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("Failed to convert payment method to response format")?;

                    Ok(Some(final_pm))
                } else {
                    Ok(None)
                }
            })
            .collect::<futures::stream::FuturesUnordered<_>>()
            .try_collect::<Vec<_>>()
            .await;

    customer_payment_methods.extend(payment_method_results?.into_iter().flatten());

    Ok(customer_payment_methods)
}

#[cfg(all(feature = "v2", feature = "olap"))]
pub async fn get_total_payment_method_count_core(
    state: &SessionState,
    merchant_context: &domain::MerchantContext,
) -> RouterResult<api::TotalPaymentMethodCountResponse> {
    let db = &*state.store;

    let total_count = db
        .get_payment_method_count_by_merchant_id_status(
            merchant_context.get_merchant_account().get_id(),
            common_enums::PaymentMethodStatus::Active,
        )
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Unable to get total payment method count")?;

    let response = api::TotalPaymentMethodCountResponse { total_count };

    Ok(response)
}

#[cfg(feature = "v2")]
#[instrument(skip_all)]
pub async fn retrieve_payment_method(
    state: SessionState,
    pm: api::PaymentMethodId,
    merchant_context: domain::MerchantContext,
) -> RouterResponse<api::PaymentMethodResponse> {
    let db = state.store.as_ref();
    let pm_id = id_type::GlobalPaymentMethodId::generate_from_string(pm.payment_method_id)
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Unable to generate GlobalPaymentMethodId")?;

    let payment_method = db
        .find_payment_method(
            &((&state).into()),
            merchant_context.get_merchant_key_store(),
            &pm_id,
            merchant_context.get_merchant_account().storage_scheme,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::PaymentMethodNotFound)?;

    let single_use_token_in_cache = get_single_use_token_from_store(
        &state.clone(),
        payment_method_data::SingleUseTokenKey::store_key(&pm_id.clone()),
    )
    .await
    .unwrap_or_default();

    transformers::generate_payment_method_response(&payment_method, &single_use_token_in_cache)
        .map(services::ApplicationResponse::Json)
}

// TODO: When we separate out microservices, this function will be an endpoint in payment_methods
#[cfg(feature = "v2")]
#[instrument(skip_all)]
pub async fn update_payment_method_status_internal(
    state: &SessionState,
    key_store: &domain::MerchantKeyStore,
    storage_scheme: enums::MerchantStorageScheme,
    status: enums::PaymentMethodStatus,
    payment_method_id: &id_type::GlobalPaymentMethodId,
) -> RouterResult<domain::PaymentMethod> {
    let db = &*state.store;
    let key_manager_state = &state.into();

    let payment_method = db
        .find_payment_method(
            &((state).into()),
            key_store,
            payment_method_id,
            storage_scheme,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::PaymentMethodNotFound)?;

    let pm_update = storage::PaymentMethodUpdate::StatusUpdate {
        status: Some(status),
    };

    let updated_pm = db
        .update_payment_method(
            key_manager_state,
            key_store,
            payment_method.clone(),
            pm_update,
            storage_scheme,
        )
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to update payment method in db")?;

    Ok(updated_pm)
}

#[cfg(feature = "v2")]
#[instrument(skip_all)]
pub async fn update_payment_method(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    profile: domain::Profile,
    req: api::PaymentMethodUpdate,
    payment_method_id: &id_type::GlobalPaymentMethodId,
) -> RouterResponse<api::PaymentMethodResponse> {
    let response =
        update_payment_method_core(&state, &merchant_context, &profile, req, payment_method_id)
            .await?;

    Ok(services::ApplicationResponse::Json(response))
}

#[cfg(feature = "v2")]
#[instrument(skip_all)]
pub async fn update_payment_method_core(
    state: &SessionState,
    merchant_context: &domain::MerchantContext,
    profile: &domain::Profile,
    request: api::PaymentMethodUpdate,
    payment_method_id: &id_type::GlobalPaymentMethodId,
) -> RouterResult<api::PaymentMethodResponse> {
    let db = state.store.as_ref();

    let payment_method = db
        .find_payment_method(
            &((state).into()),
            merchant_context.get_merchant_key_store(),
            payment_method_id,
            merchant_context.get_merchant_account().storage_scheme,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::PaymentMethodNotFound)?;
    let current_vault_id = payment_method.locker_id.clone();

    when(
        payment_method.status == enums::PaymentMethodStatus::AwaitingData,
        || {
            Err(errors::ApiErrorResponse::InvalidRequestData {
                message: "This Payment method is awaiting data and hence cannot be updated"
                    .to_string(),
            })
        },
    )?;

    let pmd: domain::PaymentMethodVaultingData = vault::retrieve_payment_method_from_vault(
        state,
        merchant_context,
        profile,
        &payment_method,
    )
    .await
    .change_context(errors::ApiErrorResponse::InternalServerError)
    .attach_printable("Failed to retrieve payment method from vault")?
    .data;

    let vault_request_data = request.payment_method_data.map(|payment_method_data| {
        pm_transforms::generate_pm_vaulting_req_from_update_request(pmd, payment_method_data)
    });

    let vaulting_response = match vault_request_data {
        // cannot use async map because of problems related to lifetimes
        // to overcome this, we will have to use a move closure and add some clones
        Some(ref vault_request_data) => {
            let (vault_response, _) = vault_payment_method(
                state,
                vault_request_data,
                merchant_context,
                profile,
                // using current vault_id for now,
                // will have to refactor this to generate new one on each vaulting later on
                current_vault_id,
                &payment_method.customer_id,
            )
            .await
            .attach_printable("Failed to add payment method in vault")?;

            Some(vault_response)
        }
        None => None,
    };

    let (vault_id, fingerprint_id) = match vaulting_response {
        Some(vaulting_response) => {
            let vault_id = vaulting_response.vault_id.get_string_repr().to_owned();
            (Some(vault_id), vaulting_response.fingerprint_id)
        }
        None => (None, None),
    };

    let pm_update = create_pm_additional_data_update(
        vault_request_data.as_ref(),
        state,
        merchant_context.get_merchant_key_store(),
        vault_id,
        fingerprint_id,
        &payment_method,
        request.connector_token_details,
        None,
        None,
        None,
        None,
    )
    .await
    .attach_printable("Unable to create Payment method data")?;

    let payment_method = db
        .update_payment_method(
            &((state).into()),
            merchant_context.get_merchant_key_store(),
            payment_method,
            pm_update,
            merchant_context.get_merchant_account().storage_scheme,
        )
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to update payment method in db")?;

    let response = pm_transforms::generate_payment_method_response(&payment_method, &None)?;

    // Add a PT task to handle payment_method delete from vault

    Ok(response)
}

#[cfg(feature = "v2")]
#[instrument(skip_all)]
pub async fn delete_payment_method(
    state: SessionState,
    pm_id: api::PaymentMethodId,
    merchant_context: domain::MerchantContext,
    profile: domain::Profile,
) -> RouterResponse<api::PaymentMethodDeleteResponse> {
    let pm_id = id_type::GlobalPaymentMethodId::generate_from_string(pm_id.payment_method_id)
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Unable to generate GlobalPaymentMethodId")?;
    let response = delete_payment_method_core(&state, pm_id, &merchant_context, &profile).await?;

    Ok(services::ApplicationResponse::Json(response))
}

#[cfg(feature = "v2")]
#[instrument(skip_all)]
pub async fn delete_payment_method_core(
    state: &SessionState,
    pm_id: id_type::GlobalPaymentMethodId,
    merchant_context: &domain::MerchantContext,
    profile: &domain::Profile,
) -> RouterResult<api::PaymentMethodDeleteResponse> {
    let db = state.store.as_ref();
    let key_manager_state = &(state).into();

    let payment_method = db
        .find_payment_method(
            &(state.into()),
            merchant_context.get_merchant_key_store(),
            &pm_id,
            merchant_context.get_merchant_account().storage_scheme,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::PaymentMethodNotFound)?;

    when(
        payment_method.status == enums::PaymentMethodStatus::Inactive,
        || Err(errors::ApiErrorResponse::PaymentMethodNotFound),
    )?;

    let _customer = db
        .find_customer_by_global_id(
            key_manager_state,
            &payment_method.customer_id,
            merchant_context.get_merchant_key_store(),
            merchant_context.get_merchant_account().storage_scheme,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Customer not found for the payment method")?;

    // Soft delete
    let pm_update = storage::PaymentMethodUpdate::StatusUpdate {
        status: Some(enums::PaymentMethodStatus::Inactive),
    };

    db.update_payment_method(
        &(state.into()),
        merchant_context.get_merchant_key_store(),
        payment_method.clone(),
        pm_update,
        merchant_context.get_merchant_account().storage_scheme,
    )
    .await
    .change_context(errors::ApiErrorResponse::InternalServerError)
    .attach_printable("Failed to update payment method in db")?;

    vault::delete_payment_method_data_from_vault(state, merchant_context, profile, &payment_method)
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to delete payment method from vault")?;

    let response = api::PaymentMethodDeleteResponse { id: pm_id };

    Ok(response)
}

#[cfg(feature = "v2")]
#[async_trait::async_trait]
trait EncryptableData {
    type Output;
    async fn encrypt_data(
        &self,
        key_manager_state: &common_utils::types::keymanager::KeyManagerState,
        key_store: &domain::MerchantKeyStore,
    ) -> RouterResult<Self::Output>;
}

#[cfg(feature = "v2")]
#[async_trait::async_trait]
impl EncryptableData for payment_methods::PaymentMethodSessionRequest {
    type Output = hyperswitch_domain_models::payment_methods::DecryptedPaymentMethodSession;

    async fn encrypt_data(
        &self,
        key_manager_state: &common_utils::types::keymanager::KeyManagerState,
        key_store: &domain::MerchantKeyStore,
    ) -> RouterResult<Self::Output> {
        use common_utils::types::keymanager::ToEncryptable;

        let encrypted_billing_address = self
            .billing
            .clone()
            .map(|address| address.encode_to_value())
            .transpose()
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Failed to encode billing address")?
            .map(Secret::new);

        let batch_encrypted_data = domain_types::crypto_operation(
            key_manager_state,
            common_utils::type_name!(hyperswitch_domain_models::payment_methods::PaymentMethodSession),
            domain_types::CryptoOperation::BatchEncrypt(
                hyperswitch_domain_models::payment_methods::FromRequestEncryptablePaymentMethodSession::to_encryptable(
                    hyperswitch_domain_models::payment_methods::FromRequestEncryptablePaymentMethodSession {
                       billing: encrypted_billing_address,
                    },
                ),
            ),
            common_utils::types::keymanager::Identifier::Merchant(key_store.merchant_id.clone()),
            key_store.key.peek(),
        )
        .await
        .and_then(|val| val.try_into_batchoperation())
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed while encrypting payment methods session details".to_string())?;

        let encrypted_data =
        hyperswitch_domain_models::payment_methods::FromRequestEncryptablePaymentMethodSession::from_encryptable(
            batch_encrypted_data,
        )
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed while encrypting payment methods session detailss")?;

        Ok(encrypted_data)
    }
}

#[cfg(feature = "v2")]
#[async_trait::async_trait]
impl EncryptableData for payment_methods::PaymentMethodsSessionUpdateRequest {
    type Output = hyperswitch_domain_models::payment_methods::DecryptedPaymentMethodSession;

    async fn encrypt_data(
        &self,
        key_manager_state: &common_utils::types::keymanager::KeyManagerState,
        key_store: &domain::MerchantKeyStore,
    ) -> RouterResult<Self::Output> {
        use common_utils::types::keymanager::ToEncryptable;

        let encrypted_billing_address = self
            .billing
            .clone()
            .map(|address| address.encode_to_value())
            .transpose()
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Failed to encode billing address")?
            .map(Secret::new);

        let batch_encrypted_data = domain_types::crypto_operation(
            key_manager_state,
            common_utils::type_name!(hyperswitch_domain_models::payment_methods::PaymentMethodSession),
            domain_types::CryptoOperation::BatchEncrypt(
                hyperswitch_domain_models::payment_methods::FromRequestEncryptablePaymentMethodSession::to_encryptable(
                    hyperswitch_domain_models::payment_methods::FromRequestEncryptablePaymentMethodSession {
                       billing: encrypted_billing_address,
                    },
                ),
            ),
            common_utils::types::keymanager::Identifier::Merchant(key_store.merchant_id.clone()),
            key_store.key.peek(),
        )
        .await
        .and_then(|val| val.try_into_batchoperation())
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed while encrypting payment methods session details".to_string())?;

        let encrypted_data =
        hyperswitch_domain_models::payment_methods::FromRequestEncryptablePaymentMethodSession::from_encryptable(
            batch_encrypted_data,
        )
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed while encrypting payment methods session detailss")?;

        Ok(encrypted_data)
    }
}

#[cfg(feature = "v2")]
pub async fn payment_methods_session_create(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    request: payment_methods::PaymentMethodSessionRequest,
) -> RouterResponse<payment_methods::PaymentMethodSessionResponse> {
    let db = state.store.as_ref();
    let key_manager_state = &(&state).into();

    db.find_customer_by_global_id(
        key_manager_state,
        &request.customer_id,
        merchant_context.get_merchant_key_store(),
        merchant_context.get_merchant_account().storage_scheme,
    )
    .await
    .to_not_found_response(errors::ApiErrorResponse::CustomerNotFound)?;

    let payment_methods_session_id =
        id_type::GlobalPaymentMethodSessionId::generate(&state.conf.cell_information.id)
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Unable to generate GlobalPaymentMethodSessionId")?;

    let encrypted_data = request
        .encrypt_data(key_manager_state, merchant_context.get_merchant_key_store())
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to encrypt payment methods session data")?;

    let billing = encrypted_data
        .billing
        .as_ref()
        .map(|data| {
            data.clone()
                .deserialize_inner_value(|value| value.parse_value("Address"))
        })
        .transpose()
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Unable to decode billing address")?;

    // If not passed in the request, use the default value from constants
    let expires_in = request
        .expires_in
        .unwrap_or(consts::DEFAULT_PAYMENT_METHOD_SESSION_EXPIRY)
        .into();

    let expires_at = common_utils::date_time::now().saturating_add(Duration::seconds(expires_in));

    let client_secret = payment_helpers::create_client_secret(
        &state,
        merchant_context.get_merchant_account().get_id(),
        util_types::authentication::ResourceId::PaymentMethodSession(
            payment_methods_session_id.clone(),
        ),
    )
    .await
    .change_context(errors::ApiErrorResponse::InternalServerError)
    .attach_printable("Unable to create client secret")?;

    let payment_method_session_domain_model =
        hyperswitch_domain_models::payment_methods::PaymentMethodSession {
            id: payment_methods_session_id,
            customer_id: request.customer_id,
            billing,
            psp_tokenization: request.psp_tokenization,
            network_tokenization: request.network_tokenization,
            tokenization_data: request.tokenization_data,
            expires_at,
            return_url: request.return_url,
            associated_payment_methods: None,
            associated_payment: None,
            associated_token_id: None,
        };

    db.insert_payment_methods_session(
        key_manager_state,
        merchant_context.get_merchant_key_store(),
        payment_method_session_domain_model.clone(),
        expires_in,
    )
    .await
    .change_context(errors::ApiErrorResponse::InternalServerError)
    .attach_printable("Failed to insert payment methods session in db")?;

    let response = transformers::generate_payment_method_session_response(
        payment_method_session_domain_model,
        client_secret.secret,
        None,
        None,
    );

    Ok(services::ApplicationResponse::Json(response))
}

#[cfg(feature = "v2")]
pub async fn payment_methods_session_update(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    payment_method_session_id: id_type::GlobalPaymentMethodSessionId,
    request: payment_methods::PaymentMethodsSessionUpdateRequest,
) -> RouterResponse<payment_methods::PaymentMethodSessionResponse> {
    let db = state.store.as_ref();
    let key_manager_state = &(&state).into();

    let existing_payment_method_session_state = db
        .get_payment_methods_session(
            key_manager_state,
            merchant_context.get_merchant_key_store(),
            &payment_method_session_id,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::GenericNotFoundError {
            message: "payment methods session does not exist or has expired".to_string(),
        })
        .attach_printable("Failed to retrieve payment methods session from db")?;

    let encrypted_data = request
        .encrypt_data(key_manager_state, merchant_context.get_merchant_key_store())
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to encrypt payment methods session data")?;

    let billing = encrypted_data
        .billing
        .as_ref()
        .map(|data| {
            data.clone()
                .deserialize_inner_value(|value| value.parse_value("Address"))
        })
        .transpose()


impl ConnectorValidation for Powertranz {}

impl ConnectorValidation for for Powertranz


pub struct WellsfargopayoutRouterData<T> {
    pub amount: StringMinorUnit, // The type of amount that a connector accepts, for example, String, i64, f64, etc.
    pub router_data: T,
}


pub async fn find_payment_merchant_connector_account(
        &self,
        state: &SessionState,
        key_store: &domain::MerchantKeyStore,
        billing_connector_account: &domain::MerchantConnectorAccount,
    ) -> CustomResult<Option<domain::MerchantConnectorAccount>, errors::RevenueRecoveryError> {
        let payment_merchant_connector_account_id = billing_connector_account
            .get_payment_merchant_connector_account_id_using_account_reference_id(
                self.0.connector_account_reference_id.clone(),
            );
        let db = &*state.store;
        let key_manager_state = &(state).into();
        let payment_merchant_connector_account = payment_merchant_connector_account_id
            .as_ref()
            .async_map(|mca_id| async move {
                db.find_merchant_connector_account_by_id(key_manager_state, mca_id, key_store)
                    .await
            })
            .await
            .transpose()
            .change_context(errors::RevenueRecoveryError::PaymentMerchantConnectorAccountNotFound)
            .attach_printable(
                "failed to fetch payment merchant connector id using account reference id",
            )?;
        Ok(payment_merchant_connector_account)
    }


pub fn get_auth_string_from_header(&self) -> RouterResult<&str> {
        self.headers
            .get(headers::AUTHORIZATION)
            .get_required_value(headers::AUTHORIZATION)?
            .to_str()
            .change_context(errors::ApiErrorResponse::InvalidDataValue {
                field_name: headers::AUTHORIZATION,
            })
            .attach_printable("Failed to convert authorization header to string")
    }


impl api::ConnectorAccessToken for Silverflow {}

impl api::ConnectorAccessToken for for Silverflow


pub struct WisePayoutSyncResponse {
    id: u64,
    status: WiseSyncStatus,
}


OpenAPI Block Path: components.schemas.SdkInformation
{
  "type": "object",
  "description": "SDK Information if request is from SDK",
  "required": [
    "sdk_app_id",
    "sdk_enc_data",
    "sdk_ephem_pub_key",
    "sdk_trans_id",
    "sdk_reference_number",
    "sdk_max_timeout"
  ],
  "properties": {
    "sdk_app_id": {
      "type": "string",
      "description": "Unique ID created on installations of the 3DS Requestor App on a Consumer Device"
    },
    "sdk_enc_data": {
      "type": "string",
      "description": "JWE Object containing data encrypted by the SDK for the DS to decrypt"
    },
    "sdk_ephem_pub_key": {
      "type": "object",
      "description": "Public key component of the ephemeral key pair generated by the 3DS SDK",
      "additionalProperties": {
        "type": "string"
      }
    },
    "sdk_trans_id": {
      "type": "string",
      "description": "Unique transaction identifier assigned by the 3DS SDK"
    },
    "sdk_reference_number": {
      "type": "string",
      "description": "Identifies the vendor and version for the 3DS SDK that is integrated in a 3DS Requestor App"
    },
    "sdk_max_timeout": {
      "type": "integer",
      "format": "int32",
      "description": "Indicates maximum amount of time in minutes",
      "minimum": 0
    },
    "sdk_type": {
      "allOf": [
        {
          "$ref": "#/components/schemas/SdkType"
        }
      ],
      "nullable": true
    }
  }
}


File: crates/router/src/types/payment_methods.rs
 Public functions: 1
 Public structs: 39

use std::fmt::Debug;

use api_models::enums as api_enums;
#[cfg(feature = "v1")]
use cards::CardNumber;
#[cfg(feature = "v2")]
use cards::{CardNumber, NetworkToken};
#[cfg(feature = "v2")]
use common_types::primitive_wrappers;
#[cfg(feature = "v2")]
use common_utils::generate_id;
use common_utils::id_type;
#[cfg(feature = "v2")]
use hyperswitch_domain_models::payment_method_data::NetworkTokenDetails;
use masking::Secret;
use serde::{Deserialize, Serialize};

use crate::types::api;
#[cfg(feature = "v2")]
use crate::{
    consts,
    types::{domain, storage},
};

#[cfg(feature = "v2")]
pub trait VaultingInterface {
    fn get_vaulting_request_url() -> &'static str;

    fn get_vaulting_flow_name() -> &'static str;
}

#[cfg(feature = "v2")]
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct VaultFingerprintRequest {
    pub data: String,
    pub key: String,
}

#[cfg(feature = "v2")]
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct VaultFingerprintResponse {
    pub fingerprint_id: String,
}

#[cfg(any(feature = "v2", feature = "tokenization_v2"))]
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct AddVaultRequest<D> {
    pub entity_id: id_type::GlobalCustomerId,
    pub vault_id: domain::VaultId,
    pub data: D,
    pub ttl: i64,
}

#[cfg(feature = "v2")]
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct AddVaultResponse {
    pub entity_id: Option<id_type::GlobalCustomerId>,
    pub vault_id: domain::VaultId,
    pub fingerprint_id: Option<String>,
}

#[cfg(feature = "v2")]
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct AddVault;

#[cfg(feature = "v2")]
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct GetVaultFingerprint;

#[cfg(feature = "v2")]
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct VaultRetrieve;

#[cfg(feature = "v2")]
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct VaultDelete;

#[cfg(feature = "v2")]
impl VaultingInterface for AddVault {
    fn get_vaulting_request_url() -> &'static str {
        consts::ADD_VAULT_REQUEST_URL
    }

    fn get_vaulting_flow_name() -> &'static str {
        consts::VAULT_ADD_FLOW_TYPE
    }
}

#[cfg(feature = "v2")]
impl VaultingInterface for GetVaultFingerprint {
    fn get_vaulting_request_url() -> &'static str {
        consts::VAULT_FINGERPRINT_REQUEST_URL
    }

    fn get_vaulting_flow_name() -> &'static str {
        consts::VAULT_GET_FINGERPRINT_FLOW_TYPE
    }
}

#[cfg(feature = "v2")]
impl VaultingInterface for VaultRetrieve {
    fn get_vaulting_request_url() -> &'static str {
        consts::VAULT_RETRIEVE_REQUEST_URL
    }

    fn get_vaulting_flow_name() -> &'static str {
        consts::VAULT_RETRIEVE_FLOW_TYPE
    }
}

#[cfg(feature = "v2")]
impl VaultingInterface for VaultDelete {
    fn get_vaulting_request_url() -> &'static str {
        consts::VAULT_DELETE_REQUEST_URL
    }

    fn get_vaulting_flow_name() -> &'static str {
        consts::VAULT_DELETE_FLOW_TYPE
    }
}

#[cfg(feature = "v2")]
pub struct SavedPMLPaymentsInfo {
    pub payment_intent: storage::PaymentIntent,
    pub profile: domain::Profile,
    pub collect_cvv_during_payment: Option<primitive_wrappers::ShouldCollectCvvDuringPayment>,
    pub off_session_payment_flag: bool,
    pub is_connector_agnostic_mit_enabled: bool,
}

#[cfg(feature = "v2")]
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct VaultRetrieveRequest {
    pub entity_id: id_type::GlobalCustomerId,
    pub vault_id: domain::VaultId,
}

#[cfg(feature = "v2")]
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct VaultRetrieveResponse {
    pub data: domain::PaymentMethodVaultingData,
}

#[cfg(feature = "v2")]
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct VaultDeleteRequest {
    pub entity_id: id_type::GlobalCustomerId,
    pub vault_id: domain::VaultId,
}

#[cfg(feature = "v2")]
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct VaultDeleteResponse {
    pub entity_id: id_type::GlobalCustomerId,
    pub vault_id: domain::VaultId,
}

#[cfg(feature = "v1")]
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CardData {
    pub card_number: CardNumber,
    pub exp_month: Secret<String>,
    pub exp_year: Secret<String>,
    pub card_security_code: Option<Secret<String>>,
}

#[cfg(feature = "v2")]
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CardData {
    pub card_number: CardNumber,
    pub exp_month: Secret<String>,
    pub exp_year: Secret<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub card_security_code: Option<Secret<String>>,
}

#[cfg(feature = "v1")]
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct OrderData {
    pub consent_id: String,
    pub customer_id: id_type::CustomerId,
}

#[cfg(feature = "v2")]
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct OrderData {
    pub consent_id: String,
    pub customer_id: id_type::GlobalCustomerId,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ApiPayload {
    pub service: String,
    pub card_data: Secret<String>, //encrypted card data
    pub order_data: OrderData,
    pub key_id: String,
    pub should_send_token: bool,
}

#[derive(Debug, Deserialize, Eq, PartialEq)]
pub struct CardNetworkTokenResponse {
    pub payload: Secret<String>, //encrypted payload
}

#[cfg(feature = "v1")]
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CardNetworkTokenResponsePayload {
    pub card_brand: api_enums::CardNetwork,
    pub card_fingerprint: Option<Secret<String>>,
    pub card_reference: String,
    pub correlation_id: String,
    pub customer_id: String,
    pub par: String,
    pub token: CardNumber,
    pub token_expiry_month: Secret<String>,
    pub token_expiry_year: Secret<String>,
    pub token_isin: String,
    pub token_last_four: String,
    pub token_status: String,
}

#[cfg(feature = "v2")]
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GenerateNetworkTokenResponsePayload {
    pub card_brand: api_enums::CardNetwork,
    pub card_fingerprint: Option<Secret<String>>,
    pub card_reference: String,
    pub correlation_id: String,
    pub customer_id: String,
    pub par: String,
    pub token: NetworkToken,
    pub token_expiry_month: Secret<String>,
    pub token_expiry_year: Secret<String>,
    pub token_isin: String,
    pub token_last_four: String,
    pub token_status: String,
}

#[cfg(feature = "v1")]
#[derive(Debug, Serialize)]
pub struct GetCardToken {
    pub card_reference: String,
    pub customer_id: id_type::CustomerId,
}

#[cfg(feature = "v2")]
#[derive(Debug, Serialize)]
pub struct GetCardToken {
    pub card_reference: String,
    pub customer_id: id_type::GlobalCustomerId,
}

#[cfg(feature = "v1")]
#[derive(Debug, Deserialize)]
pub struct AuthenticationDetails {
    pub cryptogram: Secret<String>,
    pub token: CardNumber, //network token
}

#[cfg(feature = "v2")]
#[derive(Debug, Deserialize)]
pub struct AuthenticationDetails {
    pub cryptogram: Secret<String>,
    pub token: NetworkToken, //network token
}

#[derive(Debug, Serialize, Deserialize)]
pub struct TokenDetails {
    pub exp_month: Secret<String>,
    pub exp_year: Secret<String>,
}

#[derive(Debug, Deserialize)]
pub struct TokenResponse {
    pub authentication_details: AuthenticationDetails,
    pub network: api_enums::CardNetwork,
    pub token_details: TokenDetails,
    pub eci: Option<String>,
    pub card_type: Option<String>,
    pub issuer: Option<String>,
    pub nickname: Option<Secret<String>>,
}

#[cfg(feature = "v1")]
#[derive(Debug, Serialize, Deserialize)]
pub struct DeleteCardToken {
    pub card_reference: String, //network token requestor ref id
    pub customer_id: id_type::CustomerId,
}

#[cfg(feature = "v2")]
#[derive(Debug, Serialize, Deserialize)]
pub struct DeleteCardToken {
    pub card_reference: String, //network token requestor ref id
    pub customer_id: id_type::GlobalCustomerId,
}

#[derive(Debug, Deserialize, Eq, PartialEq)]
#[serde(rename_all = "UPPERCASE")]
pub enum DeleteNetworkTokenStatus {
    Success,
}

#[derive(Debug, Deserialize, Eq, PartialEq)]
pub struct NetworkTokenErrorInfo {
    pub code: String,
    pub developer_message: String,
}

#[derive(Debug, Deserialize, Eq, PartialEq)]
pub struct NetworkTokenErrorResponse {
    pub error_message: String,
    pub error_info: NetworkTokenErrorInfo,
}

#[derive(Debug, Deserialize, Eq, PartialEq)]
pub struct DeleteNetworkTokenResponse {
    pub status: DeleteNetworkTokenStatus,
}

#[cfg(feature = "v1")]
#[derive(Debug, Serialize, Deserialize)]
pub struct CheckTokenStatus {
    pub card_reference: String,
    pub customer_id: id_type::CustomerId,
}

#[cfg(feature = "v2")]
#[derive(Debug, Serialize, Deserialize)]
pub struct CheckTokenStatus {
    pub card_reference: String,
    pub customer_id: id_type::GlobalCustomerId,
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "UPPERCASE")]
pub enum TokenStatus {
    Active,
    Inactive,
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CheckTokenStatusResponsePayload {
    pub token_expiry_month: Secret<String>,
    pub token_expiry_year: Secret<String>,
    pub token_status: TokenStatus,
}

#[derive(Debug, Deserialize)]
pub struct CheckTokenStatusResponse {
    pub payload: CheckTokenStatusResponsePayload,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct NetworkTokenRequestorData {
    pub card_reference: String,
    pub customer_id: String,
    pub expiry_year: Secret<String>,
    pub expiry_month: Secret<String>,
}

impl NetworkTokenRequestorData {
    pub fn is_update_required(
        &self,
        data_stored_in_vault: api::payment_methods::CardDetailFromLocker,
    ) -> bool {
        //if the expiry year and month in the vault are not the same as the ones in the requestor data,
        //then we need to update the vault data with the updated expiry year and month.
        !((data_stored_in_vault.expiry_year.unwrap_or_default() == self.expiry_year)
            && (data_stored_in_vault.expiry_month.unwrap_or_default() == self.expiry_month))
    }
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct NetworkTokenMetaDataUpdateBody {
    pub token: NetworkTokenRequestorData,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct PanMetadataUpdateBody {
    pub card: NetworkTokenRequestorData,
}


pub async fn delete_totp_from_redis(state: &SessionState, user_id: &str) -> UserResult<()> {
    let redis_conn = super::get_redis_connection_for_global_tenant(state)?;
    let key = format!("{}{}", consts::user::REDIS_TOTP_PREFIX, user_id);
    redis_conn
        .delete_key(&key.into())
        .await
        .change_context(UserErrors::InternalServerError)
        .map(|_| ())
}


pub struct HyperswitchVaultCreateResponse {
    id: Secret<String>,
    client_secret: Secret<String>,
}


pub struct CustomerInfo {
    card_holder_name: Secret<String>,
    billing_address: Option<BillingAddress>,
    shipping_address: Option<ShippingAddress>,
}


pub struct ZenCardDetails {
    number: CardNumber,
    expiry_date: Secret<String>,
    cvv: Secret<String>,
}


Web Documentation: Smart Routing | Hyperswitch
# Type: Web Doc

Prerequisites
To get started with Smart Router, ensure to have one or more payment processors integrated. You can integrate the payment processor of your choice on the Control Center by following the
Connector Integration
guide.
What is smart payment routing?
Selling globally or otherwise invariably brings in a requirement to adopt multiple payment processors to cater to a wide range of payment method needs of the customers and gives you the flexibility to switch between processors to manage down-time and , it could be vital to optimising your payment processing costs as your business can choose the most optimal payment processors for every payment based on the cost, region and customer.
Hence, Hyperswitch’s smart router is designed as a no-code tool to provide complete control and transparency in creating and modifying payment routing rules. Hyperswitch supports below formats of Smart Routing.
Volume Based Configuration:
Define volume distribution among multiple payment processors using percentages.
Rule Based Configuration:
More granular control which allows to define custom routing logics based on different parameters of payment.
Default Fallback Routing :
If the active routing rules are not applicable, the priority order of all configured payment processors is used to route payments. This priority order is configurable from the Dashboard.
Cost Based Configuration
(submit a feature request
here
)
:
Automatically routes transaction to the payment processor charging the least MDR (merchant discount rate) for the opted payment method.
How does the Smart Router work?
Hyperswitch Smart Router Engine evaluates every payment request against your predefined routing logic and makes a decision on the best payment processor for the payment, and executes the transaction. If the payment fails or if the payment processor is down, the payment is automatically retried through a different processor.
How to configure the Smart Router?
Hyperswitch dashboard
provides a simple, intuitive UI to configure multiple Routing rules on your dashboard under the
Routing
tab. There are three routing rule formats that Hyperswitch currently supports.
\
Rule Based Routing
Volume Based Routing
Default Fallback Routing
To test the Smart Router, after activating one rule, we can make a Test Payment using the
Hyperswitch Dashboard
Test a payment
FAQs
1. What parameters can I use to configure routing rules?
The rule-based routing supports setting up advanced rule configuration based on all critical /payments parameters such as Payment Method, Payment Method Type, Country, Currency, Amount etc.
2. Why did my payment go through 'Y' connector even though I have specified 'X' in my routing configuration? OR Why is it showing me 'Abc' payment method in SDK checkout even though I have not enabled it for the 'X' connector that I'm routing my payments through?
There can be multiple reasons why this happened but all of them can be boiled down to a "connector eligibility failure" for a given payment. We'll walk through a common scenario to examine what this really means.
Imagine that you configured two connectors for your account. Say
Stripe
, then
Adyen
, in that order. Since you configured them in that order, your default fallback looks like this:
[Stripe, Adyen]
(connectors are appended to the end of your default fallback list when configured for the first time)
In your connectors dashboard, you enable Cards for Stripe, and ApplePay for Adyen.
Now you create a new Volume-based routing configuration, and you configure it to route 100% of your traffic through Stripe for now.
Now you go ahead and open up the Hyperswitch SDK to make a test payment. In the payment method selection area, you can see two buttons, one for
Cards
and one for
ApplePay
.
This is where you run into your first question. "Why is it showing me ApplePay even though I have configured 100% of my payments to go through Stripe, and ApplePay is not enabled for Stripe?"
The answer to this is, the payment methods that are shown to the customer in the SDK aren't conscious of your routing configuration. We prioritize giving your customers the complete spread of all enabled payment methods across all of your enabled connectors. Therefore, if you specifically do not wish for ApplePay to appear on your checkout screen, you need to disable it in
Adyen
here, even though your routing configuration makes no mention of Adyen.
Now you select
ApplePay
, go through the required steps, confirm the payment, and it succeeds. You go to your payments dashboard and see that the payment went through
Adyen
.
This is where you run into the second question. "Why is the payment going through
Adyen
even though I have set my routing configuration to route 100% of my payments through
Stripe
?
ApplePay
on the checkout screen even though it wasn't enabled for your preferred connector
Stripe
, we need to adhere to your connector payment method configuration and ensure the payment goes through the right connector, here,
Adyen
since
ApplePay
is only enabled for Adyen. To briefly explain how Hyperswitch reaches this conclusion :-
Hyperswitch runs your configured routing algorithm. Since this is
100% Stripe
, Hyperswitch receives
Stripe
as the outuput.
Hyperswitch then runs an Eligibility Analysis on the output (
Stripe
) to gauge its eligibility for the current payment. The Eligibility Analysis fails once Hyperswitch realizes that the payment is made through
ApplePay
which is not enabled for
Stripe
The application then goes into fallback mode and loads your
default fallback
, which is
[Stripe, Adyen]
as seen earlier.
The application looks through the list in order. It ignores
Stripe
since it has already failed the Eligibility Analysis. It instead subjects
Adyen
This time the analysis passes since
ApplePay
is enabled for
Adyen
.
Hyperswitch takes
Adyen
as the final connector to make the payment through even though your configuration says
100% Stripe
.
This fallback flow is taken when none of the connectors in the output of routing are eligible for the payment. This is done in an effort to maximize the success rate of the payment even if it means deviating from the currently active routing configuration.
10 months ago
Was this helpful?

Smart Routing | Hyperswitch