File: crates/router/tests/connectors/facilitapay.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 FacilitapayTest;
impl ConnectorActions for FacilitapayTest {}
impl utils::Connector for FacilitapayTest {
    fn get_data(&self) -> api::ConnectorData {
        use router::connector::Facilitapay;
        utils::construct_connector_data_old(
            Box::new(Facilitapay::new()),
            types::Connector::Plaid,
            api::GetToken::Connector,
            None,
        )
    }

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

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

static CONNECTOR: FacilitapayTest = FacilitapayTest {};

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


pub async fn trigger_event<Res, F, Fut>(
        self,
        state: &SessionState,
        func: F,
    ) -> RoutingResult<RoutingEventsResponse<Res>>
    where
        F: FnOnce() -> Fut + Send,
        Res: Serialize + serde::de::DeserializeOwned + Clone,
        Fut: futures::Future<Output = RoutingResult<Option<Res>>> + Send,
    {
        let mut routing_event =
            self.routing_event
                .ok_or(errors::RoutingError::RoutingEventsError {
                    message: "Routing event is missing".to_string(),
                    status_code: 500,
                })?;

        let mut response = RoutingEventsResponse::new(None, None);

        let resp = func().await;
        match resp {
            Ok(ok_resp) => {
                if let Some(resp) = ok_resp {
                    routing_event.set_response_body(&resp);
                    // routing_event
                    //     .set_routable_connectors(ok_resp.get_routable_connectors().unwrap_or_default());
                    // routing_event.set_payment_connector(ok_resp.get_payment_connector());
                    routing_event.set_status_code(200);

                    response.set_response(resp.clone());
                    self.log_event
                        .then(|| state.event_handler().log_event(&routing_event));
                }
            }
            Err(err) => {
                // Need to figure out a generic way to log errors
                routing_event
                    .set_error(serde_json::json!({"error": err.current_context().to_string()}));

                match err.current_context() {
                    errors::RoutingError::RoutingEventsError { status_code, .. } => {
                        routing_event.set_status_code(*status_code);
                    }
                    _ => {
                        routing_event.set_status_code(500);
                    }
                }
                state.event_handler().log_event(&routing_event)
            }
        }

        response.set_event(routing_event);

        Ok(response)
    }


pub struct AcquirerDetails {
    pub acquirer_bin: String,
    pub acquirer_merchant_id: String,
    pub acquirer_country_code: Option<String>,
}


pub struct CustomerRequest {
    /// The identifier for the customer object. If not provided the customer ID will be autogenerated.
    #[schema(value_type = Option<String>, max_length = 64, min_length = 1, example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")]
    pub customer_id: Option<id_type::CustomerId>,
    /// The identifier for the Merchant Account
    #[schema(max_length = 255, example = "y3oqhf46pyzuxjbcn2giaqnb44")]
    #[serde(skip)]
    pub merchant_id: id_type::MerchantId,
    /// The customer's name
    #[schema(max_length = 255, value_type = Option<String>, example = "Jon Test")]
    pub name: Option<Secret<String>>,
    /// The customer's email address
    #[schema(value_type = Option<String>, max_length = 255, example = "JonTest@test.com")]
    pub email: Option<pii::Email>,
    /// The customer's phone number
    #[schema(value_type = Option<String>, max_length = 255, example = "9123456789")]
    pub phone: Option<Secret<String>>,
    /// An arbitrary string that you can attach to a customer object.
    #[schema(max_length = 255, example = "First Customer", value_type = Option<String>)]
    pub description: Option<Description>,
    /// The country code for the customer phone number
    #[schema(max_length = 255, example = "+65")]
    pub phone_country_code: Option<String>,
    /// The address for the customer
    #[schema(value_type = Option<AddressDetails>)]
    pub address: Option<payments::AddressDetails>,
    /// You can specify up to 50 keys, with key names up to 40 characters long and values up to 500
    /// characters long. Metadata is useful for storing additional, structured information on an
    /// object.
    #[schema(value_type = Option<Object>,example = json!({ "city": "NY", "unit": "245" }))]
    pub metadata: Option<pii::SecretSerdeValue>,
    /// Customer's tax registration ID
    #[schema(max_length = 255, value_type = Option<String>, example = "123456789")]
    pub tax_registration_id: Option<Secret<String>>,
}


pub fn get_total_blocked_amount(&self) -> common_types::MinorUnit {
        self.all_captures
            .iter()
            .fold(common_types::MinorUnit::new(0), |accumulator, capture| {
                accumulator
                    + match capture.1.status {
                        storage_enums::CaptureStatus::Charged
                        | storage_enums::CaptureStatus::Pending => capture.1.amount,
                        storage_enums::CaptureStatus::Started
                        | storage_enums::CaptureStatus::Failed => common_types::MinorUnit::new(0),
                    }
            })
    }


impl ConnectorValidation for Chargebee {
    //TODO: implement functions when support enabled
}

impl ConnectorValidation for for Chargebee


Web Documentation: Try out APIs | Hyperswitch
# Type: Web Doc

Set up your merchant account
Sign up or sign in to
Postman
.
Open our
Postman collection
and switch to the
"Variables" tab
. Update the value under the "current value" column for the
baseUrl
variable to have the hostname and port of the locally running server (

by default).
While on the "Variables" tab, add the admin API key you configured in the application configuration under the "current value" column for the
admin_api_key
variable.
If you're running Docker Compose, you can find the configuration file at
config/docker_compose.toml
, search for
admin_api_key
to find the admin API key.
If you set up the dependencies locally, you can find the configuration file at
config/development.toml
, search for
admin_api_key
to find the admin API key
Open the
"Quick Start" folder
in the collection.
Open the
"Merchant Account - Create"
request, switch to the "Body" tab and update any request parameters as required.
If you want to use a different connector for making payments with than the provided default, update the
data
field present in the
routing_algorithm
field to your liking.
Click on the "Send" button to create a merchant account (You may need to "create a fork" to fork this collection to your own workspace to send a request). You should obtain a response containing most of the data included in the request, along with some additional fields. Store the merchant ID and publishable key returned in the response.
Create an API key
Open the
"API Key - Create"
request, switch to the "Body" tab and update any request parameters as required. Click on the "Send" button to create an API key. You should obtain a response containing the data included in the request, along with the plaintext API key. Store the API key returned in the response securely.
Set up a payment connector account
Sign up on the payment connector's (say Stripe, Adyen, etc.) dashboard and store your connector API key (and any other necessary secrets) securely.
Open the
"Payment Connector - Create"
request, switch to the "Body" tab and update any request parameters as required.
Pay special attention to the
connector_name
and
connector_account_details
fields and update them. You can find connector-specific details to be included in this
spreadsheet
.
Open the
"Variables" tab
in the
Postman collection
and set the
connector_api_key
variable to your connector's API key.
Click on the "Send" button to create a payment connector account. You should obtain a response containing most of the data included in the request, along with some additional fields.
Follow the above steps if you'd like to add more payment connector accounts.
Create a Payment
Ensure that you have
set up your merchant account
and
set up at least one payment connector account
before trying to create a payment.
Open the
"Payments - Create"
request, switch to the "Body" tab and update any request parameters as required. Click on the "Send" button to create a payment. If all goes well and you had provided the correct connector credentials, the payment should be created successfully. You should see the
status
field of the response body having a value of
succeeded
in this case.
If the
status
of the payment created was
requires_confirmation
, set
confirm
to
true
in the request body and send the request again.
Open the
"Payments - Retrieve"
request and click on the "Send" button (without modifying anything). This should return the payment object for the payment created in Step 2.
Create a Refund
Open the
"Refunds - Create"
request in the
"Quick Start" folder
folder and switch to the "Body" tab. Update the amount to be refunded, if required, and click on the "Send" button. This should create a refund against the last payment made for the specified amount. Check the
status
field of the response body to verify that the refund hasn't failed.
Open the
"Refunds - Retrieve"
request and switch to the "Params" tab. Set the
id
path variable in the "Path Variables" table to the
refund_id
That's it! Hope you got a hang of our APIs. To explore more of our APIs, please check the remaining folders in the
Postman collection
.
7 months ago
Was this helpful?

Try out APIs | Hyperswitch


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 webhooks::IncomingWebhook for Paystack {
    fn get_webhook_source_verification_algorithm(
        &self,
        _request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<Box<dyn crypto::VerifySignature + Send>, errors::ConnectorError> {
        Ok(Box::new(crypto::HmacSha512))
    }

    fn get_webhook_source_verification_signature(
        &self,
        request: &webhooks::IncomingWebhookRequestDetails<'_>,
        _connector_webhook_secrets: &api_models::webhooks::ConnectorWebhookSecrets,
    ) -> CustomResult<Vec<u8>, errors::ConnectorError> {
        let signature = utils::get_header_key_value("x-paystack-signature", request.headers)
            .change_context(errors::ConnectorError::WebhookSignatureNotFound)?;

        hex::decode(signature)
            .change_context(errors::ConnectorError::WebhookVerificationSecretInvalid)
    }

    fn get_webhook_source_verification_message(
        &self,
        request: &webhooks::IncomingWebhookRequestDetails<'_>,
        _merchant_id: &common_utils::id_type::MerchantId,
        _connector_webhook_secrets: &api_models::webhooks::ConnectorWebhookSecrets,
    ) -> CustomResult<Vec<u8>, errors::ConnectorError> {
        let message = std::str::from_utf8(request.body)
            .change_context(errors::ConnectorError::WebhookSourceVerificationFailed)?;
        Ok(message.to_string().into_bytes())
    }

    fn get_webhook_object_reference_id(
        &self,
        request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<api_models::webhooks::ObjectReferenceId, errors::ConnectorError> {
        let webhook_body = request
            .body
            .parse_struct::<paystack::PaystackWebhookData>("PaystackWebhookData")
            .change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?;
        match webhook_body.data {
            paystack::PaystackWebhookEventData::Payment(data) => {
                Ok(api_models::webhooks::ObjectReferenceId::PaymentId(
                    api_models::payments::PaymentIdType::ConnectorTransactionId(data.reference),
                ))
            }
            paystack::PaystackWebhookEventData::Refund(data) => {
                Ok(api_models::webhooks::ObjectReferenceId::RefundId(
                    api_models::webhooks::RefundIdType::ConnectorRefundId(data.id),
                ))
            }
        }
    }

    fn get_webhook_event_type(
        &self,
        request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<api_models::webhooks::IncomingWebhookEvent, errors::ConnectorError> {
        let webhook_body = request
            .body
            .parse_struct::<paystack::PaystackWebhookData>("PaystackWebhookData")
            .change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?;
        Ok(api_models::webhooks::IncomingWebhookEvent::from(
            webhook_body.data,
        ))
    }

    fn get_webhook_resource_object(
        &self,
        request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<Box<dyn masking::ErasedMaskSerialize>, errors::ConnectorError> {
        let webhook_body = request
            .body
            .parse_struct::<paystack::PaystackWebhookData>("PaystackWebhookData")
            .change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?;
        Ok(match webhook_body.data {
            paystack::PaystackWebhookEventData::Payment(payment_webhook_data) => {
                Box::new(payment_webhook_data)
            }
            paystack::PaystackWebhookEventData::Refund(refund_webhook_data) => {
                Box::new(refund_webhook_data)
            }
        })
    }
}

impl webhooks::IncomingWebhook for for Paystack


impl RefundSync for Wise {}


OpenAPI Block Path: components.schemas.EventListConstraints
{
  "type": "object",
  "description": "The constraints to apply when filtering events.",
  "properties": {
    "created_after": {
      "type": "string",
      "format": "date-time",
      "description": "Filter events created after the specified time.",
      "nullable": true
    },
    "created_before": {
      "type": "string",
      "format": "date-time",
      "description": "Filter events created before the specified time.",
      "nullable": true
    },
    "limit": {
      "type": "integer",
      "format": "int32",
      "description": "Include at most the specified number of events.",
      "nullable": true,
      "minimum": 0
    },
    "offset": {
      "type": "integer",
      "format": "int32",
      "description": "Include events after the specified offset.",
      "nullable": true,
      "minimum": 0
    },
    "object_id": {
      "type": "string",
      "description": "Filter all events associated with the specified object identifier (Payment Intent ID,\nRefund ID, etc.)",
      "nullable": true
    },
    "profile_id": {
      "type": "string",
      "description": "Filter all events associated with the specified business profile ID.",
      "nullable": true
    },
    "event_classes": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/EventClass"
      },
      "description": "Filter events by their class.",
      "uniqueItems": true,
      "nullable": true
    },
    "event_types": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/EventType"
      },
      "description": "Filter events by their type.",
      "uniqueItems": true,
      "nullable": true
    },
    "is_delivered": {
      "type": "boolean",
      "description": "Filter all events by `is_overall_delivery_successful` field of the event.",
      "nullable": true
    }
  }
}


pub struct PayoutListResponse {
    /// The number of payouts included in the list
    pub size: usize,
    /// The list of payouts response objects
    pub data: Vec<PayoutCreateResponse>,
    /// The total number of available payouts for given constraints
    #[serde(skip_serializing_if = "Option::is_none")]
    pub total_count: Option<i64>,
}


impl Default for ConnectorAuthenticationMap {
    fn default() -> Self {
        Self::new()
    }
}

impl Default for for ConnectorAuthenticationMap


impl api::PaymentCapture for Elavon {}


pub struct ResponseMessages {
    result_code: ResultCode,
    pub message: Vec<ResponseMessage>,
}


impl State for TokenizeWithCard {}


pub fn apply_changeset(self, source: Customer) -> Customer {
        let Self {
            name,
            email,
            phone,
            description,
            phone_country_code,
            metadata,
            connector_customer,
            default_payment_method_id,
            default_billing_address,
            default_shipping_address,
            status,
            tax_registration_id,
            ..
        } = self;

        Customer {
            name: name.map_or(source.name, Some),
            email: email.map_or(source.email, Some),
            phone: phone.map_or(source.phone, Some),
            description: description.map_or(source.description, Some),
            phone_country_code: phone_country_code.map_or(source.phone_country_code, Some),
            metadata: metadata.map_or(source.metadata, Some),
            modified_at: common_utils::date_time::now(),
            connector_customer: connector_customer.map_or(source.connector_customer, Some),
            default_payment_method_id: default_payment_method_id
                .flatten()
                .map_or(source.default_payment_method_id, Some),
            default_billing_address: default_billing_address
                .map_or(source.default_billing_address, Some),
            default_shipping_address: default_shipping_address
                .map_or(source.default_shipping_address, Some),
            status: status.unwrap_or(source.status),
            tax_registration_id: tax_registration_id.map_or(source.tax_registration_id, Some),
            ..source
        }
    }


pub struct FacilitapayWebhookNotification {
    pub notification: FacilitapayWebhookBody,
}


impl GlobalStorageInterface for KafkaStore {
    fn get_cache_store(&self) -> Box<(dyn RedisConnInterface + Send + Sync + 'static)> {
        Box::new(self.clone())
    }
}

impl GlobalStorageInterface for for KafkaStore


File: crates/hyperswitch_connectors/src/connectors/tsys/transformers.rs
 Public structs: 17

use common_enums::enums;
use common_utils::types::StringMinorUnit;
use error_stack::ResultExt;
use hyperswitch_domain_models::{
    payment_method_data::PaymentMethodData,
    router_data::{self, ConnectorAuthType, RouterData},
    router_flow_types::refunds::{Execute, RSync},
    router_request_types::ResponseId,
    router_response_types::{PaymentsResponseData, RefundsResponseData},
    types::{
        self, PaymentsCancelRouterData, PaymentsSyncRouterData, RefundSyncRouterData,
        RefundsRouterData,
    },
};
use hyperswitch_interfaces::errors;
use masking::Secret;
use serde::{Deserialize, Serialize};
pub struct TsysRouterData<T> {
    pub amount: StringMinorUnit,
    pub router_data: T,
}

impl<T> From<(StringMinorUnit, T)> for TsysRouterData<T> {
    fn from((amount, router_data): (StringMinorUnit, T)) -> Self {
        Self {
            amount,
            router_data,
        }
    }
}

use crate::{
    types::{RefundsResponseRouterData, ResponseRouterData},
    utils::{self, CardData as _, PaymentsAuthorizeRequestData, RefundsRequestData as _},
};

#[derive(Debug, Serialize)]
pub enum TsysPaymentsRequest {
    Auth(TsysPaymentAuthSaleRequest),
    Sale(TsysPaymentAuthSaleRequest),
}

#[derive(Default, Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TsysPaymentAuthSaleRequest {
    #[serde(rename = "deviceID")]
    device_id: Secret<String>,
    transaction_key: Secret<String>,
    card_data_source: String,
    transaction_amount: StringMinorUnit,
    currency_code: enums::Currency,
    card_number: cards::CardNumber,
    expiration_date: Secret<String>,
    cvv2: Secret<String>,
    terminal_capability: String,
    terminal_operating_environment: String,
    cardholder_authentication_method: String,
    #[serde(rename = "developerID")]
    developer_id: Secret<String>,
    order_number: String,
}

impl TryFrom<&TsysRouterData<&types::PaymentsAuthorizeRouterData>> for TsysPaymentsRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item_data: &TsysRouterData<&types::PaymentsAuthorizeRouterData>,
    ) -> Result<Self, Self::Error> {
        let item = item_data.router_data.clone();
        match item.request.payment_method_data.clone() {
            PaymentMethodData::Card(ccard) => {
                let connector_auth: TsysAuthType =
                    TsysAuthType::try_from(&item.connector_auth_type)?;
                let auth_data: TsysPaymentAuthSaleRequest = TsysPaymentAuthSaleRequest {
                    device_id: connector_auth.device_id,
                    transaction_key: connector_auth.transaction_key,
                    card_data_source: "INTERNET".to_string(),
                    transaction_amount: item_data.amount.clone(),
                    currency_code: item.request.currency,
                    card_number: ccard.card_number.clone(),
                    expiration_date: ccard
                        .get_card_expiry_month_year_2_digit_with_delimiter("/".to_owned())?,
                    cvv2: ccard.card_cvc,
                    terminal_capability: "ICC_CHIP_READ_ONLY".to_string(),
                    terminal_operating_environment: "ON_MERCHANT_PREMISES_ATTENDED".to_string(),
                    cardholder_authentication_method: "NOT_AUTHENTICATED".to_string(),
                    developer_id: connector_auth.developer_id,
                    order_number: item.connector_request_reference_id.clone(),
                };
                if item.request.is_auto_capture()? {
                    Ok(Self::Sale(auth_data))
                } else {
                    Ok(Self::Auth(auth_data))
                }
            }
            PaymentMethodData::CardRedirect(_)
            | PaymentMethodData::Wallet(_)
            | PaymentMethodData::PayLater(_)
            | PaymentMethodData::BankRedirect(_)
            | PaymentMethodData::BankDebit(_)
            | PaymentMethodData::BankTransfer(_)
            | PaymentMethodData::Crypto(_)
            | PaymentMethodData::MandatePayment
            | PaymentMethodData::Reward
            | PaymentMethodData::RealTimePayment(_)
            | PaymentMethodData::MobilePayment(_)
            | PaymentMethodData::Upi(_)
            | PaymentMethodData::Voucher(_)
            | PaymentMethodData::GiftCard(_)
            | PaymentMethodData::OpenBanking(_)
            | PaymentMethodData::CardToken(_)
            | PaymentMethodData::NetworkToken(_)
            | PaymentMethodData::CardDetailsForNetworkTransactionId(_) => {
                Err(errors::ConnectorError::NotImplemented(
                    utils::get_unimplemented_payment_method_error_message("tsys"),
                ))?
            }
        }
    }
}

// Auth Struct
pub struct TsysAuthType {
    pub(super) device_id: Secret<String>,
    pub(super) transaction_key: Secret<String>,
    pub(super) developer_id: Secret<String>,
}

impl TryFrom<&ConnectorAuthType> for TsysAuthType {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(auth_type: &ConnectorAuthType) -> Result<Self, Self::Error> {
        match auth_type {
            ConnectorAuthType::SignatureKey {
                api_key,
                key1,
                api_secret,
            } => Ok(Self {
                device_id: api_key.to_owned(),
                transaction_key: key1.to_owned(),
                developer_id: api_secret.to_owned(),
            }),
            _ => Err(errors::ConnectorError::FailedToObtainAuthType.into()),
        }
    }
}

// PaymentsResponse
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "UPPERCASE")]
pub enum TsysPaymentStatus {
    Pass,
    Fail,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "UPPERCASE")]
pub enum TsysTransactionStatus {
    Approved,
    Declined,
    Void,
}

impl From<TsysTransactionDetails> for enums::AttemptStatus {
    fn from(item: TsysTransactionDetails) -> Self {
        match item.transaction_status {
            TsysTransactionStatus::Approved => {
                if item.transaction_type.contains("Auth-Only") {
                    Self::Authorized
                } else {
                    Self::Charged
                }
            }
            TsysTransactionStatus::Void => Self::Voided,
            TsysTransactionStatus::Declined => Self::Failure,
        }
    }
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TsysErrorResponse {
    pub status: TsysPaymentStatus,
    pub response_code: String,
    pub response_message: String,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TsysTransactionDetails {
    #[serde(rename = "transactionID")]
    transaction_id: String,
    transaction_type: String,
    transaction_status: TsysTransactionStatus,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TsysPaymentsSyncResponse {
    pub status: TsysPaymentStatus,
    pub response_code: String,
    pub response_message: String,
    pub transaction_details: TsysTransactionDetails,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TsysResponse {
    pub status: TsysPaymentStatus,
    pub response_code: String,
    pub response_message: String,
    #[serde(rename = "transactionID")]
    pub transaction_id: String,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(untagged)]
pub enum TsysResponseTypes {
    SuccessResponse(TsysResponse),
    ErrorResponse(TsysErrorResponse),
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[allow(clippy::enum_variant_names)]
pub enum TsysPaymentsResponse {
    AuthResponse(TsysResponseTypes),
    SaleResponse(TsysResponseTypes),
    CaptureResponse(TsysResponseTypes),
    VoidResponse(TsysResponseTypes),
}

fn get_error_response(
    connector_response: TsysErrorResponse,
    status_code: u16,
) -> router_data::ErrorResponse {
    router_data::ErrorResponse {
        code: connector_response.response_code,
        message: connector_response.response_message.clone(),
        reason: Some(connector_response.response_message),
        status_code,
        attempt_status: None,
        connector_transaction_id: None,
        network_advice_code: None,
        network_decline_code: None,
        network_error_message: None,
        connector_metadata: None,
    }
}

fn get_payments_response(connector_response: TsysResponse) -> PaymentsResponseData {
    PaymentsResponseData::TransactionResponse {
        resource_id: ResponseId::ConnectorTransactionId(connector_response.transaction_id.clone()),
        redirection_data: Box::new(None),
        mandate_reference: Box::new(None),
        connector_metadata: None,
        network_txn_id: None,
        connector_response_reference_id: Some(connector_response.transaction_id),
        incremental_authorization_allowed: None,
        charges: None,
    }
}

fn get_payments_sync_response(
    connector_response: &TsysPaymentsSyncResponse,
) -> PaymentsResponseData {
    PaymentsResponseData::TransactionResponse {
        resource_id: ResponseId::ConnectorTransactionId(
            connector_response
                .transaction_details
                .transaction_id
                .clone(),
        ),
        redirection_data: Box::new(None),
        mandate_reference: Box::new(None),
        connector_metadata: None,
        network_txn_id: None,
        connector_response_reference_id: Some(
            connector_response
                .transaction_details
                .transaction_id
                .clone(),
        ),
        incremental_authorization_allowed: None,
        charges: None,
    }
}

impl<F, T> TryFrom<ResponseRouterData<F, TsysPaymentsResponse, T, PaymentsResponseData>>
    for RouterData<F, T, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<F, TsysPaymentsResponse, T, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        let (response, status) = match item.response {
            TsysPaymentsResponse::AuthResponse(resp) => match resp {
                TsysResponseTypes::SuccessResponse(auth_response) => (
                    Ok(get_payments_response(auth_response)),
                    enums::AttemptStatus::Authorized,
                ),
                TsysResponseTypes::ErrorResponse(connector_response) => (
                    Err(get_error_response(connector_response, item.http_code)),
                    enums::AttemptStatus::AuthorizationFailed,
                ),
            },
            TsysPaymentsResponse::SaleResponse(resp) => match resp {
                TsysResponseTypes::SuccessResponse(sale_response) => (
                    Ok(get_payments_response(sale_response)),
                    enums::AttemptStatus::Charged,
                ),
                TsysResponseTypes::ErrorResponse(connector_response) => (
                    Err(get_error_response(connector_response, item.http_code)),
                    enums::AttemptStatus::Failure,
                ),
            },
            TsysPaymentsResponse::CaptureResponse(resp) => match resp {
                TsysResponseTypes::SuccessResponse(capture_response) => (
                    Ok(get_payments_response(capture_response)),
                    enums::AttemptStatus::Charged,
                ),
                TsysResponseTypes::ErrorResponse(connector_response) => (
                    Err(get_error_response(connector_response, item.http_code)),
                    enums::AttemptStatus::CaptureFailed,
                ),
            },
            TsysPaymentsResponse::VoidResponse(resp) => match resp {
                TsysResponseTypes::SuccessResponse(void_response) => (
                    Ok(get_payments_response(void_response)),
                    enums::AttemptStatus::Voided,
                ),
                TsysResponseTypes::ErrorResponse(connector_response) => (
                    Err(get_error_response(connector_response, item.http_code)),
                    enums::AttemptStatus::VoidFailed,
                ),
            },
        };
        Ok(Self {
            status,
            response,
            ..item.data
        })
    }
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TsysSearchTransactionRequest {
    #[serde(rename = "deviceID")]
    device_id: Secret<String>,
    transaction_key: Secret<String>,
    #[serde(rename = "transactionID")]
    transaction_id: String,
    #[serde(rename = "developerID")]
    developer_id: Secret<String>,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "PascalCase")]
pub struct TsysSyncRequest {
    search_transaction: TsysSearchTransactionRequest,
}

impl TryFrom<&PaymentsSyncRouterData> for TsysSyncRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &PaymentsSyncRouterData) -> Result<Self, Self::Error> {
        let connector_auth: TsysAuthType = TsysAuthType::try_from(&item.connector_auth_type)?;
        let search_transaction = TsysSearchTransactionRequest {
            device_id: connector_auth.device_id,
            transaction_key: connector_auth.transaction_key,
            transaction_id: item
                .request
                .connector_transaction_id
                .get_connector_transaction_id()
                .change_context(errors::ConnectorError::MissingConnectorTransactionID)?,
            developer_id: connector_auth.developer_id,
        };
        Ok(Self { search_transaction })
    }
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(untagged)]
pub enum SearchResponseTypes {
    SuccessResponse(TsysPaymentsSyncResponse),
    ErrorResponse(TsysErrorResponse),
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "PascalCase")]
pub struct TsysSyncResponse {
    search_transaction_response: SearchResponseTypes,
}

impl<F, T> TryFrom<ResponseRouterData<F, TsysSyncResponse, T, PaymentsResponseData>>
    for RouterData<F, T, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<F, TsysSyncResponse, T, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        let tsys_search_response = item.response.search_transaction_response;
        let (response, status) = match tsys_search_response {
            SearchResponseTypes::SuccessResponse(search_response) => (
                Ok(get_payments_sync_response(&search_response)),
                enums::AttemptStatus::from(search_response.transaction_details),
            ),
            SearchResponseTypes::ErrorResponse(connector_response) => (
                Err(get_error_response(connector_response, item.http_code)),
                item.data.status,
            ),
        };
        Ok(Self {
            status,
            response,
            ..item.data
        })
    }
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TsysCancelRequest {
    #[serde(rename = "deviceID")]
    device_id: Secret<String>,
    transaction_key: Secret<String>,
    #[serde(rename = "transactionID")]
    transaction_id: String,
    #[serde(rename = "developerID")]
    developer_id: Secret<String>,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "PascalCase")]
pub struct TsysPaymentsCancelRequest {
    void: TsysCancelRequest,
}
impl TryFrom<&PaymentsCancelRouterData> for TsysPaymentsCancelRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &PaymentsCancelRouterData) -> Result<Self, Self::Error> {
        let connector_auth: TsysAuthType = TsysAuthType::try_from(&item.connector_auth_type)?;
        let void = TsysCancelRequest {
            device_id: connector_auth.device_id,
            transaction_key: connector_auth.transaction_key,
            transaction_id: item.request.connector_transaction_id.clone(),
            developer_id: connector_auth.developer_id,
        };
        Ok(Self { void })
    }
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TsysCaptureRequest {
    #[serde(rename = "deviceID")]
    device_id: Secret<String>,
    transaction_key: Secret<String>,
    transaction_amount: StringMinorUnit,
    #[serde(rename = "transactionID")]
    transaction_id: String,
    #[serde(rename = "developerID")]
    developer_id: Secret<String>,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "PascalCase")]
pub struct TsysPaymentsCaptureRequest {
    capture: TsysCaptureRequest,
}

impl TryFrom<&TsysRouterData<&types::PaymentsCaptureRouterData>> for TsysPaymentsCaptureRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item_data: &TsysRouterData<&types::PaymentsCaptureRouterData>,
    ) -> Result<Self, Self::Error> {
        let item = item_data.router_data.clone();
        let connector_auth: TsysAuthType = TsysAuthType::try_from(&item.connector_auth_type)?;
        let capture = TsysCaptureRequest {
            device_id: connector_auth.device_id,
            transaction_key: connector_auth.transaction_key,
            transaction_id: item.request.connector_transaction_id.clone(),
            developer_id: connector_auth.developer_id,
            transaction_amount: item_data.amount.clone(),
        };
        Ok(Self { capture })
    }
}
// REFUND :
// Type definition for RefundRequest
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TsysReturnRequest {
    #[serde(rename = "deviceID")]
    device_id: Secret<String>,
    transaction_key: Secret<String>,
    transaction_amount: StringMinorUnit,
    #[serde(rename = "transactionID")]
    transaction_id: String,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "PascalCase")]
pub struct TsysRefundRequest {
    #[serde(rename = "Return")]
    return_request: TsysReturnRequest,
}

impl<F> TryFrom<&TsysRouterData<&RefundsRouterData<F>>> for TsysRefundRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item_data: &TsysRouterData<&RefundsRouterData<F>>) -> Result<Self, Self::Error> {
        let item = item_data.router_data;
        let connector_auth: TsysAuthType = TsysAuthType::try_from(&item.connector_auth_type)?;
        let return_request = TsysReturnRequest {
            device_id: connector_auth.device_id,
            transaction_key: connector_auth.transaction_key,
            transaction_amount: item_data.amount.clone(),
            transaction_id: item.request.connector_transaction_id.clone(),
        };
        Ok(Self { return_request })
    }
}

impl From<TsysPaymentStatus> for enums::RefundStatus {
    fn from(item: TsysPaymentStatus) -> Self {
        match item {
            TsysPaymentStatus::Pass => Self::Success,
            TsysPaymentStatus::Fail => Self::Failure,
        }
    }
}

impl From<TsysTransactionDetails> for enums::RefundStatus {
    fn from(item: TsysTransactionDetails) -> Self {
        match item.transaction_status {
            TsysTransactionStatus::Approved => Self::Pending,
            //Connector calls refunds as Void
            TsysTransactionStatus::Void => Self::Success,
            TsysTransactionStatus::Declined => Self::Failure,
        }
    }
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "PascalCase")]
pub struct RefundResponse {
    return_response: TsysResponseTypes,
}

impl TryFrom<RefundsResponseRouterData<Execute, RefundResponse>> for RefundsRouterData<Execute> {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: RefundsResponseRouterData<Execute, RefundResponse>,
    ) -> Result<Self, Self::Error> {
        let tsys_return_response = item.response.return_response;
        let response = match tsys_return_response {
            TsysResponseTypes::SuccessResponse(return_response) => Ok(RefundsResponseData {
                connector_refund_id: return_response.transaction_id,
                refund_status: enums::RefundStatus::from(return_response.status),
            }),
            TsysResponseTypes::ErrorResponse(connector_response) => {
                Err(get_error_response(connector_response, item.http_code))
            }
        };
        Ok(Self {
            response,
            ..item.data
        })
    }
}

impl TryFrom<&RefundSyncRouterData> for TsysSyncRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &RefundSyncRouterData) -> Result<Self, Self::Error> {
        let connector_auth: TsysAuthType = TsysAuthType::try_from(&item.connector_auth_type)?;
        let search_transaction = TsysSearchTransactionRequest {
            device_id: connector_auth.device_id,
            transaction_key: connector_auth.transaction_key,
            transaction_id: item.request.get_connector_refund_id()?,
            developer_id: connector_auth.developer_id,
        };
        Ok(Self { search_transaction })
    }
}

impl TryFrom<RefundsResponseRouterData<RSync, TsysSyncResponse>> for RefundsRouterData<RSync> {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: RefundsResponseRouterData<RSync, TsysSyncResponse>,
    ) -> Result<Self, Self::Error> {
        let tsys_search_response = item.response.search_transaction_response;
        let response = match tsys_search_response {
            SearchResponseTypes::SuccessResponse(search_response) => Ok(RefundsResponseData {
                connector_refund_id: search_response.transaction_details.transaction_id.clone(),
                refund_status: enums::RefundStatus::from(search_response.transaction_details),
            }),
            SearchResponseTypes::ErrorResponse(connector_response) => {
                Err(get_error_response(connector_response, item.http_code))
            }
        };
        Ok(Self {
            response,
            ..item.data
        })
    }
}


File: crates/diesel_models/src/authorization.rs
 Public functions: 1
 Public structs: 3

use common_utils::types::MinorUnit;
use diesel::{AsChangeset, Identifiable, Insertable, Queryable, Selectable};
use serde::{Deserialize, Serialize};
use time::PrimitiveDateTime;

use crate::{enums as storage_enums, schema::incremental_authorization};

#[derive(
    Clone, Debug, Eq, PartialEq, Identifiable, Queryable, Selectable, Serialize, Deserialize, Hash,
)]
#[diesel(table_name = incremental_authorization, primary_key(authorization_id, merchant_id), check_for_backend(diesel::pg::Pg))]
pub struct Authorization {
    pub authorization_id: String,
    pub merchant_id: common_utils::id_type::MerchantId,
    pub payment_id: common_utils::id_type::PaymentId,
    pub amount: MinorUnit,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub created_at: PrimitiveDateTime,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub modified_at: PrimitiveDateTime,
    pub status: storage_enums::AuthorizationStatus,
    pub error_code: Option<String>,
    pub error_message: Option<String>,
    pub connector_authorization_id: Option<String>,
    pub previously_authorized_amount: MinorUnit,
}

#[derive(Clone, Debug, Insertable, router_derive::DebugAsDisplay, Serialize, Deserialize)]
#[diesel(table_name = incremental_authorization)]
pub struct AuthorizationNew {
    pub authorization_id: String,
    pub merchant_id: common_utils::id_type::MerchantId,
    pub payment_id: common_utils::id_type::PaymentId,
    pub amount: MinorUnit,
    pub status: storage_enums::AuthorizationStatus,
    pub error_code: Option<String>,
    pub error_message: Option<String>,
    pub connector_authorization_id: Option<String>,
    pub previously_authorized_amount: MinorUnit,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum AuthorizationUpdate {
    StatusUpdate {
        status: storage_enums::AuthorizationStatus,
        error_code: Option<String>,
        error_message: Option<String>,
        connector_authorization_id: Option<String>,
    },
}

#[derive(Clone, Debug, Default, AsChangeset, router_derive::DebugAsDisplay)]
#[diesel(table_name = incremental_authorization)]
pub struct AuthorizationUpdateInternal {
    pub status: Option<storage_enums::AuthorizationStatus>,
    pub error_code: Option<String>,
    pub error_message: Option<String>,
    pub modified_at: Option<PrimitiveDateTime>,
    pub connector_authorization_id: Option<String>,
}

impl AuthorizationUpdateInternal {
    pub fn create_authorization(self, source: Authorization) -> Authorization {
        Authorization {
            status: self.status.unwrap_or(source.status),
            error_code: self.error_code.or(source.error_code),
            error_message: self.error_message.or(source.error_message),
            modified_at: self.modified_at.unwrap_or(common_utils::date_time::now()),
            connector_authorization_id: self
                .connector_authorization_id
                .or(source.connector_authorization_id),
            ..source
        }
    }
}

impl From<AuthorizationUpdate> for AuthorizationUpdateInternal {
    fn from(authorization_child_update: AuthorizationUpdate) -> Self {
        let now = Some(common_utils::date_time::now());
        match authorization_child_update {
            AuthorizationUpdate::StatusUpdate {
                status,
                error_code,
                error_message,
                connector_authorization_id,
            } => Self {
                status: Some(status),
                error_code,
                error_message,
                connector_authorization_id,
                modified_at: now,
            },
        }
    }
}


impl<const T: u8> DummyConnector<T> {
    pub fn new() -> &'static Self {
        &Self {
            amount_converter: &MinorUnitForConnector,
        }
    }
}


impl GetPaymentMethodType for CryptoData {
    fn get_payment_method_type(&self) -> api_enums::PaymentMethodType {
        api_enums::PaymentMethodType::CryptoCurrency
    }
}

impl GetPaymentMethodType for for CryptoData


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


pub fn is_dynamic_routing(self) -> bool {
        self == Self::Dynamic
    }


impl api::PaymentToken for Getnet {}


pub struct ClientToken {
    client_token: Secret<String>,
}


File: crates/hyperswitch_connectors/src/connectors/checkbook/transformers.rs
 Public structs: 4

use api_models::webhooks::IncomingWebhookEvent;
use common_utils::{pii, types::FloatMajorUnit};
use hyperswitch_domain_models::{
    payment_method_data::{BankTransferData, PaymentMethodData},
    router_data::{ConnectorAuthType, RouterData},
    router_request_types::ResponseId,
    router_response_types::PaymentsResponseData,
    types::PaymentsAuthorizeRouterData,
};
use hyperswitch_interfaces::errors::ConnectorError;
use masking::Secret;
use serde::{Deserialize, Serialize};

use crate::{
    types::ResponseRouterData,
    utils::{get_unimplemented_payment_method_error_message, RouterData as _},
};

#[derive(Debug, Serialize)]
pub struct CheckbookPaymentsRequest {
    name: Secret<String>,
    recipient: pii::Email,
    amount: FloatMajorUnit,
    description: String,
}

impl TryFrom<(FloatMajorUnit, &PaymentsAuthorizeRouterData)> for CheckbookPaymentsRequest {
    type Error = error_stack::Report<ConnectorError>;
    fn try_from(
        (amount, item): (FloatMajorUnit, &PaymentsAuthorizeRouterData),
    ) -> Result<Self, Self::Error> {
        match item.request.payment_method_data.clone() {
            PaymentMethodData::BankTransfer(bank_transfer_data) => match *bank_transfer_data {
                BankTransferData::AchBankTransfer {} => Ok(Self {
                    name: item.get_billing_full_name()?,
                    recipient: item.get_billing_email()?,
                    amount,
                    description: item.get_description()?,
                }),
                _ => Err(ConnectorError::NotImplemented(
                    get_unimplemented_payment_method_error_message("Checkbook"),
                )
                .into()),
            },
            _ => Err(ConnectorError::NotImplemented(
                get_unimplemented_payment_method_error_message("Checkbook"),
            )
            .into()),
        }
    }
}

pub struct CheckbookAuthType {
    pub(super) publishable_key: Secret<String>,
    pub(super) secret_key: Secret<String>,
}

impl TryFrom<&ConnectorAuthType> for CheckbookAuthType {
    type Error = error_stack::Report<ConnectorError>;
    fn try_from(auth_type: &ConnectorAuthType) -> Result<Self, Self::Error> {
        match auth_type {
            ConnectorAuthType::BodyKey { key1, api_key } => Ok(Self {
                publishable_key: key1.to_owned(),
                secret_key: api_key.to_owned(),
            }),
            _ => Err(ConnectorError::FailedToObtainAuthType.into()),
        }
    }
}
// PaymentsResponse
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum CheckbookPaymentStatus {
    Unpaid,
    InProcess,
    Paid,
    Mailed,
    Printed,
    Failed,
    Expired,
    Void,
    #[default]
    Processing,
}

impl From<CheckbookPaymentStatus> for common_enums::AttemptStatus {
    fn from(item: CheckbookPaymentStatus) -> Self {
        match item {
            CheckbookPaymentStatus::Paid
            | CheckbookPaymentStatus::Mailed
            | CheckbookPaymentStatus::Printed => Self::Charged,
            CheckbookPaymentStatus::Failed | CheckbookPaymentStatus::Expired => Self::Failure,
            CheckbookPaymentStatus::Unpaid => Self::AuthenticationPending,
            CheckbookPaymentStatus::InProcess | CheckbookPaymentStatus::Processing => Self::Pending,
            CheckbookPaymentStatus::Void => Self::Voided,
        }
    }
}

impl From<CheckbookPaymentStatus> for IncomingWebhookEvent {
    fn from(status: CheckbookPaymentStatus) -> Self {
        match status {
            CheckbookPaymentStatus::Mailed
            | CheckbookPaymentStatus::Printed
            | CheckbookPaymentStatus::Paid => Self::PaymentIntentSuccess,
            CheckbookPaymentStatus::Failed | CheckbookPaymentStatus::Expired => {
                Self::PaymentIntentFailure
            }
            CheckbookPaymentStatus::Unpaid
            | CheckbookPaymentStatus::InProcess
            | CheckbookPaymentStatus::Processing => Self::PaymentIntentProcessing,
            CheckbookPaymentStatus::Void => Self::PaymentIntentCancelled,
        }
    }
}

#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct CheckbookPaymentsResponse {
    pub status: CheckbookPaymentStatus,
    pub id: String,
    pub amount: Option<FloatMajorUnit>,
    pub description: Option<String>,
    pub name: Option<String>,
    pub recipient: Option<String>,
}

impl<F, T> TryFrom<ResponseRouterData<F, CheckbookPaymentsResponse, T, PaymentsResponseData>>
    for RouterData<F, T, PaymentsResponseData>
{
    type Error = error_stack::Report<ConnectorError>;
    fn try_from(
        item: ResponseRouterData<F, CheckbookPaymentsResponse, T, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            status: common_enums::AttemptStatus::from(item.response.status),
            response: Ok(PaymentsResponseData::TransactionResponse {
                resource_id: ResponseId::ConnectorTransactionId(item.response.id),
                redirection_data: Box::new(None),
                mandate_reference: Box::new(None),
                connector_metadata: None,
                network_txn_id: None,
                connector_response_reference_id: None,
                incremental_authorization_allowed: None,
                charges: None,
            }),
            ..item.data
        })
    }
}

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


pub struct PaymentWebhooksLink {
    pub payment: String,
}


impl api::RefundSync for Bluecode {}


impl Responder {
    use crate::services::authentication::AuthenticationData;
    let flow = Flow::RoutingRetrieveActiveConfig;
    let wrapper = routing_types::RoutingRetrieveLinkQueryWrapper {
        routing_query: query.into_inner(),
        profile_id: path.into_inner(),
    };
    Box::pin(oss_api::server_wrap(
        flow,
        state,
        &req,
        wrapper.clone(),
        |state, auth: AuthenticationData, wrapper, _| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            routing::retrieve_routing_config_under_profile(
                state,
                merchant_context,
                wrapper.routing_query,
                wrapper.profile_id,
                transaction_type,
            )
        },
        #[cfg(not(feature = "release"))]
        auth::auth_type(
            &auth::V2ApiKeyAuth {
                is_connected_allowed: false,
                is_platform_allowed: false,
            },
            &auth::JWTAuthProfileFromRoute {
                profile_id: wrapper.profile_id,
                required_permission: Permission::ProfileRoutingRead,
            },
            req.headers(),
        ),
        #[cfg(feature = "release")]
        &auth::JWTAuthProfileFromRoute {
            profile_id: wrapper.profile_id,
            required_permission: Permission::ProfileRoutingRead,
        },
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


impl api::PaymentCapture for Trustpayments {}

impl api::PaymentCapture for for Trustpayments


pub struct PaymentMethod {
    pub gateway_token: String,
    pub funding_source: RecurlyFundingTypes,
    pub object: RecurlyPaymentObject,
    pub card_type: common_enums::CardNetwork,
    pub first_six: String,
}


impl api::PaymentToken for Multisafepay {}

impl api::PaymentToken for for Multisafepay


File: crates/hyperswitch_domain_models/src/revenue_recovery.rs
 Public functions: 1
 Public structs: 4

use api_models::{payments as api_payments, webhooks};
use common_enums::enums as common_enums;
use common_utils::{id_type, types as util_types};
use time::PrimitiveDateTime;

use crate::{
    payments,
    router_response_types::revenue_recovery::{
        BillingConnectorInvoiceSyncResponse, BillingConnectorPaymentsSyncResponse,
    },
    ApiModelToDieselModelConvertor,
};

/// Recovery payload is unified struct constructed from billing connectors
#[derive(Debug)]
pub struct RevenueRecoveryAttemptData {
    /// transaction amount against invoice, accepted in minor unit.
    pub amount: util_types::MinorUnit,
    /// currency of the transaction
    pub currency: common_enums::Currency,
    /// merchant reference id at billing connector. ex: invoice_id
    pub merchant_reference_id: id_type::PaymentReferenceId,
    /// transaction id reference at payment connector
    pub connector_transaction_id: Option<util_types::ConnectorTransactionId>,
    /// error code sent by billing connector.
    pub error_code: Option<String>,
    /// error message sent by billing connector.
    pub error_message: Option<String>,
    /// mandate token at payment processor end.
    pub processor_payment_method_token: String,
    /// customer id at payment connector for which mandate is attached.
    pub connector_customer_id: String,
    /// Payment gateway identifier id at billing processor.
    pub connector_account_reference_id: String,
    /// timestamp at which transaction has been created at billing connector
    pub transaction_created_at: Option<PrimitiveDateTime>,
    /// transaction status at billing connector equivalent to payment attempt status.
    pub status: common_enums::AttemptStatus,
    /// payment method of payment attempt.
    pub payment_method_type: common_enums::PaymentMethod,
    /// payment method sub type of the payment attempt.
    pub payment_method_sub_type: common_enums::PaymentMethodType,
    /// This field can be returned for both approved and refused Mastercard payments.
    /// This code provides additional information about the type of transaction or the reason why the payment failed.
    /// If the payment failed, the network advice code gives guidance on if and when you can retry the payment.
    pub network_advice_code: Option<String>,
    /// For card errors resulting from a card issuer decline, a brand specific 2, 3, or 4 digit code which indicates the reason the authorization failed.
    pub network_decline_code: Option<String>,
    /// A string indicating how to proceed with an network error if payment gateway provide one. This is used to understand the network error code better.
    pub network_error_message: Option<String>,
    /// Number of attempts made for an invoice
    pub retry_count: Option<u16>,
    /// Time when next invoice will be generated which will be equal to the end time of the current invoice
    pub invoice_next_billing_time: Option<PrimitiveDateTime>,
    /// Time at which the invoice created
    pub invoice_billing_started_at_time: Option<PrimitiveDateTime>,
    /// stripe specific id used to validate duplicate attempts in revenue recovery flow
    pub charge_id: Option<String>,
    /// Additional card details
    pub card_info: api_payments::AdditionalCardInfo,
}

/// This is unified struct for Revenue Recovery Invoice Data and it is constructed from billing connectors
#[derive(Debug, Clone)]
pub struct RevenueRecoveryInvoiceData {
    /// invoice amount at billing connector
    pub amount: util_types::MinorUnit,
    /// currency of the amount.
    pub currency: common_enums::Currency,
    /// merchant reference id at billing connector. ex: invoice_id
    pub merchant_reference_id: id_type::PaymentReferenceId,
    /// billing address id of the invoice
    pub billing_address: Option<api_payments::Address>,
    /// Retry count of the invoice
    pub retry_count: Option<u16>,
    /// Ending date of the invoice or the Next billing time of the Subscription
    pub next_billing_at: Option<PrimitiveDateTime>,
    /// Invoice Starting Time
    pub billing_started_at: Option<PrimitiveDateTime>,
}

#[derive(Clone, Debug)]
pub struct RecoveryPaymentIntent {
    pub payment_id: id_type::GlobalPaymentId,
    pub status: common_enums::IntentStatus,
    pub feature_metadata: Option<api_payments::FeatureMetadata>,
    pub merchant_id: id_type::MerchantId,
    pub merchant_reference_id: Option<id_type::PaymentReferenceId>,
    pub invoice_amount: util_types::MinorUnit,
    pub invoice_currency: common_enums::Currency,
    pub created_at: Option<PrimitiveDateTime>,
    pub billing_address: Option<api_payments::Address>,
}

#[derive(Clone, Debug)]
pub struct RecoveryPaymentAttempt {
    pub attempt_id: id_type::GlobalAttemptId,
    pub attempt_status: common_enums::AttemptStatus,
    pub feature_metadata: Option<api_payments::PaymentAttemptFeatureMetadata>,
    pub amount: util_types::MinorUnit,
    pub network_advice_code: Option<String>,
    pub network_decline_code: Option<String>,
    pub error_code: Option<String>,
    pub created_at: PrimitiveDateTime,
}

impl RecoveryPaymentAttempt {
    pub fn get_attempt_triggered_by(&self) -> Option<common_enums::TriggeredBy> {
        self.feature_metadata.as_ref().and_then(|metadata| {
            metadata
                .revenue_recovery
                .as_ref()
                .map(|recovery| recovery.attempt_triggered_by)
        })
    }
}

impl From<&RevenueRecoveryInvoiceData> for api_payments::AmountDetails {
    fn from(data: &RevenueRecoveryInvoiceData) -> Self {
        let amount = api_payments::AmountDetailsSetter {
            order_amount: data.amount.into(),
            currency: data.currency,
            shipping_cost: None,
            order_tax_amount: None,
            skip_external_tax_calculation: common_enums::TaxCalculationOverride::Skip,
            skip_surcharge_calculation: common_enums::SurchargeCalculationOverride::Skip,
            surcharge_amount: None,
            tax_on_surcharge: None,
        };
        Self::new(amount)
    }
}

impl From<&RevenueRecoveryInvoiceData> for api_payments::PaymentsCreateIntentRequest {
    fn from(data: &RevenueRecoveryInvoiceData) -> Self {
        let amount_details = api_payments::AmountDetails::from(data);
        Self {
            amount_details,
            merchant_reference_id: Some(data.merchant_reference_id.clone()),
            routing_algorithm_id: None,
            // Payments in the revenue recovery flow are always recurring transactions,
            // so capture method will be always automatic.
            capture_method: Some(common_enums::CaptureMethod::Automatic),
            authentication_type: Some(common_enums::AuthenticationType::NoThreeDs),
            billing: data.billing_address.clone(),
            shipping: None,
            customer_id: None,
            customer_present: Some(common_enums::PresenceOfCustomerDuringPayment::Absent),
            description: None,
            return_url: None,
            setup_future_usage: None,
            apply_mit_exemption: None,
            statement_descriptor: None,
            order_details: None,
            allowed_payment_method_types: None,
            metadata: None,
            connector_metadata: None,
            feature_metadata: None,
            payment_link_enabled: None,
            payment_link_config: None,
            request_incremental_authorization: None,
            session_expiry: None,
            frm_metadata: None,
            request_external_three_ds_authentication: None,
            force_3ds_challenge: None,
            merchant_connector_details: None,
        }
    }
}

impl From<&BillingConnectorInvoiceSyncResponse> for RevenueRecoveryInvoiceData {
    fn from(data: &BillingConnectorInvoiceSyncResponse) -> Self {
        Self {
            amount: data.amount,
            currency: data.currency,
            merchant_reference_id: data.merchant_reference_id.clone(),
            billing_address: data.billing_address.clone(),
            retry_count: data.retry_count,
            next_billing_at: data.ends_at,
            billing_started_at: data.created_at,
        }
    }
}

impl
    From<(
        &BillingConnectorPaymentsSyncResponse,
        &RevenueRecoveryInvoiceData,
    )> for RevenueRecoveryAttemptData
{
    fn from(
        data: (
            &BillingConnectorPaymentsSyncResponse,
            &RevenueRecoveryInvoiceData,
        ),
    ) -> Self {
        let billing_connector_payment_details = data.0;
        let invoice_details = data.1;
        Self {
            amount: billing_connector_payment_details.amount,
            currency: billing_connector_payment_details.currency,
            merchant_reference_id: billing_connector_payment_details
                .merchant_reference_id
                .clone(),
            connector_transaction_id: billing_connector_payment_details
                .connector_transaction_id
                .clone(),
            error_code: billing_connector_payment_details.error_code.clone(),
            error_message: billing_connector_payment_details.error_message.clone(),
            processor_payment_method_token: billing_connector_payment_details
                .processor_payment_method_token
                .clone(),
            connector_customer_id: billing_connector_payment_details
                .connector_customer_id
                .clone(),
            connector_account_reference_id: billing_connector_payment_details
                .connector_account_reference_id
                .clone(),
            transaction_created_at: billing_connector_payment_details.transaction_created_at,
            status: billing_connector_payment_details.status,
            payment_method_type: billing_connector_payment_details.payment_method_type,
            payment_method_sub_type: billing_connector_payment_details.payment_method_sub_type,
            network_advice_code: None,
            network_decline_code: None,
            network_error_message: None,
            retry_count: invoice_details.retry_count,
            invoice_next_billing_time: invoice_details.next_billing_at,
            charge_id: billing_connector_payment_details.charge_id.clone(),
            invoice_billing_started_at_time: invoice_details.billing_started_at,
            card_info: billing_connector_payment_details.card_info.clone(),
        }
    }
}

impl From<&RevenueRecoveryAttemptData> for api_payments::PaymentAttemptAmountDetails {
    fn from(data: &RevenueRecoveryAttemptData) -> Self {
        Self {
            net_amount: data.amount,
            amount_to_capture: None,
            surcharge_amount: None,
            tax_on_surcharge: None,
            amount_capturable: data.amount,
            shipping_cost: None,
            order_tax_amount: None,
        }
    }
}

impl From<&RevenueRecoveryAttemptData> for Option<api_payments::RecordAttemptErrorDetails> {
    fn from(data: &RevenueRecoveryAttemptData) -> Self {
        data.error_code
            .as_ref()
            .zip(data.error_message.clone())
            .map(|(code, message)| api_payments::RecordAttemptErrorDetails {
                code: code.to_string(),
                message: message.to_string(),
                network_advice_code: data.network_advice_code.clone(),
                network_decline_code: data.network_decline_code.clone(),
                network_error_message: data.network_error_message.clone(),
            })
    }
}

impl From<&payments::PaymentIntent> for RecoveryPaymentIntent {
    fn from(payment_intent: &payments::PaymentIntent) -> Self {
        Self {
            payment_id: payment_intent.id.clone(),
            status: payment_intent.status,
            feature_metadata: payment_intent
                .feature_metadata
                .clone()
                .map(|feature_metadata| feature_metadata.convert_back()),
            merchant_reference_id: payment_intent.merchant_reference_id.clone(),
            invoice_amount: payment_intent.amount_details.order_amount,
            invoice_currency: payment_intent.amount_details.currency,
            billing_address: payment_intent
                .billing_address
                .clone()
                .map(|address| api_payments::Address::from(address.into_inner())),
            merchant_id: payment_intent.merchant_id.clone(),
            created_at: Some(payment_intent.created_at),
        }
    }
}

impl From<&payments::payment_attempt::PaymentAttempt> for RecoveryPaymentAttempt {
    fn from(payment_attempt: &payments::payment_attempt::PaymentAttempt) -> Self {
        Self {
            attempt_id: payment_attempt.id.clone(),
            attempt_status: payment_attempt.status,
            feature_metadata: payment_attempt
                .feature_metadata
                .clone()
                .map(
                    |feature_metadata| api_payments::PaymentAttemptFeatureMetadata {
                        revenue_recovery: feature_metadata.revenue_recovery.map(|recovery| {
                            api_payments::PaymentAttemptRevenueRecoveryData {
                                attempt_triggered_by: recovery.attempt_triggered_by,
                                charge_id: recovery.charge_id,
                            }
                        }),
                    },
                ),
            amount: payment_attempt.amount_details.get_net_amount(),
            network_advice_code: payment_attempt
                .error
                .clone()
                .and_then(|error| error.network_advice_code),
            network_decline_code: payment_attempt
                .error
                .clone()
                .and_then(|error| error.network_decline_code),
            error_code: payment_attempt
                .error
                .as_ref()
                .map(|error| error.code.clone()),
            created_at: payment_attempt.created_at,
        }
    }
}


impl api::UasPostAuthentication for Juspaythreedsserver {}

impl api::UasPostAuthentication for for Juspaythreedsserver


impl Responder {
        // safety: This shouldn't panic owing to the data type
        #[allow(clippy::expect_used)]
        let payload = json_payload
            .into_inner()
            .to_vec()
            .pop()
            .expect("Couldn't get GetActivePaymentsMetricRequest");
        let flow = AnalyticsFlow::GetActivePaymentsMetrics;
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            payload,
            |state, auth: AuthenticationData, req, _| async move {
                analytics::active_payments::get_metrics(
                    &state.pool,
                    &auth.merchant_account.publishable_key,
                    auth.merchant_account.get_id(),
                    req,
                )
                .await
                .map(ApplicationResponse::Json)
            },
            &auth::JWTAuth {
                permission: Permission::MerchantAnalyticsRead,
            },
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }


File: crates/router/src/core/conditional_config.rs
 Public functions: 6

#[cfg(feature = "v2")]
use api_models::conditional_configs::DecisionManagerRequest;
use api_models::conditional_configs::{
    DecisionManager, DecisionManagerRecord, DecisionManagerResponse,
};
use common_utils::ext_traits::StringExt;
#[cfg(feature = "v2")]
use common_utils::types::keymanager::KeyManagerState;
use error_stack::ResultExt;

use crate::{
    core::errors::{self, RouterResponse},
    routes::SessionState,
    services::api as service_api,
    types::domain,
};
#[cfg(feature = "v2")]
pub async fn upsert_conditional_config(
    state: SessionState,
    key_store: domain::MerchantKeyStore,
    request: DecisionManagerRequest,
    profile: domain::Profile,
) -> RouterResponse<common_types::payments::DecisionManagerRecord> {
    use common_utils::ext_traits::OptionExt;

    let key_manager_state: &KeyManagerState = &(&state).into();
    let db = &*state.store;
    let name = request.name;
    let program = request.program;
    let timestamp = common_utils::date_time::now_unix_timestamp();

    euclid::frontend::ast::lowering::lower_program(program.clone())
        .change_context(errors::ApiErrorResponse::InvalidRequestData {
            message: "Invalid Request Data".to_string(),
        })
        .attach_printable("The Request has an Invalid Comparison")?;

    let decision_manager_record = common_types::payments::DecisionManagerRecord {
        name,
        program,
        created_at: timestamp,
    };

    let business_profile_update = domain::ProfileUpdate::DecisionManagerRecordUpdate {
        three_ds_decision_manager_config: decision_manager_record,
    };
    let updated_profile = db
        .update_profile_by_profile_id(
            key_manager_state,
            &key_store,
            profile,
            business_profile_update,
        )
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to update decision manager record in business profile")?;

    Ok(service_api::ApplicationResponse::Json(
        updated_profile
            .three_ds_decision_manager_config
            .clone()
            .get_required_value("three_ds_decision_manager_config")
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable(
                "Failed to get updated decision manager record in business profile",
            )?,
    ))
}

#[cfg(feature = "v1")]
pub async fn upsert_conditional_config(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    request: DecisionManager,
) -> RouterResponse<DecisionManagerRecord> {
    use common_utils::ext_traits::{Encode, OptionExt, ValueExt};
    use diesel_models::configs;
    use storage_impl::redis::cache;

    use super::routing::helpers::update_merchant_active_algorithm_ref;

    let db = state.store.as_ref();
    let (name, prog) = match request {
        DecisionManager::DecisionManagerv0(ccr) => {
            let name = ccr.name;

            let prog = ccr
                .algorithm
                .get_required_value("algorithm")
                .change_context(errors::ApiErrorResponse::MissingRequiredField {
                    field_name: "algorithm",
                })
                .attach_printable("Algorithm for config not given")?;
            (name, prog)
        }
        DecisionManager::DecisionManagerv1(dmr) => {
            let name = dmr.name;

            let prog = dmr
                .program
                .get_required_value("program")
                .change_context(errors::ApiErrorResponse::MissingRequiredField {
                    field_name: "program",
                })
                .attach_printable("Program for config not given")?;
            (name, prog)
        }
    };
    let timestamp = common_utils::date_time::now_unix_timestamp();
    let mut algo_id: api_models::routing::RoutingAlgorithmRef = merchant_context
        .get_merchant_account()
        .routing_algorithm
        .clone()
        .map(|val| val.parse_value("routing algorithm"))
        .transpose()
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Could not decode the routing algorithm")?
        .unwrap_or_default();

    let key = merchant_context
        .get_merchant_account()
        .get_id()
        .get_payment_config_routing_id();
    let read_config_key = db.find_config_by_key(&key).await;

    euclid::frontend::ast::lowering::lower_program(prog.clone())
        .change_context(errors::ApiErrorResponse::InvalidRequestData {
            message: "Invalid Request Data".to_string(),
        })
        .attach_printable("The Request has an Invalid Comparison")?;

    match read_config_key {
        Ok(config) => {
            let previous_record: DecisionManagerRecord = config
                .config
                .parse_struct("DecisionManagerRecord")
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("The Payment Config Key Not Found")?;

            let new_algo = DecisionManagerRecord {
                name: previous_record.name,
                program: prog,
                modified_at: timestamp,
                created_at: previous_record.created_at,
            };

            let serialize_updated_str = new_algo
                .encode_to_string_of_json()
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Unable to serialize config to string")?;

            let updated_config = configs::ConfigUpdate::Update {
                config: Some(serialize_updated_str),
            };

            db.update_config_by_key(&key, updated_config)
                .await
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Error serializing the config")?;

            algo_id.update_conditional_config_id(key.clone());
            let config_key = cache::CacheKind::DecisionManager(key.into());
            update_merchant_active_algorithm_ref(
                &state,
                merchant_context.get_merchant_key_store(),
                config_key,
                algo_id,
            )
            .await
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Failed to update routing algorithm ref")?;

            Ok(service_api::ApplicationResponse::Json(new_algo))
        }
        Err(e) if e.current_context().is_db_not_found() => {
            let new_rec = DecisionManagerRecord {
                name: name
                    .get_required_value("name")
                    .change_context(errors::ApiErrorResponse::MissingRequiredField {
                        field_name: "name",
                    })
                    .attach_printable("name of the config not found")?,
                program: prog,
                modified_at: timestamp,
                created_at: timestamp,
            };

            let serialized_str = new_rec
                .encode_to_string_of_json()
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Error serializing the config")?;
            let new_config = configs::ConfigNew {
                key: key.clone(),
                config: serialized_str,
            };

            db.insert_config(new_config)
                .await
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Error fetching the config")?;

            algo_id.update_conditional_config_id(key.clone());
            let config_key = cache::CacheKind::DecisionManager(key.into());
            update_merchant_active_algorithm_ref(
                &state,
                merchant_context.get_merchant_key_store(),
                config_key,
                algo_id,
            )
            .await
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Failed to update routing algorithm ref")?;

            Ok(service_api::ApplicationResponse::Json(new_rec))
        }
        Err(e) => Err(e)
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Error fetching payment config"),
    }
}

#[cfg(feature = "v2")]
pub async fn delete_conditional_config(
    _state: SessionState,
    _merchant_context: domain::MerchantContext,
) -> RouterResponse<()> {
    todo!()
}

#[cfg(feature = "v1")]
pub async fn delete_conditional_config(
    state: SessionState,
    merchant_context: domain::MerchantContext,
) -> RouterResponse<()> {
    use common_utils::ext_traits::ValueExt;
    use storage_impl::redis::cache;

    use super::routing::helpers::update_merchant_active_algorithm_ref;

    let db = state.store.as_ref();
    let key = merchant_context
        .get_merchant_account()
        .get_id()
        .get_payment_config_routing_id();
    let mut algo_id: api_models::routing::RoutingAlgorithmRef = merchant_context
        .get_merchant_account()
        .routing_algorithm
        .clone()
        .map(|value| value.parse_value("routing algorithm"))
        .transpose()
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Could not decode the conditional_config algorithm")?
        .unwrap_or_default();
    algo_id.config_algo_id = None;
    let config_key = cache::CacheKind::DecisionManager(key.clone().into());
    update_merchant_active_algorithm_ref(
        &state,
        merchant_context.get_merchant_key_store(),
        config_key,
        algo_id,
    )
    .await
    .change_context(errors::ApiErrorResponse::InternalServerError)
    .attach_printable("Failed to update deleted algorithm ref")?;

    db.delete_config_by_key(&key)
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to delete routing config from DB")?;
    Ok(service_api::ApplicationResponse::StatusOk)
}

#[cfg(feature = "v1")]
pub async fn retrieve_conditional_config(
    state: SessionState,
    merchant_context: domain::MerchantContext,
) -> RouterResponse<DecisionManagerResponse> {
    let db = state.store.as_ref();
    let algorithm_id = merchant_context
        .get_merchant_account()
        .get_id()
        .get_payment_config_routing_id();
    let algo_config = db
        .find_config_by_key(&algorithm_id)
        .await
        .change_context(errors::ApiErrorResponse::ResourceIdNotFound)
        .attach_printable("The conditional config was not found in the DB")?;
    let record: DecisionManagerRecord = algo_config
        .config
        .parse_struct("ConditionalConfigRecord")
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("The Conditional Config Record was not found")?;

    let response = DecisionManagerRecord {
        name: record.name,
        program: record.program,
        created_at: record.created_at,
        modified_at: record.modified_at,
    };
    Ok(service_api::ApplicationResponse::Json(response))
}

#[cfg(feature = "v2")]
pub async fn retrieve_conditional_config(
    state: SessionState,
    key_store: domain::MerchantKeyStore,
    profile: domain::Profile,
) -> RouterResponse<common_types::payments::DecisionManagerResponse> {
    let db = state.store.as_ref();
    let key_manager_state: &KeyManagerState = &(&state).into();
    let profile_id = profile.get_id();

    let record = profile
        .three_ds_decision_manager_config
        .clone()
        .ok_or(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("The Conditional Config Record was not found")?;

    let response = common_types::payments::DecisionManagerRecord {
        name: record.name,
        program: record.program,
        created_at: record.created_at,
    };
    Ok(service_api::ApplicationResponse::Json(response))
}