pub async fn save_payment_method<FData>(
    state: &SessionState,
    connector_name: String,
    save_payment_method_data: SavePaymentMethodData<FData>,
    customer_id: Option<id_type::CustomerId>,
    merchant_context: &domain::MerchantContext,
    payment_method_type: Option<storage_enums::PaymentMethodType>,
    billing_name: Option<Secret<String>>,
    payment_method_billing_address: Option<&hyperswitch_domain_models::address::Address>,
    business_profile: &domain::Profile,
    mut original_connector_mandate_reference_id: Option<ConnectorMandateReferenceId>,
    merchant_connector_id: Option<id_type::MerchantConnectorAccountId>,
    vault_operation: Option<hyperswitch_domain_models::payments::VaultOperation>,
    payment_method_info: Option<domain::PaymentMethod>,
) -> RouterResult<SavePaymentMethodDataResponse>
where
    FData: mandate::MandateBehaviour + Clone,
{
    let mut pm_status = None;
    let cards = PmCards {
        state,
        merchant_context,
    };
    match save_payment_method_data.response {
        Ok(responses) => {
            let db = &*state.store;
            let token_store = state
                .conf
                .tokenization
                .0
                .get(&connector_name.to_string())
                .map(|token_filter| token_filter.long_lived_token)
                .unwrap_or(false);

            let network_transaction_id = match &responses {
                types::PaymentsResponseData::TransactionResponse { network_txn_id, .. } => {
                    network_txn_id.clone()
                }
                _ => None,
            };

            let network_transaction_id =
                if save_payment_method_data.request.get_setup_future_usage()
                    == Some(storage_enums::FutureUsage::OffSession)
                {
                    if network_transaction_id.is_some() {
                        network_transaction_id
                    } else {
                        logger::info!("Skip storing network transaction id");
                        None
                    }
                } else {
                    None
                };

            let connector_token = if token_store {
                let tokens = save_payment_method_data
                    .payment_method_token
                    .to_owned()
                    .get_required_value("payment_token")?;
                let token = match tokens {
                    types::PaymentMethodToken::Token(connector_token) => connector_token.expose(),
                    types::PaymentMethodToken::ApplePayDecrypt(_) => {
                        Err(errors::ApiErrorResponse::NotSupported {
                            message: "Apple Pay Decrypt token is not supported".to_string(),
                        })?
                    }
                    types::PaymentMethodToken::PazeDecrypt(_) => {
                        Err(errors::ApiErrorResponse::NotSupported {
                            message: "Paze Decrypt token is not supported".to_string(),
                        })?
                    }
                    types::PaymentMethodToken::GooglePayDecrypt(_) => {
                        Err(errors::ApiErrorResponse::NotSupported {
                            message: "Google Pay Decrypt token is not supported".to_string(),
                        })?
                    }
                };
                Some((connector_name, token))
            } else {
                None
            };

            let mandate_data_customer_acceptance = save_payment_method_data
                .request
                .get_setup_mandate_details()
                .and_then(|mandate_data| mandate_data.customer_acceptance.clone());

            let customer_acceptance = save_payment_method_data
                .request
                .get_customer_acceptance()
                .or(mandate_data_customer_acceptance.clone())
                .map(|ca| ca.encode_to_value())
                .transpose()
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Unable to serialize customer acceptance to value")?;

            let (connector_mandate_id, mandate_metadata, connector_mandate_request_reference_id) =
                match responses {
                    types::PaymentsResponseData::TransactionResponse {
                        mandate_reference, ..
                    } => {
                        if let Some(ref mandate_ref) = *mandate_reference {
                            (
                                mandate_ref.connector_mandate_id.clone(),
                                mandate_ref.mandate_metadata.clone(),
                                mandate_ref.connector_mandate_request_reference_id.clone(),
                            )
                        } else {
                            (None, None, None)
                        }
                    }
                    _ => (None, None, None),
                };

            let pm_id = if customer_acceptance.is_some() {
                let payment_method_data =
                    save_payment_method_data.request.get_payment_method_data();
                let payment_method_create_request =
                    payment_methods::get_payment_method_create_request(
                        Some(&payment_method_data),
                        Some(save_payment_method_data.payment_method),
                        payment_method_type,
                        &customer_id.clone(),
                        billing_name,
                        payment_method_billing_address,
                    )
                    .await?;
                let payment_methods_data =
                    &save_payment_method_data.request.get_payment_method_data();

                let co_badged_card_data = payment_methods_data.get_co_badged_card_data();

                let customer_id = customer_id.to_owned().get_required_value("customer_id")?;
                let merchant_id = merchant_context.get_merchant_account().get_id();
                let is_network_tokenization_enabled =
                    business_profile.is_network_tokenization_enabled;
                let (
                    (mut resp, duplication_check, network_token_requestor_ref_id),
                    network_token_resp,
                ) = if !state.conf.locker.locker_enabled {
                    let (res, dc) = skip_saving_card_in_locker(
                        merchant_context,
                        payment_method_create_request.to_owned(),
                    )
                    .await?;
                    ((res, dc, None), None)
                } else {
                    let payment_method_status = common_enums::PaymentMethodStatus::from(
                        save_payment_method_data.attempt_status,
                    );
                    pm_status = Some(payment_method_status);
                    save_card_and_network_token_in_locker(
                        state,
                        customer_id.clone(),
                        payment_method_status,
                        payment_method_data.clone(),
                        vault_operation,
                        payment_method_info,
                        merchant_context,
                        payment_method_create_request.clone(),
                        is_network_tokenization_enabled,
                    )
                    .await?
                };
                let network_token_locker_id = match network_token_resp {
                    Some(ref token_resp) => {
                        if network_token_requestor_ref_id.is_some() {
                            Some(token_resp.payment_method_id.clone())
                        } else {
                            None
                        }
                    }
                    None => None,
                };

                let optional_pm_details = match (resp.card.as_ref(), payment_method_data) {
                    (Some(card), _) => Some(PaymentMethodsData::Card(
                        CardDetailsPaymentMethod::from((card.clone(), co_badged_card_data)),
                    )),
                    (
                        _,
                        domain::PaymentMethodData::Wallet(domain::WalletData::ApplePay(applepay)),
                    ) => Some(PaymentMethodsData::WalletDetails(
                        PaymentMethodDataWalletInfo::from(applepay),
                    )),
                    (
                        _,
                        domain::PaymentMethodData::Wallet(domain::WalletData::GooglePay(googlepay)),
                    ) => Some(PaymentMethodsData::WalletDetails(
                        PaymentMethodDataWalletInfo::from(googlepay),
                    )),
                    _ => None,
                };

                let key_manager_state = state.into();
                let pm_data_encrypted: Option<Encryptable<Secret<serde_json::Value>>> =
                    optional_pm_details
                        .async_map(|pm| {
                            create_encrypted_data(
                                &key_manager_state,
                                merchant_context.get_merchant_key_store(),
                                pm,
                            )
                        })
                        .await
                        .transpose()
                        .change_context(errors::ApiErrorResponse::InternalServerError)
                        .attach_printable("Unable to encrypt payment method data")?;

                let pm_network_token_data_encrypted: Option<
                    Encryptable<Secret<serde_json::Value>>,
                > = match network_token_resp {
                    Some(token_resp) => {
                        let pm_token_details = token_resp.card.as_ref().map(|card| {
                            PaymentMethodsData::Card(CardDetailsPaymentMethod::from((
                                card.clone(),
                                None,
                            )))
                        });

                        pm_token_details
                            .async_map(|pm_card| {
                                create_encrypted_data(
                                    &key_manager_state,
                                    merchant_context.get_merchant_key_store(),
                                    pm_card,
                                )
                            })
                            .await
                            .transpose()
                            .change_context(errors::ApiErrorResponse::InternalServerError)
                            .attach_printable("Unable to encrypt payment method data")?
                    }
                    None => None,
                };

                let encrypted_payment_method_billing_address: Option<
                    Encryptable<Secret<serde_json::Value>>,
                > = payment_method_billing_address
                    .async_map(|address| {
                        create_encrypted_data(
                            &key_manager_state,
                            merchant_context.get_merchant_key_store(),
                            address.clone(),
                        )
                    })
                    .await
                    .transpose()
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("Unable to encrypt payment method billing address")?;

                let mut payment_method_id = resp.payment_method_id.clone();
                let mut locker_id = None;

                match duplication_check {
                    Some(duplication_check) => match duplication_check {
                        payment_methods::transformers::DataDuplicationCheck::Duplicated => {
                            let payment_method = {
                                let existing_pm_by_pmid = db
                                    .find_payment_method(
                                        &(state.into()),
                                        merchant_context.get_merchant_key_store(),
                                        &payment_method_id,
                                        merchant_context.get_merchant_account().storage_scheme,
                                    )
                                    .await;

                                if let Err(err) = existing_pm_by_pmid {
                                    if err.current_context().is_db_not_found() {
                                        locker_id = Some(payment_method_id.clone());
                                        let existing_pm_by_locker_id = db
                                            .find_payment_method_by_locker_id(
                                                &(state.into()),
                                                merchant_context.get_merchant_key_store(),
                                                &payment_method_id,
                                                merchant_context
                                                    .get_merchant_account()
                                                    .storage_scheme,
                                            )
                                            .await;

                                        match &existing_pm_by_locker_id {
                                            Ok(pm) => {
                                                payment_method_id.clone_from(&pm.payment_method_id);
                                            }
                                            Err(_) => {
                                                payment_method_id =
                                                    generate_id(consts::ID_LENGTH, "pm")
                                            }
                                        };
                                        existing_pm_by_locker_id
                                    } else {
                                        Err(err)
                                    }
                                } else {
                                    existing_pm_by_pmid
                                }
                            };

                            resp.payment_method_id = payment_method_id;

                            match payment_method {
                                Ok(pm) => {
                                    let pm_metadata = create_payment_method_metadata(
                                        pm.metadata.as_ref(),
                                        connector_token,
                                    )?;
                                    payment_methods::cards::update_payment_method_metadata_and_last_used(
                                        state,
                                        merchant_context.get_merchant_key_store(),
                                        db,
                                        pm.clone(),
                                        pm_metadata,
                                        merchant_context.get_merchant_account().storage_scheme,
                                    )
                                    .await
                                    .change_context(errors::ApiErrorResponse::InternalServerError)
                                    .attach_printable("Failed to add payment method in db")?;
                                }
                                Err(err) => {
                                    if err.current_context().is_db_not_found() {
                                        let pm_metadata =
                                            create_payment_method_metadata(None, connector_token)?;
                                        cards
                                            .create_payment_method(
                                                &payment_method_create_request,
                                                &customer_id,
                                                &resp.payment_method_id,
                                                locker_id,
                                                merchant_id,
                                                pm_metadata,
                                                customer_acceptance,
                                                pm_data_encrypted,
                                                None,
                                                pm_status,
                                                network_transaction_id,
                                                encrypted_payment_method_billing_address,
                                                resp.card.and_then(|card| {
                                                    card.card_network.map(|card_network| {
                                                        card_network.to_string()
                                                    })
                                                }),
                                                network_token_requestor_ref_id,
                                                network_token_locker_id,
                                                pm_network_token_data_encrypted,
                                            )
                                            .await
                                    } else {
                                        Err(err)
                                            .change_context(
                                                errors::ApiErrorResponse::InternalServerError,
                                            )
                                            .attach_printable("Error while finding payment method")
                                    }?;
                                }
                            };
                        }
                        payment_methods::transformers::DataDuplicationCheck::MetaDataChanged => {
                            if let Some(card) = payment_method_create_request.card.clone() {
                                let payment_method = {
                                    let existing_pm_by_pmid = db
                                        .find_payment_method(
                                            &(state.into()),
                                            merchant_context.get_merchant_key_store(),
                                            &payment_method_id,
                                            merchant_context.get_merchant_account().storage_scheme,
                                        )
                                        .await;

                                    if let Err(err) = existing_pm_by_pmid {
                                        if err.current_context().is_db_not_found() {
                                            locker_id = Some(payment_method_id.clone());
                                            let existing_pm_by_locker_id = db
                                                .find_payment_method_by_locker_id(
                                                    &(state.into()),
                                                    merchant_context.get_merchant_key_store(),
                                                    &payment_method_id,
                                                    merchant_context
                                                        .get_merchant_account()
                                                        .storage_scheme,
                                                )
                                                .await;

                                            match &existing_pm_by_locker_id {
                                                Ok(pm) => {
                                                    payment_method_id
                                                        .clone_from(&pm.payment_method_id);
                                                }
                                                Err(_) => {
                                                    payment_method_id =
                                                        generate_id(consts::ID_LENGTH, "pm")
                                                }
                                            };
                                            existing_pm_by_locker_id
                                        } else {
                                            Err(err)
                                        }
                                    } else {
                                        existing_pm_by_pmid
                                    }
                                };

                                resp.payment_method_id = payment_method_id;

                                let existing_pm = match payment_method {
                                    Ok(pm) => {
                                        let mandate_details =    pm
                                        .connector_mandate_details
                                        .clone()
                                        .map(|val| {
                                            val.parse_value::<PaymentsMandateReference>(
                                                "PaymentsMandateReference",
                                            )
                                        })
                                        .transpose()
                                        .change_context(errors::ApiErrorResponse::InternalServerError)
                                        .attach_printable("Failed to deserialize to Payment Mandate Reference ")?;
                                        if let Some((mandate_details, merchant_connector_id)) =
                                            mandate_details.zip(merchant_connector_id)
                                        {
                                            let connector_mandate_details =
                                                update_connector_mandate_details_status(
                                                    merchant_connector_id,
                                                    mandate_details,
                                                    ConnectorMandateStatus::Inactive,
                                                )?;
                                            payment_methods::cards::update_payment_method_connector_mandate_details(
                                            state,
                                            merchant_context.get_merchant_key_store(),
                                            db,
                                            pm.clone(),
                                            connector_mandate_details,
                                            merchant_context.get_merchant_account().storage_scheme,
                                        )
                                        .await
                                        .change_context(errors::ApiErrorResponse::InternalServerError)
                                        .attach_printable("Failed to add payment method in db")?;
                                        }
                                        Ok(pm)
                                    }
                                    Err(err) => {
                                        if err.current_context().is_db_not_found() {
                                            cards
                                                .create_payment_method(
                                                    &payment_method_create_request,
                                                    &customer_id,
                                                    &resp.payment_method_id,
                                                    locker_id,
                                                    merchant_id,
                                                    resp.metadata.clone().map(|val| val.expose()),
                                                    customer_acceptance,
                                                    pm_data_encrypted,
                                                    None,
                                                    pm_status,
                                                    network_transaction_id,
                                                    encrypted_payment_method_billing_address,
                                                    resp.card.and_then(|card| {
                                                        card.card_network.map(|card_network| {
                                                            card_network.to_string()
                                                        })
                                                    }),
                                                    network_token_requestor_ref_id,
                                                    network_token_locker_id,
                                                    pm_network_token_data_encrypted,
                                                )
                                                .await
                                        } else {
                                            Err(err)
                                                .change_context(
                                                    errors::ApiErrorResponse::InternalServerError,
                                                )
                                                .attach_printable(
                                                    "Error while finding payment method",
                                                )
                                        }
                                    }
                                }?;

                                cards
                                    .delete_card_from_locker(
                                        &customer_id,
                                        merchant_id,
                                        existing_pm
                                            .locker_id
                                            .as_ref()
                                            .unwrap_or(&existing_pm.payment_method_id),
                                    )
                                    .await?;

                                let add_card_resp = cards
                                    .add_card_hs(
                                        payment_method_create_request,
                                        &card,
                                        &customer_id,
                                        api::enums::LockerChoice::HyperswitchCardVault,
                                        Some(
                                            existing_pm
                                                .locker_id
                                                .as_ref()
                                                .unwrap_or(&existing_pm.payment_method_id),
                                        ),
                                    )
                                    .await;

                                if let Err(err) = add_card_resp {
                                    logger::error!(vault_err=?err);
                                    db.delete_payment_method_by_merchant_id_payment_method_id(
                                        &(state.into()),
                                        merchant_context.get_merchant_key_store(),
                                        merchant_id,
                                        &resp.payment_method_id,
                                    )
                                    .await
                                    .to_not_found_response(
                                        errors::ApiErrorResponse::PaymentMethodNotFound,
                                    )?;

                                    Err(report!(errors::ApiErrorResponse::InternalServerError)
                                        .attach_printable(
                                            "Failed while updating card metadata changes",
                                        ))?
                                };

                                let existing_pm_data = cards
                                    .get_card_details_without_locker_fallback(&existing_pm)
                                    .await?;

                                // scheme should be updated in case of co-badged cards
                                let card_scheme = card
                                    .card_network
                                    .clone()
                                    .map(|card_network| card_network.to_string())
                                    .or(existing_pm_data.scheme.clone());

                                let updated_card = Some(CardDetailFromLocker {
                                    scheme: card_scheme.clone(),
                                    last4_digits: Some(card.card_number.get_last4()),
                                    issuer_country: card
                                        .card_issuing_country
                                        .or(existing_pm_data.issuer_country),
                                    card_isin: Some(card.card_number.get_card_isin()),
                                    card_number: Some(card.card_number),
                                    expiry_month: Some(card.card_exp_month),
                                    expiry_year: Some(card.card_exp_year),
                                    card_token: None,
                                    card_fingerprint: None,
                                    card_holder_name: card
                                        .card_holder_name
                                        .or(existing_pm_data.card_holder_name),
                                    nick_name: card.nick_name.or(existing_pm_data.nick_name),
                                    card_network: card
                                        .card_network
                                        .or(existing_pm_data.card_network),
                                    card_issuer: card.card_issuer.or(existing_pm_data.card_issuer),
                                    card_type: card.card_type.or(existing_pm_data.card_type),
                                    saved_to_locker: true,
                                });

                                let updated_pmd = updated_card.as_ref().map(|card| {
                                    PaymentMethodsData::Card(CardDetailsPaymentMethod::from((
                                        card.clone(),
                                        co_badged_card_data,
                                    )))
                                });
                                let pm_data_encrypted: Option<
                                    Encryptable<Secret<serde_json::Value>>,
                                > = updated_pmd
                                    .async_map(|pmd| {
                                        create_encrypted_data(
                                            &key_manager_state,
                                            merchant_context.get_merchant_key_store(),
                                            pmd,
                                        )
                                    })
                                    .await
                                    .transpose()
                                    .change_context(errors::ApiErrorResponse::InternalServerError)
                                    .attach_printable("Unable to encrypt payment method data")?;

                                payment_methods::cards::update_payment_method_and_last_used(
                                    state,
                                    merchant_context.get_merchant_key_store(),
                                    db,
                                    existing_pm,
                                    pm_data_encrypted.map(Into::into),
                                    merchant_context.get_merchant_account().storage_scheme,
                                    card_scheme,
                                )
                                .await
                                .change_context(errors::ApiErrorResponse::InternalServerError)
                                .attach_printable("Failed to add payment method in db")?;
                            }
                        }
                    },
                    None => {
                        let customer_saved_pm_option = if payment_method_type
                            .map(|payment_method_type_value| {
                                payment_method_type_value
                                    .should_check_for_customer_saved_payment_method_type()
                            })
                            .unwrap_or(false)
                        {
                            match state
                                .store
                                .find_payment_method_by_customer_id_merchant_id_list(
                                    &(state.into()),
                                    merchant_context.get_merchant_key_store(),
                                    &customer_id,
                                    merchant_id,
                                    None,
                                )
                                .await
                            {
                                Ok(customer_payment_methods) => Ok(customer_payment_methods
                                    .iter()
                                    .find(|payment_method| {
                                        payment_method.get_payment_method_subtype()
                                            == payment_method_type
                                    })
                                    .cloned()),
                                Err(error) => {
                                    if error.current_context().is_db_not_found() {
                                        Ok(None)
                                    } else {
                                        Err(error)
                                            .change_context(
                                                errors::ApiErrorResponse::InternalServerError,
                                            )
                                            .attach_printable(
                                                "failed to find payment methods for a customer",
                                            )
                                    }
                                }
                            }
                        } else {
                            Ok(None)
                        }?;

                        if let Some(customer_saved_pm) = customer_saved_pm_option {
                            payment_methods::cards::update_last_used_at(
                                &customer_saved_pm,
                                state,
                                merchant_context.get_merchant_account().storage_scheme,
                                merchant_context.get_merchant_key_store(),
                            )
                            .await
                            .map_err(|e| {
                                logger::error!("Failed to update last used at: {:?}", e);
                            })
                            .ok();
                            resp.payment_method_id = customer_saved_pm.payment_method_id;
                        } else {
                            let pm_metadata =
                                create_payment_method_metadata(None, connector_token)?;

                            locker_id = resp.payment_method.and_then(|pm| {
                                if pm == PaymentMethod::Card {
                                    Some(resp.payment_method_id)
                                } else {
                                    None
                                }
                            });

                            resp.payment_method_id = generate_id(consts::ID_LENGTH, "pm");
                            cards
                                .create_payment_method(
                                    &payment_method_create_request,
                                    &customer_id,
                                    &resp.payment_method_id,
                                    locker_id,
                                    merchant_id,
                                    pm_metadata,
                                    customer_acceptance,
                                    pm_data_encrypted,
                                    None,
                                    pm_status,
                                    network_transaction_id,
                                    encrypted_payment_method_billing_address,
                                    resp.card.and_then(|card| {
                                        card.card_network
                                            .map(|card_network| card_network.to_string())
                                    }),
                                    network_token_requestor_ref_id.clone(),
                                    network_token_locker_id,
                                    pm_network_token_data_encrypted,
                                )
                                .await?;

                            match network_token_requestor_ref_id {
                                Some(network_token_requestor_ref_id) => {
                                    //Insert the network token reference ID along with merchant id, customer id in CallbackMapper table for its respective webooks
                                    let callback_mapper_data =
                                        CallbackMapperData::NetworkTokenWebhook {
                                            merchant_id: merchant_context
                                                .get_merchant_account()
                                                .get_id()
                                                .clone(),
                                            customer_id,
                                            payment_method_id: resp.payment_method_id.clone(),
                                        };
                                    let callback_mapper = CallbackMapper::new(
                                        network_token_requestor_ref_id,
                                        common_enums::CallbackMapperIdType::NetworkTokenRequestorReferenceID,
                                        callback_mapper_data,
                                        common_utils::date_time::now(),
                                        common_utils::date_time::now(),
                                    );

                                    db.insert_call_back_mapper(callback_mapper)
                                        .await
                                        .change_context(
                                            errors::ApiErrorResponse::InternalServerError,
                                        )
                                        .attach_printable(
                                            "Failed to insert in Callback Mapper table",
                                        )?;
                                }
                                None => {
                                    logger::info!("Network token requestor reference ID is not available, skipping callback mapper insertion");
                                }
                            };
                        };
                    }
                }

                Some(resp.payment_method_id)
            } else {
                None
            };
            // check if there needs to be a config if yes then remove it to a different place
            let connector_mandate_reference_id = if connector_mandate_id.is_some() {
                if let Some(ref mut record) = original_connector_mandate_reference_id {
                    record.update(
                        connector_mandate_id,
                        None,
                        None,
                        mandate_metadata,
                        connector_mandate_request_reference_id,
                    );
                    Some(record.clone())
                } else {
                    Some(ConnectorMandateReferenceId::new(
                        connector_mandate_id,
                        None,
                        None,
                        mandate_metadata,
                        connector_mandate_request_reference_id,
                    ))
                }
            } else {
                None
            };

            Ok(SavePaymentMethodDataResponse {
                payment_method_id: pm_id,
                payment_method_status: pm_status,
                connector_mandate_reference_id,
            })
        }
        Err(_) => Ok(SavePaymentMethodDataResponse {
            payment_method_id: None,
            payment_method_status: None,
            connector_mandate_reference_id: None,
        }),
    }
}


impl api::RefundSync for Blackhawknetwork {}

impl api::RefundSync for for Blackhawknetwork


pub struct XenditMetadata {
    pub for_user_id: String,
}


impl ProcessTrackerBatch {
    pub fn to_redis_field_value_pairs(
        &self,
    ) -> CustomResult<Vec<(&str, String)>, errors::ProcessTrackerError> {
        Ok(vec![
            ("id", self.id.to_string()),
            ("group_name", self.group_name.to_string()),
            ("stream_name", self.stream_name.to_string()),
            ("connection_name", self.connection_name.to_string()),
            (
                "created_time",
                self.created_time.assume_utc().unix_timestamp().to_string(),
            ),
            ("rule", self.rule.to_string()),
            (
                "trackers",
                serde_json::to_string(&self.trackers)
                    .change_context(errors::ProcessTrackerError::SerializationFailed)
                    .attach_printable_lazy(|| {
                        format!("Unable to stringify trackers: {:?}", self.trackers)
                    })?,
            ),
        ])
    }

    pub fn from_redis_stream_entry(
        entry: HashMap<String, Option<String>>,
    ) -> CustomResult<Self, errors::ProcessTrackerError> {
        let mut entry = entry;
        let id = entry
            .remove("id")
            .flatten()
            .get_required_value("id")
            .change_context(errors::ProcessTrackerError::MissingRequiredField)?;
        let group_name = entry
            .remove("group_name")
            .flatten()
            .get_required_value("group_name")
            .change_context(errors::ProcessTrackerError::MissingRequiredField)?;
        let stream_name = entry
            .remove("stream_name")
            .flatten()
            .get_required_value("stream_name")
            .change_context(errors::ProcessTrackerError::MissingRequiredField)?;
        let connection_name = entry
            .remove("connection_name")
            .flatten()
            .get_required_value("connection_name")
            .change_context(errors::ProcessTrackerError::MissingRequiredField)?;
        let created_time = entry
            .remove("created_time")
            .flatten()
            .get_required_value("created_time")
            .change_context(errors::ProcessTrackerError::MissingRequiredField)?;

        //make it parser error
        let created_time = {
            let offset_date_time = time::OffsetDateTime::from_unix_timestamp(
                created_time
                    .as_str()
                    .parse::<i64>()
                    .change_context(errors::ParsingError::UnknownError)
                    .change_context(errors::ProcessTrackerError::DeserializationFailed)?,
            )
            .attach_printable_lazy(|| format!("Unable to parse time {}", &created_time))
            .change_context(errors::ProcessTrackerError::MissingRequiredField)?;
            PrimitiveDateTime::new(offset_date_time.date(), offset_date_time.time())
        };

        let rule = entry
            .remove("rule")
            .flatten()
            .get_required_value("rule")
            .change_context(errors::ProcessTrackerError::MissingRequiredField)?;
        let trackers = entry
            .remove("trackers")
            .flatten()
            .get_required_value("trackers")
            .change_context(errors::ProcessTrackerError::MissingRequiredField)?;

        let trackers = serde_json::from_str::<Vec<ProcessTracker>>(trackers.as_str())
            .change_context(errors::ParsingError::UnknownError)
            .attach_printable_lazy(|| {
                format!("Unable to parse trackers from JSON string: {trackers:?}")
            })
            .change_context(errors::ProcessTrackerError::DeserializationFailed)
            .attach_printable("Error parsing ProcessTracker from redis stream entry")?;

        Ok(Self {
            id,
            group_name,
            stream_name,
            connection_name,
            created_time,
            rule,
            trackers,
        })
    }
}


pub async fn get_payment_intent_metrics(
        &self,
        metric: &PaymentIntentMetrics,
        dimensions: &[PaymentIntentDimensions],
        auth: &AuthInfo,
        filters: &PaymentIntentFilters,
        granularity: Option<Granularity>,
        time_range: &TimeRange,
    ) -> types::MetricsResult<HashSet<(PaymentIntentMetricsBucketIdentifier, PaymentIntentMetricRow)>>
    {
        // Metrics to get the fetch time for each payment intent metric
        metrics::request::record_operation_time(
            async {
                match self {
                        Self::Sqlx(pool) => {
                        metric
                            .load_metrics(
                                dimensions,
                                auth,
                                filters,
                                granularity,
                                time_range,
                                pool,
                            )
                            .await
                    }
                                        Self::Clickhouse(pool) => {
                        metric
                            .load_metrics(
                                dimensions,
                                auth,
                                filters,
                                granularity,
                                time_range,
                                pool,
                            )
                            .await
                    }
                                    Self::CombinedCkh(sqlx_pool, ckh_pool) => {
                        let (ckh_result, sqlx_result) = tokio::join!(metric
                            .load_metrics(
                                dimensions,
                                auth,
                                filters,
                                granularity,
                                time_range,
                                ckh_pool,
                            ),
                            metric
                            .load_metrics(
                                dimensions,
                                auth,
                                filters,
                                granularity,
                                time_range,
                                sqlx_pool,
                            ));
                        match (&sqlx_result, &ckh_result) {
                            (Ok(ref sqlx_res), Ok(ref ckh_res)) if sqlx_res != ckh_res => {
                                router_env::logger::error!(clickhouse_result=?ckh_res, postgres_result=?sqlx_res, "Mismatch between clickhouse & postgres payment intents analytics metrics")
                            },
                            _ => {}

                        };

                        ckh_result
                    }
                                    Self::CombinedSqlx(sqlx_pool, ckh_pool) => {
                        let (ckh_result, sqlx_result) = tokio::join!(metric
                            .load_metrics(
                                dimensions,
                                auth,
                                filters,
                                granularity,
                                time_range,
                                ckh_pool,
                            ),
                            metric
                            .load_metrics(
                                dimensions,
                                auth,
                                filters,
                                granularity,
                                time_range,
                                sqlx_pool,
                            ));
                        match (&sqlx_result, &ckh_result) {
                            (Ok(ref sqlx_res), Ok(ref ckh_res)) if sqlx_res != ckh_res => {
                                router_env::logger::error!(clickhouse_result=?ckh_res, postgres_result=?sqlx_res, "Mismatch between clickhouse & postgres payment intents analytics metrics")
                            },
                            _ => {}

                        };

                        sqlx_result
                    }
                }
            },
            &metrics::METRIC_FETCH_TIME,
            metric,
            self,
        )
        .await
    }


File: crates/storage_impl/src/lookup.rs

use common_utils::errors::CustomResult;
use diesel_models::{
    enums as storage_enums, kv,
    reverse_lookup::{
        ReverseLookup as DieselReverseLookup, ReverseLookupNew as DieselReverseLookupNew,
    },
};
use error_stack::ResultExt;
use redis_interface::SetnxReply;

use crate::{
    diesel_error_to_data_error,
    errors::{self, RedisErrorExt},
    kv_router_store::KVRouterStore,
    redis::kv_store::{decide_storage_scheme, kv_wrapper, KvOperation, Op, PartitionKey},
    utils::{self, try_redis_get_else_try_database_get},
    DatabaseStore, RouterStore,
};

#[async_trait::async_trait]
pub trait ReverseLookupInterface {
    async fn insert_reverse_lookup(
        &self,
        _new: DieselReverseLookupNew,
        storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> CustomResult<DieselReverseLookup, errors::StorageError>;
    async fn get_lookup_by_lookup_id(
        &self,
        _id: &str,
        storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> CustomResult<DieselReverseLookup, errors::StorageError>;
}

#[async_trait::async_trait]
impl<T: DatabaseStore> ReverseLookupInterface for RouterStore<T> {
    async fn insert_reverse_lookup(
        &self,
        new: DieselReverseLookupNew,
        _storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> CustomResult<DieselReverseLookup, errors::StorageError> {
        let conn = self
            .get_master_pool()
            .get()
            .await
            .change_context(errors::StorageError::DatabaseConnectionError)?;
        new.insert(&conn).await.map_err(|er| {
            let new_err = diesel_error_to_data_error(*er.current_context());
            er.change_context(new_err)
        })
    }

    async fn get_lookup_by_lookup_id(
        &self,
        id: &str,
        _storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> CustomResult<DieselReverseLookup, errors::StorageError> {
        let conn = utils::pg_connection_read(self).await?;
        DieselReverseLookup::find_by_lookup_id(id, &conn)
            .await
            .map_err(|er| {
                let new_err = diesel_error_to_data_error(*er.current_context());
                er.change_context(new_err)
            })
    }
}

#[async_trait::async_trait]
impl<T: DatabaseStore> ReverseLookupInterface for KVRouterStore<T> {
    async fn insert_reverse_lookup(
        &self,
        new: DieselReverseLookupNew,
        storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> CustomResult<DieselReverseLookup, errors::StorageError> {
        let storage_scheme = Box::pin(decide_storage_scheme::<_, DieselReverseLookup>(
            self,
            storage_scheme,
            Op::Insert,
        ))
        .await;
        match storage_scheme {
            storage_enums::MerchantStorageScheme::PostgresOnly => {
                self.router_store
                    .insert_reverse_lookup(new, storage_scheme)
                    .await
            }
            storage_enums::MerchantStorageScheme::RedisKv => {
                let created_rev_lookup = DieselReverseLookup {
                    lookup_id: new.lookup_id.clone(),
                    sk_id: new.sk_id.clone(),
                    pk_id: new.pk_id.clone(),
                    source: new.source.clone(),
                    updated_by: storage_scheme.to_string(),
                };
                let redis_entry = kv::TypedSql {
                    op: kv::DBOperation::Insert {
                        insertable: Box::new(kv::Insertable::ReverseLookUp(new)),
                    },
                };

                match Box::pin(kv_wrapper::<DieselReverseLookup, _, _>(
                    self,
                    KvOperation::SetNx(&created_rev_lookup, redis_entry),
                    PartitionKey::CombinationKey {
                        combination: &format!("reverse_lookup_{}", &created_rev_lookup.lookup_id),
                    },
                ))
                .await
                .map_err(|err| err.to_redis_failed_response(&created_rev_lookup.lookup_id))?
                .try_into_setnx()
                {
                    Ok(SetnxReply::KeySet) => Ok(created_rev_lookup),
                    Ok(SetnxReply::KeyNotSet) => Err(errors::StorageError::DuplicateValue {
                        entity: "reverse_lookup",
                        key: Some(created_rev_lookup.lookup_id.clone()),
                    }
                    .into()),
                    Err(er) => Err(er).change_context(errors::StorageError::KVError),
                }
            }
        }
    }

    async fn get_lookup_by_lookup_id(
        &self,
        id: &str,
        storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> CustomResult<DieselReverseLookup, errors::StorageError> {
        let database_call = || async {
            self.router_store
                .get_lookup_by_lookup_id(id, storage_scheme)
                .await
        };
        let storage_scheme = Box::pin(decide_storage_scheme::<_, DieselReverseLookup>(
            self,
            storage_scheme,
            Op::Find,
        ))
        .await;
        match storage_scheme {
            storage_enums::MerchantStorageScheme::PostgresOnly => database_call().await,
            storage_enums::MerchantStorageScheme::RedisKv => {
                let redis_fut = async {
                    Box::pin(kv_wrapper(
                        self,
                        KvOperation::<DieselReverseLookup>::Get,
                        PartitionKey::CombinationKey {
                            combination: &format!("reverse_lookup_{id}"),
                        },
                    ))
                    .await?
                    .try_into_get()
                };

                Box::pin(try_redis_get_else_try_database_get(
                    redis_fut,
                    database_call,
                ))
                .await
            }
        }
    }
}


impl<'a> KafkaDisputeEvent<'a> {
    pub fn from_storage(dispute: &'a Dispute) -> Self {
        let currency = dispute.dispute_currency.unwrap_or(
            dispute
                .currency
                .to_uppercase()
                .parse_enum("Currency")
                .unwrap_or_default(),
        );
        Self {
            dispute_id: &dispute.dispute_id,
            dispute_amount: StringMinorUnitForConnector::convert_back(
                &StringMinorUnitForConnector,
                dispute.amount.clone(),
                currency,
            )
            .unwrap_or_else(|e| {
                router_env::logger::error!("Failed to convert dispute amount: {e:?}");
                MinorUnit::new(0)
            }),
            currency,
            dispute_stage: &dispute.dispute_stage,
            dispute_status: &dispute.dispute_status,
            payment_id: &dispute.payment_id,
            attempt_id: &dispute.attempt_id,
            merchant_id: &dispute.merchant_id,
            connector_status: &dispute.connector_status,
            connector_dispute_id: &dispute.connector_dispute_id,
            connector_reason: dispute.connector_reason.as_ref(),
            connector_reason_code: dispute.connector_reason_code.as_ref(),
            challenge_required_by: dispute.challenge_required_by.map(|i| i.assume_utc()),
            connector_created_at: dispute.connector_created_at.map(|i| i.assume_utc()),
            connector_updated_at: dispute.connector_updated_at.map(|i| i.assume_utc()),
            created_at: dispute.created_at.assume_utc(),
            modified_at: dispute.modified_at.assume_utc(),
            connector: &dispute.connector,
            evidence: &dispute.evidence,
            profile_id: dispute.profile_id.as_ref(),
            merchant_connector_id: dispute.merchant_connector_id.as_ref(),
            organization_id: &dispute.organization_id,
        }
    }
}


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


pub struct CardRequestAttributes {
    vault: Option<PaypalVault>,
    verification: Option<ThreeDsMethod>,
}


impl api::ConnectorAccessToken for Blackhawknetwork {}

impl api::ConnectorAccessToken for for Blackhawknetwork


impl Display for InvalidFileStorageConfig {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "file_storage: {}", self.0)
    }
}

impl Display for for InvalidFileStorageConfig


pub struct ShippingLines {
    price: StringMajorUnit,
    title: Option<String>,
}


pub async fn consumer_operations<T: SchedulerSessionState + 'static>(
    state: &T,
    settings: &SchedulerSettings,
    workflow_selector: impl workflows::ProcessTrackerWorkflows<T> + 'static + Copy + std::fmt::Debug,
) -> CustomResult<(), errors::ProcessTrackerError> {
    let stream_name = settings.stream.clone();
    let group_name = settings.consumer.consumer_group.clone();
    let consumer_name = format!("consumer_{}", Uuid::new_v4());

    let _group_created = &mut state
        .get_db()
        .consumer_group_create(&stream_name, &group_name, &RedisEntryId::AfterLastID)
        .await;

    let mut tasks = state
        .get_db()
        .as_scheduler()
        .fetch_consumer_tasks(&stream_name, &group_name, &consumer_name)
        .await?;

    if !tasks.is_empty() {
        logger::info!("{} picked {} tasks", consumer_name, tasks.len());
    }
    let mut handler = vec![];

    for task in tasks.iter_mut() {
        let pickup_time = common_utils::date_time::now();

        pt_utils::add_histogram_metrics(&pickup_time, task, &stream_name);

        metrics::TASK_CONSUMED.add(1, &[]);

        handler.push(tokio::task::spawn(start_workflow(
            state.clone(),
            task.clone(),
            pickup_time,
            workflow_selector,
        )))
    }
    future::join_all(handler).await;

    Ok(())
}


File: crates/router/src/types/storage/subscription.rs

pub use diesel_models::subscription::{Subscription, SubscriptionNew, SubscriptionUpdate};


pub struct SsoSignInRequest {
    pub state: Secret<String>,
    pub code: Secret<String>,
}


pub struct GivexGiftCardAdditionalData {
    /// Last 4 digits of the gift card number
    #[schema(value_type = String, example = "4242")]
    pub last4: Secret<String>,
}


File: crates/hyperswitch_connectors/src/connectors/paystack.rs
 Public functions: 1
 Public structs: 1

pub mod transformers;
use std::sync::LazyLock;

use common_enums::enums;
use common_utils::{
    crypto,
    errors::CustomResult,
    ext_traits::{ByteSliceExt, BytesExt},
    request::{Method, Request, RequestBuilder, RequestContent},
    types::{AmountConvertor, MinorUnit, MinorUnitForConnector},
};
use error_stack::ResultExt;
use hyperswitch_domain_models::{
    router_data::{AccessToken, ConnectorAuthType, ErrorResponse, RouterData},
    router_flow_types::{
        access_token_auth::AccessTokenAuth,
        payments::{Authorize, Capture, PSync, PaymentMethodToken, Session, SetupMandate, Void},
        refunds::{Execute, RSync},
    },
    router_request_types::{
        AccessTokenRequestData, PaymentMethodTokenizationData, PaymentsAuthorizeData,
        PaymentsCancelData, PaymentsCaptureData, PaymentsSessionData, PaymentsSyncData,
        RefundsData, SetupMandateRequestData,
    },
    router_response_types::{
        ConnectorInfo, PaymentMethodDetails, PaymentsResponseData, RefundsResponseData,
        SupportedPaymentMethods, SupportedPaymentMethodsExt,
    },
    types::{
        PaymentsAuthorizeRouterData, PaymentsCaptureRouterData, PaymentsSyncRouterData,
        RefundSyncRouterData, RefundsRouterData,
    },
};
use hyperswitch_interfaces::{
    api::{
        self, ConnectorCommon, ConnectorCommonExt, ConnectorIntegration, ConnectorSpecifications,
        ConnectorValidation,
    },
    configs::Connectors,
    errors,
    events::connector_api_logs::ConnectorEvent,
    types::{self, Response},
    webhooks,
};
use masking::{ExposeInterface, Mask};
use transformers as paystack;

use crate::{constants::headers, types::ResponseRouterData, utils};

#[derive(Clone)]
pub struct Paystack {
    amount_converter: &'static (dyn AmountConvertor<Output = MinorUnit> + Sync),
}

impl Paystack {
    pub fn new() -> &'static Self {
        &Self {
            amount_converter: &MinorUnitForConnector,
        }
    }
}

impl api::Payment for Paystack {}
impl api::PaymentSession for Paystack {}
impl api::ConnectorAccessToken for Paystack {}
impl api::MandateSetup for Paystack {}
impl api::PaymentAuthorize for Paystack {}
impl api::PaymentSync for Paystack {}
impl api::PaymentCapture for Paystack {}
impl api::PaymentVoid for Paystack {}
impl api::Refund for Paystack {}
impl api::RefundExecute for Paystack {}
impl api::RefundSync for Paystack {}
impl api::PaymentToken for Paystack {}

impl ConnectorIntegration<PaymentMethodToken, PaymentMethodTokenizationData, PaymentsResponseData>
    for Paystack
{
    // Not Implemented (R)
}

impl<Flow, Request, Response> ConnectorCommonExt<Flow, Request, Response> for Paystack
where
    Self: ConnectorIntegration<Flow, Request, Response>,
{
    fn build_headers(
        &self,
        req: &RouterData<Flow, Request, Response>,
        _connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        let mut header = vec![(
            headers::CONTENT_TYPE.to_string(),
            self.get_content_type().to_string().into(),
        )];
        let mut api_key = self.get_auth_header(&req.connector_auth_type)?;
        header.append(&mut api_key);
        Ok(header)
    }
}

impl ConnectorCommon for Paystack {
    fn id(&self) -> &'static str {
        "paystack"
    }

    fn get_currency_unit(&self) -> api::CurrencyUnit {
        api::CurrencyUnit::Minor
    }

    fn common_get_content_type(&self) -> &'static str {
        "application/json"
    }

    fn base_url<'a>(&self, connectors: &'a Connectors) -> &'a str {
        connectors.paystack.base_url.as_ref()
    }

    fn get_auth_header(
        &self,
        auth_type: &ConnectorAuthType,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        let auth = paystack::PaystackAuthType::try_from(auth_type)
            .change_context(errors::ConnectorError::FailedToObtainAuthType)?;
        Ok(vec![(
            headers::AUTHORIZATION.to_string(),
            format!("Bearer {}", auth.api_key.expose()).into_masked(),
        )])
    }

    fn build_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        let response: paystack::PaystackErrorResponse = res
            .response
            .parse_struct("PaystackErrorResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;

        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);
        let error_message = paystack::get_error_message(response.clone());

        Ok(ErrorResponse {
            status_code: res.status_code,
            code: response.code,
            message: error_message,
            reason: Some(response.message),
            attempt_status: None,
            connector_transaction_id: None,
            network_advice_code: None,
            network_decline_code: None,
            network_error_message: None,
            connector_metadata: None,
        })
    }
}

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

impl ConnectorIntegration<Session, PaymentsSessionData, PaymentsResponseData> for Paystack {
    //TODO: implement sessions flow
}

impl ConnectorIntegration<AccessTokenAuth, AccessTokenRequestData, AccessToken> for Paystack {}

impl ConnectorIntegration<SetupMandate, SetupMandateRequestData, PaymentsResponseData>
    for Paystack
{
}

impl ConnectorIntegration<Authorize, PaymentsAuthorizeData, PaymentsResponseData> for Paystack {
    fn get_headers(
        &self,
        req: &PaymentsAuthorizeRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        self.build_headers(req, connectors)
    }

    fn get_content_type(&self) -> &'static str {
        self.common_get_content_type()
    }

    fn get_url(
        &self,
        _req: &PaymentsAuthorizeRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        Ok(format!("{}/charge", self.base_url(connectors)))
    }

    fn get_request_body(
        &self,
        req: &PaymentsAuthorizeRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        let amount = utils::convert_amount(
            self.amount_converter,
            req.request.minor_amount,
            req.request.currency,
        )?;

        let connector_router_data = paystack::PaystackRouterData::from((amount, req));
        let connector_req = paystack::PaystackPaymentsRequest::try_from(&connector_router_data)?;
        Ok(RequestContent::Json(Box::new(connector_req)))
    }

    fn build_request(
        &self,
        req: &PaymentsAuthorizeRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Ok(Some(
            RequestBuilder::new()
                .method(Method::Post)
                .url(&types::PaymentsAuthorizeType::get_url(
                    self, req, connectors,
                )?)
                .attach_default_headers()
                .headers(types::PaymentsAuthorizeType::get_headers(
                    self, req, connectors,
                )?)
                .set_body(types::PaymentsAuthorizeType::get_request_body(
                    self, req, connectors,
                )?)
                .build(),
        ))
    }

    fn handle_response(
        &self,
        data: &PaymentsAuthorizeRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<PaymentsAuthorizeRouterData, errors::ConnectorError> {
        let response: paystack::PaystackPaymentsResponse = res
            .response
            .parse_struct("Paystack PaymentsAuthorizeResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);
        RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
    }

    fn get_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        self.build_error_response(res, event_builder)
    }
}

impl ConnectorIntegration<PSync, PaymentsSyncData, PaymentsResponseData> for Paystack {
    fn get_headers(
        &self,
        req: &PaymentsSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        self.build_headers(req, connectors)
    }

    fn get_content_type(&self) -> &'static str {
        self.common_get_content_type()
    }

    fn get_url(
        &self,
        req: &PaymentsSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        let connector_payment_id = req
            .request
            .connector_transaction_id
            .get_connector_transaction_id()
            .change_context(errors::ConnectorError::MissingConnectorTransactionID)?;
        Ok(format!(
            "{}{}{}",
            self.base_url(connectors),
            "/transaction/verify/",
            connector_payment_id,
        ))
    }

    fn build_request(
        &self,
        req: &PaymentsSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Ok(Some(
            RequestBuilder::new()
                .method(Method::Get)
                .url(&types::PaymentsSyncType::get_url(self, req, connectors)?)
                .attach_default_headers()
                .headers(types::PaymentsSyncType::get_headers(self, req, connectors)?)
                .build(),
        ))
    }

    fn handle_response(
        &self,
        data: &PaymentsSyncRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<PaymentsSyncRouterData, errors::ConnectorError> {
        let response: paystack::PaystackPSyncResponse = res
            .response
            .parse_struct("paystack PaymentsSyncResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);
        RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
    }

    fn get_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        self.build_error_response(res, event_builder)
    }
}

impl ConnectorIntegration<Capture, PaymentsCaptureData, PaymentsResponseData> for Paystack {
    fn get_headers(
        &self,
        req: &PaymentsCaptureRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        self.build_headers(req, connectors)
    }

    fn get_content_type(&self) -> &'static str {
        self.common_get_content_type()
    }

    fn get_url(
        &self,
        _req: &PaymentsCaptureRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into())
    }

    fn get_request_body(
        &self,
        _req: &PaymentsCaptureRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        Err(errors::ConnectorError::NotImplemented("get_request_body method".to_string()).into())
    }

    fn build_request(
        &self,
        req: &PaymentsCaptureRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Ok(Some(
            RequestBuilder::new()
                .method(Method::Post)
                .url(&types::PaymentsCaptureType::get_url(self, req, connectors)?)
                .attach_default_headers()
                .headers(types::PaymentsCaptureType::get_headers(
                    self, req, connectors,
                )?)
                .set_body(types::PaymentsCaptureType::get_request_body(
                    self, req, connectors,
                )?)
                .build(),
        ))
    }

    fn handle_response(
        &self,
        data: &PaymentsCaptureRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<PaymentsCaptureRouterData, errors::ConnectorError> {
        let response: paystack::PaystackPaymentsResponse = res
            .response
            .parse_struct("Paystack PaymentsCaptureResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);
        RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
    }

    fn get_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        self.build_error_response(res, event_builder)
    }
}

impl ConnectorIntegration<Void, PaymentsCancelData, PaymentsResponseData> for Paystack {}

impl ConnectorIntegration<Execute, RefundsData, RefundsResponseData> for Paystack {
    fn get_headers(
        &self,
        req: &RefundsRouterData<Execute>,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        self.build_headers(req, connectors)
    }

    fn get_content_type(&self) -> &'static str {
        self.common_get_content_type()
    }

    fn get_url(
        &self,
        _req: &RefundsRouterData<Execute>,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        Ok(format!("{}/refund", self.base_url(connectors)))
    }

    fn get_request_body(
        &self,
        req: &RefundsRouterData<Execute>,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        let refund_amount = utils::convert_amount(
            self.amount_converter,
            req.request.minor_refund_amount,
            req.request.currency,
        )?;

        let connector_router_data = paystack::PaystackRouterData::from((refund_amount, req));
        let connector_req = paystack::PaystackRefundRequest::try_from(&connector_router_data)?;
        Ok(RequestContent::Json(Box::new(connector_req)))
    }

    fn build_request(
        &self,
        req: &RefundsRouterData<Execute>,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        let request = RequestBuilder::new()
            .method(Method::Post)
            .url(&types::RefundExecuteType::get_url(self, req, connectors)?)
            .attach_default_headers()
            .headers(types::RefundExecuteType::get_headers(
                self, req, connectors,
            )?)
            .set_body(types::RefundExecuteType::get_request_body(
                self, req, connectors,
            )?)
            .build();
        Ok(Some(request))
    }

    fn handle_response(
        &self,
        data: &RefundsRouterData<Execute>,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<RefundsRouterData<Execute>, errors::ConnectorError> {
        let response: paystack::PaystackRefundsResponse = res
            .response
            .parse_struct("paystack RefundResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);
        RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
    }

    fn get_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        self.build_error_response(res, event_builder)
    }
}

impl ConnectorIntegration<RSync, RefundsData, RefundsResponseData> for Paystack {
    fn get_headers(
        &self,
        req: &RefundSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        self.build_headers(req, connectors)
    }

    fn get_content_type(&self) -> &'static str {
        self.common_get_content_type()
    }

    fn get_url(
        &self,
        req: &RefundSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        let connector_refund_id = req
            .request
            .connector_refund_id
            .clone()
            .ok_or(errors::ConnectorError::MissingConnectorRefundID)?;
        Ok(format!(
            "{}{}{}",
            self.base_url(connectors),
            "/refund/",
            connector_refund_id,
        ))
    }

    fn build_request(
        &self,
        req: &RefundSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Ok(Some(
            RequestBuilder::new()
                .method(Method::Get)
                .url(&types::RefundSyncType::get_url(self, req, connectors)?)
                .attach_default_headers()
                .headers(types::RefundSyncType::get_headers(self, req, connectors)?)
                .set_body(types::RefundSyncType::get_request_body(
                    self, req, connectors,
                )?)
                .build(),
        ))
    }

    fn handle_response(
        &self,
        data: &RefundSyncRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<RefundSyncRouterData, errors::ConnectorError> {
        let response: paystack::PaystackRefundsResponse = res
            .response
            .parse_struct("paystack RefundSyncResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);
        RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
    }

    fn get_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        self.build_error_response(res, event_builder)
    }
}

#[async_trait::async_trait]
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)
            }
        })
    }
}

static PAYSTACK_SUPPORTED_PAYMENT_METHODS: LazyLock<SupportedPaymentMethods> =
    LazyLock::new(|| {
        let supported_capture_methods = vec![
            enums::CaptureMethod::Automatic,
            enums::CaptureMethod::SequentialAutomatic,
        ];

        let mut paystack_supported_payment_methods = SupportedPaymentMethods::new();

        paystack_supported_payment_methods.add(
            enums::PaymentMethod::BankRedirect,
            enums::PaymentMethodType::Eft,
            PaymentMethodDetails {
                mandates: enums::FeatureStatus::NotSupported,
                refunds: enums::FeatureStatus::Supported,
                supported_capture_methods,
                specific_features: None,
            },
        );

        paystack_supported_payment_methods
    });

static PAYSTACK_CONNECTOR_INFO: ConnectorInfo = ConnectorInfo {
    display_name: "Paystack",
    description: "Paystack is a Nigerian financial technology company that provides online and offline payment solutions to businesses across Africa.",
    connector_type: enums::HyperswitchConnectorCategory::PaymentGateway,
    integration_status: enums::ConnectorIntegrationStatus::Sandbox,
};

static PAYSTACK_SUPPORTED_WEBHOOK_FLOWS: [enums::EventClass; 2] =
    [enums::EventClass::Payments, enums::EventClass::Refunds];

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

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

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


impl webhooks::IncomingWebhook for Coingate {
    fn get_webhook_source_verification_algorithm(
        &self,
        _request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<Box<dyn crypto::VerifySignature + Send>, errors::ConnectorError> {
        Ok(Box::new(crypto::HmacSha256))
    }

    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 notif: CoingateWebhookBody = request
            .body
            .parse_struct("CoingateWebhookBody")
            .change_context(errors::ConnectorError::WebhookReferenceIdNotFound)?;
        Ok(api_models::webhooks::ObjectReferenceId::PaymentId(
            api_models::payments::PaymentIdType::ConnectorTransactionId(notif.id.to_string()),
        ))
    }

    fn get_webhook_event_type(
        &self,
        request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<api_models::webhooks::IncomingWebhookEvent, errors::ConnectorError> {
        let notif: CoingateWebhookBody = request
            .body
            .parse_struct("CoingateWebhookBody")
            .change_context(errors::ConnectorError::WebhookEventTypeNotFound)?;
        match notif.status {
            transformers::CoingatePaymentStatus::Pending => {
                Ok(api_models::webhooks::IncomingWebhookEvent::PaymentIntentProcessing)
            }
            transformers::CoingatePaymentStatus::Confirming
            | transformers::CoingatePaymentStatus::New => {
                Ok(api_models::webhooks::IncomingWebhookEvent::PaymentActionRequired)
            }
            transformers::CoingatePaymentStatus::Paid => {
                Ok(api_models::webhooks::IncomingWebhookEvent::PaymentIntentSuccess)
            }
            transformers::CoingatePaymentStatus::Invalid
            | transformers::CoingatePaymentStatus::Expired
            | transformers::CoingatePaymentStatus::Canceled => {
                Ok(api_models::webhooks::IncomingWebhookEvent::PaymentIntentFailure)
            }
        }
    }
    async fn verify_webhook_source(
        &self,
        request: &webhooks::IncomingWebhookRequestDetails<'_>,
        _merchant_id: &common_utils::id_type::MerchantId,
        _connector_webhook_details: Option<common_utils::pii::SecretSerdeValue>,
        connector_account_details: crypto::Encryptable<masking::Secret<serde_json::Value>>,
        _connector_label: &str,
    ) -> CustomResult<bool, errors::ConnectorError> {
        let connector_account_details: ConnectorAuthType = connector_account_details
            .parse_value::<ConnectorAuthType>("ConnectorAuthType")
            .change_context_lazy(|| errors::ConnectorError::WebhookSourceVerificationFailed)?;
        let auth_type = coingate::CoingateAuthType::try_from(&connector_account_details)?;
        let secret_key = auth_type.merchant_token.expose();
        let request_body: CoingateWebhookBody = request
            .body
            .parse_struct("CoingateWebhookBody")
            .change_context(errors::ConnectorError::WebhookReferenceIdNotFound)?;

        let token = request_body.token.expose();
        Ok(secret_key == token)
    }
    fn get_webhook_resource_object(
        &self,
        request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<Box<dyn masking::ErasedMaskSerialize>, errors::ConnectorError> {
        let notif: CoingateWebhookBody = request
            .body
            .parse_struct("CoingateWebhookBody")
            .change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?;

        Ok(Box::new(notif))
    }
}

impl webhooks::IncomingWebhook for for Coingate


pub(super) async fn delete_payment_token_data(
    state: &SessionState,
    key_for_token: &str,
) -> errors::RouterResult<()> {
    let redis_conn = state
        .store
        .get_redis_conn()
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to get redis connection")?;
    match redis_conn.delete_key(&key_for_token.into()).await {
        Ok(_) => Ok(()),
        Err(err) => {
            {
                logger::error!("Error while deleting redis key: {:?}", err)
            };
            Ok(())
        }
    }
}


impl PaypalConnectorCredentials {
    pub fn get_client_id(&self) -> Secret<String> {
        match self {
            Self::StandardIntegration(item) => item.client_id.clone(),
            Self::PartnerIntegration(item) => item.client_id.clone(),
        }
    }

    pub fn get_client_secret(&self) -> Secret<String> {
        match self {
            Self::StandardIntegration(item) => item.client_secret.clone(),
            Self::PartnerIntegration(item) => item.client_secret.clone(),
        }
    }

    pub fn get_payer_id(&self) -> Option<Secret<String>> {
        match self {
            Self::StandardIntegration(_) => None,
            Self::PartnerIntegration(item) => Some(item.payer_id.clone()),
        }
    }

    pub fn generate_authorization_value(&self) -> String {
        let auth_id = format!(
            "{}:{}",
            self.get_client_id().expose(),
            self.get_client_secret().expose(),
        );
        format!("Basic {}", consts::BASE64_ENGINE.encode(auth_id))
    }
}


impl Responder {
        #[allow(clippy::expect_used)]
        // safety: This shouldn't panic owing to the data type
        let payload = json_payload
            .into_inner()
            .to_vec()
            .pop()
            .expect("Couldn't get GetRefundMetricRequest");
        let flow = AnalyticsFlow::GetRefundsMetrics;
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            payload,
            |state, auth: AuthenticationData, req, _| async move {
                let org_id = auth.merchant_account.get_org_id();
                let auth: AuthInfo = AuthInfo::OrgLevel {
                    org_id: org_id.clone(),
                };

                let validator_response = request_validator(
                    AnalyticsRequest {
                        refund: Some(req.clone()),
                        ..Default::default()
                    },
                    &state,
                )
                .await?;
                let ex_rates = validator_response;
                analytics::refunds::get_metrics(&state.pool, &ex_rates, &auth, req)
                    .await
                    .map(ApplicationResponse::Json)
            },
            auth::auth_type(
                &auth::PlatformOrgAdminAuth {
                    is_admin_auth_allowed: false,
                    organization_id: None,
                },
                &auth::JWTAuth {
                    permission: Permission::OrganizationAnalyticsRead,
                },
                req.headers(),
            ),
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }


pub struct SubscriptionUpdate {
    pub payment_method_id: Option<String>,
    pub status: Option<String>,
    pub modified_at: time::PrimitiveDateTime,
}


pub async fn payment_method_delete_api() {}


impl api::PaymentToken for Sift {}


impl ThemeLineage {
    /// Constructor for ThemeLineage
    pub fn new(
        entity_type: EntityType,
        tenant_id: id_type::TenantId,
        org_id: id_type::OrganizationId,
        merchant_id: id_type::MerchantId,
        profile_id: id_type::ProfileId,
    ) -> Self {
        match entity_type {
            EntityType::Tenant => Self::Tenant { tenant_id },
            EntityType::Organization => Self::Organization { tenant_id, org_id },
            EntityType::Merchant => Self::Merchant {
                tenant_id,
                org_id,
                merchant_id,
            },
            EntityType::Profile => Self::Profile {
                tenant_id,
                org_id,
                merchant_id,
                profile_id,
            },
        }
    }

    /// Get the entity_type from the lineage
    pub fn entity_type(&self) -> EntityType {
        match self {
            Self::Tenant { .. } => EntityType::Tenant,
            Self::Organization { .. } => EntityType::Organization,
            Self::Merchant { .. } => EntityType::Merchant,
            Self::Profile { .. } => EntityType::Profile,
        }
    }

    /// Get the tenant_id from the lineage
    pub fn tenant_id(&self) -> &id_type::TenantId {
        match self {
            Self::Tenant { tenant_id }
            | Self::Organization { tenant_id, .. }
            | Self::Merchant { tenant_id, .. }
            | Self::Profile { tenant_id, .. } => tenant_id,
        }
    }

    /// Get the org_id from the lineage
    pub fn org_id(&self) -> Option<&id_type::OrganizationId> {
        match self {
            Self::Tenant { .. } => None,
            Self::Organization { org_id, .. }
            | Self::Merchant { org_id, .. }
            | Self::Profile { org_id, .. } => Some(org_id),
        }
    }

    /// Get the merchant_id from the lineage
    pub fn merchant_id(&self) -> Option<&id_type::MerchantId> {
        match self {
            Self::Tenant { .. } | Self::Organization { .. } => None,
            Self::Merchant { merchant_id, .. } | Self::Profile { merchant_id, .. } => {
                Some(merchant_id)
            }
        }
    }

    /// Get the profile_id from the lineage
    pub fn profile_id(&self) -> Option<&id_type::ProfileId> {
        match self {
            Self::Tenant { .. } | Self::Organization { .. } | Self::Merchant { .. } => None,
            Self::Profile { profile_id, .. } => Some(profile_id),
        }
    }

    /// Get higher lineages from the current lineage
    pub fn get_same_and_higher_lineages(self) -> Vec<Self> {
        match &self {
            Self::Tenant { .. } => vec![self],
            Self::Organization { tenant_id, .. } => vec![
                Self::Tenant {
                    tenant_id: tenant_id.clone(),
                },
                self,
            ],
            Self::Merchant {
                tenant_id, org_id, ..
            } => vec![
                Self::Tenant {
                    tenant_id: tenant_id.clone(),
                },
                Self::Organization {
                    tenant_id: tenant_id.clone(),
                    org_id: org_id.clone(),
                },
                self,
            ],
            Self::Profile {
                tenant_id,
                org_id,
                merchant_id,
                ..
            } => vec![
                Self::Tenant {
                    tenant_id: tenant_id.clone(),
                },
                Self::Organization {
                    tenant_id: tenant_id.clone(),
                    org_id: org_id.clone(),
                },
                Self::Merchant {
                    tenant_id: tenant_id.clone(),
                    org_id: org_id.clone(),
                    merchant_id: merchant_id.clone(),
                },
                self,
            ],
        }
    }
}


pub async fn relay() {}


pub struct BillingConnectorPaymentsSyncFlowRouterData(
    router_types::BillingConnectorPaymentsSyncRouterData,
);


pub trait AuthEventMetricAccumulator {
    type MetricOutput;

    fn add_metrics_bucket(&mut self, metrics: &AuthEventMetricRow);

    fn collect(self) -> Self::MetricOutput;
}


Web Documentation: Fraud & Risk Management | Hyperswitch
# Type: Web Doc

The Hyperswitch Fraud and Risk Management (FRM) platform offers a comprehensive Unified API designed to cater to your specific payment validation needs, effectively enhancing fraud protection within your payment ecosystem.
Key Benefits:
Processor-Agnostic Integration:
Single API connect lets you seamlessly connect with an FRM solution of choice.
Customized Fraud Strategies:
Enables users to adjust fraud prevention measures by allowing them to select between Pre-Auth and Post-Auth checks based on specific payment methods and connectors.
Unified Dashboard:
A consolidated interface that displays all flagged transactions, making decision-making on potential frauds a breeze.
Real-Time Alerts:
Immediate notifications are sent when potential fraudulent activity is detected, ensuring quick action and minimal losses.
Insightful Analytics:
Detailed reports on fraud patterns help inform decisions and strategy adjustments.
Use cases:
Online Marketplaces:
Secure transactions on e-commerce platforms like Amazon or eBay, regardless of payment methods or processors. Benefit from chargeback guarantees, dispute resolution, account security, real-time fraud alerts, and streamlined fraud investigation tools.
High-Value Transactions:
Protect high value orders in luxury retail, or high-end custom services. Ensure payment validity, prevent chargebacks, optimize PSD2 compliance, reduce transaction friction, gain valuable fraud trend insights, and utilize predictive fraud analysis.
Supported FRM Processors
Processors
Supported Flows
Supported Actions
Processor Features
Signifyd / Riskified
PreAuth
PostAuth
Cancel Transactions
Flag for Manual Review
Guaranteed Fraud Protection
Complete Chargeback Protection
Auth Rate Conversions
If your FRM processor of choice is not available in the above list, please put a new features request
here
.
Activating FRM in Hyperswitch
10 months ago
Was this helpful?

Fraud & Risk Management | Hyperswitch