pub async fn recovery_incoming_webhook_flow(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    business_profile: domain::Profile,
    source_verified: bool,
    connector_enum: &connector_integration_interface::ConnectorEnum,
    billing_connector_account: hyperswitch_domain_models::merchant_connector_account::MerchantConnectorAccount,
    connector_name: &str,
    request_details: &hyperswitch_interfaces::webhooks::IncomingWebhookRequestDetails<'_>,
    event_type: webhooks::IncomingWebhookEvent,
    req_state: ReqState,
    object_ref_id: &webhooks::ObjectReferenceId,
) -> CustomResult<webhooks::WebhookResponseTracker, errors::RevenueRecoveryError> {
    // Source verification is necessary for revenue recovery webhooks flow since We don't have payment intent/attempt object created before in our system.
    common_utils::fp_utils::when(!source_verified, || {
        Err(report!(
            errors::RevenueRecoveryError::WebhookAuthenticationFailed
        ))
    })?;

    let connector = api_enums::Connector::from_str(connector_name)
        .change_context(errors::RevenueRecoveryError::InvoiceWebhookProcessingFailed)
        .attach_printable_lazy(|| format!("unable to parse connector name {connector_name:?}"))?;

    let billing_connectors_with_invoice_sync_call = &state.conf.billing_connectors_invoice_sync;

    let should_billing_connector_invoice_api_called = billing_connectors_with_invoice_sync_call
        .billing_connectors_which_requires_invoice_sync_call
        .contains(&connector);

    let billing_connectors_with_payment_sync_call = &state.conf.billing_connectors_payment_sync;

    let should_billing_connector_payment_api_called = billing_connectors_with_payment_sync_call
        .billing_connectors_which_require_payment_sync
        .contains(&connector);

    let billing_connector_payment_details =
        BillingConnectorPaymentsSyncResponseData::get_billing_connector_payment_details(
            should_billing_connector_payment_api_called,
            &state,
            &merchant_context,
            &billing_connector_account,
            connector_name,
            object_ref_id,
        )
        .await?;

    let invoice_id = billing_connector_payment_details
        .clone()
        .map(|data| data.merchant_reference_id);

    let billing_connector_invoice_details =
        BillingConnectorInvoiceSyncResponseData::get_billing_connector_invoice_details(
            should_billing_connector_invoice_api_called,
            &state,
            &merchant_context,
            &billing_connector_account,
            connector_name,
            invoice_id,
        )
        .await?;

    // Checks whether we have data in billing_connector_invoice_details , if it is there then we construct revenue recovery invoice from it else it takes from webhook
    let invoice_details = RevenueRecoveryInvoice::get_recovery_invoice_details(
        connector_enum,
        request_details,
        billing_connector_invoice_details.as_ref(),
    )?;

    // Fetch the intent using merchant reference id, if not found create new intent.
    let payment_intent = invoice_details
        .get_payment_intent(&state, &req_state, &merchant_context, &business_profile)
        .await
        .transpose()
        .async_unwrap_or_else(|| async {
            invoice_details
                .create_payment_intent(&state, &req_state, &merchant_context, &business_profile)
                .await
        })
        .await?;

    let is_event_recovery_transaction_event = event_type.is_recovery_transaction_event();
    let (recovery_attempt_from_payment_attempt, recovery_intent_from_payment_attempt) =
        RevenueRecoveryAttempt::get_recovery_payment_attempt(
            is_event_recovery_transaction_event,
            &billing_connector_account,
            &state,
            connector_enum,
            &req_state,
            billing_connector_payment_details.as_ref(),
            request_details,
            &merchant_context,
            &business_profile,
            &payment_intent,
            &invoice_details.0,
        )
        .await?;

    // Publish event to Kafka
    if let Some(ref attempt) = recovery_attempt_from_payment_attempt {
        // Passing `merchant_context` here
        let recovery_payment_tuple =
            &RecoveryPaymentTuple::new(&recovery_intent_from_payment_attempt, attempt);
        if let Err(e) = RecoveryPaymentTuple::publish_revenue_recovery_event_to_kafka(
            &state,
            recovery_payment_tuple,
            None,
        )
        .await
        {
            logger::error!(
                "Failed to publish revenue recovery event to kafka : {:?}",
                e
            );
        };
    }

    let attempt_triggered_by = recovery_attempt_from_payment_attempt
        .as_ref()
        .and_then(|attempt| attempt.get_attempt_triggered_by());

    let recovery_action = RecoveryAction {
        action: RecoveryAction::get_action(event_type, attempt_triggered_by),
    };

    let mca_retry_threshold = billing_connector_account
        .get_retry_threshold()
        .ok_or(report!(
            errors::RevenueRecoveryError::BillingThresholdRetryCountFetchFailed
        ))?;

    let intent_retry_count = recovery_intent_from_payment_attempt
        .feature_metadata
        .as_ref()
        .and_then(|metadata| metadata.get_retry_count())
        .ok_or(report!(errors::RevenueRecoveryError::RetryCountFetchFailed))?;

    logger::info!("Intent retry count: {:?}", intent_retry_count);
    recovery_action
        .handle_action(
            &state,
            &business_profile,
            &merchant_context,
            &billing_connector_account,
            mca_retry_threshold,
            intent_retry_count,
            &(
                recovery_attempt_from_payment_attempt,
                recovery_intent_from_payment_attempt,
            ),
        )
        .await
}


impl api::PaymentsPreProcessing for Paypal {}

impl api::PaymentsPreProcessing for for Paypal


impl api::PaymentVoid for Bluesnap {}


impl TokenizationInterface for KafkaStore {
    async fn insert_tokenization(
        &self,
        tokenization: hyperswitch_domain_models::tokenization::Tokenization,
        merchant_key_store: &hyperswitch_domain_models::merchant_key_store::MerchantKeyStore,
        key_manager_state: &KeyManagerState,
    ) -> CustomResult<hyperswitch_domain_models::tokenization::Tokenization, errors::StorageError>
    {
        self.diesel_store
            .insert_tokenization(tokenization, merchant_key_store, key_manager_state)
            .await
    }

    async fn get_entity_id_vault_id_by_token_id(
        &self,
        token: &id_type::GlobalTokenId,
        merchant_key_store: &hyperswitch_domain_models::merchant_key_store::MerchantKeyStore,
        key_manager_state: &KeyManagerState,
    ) -> CustomResult<hyperswitch_domain_models::tokenization::Tokenization, errors::StorageError>
    {
        self.diesel_store
            .get_entity_id_vault_id_by_token_id(token, merchant_key_store, key_manager_state)
            .await
    }

    async fn update_tokenization_record(
        &self,
        tokenization: hyperswitch_domain_models::tokenization::Tokenization,
        tokenization_update: hyperswitch_domain_models::tokenization::TokenizationUpdate,
        merchant_key_store: &hyperswitch_domain_models::merchant_key_store::MerchantKeyStore,
        key_manager_state: &KeyManagerState,
    ) -> CustomResult<hyperswitch_domain_models::tokenization::Tokenization, errors::StorageError>
    {
        self.diesel_store
            .update_tokenization_record(
                tokenization,
                tokenization_update,
                merchant_key_store,
                key_manager_state,
            )
            .await
    }
}

impl TokenizationInterface for for KafkaStore


pub async fn list_user_authentication_methods_for_owner_id(
        conn: &PgPooledConn,
        owner_id: &str,
    ) -> StorageResult<Vec<Self>> {
        generics::generic_filter::<<Self as HasTable>::Table, _, _, _>(
            conn,
            dsl::owner_id.eq(owner_id.to_owned()),
            None,
            None,
            Some(dsl::last_modified_at.asc()),
        )
        .await
    }


pub struct EmailSettings {
    /// The AWS region to send SES requests to.
    pub aws_region: String,

    /// Number of days for verification of the email
    pub allowed_unverified_days: i64,

    /// Sender email
    pub sender_email: String,

    #[serde(flatten)]
    /// The client specific configurations
    pub client_config: EmailClientConfigs,

    /// Recipient email for recon emails
    pub recon_recipient_email: pii::Email,

    /// Recipient email for recon emails
    pub prod_intent_recipient_email: pii::Email,
}


pub struct EventDetails {
    #[serde(rename = "type")]
    pub event_type: EventType,
    pub transaction_reference: String,
    /// Mandate's token
    pub token: Option<MandateToken>,
    /// Network transaction ID
    pub scheme_reference: Option<Secret<String>>,
}


pub fn get_api_event_dimensions() -> Vec<NameDescription> {
    ApiEventDimensions::iter().map(Into::into).collect()
}


pub async fn setup_mandate_preprocessing_steps<F: Clone>(
    state: &SessionState,
    router_data: &types::RouterData<F, types::SetupMandateRequestData, types::PaymentsResponseData>,
    confirm: bool,
    connector: &api::ConnectorData,
) -> RouterResult<types::RouterData<F, types::SetupMandateRequestData, types::PaymentsResponseData>>
{
    if confirm {
        let connector_integration: services::BoxedPaymentConnectorIntegrationInterface<
            api::PreProcessing,
            types::PaymentsPreProcessingData,
            types::PaymentsResponseData,
        > = connector.connector.get_connector_integration();

        let preprocessing_request_data =
            types::PaymentsPreProcessingData::try_from(router_data.request.clone())?;

        let preprocessing_response_data: Result<types::PaymentsResponseData, types::ErrorResponse> =
            Err(types::ErrorResponse::default());

        let preprocessing_router_data =
            helpers::router_data_type_conversion::<_, api::PreProcessing, _, _, _, _>(
                router_data.clone(),
                preprocessing_request_data,
                preprocessing_response_data,
            );

        let resp = services::execute_connector_processing_step(
            state,
            connector_integration,
            &preprocessing_router_data,
            payments::CallConnectorAction::Trigger,
            None,
            None,
        )
        .await
        .to_payment_failed_response()?;

        let mut setup_mandate_router_data = helpers::router_data_type_conversion::<_, F, _, _, _, _>(
            resp.clone(),
            router_data.request.to_owned(),
            resp.response.clone(),
        );

        if connector.connector_name == api_models::enums::Connector::Nuvei {
            let (enrolled_for_3ds, related_transaction_id) =
                match &setup_mandate_router_data.response {
                    Ok(types::PaymentsResponseData::ThreeDSEnrollmentResponse {
                        enrolled_v2,
                        related_transaction_id,
                    }) => (*enrolled_v2, related_transaction_id.clone()),
                    _ => (false, None),
                };
            setup_mandate_router_data.request.enrolled_for_3ds = enrolled_for_3ds;
            setup_mandate_router_data.request.related_transaction_id = related_transaction_id;
        }

        Ok(setup_mandate_router_data)
    } else {
        Ok(router_data.clone())
    }
}


pub async fn payment_intents_capture(
    state: web::Data<routes::AppState>,
    qs_config: web::Data<serde_qs::Config>,
    req: HttpRequest,
    form_payload: web::Bytes,
    path: web::Path<common_utils::id_type::PaymentId>,
) -> HttpResponse {
    let stripe_payload: payment_types::PaymentsCaptureRequest = match qs_config
        .deserialize_bytes(&form_payload)
    {
        Ok(p) => p,
        Err(err) => {
            return api::log_and_return_error_response(report!(errors::StripeErrorCode::from(err)))
        }
    };

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

    logger::info!(tag = ?Tag::CompatibilityLayerRequest, payload = ?stripe_payload);

    let payload = payment_types::PaymentsCaptureRequest {
        payment_id: path.into_inner(),
        ..stripe_payload
    };

    let flow = Flow::PaymentsCapture;
    let locking_action = payload.get_locking_input(flow.clone());
    Box::pin(wrap::compatibility_api_wrap::<
        _,
        _,
        _,
        _,
        _,
        types::StripePaymentIntentResponse,
        errors::StripeErrorCode,
        _,
    >(
        flow,
        state.into_inner(),
        &req,
        payload,
        |state, auth: auth::AuthenticationData, payload, req_state| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            payments::payments_core::<
                api_types::Capture,
                api_types::PaymentsResponse,
                _,
                _,
                _,
                payments::PaymentData<api_types::Capture>,
            >(
                state,
                req_state,
                merchant_context,
                None,
                payments::PaymentCapture,
                payload,
                api::AuthFlow::Merchant,
                payments::CallConnectorAction::Trigger,
                None,
                hyperswitch_domain_models::payments::HeaderPayload::default(),
            )
        },
        &auth::HeaderAuth(auth::ApiKeyAuth {
            is_connected_allowed: false,
            is_platform_allowed: false,
        }),
        locking_action,
    ))
    .await
}


File: crates/hyperswitch_connectors/src/connectors/adyenplatform/transformers.rs
 Public structs: 2

use common_utils::types::MinorUnit;
use error_stack::Report;
use hyperswitch_domain_models::router_data::ConnectorAuthType;
use hyperswitch_interfaces::errors::ConnectorError;
use masking::Secret;
use serde::Serialize;

#[cfg(feature = "payouts")]
pub mod payouts;
#[cfg(feature = "payouts")]
pub use payouts::*;

// Error signature
type Error = Report<ConnectorError>;

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

impl TryFrom<&ConnectorAuthType> for AdyenplatformAuthType {
    type Error = Error;
    fn try_from(auth_type: &ConnectorAuthType) -> Result<Self, Self::Error> {
        match auth_type {
            ConnectorAuthType::HeaderKey { api_key } => Ok(Self {
                api_key: api_key.to_owned(),
            }),
            _ => Err(ConnectorError::FailedToObtainAuthType.into()),
        }
    }
}

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

impl<T> TryFrom<(MinorUnit, T)> for AdyenPlatformRouterData<T> {
    type Error = Report<ConnectorError>;
    fn try_from((amount, item): (MinorUnit, T)) -> Result<Self, Self::Error> {
        Ok(Self {
            amount,
            router_data: item,
        })
    }
}


OpenAPI Block Path: components.schemas.PriorityLogicData
{
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "nullable": true
    },
    "status": {
      "type": "string",
      "nullable": true
    },
    "failure_reason": {
      "type": "string",
      "nullable": true
    }
  }
}


impl super::routing_events::events::RoutingEventLogAnalytics for ClickhouseClient {}

impl super::routing_events::events::RoutingEventLogAnalytics for for ClickhouseClient


OpenAPI Block Path: components.schemas.PaymentsCreateResponseOpenApi
{
  "type": "object",
  "required": [
    "payment_id",
    "merchant_id",
    "status",
    "amount",
    "net_amount",
    "amount_capturable",
    "currency",
    "payment_method",
    "attempt_count"
  ],
  "properties": {
    "payment_id": {
      "type": "string",
      "description": "Unique identifier for the payment. This ensures idempotency for multiple payments\nthat have been done by a single merchant.",
      "example": "pay_mbabizu24mvu3mela5njyhpit4",
      "maxLength": 30,
      "minLength": 30
    },
    "merchant_id": {
      "type": "string",
      "description": "This is an identifier for the merchant account. This is inferred from the API key\nprovided during the request",
      "example": "merchant_1668273825",
      "maxLength": 255
    },
    "status": {
      "allOf": [
        {
          "$ref": "#/components/schemas/IntentStatus"
        }
      ],
      "default": "requires_confirmation"
    },
    "amount": {
      "type": "integer",
      "format": "int64",
      "description": "The payment amount. Amount for the payment in lowest denomination of the currency. (i.e) in cents for USD denomination, in paisa for INR denomination etc.,",
      "example": 6540
    },
    "net_amount": {
      "type": "integer",
      "format": "int64",
      "description": "The payment net amount. net_amount = amount + surcharge_details.surcharge_amount + surcharge_details.tax_amount + shipping_cost + order_tax_amount,\nIf no surcharge_details, shipping_cost, order_tax_amount, net_amount = amount",
      "example": 6540
    },
    "shipping_cost": {
      "type": "integer",
      "format": "int64",
      "description": "The shipping cost for the payment.",
      "example": 6540,
      "nullable": true
    },
    "amount_capturable": {
      "type": "integer",
      "format": "int64",
      "description": "The amount (in minor units) that can still be captured for this payment. This is relevant when `capture_method` is `manual`. Once fully captured, or if `capture_method` is `automatic` and payment succeeded, this will be 0.",
      "example": 6540,
      "minimum": 100
    },
    "amount_received": {
      "type": "integer",
      "format": "int64",
      "description": "The total amount (in minor units) that has been captured for this payment. For `fauxpay` sandbox connector, this might reflect the authorized amount if `status` is `succeeded` even if `capture_method` was `manual`.",
      "example": 6540,
      "nullable": true
    },
    "connector": {
      "type": "string",
      "description": "The name of the payment connector (e.g., 'stripe', 'adyen') that processed or is processing this payment.",
      "example": "stripe",
      "nullable": true
    },
    "client_secret": {
      "type": "string",
      "description": "A secret token unique to this payment intent. It is primarily used by client-side applications (e.g., Hyperswitch SDKs) to authenticate actions like confirming the payment or handling next actions. This secret should be handled carefully and not exposed publicly beyond its intended client-side use.",
      "example": "pay_U42c409qyHwOkWo3vK60_secret_el9ksDkiB8hi6j9N78yo",
      "nullable": true
    },
    "created": {
      "type": "string",
      "format": "date-time",
      "description": "Timestamp indicating when this payment intent was created, in ISO 8601 format.",
      "example": "2022-09-10T10:11:12Z",
      "nullable": true
    },
    "currency": {
      "$ref": "#/components/schemas/Currency"
    },
    "customer_id": {
      "type": "string",
      "description": "The identifier for the customer object. If not provided the customer ID will be autogenerated.\nThis field will be deprecated soon. Please refer to `customer.id`",
      "deprecated": true,
      "example": "cus_y3oqhf46pyzuxjbcn2giaqnb44",
      "nullable": true,
      "maxLength": 64,
      "minLength": 1
    },
    "description": {
      "type": "string",
      "description": "An arbitrary string providing a description for the payment, often useful for display or internal record-keeping.",
      "example": "It's my first payment request",
      "nullable": true
    },
    "refunds": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/RefundResponse"
      },
      "description": "An array of refund objects associated with this payment. Empty or null if no refunds have been processed.",
      "nullable": true
    },
    "disputes": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/DisputeResponsePaymentsRetrieve"
      },
      "description": "List of disputes that happened on this intent",
      "nullable": true
    },
    "attempts": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/PaymentAttemptResponse"
      },
      "description": "List of attempts that happened on this intent",
      "nullable": true
    },
    "captures": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/CaptureResponse"
      },
      "description": "List of captures done on latest attempt",
      "nullable": true
    },
    "mandate_id": {
      "type": "string",
      "description": "A unique identifier to link the payment to a mandate, can be used instead of payment_method_data, in case of setting up recurring payments",
      "example": "mandate_iwer89rnjef349dni3",
      "nullable": true,
      "maxLength": 255
    },
    "mandate_data": {
      "allOf": [
        {
          "$ref": "#/components/schemas/MandateData"
        }
      ],
      "nullable": true
    },
    "setup_future_usage": {
      "allOf": [
        {
          "$ref": "#/components/schemas/FutureUsage"
        }
      ],
      "nullable": true
    },
    "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. This parameter can only be used with confirm=true.",
      "example": true,
      "nullable": true
    },
    "capture_method": {
      "allOf": [
        {
          "$ref": "#/components/schemas/CaptureMethod"
        }
      ],
      "nullable": true
    },
    "payment_method": {
      "$ref": "#/components/schemas/PaymentMethod"
    },
    "payment_method_data": {
      "allOf": [
        {
          "$ref": "#/components/schemas/PaymentMethodDataResponseWithBilling"
        }
      ],
      "nullable": true
    },
    "payment_token": {
      "type": "string",
      "description": "Provide a reference to a stored payment method",
      "example": "187282ab-40ef-47a9-9206-5099ba31e432",
      "nullable": true
    },
    "shipping": {
      "allOf": [
        {
          "$ref": "#/components/schemas/Address"
        }
      ],
      "nullable": true
    },
    "billing": {
      "allOf": [
        {
          "$ref": "#/components/schemas/Address"
        }
      ],
      "nullable": true
    },
    "order_details": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/OrderDetailsWithAmount"
      },
      "description": "Information about the product , quantity and amount for connectors. (e.g. Klarna)",
      "example": "[{\n        \"product_name\": \"gillete creme\",\n        \"quantity\": 15,\n        \"amount\" : 900\n    }]",
      "nullable": true
    },
    "email": {
      "type": "string",
      "description": "description: The customer's email address\nThis field will be deprecated soon. Please refer to `customer.email` object",
      "deprecated": true,
      "example": "johntest@test.com",
      "nullable": true,
      "maxLength": 255
    },
    "name": {
      "type": "string",
      "description": "description: The customer's name\nThis field will be deprecated soon. Please refer to `customer.name` object",
      "deprecated": true,
      "example": "John Test",
      "nullable": true,
      "maxLength": 255
    },
    "phone": {
      "type": "string",
      "description": "The customer's phone number\nThis field will be deprecated soon. Please refer to `customer.phone` object",
      "deprecated": true,
      "example": "9123456789",
      "nullable": true,
      "maxLength": 255
    },
    "return_url": {
      "type": "string",
      "description": "The URL to redirect after the completion of the operation",
      "example": "https://hyperswitch.io",
      "nullable": true
    },
    "authentication_type": {
      "allOf": [
        {
          "$ref": "#/components/schemas/AuthenticationType"
        }
      ],
      "default": "three_ds",
      "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 255 characters for the concatenated descriptor.",
      "example": "Payment for shoes purchase",
      "nullable": true,
      "maxLength": 255
    },
    "next_action": {
      "allOf": [
        {
          "$ref": "#/components/schemas/NextActionData"
        }
      ],
      "nullable": true
    },
    "cancellation_reason": {
      "type": "string",
      "description": "If the payment intent was cancelled, this field provides a textual reason for the cancellation (e.g., \"requested_by_customer\", \"abandoned\").",
      "nullable": true
    },
    "error_code": {
      "type": "string",
      "description": "The connector-specific error code from the last failed payment attempt associated with this payment intent.",
      "example": "E0001",
      "nullable": true
    },
    "error_message": {
      "type": "string",
      "description": "A human-readable error message from the last failed payment attempt associated with this payment intent.",
      "example": "Failed while verifying the card",
      "nullable": true
    },
    "payment_experience": {
      "allOf": [
        {
          "$ref": "#/components/schemas/PaymentExperience"
        }
      ],
      "nullable": true
    },
    "payment_method_type": {
      "allOf": [
        {
          "$ref": "#/components/schemas/PaymentMethodType"
        }
      ],
      "nullable": true
    },
    "connector_label": {
      "type": "string",
      "description": "A label identifying the specific merchant connector account (MCA) used for this payment. This often combines the connector name, business country, and a custom label (e.g., \"stripe_US_primary\").",
      "example": "stripe_US_food",
      "nullable": true
    },
    "business_country": {
      "allOf": [
        {
          "$ref": "#/components/schemas/CountryAlpha2"
        }
      ],
      "nullable": true
    },
    "business_label": {
      "type": "string",
      "description": "The label identifying the specific business unit or profile under which this payment was processed by the merchant.",
      "nullable": true
    },
    "business_sub_label": {
      "type": "string",
      "description": "An optional sub-label for further categorization of the business unit or profile used for this payment.",
      "nullable": true
    },
    "allowed_payment_method_types": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/PaymentMethodType"
      },
      "description": "Allowed Payment Method Types for a given PaymentIntent",
      "nullable": true
    },
    "ephemeral_key": {
      "allOf": [
        {
          "$ref": "#/components/schemas/EphemeralKeyCreateResponse"
        }
      ],
      "nullable": true
    },
    "manual_retry_allowed": {
      "type": "boolean",
      "description": "If true the payment can be retried with same or different payment method which means the confirm call can be made again.",
      "nullable": true
    },
    "connector_transaction_id": {
      "type": "string",
      "description": "A unique identifier for a payment provided by the connector",
      "example": "993672945374576J",
      "nullable": true
    },
    "frm_message": {
      "allOf": [
        {
          "$ref": "#/components/schemas/FrmMessage"
        }
      ],
      "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
    },
    "feature_metadata": {
      "allOf": [
        {
          "$ref": "#/components/schemas/FeatureMetadata"
        }
      ],
      "nullable": true
    },
    "reference_id": {
      "type": "string",
      "description": "reference(Identifier) to the payment at connector side",
      "example": "993672945374576J",
      "nullable": true
    },
    "payment_link": {
      "allOf": [
        {
          "$ref": "#/components/schemas/PaymentLinkResponse"
        }
      ],
      "nullable": true
    },
    "profile_id": {
      "type": "string",
      "description": "The business profile that is associated with this payment",
      "nullable": true
    },
    "surcharge_details": {
      "allOf": [
        {
          "$ref": "#/components/schemas/RequestSurchargeDetails"
        }
      ],
      "nullable": true
    },
    "attempt_count": {
      "type": "integer",
      "format": "int32",
      "description": "Total number of attempts associated with this payment"
    },
    "merchant_decision": {
      "type": "string",
      "description": "Denotes the action(approve or reject) taken by merchant in case of manual review. Manual review can occur when the transaction is marked as risky by the frm_processor, payment processor or when there is underpayment/over payment incase of crypto payment",
      "nullable": true
    },
    "merchant_connector_id": {
      "type": "string",
      "description": "Identifier of the connector ( merchant connector account ) which was chosen to make the payment",
      "nullable": true
    },
    "incremental_authorization_allowed": {
      "type": "boolean",
      "description": "If true, incremental authorization can be performed on this payment, in case the funds authorized initially fall short.",
      "nullable": true
    },
    "authorization_count": {
      "type": "integer",
      "format": "int32",
      "description": "Total number of authorizations happened in an incremental_authorization payment",
      "nullable": true
    },
    "incremental_authorizations": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/IncrementalAuthorizationResponse"
      },
      "description": "List of incremental authorizations happened to the payment",
      "nullable": true
    },
    "external_authentication_details": {
      "allOf": [
        {
          "$ref": "#/components/schemas/ExternalAuthenticationDetailsResponse"
        }
      ],
      "nullable": true
    },
    "external_3ds_authentication_attempted": {
      "type": "boolean",
      "description": "Flag indicating if external 3ds authentication is made or not",
      "nullable": true
    },
    "expires_on": {
      "type": "string",
      "format": "date-time",
      "description": "Date Time for expiry of the payment",
      "example": "2022-09-10T10:11:12Z",
      "nullable": true
    },
    "fingerprint": {
      "type": "string",
      "description": "Payment Fingerprint, to identify a particular card.\nIt is a 20 character long alphanumeric code.",
      "nullable": true
    },
    "browser_info": {
      "allOf": [
        {
          "$ref": "#/components/schemas/BrowserInformation"
        }
      ],
      "nullable": true
    },
    "payment_channel": {
      "allOf": [
        {
          "$ref": "#/components/schemas/PaymentChannel"
        }
      ],
      "nullable": true
    },
    "payment_method_id": {
      "type": "string",
      "description": "A unique identifier for the payment method used in this payment. If the payment method was saved or tokenized, this ID can be used to reference it for future transactions or recurring payments.",
      "nullable": true
    },
    "network_transaction_id": {
      "type": "string",
      "description": "The network transaction ID is a unique identifier for the transaction as recognized by the payment network (e.g., Visa, Mastercard), this ID can be used to reference it for future transactions or recurring payments.",
      "nullable": true
    },
    "payment_method_status": {
      "allOf": [
        {
          "$ref": "#/components/schemas/PaymentMethodStatus"
        }
      ],
      "nullable": true
    },
    "updated": {
      "type": "string",
      "format": "date-time",
      "description": "Date time at which payment was updated",
      "example": "2022-09-10T10:11:12Z",
      "nullable": true
    },
    "split_payments": {
      "allOf": [
        {
          "$ref": "#/components/schemas/ConnectorChargeResponseData"
        }
      ],
      "nullable": true
    },
    "frm_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. FRM Metadata is useful for storing additional, structured information on an object related to FRM.",
      "nullable": true
    },
    "extended_authorization_applied": {
      "type": "boolean",
      "description": "flag that indicates if extended authorization is applied on this payment or not",
      "nullable": true
    },
    "capture_before": {
      "type": "string",
      "format": "date-time",
      "description": "date and time after which this payment cannot be captured",
      "nullable": true
    },
    "merchant_order_reference_id": {
      "type": "string",
      "description": "Merchant's identifier for the payment/invoice. This will be sent to the connector\nif the connector provides support to accept multiple reference ids.\nIn case the connector supports only one reference id, Hyperswitch's Payment ID will be sent as reference.",
      "example": "Custom_Order_id_123",
      "nullable": true,
      "maxLength": 255
    },
    "order_tax_amount": {
      "allOf": [
        {
          "$ref": "#/components/schemas/MinorUnit"
        }
      ],
      "nullable": true
    },
    "connector_mandate_id": {
      "type": "string",
      "description": "Connector Identifier for the payment method",
      "nullable": true
    },
    "card_discovery": {
      "allOf": [
        {
          "$ref": "#/components/schemas/CardDiscovery"
        }
      ],
      "nullable": true
    },
    "force_3ds_challenge": {
      "type": "boolean",
      "description": "Indicates if 3ds challenge is forced",
      "nullable": true
    },
    "force_3ds_challenge_trigger": {
      "type": "boolean",
      "description": "Indicates if 3ds challenge is triggered",
      "nullable": true
    },
    "issuer_error_code": {
      "type": "string",
      "description": "Error code received from the issuer in case of failed payments",
      "nullable": true
    },
    "issuer_error_message": {
      "type": "string",
      "description": "Error message received from the issuer in case of failed payments",
      "nullable": true
    },
    "is_iframe_redirection_enabled": {
      "type": "boolean",
      "description": "Indicates if the redirection has to open in the iframe",
      "nullable": true
    },
    "whole_connector_response": {
      "type": "string",
      "description": "Contains whole connector response",
      "nullable": true
    },
    "enable_partial_authorization": {
      "type": "boolean",
      "description": "Allow partial authorization for this payment",
      "nullable": true
    }
  }
}


pub struct PaymentMetricsBucketIdentifier {
    pub currency: Option<Currency>,
    pub status: Option<AttemptStatus>,
    pub connector: Option<String>,
    #[serde(rename = "authentication_type")]
    pub auth_type: Option<AuthenticationType>,
    pub payment_method: Option<String>,
    pub payment_method_type: Option<String>,
    pub client_source: Option<String>,
    pub client_version: Option<String>,
    pub profile_id: Option<String>,
    pub card_network: Option<String>,
    pub merchant_id: Option<String>,
    pub card_last_4: Option<String>,
    pub card_issuer: Option<String>,
    pub error_reason: Option<String>,
    pub routing_approach: Option<RoutingApproach>,
    pub signature_network: Option<String>,
    pub is_issuer_regulated: Option<bool>,
    pub is_debit_routed: Option<bool>,
    #[serde(rename = "time_range")]
    pub time_bucket: TimeRange,
    // Coz FE sucks
    #[serde(rename = "time_bucket")]
    #[serde(with = "common_utils::custom_serde::iso8601custom")]
    pub start_time: time::PrimitiveDateTime,
}


OpenAPI Block Path: components.schemas.CardNetworkTokenizeRequest
{
  "allOf": [
    {
      "$ref": "#/components/schemas/TokenizeDataRequest"
    },
    {
      "type": "object",
      "required": [
        "merchant_id",
        "customer"
      ],
      "properties": {
        "merchant_id": {
          "type": "string",
          "description": "Merchant ID associated with the tokenization request",
          "example": "merchant_1671528864"
        },
        "customer": {
          "$ref": "#/components/schemas/CustomerDetails"
        },
        "billing": {
          "allOf": [
            {
              "$ref": "#/components/schemas/Address"
            }
          ],
          "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
        },
        "payment_method_issuer": {
          "type": "string",
          "description": "The name of the bank/ provider issuing the payment method to the end user",
          "nullable": true
        }
      }
    }
  ]
}


OpenAPI Block Path: components.schemas.PazeSessionTokenResponse
{
  "type": "object",
  "required": [
    "client_id",
    "client_name",
    "client_profile_id",
    "transaction_currency_code",
    "transaction_amount"
  ],
  "properties": {
    "client_id": {
      "type": "string",
      "description": "Paze Client ID"
    },
    "client_name": {
      "type": "string",
      "description": "Client Name to be displayed on the Paze screen"
    },
    "client_profile_id": {
      "type": "string",
      "description": "Paze Client Profile ID"
    },
    "transaction_currency_code": {
      "$ref": "#/components/schemas/Currency"
    },
    "transaction_amount": {
      "type": "string",
      "description": "The transaction amount",
      "example": "38.02"
    },
    "email_address": {
      "type": "string",
      "description": "Email Address",
      "example": "johntest@test.com",
      "nullable": true,
      "maxLength": 255
    }
  }
}


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

#[cfg(feature = "olap")]
use std::collections::HashMap;

use common_utils::{ext_traits::AsyncExt, types::keymanager::KeyManagerState};
use diesel_models::MerchantAccountUpdateInternal;
use error_stack::{report, ResultExt};
use router_env::{instrument, tracing};
#[cfg(feature = "accounts_cache")]
use storage_impl::redis::cache::{self, CacheKind, ACCOUNTS_CACHE};

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

#[async_trait::async_trait]
pub trait MerchantAccountInterface
where
    domain::MerchantAccount:
        Conversion<DstType = storage::MerchantAccount, NewDstType = storage::MerchantAccountNew>,
{
    async fn insert_merchant(
        &self,
        state: &KeyManagerState,
        merchant_account: domain::MerchantAccount,
        merchant_key_store: &domain::MerchantKeyStore,
    ) -> CustomResult<domain::MerchantAccount, errors::StorageError>;

    async fn find_merchant_account_by_merchant_id(
        &self,
        state: &KeyManagerState,
        merchant_id: &common_utils::id_type::MerchantId,
        merchant_key_store: &domain::MerchantKeyStore,
    ) -> CustomResult<domain::MerchantAccount, errors::StorageError>;

    async fn update_all_merchant_account(
        &self,
        merchant_account: storage::MerchantAccountUpdate,
    ) -> CustomResult<usize, errors::StorageError>;

    async fn update_merchant(
        &self,
        state: &KeyManagerState,
        this: domain::MerchantAccount,
        merchant_account: storage::MerchantAccountUpdate,
        merchant_key_store: &domain::MerchantKeyStore,
    ) -> CustomResult<domain::MerchantAccount, errors::StorageError>;

    async fn update_specific_fields_in_merchant(
        &self,
        state: &KeyManagerState,
        merchant_id: &common_utils::id_type::MerchantId,
        merchant_account: storage::MerchantAccountUpdate,
        merchant_key_store: &domain::MerchantKeyStore,
    ) -> CustomResult<domain::MerchantAccount, errors::StorageError>;

    async fn find_merchant_account_by_publishable_key(
        &self,
        state: &KeyManagerState,
        publishable_key: &str,
    ) -> CustomResult<(domain::MerchantAccount, domain::MerchantKeyStore), errors::StorageError>;

    #[cfg(feature = "olap")]
    async fn list_merchant_accounts_by_organization_id(
        &self,
        state: &KeyManagerState,
        organization_id: &common_utils::id_type::OrganizationId,
    ) -> CustomResult<Vec<domain::MerchantAccount>, errors::StorageError>;

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

    #[cfg(feature = "olap")]
    async fn list_multiple_merchant_accounts(
        &self,
        state: &KeyManagerState,
        merchant_ids: Vec<common_utils::id_type::MerchantId>,
    ) -> CustomResult<Vec<domain::MerchantAccount>, errors::StorageError>;

    #[cfg(feature = "olap")]
    async fn list_merchant_and_org_ids(
        &self,
        state: &KeyManagerState,
        limit: u32,
        offset: Option<u32>,
    ) -> CustomResult<
        Vec<(
            common_utils::id_type::MerchantId,
            common_utils::id_type::OrganizationId,
        )>,
        errors::StorageError,
    >;
}

#[async_trait::async_trait]
impl MerchantAccountInterface for Store {
    #[instrument(skip_all)]
    async fn insert_merchant(
        &self,
        state: &KeyManagerState,
        merchant_account: domain::MerchantAccount,
        merchant_key_store: &domain::MerchantKeyStore,
    ) -> CustomResult<domain::MerchantAccount, errors::StorageError> {
        let conn = connection::pg_accounts_connection_write(self).await?;
        merchant_account
            .construct_new()
            .await
            .change_context(errors::StorageError::EncryptionError)?
            .insert(&conn)
            .await
            .map_err(|error| report!(errors::StorageError::from(error)))?
            .convert(
                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_merchant_account_by_merchant_id(
        &self,
        state: &KeyManagerState,
        merchant_id: &common_utils::id_type::MerchantId,
        merchant_key_store: &domain::MerchantKeyStore,
    ) -> CustomResult<domain::MerchantAccount, errors::StorageError> {
        let fetch_func = || async {
            let conn = connection::pg_accounts_connection_read(self).await?;
            storage::MerchantAccount::find_by_merchant_id(&conn, merchant_id)
                .await
                .map_err(|error| report!(errors::StorageError::from(error)))
        };

        #[cfg(not(feature = "accounts_cache"))]
        {
            fetch_func()
                .await?
                .convert(
                    state,
                    merchant_key_store.key.get_inner(),
                    merchant_id.to_owned().into(),
                )
                .await
                .change_context(errors::StorageError::DecryptionError)
        }

        #[cfg(feature = "accounts_cache")]
        {
            cache::get_or_populate_in_memory(
                self,
                merchant_id.get_string_repr(),
                fetch_func,
                &ACCOUNTS_CACHE,
            )
            .await?
            .convert(
                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_merchant(
        &self,
        state: &KeyManagerState,
        this: domain::MerchantAccount,
        merchant_account: storage::MerchantAccountUpdate,
        merchant_key_store: &domain::MerchantKeyStore,
    ) -> CustomResult<domain::MerchantAccount, errors::StorageError> {
        let conn = connection::pg_accounts_connection_write(self).await?;

        let updated_merchant_account = Conversion::convert(this)
            .await
            .change_context(errors::StorageError::EncryptionError)?
            .update(&conn, merchant_account.into())
            .await
            .map_err(|error| report!(errors::StorageError::from(error)))?;

        #[cfg(feature = "accounts_cache")]
        {
            publish_and_redact_merchant_account_cache(self, &updated_merchant_account).await?;
        }
        updated_merchant_account
            .convert(
                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_specific_fields_in_merchant(
        &self,
        state: &KeyManagerState,
        merchant_id: &common_utils::id_type::MerchantId,
        merchant_account: storage::MerchantAccountUpdate,
        merchant_key_store: &domain::MerchantKeyStore,
    ) -> CustomResult<domain::MerchantAccount, errors::StorageError> {
        let conn = connection::pg_accounts_connection_write(self).await?;
        let updated_merchant_account = storage::MerchantAccount::update_with_specific_fields(
            &conn,
            merchant_id,
            merchant_account.into(),
        )
        .await
        .map_err(|error| report!(errors::StorageError::from(error)))?;

        #[cfg(feature = "accounts_cache")]
        {
            publish_and_redact_merchant_account_cache(self, &updated_merchant_account).await?;
        }
        updated_merchant_account
            .convert(
                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_merchant_account_by_publishable_key(
        &self,
        state: &KeyManagerState,
        publishable_key: &str,
    ) -> CustomResult<(domain::MerchantAccount, domain::MerchantKeyStore), errors::StorageError>
    {
        let fetch_by_pub_key_func = || async {
            let conn = connection::pg_accounts_connection_read(self).await?;

            storage::MerchantAccount::find_by_publishable_key(&conn, publishable_key)
                .await
                .map_err(|error| report!(errors::StorageError::from(error)))
        };

        let merchant_account;
        #[cfg(not(feature = "accounts_cache"))]
        {
            merchant_account = fetch_by_pub_key_func().await?;
        }

        #[cfg(feature = "accounts_cache")]
        {
            merchant_account = cache::get_or_populate_in_memory(
                self,
                publishable_key,
                fetch_by_pub_key_func,
                &ACCOUNTS_CACHE,
            )
            .await?;
        }
        let key_store = self
            .get_merchant_key_store_by_merchant_id(
                state,
                merchant_account.get_id(),
                &self.get_master_key().to_vec().into(),
            )
            .await?;
        let domain_merchant_account = merchant_account
            .convert(
                state,
                key_store.key.get_inner(),
                key_store.merchant_id.clone().into(),
            )
            .await
            .change_context(errors::StorageError::DecryptionError)?;
        Ok((domain_merchant_account, key_store))
    }

    #[cfg(feature = "olap")]
    #[instrument(skip_all)]
    async fn list_merchant_accounts_by_organization_id(
        &self,
        state: &KeyManagerState,
        organization_id: &common_utils::id_type::OrganizationId,
    ) -> CustomResult<Vec<domain::MerchantAccount>, errors::StorageError> {
        use futures::future::try_join_all;
        let conn = connection::pg_accounts_connection_read(self).await?;

        let encrypted_merchant_accounts =
            storage::MerchantAccount::list_by_organization_id(&conn, organization_id)
                .await
                .map_err(|error| report!(errors::StorageError::from(error)))?;

        let db_master_key = self.get_master_key().to_vec().into();

        let merchant_key_stores =
            try_join_all(encrypted_merchant_accounts.iter().map(|merchant_account| {
                self.get_merchant_key_store_by_merchant_id(
                    state,
                    merchant_account.get_id(),
                    &db_master_key,
                )
            }))
            .await?;

        let merchant_accounts = try_join_all(
            encrypted_merchant_accounts
                .into_iter()
                .zip(merchant_key_stores.iter())
                .map(|(merchant_account, key_store)| async {
                    merchant_account
                        .convert(
                            state,
                            key_store.key.get_inner(),
                            key_store.merchant_id.clone().into(),
                        )
                        .await
                        .change_context(errors::StorageError::DecryptionError)
                }),
        )
        .await?;

        Ok(merchant_accounts)
    }

    #[instrument(skip_all)]
    async fn delete_merchant_account_by_merchant_id(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
    ) -> CustomResult<bool, errors::StorageError> {
        let conn = connection::pg_accounts_connection_write(self).await?;

        let is_deleted_func = || async {
            storage::MerchantAccount::delete_by_merchant_id(&conn, merchant_id)
                .await
                .map_err(|error| report!(errors::StorageError::from(error)))
        };

        let is_deleted;

        #[cfg(not(feature = "accounts_cache"))]
        {
            is_deleted = is_deleted_func().await?;
        }

        #[cfg(feature = "accounts_cache")]
        {
            let merchant_account =
                storage::MerchantAccount::find_by_merchant_id(&conn, merchant_id)
                    .await
                    .map_err(|error| report!(errors::StorageError::from(error)))?;

            is_deleted = is_deleted_func().await?;

            publish_and_redact_merchant_account_cache(self, &merchant_account).await?;
        }

        Ok(is_deleted)
    }

    #[cfg(feature = "olap")]
    #[instrument(skip_all)]
    async fn list_multiple_merchant_accounts(
        &self,
        state: &KeyManagerState,
        merchant_ids: Vec<common_utils::id_type::MerchantId>,
    ) -> CustomResult<Vec<domain::MerchantAccount>, errors::StorageError> {
        let conn = connection::pg_accounts_connection_read(self).await?;

        let encrypted_merchant_accounts =
            storage::MerchantAccount::list_multiple_merchant_accounts(&conn, merchant_ids)
                .await
                .map_err(|error| report!(errors::StorageError::from(error)))?;

        let db_master_key = self.get_master_key().to_vec().into();

        let merchant_key_stores = self
            .list_multiple_key_stores(
                state,
                encrypted_merchant_accounts
                    .iter()
                    .map(|merchant_account| merchant_account.get_id())
                    .cloned()
                    .collect(),
                &db_master_key,
            )
            .await?;

        let key_stores_by_id: HashMap<_, _> = merchant_key_stores
            .iter()
            .map(|key_store| (key_store.merchant_id.to_owned(), key_store))
            .collect();

        let merchant_accounts =
            futures::future::try_join_all(encrypted_merchant_accounts.into_iter().map(
                |merchant_account| async {
                    let key_store = key_stores_by_id.get(merchant_account.get_id()).ok_or(
                        errors::StorageError::ValueNotFound(format!(
                            "merchant_key_store with merchant_id = {:?}",
                            merchant_account.get_id()
                        )),
                    )?;
                    merchant_account
                        .convert(
                            state,
                            key_store.key.get_inner(),
                            key_store.merchant_id.clone().into(),
                        )
                        .await
                        .change_context(errors::StorageError::DecryptionError)
                },
            ))
            .await?;

        Ok(merchant_accounts)
    }

    #[cfg(feature = "olap")]
    #[instrument(skip_all)]
    async fn list_merchant_and_org_ids(
        &self,
        _state: &KeyManagerState,
        limit: u32,
        offset: Option<u32>,
    ) -> CustomResult<
        Vec<(
            common_utils::id_type::MerchantId,
            common_utils::id_type::OrganizationId,
        )>,
        errors::StorageError,
    > {
        let conn = connection::pg_accounts_connection_read(self).await?;
        let encrypted_merchant_accounts =
            storage::MerchantAccount::list_all_merchant_accounts(&conn, limit, offset)
                .await
                .map_err(|error| report!(errors::StorageError::from(error)))?;

        let merchant_and_org_ids = encrypted_merchant_accounts
            .into_iter()
            .map(|merchant_account| {
                let merchant_id = merchant_account.get_id().clone();
                let org_id = merchant_account.organization_id;
                (merchant_id, org_id)
            })
            .collect();
        Ok(merchant_and_org_ids)
    }

    async fn update_all_merchant_account(
        &self,
        merchant_account: storage::MerchantAccountUpdate,
    ) -> CustomResult<usize, errors::StorageError> {
        let conn = connection::pg_accounts_connection_read(self).await?;

        let db_func = || async {
            storage::MerchantAccount::update_all_merchant_accounts(
                &conn,
                MerchantAccountUpdateInternal::from(merchant_account),
            )
            .await
            .map_err(|error| report!(errors::StorageError::from(error)))
        };

        let total;
        #[cfg(not(feature = "accounts_cache"))]
        {
            let ma = db_func().await?;
            total = ma.len();
        }

        #[cfg(feature = "accounts_cache")]
        {
            let ma = db_func().await?;
            publish_and_redact_all_merchant_account_cache(self, &ma).await?;
            total = ma.len();
        }

        Ok(total)
    }
}

#[async_trait::async_trait]
impl MerchantAccountInterface for MockDb {
    #[allow(clippy::panic)]
    async fn insert_merchant(
        &self,
        state: &KeyManagerState,
        merchant_account: domain::MerchantAccount,
        merchant_key_store: &domain::MerchantKeyStore,
    ) -> CustomResult<domain::MerchantAccount, errors::StorageError> {
        let mut accounts = self.merchant_accounts.lock().await;
        let account = Conversion::convert(merchant_account)
            .await
            .change_context(errors::StorageError::EncryptionError)?;
        accounts.push(account.clone());

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

    #[allow(clippy::panic)]
    async fn find_merchant_account_by_merchant_id(
        &self,
        state: &KeyManagerState,
        merchant_id: &common_utils::id_type::MerchantId,
        merchant_key_store: &domain::MerchantKeyStore,
    ) -> CustomResult<domain::MerchantAccount, errors::StorageError> {
        let accounts = self.merchant_accounts.lock().await;
        accounts
            .iter()
            .find(|account| account.get_id() == merchant_id)
            .cloned()
            .ok_or(errors::StorageError::ValueNotFound(format!(
                "Merchant ID: {merchant_id:?} not found",
            )))?
            .convert(
                state,
                merchant_key_store.key.get_inner(),
                merchant_key_store.merchant_id.clone().into(),
            )
            .await
            .change_context(errors::StorageError::DecryptionError)
    }

    async fn update_merchant(
        &self,
        state: &KeyManagerState,
        merchant_account: domain::MerchantAccount,
        merchant_account_update: storage::MerchantAccountUpdate,
        merchant_key_store: &domain::MerchantKeyStore,
    ) -> CustomResult<domain::MerchantAccount, errors::StorageError> {
        let merchant_id = merchant_account.get_id().to_owned();
        let mut accounts = self.merchant_accounts.lock().await;
        accounts
            .iter_mut()
            .find(|account| account.get_id() == merchant_account.get_id())
            .async_map(|account| async {
                let update = MerchantAccountUpdateInternal::from(merchant_account_update)
                    .apply_changeset(
                        Conversion::convert(merchant_account)
                            .await
                            .change_context(errors::StorageError::EncryptionError)?,
                    );
                *account = update.clone();
                update
                    .convert(
                        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!(
                    "Merchant ID: {merchant_id:?} not found",
                ))
                .into(),
            )
    }

    async fn update_specific_fields_in_merchant(
        &self,
        state: &KeyManagerState,
        merchant_id: &common_utils::id_type::MerchantId,
        merchant_account_update: storage::MerchantAccountUpdate,
        merchant_key_store: &domain::MerchantKeyStore,
    ) -> CustomResult<domain::MerchantAccount, errors::StorageError> {
        let mut accounts = self.merchant_accounts.lock().await;
        accounts
            .iter_mut()
            .find(|account| account.get_id() == merchant_id)
            .async_map(|account| async {
                let update = MerchantAccountUpdateInternal::from(merchant_account_update)
                    .apply_changeset(account.clone());
                *account = update.clone();
                update
                    .convert(
                        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!(
                    "Merchant ID: {merchant_id:?} not found",
                ))
                .into(),
            )
    }

    async fn find_merchant_account_by_publishable_key(
        &self,
        state: &KeyManagerState,
        publishable_key: &str,
    ) -> CustomResult<(domain::MerchantAccount, domain::MerchantKeyStore), errors::StorageError>
    {
        let accounts = self.merchant_accounts.lock().await;
        let account = accounts
            .iter()
            .find(|account| {
                account
                    .publishable_key
                    .as_ref()
                    .is_some_and(|key| key == publishable_key)
            })
            .ok_or(errors::StorageError::ValueNotFound(format!(
                "Publishable Key: {publishable_key} not found",
            )))?;
        let key_store = self
            .get_merchant_key_store_by_merchant_id(
                state,
                account.get_id(),
                &self.get_master_key().to_vec().into(),
            )
            .await?;
        let merchant_account = account
            .clone()
            .convert(
                state,
                key_store.key.get_inner(),
                key_store.merchant_id.clone().into(),
            )
            .await
            .change_context(errors::StorageError::DecryptionError)?;
        Ok((merchant_account, key_store))
    }

    async fn update_all_merchant_account(
        &self,
        merchant_account_update: storage::MerchantAccountUpdate,
    ) -> CustomResult<usize, errors::StorageError> {
        let mut accounts = self.merchant_accounts.lock().await;
        Ok(accounts.iter_mut().fold(0, |acc, account| {
            let update = MerchantAccountUpdateInternal::from(merchant_account_update.clone())
                .apply_changeset(account.clone());
            *account = update;
            acc + 1
        }))
    }

    async fn delete_merchant_account_by_merchant_id(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
    ) -> CustomResult<bool, errors::StorageError> {
        let mut accounts = self.merchant_accounts.lock().await;
        accounts.retain(|x| x.get_id() != merchant_id);
        Ok(true)
    }

    #[cfg(feature = "olap")]
    async fn list_merchant_accounts_by_organization_id(
        &self,
        state: &KeyManagerState,
        organization_id: &common_utils::id_type::OrganizationId,
    ) -> CustomResult<Vec<domain::MerchantAccount>, errors::StorageError> {
        let accounts = self.merchant_accounts.lock().await;
        let futures = accounts
            .iter()
            .filter(|account| account.organization_id == *organization_id)
            .map(|account| async {
                let key_store = self
                    .get_merchant_key_store_by_merchant_id(
                        state,
                        account.get_id(),
                        &self.get_master_key().to_vec().into(),
                    )
                    .await;
                match key_store {
                    Ok(key) => account
                        .clone()
                        .convert(state, key.key.get_inner(), key.merchant_id.clone().into())
                        .await
                        .change_context(errors::StorageError::DecryptionError),
                    Err(err) => Err(err),
                }
            });
        futures::future::join_all(futures)
            .await
            .into_iter()
            .collect()
    }

    #[cfg(feature = "olap")]
    async fn list_multiple_merchant_accounts(
        &self,
        state: &KeyManagerState,
        merchant_ids: Vec<common_utils::id_type::MerchantId>,
    ) -> CustomResult<Vec<domain::MerchantAccount>, errors::StorageError> {
        let accounts = self.merchant_accounts.lock().await;
        let futures = accounts
            .iter()
            .filter(|account| merchant_ids.contains(account.get_id()))
            .map(|account| async {
                let key_store = self
                    .get_merchant_key_store_by_merchant_id(
                        state,
                        account.get_id(),
                        &self.get_master_key().to_vec().into(),
                    )
                    .await;
                match key_store {
                    Ok(key) => account
                        .clone()
                        .convert(state, key.key.get_inner(), key.merchant_id.clone().into())
                        .await
                        .change_context(errors::StorageError::DecryptionError),
                    Err(err) => Err(err),
                }
            });
        futures::future::join_all(futures)
            .await
            .into_iter()
            .collect()
    }

    #[cfg(feature = "olap")]
    async fn list_merchant_and_org_ids(
        &self,
        _state: &KeyManagerState,
        limit: u32,
        offset: Option<u32>,
    ) -> CustomResult<
        Vec<(
            common_utils::id_type::MerchantId,
            common_utils::id_type::OrganizationId,
        )>,
        errors::StorageError,
    > {
        let accounts = self.merchant_accounts.lock().await;
        let limit = limit.try_into().unwrap_or(accounts.len());
        let offset = offset.unwrap_or(0).try_into().unwrap_or(0);

        let merchant_and_org_ids = accounts
            .iter()
            .skip(offset)
            .take(limit)
            .map(|account| (account.get_id().clone(), account.organization_id.clone()))
            .collect::<Vec<_>>();

        Ok(merchant_and_org_ids)
    }
}

#[cfg(feature = "accounts_cache")]
async fn publish_and_redact_merchant_account_cache(
    store: &dyn super::StorageInterface,
    merchant_account: &storage::MerchantAccount,
) -> CustomResult<(), errors::StorageError> {
    let publishable_key = merchant_account
        .publishable_key
        .as_ref()
        .map(|publishable_key| CacheKind::Accounts(publishable_key.into()));

    #[cfg(feature = "v1")]
    let cgraph_key = merchant_account.default_profile.as_ref().map(|profile_id| {
        CacheKind::CGraph(
            format!(
                "cgraph_{}_{}",
                merchant_account.get_id().get_string_repr(),
                profile_id.get_string_repr(),
            )
            .into(),
        )
    });

    // TODO: we will not have default profile in v2
    #[cfg(feature = "v2")]
    let cgraph_key = None;

    let mut cache_keys = vec![CacheKind::Accounts(
        merchant_account.get_id().get_string_repr().into(),
    )];

    cache_keys.extend(publishable_key.into_iter());
    cache_keys.extend(cgraph_key.into_iter());

    cache::redact_from_redis_and_publish(store.get_cache_store().as_ref(), cache_keys).await?;
    Ok(())
}

#[cfg(feature = "accounts_cache")]
async fn publish_and_redact_all_merchant_account_cache(
    store: &dyn super::StorageInterface,
    merchant_accounts: &[storage::MerchantAccount],
) -> CustomResult<(), errors::StorageError> {
    let merchant_ids = merchant_accounts
        .iter()
        .map(|merchant_account| merchant_account.get_id().get_string_repr().to_string());
    let publishable_keys = merchant_accounts
        .iter()
        .filter_map(|m| m.publishable_key.clone());

    let cache_keys: Vec<CacheKind<'_>> = merchant_ids
        .chain(publishable_keys)
        .map(|s| CacheKind::Accounts(s.into()))
        .collect();

    cache::redact_from_redis_and_publish(store.get_cache_store().as_ref(), cache_keys).await?;
    Ok(())
}


/// Uses the [`Serialize`] implementation of a type to derive a function implementation
/// for converting nested keys structure into a HashMap of key, value where key is in
/// the flattened form.
///
/// Example
///
/// ```
/// #[derive(Default, Serialize, FlatStruct)]
/// pub struct User {
///     name: String,
///     address: Address,
///     email: String,
/// }
///
/// #[derive(Default, Serialize)]
/// pub struct Address {
///     line1: String,
///     line2: String,
///     zip: String,
/// }
///
/// let user = User::default();
/// let flat_struct_map = user.flat_struct();
///
/// [
///     ("name", "Test"),
///     ("address.line1", "1397"),
///     ("address.line2", "Some street"),
///     ("address.zip", "941222"),
///     ("email", "test@example.com"),
/// ]
/// ```
#[proc_macro_derive(FlatStruct)]
pub fn flat_struct_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let input = parse_macro_input!(input as syn::DeriveInput);
    let name = &input.ident;

    let expanded = quote::quote! {
        impl #name {
            pub fn flat_struct(&self) -> std::collections::HashMap<String, String> {
                use serde_json::Value;
                use std::collections::HashMap;

                fn flatten_value(
                    value: &Value,
                    prefix: &str,
                    result: &mut HashMap<String, String>
                ) {
                    match value {
                        Value::Object(map) => {
                            for (key, val) in map {
                                let new_key = if prefix.is_empty() {
                                    key.to_string()
                                } else {
                                    format!("{}.{}", prefix, key)
                                };
                                flatten_value(val, &new_key, result);
                            }
                        }
                        Value::String(s) => {
                            result.insert(prefix.to_string(), s.clone());
                        }
                        Value::Number(n) => {
                            result.insert(prefix.to_string(), n.to_string());
                        }
                        Value::Bool(b) => {
                            result.insert(prefix.to_string(), b.to_string());
                        }
                        _ => {}
                    }
                }

                let mut result = HashMap::new();
                let value = serde_json::to_value(self).unwrap();
                flatten_value(&value, "", &mut result);
                result
            }
        }
    };

    proc_macro::TokenStream::from(expanded)
}


impl api::PaymentVoid for Forte {}


pub fn get_headers(&self) -> Vec<(String, masking::Maskable<String>)> {
        self.0
            .headers
            .as_map()
            .iter()
            .map(|(key, value)| (key.clone(), value.clone().into_masked()))
            .collect()
    }


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))
}


pub struct Card {
    number: cards::CardNumber,
    expiry_month: Secret<String>,
    expiry_year: Secret<String>,
    cvc: Secret<String>,
    holder_name: Option<Secret<String>>,
}


pub struct DwollaRouterData<'a, T> {
    pub amount: StringMajorUnit,
    pub router_data: T,
    pub base_url: &'a str,
}


impl FromStr for MerchantConnectorAccountId {
    type Err = std::fmt::Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Self::try_from(std::borrow::Cow::Owned(s.to_string())).map_err(|_| std::fmt::Error)
    }
}

impl FromStr for for MerchantConnectorAccountId


pub async fn get_and_deserialize_key<T>(
    db: &dyn StorageInterface,
    key: &str,
    type_name: &'static str,
) -> CustomResult<T, RedisError>
where
    T: serde::de::DeserializeOwned,
{
    use common_utils::ext_traits::ByteSliceExt;

    let bytes = db.get_key(key).await?;
    bytes
        .parse_struct(type_name)
        .change_context(RedisError::JsonDeserializationFailed)
}