- **connectors:** Fix wallet token deserialization error ([#4133](https://github.com/juspay/hyperswitch/pull/4133)) ([`929848f`](https://github.com/juspay/hyperswitch/commit/929848f8713b45daf479ba24fb0a49b8e327b6fd))
- **core:** Amount capturable remain same for `processing` status in capture ([#4229](https://github.com/juspay/hyperswitch/pull/4229)) ([`9523cf4`](https://github.com/juspay/hyperswitch/commit/9523cf4bbac488503c31640cade326095937e33c))
- **euclid_wasm:** Checkout wasm metadata issue ([#4198](https://github.com/juspay/hyperswitch/pull/4198)) ([`246898f`](https://github.com/juspay/hyperswitch/commit/246898fbb00a67d5791827527ce45e01b01b232c))
- **log:** Adding span metadata to `tokio` spawned futures ([#4118](https://github.com/juspay/hyperswitch/pull/4118)) ([`0706221`](https://github.com/juspay/hyperswitch/commit/070622125f49c4cc9c35f5ba9c634f1fef6b26d2))
- **trustpay:** [Trustpay] Add error code mapping '800.100.100' ([#4224](https://github.com/juspay/hyperswitch/pull/4224)) ([`9798db4`](https://github.com/juspay/hyperswitch/commit/9798db4558d926a218a0ca6f7f7c4e24a187b3da))

### Refactors

- **config:** Allow wildcard origin for development and Docker Compose setups ([#4231](https://github.com/juspay/hyperswitch/pull/4231)) ([`6587472`](https://github.com/juspay/hyperswitch/commit/65874728094bb550d6c311965fbb5f1577091bbb))

**Full Changelog:** [`2024.03.27.0...2024.03.28.0`](https://github.com/juspay/hyperswitch/compare/2024.03.27.0...2024.03.28.0)

- - -

## 2024.03.27.0

### Bug Fixes

- **connector:**
  - [Trustpay] fix deserialization error for incoming webhook response for trustpay and add error code mapping '800.100.203' ([#4199](https://github.com/juspay/hyperswitch/pull/4199)) ([`84bef25`](https://github.com/juspay/hyperswitch/commit/84bef251480a77027b43c3dc91353a0cb40d5ff1))
  - [CRYPTOPAY] Skip metadata serialization if none ([#4205](https://github.com/juspay/hyperswitch/pull/4205)) ([`0429399`](https://github.com/juspay/hyperswitch/commit/0429399c29f76c97bf2096bbe9e9b429c025e56b))
- **core:** Make eci in AuthenticationData optional ([#4187](https://github.com/juspay/hyperswitch/pull/4187)) ([`4f0c788`](https://github.com/juspay/hyperswitch/commit/4f0c788cf26907e2be784978c412081a93386d04))

**Full Changelog:** [`2024.03.26.0...2024.03.27.0`](https://github.com/juspay/hyperswitch/compare/2024.03.26.0...2024.03.27.0)

- - -

## 2024.03.26.0

### Features

- **events:** Allow listing webhook events and webhook delivery attempts by business profile ([#4159](https://github.com/juspay/hyperswitch/pull/4159)) ([`4c8cdf1`](https://github.com/juspay/hyperswitch/commit/4c8cdf1475ac74fb2df5bea419dfa7657f26f298))
- **payouts:** Add user roles for payouts ([#4167](https://github.com/juspay/hyperswitch/pull/4167)) ([`13fe584`](https://github.com/juspay/hyperswitch/commit/13fe58450bad094fb2b4745ecf76bc2df8b96798))

### Miscellaneous Tasks

- Address Rust 1.77 clippy lints ([#4172](https://github.com/juspay/hyperswitch/pull/4172)) ([`f213c51`](https://github.com/juspay/hyperswitch/commit/f213c51b3e5c4f0b3546b35bac4dde9698818e01))

**Full Changelog:** [`2024.03.22.0...2024.03.26.0`](https://github.com/juspay/hyperswitch/compare/2024.03.22.0...2024.03.26.0)

- - -

## 2024.03.22.0

### Features

- **events:** Add APIs to list webhook events and webhook delivery attempts ([#4131](https://github.com/juspay/hyperswitch/pull/4131)) ([`14e1bba`](https://github.com/juspay/hyperswitch/commit/14e1bbaf071d1178f91124fe85580f178cb1cf96))
- **global-search-regex-escape:** Escape reserved characters which break global search query ([#4135](https://github.com/juspay/hyperswitch/pull/4135)) ([`4f8461b`](https://github.com/juspay/hyperswitch/commit/4f8461b2a949fd2a6d24b8b42f1bf8bab55cfeeb))

### Miscellaneous Tasks

- Update Slack workspace URL ([#4168](https://github.com/juspay/hyperswitch/pull/4168)) ([`75b4bac`](https://github.com/juspay/hyperswitch/commit/75b4bacc984d11cb755a8c36821ec41d3f1e2187))

**Full Changelog:** [`2024.03.21.1...2024.03.22.0`](https://github.com/juspay/hyperswitch/compare/2024.03.21.1...2024.03.22.0)

- - -

## 2024.03.21.1

### Features

- **payouts:**
  - Implement list and filter APIs ([#3651](https://github.com/juspay/hyperswitch/pull/3651)) ([`fb5f0e6`](https://github.com/juspay/hyperswitch/commit/fb5f0e6c7eb7255ac423ed4385613e9a78227c77))
  - Add payout types in euclid crate ([#3862](https://github.com/juspay/hyperswitch/pull/3862)) ([`a151485`](https://github.com/juspay/hyperswitch/commit/a1514853176e6cdac73e69d90165416613c97d70))

### Bug Fixes

- **router:** Handle redirection to return_url from nested iframe in separate 3ds flow ([#4164](https://github.com/juspay/hyperswitch/pull/4164)) ([`b8c9275`](https://github.com/juspay/hyperswitch/commit/b8c927593a85792588e582bf25f2daadfa5f7fb0))

**Full Changelog:** [`2024.03.21.0...2024.03.21.1`](https://github.com/juspay/hyperswitch/compare/2024.03.21.0...2024.03.21.1)

- - -

## 2024.03.21.0

### Features

- Store payment check codes and authentication data from processors ([#3958](https://github.com/juspay/hyperswitch/pull/3958)) ([`7afc44e`](https://github.com/juspay/hyperswitch/commit/7afc44e8357b09c900a1e9aa384619f93f3bc81d))

### Bug Fixes

- **payment_methods:**
  - Update payment method status only if existing status is not active ([#4149](https://github.com/juspay/hyperswitch/pull/4149)) ([`0e9b252`](https://github.com/juspay/hyperswitch/commit/0e9b2524cf22a220abeb604dd172aa00855a7ee6))
  - Make `ApplepayPaymentMethod` in payment_method_data column of `payment_attempt` table as json ([#4154](https://github.com/juspay/hyperswitch/pull/4154)) ([`7c0e4c7`](https://github.com/juspay/hyperswitch/commit/7c0e4c7229acacbeb93102bcdc25b74fd7a3314c))

### Refactors

- **connector:** [Stripe] update stripe-api-version in API-headers ([#4120](https://github.com/juspay/hyperswitch/pull/4120)) ([`3653c2c`](https://github.com/juspay/hyperswitch/commit/3653c2c108b80a20df6e8a2bf980d48c204376cd))
- **payment_method_data:** Add a trait to retrieve billing from payment method data ([#4095](https://github.com/juspay/hyperswitch/pull/4095)) ([`9b9bce8`](https://github.com/juspay/hyperswitch/commit/9b9bce80a6419abdd5318d993f1abd6598853dd3))

### Build System / Dependencies

- **router_env:** Obtain workspace member package names from `cargo_metadata` more deterministically ([#4139](https://github.com/juspay/hyperswitch/pull/4139)) ([`8f7d9fb`](https://github.com/juspay/hyperswitch/commit/8f7d9fbc3a002127e220d8a968a6a4e15796e2fd))

**Full Changelog:** [`2024.03.20.0...2024.03.21.0`](https://github.com/juspay/hyperswitch/compare/2024.03.20.0...2024.03.21.0)

- - -

## 2024.03.20.0

### Features

- **global-search:** Add dispute events index to global-search ([#4068](https://github.com/juspay/hyperswitch/pull/4068)) ([`9345379`](https://github.com/juspay/hyperswitch/commit/9345379f85a5da786c8f733542d796da567b6ffc))
- **payouts:** Implement KVRouterStore ([#3889](https://github.com/juspay/hyperswitch/pull/3889)) ([`944089d`](https://github.com/juspay/hyperswitch/commit/944089d6914cb6bece9056f78b9aabf90e485151))
- **router:**
  - Add offset in mandate list route ([#3923](https://github.com/juspay/hyperswitch/pull/3923)) ([`17a866a`](https://github.com/juspay/hyperswitch/commit/17a866a73541c2340547c67e47b60f813c53f744))
  - Handle redirection to return_url from iframe for separate 3ds flow ([#4119](https://github.com/juspay/hyperswitch/pull/4119)) ([`3eb4642`](https://github.com/juspay/hyperswitch/commit/3eb464250e5d604d90a99d61d1c9d6115252f0ef))

### Refactors

- **connector:** [Stripe] make name field of StripeShippingAddress mandatory ([#4111](https://github.com/juspay/hyperswitch/pull/4111)) ([`ab1ec2a`](https://github.com/juspay/hyperswitch/commit/ab1ec2ad4e3f1197d08c5ff947c31e7f0fcf5c65))
- **core:** Move authentication data fields to authentication table ([#4093](https://github.com/juspay/hyperswitch/pull/4093)) ([`a3dec0b`](https://github.com/juspay/hyperswitch/commit/a3dec0b6bc52f20246a65ed5255768fcf585147a))

**Full Changelog:** [`2024.03.19.0...2024.03.20.0`](https://github.com/juspay/hyperswitch/compare/2024.03.19.0...2024.03.20.0)

- - -

## 2024.03.19.0

### Features

- **events:** Add audit events scaffolding ([#3863](https://github.com/juspay/hyperswitch/pull/3863)) ([`6f67985`](https://github.com/juspay/hyperswitch/commit/6f679851dfaca8690fa3c5c2d1a2978bfe6d42b6))

### Bug Fixes

- **payments:** Populate merchant connector id and profile id in list ([#4104](https://github.com/juspay/hyperswitch/pull/4104)) ([`1dac028`](https://github.com/juspay/hyperswitch/commit/1dac0286bb402fec5e4ac3270112fcb7c3f35cd6))

### Refactors

- **connector:**
  - [Coinbase][Cryptopay] Mask PII data ([#3936](https://github.com/juspay/hyperswitch/pull/3936)) ([`8eb31f9`](https://github.com/juspay/hyperswitch/commit/8eb31f94f4aae5daa41799dc78eb3a116653aa0d))
  - [Prophetpay][Rapyd][Shift4][Square] Mask PII data ([#3930](https://github.com/juspay/hyperswitch/pull/3930)) ([`b1face6`](https://github.com/juspay/hyperswitch/commit/b1face64424cd68f0a21192b981698abaec05ec3))
  - [Worldline][Worldpay][Zen] Mask PII data ([#3935](https://github.com/juspay/hyperswitch/pull/3935)) ([`612d2b1`](https://github.com/juspay/hyperswitch/commit/612d2b17e233985b70e72fa5c12164f57dede0ee))
  - [Adyen] change error message from not supported to not implemented ([#2845](https://github.com/juspay/hyperswitch/pull/2845)) ([`c3ef599`](https://github.com/juspay/hyperswitch/commit/c3ef599ad736dee34286150ec6bf5143a526ae6c))
  - [Aci] remove default case handling ([#2513](https://github.com/juspay/hyperswitch/pull/2513)) ([`7398371`](https://github.com/juspay/hyperswitch/commit/73983710a068ff04da54152d1a1a84639f961622))
  - [Klarna] Mask PII data ([#3854](https://github.com/juspay/hyperswitch/pull/3854)) ([`384f32b`](https://github.com/juspay/hyperswitch/commit/384f32ba2d8fcd57eba6c06cf49c0fa08fb21c81))
- **payment_link:** Make performance optimisation for payment_link ([#4092](https://github.com/juspay/hyperswitch/pull/4092)) ([`fcfd567`](https://github.com/juspay/hyperswitch/commit/fcfd567bfe55747dcb05c88def96373a707f8c78))
- **router:** Add FE error logs to loki ([#4077](https://github.com/juspay/hyperswitch/pull/4077)) ([`6149d4f`](https://github.com/juspay/hyperswitch/commit/6149d4fb607304ccdf184c8c5f28269a45ef3974))
- **stripe:** Change NotSupported to NotImplemented error for Stripe ([#3690](https://github.com/juspay/hyperswitch/pull/3690)) ([`6ff8f75`](https://github.com/juspay/hyperswitch/commit/6ff8f75d6cd58e36882877e5194da6a160a88f9e))

### Miscellaneous Tasks

- **config:** Add wasm changes for checkout connector to support external authentication flow ([#4096](https://github.com/juspay/hyperswitch/pull/4096)) ([`ce5cbfb`](https://github.com/juspay/hyperswitch/commit/ce5cbfbda6d1d75790eba6ae68d401e8817aba55))

**Full Changelog:** [`2024.03.18.0...2024.03.19.0`](https://github.com/juspay/hyperswitch/compare/2024.03.18.0...2024.03.19.0)

- - -

## 2024.03.18.0

### Features

- **connector:**
  - [Paypal] Unify error code and error message in Paypal ([#2354](https://github.com/juspay/hyperswitch/pull/2354)) ([`fc81f90`](https://github.com/juspay/hyperswitch/commit/fc81f90f6168dc6e08cbfacdda0f59e99def07da))
  - [BOA/CYB] Add support for payment status ACCEPTED and CANCELLED ([#4107](https://github.com/juspay/hyperswitch/pull/4107)) ([`c52dbd6`](https://github.com/juspay/hyperswitch/commit/c52dbd6fc21c9c16ebc8f2abed1d2979bc5a606b))
- **pm_auth:** Support different pm types in PM auth ([#3114](https://github.com/juspay/hyperswitch/pull/3114)) ([`290c456`](https://github.com/juspay/hyperswitch/commit/290c456a235072ac5a5b900c11ca8a4fa1a3b9e4))

### Bug Fixes

- **api_response:** Ghost payment_method_billing being populated in the response ([#4085](https://github.com/juspay/hyperswitch/pull/4085)) ([`3d4baa2`](https://github.com/juspay/hyperswitch/commit/3d4baa230cdfa0e4e0f0ab36f3ca8c96e9b705ad))

### Refactors

- **connector:**
  - [Wise] Response Fields made Optional ([#4007](https://github.com/juspay/hyperswitch/pull/4007)) ([`8c103c0`](https://github.com/juspay/hyperswitch/commit/8c103c0f8e838a899a0c1207c88fa7617b37f138))
  - [Stripe] add stripe-api-version in API-headers ([#4109](https://github.com/juspay/hyperswitch/pull/4109)) ([`ed6fdad`](https://github.com/juspay/hyperswitch/commit/ed6fdad73757425d9419575ce6aba80fae8daf4d))
  - [Payu][Placetopay][PowerTranz] Mask PII data ([#3928](https://github.com/juspay/hyperswitch/pull/3928)) ([`4cbd00b`](https://github.com/juspay/hyperswitch/commit/4cbd00ba410c093878a6e7bc4b3cb76941a57351))
  - [NMI] Mask PII data ([#3876](https://github.com/juspay/hyperswitch/pull/3876)) ([`bbf20c5`](https://github.com/juspay/hyperswitch/commit/bbf20c5b155c003bcef91880653f87c9dedc928f))
- **core:** Remove pament_method_status from payment_data ([#4061](https://github.com/juspay/hyperswitch/pull/4061)) ([`0f6c97c`](https://github.com/juspay/hyperswitch/commit/0f6c97c47ddd0980ace13840faadc4b6eefaa48e))

**Full Changelog:** [`2024.03.15.0...2024.03.18.0`](https://github.com/juspay/hyperswitch/compare/2024.03.15.0...2024.03.18.0)

- - -

## 2024.03.15.0

### Features

- **connector:** [cybersource] add card holder name in dynamic fields ([#4082](https://github.com/juspay/hyperswitch/pull/4082)) ([`5185d65`](https://github.com/juspay/hyperswitch/commit/5185d65ef5d48c21b203250cbc310b94212511c9))
- **webhooks:** Store request and response payloads in `events` table ([#4029](https://github.com/juspay/hyperswitch/pull/4029)) ([`fd67a6c`](https://github.com/juspay/hyperswitch/commit/fd67a6c2255b866ca20823e25c4a2a6fa3304fa7))

### Bug Fixes

- **connector:** [Iatapay] remove unused fields from auth response ([#4091](https://github.com/juspay/hyperswitch/pull/4091)) ([`e5b7bc6`](https://github.com/juspay/hyperswitch/commit/e5b7bc62fbfee7c1e6631b4d38fef5859dd736c1))

### Refactors

- **payment_methods:** Enable country currency filter for cards ([#4056](https://github.com/juspay/hyperswitch/pull/4056)) ([`9ae10dc`](https://github.com/juspay/hyperswitch/commit/9ae10dc4d050f3aa705c72b27e676cdcb0e379c4))
- **router:** Add IO level application logs ([#4042](https://github.com/juspay/hyperswitch/pull/4042)) ([`ad17cc7`](https://github.com/juspay/hyperswitch/commit/ad17cc738372e7397d73d6f55cae56beafa4e849))

### Miscellaneous Tasks

- **config:** [AUTHORIZEDOTNET] Add apple pay manual flow to dashboard ([#4080](https://github.com/juspay/hyperswitch/pull/4080)) ([`59a2bc4`](https://github.com/juspay/hyperswitch/commit/59a2bc434dca0d9faeceaa42b965f4ba4e93b1a9))

**Full Changelog:** [`2024.03.13.3...2024.03.15.0`](https://github.com/juspay/hyperswitch/compare/2024.03.13.3...2024.03.15.0)

- - -

## 2024.03.13.3

### Bug Fixes

- **mandates:** Give higher precedence to connector mandate id over network txn id in mandates ([#4073](https://github.com/juspay/hyperswitch/pull/4073)) ([`d28e415`](https://github.com/juspay/hyperswitch/commit/d28e415dc289a58468e24c15bbaf7fc15b4a91ee))
- Get valid test cards list based on wasm feature config ([#4066](https://github.com/juspay/hyperswitch/pull/4066)) ([`fad23ad`](https://github.com/juspay/hyperswitch/commit/fad23ad032971497b07035c530397539413b7653))

**Full Changelog:** [`2024.03.13.2...2024.03.13.3`](https://github.com/juspay/hyperswitch/compare/2024.03.13.2...2024.03.13.3)

- - -

## 2024.03.13.2

### Bug Fixes

- **connector:** [cybersource] update mandate condition ([#4048](https://github.com/juspay/hyperswitch/pull/4048)) ([`d82960c`](https://github.com/juspay/hyperswitch/commit/d82960c1cca5ae43d1a51f8fff6f7b6b9e016c2b))
- **payment_methods:** Set requires-cvv to false for cards in customer payment methods list if making an off-session payment ([#4075](https://github.com/juspay/hyperswitch/pull/4075)) ([`db25dac`](https://github.com/juspay/hyperswitch/commit/db25dac5c0023ff49a839e7914a639403c733e8a))

**Full Changelog:** [`2024.03.13.1...2024.03.13.2`](https://github.com/juspay/hyperswitch/compare/2024.03.13.1...2024.03.13.2)

- - -

## 2024.03.13.1

### Bug Fixes

- **router:** Fix token fetch logic in complete authorize flow for three ds payments ([#4052](https://github.com/juspay/hyperswitch/pull/4052)) ([`ada0002`](https://github.com/juspay/hyperswitch/commit/ada000245522662c36032034a76c3e8b57152582))

**Full Changelog:** [`2024.03.13.0...2024.03.13.1`](https://github.com/juspay/hyperswitch/compare/2024.03.13.0...2024.03.13.1)

- - -

## 2024.03.13.0

### Features

- **connector:** [AUTHORIZEDOTNET] Audit Connector ([#4035](https://github.com/juspay/hyperswitch/pull/4035)) ([`7840bdb`](https://github.com/juspay/hyperswitch/commit/7840bdb95f90065f3f6d671b07c3044e77740ed2))
- **core:** Confirm flow and authorization api changes for external authentication ([#4015](https://github.com/juspay/hyperswitch/pull/4015)) ([`ce3625c`](https://github.com/juspay/hyperswitch/commit/ce3625cb0cdccc750a073c012f0e541b014c3190))
- **global-search:** Dashboard globalsearch apis ([#3831](https://github.com/juspay/hyperswitch/pull/3831)) ([`ac8ddd4`](https://github.com/juspay/hyperswitch/commit/ac8ddd40208f3da5f65ca97bf5033cea5ca3ebe3))

### Bug Fixes

- **connector:** [Adyen] update config and add required fields ([#4046](https://github.com/juspay/hyperswitch/pull/4046)) ([`16d73cb`](https://github.com/juspay/hyperswitch/commit/16d73cb5f9f469d791f8880f3a2fd79135c821cd))
- **core:** [REFUNDS] Fix Not Supported Connector Error ([#4045](https://github.com/juspay/hyperswitch/pull/4045)) ([`7513423`](https://github.com/juspay/hyperswitch/commit/7513423631ddf0fe86ef656ec6cad76d82c807bc))

### Refactors

- **address:** Pass payment method billing to the connector module ([#3828](https://github.com/juspay/hyperswitch/pull/3828)) ([`195c700`](https://github.com/juspay/hyperswitch/commit/195c700e6c88e457cecc0722a7e5990db1379f22))


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

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

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

impl ConnectorSpecifications for for Moneris


impl AuthenticationId {
    /// Generate Authentication Id from prefix
    pub fn generate_authentication_id(prefix: &'static str) -> Self {
        Self(crate::generate_ref_id_with_default_length(prefix))
    }

    /// Get external authentication request poll id
    pub fn get_external_authentication_request_poll_id(&self) -> String {
        format!("external_authentication_{}", self.get_string_repr())
    }
}


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

pub mod transformers;

use std::sync::LazyLock;

use base64::Engine;
use common_enums::enums;
use common_utils::{
    errors::CustomResult,
    ext_traits::BytesExt,
    request::{Method, Request, RequestBuilder, RequestContent},
    types::{AmountConvertor, FloatMajorUnit, FloatMajorUnitForConnector},
};
use error_stack::{report, 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, PaymentsCancelRouterData, 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, PeekInterface};
use transformers as authipay;

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

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

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

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

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

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

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

impl<Flow, Request, Response> ConnectorCommonExt<Flow, Request, Response> for Authipay
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 timestamp = time::OffsetDateTime::now_utc().unix_timestamp_nanos() / 1_000_000;
        let auth: authipay::AuthipayAuthType =
            authipay::AuthipayAuthType::try_from(&req.connector_auth_type)?;
        let mut auth_header = self.get_auth_header(&req.connector_auth_type)?;

        let authipay_req = self.get_request_body(req, connectors)?;

        let client_request_id = uuid::Uuid::new_v4().to_string();
        let hmac = self
            .generate_authorization_signature(
                auth,
                &client_request_id,
                authipay_req.get_inner_value().peek(),
                timestamp,
            )
            .change_context(errors::ConnectorError::RequestEncodingFailed)?;
        let mut headers = vec![
            (
                headers::CONTENT_TYPE.to_string(),
                types::PaymentsAuthorizeType::get_content_type(self)
                    .to_string()
                    .into(),
            ),
            ("Client-Request-Id".to_string(), client_request_id.into()),
            ("Auth-Token-Type".to_string(), "HMAC".to_string().into()),
            (headers::TIMESTAMP.to_string(), timestamp.to_string().into()),
            ("Message-Signature".to_string(), hmac.into_masked()),
        ];
        headers.append(&mut auth_header);
        Ok(headers)
    }
}

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

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

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

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

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

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

        event_builder.map(|i| i.set_error_response_body(&response));

        let mut error_response = ErrorResponse::from(&response);

        // Set status code from the response, or 400 if error code is a "404"
        if let Some(error_code) = &response.error.code {
            if error_code == "404" {
                error_response.status_code = 404;
            } else {
                error_response.status_code = res.status_code;
            }
        } else {
            error_response.status_code = res.status_code;
        }

        Ok(error_response)
    }
}

impl ConnectorValidation for Authipay {
    fn validate_connector_against_payment_request(
        &self,
        capture_method: Option<enums::CaptureMethod>,
        _payment_method: enums::PaymentMethod,
        _pmt: Option<enums::PaymentMethodType>,
    ) -> CustomResult<(), errors::ConnectorError> {
        let capture_method = capture_method.unwrap_or_default();
        match capture_method {
            enums::CaptureMethod::Automatic
            | enums::CaptureMethod::Manual
            | enums::CaptureMethod::SequentialAutomatic => Ok(()),
            enums::CaptureMethod::Scheduled | enums::CaptureMethod::ManualMultiple => Err(
                utils::construct_not_implemented_error_report(capture_method, self.id()),
            ),
        }
    }
}

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

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

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

impl ConnectorIntegration<Authorize, PaymentsAuthorizeData, PaymentsResponseData> for Authipay {
    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!("{}payments", 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 = authipay::AuthipayRouterData::from((amount, req));
        let connector_req = authipay::AuthipayPaymentsRequest::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: authipay::AuthipayPaymentsResponse = res
            .response
            .parse_struct("Authipay PaymentsAuthorizeResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
        event_builder.map(|i| i.set_response_body(&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 Authipay {
    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_transaction_id = req
            .request
            .connector_transaction_id
            .get_connector_transaction_id()
            .change_context(errors::ConnectorError::RequestEncodingFailed)?;
        Ok(format!(
            "{}payments/{}",
            self.base_url(connectors),
            connector_transaction_id
        ))
    }

    fn get_request_body(
        &self,
        _req: &PaymentsSyncRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        Ok(RequestContent::RawBytes(Vec::new()))
    }

    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: authipay::AuthipayPaymentsResponse = res
            .response
            .parse_struct("authipay PaymentsSyncResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
        event_builder.map(|i| i.set_response_body(&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 Authipay {
    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> {
        let connector_transaction_id = req.request.connector_transaction_id.clone();
        Ok(format!(
            "{}payments/{}",
            self.base_url(connectors),
            connector_transaction_id
        ))
    }

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

        let connector_router_data = authipay::AuthipayRouterData::from((amount, req));
        let connector_req = authipay::AuthipayCaptureRequest::try_from(&connector_router_data)?;
        Ok(RequestContent::Json(Box::new(connector_req)))
    }

    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: authipay::AuthipayPaymentsResponse = res
            .response
            .parse_struct("Authipay PaymentsCaptureResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
        event_builder.map(|i| i.set_response_body(&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 Authipay {
    fn get_headers(
        &self,
        req: &PaymentsCancelRouterData,
        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: &PaymentsCancelRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        // For void operations, Authipay requires using the /orders/{orderId} endpoint
        // The orderId should be stored in connector_meta from the authorization response
        let order_id = req
            .request
            .connector_meta
            .as_ref()
            .and_then(|meta| meta.get("order_id"))
            .and_then(|v| v.as_str())
            .ok_or(errors::ConnectorError::RequestEncodingFailed)?;

        Ok(format!("{}orders/{}", self.base_url(connectors), order_id))
    }

    fn get_request_body(
        &self,
        req: &PaymentsCancelRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        // For void, we don't need amount conversion since it's always full amount
        let connector_router_data =
            authipay::AuthipayRouterData::from((FloatMajorUnit::zero(), req));
        let connector_req = authipay::AuthipayVoidRequest::try_from(&connector_router_data)?;
        Ok(RequestContent::Json(Box::new(connector_req)))
    }

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

    fn handle_response(
        &self,
        data: &PaymentsCancelRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<PaymentsCancelRouterData, errors::ConnectorError> {
        let response: authipay::AuthipayPaymentsResponse = res
            .response
            .parse_struct("Authipay PaymentsVoidResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
        event_builder.map(|i| i.set_response_body(&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<Execute, RefundsData, RefundsResponseData> for Authipay {
    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> {
        let connector_payment_id = req.request.connector_transaction_id.clone();
        Ok(format!(
            "{}payments/{}",
            self.base_url(connectors),
            connector_payment_id
        ))
    }

    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 = authipay::AuthipayRouterData::from((refund_amount, req));
        let connector_req = authipay::AuthipayRefundRequest::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: authipay::RefundResponse = res
            .response
            .parse_struct("authipay RefundResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
        event_builder.map(|i| i.set_response_body(&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 Authipay {
    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 refund_id = req
            .request
            .connector_refund_id
            .clone()
            .ok_or(errors::ConnectorError::RequestEncodingFailed)?;
        Ok(format!(
            "{}payments/{}",
            self.base_url(connectors),
            refund_id
        ))
    }

    fn get_request_body(
        &self,
        _req: &RefundSyncRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        Ok(RequestContent::RawBytes(Vec::new()))
    }

    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)?)
                .build(),
        ))
    }

    fn handle_response(
        &self,
        data: &RefundSyncRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<RefundSyncRouterData, errors::ConnectorError> {
        let response: authipay::RefundResponse = res
            .response
            .parse_struct("authipay RefundSyncResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
        event_builder.map(|i| i.set_response_body(&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 Authipay {
    fn get_webhook_object_reference_id(
        &self,
        _request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<api_models::webhooks::ObjectReferenceId, errors::ConnectorError> {
        Err(report!(errors::ConnectorError::WebhooksNotImplemented))
    }

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

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

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

        let supported_card_network = vec![
            common_enums::CardNetwork::Visa,
            common_enums::CardNetwork::Mastercard,
        ];

        let mut authipay_supported_payment_methods = SupportedPaymentMethods::new();

        authipay_supported_payment_methods.add(
            enums::PaymentMethod::Card,
            enums::PaymentMethodType::Credit,
            PaymentMethodDetails {
                mandates: enums::FeatureStatus::NotSupported,
                refunds: enums::FeatureStatus::Supported,
                supported_capture_methods: supported_capture_methods.clone(),
                specific_features: Some(
                    api_models::feature_matrix::PaymentMethodSpecificFeatures::Card({
                        api_models::feature_matrix::CardSpecificFeatures {
                            three_ds: common_enums::FeatureStatus::NotSupported,
                            no_three_ds: common_enums::FeatureStatus::Supported,
                            supported_card_networks: supported_card_network.clone(),
                        }
                    }),
                ),
            },
        );

        authipay_supported_payment_methods.add(
            enums::PaymentMethod::Card,
            enums::PaymentMethodType::Debit,
            PaymentMethodDetails {
                mandates: enums::FeatureStatus::NotSupported,
                refunds: enums::FeatureStatus::Supported,
                supported_capture_methods: supported_capture_methods.clone(),
                specific_features: Some(
                    api_models::feature_matrix::PaymentMethodSpecificFeatures::Card({
                        api_models::feature_matrix::CardSpecificFeatures {
                            three_ds: common_enums::FeatureStatus::NotSupported,
                            no_three_ds: common_enums::FeatureStatus::Supported,
                            supported_card_networks: supported_card_network.clone(),
                        }
                    }),
                ),
            },
        );

        authipay_supported_payment_methods
    });

static AUTHIPAY_CONNECTOR_INFO: ConnectorInfo = ConnectorInfo {
    display_name: "Authipay",
    description: "Authipay is a Fiserv-powered payment gateway for the EMEA region supporting Visa and Mastercard transactions. Features include flexible capture methods (automatic, manual, sequential), partial captures/refunds, payment tokenization, and secure HMAC SHA256 authentication.",
    connector_type: enums::HyperswitchConnectorCategory::PaymentGateway,
    integration_status: enums::ConnectorIntegrationStatus::Sandbox,
};

static AUTHIPAY_SUPPORTED_WEBHOOK_FLOWS: [enums::EventClass; 0] = [];

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

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

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


pub struct AuthorizedResponse {
    pub payment_instrument: PaymentsResPaymentInstrument,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub issuer: Option<Issuer>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub scheme: Option<PaymentsResponseScheme>,
    #[serde(rename = "_links", skip_serializing_if = "Option::is_none")]
    pub links: Option<SelfLink>,
    #[serde(rename = "_actions")]
    pub actions: Option<ActionLinks>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub description: Option<String>,
    pub risk_factors: Option<Vec<RiskFactorsInner>>,
    pub fraud: Option<Fraud>,
    /// Mandate's token
    pub token: Option<MandateToken>,
    /// Network transaction ID
    pub scheme_reference: Option<Secret<String>>,
}


pub struct DeletePaymentMethodFromVault {
    client_mutation_id: Option<String>,
}


impl AwsSes {
    /// Constructs a new AwsSes client
    pub async fn create(
        conf: &EmailSettings,
        ses_config: &SESConfig,
        proxy_url: Option<impl AsRef<str>>,
    ) -> Self {
        // Build the client initially which will help us know if the email configuration is correct
        Self::create_client(conf, ses_config, proxy_url)
            .await
            .map_err(|error| logger::error!(?error, "Failed to initialize SES Client"))
            .ok();

        Self {
            sender: conf.sender_email.clone(),
            ses_config: ses_config.clone(),
            settings: conf.clone(),
        }
    }

    /// A helper function to create ses client
    pub async fn create_client(
        conf: &EmailSettings,
        ses_config: &SESConfig,
        proxy_url: Option<impl AsRef<str>>,
    ) -> CustomResult<Client, AwsSesError> {
        let sts_config = Self::get_shared_config(conf.aws_region.to_owned(), proxy_url.as_ref())?
            .load()
            .await;

        let role = aws_sdk_sts::Client::new(&sts_config)
            .assume_role()
            .role_arn(&ses_config.email_role_arn)
            .role_session_name(&ses_config.sts_role_session_name)
            .send()
            .await
            .change_context(AwsSesError::AssumeRoleFailure {
                region: conf.aws_region.to_owned(),
                role_arn: ses_config.email_role_arn.to_owned(),
                session_name: ses_config.sts_role_session_name.to_owned(),
            })?;

        let creds = role.credentials().ok_or(
            report!(AwsSesError::TemporaryCredentialsMissing(format!(
                "{role:?}"
            )))
            .attach_printable("Credentials object not available"),
        )?;

        let credentials = Credentials::new(
            creds.access_key_id(),
            creds.secret_access_key(),
            Some(creds.session_token().to_owned()),
            u64::try_from(creds.expiration().as_nanos())
                .ok()
                .map(Duration::from_nanos)
                .and_then(|val| SystemTime::UNIX_EPOCH.checked_add(val)),
            "custom_provider",
        );

        logger::debug!(
            "Obtained SES temporary credentials with expiry {:?}",
            credentials.expiry()
        );

        let ses_config = Self::get_shared_config(conf.aws_region.to_owned(), proxy_url)?
            .credentials_provider(credentials)
            .load()
            .await;

        Ok(Client::new(&ses_config))
    }

    fn get_shared_config(
        region: String,
        proxy_url: Option<impl AsRef<str>>,
    ) -> CustomResult<aws_config::ConfigLoader, AwsSesError> {
        let region_provider = Region::new(region);
        let mut config = aws_config::from_env().region(region_provider);

        if let Some(proxy_url) = proxy_url {
            let proxy_connector = Self::get_proxy_connector(proxy_url)?;
            let http_client = HyperClientBuilder::new().build(proxy_connector);
            config = config.http_client(http_client);
        };
        Ok(config)
    }

    fn get_proxy_connector(
        proxy_url: impl AsRef<str>,
    ) -> CustomResult<hyper_proxy::ProxyConnector<hyper::client::HttpConnector>, AwsSesError> {
        let proxy_uri = proxy_url
            .as_ref()
            .parse::<Uri>()
            .attach_printable("Unable to parse the proxy url {proxy_url}")
            .change_context(AwsSesError::BuildingProxyConnectorFailed)?;

        let proxy = hyper_proxy::Proxy::new(hyper_proxy::Intercept::All, proxy_uri);

        hyper_proxy::ProxyConnector::from_proxy(hyper::client::HttpConnector::new(), proxy)
            .change_context(AwsSesError::BuildingProxyConnectorFailed)
    }
}


pub struct OutgoingWebhookLogsResult {
    pub merchant_id: common_utils::id_type::MerchantId,
    pub event_id: String,
    pub event_type: String,
    pub outgoing_webhook_event_type: String,
    pub payment_id: common_utils::id_type::PaymentId,
    pub refund_id: Option<String>,
    pub attempt_id: Option<String>,
    pub dispute_id: Option<String>,
    pub payment_method_id: Option<String>,
    pub mandate_id: Option<String>,
    pub content: Option<String>,
    pub is_error: bool,
    pub error: Option<String>,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub created_at: PrimitiveDateTime,
}


pub struct ChargebeeCardDetails {
    funding_type: ChargebeeFundingType,
    brand: common_enums::CardNetwork,
    iin: String,
}


pub async fn validate_data_for_blocklist<F>(
    state: &SessionState,
    merchant_context: &domain::MerchantContext,
    payment_data: &mut PaymentData<F>,
) -> CustomResult<bool, errors::ApiErrorResponse>
where
    F: Send + Clone,
{
    let db = &state.store;
    let merchant_id = merchant_context.get_merchant_account().get_id();
    let merchant_fingerprint_secret = get_merchant_fingerprint_secret(state, merchant_id).await?;

    // Hashed Fingerprint to check whether or not this payment should be blocked.
    let card_number_fingerprint = if let Some(domain::PaymentMethodData::Card(card)) =
        payment_data.payment_method_data.as_ref()
    {
        generate_fingerprint(
            state,
            StrongSecret::new(card.card_number.get_card_no()),
            StrongSecret::new(merchant_fingerprint_secret.clone()),
            api_models::enums::LockerChoice::HyperswitchCardVault,
        )
        .await
        .attach_printable("error in pm fingerprint creation")
        .map_or_else(
            |error| {
                logger::error!(?error);
                None
            },
            Some,
        )
        .map(|payload| payload.card_fingerprint)
    } else {
        None
    };

    // Hashed Cardbin to check whether or not this payment should be blocked.
    let card_bin_fingerprint = payment_data
        .payment_method_data
        .as_ref()
        .and_then(|pm_data| match pm_data {
            domain::PaymentMethodData::Card(card) => Some(card.card_number.get_card_isin()),
            _ => None,
        });

    // Hashed Extended Cardbin to check whether or not this payment should be blocked.
    let extended_card_bin_fingerprint =
        payment_data
            .payment_method_data
            .as_ref()
            .and_then(|pm_data| match pm_data {
                domain::PaymentMethodData::Card(card) => {
                    Some(card.card_number.get_extended_card_bin())
                }
                _ => None,
            });

    //validating the payment method.
    let mut blocklist_futures = Vec::new();
    if let Some(card_number_fingerprint) = card_number_fingerprint.as_ref() {
        blocklist_futures.push(db.find_blocklist_entry_by_merchant_id_fingerprint_id(
            merchant_id,
            card_number_fingerprint,
        ));
    }

    if let Some(card_bin_fingerprint) = card_bin_fingerprint.as_ref() {
        blocklist_futures.push(
            db.find_blocklist_entry_by_merchant_id_fingerprint_id(
                merchant_id,
                card_bin_fingerprint,
            ),
        );
    }

    if let Some(extended_card_bin_fingerprint) = extended_card_bin_fingerprint.as_ref() {
        blocklist_futures.push(db.find_blocklist_entry_by_merchant_id_fingerprint_id(
            merchant_id,
            extended_card_bin_fingerprint,
        ));
    }

    let blocklist_lookups = futures::future::join_all(blocklist_futures).await;

    let mut should_payment_be_blocked = false;
    for lookup in blocklist_lookups {
        match lookup {
            Ok(_) => {
                should_payment_be_blocked = true;
            }
            Err(e) => {
                logger::error!(blocklist_db_error=?e, "failed db operations for blocklist");
            }
        }
    }
    if should_payment_be_blocked {
        // Update db for attempt and intent status.
        db.update_payment_intent(
            &state.into(),
            payment_data.payment_intent.clone(),
            storage::PaymentIntentUpdate::RejectUpdate {
                status: common_enums::IntentStatus::Failed,
                merchant_decision: Some(MerchantDecision::Rejected.to_string()),
                updated_by: merchant_context
                    .get_merchant_account()
                    .storage_scheme
                    .to_string(),
            },
            merchant_context.get_merchant_key_store(),
            merchant_context.get_merchant_account().storage_scheme,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)
        .attach_printable(
            "Failed to update status in Payment Intent to failed due to it being blocklisted",
        )?;

        // If payment is blocked not showing connector details
        let attempt_update = storage::PaymentAttemptUpdate::BlocklistUpdate {
            status: common_enums::AttemptStatus::Failure,
            error_code: Some(Some("HE-03".to_string())),
            error_message: Some(Some("This payment method is blocked".to_string())),
            updated_by: merchant_context
                .get_merchant_account()
                .storage_scheme
                .to_string(),
        };
        db.update_payment_attempt_with_attempt_id(
            payment_data.payment_attempt.clone(),
            attempt_update,
            merchant_context.get_merchant_account().storage_scheme,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)
        .attach_printable(
            "Failed to update status in Payment Attempt to failed, due to it being blocklisted",
        )?;

        Err(errors::ApiErrorResponse::PaymentBlockedError {
            code: 200,
            message: "This payment method is blocked".to_string(),
            status: "Failed".to_string(),
            reason: "Blocked".to_string(),
        }
        .into())
    } else {
        payment_data.payment_attempt.fingerprint_id = generate_payment_fingerprint(
            state,
            payment_data.payment_attempt.merchant_id.clone(),
            payment_data.payment_method_data.clone(),
        )
        .await?;
        Ok(false)
    }
}


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

pub use diesel_models::blocklist_fingerprint::{BlocklistFingerprint, BlocklistFingerprintNew};


pub struct RelayUpdateInternal {
    pub connector_reference_id: Option<String>,
    pub status: Option<storage_enums::RelayStatus>,
    pub error_code: Option<String>,
    pub error_message: Option<String>,
    pub modified_at: PrimitiveDateTime,
}


pub struct AdditionalErrorDetails {
    pub legacy_code: Option<String>,
}


pub struct TransactionDetailsUiConfiguration {
    /// Position of the key-value pair in the UI
    #[schema(value_type = Option<i8>, example = 5)]
    pub position: Option<i8>,
    /// Whether the key should be bold
    #[schema(default = false, example = true)]
    pub is_key_bold: Option<bool>,
    /// Whether the value should be bold
    #[schema(default = false, example = true)]
    pub is_value_bold: Option<bool>,
}


impl ConnectorPreAuthentication for Gpayments {}

impl ConnectorPreAuthentication for for Gpayments


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


File: crates/router/src/core/authentication/utils.rs
 Public functions: 5

use common_utils::ext_traits::AsyncExt;
use error_stack::ResultExt;
use hyperswitch_domain_models::router_data_v2::ExternalAuthenticationFlowData;
use masking::ExposeInterface;

use crate::{
    consts,
    core::{
        errors::{self, ConnectorErrorExt, StorageErrorExt},
        payments,
    },
    errors::RouterResult,
    routes::SessionState,
    services::{self, execute_connector_processing_step},
    types::{
        api, authentication::AuthenticationResponseData, domain, storage,
        transformers::ForeignFrom, RouterData,
    },
    utils::OptionExt,
};

#[cfg(feature = "v1")]
pub fn get_connector_data_if_separate_authn_supported(
    connector_call_type: &api::ConnectorCallType,
) -> Option<api::ConnectorData> {
    match connector_call_type {
        api::ConnectorCallType::PreDetermined(connector_routing_data) => {
            if connector_routing_data
                .connector_data
                .connector_name
                .is_separate_authentication_supported()
            {
                Some(connector_routing_data.connector_data.clone())
            } else {
                None
            }
        }
        api::ConnectorCallType::Retryable(connector_routing_data) => connector_routing_data
            .first()
            .and_then(|connector_routing_data| {
                if connector_routing_data
                    .connector_data
                    .connector_name
                    .is_separate_authentication_supported()
                {
                    Some(connector_routing_data.connector_data.clone())
                } else {
                    None
                }
            }),
        api::ConnectorCallType::SessionMultiple(_) => None,
    }
}

pub async fn update_trackers<F: Clone, Req>(
    state: &SessionState,
    router_data: RouterData<F, Req, AuthenticationResponseData>,
    authentication: storage::Authentication,
    acquirer_details: Option<super::types::AcquirerDetails>,
    merchant_key_store: &hyperswitch_domain_models::merchant_key_store::MerchantKeyStore,
) -> RouterResult<storage::Authentication> {
    let authentication_update = match router_data.response {
        Ok(response) => match response {
            AuthenticationResponseData::PreAuthNResponse {
                threeds_server_transaction_id,
                maximum_supported_3ds_version,
                connector_authentication_id,
                three_ds_method_data,
                three_ds_method_url,
                message_version,
                connector_metadata,
                directory_server_id,
            } => storage::AuthenticationUpdate::PreAuthenticationUpdate {
                threeds_server_transaction_id,
                maximum_supported_3ds_version,
                connector_authentication_id,
                three_ds_method_data,
                three_ds_method_url,
                message_version,
                connector_metadata,
                authentication_status: common_enums::AuthenticationStatus::Pending,
                acquirer_bin: acquirer_details
                    .as_ref()
                    .map(|acquirer_details| acquirer_details.acquirer_bin.clone()),
                acquirer_merchant_id: acquirer_details
                    .as_ref()
                    .map(|acquirer_details| acquirer_details.acquirer_merchant_id.clone()),
                acquirer_country_code: acquirer_details
                    .and_then(|acquirer_details| acquirer_details.acquirer_country_code),
                directory_server_id,
                billing_address: None,
                shipping_address: None,
                browser_info: Box::new(None),
                email: None,
            },
            AuthenticationResponseData::AuthNResponse {
                authn_flow_type,
                authentication_value,
                trans_status,
                connector_metadata,
                ds_trans_id,
                eci,
                challenge_code,
                challenge_cancel,
                challenge_code_reason,
                message_extension,
            } => {
                authentication_value
                    .async_map(|auth_val| {
                        crate::core::payment_methods::vault::create_tokenize(
                            state,
                            auth_val.expose(),
                            None,
                            authentication
                                .authentication_id
                                .get_string_repr()
                                .to_string(),
                            merchant_key_store.key.get_inner(),
                        )
                    })
                    .await
                    .transpose()?;

                let authentication_status =
                    common_enums::AuthenticationStatus::foreign_from(trans_status.clone());

                storage::AuthenticationUpdate::AuthenticationUpdate {
                    trans_status,
                    acs_url: authn_flow_type.get_acs_url(),
                    challenge_request: authn_flow_type.get_challenge_request(),
                    acs_reference_number: authn_flow_type.get_acs_reference_number(),
                    acs_trans_id: authn_flow_type.get_acs_trans_id(),
                    acs_signed_content: authn_flow_type.get_acs_signed_content(),
                    authentication_type: authn_flow_type.get_decoupled_authentication_type(),
                    authentication_status,
                    connector_metadata,
                    ds_trans_id,
                    eci,
                    challenge_code,
                    challenge_cancel,
                    challenge_code_reason,
                    message_extension,
                }
            }
            AuthenticationResponseData::PostAuthNResponse {
                trans_status,
                authentication_value,
                eci,
                challenge_cancel,
                challenge_code_reason,
            } => {
                authentication_value
                    .async_map(|auth_val| {
                        crate::core::payment_methods::vault::create_tokenize(
                            state,
                            auth_val.expose(),
                            None,
                            authentication
                                .authentication_id
                                .get_string_repr()
                                .to_string(),
                            merchant_key_store.key.get_inner(),
                        )
                    })
                    .await
                    .transpose()?;
                storage::AuthenticationUpdate::PostAuthenticationUpdate {
                    authentication_status: common_enums::AuthenticationStatus::foreign_from(
                        trans_status.clone(),
                    ),
                    trans_status,
                    eci,
                    challenge_cancel,
                    challenge_code_reason,
                }
            }
            AuthenticationResponseData::PreAuthVersionCallResponse {
                maximum_supported_3ds_version,
            } => storage::AuthenticationUpdate::PreAuthenticationVersionCallUpdate {
                message_version: maximum_supported_3ds_version.clone(),
                maximum_supported_3ds_version,
            },
            AuthenticationResponseData::PreAuthThreeDsMethodCallResponse {
                threeds_server_transaction_id,
                three_ds_method_data,
                three_ds_method_url,
                connector_metadata,
            } => storage::AuthenticationUpdate::PreAuthenticationThreeDsMethodCall {
                threeds_server_transaction_id,
                three_ds_method_data,
                three_ds_method_url,
                connector_metadata,
                acquirer_bin: acquirer_details
                    .as_ref()
                    .map(|acquirer_details| acquirer_details.acquirer_bin.clone()),
                acquirer_merchant_id: acquirer_details
                    .map(|acquirer_details| acquirer_details.acquirer_merchant_id),
            },
        },
        Err(error) => storage::AuthenticationUpdate::ErrorUpdate {
            connector_authentication_id: error.connector_transaction_id,
            authentication_status: common_enums::AuthenticationStatus::Failed,
            error_message: error
                .reason
                .map(|reason| format!("message: {}, reason: {}", error.message, reason))
                .or(Some(error.message)),
            error_code: Some(error.code),
        },
    };
    state
        .store
        .update_authentication_by_merchant_id_authentication_id(
            authentication,
            authentication_update,
        )
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Error while updating authentication")
}

impl ForeignFrom<common_enums::AuthenticationStatus> for common_enums::AttemptStatus {
    fn foreign_from(from: common_enums::AuthenticationStatus) -> Self {
        match from {
            common_enums::AuthenticationStatus::Started
            | common_enums::AuthenticationStatus::Pending => Self::AuthenticationPending,
            common_enums::AuthenticationStatus::Success => Self::AuthenticationSuccessful,
            common_enums::AuthenticationStatus::Failed => Self::AuthenticationFailed,
        }
    }
}

#[allow(clippy::too_many_arguments)]
pub async fn create_new_authentication(
    state: &SessionState,
    merchant_id: common_utils::id_type::MerchantId,
    authentication_connector: String,
    token: String,
    profile_id: common_utils::id_type::ProfileId,
    payment_id: common_utils::id_type::PaymentId,
    merchant_connector_id: common_utils::id_type::MerchantConnectorAccountId,
    organization_id: common_utils::id_type::OrganizationId,
    force_3ds_challenge: Option<bool>,
    psd2_sca_exemption_type: Option<common_enums::ScaExemptionType>,
) -> RouterResult<storage::Authentication> {
    let authentication_id = common_utils::id_type::AuthenticationId::generate_authentication_id(
        consts::AUTHENTICATION_ID_PREFIX,
    );
    let authentication_client_secret = Some(common_utils::generate_id_with_default_len(&format!(
        "{}_secret",
        authentication_id.get_string_repr()
    )));
    let new_authorization = storage::AuthenticationNew {
        authentication_id: authentication_id.clone(),
        merchant_id,
        authentication_connector: Some(authentication_connector),
        connector_authentication_id: None,
        payment_method_id: format!("eph_{token}"),
        authentication_type: None,
        authentication_status: common_enums::AuthenticationStatus::Started,
        authentication_lifecycle_status: common_enums::AuthenticationLifecycleStatus::Unused,
        error_message: None,
        error_code: None,
        connector_metadata: None,
        maximum_supported_version: None,
        threeds_server_transaction_id: None,
        cavv: None,
        authentication_flow_type: None,
        message_version: None,
        eci: None,
        trans_status: None,
        acquirer_bin: None,
        acquirer_merchant_id: None,
        three_ds_method_data: None,
        three_ds_method_url: None,
        acs_url: None,
        challenge_request: None,
        acs_reference_number: None,
        acs_trans_id: None,
        acs_signed_content: None,
        profile_id,
        payment_id: Some(payment_id),
        merchant_connector_id: Some(merchant_connector_id),
        ds_trans_id: None,
        directory_server_id: None,
        acquirer_country_code: None,
        service_details: None,
        organization_id,
        authentication_client_secret,
        force_3ds_challenge,
        psd2_sca_exemption_type,
        return_url: None,
        amount: None,
        currency: None,
        billing_address: None,
        shipping_address: None,
        browser_info: None,
        email: None,
        profile_acquirer_id: None,
        challenge_code: None,
        challenge_cancel: None,
        challenge_code_reason: None,
        message_extension: None,
    };
    state
        .store
        .insert_authentication(new_authorization)
        .await
        .to_duplicate_response(errors::ApiErrorResponse::GenericDuplicateError {
            message: format!(
                "Authentication with authentication_id {} already exists",
                authentication_id.get_string_repr()
            ),
        })
}

pub async fn do_auth_connector_call<F, Req, Res>(
    state: &SessionState,
    authentication_connector_name: String,
    router_data: RouterData<F, Req, Res>,
) -> RouterResult<RouterData<F, Req, Res>>
where
    Req: std::fmt::Debug + Clone + 'static,
    Res: std::fmt::Debug + Clone + 'static,
    F: std::fmt::Debug + Clone + 'static,
    dyn api::Connector + Sync: services::api::ConnectorIntegration<F, Req, Res>,
    dyn api::ConnectorV2 + Sync:
        services::api::ConnectorIntegrationV2<F, ExternalAuthenticationFlowData, Req, Res>,
{
    let connector_data =
        api::AuthenticationConnectorData::get_connector_by_name(&authentication_connector_name)?;
    let connector_integration: services::BoxedExternalAuthenticationConnectorIntegrationInterface<
        F,
        Req,
        Res,
    > = connector_data.connector.get_connector_integration();
    let router_data = execute_connector_processing_step(
        state,
        connector_integration,
        &router_data,
        payments::CallConnectorAction::Trigger,
        None,
        None,
    )
    .await
    .to_payment_failed_response()?;
    Ok(router_data)
}

pub async fn get_authentication_connector_data(
    state: &SessionState,
    key_store: &domain::MerchantKeyStore,
    business_profile: &domain::Profile,
    authentication_connector: Option<String>,
) -> RouterResult<(
    common_enums::AuthenticationConnectors,
    payments::helpers::MerchantConnectorAccountType,
)> {
    let authentication_connector = if let Some(authentication_connector) = authentication_connector
    {
        api_models::enums::convert_authentication_connector(&authentication_connector).ok_or(
            errors::ApiErrorResponse::UnprocessableEntity {
                message: format!(
                "Invalid authentication_connector found in request : {authentication_connector}",
            ),
            },
        )?
    } else {
        let authentication_details = business_profile
            .authentication_connector_details
            .clone()
            .get_required_value("authentication_details")
            .change_context(errors::ApiErrorResponse::UnprocessableEntity {
                message: "authentication_connector_details is not available in business profile"
                    .into(),
            })
            .attach_printable("authentication_connector_details not configured by the merchant")?;

        authentication_details
            .authentication_connectors
            .first()
            .ok_or(errors::ApiErrorResponse::UnprocessableEntity {
                message: format!(
                    "No authentication_connector found for profile_id {:?}",
                    business_profile.get_id()
                ),
            })
            .attach_printable(
                "No authentication_connector found from merchant_account.authentication_details",
            )?
            .to_owned()
    };

    let profile_id = business_profile.get_id();
    let authentication_connector_mca = payments::helpers::get_merchant_connector_account(
        state,
        &business_profile.merchant_id,
        None,
        key_store,
        profile_id,
        authentication_connector.to_string().as_str(),
        None,
    )
    .await?;
    Ok((authentication_connector, authentication_connector_mca))
}


File: crates/storage_impl/src/address.rs

use diesel_models::address::Address;

use crate::redis::kv_store::KvStorePartition;

impl KvStorePartition for Address {}


pub struct CardsInfoUpdateForm {
    #[multipart(limit = "1MB")]
    pub file: Bytes,
}


impl Profile {
    pub fn server(state: AppState) -> Scope {
        let mut route = web::scope("/account/{account_id}/business_profile")
            .app_data(web::Data::new(state))
            .service(
                web::resource("")
                    .route(web::post().to(profiles::profile_create))
                    .route(web::get().to(profiles::profiles_list)),
            );

        #[cfg(feature = "dynamic_routing")]
        {
            route = route.service(
                web::scope("/{profile_id}/dynamic_routing")
                    .service(
                        web::scope("/success_based")
                            .service(
                                web::resource("/toggle")
                                    .route(web::post().to(routing::toggle_success_based_routing)),
                            )
                            .service(
                                web::resource("/create")
                                    .route(web::post().to(routing::create_success_based_routing)),
                            )
                            .service(web::resource("/config/{algorithm_id}").route(
                                web::patch().to(|state, req, path, payload| {
                                    routing::success_based_routing_update_configs(
                                        state, req, path, payload,
                                    )
                                }),
                            )),
                    )
                    .service(
                        web::resource("/set_volume_split")
                            .route(web::post().to(routing::set_dynamic_routing_volume_split)),
                    )
                    .service(
                        web::resource("/get_volume_split")
                            .route(web::get().to(routing::get_dynamic_routing_volume_split)),
                    )
                    .service(
                        web::scope("/elimination")
                            .service(
                                web::resource("/toggle")
                                    .route(web::post().to(routing::toggle_elimination_routing)),
                            )
                            .service(
                                web::resource("/create")
                                    .route(web::post().to(routing::create_elimination_routing)),
                            )
                            .service(web::resource("config/{algorithm_id}").route(
                                web::patch().to(|state, req, path, payload| {
                                    routing::elimination_routing_update_configs(
                                        state, req, path, payload,
                                    )
                                }),
                            )),
                    )
                    .service(
                        web::scope("/contracts")
                            .service(web::resource("/toggle").route(
                                web::post().to(routing::contract_based_routing_setup_config),
                            ))
                            .service(web::resource("/config/{algorithm_id}").route(
                                web::patch().to(|state, req, path, payload| {
                                    routing::contract_based_routing_update_configs(
                                        state, req, path, payload,
                                    )
                                }),
                            )),
                    ),
            );
        }

        route = route.service(
            web::scope("/{profile_id}")
                .service(
                    web::resource("")
                        .route(web::get().to(profiles::profile_retrieve))
                        .route(web::post().to(profiles::profile_update))
                        .route(web::delete().to(profiles::profile_delete)),
                )
                .service(
                    web::resource("/toggle_extended_card_info")
                        .route(web::post().to(profiles::toggle_extended_card_info)),
                )
                .service(
                    web::resource("/toggle_connector_agnostic_mit")
                        .route(web::post().to(profiles::toggle_connector_agnostic_mit)),
                ),
        );

        route
    }
}


pub async fn recovery_receive_incoming_webhook<W: types::OutgoingWebhookType>(
    state: web::Data<AppState>,
    req: HttpRequest,
    body: web::Bytes,
    path: web::Path<(
        common_utils::id_type::MerchantId,
        common_utils::id_type::ProfileId,
        common_utils::id_type::MerchantConnectorAccountId,
    )


pub async fn deep_health_check_func(
    conf: web::Data<Settings>,
    store: &Arc<Store>,
) -> Result<DrainerHealthCheckResponse, error_stack::Report<HealthCheckError>> {
    logger::info!("Deep health check was called");

    logger::debug!("Database health check begin");

    let db_status = store
        .health_check_db()
        .await
        .map(|_| true)
        .map_err(|error| {
            let message = error.to_string();
            error.change_context(HealthCheckError::DbError { message })
        })?;

    logger::debug!("Database health check end");

    logger::debug!("Redis health check begin");

    let redis_status = store
        .health_check_redis(&conf.into_inner())
        .await
        .map(|_| true)
        .map_err(|error| {
            let message = error.to_string();
            error.change_context(HealthCheckError::RedisError { message })
        })?;

    logger::debug!("Redis health check end");

    Ok(DrainerHealthCheckResponse {
        database: db_status,
        redis: redis_status,
    })
}


impl webhooks::IncomingWebhook for Inespay {
    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_signature(
        &self,
        request: &webhooks::IncomingWebhookRequestDetails<'_>,
        _connector_webhook_secrets: &api_models::webhooks::ConnectorWebhookSecrets,
    ) -> CustomResult<Vec<u8>, errors::ConnectorError> {
        let notif_item = serde_urlencoded::from_bytes::<inespay::InespayWebhookEvent>(request.body)
            .change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?;

        Ok(notif_item.signature_data_return.as_bytes().to_owned())
    }

    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 notif_item = serde_urlencoded::from_bytes::<inespay::InespayWebhookEvent>(request.body)
            .change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?;

        Ok(notif_item.data_return.into_bytes())
    }

    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<Secret<serde_json::Value>>,
        connector_label: &str,
    ) -> CustomResult<bool, errors::ConnectorError> {
        let connector_webhook_secrets = self
            .get_webhook_source_verification_merchant_secret(
                merchant_id,
                connector_label,
                connector_webhook_details,
            )
            .await?;
        let signature =
            self.get_webhook_source_verification_signature(request, &connector_webhook_secrets)?;

        let message = self.get_webhook_source_verification_message(
            request,
            merchant_id,
            &connector_webhook_secrets,
        )?;
        let secret = connector_webhook_secrets.secret;

        let signing_key = hmac::Key::new(hmac::HMAC_SHA256, &secret);
        let signed_message = hmac::sign(&signing_key, &message);
        let computed_signature = hex::encode(signed_message.as_ref());
        let payload_sign = BASE64_ENGINE.encode(computed_signature);
        Ok(payload_sign.as_bytes().eq(&signature))
    }

    fn get_webhook_object_reference_id(
        &self,
        request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<api_models::webhooks::ObjectReferenceId, errors::ConnectorError> {
        let data_return = get_webhook_body(request.body)
            .change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?;
        match data_return {
            inespay::InespayWebhookEventData::Payment(data) => {
                Ok(api_models::webhooks::ObjectReferenceId::PaymentId(
                    api_models::payments::PaymentIdType::ConnectorTransactionId(
                        data.single_payin_id,
                    ),
                ))
            }
            inespay::InespayWebhookEventData::Refund(data) => {
                Ok(api_models::webhooks::ObjectReferenceId::RefundId(
                    api_models::webhooks::RefundIdType::ConnectorRefundId(data.refund_id),
                ))
            }
        }
    }

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

    fn get_webhook_resource_object(
        &self,
        request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<Box<dyn masking::ErasedMaskSerialize>, errors::ConnectorError> {
        let data_return = get_webhook_body(request.body)
            .change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?;
        Ok(match data_return {
            inespay::InespayWebhookEventData::Payment(payment_webhook_data) => {
                Box::new(payment_webhook_data)
            }
            inespay::InespayWebhookEventData::Refund(refund_webhook_data) => {
                Box::new(refund_webhook_data)
            }
        })
    }
}

impl webhooks::IncomingWebhook for for Inespay


pub struct FraudCheckFulfillmentData {
    pub amount: i64,
    pub order_details: Option<Vec<Secret<serde_json::Value>>>,
    pub fulfillment_req: FrmFulfillmentRequest,
}


File: crates/drainer/src/query.rs

use std::sync::Arc;

use common_utils::errors::CustomResult;
use diesel_models::errors::DatabaseError;

use crate::{kv, logger, metrics, pg_connection, services::Store};

#[async_trait::async_trait]
pub trait ExecuteQuery {
    async fn execute_query(
        self,
        store: &Arc<Store>,
        pushed_at: i64,
    ) -> CustomResult<(), DatabaseError>;
}

#[async_trait::async_trait]
impl ExecuteQuery for kv::DBOperation {
    async fn execute_query(
        self,
        store: &Arc<Store>,
        pushed_at: i64,
    ) -> CustomResult<(), DatabaseError> {
        let conn = pg_connection(&store.master_pool).await;
        let operation = self.operation();
        let table = self.table();

        let tags = router_env::metric_attributes!(("operation", operation), ("table", table));

        let (result, execution_time) =
            Box::pin(common_utils::date_time::time_it(|| self.execute(&conn))).await;

        push_drainer_delay(pushed_at, operation, table, tags);
        metrics::QUERY_EXECUTION_TIME.record(execution_time, tags);

        match result {
            Ok(result) => {
                logger::info!(operation = operation, table = table, ?result);
                metrics::SUCCESSFUL_QUERY_EXECUTION.add(1, tags);
                Ok(())
            }
            Err(err) => {
                logger::error!(operation = operation, table = table, ?err);
                metrics::ERRORS_WHILE_QUERY_EXECUTION.add(1, tags);
                Err(err)
            }
        }
    }
}

#[inline(always)]
fn push_drainer_delay(
    pushed_at: i64,
    operation: &str,
    table: &str,
    tags: &[router_env::opentelemetry::KeyValue],
) {
    let drained_at = common_utils::date_time::now_unix_timestamp();
    let delay = drained_at - pushed_at;

    logger::debug!(operation, table, delay = format!("{delay} secs"));

    match u64::try_from(delay) {
        Ok(delay) => metrics::DRAINER_DELAY_SECONDS.record(delay, tags),
        Err(error) => logger::error!(
            pushed_at,
            drained_at,
            delay,
            ?error,
            "Invalid drainer delay"
        ),
    }
}


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

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

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

impl ConnectorSpecifications for for Tokenio


OpenAPI Block Path: components.schemas.CardToken
{
  "type": "object",
  "required": [
    "card_holder_name"
  ],
  "properties": {
    "card_holder_name": {
      "type": "string",
      "description": "The card holder's name",
      "example": "John Test"
    },
    "card_cvc": {
      "type": "string",
      "description": "The CVC number for the card",
      "nullable": true
    }
  }
}


/// Get the encrypted Apple Pay payment data, returning an error if it does not exist
    pub fn get_encrypted_apple_pay_payment_data_mandatory(
        &self,
    ) -> Result<&String, errors::ValidationError> {
        self.get_encrypted_apple_pay_payment_data_optional()
            .get_required_value("Encrypted Apple Pay payment data")
            .attach_printable("Encrypted Apple Pay payment data is mandatory")
    }


pub struct DecisionEngineConfigSetupRequest {
    pub merchant_id: String,
    pub config: DecisionEngineConfigVariant,
}


pub async fn generic_list_roles_by_entity_type(
        conn: &PgPooledConn,
        payload: ListRolesByEntityPayload,
        is_lineage_data_required: bool,
        tenant_id: id_type::TenantId,
        org_id: id_type::OrganizationId,
    ) -> StorageResult<Vec<Self>> {
        let mut query = <Self as HasTable>::table()
            .into_boxed()
            .filter(dsl::tenant_id.eq(tenant_id))
            .filter(dsl::org_id.eq(org_id));

        match payload {
            ListRolesByEntityPayload::Organization => {
                let entity_in_vec =
                    Self::get_entity_list(EntityType::Organization, is_lineage_data_required);
                query = query.filter(dsl::entity_type.eq_any(entity_in_vec))
            }

            ListRolesByEntityPayload::Merchant(merchant_id) => {
                let entity_in_vec =
                    Self::get_entity_list(EntityType::Merchant, is_lineage_data_required);
                query = query
                    .filter(
                        dsl::scope
                            .eq(RoleScope::Organization)
                            .or(dsl::merchant_id.eq(merchant_id)),
                    )
                    .filter(dsl::entity_type.eq_any(entity_in_vec))
            }

            ListRolesByEntityPayload::Profile(merchant_id, profile_id) => {
                let entity_in_vec =
                    Self::get_entity_list(EntityType::Profile, is_lineage_data_required);
                query = query
                    .filter(
                        dsl::scope
                            .eq(RoleScope::Organization)
                            .or(dsl::scope
                                .eq(RoleScope::Merchant)
                                .and(dsl::merchant_id.eq(merchant_id.clone())))
                            .or(dsl::profile_id.eq(profile_id)),
                    )
                    .filter(dsl::entity_type.eq_any(entity_in_vec))
            }
        };

        router_env::logger::debug!(query = %debug_query::<Pg,_>(&query).to_string());

        match generics::db_metrics::track_database_call::<Self, _, _>(
            query.get_results_async(conn),
            generics::db_metrics::DatabaseOperation::Filter,
        )
        .await
        {
            Ok(value) => Ok(value),
            Err(err) => Err(report!(err)).change_context(errors::DatabaseError::Others),
        }
    }


pub async fn log_dispute(
        &self,
        dispute: &Dispute,
        old_dispute: Option<Dispute>,
        tenant_id: TenantID,
    ) -> MQResult<()> {
        if let Some(negative_event) = old_dispute {
            self.log_event(&KafkaEvent::old(
                &KafkaDispute::from_storage(&negative_event),
                tenant_id.clone(),
                self.ckh_database_name.clone(),
            ))
            .attach_printable_lazy(|| {
                format!("Failed to add negative dispute event {negative_event:?}")
            })?;
        };

        self.log_event(&KafkaEvent::new(
            &KafkaDispute::from_storage(dispute),
            tenant_id.clone(),
            self.ckh_database_name.clone(),
        ))
        .attach_printable_lazy(|| format!("Failed to add positive dispute event {dispute:?}"))?;

        self.log_event(&KafkaConsolidatedEvent::new(
            &KafkaDisputeEvent::from_storage(dispute),
            tenant_id.clone(),
        ))
        .attach_printable_lazy(|| format!("Failed to add consolidated dispute event {dispute:?}"))
    }


OpenAPI Block Path: components.schemas.RoutableConnectors
{
  "type": "string",
  "description": "RoutableConnectors are the subset of Connectors that are eligible for payments routing",
  "enum": [
    "authipay",
    "adyenplatform",
    "stripe_billing_test",
    "phonypay",
    "fauxpay",
    "pretendpay",
    "stripe_test",
    "adyen_test",
    "checkout_test",
    "paypal_test",
    "aci",
    "adyen",
    "affirm",
    "airwallex",
    "amazonpay",
    "archipel",
    "authorizedotnet",
    "bankofamerica",
    "barclaycard",
    "billwerk",
    "bitpay",
    "bambora",
    "blackhawknetwork",
    "bamboraapac",
    "bluesnap",
    "bluecode",
    "boku",
    "braintree",
    "breadpay",
    "cashtocode",
    "celero",
    "chargebee",
    "custombilling",
    "checkbook",
    "checkout",
    "coinbase",
    "coingate",
    "cryptopay",
    "cybersource",
    "datatrans",
    "deutschebank",
    "digitalvirgo",
    "dlocal",
    "dwolla",
    "ebanx",
    "elavon",
    "facilitapay",
    "fiserv",
    "fiservemea",
    "fiuu",
    "flexiti",
    "forte",
    "getnet",
    "globalpay",
    "globepay",
    "gocardless",
    "hipay",
    "helcim",
    "iatapay",
    "inespay",
    "itaubank",
    "jpmorgan",
    "klarna",
    "mifinity",
    "mollie",
    "moneris",
    "multisafepay",
    "nexinets",
    "nexixpay",
    "nmi",
    "nomupay",
    "noon",
    "nordea",
    "novalnet",
    "nuvei",
    "opennode",
    "paybox",
    "payme",
    "payload",
    "payone",
    "paypal",
    "paysafe",
    "paystack",
    "paytm",
    "payu",
    "phonepe",
    "placetopay",
    "powertranz",
    "prophetpay",
    "rapyd",
    "razorpay",
    "recurly",
    "redsys",
    "riskified",
    "santander",
    "shift4",
    "signifyd",
    "silverflow",
    "square",
    "stax",
    "stripe",
    "stripebilling",
    "trustpay",
    "tokenio",
    "tsys",
    "volt",
    "wellsfargo",
    "wise",
    "worldline",
    "worldpay",
    "worldpayvantiv",
    "worldpayxml",
    "xendit",
    "zen",
    "plaid",
    "zsl"
  ]
}