OpenAPI Block Path: components.schemas.PaymentsResponse
{
  "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
    },
    "customer": {
      "allOf": [
        {
          "$ref": "#/components/schemas/CustomerDetailsResponse"
        }
      ],
      "nullable": true
    },
    "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_on": {
      "type": "string",
      "format": "date-time",
      "description": "A timestamp (ISO 8601 code) that determines when the payment should be captured.\nProviding this field will automatically set `capture` to true",
      "example": "2022-09-10T10:11:12Z",
      "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
    },
    "unified_code": {
      "type": "string",
      "description": "error code unified across the connectors is received here if there was an error while calling connector",
      "nullable": true
    },
    "unified_message": {
      "type": "string",
      "description": "error message unified across the connectors is received here if there was an error while calling connector",
      "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 ThreeDsMethodData {
    pub three_ds_method_notification_url: String,
    pub server_transaction_id: String,
}


impl AnalyticsDataSource for SqlxClient {
    type Row = PgRow;

    async fn load_results<T>(&self, query: &str) -> CustomResult<Vec<T>, QueryExecutionError>
    where
        Self: LoadRow<T>,
    {
        sqlx::query(&format!("{query};"))
            .fetch_all(&self.pool)
            .await
            .change_context(QueryExecutionError::DatabaseError)
            .attach_printable_lazy(|| format!("Failed to run query {query}"))?
            .into_iter()
            .map(Self::load_row)
            .collect::<Result<Vec<_>, _>>()
            .change_context(QueryExecutionError::RowExtractionFailure)
    }
}

impl AnalyticsDataSource for for SqlxClient


impl PayoutAttempt {
    pub async fn update_with_attempt_id(
        self,
        conn: &PgPooledConn,
        payout_attempt_update: PayoutAttemptUpdate,
    ) -> StorageResult<Self> {
        match generics::generic_update_with_unique_predicate_get_result::<
            <Self as HasTable>::Table,
            _,
            _,
            _,
        >(
            conn,
            dsl::payout_attempt_id
                .eq(self.payout_attempt_id.to_owned())
                .and(dsl::merchant_id.eq(self.merchant_id.to_owned())),
            PayoutAttemptUpdateInternal::from(payout_attempt_update),
        )
        .await
        {
            Err(error) => match error.current_context() {
                DatabaseError::NoFieldsToUpdate => Ok(self),
                _ => Err(error),
            },
            result => result,
        }
    }

    pub async fn find_by_merchant_id_payout_id(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        payout_id: &common_utils::id_type::PayoutId,
    ) -> StorageResult<Self> {
        generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::merchant_id
                .eq(merchant_id.to_owned())
                .and(dsl::payout_id.eq(payout_id.to_owned())),
        )
        .await
    }

    pub async fn find_by_merchant_id_payout_attempt_id(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        payout_attempt_id: &str,
    ) -> StorageResult<Self> {
        generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::merchant_id
                .eq(merchant_id.to_owned())
                .and(dsl::payout_attempt_id.eq(payout_attempt_id.to_owned())),
        )
        .await
    }

    pub async fn find_by_merchant_id_connector_payout_id(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        connector_payout_id: &str,
    ) -> StorageResult<Self> {
        generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::merchant_id
                .eq(merchant_id.to_owned())
                .and(dsl::connector_payout_id.eq(connector_payout_id.to_owned())),
        )
        .await
    }

    pub async fn find_by_merchant_id_merchant_order_reference_id(
        conn: &PgPooledConn,
        merchant_id_input: &common_utils::id_type::MerchantId,
        merchant_order_reference_id_input: &str,
    ) -> StorageResult<Self> {
        generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::merchant_id.eq(merchant_id_input.to_owned()).and(
                dsl::merchant_order_reference_id.eq(merchant_order_reference_id_input.to_owned()),
            ),
        )
        .await
    }

    pub async fn update_by_merchant_id_payout_id(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        payout_id: &common_utils::id_type::PayoutId,
        payout: PayoutAttemptUpdate,
    ) -> StorageResult<Self> {
        generics::generic_update_with_results::<<Self as HasTable>::Table, _, _, _>(
            conn,
            dsl::merchant_id
                .eq(merchant_id.to_owned())
                .and(dsl::payout_id.eq(payout_id.to_owned())),
            PayoutAttemptUpdateInternal::from(payout),
        )
        .await?
        .first()
        .cloned()
        .ok_or_else(|| {
            report!(DatabaseError::NotFound).attach_printable("Error while updating payout")
        })
    }

    pub async fn update_by_merchant_id_payout_attempt_id(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        payout_attempt_id: &str,
        payout: PayoutAttemptUpdate,
    ) -> StorageResult<Self> {
        generics::generic_update_with_results::<<Self as HasTable>::Table, _, _, _>(
            conn,
            dsl::merchant_id
                .eq(merchant_id.to_owned())
                .and(dsl::payout_attempt_id.eq(payout_attempt_id.to_owned())),
            PayoutAttemptUpdateInternal::from(payout),
        )
        .await?
        .first()
        .cloned()
        .ok_or_else(|| {
            report!(DatabaseError::NotFound).attach_printable("Error while updating payout")
        })
    }

    pub async fn get_filters_for_payouts(
        conn: &PgPooledConn,
        payouts: &[Payouts],
        merchant_id: &common_utils::id_type::MerchantId,
    ) -> StorageResult<(
        Vec<String>,
        Vec<enums::Currency>,
        Vec<enums::PayoutStatus>,
        Vec<enums::PayoutType>,
    )> {
        let active_attempt_ids = payouts
            .iter()
            .map(|payout| {
                format!(
                    "{}_{}",
                    payout.payout_id.get_string_repr(),
                    payout.attempt_count.clone()
                )
            })
            .collect::<Vec<String>>();

        let active_payout_ids = payouts
            .iter()
            .map(|payout| payout.payout_id.to_owned())
            .collect::<Vec<common_utils::id_type::PayoutId>>();

        let filter = <Self as HasTable>::table()
            .filter(dsl::merchant_id.eq(merchant_id.to_owned()))
            .filter(dsl::payout_attempt_id.eq_any(active_attempt_ids));

        let payouts_filter = <Payouts as HasTable>::table()
            .filter(payout_dsl::merchant_id.eq(merchant_id.to_owned()))
            .filter(payout_dsl::payout_id.eq_any(active_payout_ids));

        let payout_status: Vec<enums::PayoutStatus> = payouts
            .iter()
            .map(|payout| payout.status)
            .collect::<HashSet<enums::PayoutStatus>>()
            .into_iter()
            .collect();

        let filter_connector = filter
            .clone()
            .select(dsl::connector)
            .distinct()
            .get_results_async::<Option<String>>(conn)
            .await
            .change_context(DatabaseError::Others)
            .attach_printable("Error filtering records by connector")?
            .into_iter()
            .flatten()
            .collect::<Vec<String>>();

        let filter_currency = payouts_filter
            .clone()
            .select(payout_dsl::destination_currency)
            .distinct()
            .get_results_async::<enums::Currency>(conn)
            .await
            .change_context(DatabaseError::Others)
            .attach_printable("Error filtering records by currency")?
            .into_iter()
            .collect::<Vec<enums::Currency>>();

        let filter_payout_method = payouts_filter
            .clone()
            .select(payout_dsl::payout_type)
            .distinct()
            .get_results_async::<Option<enums::PayoutType>>(conn)
            .await
            .change_context(DatabaseError::Others)
            .attach_printable("Error filtering records by payout type")?
            .into_iter()
            .flatten()
            .collect::<Vec<enums::PayoutType>>();

        Ok((
            filter_connector,
            filter_currency,
            payout_status,
            filter_payout_method,
        ))
    }
}


pub struct DeutschebankThreeDSInitializeRequest {
    means_of_payment: DeutschebankThreeDSInitializeRequestMeansOfPayment,
    tds_20_data: DeutschebankThreeDSInitializeRequestTds20Data,
    amount_total: DeutschebankThreeDSInitializeRequestAmountTotal,
}


impl super::KafkaMessage for RevenueRecovery<'_> {
    fn key(&self) -> String {
        self.merchant_id.get_string_repr().to_string()
    }

    fn event_type(&self) -> crate::events::EventType {
        crate::events::EventType::RevenueRecovery
    }
}

impl super::KafkaMessage for for RevenueRecovery


pub struct SignifydPaymentsSaleRequest {
    order_id: String,
    purchase: Purchase,
    decision_delivery: DecisionDelivery,
    coverage_requests: Option<CoverageRequests>,
}


pub async fn log_payment_attempt_delete(
        &self,
        delete_old_attempt: &PaymentAttempt,
        tenant_id: TenantID,
    ) -> MQResult<()> {
        self.log_event(&KafkaEvent::old(
            &KafkaPaymentAttempt::from_storage(delete_old_attempt),
            tenant_id.clone(),
            self.ckh_database_name.clone(),
        ))
        .attach_printable_lazy(|| {
            format!("Failed to add negative attempt event {delete_old_attempt:?}")
        })
    }


pub fn new(company_name: String) -> UserResult<Self> {
        let company_name = company_name.trim();
        let is_empty_or_whitespace = company_name.is_empty();
        let is_too_long =
            company_name.graphemes(true).count() > consts::user::MAX_COMPANY_NAME_LENGTH;

        let is_all_valid_characters = company_name
            .chars()
            .all(|x| x.is_alphanumeric() || x.is_ascii_whitespace() || x == '_');
        if is_empty_or_whitespace || is_too_long || !is_all_valid_characters {
            Err(UserErrors::CompanyNameParsingError.into())
        } else {
            Ok(Self(company_name.to_string()))
        }
    }


File: crates/analytics/src/enums.rs

pub use common_utils::types::authentication::AuthInfo;


impl JWTFlow {
    async fn is_required(
        &self,
        _user: &UserFromStorage,
        _state: &SessionState,
    ) -> UserResult<bool> {
        Ok(true)
    }

    pub async fn generate_jwt(
        self,
        state: &SessionState,
        next_flow: &NextFlow,
        user_role: &UserRole,
    ) -> UserResult<Secret<String>> {
        let user_id = next_flow.user.get_user_id();
        // Fetch lineage context from DB
        let lineage_context_from_db = state
            .global_store
            .find_user_by_id(user_id)
            .await
            .inspect_err(|e| {
                logger::error!(
                    "Failed to fetch lineage context from DB for user {}: {:?}",
                    user_id,
                    e
                )
            })
            .ok()
            .and_then(|user| user.lineage_context);

        let new_lineage_context = match lineage_context_from_db {
            Some(ctx) => {
                let tenant_id = ctx.tenant_id.clone();
                let user_role_match_v2 = state
                    .global_store
                    .find_user_role_by_user_id_and_lineage(
                        &ctx.user_id,
                        &tenant_id,
                        &ctx.org_id,
                        &ctx.merchant_id,
                        &ctx.profile_id,
                        UserRoleVersion::V2,
                    )
                    .await
                    .inspect_err(|e| {
                        logger::error!("Failed to validate V2 role: {e:?}");
                    })
                    .map(|role| role.role_id == ctx.role_id)
                    .unwrap_or_default();

                if user_role_match_v2 {
                    ctx
                } else {
                    let user_role_match_v1 = state
                        .global_store
                        .find_user_role_by_user_id_and_lineage(
                            &ctx.user_id,
                            &tenant_id,
                            &ctx.org_id,
                            &ctx.merchant_id,
                            &ctx.profile_id,
                            UserRoleVersion::V1,
                        )
                        .await
                        .inspect_err(|e| {
                            logger::error!("Failed to validate V1 role: {e:?}");
                        })
                        .map(|role| role.role_id == ctx.role_id)
                        .unwrap_or_default();

                    if user_role_match_v1 {
                        ctx
                    } else {
                        // fallback to default lineage if cached context is invalid
                        Self::resolve_lineage_from_user_role(state, user_role, user_id).await?
                    }
                }
            }
            None =>
            // no cached context found
            {
                Self::resolve_lineage_from_user_role(state, user_role, user_id).await?
            }
        };

        utils::user::spawn_async_lineage_context_update_to_db(
            state,
            user_id,
            new_lineage_context.clone(),
        );

        auth::AuthToken::new_token(
            new_lineage_context.user_id,
            new_lineage_context.merchant_id,
            new_lineage_context.role_id,
            &state.conf,
            new_lineage_context.org_id,
            new_lineage_context.profile_id,
            Some(new_lineage_context.tenant_id),
        )
        .await
        .map(|token| token.into())
    }

    pub async fn resolve_lineage_from_user_role(
        state: &SessionState,
        user_role: &UserRole,
        user_id: &str,
    ) -> UserResult<LineageContext> {
        let org_id = utils::user_role::get_single_org_id(state, user_role).await?;
        let merchant_id =
            utils::user_role::get_single_merchant_id(state, user_role, &org_id).await?;
        let profile_id =
            utils::user_role::get_single_profile_id(state, user_role, &merchant_id).await?;

        Ok(LineageContext {
            user_id: user_id.to_string(),
            org_id,
            merchant_id,
            profile_id,
            role_id: user_role.role_id.clone(),
            tenant_id: user_role.tenant_id.clone(),
        })
    }
}


pub struct FraudResult {
    pub avs_result: Option<String>,
    pub card_validation_result: Option<String>,
    pub authentication_result: Option<String>,
    pub advanced_a_v_s_result: Option<String>,
    pub advanced_fraud_results: Option<AdvancedFraudResults>,
}


pub struct Messages {
    #[serde(rename = "Version")]
    version: VersionData,
    #[serde(rename = "Signature")]
    signature: String,
    #[serde(rename = "SignatureVersion")]
    signature_version: String,
}


impl api::RefundSync for Elavon {}


pub struct CardholderAccount {
    /// Indicates the type of account.
    /// This is required if 3DS Requestor is asking Cardholder which Account Type they are using before making
    /// the purchase. This field is required in some markets. Otherwise, it is optional.
    pub acct_type: Option<AccountType>,
    /// Expiry date of the PAN or token supplied to the 3DS Requestor by the Cardholder.
    /// The field has 4 characters in a format YYMM.
    ///
    /// The requirements of the presence of this field are DS specific.
    pub card_expiry_date: Option<masking::Secret<String>>,
    /// This field contains additional information about the Cardholder’s account provided by the 3DS Requestor.
    ///
    /// The field is optional but recommended to include.
    ///
    /// Starting from EMV 3DS 2.3.1, added new field:
    /// - `ch_acc_req_id` -> The 3DS Requestor assigned account identifier of the transacting Cardholder.
    ///   This identifier is a unique representation of the account identifier for the 3DS Requestor and
    ///   is provided as a String.
    pub acct_info: Option<CardHolderAccountInformation>,
    /// Account number that will be used in the authorization request for payment transactions.
    /// May be represented by PAN or token.
    ///
    /// This field is required.
    pub acct_number: cards::CardNumber,
    /// ID for the scheme to which the Cardholder's acctNumber belongs to.
    /// It will be used to identify the Scheme from the 3DS Server configuration.
    ///
    /// This field is optional, but recommended to include.
    /// It should be present when it is not one of the schemes for which scheme resolving regular expressions
    /// are provided in the 3DS Server Configuration Properties. Additionally,
    /// if the schemeId is present in the request and there are card ranges found by multiple schemes, the schemeId will be
    /// used for proper resolving of the versioning data.
    pub scheme_id: Option<SchemeId>,
    /// Additional information about the account optionally provided by the 3DS Requestor.
    ///
    /// This field is limited to 64 characters and it is optional to use.
    #[serde(rename = "acctID")]
    pub acct_id: Option<String>,
    /// Indicates if the transaction was de-tokenized prior to being received by the ACS.
    ///
    /// The boolean value of true is the only valid response for this field when it is present.
    ///
    /// The field is required only if there is a de-tokenization of an Account Number.
    pub pay_token_ind: Option<bool>,
    /// Information about the de-tokenised Payment Token.
    /// Note: Data will be formatted into a JSON object prior to being placed into the EMV Payment Token field of the message.
    ///
    /// This field is optional.
    pub pay_token_info: Option<String>,
    /// Three or four-digit security code printed on the card.
    /// The value is numeric and limited to 3-4 characters.
    ///
    /// This field is required depending on the rules provided by the Directory Server.
    /// Available for supporting EMV 3DS 2.3.1 and later versions.
    pub card_security_code: Option<masking::Secret<String>>,
}


pub trait AuthServiceExchangeToken:
    super::ConnectorIntegration<ExchangeToken, ExchangeTokenRequest, ExchangeTokenResponse>
{
}


pub struct RawGooglePayToken {
    pub signature: Secret<String>,
    pub protocol_version: String,
    pub signed_message: Secret<String>,
    pub intermediate_signing_key: IntermediateSigningKey,
}


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

# External Services

This crate includes interactions with external systems, which may be shared
across crates within this workspace.


pub struct CardNetworkTypes {
    /// The card network enabled
    #[schema(value_type = Option<CardNetwork>, example = "Visa")]
    pub card_network: api_enums::CardNetwork,

    /// surcharge details for this card network
    pub surcharge_details: Option<SurchargeDetailsResponse>,

    /// The list of eligible connectors for a given card network
    #[schema(example = json!(["stripe", "adyen"]))]
    pub eligible_connectors: Vec<String>,
}


impl ConnectorConfig {
    fn new() -> Result<Self, String> {
        let config_str = if cfg!(feature = "production") {
            include_str!("../toml/production.toml")
        } else if cfg!(feature = "sandbox") {
            include_str!("../toml/sandbox.toml")
        } else {
            include_str!("../toml/development.toml")
        };
        let config = toml::from_str::<Self>(config_str);
        match config {
            Ok(data) => Ok(data),
            Err(err) => Err(err.to_string()),
        }
    }

    #[cfg(feature = "payouts")]
    pub fn get_payout_connector_config(
        connector: PayoutConnectors,
    ) -> Result<Option<ConnectorTomlConfig>, String> {
        let connector_data = Self::new()?;
        match connector {
            PayoutConnectors::Adyen => Ok(connector_data.adyen_payout),
            PayoutConnectors::Adyenplatform => Ok(connector_data.adyenplatform_payout),
            PayoutConnectors::Cybersource => Ok(connector_data.cybersource_payout),
            PayoutConnectors::Ebanx => Ok(connector_data.ebanx_payout),
            PayoutConnectors::Nomupay => Ok(connector_data.nomupay_payout),
            PayoutConnectors::Payone => Ok(connector_data.payone_payout),
            PayoutConnectors::Paypal => Ok(connector_data.paypal_payout),
            PayoutConnectors::Stripe => Ok(connector_data.stripe_payout),
            PayoutConnectors::Wise => Ok(connector_data.wise_payout),
        }
    }

    pub fn get_authentication_connector_config(
        connector: AuthenticationConnectors,
    ) -> Result<Option<ConnectorTomlConfig>, String> {
        let connector_data = Self::new()?;
        match connector {
            AuthenticationConnectors::Threedsecureio => Ok(connector_data.threedsecureio),
            AuthenticationConnectors::Netcetera => Ok(connector_data.netcetera),
            AuthenticationConnectors::Gpayments => Ok(connector_data.gpayments),
            AuthenticationConnectors::CtpMastercard => Ok(connector_data.ctp_mastercard),
            AuthenticationConnectors::CtpVisa => Ok(connector_data.ctp_visa),
            AuthenticationConnectors::UnifiedAuthenticationService => {
                Ok(connector_data.unified_authentication_service)
            }
            AuthenticationConnectors::Juspaythreedsserver => Ok(connector_data.juspaythreedsserver),
        }
    }

    pub fn get_tax_processor_config(
        connector: TaxConnectors,
    ) -> Result<Option<ConnectorTomlConfig>, String> {
        let connector_data = Self::new()?;
        match connector {
            TaxConnectors::Taxjar => Ok(connector_data.taxjar),
        }
    }

    pub fn get_pm_authentication_processor_config(
        connector: PmAuthConnectors,
    ) -> Result<Option<ConnectorTomlConfig>, String> {
        let connector_data = Self::new()?;
        match connector {
            PmAuthConnectors::Plaid => Ok(connector_data.plaid),
        }
    }

    pub fn get_connector_config(
        connector: Connector,
    ) -> Result<Option<ConnectorTomlConfig>, String> {
        let connector_data = Self::new()?;
        match connector {
            Connector::Aci => Ok(connector_data.aci),
            Connector::Authipay => Ok(connector_data.authipay),
            Connector::Adyen => Ok(connector_data.adyen),
            Connector::Affirm => Ok(connector_data.affirm),
            Connector::Adyenplatform => Err("Use get_payout_connector_config".to_string()),
            Connector::Airwallex => Ok(connector_data.airwallex),
            Connector::Amazonpay => Ok(connector_data.amazonpay),
            Connector::Archipel => Ok(connector_data.archipel),
            Connector::Authorizedotnet => Ok(connector_data.authorizedotnet),
            Connector::Bamboraapac => Ok(connector_data.bamboraapac),
            Connector::Bankofamerica => Ok(connector_data.bankofamerica),
            Connector::Barclaycard => Ok(connector_data.barclaycard),
            Connector::Billwerk => Ok(connector_data.billwerk),
            Connector::Bitpay => Ok(connector_data.bitpay),
            Connector::Bluesnap => Ok(connector_data.bluesnap),
            Connector::Bluecode => Ok(connector_data.bluecode),
            Connector::Blackhawknetwork => Ok(connector_data.blackhawknetwork),
            Connector::Boku => Ok(connector_data.boku),
            Connector::Braintree => Ok(connector_data.braintree),
            Connector::Breadpay => Ok(connector_data.breadpay),
            Connector::Cashtocode => Ok(connector_data.cashtocode),
            Connector::Celero => Ok(connector_data.celero),
            Connector::Chargebee => Ok(connector_data.chargebee),
            Connector::Checkbook => Ok(connector_data.checkbook),
            Connector::Checkout => Ok(connector_data.checkout),
            Connector::Coinbase => Ok(connector_data.coinbase),
            Connector::Coingate => Ok(connector_data.coingate),
            Connector::Cryptopay => Ok(connector_data.cryptopay),
            Connector::CtpVisa => Ok(connector_data.ctp_visa),
            Connector::Custombilling => Ok(connector_data.custombilling),
            Connector::Cybersource => Ok(connector_data.cybersource),
            #[cfg(feature = "dummy_connector")]
            Connector::DummyBillingConnector => Ok(connector_data.dummy_connector),
            Connector::Iatapay => Ok(connector_data.iatapay),
            Connector::Itaubank => Ok(connector_data.itaubank),
            Connector::Opennode => Ok(connector_data.opennode),
            Connector::Bambora => Ok(connector_data.bambora),
            Connector::Datatrans => Ok(connector_data.datatrans),
            Connector::Deutschebank => Ok(connector_data.deutschebank),
            Connector::Digitalvirgo => Ok(connector_data.digitalvirgo),
            Connector::Dlocal => Ok(connector_data.dlocal),
            Connector::Dwolla => Ok(connector_data.dwolla),
            Connector::Ebanx => Ok(connector_data.ebanx_payout),
            Connector::Elavon => Ok(connector_data.elavon),
            Connector::Facilitapay => Ok(connector_data.facilitapay),
            Connector::Fiserv => Ok(connector_data.fiserv),
            Connector::Fiservemea => Ok(connector_data.fiservemea),
            Connector::Fiuu => Ok(connector_data.fiuu),
            Connector::Flexiti => Ok(connector_data.flexiti),
            Connector::Forte => Ok(connector_data.forte),
            Connector::Getnet => Ok(connector_data.getnet),
            Connector::Globalpay => Ok(connector_data.globalpay),
            Connector::Globepay => Ok(connector_data.globepay),
            Connector::Gocardless => Ok(connector_data.gocardless),
            Connector::Gpayments => Ok(connector_data.gpayments),
            Connector::Hipay => Ok(connector_data.hipay),
            Connector::HyperswitchVault => Ok(connector_data.hyperswitch_vault),
            Connector::Helcim => Ok(connector_data.helcim),
            Connector::Inespay => Ok(connector_data.inespay),
            Connector::Jpmorgan => Ok(connector_data.jpmorgan),
            Connector::Juspaythreedsserver => Ok(connector_data.juspaythreedsserver),
            Connector::Klarna => Ok(connector_data.klarna),
            Connector::Mifinity => Ok(connector_data.mifinity),
            Connector::Mollie => Ok(connector_data.mollie),
            Connector::Moneris => Ok(connector_data.moneris),
            Connector::Multisafepay => Ok(connector_data.multisafepay),
            Connector::Nexinets => Ok(connector_data.nexinets),
            Connector::Nexixpay => Ok(connector_data.nexixpay),
            Connector::Prophetpay => Ok(connector_data.prophetpay),
            Connector::Nmi => Ok(connector_data.nmi),
            Connector::Nordea => Ok(connector_data.nordea),
            Connector::Nomupay => Err("Use get_payout_connector_config".to_string()),
            Connector::Novalnet => Ok(connector_data.novalnet),
            Connector::Noon => Ok(connector_data.noon),
            Connector::Nuvei => Ok(connector_data.nuvei),
            Connector::Paybox => Ok(connector_data.paybox),
            Connector::Payload => Ok(connector_data.payload),
            Connector::Payme => Ok(connector_data.payme),
            Connector::Payone => Err("Use get_payout_connector_config".to_string()),
            Connector::Paypal => Ok(connector_data.paypal),
            Connector::Paysafe => Ok(connector_data.paysafe),
            Connector::Paystack => Ok(connector_data.paystack),
            Connector::Payu => Ok(connector_data.payu),
            Connector::Placetopay => Ok(connector_data.placetopay),
            Connector::Plaid => Ok(connector_data.plaid),
            Connector::Powertranz => Ok(connector_data.powertranz),
            Connector::Razorpay => Ok(connector_data.razorpay),
            Connector::Rapyd => Ok(connector_data.rapyd),
            Connector::Recurly => Ok(connector_data.recurly),
            Connector::Redsys => Ok(connector_data.redsys),
            Connector::Riskified => Ok(connector_data.riskified),
            Connector::Santander => Ok(connector_data.santander),
            Connector::Shift4 => Ok(connector_data.shift4),
            Connector::Signifyd => Ok(connector_data.signifyd),
            Connector::Silverflow => Ok(connector_data.silverflow),
            Connector::Square => Ok(connector_data.square),
            Connector::Stax => Ok(connector_data.stax),
            Connector::Stripe => Ok(connector_data.stripe),
            Connector::Stripebilling => Ok(connector_data.stripebilling),
            Connector::Tokenio => Ok(connector_data.tokenio),
            Connector::Trustpay => Ok(connector_data.trustpay),
            Connector::Threedsecureio => Ok(connector_data.threedsecureio),
            Connector::Taxjar => Ok(connector_data.taxjar),
            Connector::Tsys => Ok(connector_data.tsys),
            Connector::Vgs => Ok(connector_data.vgs),
            Connector::Volt => Ok(connector_data.volt),
            Connector::Wellsfargo => Ok(connector_data.wellsfargo),
            Connector::Wise => Err("Use get_payout_connector_config".to_string()),
            Connector::Worldline => Ok(connector_data.worldline),
            Connector::Worldpay => Ok(connector_data.worldpay),
            Connector::Worldpayvantiv => Ok(connector_data.worldpayvantiv),
            Connector::Worldpayxml => Ok(connector_data.worldpayxml),
            Connector::Zen => Ok(connector_data.zen),
            Connector::Zsl => Ok(connector_data.zsl),
            #[cfg(feature = "dummy_connector")]
            Connector::DummyConnector1 => Ok(connector_data.dummy_connector),
            #[cfg(feature = "dummy_connector")]
            Connector::DummyConnector2 => Ok(connector_data.dummy_connector),
            #[cfg(feature = "dummy_connector")]
            Connector::DummyConnector3 => Ok(connector_data.dummy_connector),
            #[cfg(feature = "dummy_connector")]
            Connector::DummyConnector4 => Ok(connector_data.stripe_test),
            #[cfg(feature = "dummy_connector")]
            Connector::DummyConnector5 => Ok(connector_data.dummy_connector),
            #[cfg(feature = "dummy_connector")]
            Connector::DummyConnector6 => Ok(connector_data.dummy_connector),
            #[cfg(feature = "dummy_connector")]
            Connector::DummyConnector7 => Ok(connector_data.paypal_test),
            Connector::Netcetera => Ok(connector_data.netcetera),
            Connector::CtpMastercard => Ok(connector_data.ctp_mastercard),
            Connector::Xendit => Ok(connector_data.xendit),
            Connector::Paytm => Ok(connector_data.paytm),
            Connector::Phonepe => Ok(connector_data.phonepe),
        }
    }
}


pub fn validate(&self) -> Result<(), ApplicationError> {
        common_utils::fp_utils::when(self.stream_name.is_default_or_empty(), || {
            Err(ApplicationError::InvalidConfigurationValueError(
                "drainer stream name must not be empty".into(),
            ))
        })
    }


pub async fn merchant_account_update(
    state: SessionState,
    merchant_id: &id_type::MerchantId,
    _profile_id: Option<id_type::ProfileId>,
    req: api::MerchantAccountUpdate,
) -> RouterResponse<api::MerchantAccountResponse> {
    let db = state.store.as_ref();
    let key_manager_state = &(&state).into();
    let key_store = db
        .get_merchant_key_store_by_merchant_id(
            key_manager_state,
            merchant_id,
            &db.get_master_key().to_vec().into(),
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?;

    let merchant_account_storage_object = req
        .get_update_merchant_object(&state, merchant_id, &key_store)
        .await
        .attach_printable("Failed to create merchant account update object")?;

    let response = db
        .update_specific_fields_in_merchant(
            key_manager_state,
            merchant_id,
            merchant_account_storage_object,
            &key_store,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?;

    Ok(service_api::ApplicationResponse::Json(
        api::MerchantAccountResponse::foreign_try_from(response)
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Failed while generating response")?,
    ))
}


pub struct ConnectorVolumeSplit {
    pub connector: RoutableConnectorChoice,
    pub split: u8,
}


File: crates/router/src/connector/utils.rs
 Public functions: 13
 Public structs: 1

use std::{
    collections::{HashMap, HashSet},
    str::FromStr,
    sync::LazyLock,
};

#[cfg(feature = "payouts")]
use api_models::payouts::{self, PayoutVendorAccountDetails};
use api_models::{
    enums::{CanadaStatesAbbreviation, UsStatesAbbreviation},
    payments,
};
use base64::Engine;
use common_utils::{
    date_time,
    errors::{ParsingError, ReportSwitchExt},
    ext_traits::StringExt,
    id_type,
    pii::{self, Email, IpAddress},
    types::{AmountConvertor, MinorUnit},
};
use diesel_models::{enums, types::OrderDetailsWithAmount};
use error_stack::{report, ResultExt};
use hyperswitch_domain_models::{
    network_tokenization::NetworkTokenNumber, payments::payment_attempt::PaymentAttempt,
};
use masking::{Deserialize, ExposeInterface, Secret};
use regex::Regex;

#[cfg(feature = "frm")]
use crate::types::fraud_check;
use crate::{
    consts,
    core::{
        errors::{self, ApiErrorResponse, CustomResult},
        payments::{types::AuthenticationData, PaymentData},
    },
    pii::PeekInterface,
    types::{
        self, api, domain,
        storage::enums as storage_enums,
        transformers::{ForeignFrom, ForeignTryFrom},
        BrowserInformation, PaymentsCancelData, ResponseId,
    },
    utils::{OptionExt, ValueExt},
};

pub fn missing_field_err(
    message: &'static str,
) -> Box<dyn Fn() -> error_stack::Report<errors::ConnectorError> + 'static> {
    Box::new(move || {
        errors::ConnectorError::MissingRequiredField {
            field_name: message,
        }
        .into()
    })
}

type Error = error_stack::Report<errors::ConnectorError>;

pub trait AccessTokenRequestInfo {
    fn get_request_id(&self) -> Result<Secret<String>, Error>;
}

impl AccessTokenRequestInfo for types::RefreshTokenRouterData {
    fn get_request_id(&self) -> Result<Secret<String>, Error> {
        self.request
            .id
            .clone()
            .ok_or_else(missing_field_err("request.id"))
    }
}

pub trait RouterData {
    fn get_billing(&self) -> Result<&hyperswitch_domain_models::address::Address, Error>;
    fn get_billing_country(&self) -> Result<api_models::enums::CountryAlpha2, Error>;
    fn get_billing_phone(&self)
        -> Result<&hyperswitch_domain_models::address::PhoneDetails, Error>;
    fn get_description(&self) -> Result<String, Error>;
    fn get_billing_address(
        &self,
    ) -> Result<&hyperswitch_domain_models::address::AddressDetails, Error>;
    fn get_shipping_address(
        &self,
    ) -> Result<&hyperswitch_domain_models::address::AddressDetails, Error>;
    fn get_shipping_address_with_phone_number(
        &self,
    ) -> Result<&hyperswitch_domain_models::address::Address, Error>;
    fn get_connector_meta(&self) -> Result<pii::SecretSerdeValue, Error>;
    fn get_session_token(&self) -> Result<String, Error>;
    fn get_billing_first_name(&self) -> Result<Secret<String>, Error>;
    fn get_billing_full_name(&self) -> Result<Secret<String>, Error>;
    fn get_billing_last_name(&self) -> Result<Secret<String>, Error>;
    fn get_billing_line1(&self) -> Result<Secret<String>, Error>;
    fn get_billing_city(&self) -> Result<String, Error>;
    fn get_billing_email(&self) -> Result<Email, Error>;
    fn get_billing_phone_number(&self) -> Result<Secret<String>, Error>;
    fn to_connector_meta<T>(&self) -> Result<T, Error>
    where
        T: serde::de::DeserializeOwned;
    fn is_three_ds(&self) -> bool;
    fn get_payment_method_token(&self) -> Result<types::PaymentMethodToken, Error>;
    fn get_customer_id(&self) -> Result<id_type::CustomerId, Error>;
    fn get_connector_customer_id(&self) -> Result<String, Error>;
    fn get_preprocessing_id(&self) -> Result<String, Error>;
    fn get_recurring_mandate_payment_data(
        &self,
    ) -> Result<types::RecurringMandatePaymentData, Error>;
    #[cfg(feature = "payouts")]
    fn get_payout_method_data(&self) -> Result<api::PayoutMethodData, Error>;
    #[cfg(feature = "payouts")]
    fn get_quote_id(&self) -> Result<String, Error>;

    fn get_optional_billing(&self) -> Option<&hyperswitch_domain_models::address::Address>;
    fn get_optional_shipping(&self) -> Option<&hyperswitch_domain_models::address::Address>;
    fn get_optional_shipping_line1(&self) -> Option<Secret<String>>;
    fn get_optional_shipping_line2(&self) -> Option<Secret<String>>;
    fn get_optional_shipping_line3(&self) -> Option<Secret<String>>;
    fn get_optional_shipping_city(&self) -> Option<String>;
    fn get_optional_shipping_country(&self) -> Option<enums::CountryAlpha2>;
    fn get_optional_shipping_zip(&self) -> Option<Secret<String>>;
    fn get_optional_shipping_state(&self) -> Option<Secret<String>>;
    fn get_optional_shipping_first_name(&self) -> Option<Secret<String>>;
    fn get_optional_shipping_last_name(&self) -> Option<Secret<String>>;
    fn get_optional_shipping_phone_number(&self) -> Option<Secret<String>>;
    fn get_optional_shipping_email(&self) -> Option<Email>;

    fn get_optional_billing_full_name(&self) -> Option<Secret<String>>;
    fn get_optional_billing_line1(&self) -> Option<Secret<String>>;
    fn get_optional_billing_line2(&self) -> Option<Secret<String>>;
    fn get_optional_billing_city(&self) -> Option<String>;
    fn get_optional_billing_country(&self) -> Option<enums::CountryAlpha2>;
    fn get_optional_billing_zip(&self) -> Option<Secret<String>>;
    fn get_optional_billing_state(&self) -> Option<Secret<String>>;
    fn get_optional_billing_first_name(&self) -> Option<Secret<String>>;
    fn get_optional_billing_last_name(&self) -> Option<Secret<String>>;
    fn get_optional_billing_phone_number(&self) -> Option<Secret<String>>;
    fn get_optional_billing_email(&self) -> Option<Email>;
}

pub trait PaymentResponseRouterData {
    fn get_attempt_status_for_db_update<F>(
        &self,
        payment_data: &PaymentData<F>,
        amount_captured: Option<i64>,
        amount_capturable: Option<i64>,
    ) -> CustomResult<enums::AttemptStatus, ApiErrorResponse>
    where
        F: Clone;
}

#[cfg(feature = "v1")]
impl<Flow, Request, Response> PaymentResponseRouterData
    for types::RouterData<Flow, Request, Response>
where
    Request: types::Capturable,
{
    fn get_attempt_status_for_db_update<F>(
        &self,
        payment_data: &PaymentData<F>,
        amount_captured: Option<i64>,
        amount_capturable: Option<i64>,
    ) -> CustomResult<enums::AttemptStatus, ApiErrorResponse>
    where
        F: Clone,
    {
        match self.status {
            enums::AttemptStatus::Voided => {
                if payment_data.payment_intent.amount_captured > Some(MinorUnit::new(0)) {
                    Ok(enums::AttemptStatus::PartialCharged)
                } else {
                    Ok(self.status)
                }
            }
            enums::AttemptStatus::Charged => {
                let captured_amount = types::Capturable::get_captured_amount(
                    &self.request,
                    amount_captured,
                    payment_data,
                );
                let total_capturable_amount = payment_data.payment_attempt.get_total_amount();
                if Some(total_capturable_amount) == captured_amount.map(MinorUnit::new) {
                    Ok(enums::AttemptStatus::Charged)
                } else if captured_amount.is_some() {
                    Ok(enums::AttemptStatus::PartialCharged)
                } else {
                    Ok(self.status)
                }
            }
            enums::AttemptStatus::Authorized => {
                let capturable_amount = types::Capturable::get_amount_capturable(
                    &self.request,
                    payment_data,
                    amount_capturable,
                    payment_data.payment_attempt.status,
                );
                if Some(payment_data.payment_attempt.get_total_amount())
                    == capturable_amount.map(MinorUnit::new)
                {
                    Ok(enums::AttemptStatus::Authorized)
                } else if capturable_amount.is_some()
                    && payment_data
                        .payment_intent
                        .enable_partial_authorization
                        .is_some_and(|val| val)
                {
                    Ok(enums::AttemptStatus::PartiallyAuthorized)
                } else if capturable_amount.is_some()
                    && !payment_data
                        .payment_intent
                        .enable_partial_authorization
                        .is_some_and(|val| val)
                {
                    Err(ApiErrorResponse::IntegrityCheckFailed {
                        reason: "capturable_amount is less than the total attempt amount"
                            .to_string(),
                        field_names: "amount_capturable".to_string(),
                        connector_transaction_id: payment_data
                            .payment_attempt
                            .connector_transaction_id
                            .clone(),
                    })?
                } else {
                    Ok(self.status)
                }
            }
            _ => Ok(self.status),
        }
    }
}

#[cfg(feature = "v2")]
impl<Flow, Request, Response> PaymentResponseRouterData
    for types::RouterData<Flow, Request, Response>
where
    Request: types::Capturable,
{
    fn get_attempt_status_for_db_update<F>(
        &self,
        payment_data: &PaymentData<F>,
        amount_captured: Option<i64>,
        amount_capturable: Option<i64>,
    ) -> CustomResult<enums::AttemptStatus, ApiErrorResponse>
    where
        F: Clone,
    {
        match self.status {
            enums::AttemptStatus::Voided => {
                if payment_data.payment_intent.amount_captured > Some(MinorUnit::new(0)) {
                    Ok(enums::AttemptStatus::PartialCharged)
                } else {
                    Ok(self.status)
                }
            }
            enums::AttemptStatus::Charged => {
                let captured_amount = types::Capturable::get_captured_amount(
                    &self.request,
                    amount_captured,
                    payment_data,
                );
                let total_capturable_amount = payment_data.payment_attempt.get_total_amount();
                if Some(total_capturable_amount) == captured_amount.map(MinorUnit::new) {
                    Ok(enums::AttemptStatus::Charged)
                } else if captured_amount.is_some() {
                    Ok(enums::AttemptStatus::PartialCharged)
                } else {
                    Ok(self.status)
                }
            }
            enums::AttemptStatus::Authorized => {
                let capturable_amount = types::Capturable::get_amount_capturable(
                    &self.request,
                    payment_data,
                    amount_capturable,
                    payment_data.payment_attempt.status,
                );
                if Some(payment_data.payment_attempt.get_total_amount())
                    == capturable_amount.map(MinorUnit::new)
                {
                    Ok(enums::AttemptStatus::Authorized)
                } else if capturable_amount.is_some() {
                    Ok(enums::AttemptStatus::PartiallyAuthorized)
                } else {
                    Ok(self.status)
                }
            }
            _ => Ok(self.status),
        }
    }
}

pub const SELECTED_PAYMENT_METHOD: &str = "Selected payment method";

pub fn get_unimplemented_payment_method_error_message(connector: &str) -> String {
    format!("{SELECTED_PAYMENT_METHOD} through {connector}")
}

impl<Flow, Request, Response> RouterData for types::RouterData<Flow, Request, Response> {
    fn get_billing(&self) -> Result<&hyperswitch_domain_models::address::Address, Error> {
        self.address
            .get_payment_method_billing()
            .ok_or_else(missing_field_err("billing"))
    }

    fn get_billing_country(&self) -> Result<api_models::enums::CountryAlpha2, Error> {
        self.address
            .get_payment_method_billing()
            .and_then(|a| a.address.as_ref())
            .and_then(|ad| ad.country)
            .ok_or_else(missing_field_err(
                "payment_method_data.billing.address.country",
            ))
    }

    fn get_billing_phone(
        &self,
    ) -> Result<&hyperswitch_domain_models::address::PhoneDetails, Error> {
        self.address
            .get_payment_method_billing()
            .and_then(|a| a.phone.as_ref())
            .ok_or_else(missing_field_err("billing.phone"))
    }

    fn get_optional_billing(&self) -> Option<&hyperswitch_domain_models::address::Address> {
        self.address.get_payment_method_billing()
    }

    fn get_optional_shipping(&self) -> Option<&hyperswitch_domain_models::address::Address> {
        self.address.get_shipping()
    }

    fn get_optional_shipping_first_name(&self) -> Option<Secret<String>> {
        self.address.get_shipping().and_then(|shipping_address| {
            shipping_address
                .clone()
                .address
                .and_then(|shipping_details| shipping_details.first_name)
        })
    }

    fn get_optional_shipping_last_name(&self) -> Option<Secret<String>> {
        self.address.get_shipping().and_then(|shipping_address| {
            shipping_address
                .clone()
                .address
                .and_then(|shipping_details| shipping_details.last_name)
        })
    }

    fn get_optional_shipping_line1(&self) -> Option<Secret<String>> {
        self.address.get_shipping().and_then(|shipping_address| {
            shipping_address
                .clone()
                .address
                .and_then(|shipping_details| shipping_details.line1)
        })
    }

    fn get_optional_shipping_line2(&self) -> Option<Secret<String>> {
        self.address.get_shipping().and_then(|shipping_address| {
            shipping_address
                .clone()
                .address
                .and_then(|shipping_details| shipping_details.line2)
        })
    }

    fn get_optional_shipping_line3(&self) -> Option<Secret<String>> {
        self.address.get_shipping().and_then(|shipping_address| {
            shipping_address
                .clone()
                .address
                .and_then(|shipping_details| shipping_details.line3)
        })
    }

    fn get_optional_shipping_city(&self) -> Option<String> {
        self.address.get_shipping().and_then(|shipping_address| {
            shipping_address
                .clone()
                .address
                .and_then(|shipping_details| shipping_details.city)
        })
    }

    fn get_optional_shipping_state(&self) -> Option<Secret<String>> {
        self.address.get_shipping().and_then(|shipping_address| {
            shipping_address
                .clone()
                .address
                .and_then(|shipping_details| shipping_details.state)
        })
    }

    fn get_optional_shipping_country(&self) -> Option<enums::CountryAlpha2> {
        self.address.get_shipping().and_then(|shipping_address| {
            shipping_address
                .clone()
                .address
                .and_then(|shipping_details| shipping_details.country)
        })
    }

    fn get_optional_shipping_zip(&self) -> Option<Secret<String>> {
        self.address.get_shipping().and_then(|shipping_address| {
            shipping_address
                .clone()
                .address
                .and_then(|shipping_details| shipping_details.zip)
        })
    }

    fn get_optional_shipping_email(&self) -> Option<Email> {
        self.address
            .get_shipping()
            .and_then(|shipping_address| shipping_address.clone().email)
    }

    fn get_optional_shipping_phone_number(&self) -> Option<Secret<String>> {
        self.address
            .get_shipping()
            .and_then(|shipping_address| shipping_address.clone().phone)
            .and_then(|phone_details| phone_details.get_number_with_country_code().ok())
    }

    fn get_description(&self) -> Result<String, Error> {
        self.description
            .clone()
            .ok_or_else(missing_field_err("description"))
    }
    fn get_billing_address(
        &self,
    ) -> Result<&hyperswitch_domain_models::address::AddressDetails, Error> {
        self.address
            .get_payment_method_billing()
            .as_ref()
            .and_then(|a| a.address.as_ref())
            .ok_or_else(missing_field_err("billing.address"))
    }

    fn get_connector_meta(&self) -> Result<pii::SecretSerdeValue, Error> {
        self.connector_meta_data
            .clone()
            .ok_or_else(missing_field_err("connector_meta_data"))
    }

    fn get_session_token(&self) -> Result<String, Error> {
        self.session_token
            .clone()
            .ok_or_else(missing_field_err("session_token"))
    }

    fn get_billing_first_name(&self) -> Result<Secret<String>, Error> {
        self.address
            .get_payment_method_billing()
            .and_then(|billing_address| {
                billing_address
                    .clone()
                    .address
                    .and_then(|billing_details| billing_details.first_name.clone())
            })
            .ok_or_else(missing_field_err(
                "payment_method_data.billing.address.first_name",
            ))
    }

    fn get_billing_full_name(&self) -> Result<Secret<String>, Error> {
        self.get_optional_billing()
            .and_then(|billing_details| billing_details.address.as_ref())
            .and_then(|billing_address| billing_address.get_optional_full_name())
            .ok_or_else(missing_field_err(
                "payment_method_data.billing.address.first_name",
            ))
    }

    fn get_billing_last_name(&self) -> Result<Secret<String>, Error> {
        self.address
            .get_payment_method_billing()
            .and_then(|billing_address| {
                billing_address
                    .clone()
                    .address
                    .and_then(|billing_details| billing_details.last_name.clone())
            })
            .ok_or_else(missing_field_err(
                "payment_method_data.billing.address.last_name",
            ))
    }

    fn get_billing_line1(&self) -> Result<Secret<String>, Error> {
        self.address
            .get_payment_method_billing()
            .and_then(|billing_address| {
                billing_address
                    .clone()
                    .address
                    .and_then(|billing_details| billing_details.line1.clone())
            })
            .ok_or_else(missing_field_err(
                "payment_method_data.billing.address.line1",
            ))
    }
    fn get_billing_city(&self) -> Result<String, Error> {
        self.address
            .get_payment_method_billing()
            .and_then(|billing_address| {
                billing_address
                    .clone()
                    .address
                    .and_then(|billing_details| billing_details.city)
            })
            .ok_or_else(missing_field_err(
                "payment_method_data.billing.address.city",
            ))
    }

    fn get_billing_email(&self) -> Result<Email, Error> {
        self.address
            .get_payment_method_billing()
            .and_then(|billing_address| billing_address.email.clone())
            .ok_or_else(missing_field_err("payment_method_data.billing.email"))
    }

    fn get_billing_phone_number(&self) -> Result<Secret<String>, Error> {
        self.address
            .get_payment_method_billing()
            .and_then(|billing_address| billing_address.clone().phone)
            .map(|phone_details| phone_details.get_number_with_country_code())
            .transpose()?
            .ok_or_else(missing_field_err("payment_method_data.billing.phone"))
    }

    fn get_optional_billing_line1(&self) -> Option<Secret<String>> {
        self.address
            .get_payment_method_billing()
            .and_then(|billing_address| {
                billing_address
                    .clone()
                    .address
                    .and_then(|billing_details| billing_details.line1)
            })
    }

    fn get_optional_billing_line2(&self) -> Option<Secret<String>> {
        self.address
            .get_payment_method_billing()
            .and_then(|billing_address| {
                billing_address
                    .clone()
                    .address
                    .and_then(|billing_details| billing_details.line2)
            })
    }

    fn get_optional_billing_city(&self) -> Option<String> {
        self.address
            .get_payment_method_billing()
            .and_then(|billing_address| {
                billing_address
                    .clone()
                    .address
                    .and_then(|billing_details| billing_details.city)
            })
    }

    fn get_optional_billing_country(&self) -> Option<enums::CountryAlpha2> {
        self.address
            .get_payment_method_billing()
            .and_then(|billing_address| {
                billing_address
                    .clone()
                    .address
                    .and_then(|billing_details| billing_details.country)
            })
    }

    fn get_optional_billing_zip(&self) -> Option<Secret<String>> {
        self.address
            .get_payment_method_billing()
            .and_then(|billing_address| {
                billing_address
                    .clone()
                    .address
                    .and_then(|billing_details| billing_details.zip)
            })
    }

    fn get_optional_billing_state(&self) -> Option<Secret<String>> {
        self.address
            .get_payment_method_billing()
            .and_then(|billing_address| {
                billing_address
                    .clone()
                    .address
                    .and_then(|billing_details| billing_details.state)
            })
    }

    fn get_optional_billing_first_name(&self) -> Option<Secret<String>> {
        self.address
            .get_payment_method_billing()
            .and_then(|billing_address| {
                billing_address
                    .clone()
                    .address
                    .and_then(|billing_details| billing_details.first_name)
            })
    }

    fn get_optional_billing_last_name(&self) -> Option<Secret<String>> {
        self.address
            .get_payment_method_billing()
            .and_then(|billing_address| {
                billing_address
                    .clone()
                    .address
                    .and_then(|billing_details| billing_details.last_name)
            })
    }

    fn get_optional_billing_phone_number(&self) -> Option<Secret<String>> {
        self.address
            .get_payment_method_billing()
            .and_then(|billing_address| {
                billing_address
                    .clone()
                    .phone
                    .and_then(|phone_data| phone_data.number)
            })
    }

    fn get_optional_billing_email(&self) -> Option<Email> {
        self.address
            .get_payment_method_billing()
            .and_then(|billing_address| billing_address.clone().email)
    }
    fn to_connector_meta<T>(&self) -> Result<T, Error>
    where
        T: serde::de::DeserializeOwned,
    {
        self.get_connector_meta()?
            .parse_value(std::any::type_name::<T>())
            .change_context(errors::ConnectorError::NoConnectorMetaData)
    }

    fn is_three_ds(&self) -> bool {
        matches!(self.auth_type, enums::AuthenticationType::ThreeDs)
    }

    fn get_shipping_address(
        &self,
    ) -> Result<&hyperswitch_domain_models::address::AddressDetails, Error> {
        self.address
            .get_shipping()
            .and_then(|a| a.address.as_ref())
            .ok_or_else(missing_field_err("shipping.address"))
    }

    fn get_shipping_address_with_phone_number(
        &self,
    ) -> Result<&hyperswitch_domain_models::address::Address, Error> {
        self.address
            .get_shipping()
            .ok_or_else(missing_field_err("shipping"))
    }

    fn get_payment_method_token(&self) -> Result<types::PaymentMethodToken, Error> {
        self.payment_method_token
            .clone()
            .ok_or_else(missing_field_err("payment_method_token"))
    }
    fn get_customer_id(&self) -> Result<id_type::CustomerId, Error> {
        self.customer_id
            .to_owned()
            .ok_or_else(missing_field_err("customer_id"))
    }
    fn get_connector_customer_id(&self) -> Result<String, Error> {
        self.connector_customer
            .to_owned()
            .ok_or_else(missing_field_err("connector_customer_id"))
    }
    fn get_preprocessing_id(&self) -> Result<String, Error> {
        self.preprocessing_id
            .to_owned()
            .ok_or_else(missing_field_err("preprocessing_id"))
    }
    fn get_recurring_mandate_payment_data(
        &self,
    ) -> Result<types::RecurringMandatePaymentData, Error> {
        self.recurring_mandate_payment_data
            .to_owned()
            .ok_or_else(missing_field_err("recurring_mandate_payment_data"))
    }

    fn get_optional_billing_full_name(&self) -> Option<Secret<String>> {
        self.get_optional_billing()
            .and_then(|billing_details| billing_details.address.as_ref())
            .and_then(|billing_address| billing_address.get_optional_full_name())
    }

    #[cfg(feature = "payouts")]
    fn get_payout_method_data(&self) -> Result<api::PayoutMethodData, Error> {
        self.payout_method_data
            .to_owned()
            .ok_or_else(missing_field_err("payout_method_data"))
    }
    #[cfg(feature = "payouts")]
    fn get_quote_id(&self) -> Result<String, Error> {
        self.quote_id
            .to_owned()
            .ok_or_else(missing_field_err("quote_id"))
    }
}

pub trait AddressData {
    fn get_email(&self) -> Result<Email, Error>;
    fn get_phone_with_country_code(&self) -> Result<Secret<String>, Error>;
    fn get_optional_country(&self) -> Option<enums::CountryAlpha2>;
    fn get_optional_full_name(&self) -> Option<Secret<String>>;
}

impl AddressData for hyperswitch_domain_models::address::Address {
    fn get_email(&self) -> Result<Email, Error> {
        self.email.clone().ok_or_else(missing_field_err("email"))
    }

    fn get_phone_with_country_code(&self) -> Result<Secret<String>, Error> {
        self.phone
            .clone()
            .map(|phone_details| phone_details.get_number_with_country_code())
            .transpose()?
            .ok_or_else(missing_field_err("phone"))
    }

    fn get_optional_country(&self) -> Option<enums::CountryAlpha2> {
        self.address
            .as_ref()
            .and_then(|billing_details| billing_details.country)
    }

    fn get_optional_full_name(&self) -> Option<Secret<String>> {
        self.address
            .as_ref()
            .and_then(|billing_address| billing_address.get_optional_full_name())
    }
}

pub trait PaymentsPreProcessingData {
    fn get_redirect_response_payload(&self) -> Result<pii::SecretSerdeValue, Error>;
    fn get_email(&self) -> Result<Email, Error>;
    fn get_payment_method_type(&self) -> Result<enums::PaymentMethodType, Error>;
    fn get_currency(&self) -> Result<enums::Currency, Error>;
    fn get_amount(&self) -> Result<i64, Error>;
    fn get_minor_amount(&self) -> Result<MinorUnit, Error>;
    fn is_auto_capture(&self) -> Result<bool, Error>;
    fn get_order_details(&self) -> Result<Vec<OrderDetailsWithAmount>, Error>;
    fn get_webhook_url(&self) -> Result<String, Error>;
    fn get_router_return_url(&self) -> Result<String, Error>;
    fn get_browser_info(&self) -> Result<BrowserInformation, Error>;
    fn get_complete_authorize_url(&self) -> Result<String, Error>;
    fn connector_mandate_id(&self) -> Option<String>;
}

impl PaymentsPreProcessingData for types::PaymentsPreProcessingData {
    fn get_email(&self) -> Result<Email, Error> {
        self.email.clone().ok_or_else(missing_field_err("email"))
    }
    fn get_payment_method_type(&self) -> Result<enums::PaymentMethodType, Error> {
        self.payment_method_type
            .to_owned()
            .ok_or_else(missing_field_err("payment_method_type"))
    }
    fn get_currency(&self) -> Result<enums::Currency, Error> {
        self.currency.ok_or_else(missing_field_err("currency"))
    }
    fn get_amount(&self) -> Result<i64, Error> {
        self.amount.ok_or_else(missing_field_err("amount"))
    }

    // New minor amount function for amount framework
    fn get_minor_amount(&self) -> Result<MinorUnit, Error> {
        self.minor_amount.ok_or_else(missing_field_err("amount"))
    }

    fn is_auto_capture(&self) -> Result<bool, Error> {
        match self.capture_method {
            Some(enums::CaptureMethod::Automatic)
            | None
            | Some(enums::CaptureMethod::SequentialAutomatic) => Ok(true),
            Some(enums::CaptureMethod::Manual) => Ok(false),
            Some(_) => Err(errors::ConnectorError::CaptureMethodNotSupported.into()),
        }
    }
    fn get_order_details(&self) -> Result<Vec<OrderDetailsWithAmount>, Error> {
        self.order_details
            .clone()
            .ok_or_else(missing_field_err("order_details"))
    }
    fn get_webhook_url(&self) -> Result<String, Error> {
        self.webhook_url
            .clone()
            .ok_or_else(missing_field_err("webhook_url"))
    }
    fn get_router_return_url(&self) -> Result<String, Error> {
        self.router_return_url
            .clone()
            .ok_or_else(missing_field_err("return_url"))
    }
    fn get_browser_info(&self) -> Result<BrowserInformation, Error> {
        self.browser_info
            .clone()
            .ok_or_else(missing_field_err("browser_info"))
    }
    fn get_complete_authorize_url(&self) -> Result<String, Error> {
        self.complete_authorize_url
            .clone()
            .ok_or_else(missing_field_err("complete_authorize_url"))
    }
    fn get_redirect_response_payload(&self) -> Result<pii::SecretSerdeValue, Error> {
        self.redirect_response
            .as_ref()
            .and_then(|res| res.payload.to_owned())
            .ok_or(
                errors::ConnectorError::MissingConnectorRedirectionPayload {
                    field_name: "request.redirect_response.payload",
                }
                .into(),
            )
    }
    fn connector_mandate_id(&self) -> Option<String> {
        self.mandate_id
            .as_ref()
            .and_then(|mandate_ids| match &mandate_ids.mandate_reference_id {
                Some(payments::MandateReferenceId::ConnectorMandateId(connector_mandate_ids)) => {
                    connector_mandate_ids.get_connector_mandate_id()
                }
                Some(payments::MandateReferenceId::NetworkMandateId(_))
                | None
                | Some(payments::MandateReferenceId::NetworkTokenWithNTI(_)) => None,
            })
    }
}

pub trait PaymentsCaptureRequestData {
    fn is_multiple_capture(&self) -> bool;
    fn get_browser_info(&self) -> Result<BrowserInformation, Error>;
    fn get_capture_method(&self) -> Option<enums::CaptureMethod>;
}

impl PaymentsCaptureRequestData for types::PaymentsCaptureData {
    fn is_multiple_capture(&self) -> bool {
        self.multiple_capture_data.is_some()
    }
    fn get_browser_info(&self) -> Result<BrowserInformation, Error> {
        self.browser_info
            .clone()
            .ok_or_else(missing_field_err("browser_info"))
    }
    fn get_capture_method(&self) -> Option<enums::CaptureMethod> {
        self.capture_method.to_owned()
    }
}
pub trait SplitPaymentData {
    fn get_split_payment_data(&self) -> Option<common_types::payments::SplitPaymentsRequest>;
}

impl SplitPaymentData for types::PaymentsCaptureData {
    fn get_split_payment_data(&self) -> Option<common_types::payments::SplitPaymentsRequest> {
        None
    }
}

impl SplitPaymentData for types::PaymentsAuthorizeData {
    fn get_split_payment_data(&self) -> Option<common_types::payments::SplitPaymentsRequest> {
        self.split_payments.clone()
    }
}

impl SplitPaymentData for types::PaymentsSyncData {
    fn get_split_payment_data(&self) -> Option<common_types::payments::SplitPaymentsRequest> {
        self.split_payments.clone()
    }
}

impl SplitPaymentData for PaymentsCancelData {
    fn get_split_payment_data(&self) -> Option<common_types::payments::SplitPaymentsRequest> {
        None
    }
}

impl SplitPaymentData for types::SetupMandateRequestData {
    fn get_split_payment_data(&self) -> Option<common_types::payments::SplitPaymentsRequest> {
        None
    }
}

pub trait RevokeMandateRequestData {
    fn get_connector_mandate_id(&self) -> Result<String, Error>;
}

impl RevokeMandateRequestData for types::MandateRevokeRequestData {
    fn get_connector_mandate_id(&self) -> Result<String, Error> {
        self.connector_mandate_id
            .clone()
            .ok_or_else(missing_field_err("connector_mandate_id"))
    }
}

pub trait PaymentsSetupMandateRequestData {
    fn get_browser_info(&self) -> Result<BrowserInformation, Error>;
    fn get_email(&self) -> Result<Email, Error>;
    fn is_card(&self) -> bool;
}

impl PaymentsSetupMandateRequestData for types::SetupMandateRequestData {
    fn get_browser_info(&self) -> Result<BrowserInformation, Error> {
        self.browser_info
            .clone()
            .ok_or_else(missing_field_err("browser_info"))
    }
    fn get_email(&self) -> Result<Email, Error> {
        self.email.clone().ok_or_else(missing_field_err("email"))
    }
    fn is_card(&self) -> bool {
        matches!(self.payment_method_data, domain::PaymentMethodData::Card(_))
    }
}
pub trait PaymentsAuthorizeRequestData {
    fn is_auto_capture(&self) -> Result<bool, Error>;
    fn get_email(&self) -> Result<Email, Error>;
    fn get_optional_email(&self) -> Option<Email>;
    fn get_browser_info(&self) -> Result<BrowserInformation, Error>;
    fn get_order_details(&self) -> Result<Vec<OrderDetailsWithAmount>, Error>;
    fn get_card(&self) -> Result<domain::Card, Error>;
    fn connector_mandate_id(&self) -> Option<String>;
    fn get_optional_network_transaction_id(&self) -> Option<String>;
    fn is_mandate_payment(&self) -> bool;
    fn is_cit_mandate_payment(&self) -> bool;
    fn is_customer_initiated_mandate_payment(&self) -> bool;
    fn get_webhook_url(&self) -> Result<String, Error>;
    fn get_router_return_url(&self) -> Result<String, Error>;
    fn is_wallet(&self) -> bool;
    fn is_card(&self) -> bool;
    fn get_payment_method_type(&self) -> Result<enums::PaymentMethodType, Error>;
    fn get_connector_mandate_id(&self) -> Result<String, Error>;
    fn get_complete_authorize_url(&self) -> Result<String, Error>;
    fn get_ip_address_as_optional(&self) -> Option<Secret<String, IpAddress>>;
    fn get_original_amount(&self) -> i64;
    fn get_surcharge_amount(&self) -> Option<i64>;
    fn get_tax_on_surcharge_amount(&self) -> Option<i64>;
    fn get_total_surcharge_amount(&self) -> Option<i64>;
    fn get_metadata_as_object(&self) -> Option<pii::SecretSerdeValue>;
    fn get_authentication_data(&self) -> Result<AuthenticationData, Error>;
    fn get_connector_mandate_request_reference_id(&self) -> Result<String, Error>;
}

pub trait PaymentMethodTokenizationRequestData {
    fn get_browser_info(&self) -> Result<BrowserInformation, Error>;
}

impl PaymentMethodTokenizationRequestData for types::PaymentMethodTokenizationData {
    fn get_browser_info(&self) -> Result<BrowserInformation, Error> {
        self.browser_info
            .clone()
            .ok_or_else(missing_field_err("browser_info"))
    }
}

impl PaymentsAuthorizeRequestData for types::PaymentsAuthorizeData {
    fn is_auto_capture(&self) -> Result<bool, Error> {
        match self.capture_method {
            Some(enums::CaptureMethod::Automatic)
            | None
            | Some(enums::CaptureMethod::SequentialAutomatic) => Ok(true),
            Some(enums::CaptureMethod::Manual) => Ok(false),
            Some(_) => Err(errors::ConnectorError::CaptureMethodNotSupported.into()),
        }
    }
    fn get_email(&self) -> Result<Email, Error> {
        self.email.clone().ok_or_else(missing_field_err("email"))
    }
    fn get_optional_email(&self) -> Option<Email> {
        self.email.clone()
    }
    fn get_browser_info(&self) -> Result<BrowserInformation, Error> {
        self.browser_info
            .clone()
            .ok_or_else(missing_field_err("browser_info"))
    }
    fn get_order_details(&self) -> Result<Vec<OrderDetailsWithAmount>, Error> {
        self.order_details
            .clone()
            .ok_or_else(missing_field_err("order_details"))
    }

    fn get_card(&self) -> Result<domain::Card, Error> {
        match self.payment_method_data.clone() {
            domain::PaymentMethodData::Card(card) => Ok(card),
            _ => Err(missing_field_err("card")()),
        }
    }

    fn get_complete_authorize_url(&self) -> Result<String, Error> {
        self.complete_authorize_url
            .clone()
            .ok_or_else(missing_field_err("complete_authorize_url"))
    }


impl auth_service::AuthService for Plaid {}

impl auth_service::AuthService for for Plaid


pub async fn signup_with_merchant_id(
    state: SessionState,
    request: user_api::SignUpWithMerchantIdRequest,
    auth_id: Option<String>,
    theme_id: Option<String>,
) -> UserResponse<user_api::SignUpWithMerchantIdResponse> {
    let new_user = domain::NewUser::try_from(request.clone())?;
    new_user
        .get_new_merchant()
        .get_new_organization()
        .insert_org_in_db(state.clone())
        .await?;

    let user_from_db = new_user
        .insert_user_and_merchant_in_db(state.clone())
        .await?;

    let _user_role = new_user
        .insert_org_level_user_role_in_db(
            state.clone(),
            common_utils::consts::ROLE_ID_ORGANIZATION_ADMIN.to_string(),
            UserStatus::Active,
        )
        .await?;

    let theme = theme_utils::get_theme_using_optional_theme_id(&state, theme_id).await?;

    let email_contents = email_types::ResetPassword {
        recipient_email: user_from_db.get_email().try_into()?,
        user_name: domain::UserName::new(user_from_db.get_name())?,
        settings: state.conf.clone(),
        auth_id,
        theme_id: theme.as_ref().map(|theme| theme.theme_id.clone()),
        theme_config: theme
            .map(|theme| theme.email_config())
            .unwrap_or(state.conf.theme.email_config.clone()),
    };

    let send_email_result = state
        .email_client
        .compose_and_send_email(
            user_utils::get_base_url(&state),
            Box::new(email_contents),
            state.conf.proxy.https_url.as_ref(),
        )
        .await;

    logger::info!(?send_email_result);
    Ok(ApplicationResponse::Json(user_api::AuthorizeResponse {
        is_email_sent: send_email_result.is_ok(),
        user_id: user_from_db.get_user_id().to_string(),
    }))
}


pub struct GlobepayRouterData<T> {
    pub amount: MinorUnit,
    pub router_data: T,
}


impl EuclidAnalysable for ConnectorSelection {
    fn get_dir_value_for_analysis(
        &self,
        rule_name: String,
    ) -> Vec<(euclid::frontend::dir::DirValue, euclid::types::Metadata)> {
        self.get_connector_list()
            .into_iter()
            .map(|connector_choice| {
                let connector_name = connector_choice.connector.to_string();
                let mca_id = connector_choice.merchant_connector_id.clone();

                (
                    euclid::frontend::dir::DirValue::Connector(Box::new(connector_choice.into())),
                    std::collections::HashMap::from_iter([(
                        "CONNECTOR_SELECTION".to_string(),
                        serde_json::json!({
                            "rule_name": rule_name,
                            "connector_name": connector_name,
                            "mca_id": mca_id,
                        }),
                    )]),
                )
            })
            .collect()
    }
}

impl EuclidAnalysable for for ConnectorSelection


pub struct PaymentCount;


pub struct StaxPaymentsRequest {
    payment_method_id: Secret<String>,
    total: f64,
    is_refundable: bool,
    pre_auth: bool,
    meta: StaxPaymentsRequestMetaData,
    idempotency_id: Option<String>,
}


pub struct NmiWebhookObject {
    pub transaction_id: String,
    pub order_id: String,
    pub condition: String,
    pub action: NmiActionBody,
}


pub struct RevenueRecoveryPaymentData {
    pub merchant_account: merchant_account::MerchantAccount,
    pub profile: business_profile::Profile,
    pub key_store: merchant_key_store::MerchantKeyStore,
    pub billing_mca: merchant_connector_account::MerchantConnectorAccount,
    pub retry_algorithm: enums::RevenueRecoveryAlgorithmType,
}


pub fn get_jwt_from_cookies(cookies: &str) -> RouterResult<String> {
    Cookie::split_parse(cookies)
        .find_map(|cookie| {
            cookie
                .ok()
                .filter(|parsed_cookie| parsed_cookie.name() == JWT_TOKEN_COOKIE_NAME)
                .map(|parsed_cookie| parsed_cookie.value().to_owned())
        })
        .ok_or(report!(ApiErrorResponse::InvalidJwtToken))
        .attach_printable("Unable to find JWT token in cookies")
}


pub struct UserAuthenticationMethodNew {
    pub id: String,
    pub auth_id: String,
    pub owner_id: String,
    pub owner_type: enums::Owner,
    pub auth_type: enums::UserAuthType,
    pub private_config: Option<Encryption>,
    pub public_config: Option<serde_json::Value>,
    pub allow_signup: bool,
    pub created_at: PrimitiveDateTime,
    pub last_modified_at: PrimitiveDateTime,
    pub email_domain: String,
}


pub async fn upload_file_to_theme_storage(
    state: web::Data<AppState>,
    req: HttpRequest,
    path: web::Path<String>,
    MultipartForm(payload)


pub async fn profile_acquirer_update(
    state: web::Data<AppState>,
    req: HttpRequest,
    path: web::Path<(
        common_utils::id_type::ProfileId,
        common_utils::id_type::ProfileAcquirerId,
    )


impl api::ConnectorAccessToken for Placetopay {}

impl api::ConnectorAccessToken for for Placetopay


pub struct WellsfargoPaymentInitiator {
    #[serde(rename = "type")]
    initiator_type: Option<WellsfargoPaymentInitiatorTypes>,
    credential_stored_on_file: Option<bool>,
    stored_credential_used: Option<bool>,
}


impl std::ops::Deref for Handler {
    type Target = HandlerInner;

    fn deref(&self) -> &Self::Target {
        &self.inner
    }
}


pub fn generate_authorization_signature(
        &self,
        auth: fiservemea::FiservemeaAuthType,
        request_id: &str,
        payload: &str,
        timestamp: i128,
    ) -> CustomResult<String, errors::ConnectorError> {
        let fiservemea::FiservemeaAuthType {
            api_key,
            secret_key,
        } = auth;
        let raw_signature = format!("{}{request_id}{timestamp}{payload}", api_key.peek());

        let key = hmac::Key::new(hmac::HMAC_SHA256, secret_key.expose().as_bytes());
        let signature_value = common_utils::consts::BASE64_ENGINE
            .encode(hmac::sign(&key, raw_signature.as_bytes()).as_ref());
        Ok(signature_value)
    }


pub async fn delete_by_address_id(
        conn: &PgPooledConn,
        address_id: &str,
    ) -> StorageResult<bool> {
        generics::generic_delete::<<Self as HasTable>::Table, _>(
            conn,
            dsl::address_id.eq(address_id.to_owned()),
        )
        .await
    }


impl api::PayoutFulfill for Nomupay {}


impl Responder {
    let flow = Flow::AuthenticationSync;
    let api_auth = auth::ApiKeyAuth::default();
    let (_merchant_id, authentication_id) = path.into_inner();
    let payload = AuthenticationSyncRequest {
        authentication_id,
        ..json_payload.into_inner()
    };
    let (auth, auth_flow) =
        match auth::check_client_secret_and_get_auth(req.headers(), &payload, api_auth) {
            Ok((auth, auth_flow)) => (auth, auth_flow),
            Err(e) => return api::log_and_return_error_response(e),
        };

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        payload,
        |state, auth: auth::AuthenticationData, req, _| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            unified_authentication_service::authentication_sync_core(
                state,
                merchant_context,
                auth_flow,
                req,
            )
        },
        &*auth,
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


impl ConnectorRedirectResponse for Bambora {
    fn get_flow_type(
        &self,
        _query_params: &str,
        _json_payload: Option<serde_json::Value>,
        action: enums::PaymentAction,
    ) -> CustomResult<enums::CallConnectorAction, errors::ConnectorError> {
        match action {
            enums::PaymentAction::PSync
            | enums::PaymentAction::CompleteAuthorize
            | enums::PaymentAction::PaymentAuthenticateCompleteAuthorize => {
                Ok(enums::CallConnectorAction::Trigger)
            }
        }
    }
}

impl ConnectorRedirectResponse for for Bambora