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

use hyperswitch_domain_models::payment_method_data::{Card, PaymentMethodData};
use masking::Secret;
use router::types::{self, api, storage::enums};
use test_utils::connector_auth;

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

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

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

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

static CONNECTOR: TrustpaymentsTest = TrustpaymentsTest {};

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// Connector dependent test cases goes here

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


impl<M> Metadata for M where M: erased_serde::Serialize + Any + Send + Sync + fmt::Debug {}


pub struct InsertResourceParams<'a> {
    pub insertable: kv::Insertable,
    pub reverse_lookups: Vec<String>,
    pub key: PartitionKey<'a>,
    // secondary key
    pub identifier: String,
    // type of resource Eg: "payment_attempt"
    pub resource_type: &'static str,
}


File: crates/drainer/build.rs

fn main() {
    #[cfg(feature = "vergen")]
    router_env::vergen::generate_cargo_instructions();
}


/// After we get the access token, check if there was an error and if the flow should proceed further
/// Returns bool
/// true - Everything is well, continue with the flow
/// false - There was an error, cannot proceed further
pub fn update_router_data_with_access_token_result<F, Req, Res>(
    add_access_token_result: &types::AddAccessTokenResult,
    router_data: &mut types::RouterData<F, Req, Res>,
    call_connector_action: &payments::CallConnectorAction,
) -> bool {
    // Update router data with access token or error only if it will be calling connector
    let should_update_router_data = matches!(
        (
            add_access_token_result.connector_supports_access_token,
            call_connector_action
        ),
        (true, payments::CallConnectorAction::Trigger)
    );

    if should_update_router_data {
        match add_access_token_result.access_token_result.as_ref() {
            Ok(access_token) => {
                router_data.access_token.clone_from(access_token);
                true
            }
            Err(connector_error) => {
                router_data.response = Err(connector_error.clone());
                false
            }
        }
    } else {
        true
    }
}


impl api::ConnectorAccessToken for Zen {}

impl api::ConnectorAccessToken for for Zen


pub struct SupportedMsgExt {
    pub id: String,
    pub version: String,
}


pub struct NetworkField {
    #[serde(rename = "@fieldNumber")]
    pub field_number: String,
    #[serde(rename = "@fieldName", skip_serializing_if = "Option::is_none")]
    pub field_name: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub field_value: Option<String>,
    #[serde(default)]
    #[serde(rename = "networkSubField")]
    pub network_sub_fields: Vec<NetworkSubField>,
}


pub async fn pg_accounts_connection_read<T: storage_impl::DatabaseStore>(
    store: &T,
) -> errors::CustomResult<
    PooledConnection<'_, async_bb8_diesel::ConnectionManager<PgConnection>>,
    storage_errors::StorageError,
> {
    // If only OLAP is enabled get replica pool.
    #[cfg(all(feature = "olap", not(feature = "oltp")))]
    let pool = store.get_accounts_replica_pool();

    // If either one of these are true we need to get master pool.
    //  1. Only OLTP is enabled.
    //  2. Both OLAP and OLTP is enabled.
    //  3. Both OLAP and OLTP is disabled.
    #[cfg(any(
        all(not(feature = "olap"), feature = "oltp"),
        all(feature = "olap", feature = "oltp"),
        all(not(feature = "olap"), not(feature = "oltp"))
    ))]
    let pool = store.get_accounts_master_pool();

    pool.get()
        .await
        .change_context(storage_errors::StorageError::DatabaseConnectionError)
}


pub struct PartnerConfigOverride {
    pub partner_logo_url: String,
    pub return_url: String,
}


OpenAPI Block Path: components.schemas.ToggleBlocklistResponse
{
  "type": "object",
  "required": [
    "blocklist_guard_status"
  ],
  "properties": {
    "blocklist_guard_status": {
      "type": "string"
    }
  }
}


pub struct Recon;


impl api::PaymentToken for Wellsfargo {}

impl api::PaymentToken for for Wellsfargo


pub struct SmartRetriedAmount;


impl IncomingWebhook for Threedsecureio {
    fn get_webhook_object_reference_id(
        &self,
        _request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<ObjectReferenceId, ConnectorError> {
        Err(report!(ConnectorError::WebhooksNotImplemented))
    }

    fn get_webhook_event_type(
        &self,
        _request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<IncomingWebhookEvent, ConnectorError> {
        Err(report!(ConnectorError::WebhooksNotImplemented))
    }

    fn get_webhook_resource_object(
        &self,
        _request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<Box<dyn masking::ErasedMaskSerialize>, ConnectorError> {
        Err(report!(ConnectorError::WebhooksNotImplemented))
    }
}

impl IncomingWebhook for for Threedsecureio


pub async fn get_or_create_custom_recovery_intent(
        data: api_models::payments::RecoveryPaymentsCreate,
        state: &SessionState,
        req_state: &ReqState,
        merchant_context: &domain::MerchantContext,
        profile: &domain::Profile,
    ) -> CustomResult<revenue_recovery::RecoveryPaymentIntent, errors::RevenueRecoveryError> {
        let recovery_intent = Self(revenue_recovery::RevenueRecoveryInvoiceData::foreign_from(
            data,
        ));
        recovery_intent
            .get_payment_intent(state, req_state, merchant_context, profile)
            .await
            .transpose()
            .async_unwrap_or_else(|| async {
                recovery_intent
                    .create_payment_intent(state, req_state, merchant_context, profile)
                    .await
            })
            .await
    }


impl PMAuthConfigValidation<'_> {
    async fn validate_pm_auth(&self, val: &pii::SecretSerdeValue) -> RouterResponse<()> {
        let config = serde_json::from_value::<api_models::pm_auth::PaymentMethodAuthConfig>(
            val.clone().expose(),
        )
        .change_context(errors::ApiErrorResponse::InvalidRequestData {
            message: "invalid data received for payment method auth config".to_string(),
        })
        .attach_printable("Failed to deserialize Payment Method Auth config")?;

        let all_mcas = self
            .db
            .find_merchant_connector_account_by_merchant_id_and_disabled_list(
                self.key_manager_state,
                self.merchant_id,
                true,
                self.key_store,
            )
            .await
            .change_context(errors::ApiErrorResponse::MerchantConnectorAccountNotFound {
                id: self.merchant_id.get_string_repr().to_owned(),
            })?;
        for conn_choice in config.enabled_payment_methods {
            let pm_auth_mca = all_mcas
                .iter()
                .find(|mca| mca.get_id() == conn_choice.mca_id)
                .ok_or(errors::ApiErrorResponse::GenericNotFoundError {
                    message: "payment method auth connector account not found".to_string(),
                })?;

            if &pm_auth_mca.profile_id != self.profile_id {
                return Err(errors::ApiErrorResponse::GenericNotFoundError {
                    message: "payment method auth profile_id differs from connector profile_id"
                        .to_string(),
                }
                .into());
            }
        }

        Ok(services::ApplicationResponse::StatusOk)
    }

    async fn validate_pm_auth_config(&self) -> RouterResult<()> {
        if self.connector_type != &api_enums::ConnectorType::PaymentMethodAuth {
            if let Some(val) = self.pm_auth_config.clone() {
                self.validate_pm_auth(&val).await?;
            }
        }
        Ok(())
    }
}


impl api::RefundExecute for Fiserv {}


impl super::auth_events::filters::AuthEventFilterAnalytics for ClickhouseClient {}

impl super::auth_events::filters::AuthEventFilterAnalytics for for ClickhouseClient


pub fn is_all_none(&self) -> bool {
        self.payment_method.is_none()
            && self.currency.is_none()
            && self.status.is_none()
            && self.customer_email.is_none()
            && self.search_tags.is_none()
            && self.connector.is_none()
            && self.payment_method_type.is_none()
            && self.card_network.is_none()
            && self.card_last_4.is_none()
            && self.payment_id.is_none()
            && self.amount.is_none()
            && self.customer_id.is_none()
    }


### Features

- **connector:**
  - [Worldpay] Use connector_request_reference_id as reference to the connector ([#2553](https://github.com/juspay/hyperswitch/pull/2553)) ([`9ea5830`](https://github.com/juspay/hyperswitch/commit/9ea5830befe333270f8f424753e1b46a439e79bb))
  - [ProphetPay] Template generation ([#2610](https://github.com/juspay/hyperswitch/pull/2610)) ([`7e6207e`](https://github.com/juspay/hyperswitch/commit/7e6207e6ca98fe2af9a61e272735e9d2292d6a92))
  - [Bambora] Use connector_response_reference_id as reference to the connector ([#2635](https://github.com/juspay/hyperswitch/pull/2635)) ([`a9b5dc9`](https://github.com/juspay/hyperswitch/commit/a9b5dc9ab767eb54a95bcebc4fd5a7b00dbf65f6))
  - [Klarna] Add order id as the reference id to merchant ([#2614](https://github.com/juspay/hyperswitch/pull/2614)) ([`b7d5573`](https://github.com/juspay/hyperswitch/commit/b7d557367a3a5aca478ffd2087af8077bc4e7e2b))

### Bug Fixes

- Payment_method_data and description null during payment confirm ([#2618](https://github.com/juspay/hyperswitch/pull/2618)) ([`6765a1c`](https://github.com/juspay/hyperswitch/commit/6765a1c695493499d1907c56d05bdcd80a2fea93))

### Refactors

- **connector:**
  - [Dlocal] Currency Unit Conversion ([#2615](https://github.com/juspay/hyperswitch/pull/2615)) ([`1f2fe51`](https://github.com/juspay/hyperswitch/commit/1f2fe5170ae318a8b1613f6f02538a36f30f0b3d))
  - [Iatapay] remove default case handling ([#2587](https://github.com/juspay/hyperswitch/pull/2587)) ([`6494e8a`](https://github.com/juspay/hyperswitch/commit/6494e8a6e4a195ecc9ca5b2f6ac0a636f06b03f7))
  - [noon] remove cancellation_reason ([#2627](https://github.com/juspay/hyperswitch/pull/2627)) ([`41b7742`](https://github.com/juspay/hyperswitch/commit/41b7742b5498bfa9ef32b9408ab2d9a7a43b01dc))
  - [Forte] Remove Default Case Handling ([#2625](https://github.com/juspay/hyperswitch/pull/2625)) ([`418715b`](https://github.com/juspay/hyperswitch/commit/418715b816337bcaeee1aceeb911e6d329add2ad))
  - [Dlocal] remove default case handling ([#2624](https://github.com/juspay/hyperswitch/pull/2624)) ([`1584313`](https://github.com/juspay/hyperswitch/commit/158431391d560be4a79160ccea7bf5feaa4b52db))
- Remove code related to temp locker ([#2640](https://github.com/juspay/hyperswitch/pull/2640)) ([`cc0b422`](https://github.com/juspay/hyperswitch/commit/cc0b42263257b6cf6c7f94350442a74d3c02750b))
- Add surcharge_applicable to payment_intent and remove surcharge_metadata from payment_attempt ([#2642](https://github.com/juspay/hyperswitch/pull/2642)) ([`e5fbaae`](https://github.com/juspay/hyperswitch/commit/e5fbaae0d4278681e5f589aa46c867e7904c4646))

### Testing

- **postman:** Update postman collection files ([`2593dd1`](https://github.com/juspay/hyperswitch/commit/2593dd17c30d7f327b54f3c386a9fd42ae8146ca))

### Miscellaneous Tasks

- **deps:** Bump rustix from 0.37.24 to 0.37.25 ([#2637](https://github.com/juspay/hyperswitch/pull/2637)) ([`67d0062`](https://github.com/juspay/hyperswitch/commit/67d006272158372a4b9ec65cbbe7b2ae8f35eb69))

### Build System / Dependencies

- **deps:** Use `async-bb8-diesel` from `crates.io` instead of git repository ([#2619](https://github.com/juspay/hyperswitch/pull/2619)) ([`14c0821`](https://github.com/juspay/hyperswitch/commit/14c0821b8085279072db3484a3b1bcdde0f7893b))

**Full Changelog:** [`v1.61.0...v1.62.0`](https://github.com/juspay/hyperswitch/compare/v1.61.0...v1.62.0)

- - -

## 1.61.0 (2023-10-18)

### Features

- **Connector:** [Paypal] add support for dispute webhooks for paypal connector ([#2353](https://github.com/juspay/hyperswitch/pull/2353)) ([`6cf8f05`](https://github.com/juspay/hyperswitch/commit/6cf8f0582cfa4f6a58c67a868cb67846970b3835))
- **apple_pay:** Add support for decrypted apple pay token for checkout ([#2628](https://github.com/juspay/hyperswitch/pull/2628)) ([`794dbc6`](https://github.com/juspay/hyperswitch/commit/794dbc6a766d12ff3cdf0b782abb4c48b8fa77d0))
- **connector:**
  - [Aci] Update connector_response_reference_id with merchant reference ([#2551](https://github.com/juspay/hyperswitch/pull/2551)) ([`9e450b8`](https://github.com/juspay/hyperswitch/commit/9e450b81ca8bc4b1ddbbe2c1d732dbc58c61934e))
  - [Bambora] use connector_request_reference_id ([#2518](https://github.com/juspay/hyperswitch/pull/2518)) ([`73e9391`](https://github.com/juspay/hyperswitch/commit/73e93910cd3bd668d721b15edb86240adc18f46b))
  - [Tsys] Use connector_request_reference_id as reference to the connector ([#2631](https://github.com/juspay/hyperswitch/pull/2631)) ([`b145463`](https://github.com/juspay/hyperswitch/commit/b1454634259144d896716e5cef37d9b8491f55b9))
- **core:** Replace temp locker with redis ([#2594](https://github.com/juspay/hyperswitch/pull/2594)) ([`2edbd61`](https://github.com/juspay/hyperswitch/commit/2edbd6123512a6f2f4d51d5c2d1ed8b6ee502813))
- **events:** Add events for incoming API requests ([#2621](https://github.com/juspay/hyperswitch/pull/2621)) ([`7a76d6c`](https://github.com/juspay/hyperswitch/commit/7a76d6c01a0c6087c6429e58cc9dd6b4ea7fc0aa))
- **merchant_account:** Add merchant account list endpoint  ([#2560](https://github.com/juspay/hyperswitch/pull/2560)) ([`a1472c6`](https://github.com/juspay/hyperswitch/commit/a1472c6b78afa819cbe026a7db1e0c2b9016715e))
- Update surcharge_amount and tax_amount in update_trackers of payment_confirm ([#2603](https://github.com/juspay/hyperswitch/pull/2603)) ([`2f9a355`](https://github.com/juspay/hyperswitch/commit/2f9a3557f63150bcd27e27c6510a799669706718))

### Bug Fixes

- **connector:**
  - [Authorizedotnet]fix error deserialization incase of authentication failure ([#2600](https://github.com/juspay/hyperswitch/pull/2600)) ([`4859b7d`](https://github.com/juspay/hyperswitch/commit/4859b7da73125c2da72f4754863ff4485bebce29))
  - [Paypal]fix error deserelization for source verification call ([#2611](https://github.com/juspay/hyperswitch/pull/2611)) ([`da77d13`](https://github.com/juspay/hyperswitch/commit/da77d1393b8f6ab658dd7f3c202dd6c7d15c0ebd))
- **payments:** Fix payment update enum being inserted into kv ([#2612](https://github.com/juspay/hyperswitch/pull/2612)) ([`9aa1c75`](https://github.com/juspay/hyperswitch/commit/9aa1c75eca24caa14af5f4801173cd59f76d7e57))

### Refactors

- **events:** Allow box dyn for event handler ([#2629](https://github.com/juspay/hyperswitch/pull/2629)) ([`01410bb`](https://github.com/juspay/hyperswitch/commit/01410bb9f233637e98f27ebe509e859c7dad2cf4))
- **payment_connector:** Allow connector label to be updated ([#2622](https://github.com/juspay/hyperswitch/pull/2622)) ([`c86ac9b`](https://github.com/juspay/hyperswitch/commit/c86ac9b1fe5388666463aa16c899427a2bf442fb))
- **router:** Remove unnecessary function from Refunds Validate Flow ([#2609](https://github.com/juspay/hyperswitch/pull/2609)) ([`3399328`](https://github.com/juspay/hyperswitch/commit/3399328ae7f525fb72e0751182cf32d0b2470594))
- Refactor connector auth type failure to 4xx ([#2616](https://github.com/juspay/hyperswitch/pull/2616)) ([`1dad745`](https://github.com/juspay/hyperswitch/commit/1dad7455c4ae8d26d52c44d90f5b8d815d85d205))

### Testing

- **postman:** Update postman collection files ([`d899025`](https://github.com/juspay/hyperswitch/commit/d89902507486b8b97011fb63ed0343f727255ca2))

### Documentation

- **postman:** Rewrite postman documentation to help devs develop tests for their features ([#2613](https://github.com/juspay/hyperswitch/pull/2613)) ([`1548ee6`](https://github.com/juspay/hyperswitch/commit/1548ee62b661200fcb9d439d16c072a66dbfa718))

### Miscellaneous Tasks

- **scripts:** Add connector script changes ([#2620](https://github.com/juspay/hyperswitch/pull/2620)) ([`373a10b`](https://github.com/juspay/hyperswitch/commit/373a10beffc7cddef6ff76f5c8fff91ca3618581))

**Full Changelog:** [`v1.60.0...v1.61.0`](https://github.com/juspay/hyperswitch/compare/v1.60.0...v1.61.0)

- - -

## 1.60.0 (2023-10-17)

### Features

- **compatibility:** Added support to connector txn id ([#2606](https://github.com/juspay/hyperswitch/pull/2606)) ([`82980a8`](https://github.com/juspay/hyperswitch/commit/82980a86ad7966c6645d26a4abec85c8c7e3bdad))
- **router:** Better UI payment link and order details product image and merchant config support ([#2583](https://github.com/juspay/hyperswitch/pull/2583)) ([`fdd9580`](https://github.com/juspay/hyperswitch/commit/fdd95800127bb79fe2a9eeca1b7e0e158b6d2783))
- Add updated_by to tracker tables ([#2604](https://github.com/juspay/hyperswitch/pull/2604)) ([`6a74e8c`](https://github.com/juspay/hyperswitch/commit/6a74e8cba9078529fd9662d29ac7b941a191fbf4))

### Bug Fixes

- Make push to drainer generic and add application metrics for KV ([#2563](https://github.com/juspay/hyperswitch/pull/2563)) ([`274a783`](https://github.com/juspay/hyperswitch/commit/274a78343e5e3de614cfb1476570b5c449ee0c1e))

### Refactors

- **connector:** [Nuvei] remove default case handling ([#2584](https://github.com/juspay/hyperswitch/pull/2584)) ([`3807601`](https://github.com/juspay/hyperswitch/commit/3807601ee1c140310abf7a7e6ee4b83d44de9558))
- **router:** Throw bad request error on applepay verification failure ([#2607](https://github.com/juspay/hyperswitch/pull/2607)) ([`cecea87`](https://github.com/juspay/hyperswitch/commit/cecea8718a48b4e896b2bafce0f909ef8d9a6e8a))

**Full Changelog:** [`v1.59.0...v1.60.0`](https://github.com/juspay/hyperswitch/compare/v1.59.0...v1.60.0)

- - -

## 1.59.0 (2023-10-16)

### Features

- **connector:**
  - Add support for surcharge in trustpay ([#2581](https://github.com/juspay/hyperswitch/pull/2581)) ([`2d5d3b8`](https://github.com/juspay/hyperswitch/commit/2d5d3b8efbf782bf03e5f5ef1aa557d3dd3f5860))
  - Add surcharge support in paypal connector ([#2568](https://github.com/juspay/hyperswitch/pull/2568)) ([`92ee1db`](https://github.com/juspay/hyperswitch/commit/92ee1db107ac41326ecfb31b4565664a29a4b80a))
- **events:** Add basic event handler to collect application events ([#2602](https://github.com/juspay/hyperswitch/pull/2602)) ([`5d88dbc`](https://github.com/juspay/hyperswitch/commit/5d88dbc92ce470c951717debe246e182b3fe5656))

### Refactors

- **connector:** [multisafepay] Remove Default Case Handling ([#2586](https://github.com/juspay/hyperswitch/pull/2586)) ([`7adc6a0`](https://github.com/juspay/hyperswitch/commit/7adc6a05b60fa9143260b2a7f623907647557621))

**Full Changelog:** [`v1.58.0...v1.59.0`](https://github.com/juspay/hyperswitch/compare/v1.58.0...v1.59.0)

- - -

## 1.58.0 (2023-10-15)

### Features

- **connector:**
  - [HELCIM] Implement Cards for Helcim ([#2210](https://github.com/juspay/hyperswitch/pull/2210)) ([`b5feab6`](https://github.com/juspay/hyperswitch/commit/b5feab61d950921c75267ad88e944e7e2c4af3ca))
  - [Paypal] use connector request reference id as reference for paypal ([#2577](https://github.com/juspay/hyperswitch/pull/2577)) ([`500405d`](https://github.com/juspay/hyperswitch/commit/500405d78938772e0e9f8e3ce4f930d782c670fa))
  - [Airwallex] Currency Unit Conversion ([#2571](https://github.com/juspay/hyperswitch/pull/2571)) ([`8971b17`](https://github.com/juspay/hyperswitch/commit/8971b17b073315f869e3c843b0aee7644dcf6479))
  - [Klarna] Use connector_request_reference_id as reference to connector ([#2494](https://github.com/juspay/hyperswitch/pull/2494)) ([`2609ef6`](https://github.com/juspay/hyperswitch/commit/2609ef6aeb17e1e89d8f98ff84a2c33b9704e6b2))
  - [Dlocal] Use connector_response_reference_id as reference to merchant ([#2446](https://github.com/juspay/hyperswitch/pull/2446)) ([`f6677b8`](https://github.com/juspay/hyperswitch/commit/f6677b8e9300a75810a39de5b60243e34cf1d76c))
- **nexinets:** Use connector_request_reference_id as reference to the connector - Work In Progress  ([#2515](https://github.com/juspay/hyperswitch/pull/2515)) ([`088dce0`](https://github.com/juspay/hyperswitch/commit/088dce076d8d8ff86769717368150e09d7d92593))
- **router:** Add Cancel Event in Webhooks and Mapping it in Stripe ([#2573](https://github.com/juspay/hyperswitch/pull/2573)) ([`92f7918`](https://github.com/juspay/hyperswitch/commit/92f7918e6f98460fb739d50b908ae33fda2f80b8))

### Refactors

- **connector:**
  - [Worldline] Currency Unit Conversion ([#2569](https://github.com/juspay/hyperswitch/pull/2569)) ([`9f03a41`](https://github.com/juspay/hyperswitch/commit/9f03a4118ccdd6036d27074c9126a79d6e9b0495))
  - [Authorizedotnet] Enhance currency Mapping with ConnectorCurrencyCommon Trait ([#2570](https://github.com/juspay/hyperswitch/pull/2570)) ([`d401975`](https://github.com/juspay/hyperswitch/commit/d4019751ff4acbd26abb2c32a600e8e6c55893f6))
  - [noon] enhance response status mapping ([#2575](https://github.com/juspay/hyperswitch/pull/2575)) ([`053c79d`](https://github.com/juspay/hyperswitch/commit/053c79d248df0ff6ec702c3c301acc5654a1735a))
- **storage:** Update paymentintent object to provide a relation with attempts ([#2502](https://github.com/juspay/hyperswitch/pull/2502)) ([`fbf3c03`](https://github.com/juspay/hyperswitch/commit/fbf3c03d418242b1f5f1a15c69029023d0b25b4e))

### Testing

- **postman:** Update postman collection files ([`08141ab`](https://github.com/juspay/hyperswitch/commit/08141abb3e87504bb4fe54fdfea92e6c889d729a))

**Full Changelog:** [`v1.57.1+hotfix.1...v1.58.0`](https://github.com/juspay/hyperswitch/compare/v1.57.1+hotfix.1...v1.58.0)

- - -

## 1.57.1 (2023-10-12)

### Bug Fixes

- **connector:** Trigger Psync after redirection url ([#2422](https://github.com/juspay/hyperswitch/pull/2422)) ([`8029a89`](https://github.com/juspay/hyperswitch/commit/8029a895b2c27a1ac14a19aea23bbc06cc364809))

**Full Changelog:** [`v1.57.0...v1.57.1`](https://github.com/juspay/hyperswitch/compare/v1.57.0...v1.57.1)

- - -

## 1.57.0 (2023-10-12)

### Features

- **connector:**
  - [Tsys] Use `connector_response_reference_id` as reference to the connector ([#2546](https://github.com/juspay/hyperswitch/pull/2546)) ([`550377a`](https://github.com/juspay/hyperswitch/commit/550377a6c3943d9fec4ca6a8be5a5f3aafe109ab))
  - [Cybersource] Use connector_request_reference_id as reference to the connector ([#2512](https://github.com/juspay/hyperswitch/pull/2512)) ([`81cb8da`](https://github.com/juspay/hyperswitch/commit/81cb8da4d47fe2a75330d39c665bb259faa35b00))
  - [Iatapay] use connector_response_reference_id as reference to connector ([#2524](https://github.com/juspay/hyperswitch/pull/2524)) ([`ef647b7`](https://github.com/juspay/hyperswitch/commit/ef647b7ab942707a06971b6545c81168f28cb94c))
  - [ACI] Use connector_request_reference_id as reference to the connector ([#2549](https://github.com/juspay/hyperswitch/pull/2549)) ([`c2ad200`](https://github.com/juspay/hyperswitch/commit/c2ad2002c0e6d673f62ec4c72c8fd98b07a05c0b))
- **customers:** Add customer list endpoint ([#2564](https://github.com/juspay/hyperswitch/pull/2564)) ([`c26620e`](https://github.com/juspay/hyperswitch/commit/c26620e041add914abc60c6149787be62ea5985d))
- **router:**
  - Add kv implementation for update address in update payments flow ([#2542](https://github.com/juspay/hyperswitch/pull/2542)) ([`9f446bc`](https://github.com/juspay/hyperswitch/commit/9f446bc1742c06a7fab3d92128ba4e7d3be80ea6))
  - Add payment link support ([#2105](https://github.com/juspay/hyperswitch/pull/2105)) ([`642085d`](https://github.com/juspay/hyperswitch/commit/642085dc745f87b4edd2f7a744c31b8979b23cfa))

### Bug Fixes

- **connector:**
  - [noon] sync with reference_id ([#2544](https://github.com/juspay/hyperswitch/pull/2544)) ([`9ef60e4`](https://github.com/juspay/hyperswitch/commit/9ef60e425d0cbe764ce66c65c8c09b1992cbe99f))
  - [braintree] add 3ds redirection error mapping and metadata validation ([#2552](https://github.com/juspay/hyperswitch/pull/2552)) ([`28d02f9`](https://github.com/juspay/hyperswitch/commit/28d02f94c6d52d05b6f520e4d48ba88adf7be619))
- **router:** Add customer_id validation for `payment method create` flow ([#2543](https://github.com/juspay/hyperswitch/pull/2543)) ([`53d7604`](https://github.com/juspay/hyperswitch/commit/53d760460305e16f03d86f699acb035151dfdfad))
- Percentage float inconsistency problem and api models changes to support surcharge feature ([#2550](https://github.com/juspay/hyperswitch/pull/2550)) ([`1ee1184`](https://github.com/juspay/hyperswitch/commit/1ee11849d4a60afbf3d05103cb491a11e905b811))
- Consume profile_id throughout payouts flow ([#2501](https://github.com/juspay/hyperswitch/pull/2501)) ([`7eabd24`](https://github.com/juspay/hyperswitch/commit/7eabd24a4da6f82fd30f8a4be739962538654214))
- Parse allowed_payment_method_types only if there is some value p… ([#2161](https://github.com/juspay/hyperswitch/pull/2161)) ([`46f1419`](https://github.com/juspay/hyperswitch/commit/46f14191ab7e036539ef3fd58acd9376b6b6b63c))

### Refactors

- **connector:**
  - [Worldpay] Currency Unit Conversion ([#2436](https://github.com/juspay/hyperswitch/pull/2436)) ([`b78109b`](https://github.com/juspay/hyperswitch/commit/b78109bc93433e0886b0b8656231899df84da8cf))
  - [noon] use connector_request_reference_id for sync ([#2558](https://github.com/juspay/hyperswitch/pull/2558)) ([`0889a6e`](https://github.com/juspay/hyperswitch/commit/0889a6ed0691abeed7bba44e7024545abcc74aef))
  - [noon] update and add recommended fields  ([#2381](https://github.com/juspay/hyperswitch/pull/2381)) ([`751f16e`](https://github.com/juspay/hyperswitch/commit/751f16eaee254ab8f0068e2e9e81e3e4b7fe133f))
- **worldline:** Use `connector_request_reference_id` as reference to the connector ([#2498](https://github.com/juspay/hyperswitch/pull/2498)) ([`efa5320`](https://github.com/juspay/hyperswitch/commit/efa53204e8ab1ef1192bcdc07ed99306475badbc))

### Revert

- Fix(connector): [noon] sync with reference_id ([#2556](https://github.com/juspay/hyperswitch/pull/2556)) ([`13be4d3`](https://github.com/juspay/hyperswitch/commit/13be4d36eac3d1e17d8ad9b3f3ef8993547f548b))

**Full Changelog:** [`v1.56.0...v1.57.0`](https://github.com/juspay/hyperswitch/compare/v1.56.0...v1.57.0)

- - -

## 1.56.0 (2023-10-11)

### Features

- **connector:**
  - [Volt] Template generation ([#2480](https://github.com/juspay/hyperswitch/pull/2480)) ([`ee321bb`](https://github.com/juspay/hyperswitch/commit/ee321bb82686559643d8c2725b0491997af717b2))


pub struct SdkEventMetricRow {
    pub total: Option<bigdecimal::BigDecimal>,
    pub count: Option<i64>,
    pub time_bucket: Option<String>,
    pub payment_method: Option<String>,
    pub platform: Option<String>,
    pub browser_name: Option<String>,
    pub source: Option<String>,
    pub component: Option<String>,
    pub payment_experience: Option<String>,
}


pub fn create_address(self, source: Address) -> Address {
        Address {
            city: self.city,
            country: self.country,
            line1: self.line1,
            line2: self.line2,
            line3: self.line3,
            state: self.state,
            zip: self.zip,
            first_name: self.first_name,
            last_name: self.last_name,
            phone_number: self.phone_number,
            country_code: self.country_code,
            modified_at: self.modified_at,
            updated_by: self.updated_by,
            origin_zip: self.origin_zip,
            ..source
        }
    }


pub struct Balance;


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


impl api::PaymentVoid for Stripe {}


impl CardInfoBuilder<CardInfoUpdate> {
    fn set_updated_card_info(
        self,
        card_info: card_info_models::CardInfo,
    ) -> CardInfoBuilder<CardInfoResponse> {
        CardInfoBuilder {
            state: std::marker::PhantomData,
            card_info: Some(card_info),
        }
    }
}


impl DecisionEngineSRSubLevelInputConfig {
    pub fn update(&mut self, new_config: Self) {
        if let Some(payment_method_type) = new_config.payment_method_type {
            self.payment_method_type = Some(payment_method_type);
        }
        if let Some(payment_method) = new_config.payment_method {
            self.payment_method = Some(payment_method);
        }
        if let Some(latency_threshold) = new_config.latency_threshold {
            self.latency_threshold = Some(latency_threshold);
        }
        if let Some(bucket_size) = new_config.bucket_size {
            self.bucket_size = Some(bucket_size);
        }
        if let Some(hedging_percent) = new_config.hedging_percent {
            self.hedging_percent = Some(hedging_percent);
        }
        if let Some(lower_reset_factor) = new_config.lower_reset_factor {
            self.lower_reset_factor = Some(lower_reset_factor);
        }
        if let Some(upper_reset_factor) = new_config.upper_reset_factor {
            self.upper_reset_factor = Some(upper_reset_factor);
        }
        if let Some(gateway_extra_score) = new_config.gateway_extra_score {
            self.gateway_extra_score
                .as_mut()
                .map(|score| score.extend(gateway_extra_score));
        }
    }
}


pub struct ConsumerSettings {
    pub disabled: bool,
    pub consumer_group: String,
}


pub async fn get_dynamic_routing_volume_split(
    state: web::Data<AppState>,
    req: HttpRequest,
    path: web::Path<routing_types::ToggleDynamicRoutingPath>,
) -> impl Responder {
    let flow = Flow::VolumeSplitOnRoutingType;

    let payload = path.into_inner();
    Box::pin(oss_api::server_wrap(
        flow,
        state,
        &req,
        payload.clone(),
        |state, auth: auth::AuthenticationData, payload, _| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            routing::retrieve_dynamic_routing_volume_split(
                state,
                merchant_context,
                payload.profile_id,
            )
        },
        auth::auth_type(
            &auth::HeaderAuth(auth::ApiKeyAuth {
                is_connected_allowed: false,
                is_platform_allowed: false,
            }),
            &auth::JWTAuthProfileFromRoute {
                profile_id: payload.profile_id,
                required_permission: Permission::ProfileRoutingRead,
            },
            req.headers(),
        ),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


pub struct PaymentAttempt {
    pub payment_id: id_type::PaymentId,
    pub merchant_id: id_type::MerchantId,
    pub attempt_id: String,
    pub status: storage_enums::AttemptStatus,
    pub net_amount: NetAmount,
    pub currency: Option<storage_enums::Currency>,
    pub save_to_locker: Option<bool>,
    pub connector: Option<String>,
    pub error_message: Option<String>,
    pub offer_amount: Option<MinorUnit>,
    pub payment_method_id: Option<String>,
    pub payment_method: Option<storage_enums::PaymentMethod>,
    pub connector_transaction_id: Option<String>,
    pub capture_method: Option<storage_enums::CaptureMethod>,
    #[serde(default, with = "common_utils::custom_serde::iso8601::option")]
    pub capture_on: Option<PrimitiveDateTime>,
    pub confirm: bool,
    pub authentication_type: Option<storage_enums::AuthenticationType>,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub created_at: PrimitiveDateTime,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub modified_at: PrimitiveDateTime,
    #[serde(default, with = "common_utils::custom_serde::iso8601::option")]
    pub last_synced: Option<PrimitiveDateTime>,
    pub cancellation_reason: Option<String>,
    pub amount_to_capture: Option<MinorUnit>,
    pub mandate_id: Option<String>,
    pub browser_info: Option<serde_json::Value>,
    pub error_code: Option<String>,
    pub payment_token: Option<String>,
    pub connector_metadata: Option<serde_json::Value>,
    pub payment_experience: Option<storage_enums::PaymentExperience>,
    pub payment_method_type: Option<storage_enums::PaymentMethodType>,
    pub payment_method_data: Option<serde_json::Value>,
    pub business_sub_label: Option<String>,
    pub straight_through_algorithm: Option<serde_json::Value>,
    pub preprocessing_step_id: Option<String>,
    // providing a location to store mandate details intermediately for transaction
    pub mandate_details: Option<MandateDataType>,
    pub error_reason: Option<String>,
    pub multiple_capture_count: Option<i16>,
    // reference to the payment at connector side
    pub connector_response_reference_id: Option<String>,
    pub amount_capturable: MinorUnit,
    pub updated_by: String,
    pub authentication_data: Option<serde_json::Value>,
    pub encoded_data: Option<String>,
    pub merchant_connector_id: Option<id_type::MerchantConnectorAccountId>,
    pub unified_code: Option<String>,
    pub unified_message: Option<String>,
    pub external_three_ds_authentication_attempted: Option<bool>,
    pub authentication_connector: Option<String>,
    pub authentication_id: Option<id_type::AuthenticationId>,
    pub mandate_data: Option<MandateDetails>,
    pub payment_method_billing_address_id: Option<String>,
    pub fingerprint_id: Option<String>,
    pub charge_id: Option<String>,
    pub client_source: Option<String>,
    pub client_version: Option<String>,
    pub customer_acceptance: Option<pii::SecretSerdeValue>,
    pub profile_id: id_type::ProfileId,
    pub organization_id: id_type::OrganizationId,
    pub connector_mandate_detail: Option<ConnectorMandateReferenceId>,
    pub request_extended_authorization: Option<RequestExtendedAuthorizationBool>,
    pub extended_authorization_applied: Option<ExtendedAuthorizationAppliedBool>,
    pub capture_before: Option<PrimitiveDateTime>,
    pub card_discovery: Option<common_enums::CardDiscovery>,
    pub charges: Option<common_types::payments::ConnectorChargeResponseData>,
    pub issuer_error_code: Option<String>,
    pub issuer_error_message: Option<String>,
    /// merchant who owns the credentials of the processor, i.e. processor owner
    pub processor_merchant_id: id_type::MerchantId,
    /// merchantwho invoked the resource based api (identifier) and through what source (Api, Jwt(Dashboard))
    pub created_by: Option<CreatedBy>,
    pub setup_future_usage_applied: Option<storage_enums::FutureUsage>,
    pub routing_approach: Option<storage_enums::RoutingApproach>,
    pub connector_request_reference_id: Option<String>,
    pub debit_routing_savings: Option<MinorUnit>,
    pub network_transaction_id: Option<String>,
}


impl api::PaymentSync for Nomupay {}


OpenAPI Block Path: paths."/events/{merchant_id}/{event_id}/attempts"
{
  "get": {
    "tags": [
      "Event"
    ],
    "summary": "Events - Delivery Attempt List",
    "description": "List all delivery attempts for the specified Event.",
    "operationId": "List all delivery attempts for an Event",
    "parameters": [
      {
        "name": "merchant_id",
        "in": "path",
        "description": "The unique identifier for the Merchant Account.",
        "required": true,
        "schema": {
          "type": "string"
        }
      },
      {
        "name": "event_id",
        "in": "path",
        "description": "The unique identifier for the Event",
        "required": true,
        "schema": {
          "type": "string"
        }
      }
    ],
    "responses": {
      "200": {
        "description": "List of delivery attempts retrieved successfully",
        "content": {
          "application/json": {
            "schema": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/EventRetrieveResponse"
              }
            }
          }
        }
      }
    },
    "security": [
      {
        "admin_api_key": []
      }
    ]
  }
}


impl AuthInfo for AuthenticationData {
    fn get_merchant_id(&self) -> Option<&id_type::MerchantId> {
        Some(self.merchant_account.get_id())
    }
}

impl AuthInfo for for AuthenticationData


impl ConnectorSpecifications for Opayo {}

impl ConnectorSpecifications for for Opayo


File: crates/currency_conversion/src/types.rs
 Public functions: 5
 Public structs: 2

use std::collections::HashMap;

use common_enums::Currency;
use rust_decimal::Decimal;
use rusty_money::iso;

use crate::error::CurrencyConversionError;

/// Cached currency store of base currency
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct ExchangeRates {
    pub base_currency: Currency,
    pub conversion: HashMap<Currency, CurrencyFactors>,
}

/// Stores the multiplicative factor for  conversion between currency to base and vice versa
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct CurrencyFactors {
    /// The factor that will be multiplied to provide Currency output
    pub to_factor: Decimal,
    /// The factor that will be multiplied to provide for the base output
    pub from_factor: Decimal,
}

impl CurrencyFactors {
    pub fn new(to_factor: Decimal, from_factor: Decimal) -> Self {
        Self {
            to_factor,
            from_factor,
        }
    }
}

impl ExchangeRates {
    pub fn new(base_currency: Currency, conversion: HashMap<Currency, CurrencyFactors>) -> Self {
        Self {
            base_currency,
            conversion,
        }
    }

    /// The flow here is from_currency -> base_currency -> to_currency
    /// from to_currency -> base currency
    pub fn forward_conversion(
        &self,
        amt: Decimal,
        from_currency: Currency,
    ) -> Result<Decimal, CurrencyConversionError> {
        let from_factor = self
            .conversion
            .get(&from_currency)
            .ok_or_else(|| {
                CurrencyConversionError::ConversionNotSupported(from_currency.to_string())
            })?
            .from_factor;
        amt.checked_mul(from_factor)
            .ok_or(CurrencyConversionError::DecimalMultiplicationFailed)
    }

    /// from base_currency -> to_currency
    pub fn backward_conversion(
        &self,
        amt: Decimal,
        to_currency: Currency,
    ) -> Result<Decimal, CurrencyConversionError> {
        let to_factor = self
            .conversion
            .get(&to_currency)
            .ok_or_else(|| {
                CurrencyConversionError::ConversionNotSupported(to_currency.to_string())
            })?
            .to_factor;
        amt.checked_mul(to_factor)
            .ok_or(CurrencyConversionError::DecimalMultiplicationFailed)
    }
}

pub fn currency_match(currency: Currency) -> &'static iso::Currency {
    match currency {
        Currency::AED => iso::AED,
        Currency::AFN => iso::AFN,
        Currency::ALL => iso::ALL,
        Currency::AMD => iso::AMD,
        Currency::ANG => iso::ANG,
        Currency::AOA => iso::AOA,
        Currency::ARS => iso::ARS,
        Currency::AUD => iso::AUD,
        Currency::AWG => iso::AWG,
        Currency::AZN => iso::AZN,
        Currency::BAM => iso::BAM,
        Currency::BBD => iso::BBD,
        Currency::BDT => iso::BDT,
        Currency::BGN => iso::BGN,
        Currency::BHD => iso::BHD,
        Currency::BIF => iso::BIF,
        Currency::BMD => iso::BMD,
        Currency::BND => iso::BND,
        Currency::BOB => iso::BOB,
        Currency::BRL => iso::BRL,
        Currency::BSD => iso::BSD,
        Currency::BTN => iso::BTN,
        Currency::BWP => iso::BWP,
        Currency::BYN => iso::BYN,
        Currency::BZD => iso::BZD,
        Currency::CAD => iso::CAD,
        Currency::CDF => iso::CDF,
        Currency::CHF => iso::CHF,
        Currency::CLF => iso::CLF,
        Currency::CLP => iso::CLP,
        Currency::CNY => iso::CNY,
        Currency::COP => iso::COP,
        Currency::CRC => iso::CRC,
        Currency::CUC => iso::CUC,
        Currency::CUP => iso::CUP,
        Currency::CVE => iso::CVE,
        Currency::CZK => iso::CZK,
        Currency::DJF => iso::DJF,
        Currency::DKK => iso::DKK,
        Currency::DOP => iso::DOP,
        Currency::DZD => iso::DZD,
        Currency::EGP => iso::EGP,
        Currency::ERN => iso::ERN,
        Currency::ETB => iso::ETB,
        Currency::EUR => iso::EUR,
        Currency::FJD => iso::FJD,
        Currency::FKP => iso::FKP,
        Currency::GBP => iso::GBP,
        Currency::GEL => iso::GEL,
        Currency::GHS => iso::GHS,
        Currency::GIP => iso::GIP,
        Currency::GMD => iso::GMD,
        Currency::GNF => iso::GNF,
        Currency::GTQ => iso::GTQ,
        Currency::GYD => iso::GYD,
        Currency::HKD => iso::HKD,
        Currency::HNL => iso::HNL,
        Currency::HRK => iso::HRK,
        Currency::HTG => iso::HTG,
        Currency::HUF => iso::HUF,
        Currency::IDR => iso::IDR,
        Currency::ILS => iso::ILS,
        Currency::INR => iso::INR,
        Currency::IQD => iso::IQD,
        Currency::IRR => iso::IRR,
        Currency::ISK => iso::ISK,
        Currency::JMD => iso::JMD,
        Currency::JOD => iso::JOD,
        Currency::JPY => iso::JPY,
        Currency::KES => iso::KES,
        Currency::KGS => iso::KGS,
        Currency::KHR => iso::KHR,
        Currency::KMF => iso::KMF,
        Currency::KPW => iso::KPW,
        Currency::KRW => iso::KRW,
        Currency::KWD => iso::KWD,
        Currency::KYD => iso::KYD,
        Currency::KZT => iso::KZT,
        Currency::LAK => iso::LAK,
        Currency::LBP => iso::LBP,
        Currency::LKR => iso::LKR,
        Currency::LRD => iso::LRD,
        Currency::LSL => iso::LSL,
        Currency::LYD => iso::LYD,
        Currency::MAD => iso::MAD,
        Currency::MDL => iso::MDL,
        Currency::MGA => iso::MGA,
        Currency::MKD => iso::MKD,
        Currency::MMK => iso::MMK,
        Currency::MNT => iso::MNT,
        Currency::MOP => iso::MOP,
        Currency::MRU => iso::MRU,
        Currency::MUR => iso::MUR,
        Currency::MVR => iso::MVR,
        Currency::MWK => iso::MWK,
        Currency::MXN => iso::MXN,
        Currency::MYR => iso::MYR,
        Currency::MZN => iso::MZN,
        Currency::NAD => iso::NAD,
        Currency::NGN => iso::NGN,
        Currency::NIO => iso::NIO,
        Currency::NOK => iso::NOK,
        Currency::NPR => iso::NPR,
        Currency::NZD => iso::NZD,
        Currency::OMR => iso::OMR,
        Currency::PAB => iso::PAB,
        Currency::PEN => iso::PEN,
        Currency::PGK => iso::PGK,
        Currency::PHP => iso::PHP,
        Currency::PKR => iso::PKR,
        Currency::PLN => iso::PLN,
        Currency::PYG => iso::PYG,
        Currency::QAR => iso::QAR,
        Currency::RON => iso::RON,
        Currency::RSD => iso::RSD,
        Currency::RUB => iso::RUB,
        Currency::RWF => iso::RWF,
        Currency::SAR => iso::SAR,
        Currency::SBD => iso::SBD,
        Currency::SCR => iso::SCR,
        Currency::SDG => iso::SDG,
        Currency::SEK => iso::SEK,
        Currency::SGD => iso::SGD,
        Currency::SHP => iso::SHP,
        Currency::SLE => iso::SLE,
        Currency::SLL => iso::SLL,
        Currency::SOS => iso::SOS,
        Currency::SRD => iso::SRD,
        Currency::SSP => iso::SSP,
        Currency::STD => iso::STD,
        Currency::STN => iso::STN,
        Currency::SVC => iso::SVC,
        Currency::SYP => iso::SYP,
        Currency::SZL => iso::SZL,
        Currency::THB => iso::THB,
        Currency::TJS => iso::TJS,
        Currency::TND => iso::TND,
        Currency::TMT => iso::TMT,
        Currency::TOP => iso::TOP,
        Currency::TTD => iso::TTD,
        Currency::TRY => iso::TRY,
        Currency::TWD => iso::TWD,
        Currency::TZS => iso::TZS,
        Currency::UAH => iso::UAH,
        Currency::UGX => iso::UGX,
        Currency::USD => iso::USD,
        Currency::UYU => iso::UYU,
        Currency::UZS => iso::UZS,
        Currency::VES => iso::VES,
        Currency::VND => iso::VND,
        Currency::VUV => iso::VUV,
        Currency::WST => iso::WST,
        Currency::XAF => iso::XAF,
        Currency::XCD => iso::XCD,
        Currency::XOF => iso::XOF,
        Currency::XPF => iso::XPF,
        Currency::YER => iso::YER,
        Currency::ZAR => iso::ZAR,
        Currency::ZMW => iso::ZMW,
        Currency::ZWL => iso::ZWL,
    }
}


impl Recurly {
    pub fn new() -> &'static Self {
        &Self {}
    }

    fn get_signature_elements_from_header(
        headers: &actix_web::http::header::HeaderMap,
    ) -> CustomResult<Vec<Vec<u8>>, errors::ConnectorError> {
        let security_header = headers
            .get("recurly-signature")
            .ok_or(errors::ConnectorError::WebhookSignatureNotFound)?;
        let security_header_str = security_header
            .to_str()
            .change_context(errors::ConnectorError::WebhookSignatureNotFound)?;
        let header_parts: Vec<Vec<u8>> = security_header_str
            .split(',')
            .map(|part| part.trim().as_bytes().to_vec())
            .collect();

        Ok(header_parts)
    }
}


pub struct ErrorMessage {
    pub error_code: String,
    pub error_text: String,
}


impl api::Payment for Payload {}


pub async fn update_payouts_and_payout_attempt(
    payout_data: &mut PayoutData,
    merchant_context: &domain::MerchantContext,
    req: &payouts::PayoutCreateRequest,
    state: &SessionState,
) -> CustomResult<(), errors::ApiErrorResponse> {
    let payout_attempt = payout_data.payout_attempt.to_owned();
    let status = payout_attempt.status;
    let payout_id = payout_attempt.payout_id.clone();
    // Verify update feasibility
    if is_payout_terminal_state(status) || is_payout_initiated(status) {
        return Err(report!(errors::ApiErrorResponse::InvalidRequestData {
            message: format!(
                "Payout {} cannot be updated for status {status}",
                payout_id.get_string_repr()
            ),
        }));
    }

    // Fetch customer details from request and create new or else use existing customer that was attached
    let customer = get_customer_details_from_request(req);
    let customer_id = if customer.customer_id.is_some()
        || customer.name.is_some()
        || customer.email.is_some()
        || customer.phone.is_some()
        || customer.phone_country_code.is_some()
    {
        payout_data.customer_details =
            get_or_create_customer_details(state, &customer, merchant_context).await?;
        payout_data
            .customer_details
            .as_ref()
            .map(|customer| customer.customer_id.clone())
    } else {
        payout_data.payouts.customer_id.clone()
    };

    let (billing_address, address_id) = resolve_billing_address_for_payout(
        state,
        req.billing.as_ref(),
        payout_data.payouts.address_id.as_ref(),
        payout_data.payment_method.as_ref(),
        merchant_context,
        customer_id.as_ref(),
        &payout_id,
    )
    .await?;

    // Update payout state with resolved billing address
    payout_data.billing_address = billing_address;

    // Update DB with new data
    let payouts = payout_data.payouts.to_owned();
    let amount = MinorUnit::from(req.amount.unwrap_or(payouts.amount.into()));
    let updated_payouts = storage::PayoutsUpdate::Update {
        amount,
        destination_currency: req
            .currency
            .to_owned()
            .unwrap_or(payouts.destination_currency),
        source_currency: req.currency.to_owned().unwrap_or(payouts.source_currency),
        description: req
            .description
            .to_owned()
            .clone()
            .or(payouts.description.clone()),
        recurring: req.recurring.to_owned().unwrap_or(payouts.recurring),
        auto_fulfill: req.auto_fulfill.to_owned().unwrap_or(payouts.auto_fulfill),
        return_url: req
            .return_url
            .to_owned()
            .clone()
            .or(payouts.return_url.clone()),
        entity_type: req.entity_type.to_owned().unwrap_or(payouts.entity_type),
        metadata: req.metadata.clone().or(payouts.metadata.clone()),
        status: Some(status),
        profile_id: Some(payout_attempt.profile_id.clone()),
        confirm: req.confirm.to_owned(),
        payout_type: req
            .payout_type
            .to_owned()
            .or(payouts.payout_type.to_owned()),
        address_id: address_id.clone(),
        customer_id: customer_id.clone(),
    };
    let db = &*state.store;
    payout_data.payouts = db
        .update_payout(
            &payouts,
            updated_payouts,
            &payout_attempt,
            merchant_context.get_merchant_account().storage_scheme,
        )
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Error updating payouts")?;
    let updated_business_country =
        payout_attempt
            .business_country
            .map_or(req.business_country.to_owned(), |c| {
                req.business_country
                    .to_owned()
                    .and_then(|nc| if nc != c { Some(nc) } else { None })
            });
    let updated_business_label =
        payout_attempt
            .business_label
            .map_or(req.business_label.to_owned(), |l| {
                req.business_label
                    .to_owned()
                    .and_then(|nl| if nl != l { Some(nl) } else { None })
            });
    if updated_business_country.is_some()
        || updated_business_label.is_some()
        || customer_id.is_some()
        || address_id.is_some()
    {
        let payout_attempt = &payout_data.payout_attempt;
        let updated_payout_attempt = storage::PayoutAttemptUpdate::BusinessUpdate {
            business_country: updated_business_country,
            business_label: updated_business_label,
            address_id,
            customer_id,
        };
        payout_data.payout_attempt = db
            .update_payout_attempt(
                payout_attempt,
                updated_payout_attempt,
                &payout_data.payouts,
                merchant_context.get_merchant_account().storage_scheme,
            )
            .await
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Error updating payout_attempt")?;
    }
    Ok(())
}


File: crates/analytics/src/disputes/metrics/total_amount_disputed.rs

use std::collections::HashSet;

use api_models::analytics::{
    disputes::{DisputeDimensions, DisputeFilters, DisputeMetricsBucketIdentifier},
    Granularity, TimeRange,
};
use common_utils::errors::ReportSwitchExt;
use error_stack::ResultExt;
use time::PrimitiveDateTime;

use super::DisputeMetricRow;
use crate::{
    enums::AuthInfo,
    query::{Aggregate, GroupByClause, QueryBuilder, QueryFilter, SeriesBucket, ToSql, Window},
    types::{AnalyticsCollection, AnalyticsDataSource, MetricsError, MetricsResult},
};
#[derive(Default)]
pub(super) struct TotalAmountDisputed {}

#[async_trait::async_trait]
impl<T> super::DisputeMetric<T> for TotalAmountDisputed
where
    T: AnalyticsDataSource + super::DisputeMetricAnalytics,
    PrimitiveDateTime: ToSql<T>,
    AnalyticsCollection: ToSql<T>,
    Granularity: GroupByClause<T>,
    Aggregate<&'static str>: ToSql<T>,
    Window<&'static str>: ToSql<T>,
{
    async fn load_metrics(
        &self,
        dimensions: &[DisputeDimensions],
        auth: &AuthInfo,
        filters: &DisputeFilters,
        granularity: Option<Granularity>,
        time_range: &TimeRange,
        pool: &T,
    ) -> MetricsResult<HashSet<(DisputeMetricsBucketIdentifier, DisputeMetricRow)>>
    where
        T: AnalyticsDataSource + super::DisputeMetricAnalytics,
    {
        let mut query_builder: QueryBuilder<T> = QueryBuilder::new(AnalyticsCollection::Dispute);

        for dim in dimensions {
            query_builder.add_select_column(dim).switch()?;
        }

        query_builder
            .add_select_column(Aggregate::Sum {
                field: "dispute_amount",
                alias: Some("total"),
            })
            .switch()?;
        query_builder
            .add_select_column(Aggregate::Min {
                field: "created_at",
                alias: Some("start_bucket"),
            })
            .switch()?;
        query_builder
            .add_select_column(Aggregate::Max {
                field: "created_at",
                alias: Some("end_bucket"),
            })
            .switch()?;

        filters.set_filter_clause(&mut query_builder).switch()?;

        auth.set_filter_clause(&mut query_builder).switch()?;

        time_range
            .set_filter_clause(&mut query_builder)
            .attach_printable("Error filtering time range")
            .switch()?;

        for dim in dimensions.iter() {
            query_builder.add_group_by_clause(dim).switch()?;
        }

        if let Some(granularity) = granularity {
            granularity
                .set_group_by_clause(&mut query_builder)
                .switch()?;
        }
        query_builder
            .add_filter_clause("dispute_status", "dispute_won")
            .switch()?;

        query_builder
            .execute_query::<DisputeMetricRow, _>(pool)
            .await
            .change_context(MetricsError::QueryBuildingError)?
            .change_context(MetricsError::QueryExecutionFailure)?
            .into_iter()
            .map(|i| {
                Ok((
                    DisputeMetricsBucketIdentifier::new(
                        i.dispute_stage.as_ref().map(|i| i.0),
                        i.connector.clone(),
                        i.currency.as_ref().map(|i| i.0),
                        TimeRange {
                            start_time: match (granularity, i.start_bucket) {
                                (Some(g), Some(st)) => g.clip_to_start(st)?,
                                _ => time_range.start_time,
                            },
                            end_time: granularity.as_ref().map_or_else(
                                || Ok(time_range.end_time),
                                |g| i.end_bucket.map(|et| g.clip_to_end(et)).transpose(),
                            )?,
                        },
                    ),
                    i,
                ))
            })
            .collect::<error_stack::Result<HashSet<_>, crate::query::PostProcessingError>>()
            .change_context(MetricsError::PostProcessingFailure)
    }
}


impl api::MandateSetup for Datatrans {}

impl api::MandateSetup for for Datatrans


impl api::PaymentToken for Tokenio {}


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

    let customer_id = path.into_inner().clone();
    let request = customer_types::CustomerUpdateRequest::from(payload);
    let request_internal = customer_types::CustomerUpdateRequestInternal {
        customer_id,
        request,
    };

    let flow = Flow::CustomersUpdate;

    Box::pin(wrap::compatibility_api_wrap::<
        _,
        _,
        _,
        _,
        _,
        types::CustomerUpdateResponse,
        errors::StripeErrorCode,
        _,
    >(
        flow,
        state.into_inner(),
        &req,
        request_internal,
        |state, auth: auth::AuthenticationData, request_internal, _| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            customers::update_customer(state, merchant_context, request_internal)
        },
        &auth::HeaderAuth(auth::ApiKeyAuth {
            is_connected_allowed: false,
            is_platform_allowed: false,
        }),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


pub async fn get_attach_evidence_request(
    mut payload: Multipart,
) -> CustomResult<disputes::AttachEvidenceRequest, errors::ApiErrorResponse> {
    let mut option_evidence_type: Option<disputes::EvidenceType> = None;
    let mut dispute_id: Option<String> = None;

    let mut file_name: Option<String> = None;
    let mut file_content: Option<Vec<Bytes>> = None;

    while let Ok(Some(mut field)) = payload.try_next().await {
        let content_disposition = field.content_disposition();
        let field_name = content_disposition.get_name();
        // Parse the different parameters expected in the multipart request
        match field_name {
            Some("file") => {
                file_name = content_disposition.get_filename().map(String::from);
                //Collect the file content and throw error if something fails
                let mut file_data = Vec::new();
                let mut stream = field.into_stream();
                while let Some(chunk) = stream.next().await {
                    match chunk {
                        Ok(bytes) => file_data.push(bytes),
                        Err(err) => Err(errors::ApiErrorResponse::InternalServerError)
                            .attach_printable_lazy(|| format!("File parsing error: {err}"))?,
                    }
                }
                file_content = Some(file_data)
            }
            Some("dispute_id") => {
                dispute_id = helpers::read_string(&mut field).await;
            }
            Some("evidence_type") => {
                option_evidence_type = parse_evidence_type(&mut field).await?;
            }
            // Can ignore other params
            _ => (),
        }
    }
    let evidence_type = option_evidence_type.get_required_value("evidence_type")?;
    let file = file_content.get_required_value("file")?.concat().to_vec();
    //Get and validate file size
    let file_size = i32::try_from(file.len())
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("File size error")?;
    // Check if empty file and throw error
    fp_utils::when(file_size <= 0, || {
        Err(errors::ApiErrorResponse::MissingFile)
            .attach_printable("Missing / Invalid file in the request")
    })?;
    // Get file mime type using 'infer'
    let kind = infer::get(&file).ok_or(errors::ApiErrorResponse::MissingFileContentType)?;
    let file_type = kind
        .mime_type()
        .parse::<mime::Mime>()
        .change_context(errors::ApiErrorResponse::MissingFileContentType)
        .attach_printable("File content type error")?;
    let create_file_request = files::CreateFileRequest {
        file,
        file_name,
        file_size,
        file_type,
        purpose: files::FilePurpose::DisputeEvidence,
        dispute_id,
    };
    Ok(disputes::AttachEvidenceRequest {
        evidence_type,
        create_file_request,
    })
}


pub async fn insert_in_v2(self, state: &SessionState) -> UserResult<UserRole> {
        let entity = self.entity.clone();

        let new_v2_role = self.convert_to_new_v2_role(entity.into());

        state
            .global_store
            .insert_user_role(new_v2_role)
            .await
            .change_context(UserErrors::InternalServerError)
    }


impl api::MandateSetup for Redsys {}


impl Adyen {
    pub const fn new() -> &'static Self {
        &Self {
            amount_converter: &MinorUnitForConnector,
            amount_converter_webhooks: &StringMinorUnitForConnector,
        }
    }
}


impl ConnectorValidation for Payeezy {}

impl ConnectorValidation for for Payeezy


impl Mandates {
    pub fn server(state: AppState) -> Scope {
        let mut route = web::scope("/mandates").app_data(web::Data::new(state));

        #[cfg(feature = "olap")]
        {
            route =
                route.service(web::resource("/list").route(web::get().to(retrieve_mandates_list)));
            route = route.service(web::resource("/{id}").route(web::get().to(get_mandate)));
        }
        #[cfg(feature = "oltp")]
        {
            route =
                route.service(web::resource("/revoke/{id}").route(web::post().to(revoke_mandate)));
        }
        route
    }
}


pub struct ChargebeeMandateDetails {
    pub customer_id: String,
    pub mandate_id: String,
}


pub struct NodeId(usize);


pub struct ProphetpayRefundResponse {
    pub success: bool,
    pub response_text: String,
    pub tran_seq_number: Option<String>,
}


pub struct Shift4WebhookObjectId {
    #[serde(rename = "type")]
    pub event_type: Shift4WebhookEvent,
    pub data: Shift4WebhookObjectData,
}


pub struct EnhancedData {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub customer_reference: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub sales_tax: Option<MinorUnit>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub tax_exempt: Option<bool>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub discount_amount: Option<MinorUnit>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub shipping_amount: Option<MinorUnit>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub duty_amount: Option<MinorUnit>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub ship_from_postal_code: Option<Secret<String>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub destination_postal_code: Option<Secret<String>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub destination_country_code: Option<common_enums::CountryAlpha2>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub detail_tax: Option<DetailTax>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub line_item_data: Option<Vec<LineItemData>>,
}


pub struct Relay {
    pub id: id_type::RelayId,
    pub connector_resource_id: String,
    pub connector_id: id_type::MerchantConnectorAccountId,
    pub profile_id: id_type::ProfileId,
    pub merchant_id: id_type::MerchantId,
    pub relay_type: enums::RelayType,
    pub request_data: Option<RelayData>,
    pub status: enums::RelayStatus,
    pub connector_reference_id: Option<String>,
    pub error_code: Option<String>,
    pub error_message: Option<String>,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub created_at: PrimitiveDateTime,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub modified_at: PrimitiveDateTime,
    pub response_data: Option<pii::SecretSerdeValue>,
}


OpenAPI Block Path: components.schemas.MerchantConnectorId
{
  "type": "object",
  "required": [
    "merchant_id",
    "merchant_connector_id"
  ],
  "properties": {
    "merchant_id": {
      "type": "string"
    },
    "merchant_connector_id": {
      "type": "string"
    }
  }
}


pub struct NoonApplePayPaymentMethod {
    pub display_name: String,
    pub network: String,
    #[serde(rename = "type")]
    pub pm_type: String,
}


impl Responder {
    let flow = Flow::UpdateDynamicRoutingConfigs;
    let routing_payload_wrapper = routing_types::SuccessBasedRoutingPayloadWrapper {
        updated_config: json_payload.into_inner(),
        algorithm_id: path.clone().algorithm_id,
        profile_id: path.clone().profile_id,
    };
    Box::pin(oss_api::server_wrap(
        flow,
        state,
        &req,
        routing_payload_wrapper.clone(),
        |state, _, wrapper: routing_types::SuccessBasedRoutingPayloadWrapper, _| async {
            Box::pin(routing::success_based_routing_update_configs(
                state,
                wrapper.updated_config,
                wrapper.algorithm_id,
                wrapper.profile_id,
            ))
            .await
        },
        auth::auth_type(
            &auth::HeaderAuth(auth::ApiKeyAuth {
                is_connected_allowed: false,
                is_platform_allowed: false,
            }),
            &auth::JWTAuthProfileFromRoute {
                profile_id: routing_payload_wrapper.profile_id,
                required_permission: Permission::ProfileRoutingWrite,
            },
            req.headers(),
        ),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


pub struct RoleNew {
    pub role_name: String,
    pub role_id: String,
    pub merchant_id: Option<id_type::MerchantId>,
    pub org_id: id_type::OrganizationId,
    #[diesel(deserialize_as = super::DieselArray<enums::PermissionGroup>)]
    pub groups: Vec<enums::PermissionGroup>,
    pub scope: enums::RoleScope,
    pub created_at: PrimitiveDateTime,
    pub created_by: String,
    pub last_modified_at: PrimitiveDateTime,
    pub last_modified_by: String,
    pub entity_type: enums::EntityType,
    pub profile_id: Option<id_type::ProfileId>,
    pub tenant_id: id_type::TenantId,
}


impl api::PaymentAuthorize for Placetopay {}

impl api::PaymentAuthorize for for Placetopay


OpenAPI Block Path: components.schemas.RequiredFieldInfo
{
  "type": "object",
  "description": "Required fields info used while listing the payment_method_data",
  "required": [
    "required_field",
    "display_name",
    "field_type"
  ],
  "properties": {
    "required_field": {
      "type": "string",
      "description": "Required field for a payment_method through a payment_method_type"
    },
    "display_name": {
      "type": "string",
      "description": "Display name of the required field in the front-end"
    },
    "field_type": {
      "$ref": "#/components/schemas/FieldType"
    },
    "value": {
      "type": "string",
      "nullable": true
    }
  }
}


pub fn server(state: AppState) -> Scope {
        web::scope("/api_keys/{merchant_id}")
            .app_data(web::Data::new(state))
            .service(web::resource("").route(web::post().to(api_keys::api_key_create)))
            .service(web::resource("/list").route(web::get().to(api_keys::api_key_list)))
            .service(
                web::resource("/{key_id}")
                    .route(web::get().to(api_keys::api_key_retrieve))
                    .route(web::post().to(api_keys::api_key_update))
                    .route(web::delete().to(api_keys::api_key_revoke)),
            )
    }


impl IncomingWebhook for Prophetpay {
    fn get_webhook_object_reference_id(
        &self,
        _request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<ObjectReferenceId, errors::ConnectorError> {
        Err(report!(errors::ConnectorError::WebhooksNotImplemented))
    }

    fn get_webhook_event_type(
        &self,
        _request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<IncomingWebhookEvent, errors::ConnectorError> {
        Err(report!(errors::ConnectorError::WebhooksNotImplemented))
    }

    fn get_webhook_resource_object(
        &self,
        _request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<Box<dyn masking::ErasedMaskSerialize>, errors::ConnectorError> {
        Err(report!(errors::ConnectorError::WebhooksNotImplemented))
    }
}


pub fn create_config(self, source: Config) -> Config {
        Config { ..source }
    }


pub struct Multisafepay {
    amount_converter: &'static (dyn AmountConvertor<Output = MinorUnit> + Sync),
}


impl Capturable for SetupMandateRequestData {}

impl Capturable for for SetupMandateRequestData


pub struct Vgs;


pub struct OrganizationResponse {
    /// The unique identifier for the Organization
    #[schema(value_type = String, max_length = 64, min_length = 1, example = "org_q98uSGAYbjEwqs0mJwnz")]
    pub organization_id: id_type::OrganizationId,

    /// Name of the Organization
    pub organization_name: Option<String>,

    /// Details about the organization
    #[schema(value_type = Option<Object>)]
    pub organization_details: Option<pii::SecretSerdeValue>,

    /// Metadata is useful for storing additional, unstructured information on an object.
    #[schema(value_type = Option<Object>)]
    pub metadata: Option<pii::SecretSerdeValue>,
    pub modified_at: time::PrimitiveDateTime,
    pub created_at: time::PrimitiveDateTime,

    /// Organization Type of the organization
    #[schema(value_type = Option<OrganizationType>, example = "standard")]
    pub organization_type: Option<OrganizationType>,
}


impl webhooks::IncomingWebhook for UnifiedAuthenticationService {
    fn get_webhook_object_reference_id(
        &self,
        _request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<api_models::webhooks::ObjectReferenceId, errors::ConnectorError> {
        Err(report!(errors::ConnectorError::WebhooksNotImplemented))
    }

    fn get_webhook_event_type(
        &self,
        _request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<api_models::webhooks::IncomingWebhookEvent, errors::ConnectorError> {
        Err(report!(errors::ConnectorError::WebhooksNotImplemented))
    }

    fn get_webhook_resource_object(
        &self,
        _request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<Box<dyn masking::ErasedMaskSerialize>, errors::ConnectorError> {
        Err(report!(errors::ConnectorError::WebhooksNotImplemented))
    }
}

impl webhooks::IncomingWebhook for for UnifiedAuthenticationService


pub struct AuthorizedotnetPaymentCancelOrCaptureRequest {
    merchant_authentication: AuthorizedotnetAuthType,
    transaction_request: TransactionVoidOrCaptureRequest,
}


/// A trait which converts the struct to Hashmap required for encryption and back to struct
pub trait ToEncryptable<T, S: Clone, E>: Sized {
    /// Serializes the type to a hashmap
    fn to_encryptable(self) -> FxHashMap<String, E>;
    /// Deserializes the hashmap back to the type
    fn from_encryptable(
        hashmap: FxHashMap<String, Encryptable<S>>,
    ) -> CustomResult<T, errors::ParsingError>;
}


pub async fn generic_find_one_optional<T, P, R>(
    conn: &PgPooledConn,
    predicate: P,
) -> StorageResult<Option<R>>
where
    T: FilterDsl<P> + HasTable<Table = T> + Table + 'static,
    Filter<T, P>: LoadQuery<'static, PgConnection, R> + QueryFragment<Pg> + Send + 'static,
    R: Send + 'static,
{
    to_optional(generic_find_one_core::<T, _, _>(conn, predicate).await)
}


pub struct ThreedsecureioPreAuthenticationResponseData {
    pub ds_start_protocol_version: String,
    pub ds_end_protocol_version: String,
    pub acs_start_protocol_version: String,
    pub acs_end_protocol_version: String,
    #[serde(rename = "threeDSMethodURL")]
    pub threeds_method_url: Option<String>,
    #[serde(rename = "threeDSServerTransID")]
    pub threeds_server_trans_id: String,
    pub scheme: Option<String>,
    pub message_type: Option<String>,
}


impl VaultingInterface for AddVault {
    fn get_vaulting_request_url() -> &'static str {
        consts::ADD_VAULT_REQUEST_URL
    }

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


pub async fn add_external_account_addition_task(
    db: &dyn StorageInterface,
    payout_data: &PayoutData,
    schedule_time: time::PrimitiveDateTime,
) -> CustomResult<(), errors::StorageError> {
    let runner = storage::ProcessTrackerRunner::AttachPayoutAccountWorkflow;
    let task = "STRPE_ATTACH_EXTERNAL_ACCOUNT";
    let tag = ["PAYOUTS", "STRIPE", "ACCOUNT", "CREATE"];
    let process_tracker_id = pt_utils::get_process_tracker_id(
        runner,
        task,
        &payout_data.payout_attempt.payout_attempt_id,
        &payout_data.payout_attempt.merchant_id,
    );
    let tracking_data = api::PayoutRetrieveRequest {
        payout_id: payout_data.payouts.payout_id.to_owned(),
        force_sync: None,
        merchant_id: Some(payout_data.payouts.merchant_id.to_owned()),
    };
    let process_tracker_entry = storage::ProcessTrackerNew::new(
        process_tracker_id,
        task,
        runner,
        tag,
        tracking_data,
        None,
        schedule_time,
        common_types::consts::API_VERSION,
    )
    .map_err(errors::StorageError::from)?;

    db.insert_process(process_tracker_entry).await?;
    Ok(())
}


impl<'a> NetworkTokenizationBuilder<'a, CustomerAssigned> {
    pub fn get_optional_card_and_cvc(
        &self,
    ) -> (Option<domain::CardDetail>, Option<masking::Secret<String>>) {
        (self.card.clone(), self.card_cvc.clone())
    }
    pub fn set_token_details(
        self,
        network_token: &'a NetworkTokenizationResponse,
    ) -> NetworkTokenizationBuilder<'a, CardTokenized> {
        NetworkTokenizationBuilder {
            state: std::marker::PhantomData,
            network_token: Some(&network_token.0),
            customer: self.customer,
            card: self.card,
            card_cvc: self.card_cvc,
            stored_card: self.stored_card,
            stored_token: self.stored_token,
            payment_method_response: self.payment_method_response,
            card_tokenized: self.card_tokenized,
            error_code: self.error_code,
            error_message: self.error_message,
        }
    }
}


pub fn set_shipping_data(mut self, address: Option<&Address>) -> Result<Self, Error> {
        self.shipping_data = address
            .and_then(|address| {
                address.address.as_ref().map(|address_details| {
                    let state = address_details
                        .get_optional_state()
                        .map(Self::get_state_code)
                        .transpose();
                    match state {
                        Ok(ship_addr_state) => Ok(ShippingData {
                            ship_addr_city: address_details.get_optional_city(),
                            ship_addr_country: address_details.get_optional_country().map(
                                |country| {
                                    common_enums::CountryAlpha2::from_alpha2_to_alpha3(country)
                                        .to_string()
                                },
                            ),
                            ship_addr_line1: address_details.get_optional_line1(),
                            ship_addr_line2: address_details.get_optional_line2(),
                            ship_addr_line3: address_details.get_optional_line3(),
                            ship_addr_postal_code: address_details.get_optional_zip(),
                            ship_addr_state,
                        }),
                        Err(err) => Err(err),
                    }
                })
            })
            .transpose()?;
        Ok(self)
    }


pub async fn reset_password(
    state: web::Data<AppState>,
    req: HttpRequest,
    payload: web::Json<user_api::ResetPasswordRequest>,
) -> HttpResponse {
    let flow = Flow::ResetPassword;
    Box::pin(api::server_wrap(
        flow,
        state.clone(),
        &req,
        payload.into_inner(),
        |state, user, payload, _| user_core::reset_password_token_only_flow(state, user, payload),
        &auth::SinglePurposeJWTAuth(TokenPurpose::ResetPassword),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


pub struct BankOfAmericaServerErrorResponse {
    pub status: Option<String>,
    pub message: Option<String>,
    pub reason: Option<Reason>,
}


pub fn build_filter_array(
        &self,
        case_sensitive_filters: Vec<&(String, Vec<Value>)


pub struct AciWebhookRiskDetails {
    pub score: Option<String>,
}


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

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

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

impl ConnectorSpecifications for for Tsys


pub struct RiskifiedPaymentsResponse {
    order: OrderResponse,
}


impl api::PaymentSync for Volt {}


File: crates/router/src/routes/connector_onboarding.rs
 Public functions: 3

use actix_web::{web, HttpRequest, HttpResponse};
use api_models::connector_onboarding as api_types;
use router_env::Flow;

use super::AppState;
use crate::{
    core::{api_locking, connector_onboarding as core},
    services::{api, authentication as auth, authorization::permissions::Permission},
};

pub async fn get_action_url(
    state: web::Data<AppState>,
    http_req: HttpRequest,
    json_payload: web::Json<api_types::ActionUrlRequest>,
) -> HttpResponse {
    let flow = Flow::GetActionUrl;
    let req_payload = json_payload.into_inner();
    Box::pin(api::server_wrap(
        flow.clone(),
        state,
        &http_req,
        req_payload.clone(),
        core::get_action_url,
        &auth::JWTAuth {
            permission: Permission::MerchantAccountWrite,
        },
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

pub async fn sync_onboarding_status(
    state: web::Data<AppState>,
    http_req: HttpRequest,
    json_payload: web::Json<api_types::OnboardingSyncRequest>,
) -> HttpResponse {
    let flow = Flow::SyncOnboardingStatus;
    let req_payload = json_payload.into_inner();
    Box::pin(api::server_wrap(
        flow.clone(),
        state,
        &http_req,
        req_payload.clone(),
        core::sync_onboarding_status,
        &auth::JWTAuth {
            permission: Permission::MerchantAccountWrite,
        },
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

pub async fn reset_tracking_id(
    state: web::Data<AppState>,
    http_req: HttpRequest,
    json_payload: web::Json<api_types::ResetTrackingIdRequest>,
) -> HttpResponse {
    let flow = Flow::ResetTrackingId;
    let req_payload = json_payload.into_inner();
    Box::pin(api::server_wrap(
        flow.clone(),
        state,
        &http_req,
        req_payload.clone(),
        core::reset_tracking_id,
        &auth::JWTAuth {
            permission: Permission::MerchantAccountWrite,
        },
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


pub async fn get_info(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        domain: web::Path<analytics::AnalyticsDomain>,
    ) -> impl Responder {
        let flow = AnalyticsFlow::GetInfo;
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            domain.into_inner(),
            |_, _: (), domain: analytics::AnalyticsDomain, _| async {
                analytics::core::get_domain_info(domain)
                    .await
                    .map(ApplicationResponse::Json)
            },
            &auth::NoAuth,
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }


pub async fn upsert_payment_processor_token(
        state: &SessionState,
        connector_customer_id: &str,
        token_data: PaymentProcessorTokenStatus,
    ) -> CustomResult<bool, errors::StorageError> {
        let mut token_map =
            Self::get_connector_customer_payment_processor_tokens(state, connector_customer_id)
                .await?;

        let token_id = token_data
            .payment_processor_token_details
            .payment_processor_token
            .clone();

        let was_existing = token_map.contains_key(&token_id);

        let error_code = token_data.error_code.clone();
        let today = OffsetDateTime::now_utc().date();

        token_map
            .get_mut(&token_id)
            .map(|existing_token| {
                error_code.map(|err| existing_token.error_code = Some(err));

                Self::normalize_retry_window(existing_token, today);

                for (date, &value) in &token_data.daily_retry_history {
                    existing_token
                        .daily_retry_history
                        .entry(*date)
                        .and_modify(|v| *v += value)
                        .or_insert(value);
                }
            })
            .or_else(|| {
                token_map.insert(token_id.clone(), token_data);
                None
            });

        Self::update_or_add_connector_customer_payment_processor_tokens(
            state,
            connector_customer_id,
            token_map,
        )
        .await?;
        tracing::debug!(
            connector_customer_id = connector_customer_id,
            "Upsert payment processor tokens",
        );

        Ok(!was_existing)
    }


OpenAPI Block Path: components.schemas.PaymentMethodCreateData
{
  "oneOf": [
    {
      "type": "object",
      "required": [
        "card"
      ],
      "properties": {
        "card": {
          "$ref": "#/components/schemas/CardDetail"
        }
      }
    }
  ]
}


pub async fn profile_payments_list() {}


pub struct ServiceDetails {
    pub service_session_ids: Option<ServiceSessionIds>,
    pub merchant_details: Option<MerchantDetails>,
}


File: crates/analytics/src/metrics/request.rs
 Public functions: 1

#[inline]
pub async fn record_operation_time<F, R, T>(
    future: F,
    metric: &router_env::opentelemetry::metrics::Histogram<f64>,
    metric_name: &T,
    source: &crate::AnalyticsProvider,
) -> R
where
    F: futures::Future<Output = R>,
    T: ToString,
{
    let (result, time) = common_utils::metrics::utils::time_future(future).await;
    let attributes = router_env::metric_attributes!(
        ("metric_name", metric_name.to_string()),
        ("source", source.to_string()),
    );
    let value = time.as_secs_f64();
    metric.record(value, attributes);

    router_env::logger::debug!("Attributes: {:?}, Time: {}", attributes, value);
    result
}


pub struct TokenizedBankTransferValue2 {
    pub customer_id: Option<id_type::CustomerId>,
}


pub struct PayRequest {
    buyer_name: Secret<String>,
    buyer_email: pii::Email,
    payme_sale_id: String,
    #[serde(flatten)]
    card: PaymeCard,
    language: String,
}


impl api::RefundSync for Bluesnap {}


pub struct AddCardRequest {
    pub card_number: cards::CardNumber,
    pub customer_id: id_type::CustomerId,
    pub card_exp_month: Secret<String>,
    pub card_exp_year: Secret<String>,
    pub merchant_id: id_type::MerchantId,
    pub email_address: Option<Email>,
    pub name_on_card: Option<Secret<String>>,
    pub nickname: Option<String>,
}


pub async fn payment_start_redirection(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    req: api_models::payments::PaymentStartRedirectionRequest,
) -> RouterResponse<serde_json::Value> {
    let db = &*state.store;
    let key_manager_state = &(&state).into();

    let storage_scheme = merchant_context.get_merchant_account().storage_scheme;

    let payment_intent = db
        .find_payment_intent_by_id(
            key_manager_state,
            &req.id,
            merchant_context.get_merchant_key_store(),
            storage_scheme,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?;

    //TODO: send valid html error pages in this case, or atleast redirect to valid html error pages
    utils::when(
        payment_intent.status != storage_enums::IntentStatus::RequiresCustomerAction,
        || {
            Err(errors::ApiErrorResponse::PaymentUnexpectedState {
                current_flow: "PaymentStartRedirection".to_string(),
                field_name: "status".to_string(),
                current_value: payment_intent.status.to_string(),
                states: ["requires_customer_action".to_string()].join(", "),
            })
        },
    )?;

    let payment_attempt = db
        .find_payment_attempt_by_id(
            key_manager_state,
            merchant_context.get_merchant_key_store(),
            payment_intent
                .active_attempt_id
                .as_ref()
                .ok_or(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("missing active attempt in payment_intent")?,
            storage_scheme,
        )
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Error while fetching payment_attempt")?;
    let redirection_data = payment_attempt
        .redirection_data
        .clone()
        .ok_or(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("missing authentication_data in payment_attempt")?;

    Ok(services::ApplicationResponse::Form(Box::new(
        services::RedirectionFormData {
            redirect_form: redirection_data,
            payment_method_data: None,
            amount: payment_attempt.amount_details.get_net_amount().to_string(),
            currency: payment_intent.amount_details.currency.to_string(),
        },
    )))
}


pub struct MandateRevoke;


pub struct Riskified {
    #[cfg(feature = "frm")]
    amount_converter: &'static (dyn AmountConvertor<Output = StringMajorUnit> + Sync),
}


impl Capturable for SdkPaymentsSessionUpdateData {}

impl Capturable for for SdkPaymentsSessionUpdateData


impl api::PaymentCapture for Bamboraapac {}

impl api::PaymentCapture for for Bamboraapac