Documentation: CHANGELOG.md
# Type: Doc File

# Changelog

All notable changes to HyperSwitch will be documented here.

- - -

## 2025.09.08.0

### Features

- **nuvei:** Map NTID for nuvei payment responses ([#9261](https://github.com/juspay/hyperswitch/pull/9261)) ([`0062264`](https://github.com/juspay/hyperswitch/commit/00622647a35ba2f0086608a839c1aef74786da96))

### Bug Fixes

- **connector:** [CYBERSOURCE] Add extra field for Cybersource MIT payment for acquirer CMCIC ([#9265](https://github.com/juspay/hyperswitch/pull/9265)) ([`b45ddc9`](https://github.com/juspay/hyperswitch/commit/b45ddc9abb203cb608b9ac43a5fd053d25c79623))
- **payout:** Send latest status in outgoing webhook notifications during IncomingWebhook flow ([#9283](https://github.com/juspay/hyperswitch/pull/9283)) ([`2fe6cdd`](https://github.com/juspay/hyperswitch/commit/2fe6cdd5f27196b1b6545dc806890ad537977104))

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

- - -

## 2025.09.04.1

### Features

- **connector:** [SHIFT4] Pass metadata to connector ([#9254](https://github.com/juspay/hyperswitch/pull/9254)) ([`bcfb13e`](https://github.com/juspay/hyperswitch/commit/bcfb13eec06f6366d76a28b292e417c32bf28cf9))

### Bug Fixes

- **connector:** Revert [AUTHORIZEDOTNET] customer id population fixed in authorize flow ([#9253](https://github.com/juspay/hyperswitch/pull/9253)) ([`f10a713`](https://github.com/juspay/hyperswitch/commit/f10a713219ab13a4c584a1178e7ee2488f634f9e))

### Refactors

- Alter subscription table to update primary key ([#9272](https://github.com/juspay/hyperswitch/pull/9272)) ([`5e1fd0b`](https://github.com/juspay/hyperswitch/commit/5e1fd0b187b99698936a8a0cb0d839a60b830de2))

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

- - -

## 2025.09.04.0

### Features

- **connector:**
  - [SANTANDER] Added Boleto Payment Method ([#9008](https://github.com/juspay/hyperswitch/pull/9008)) ([`63857a8`](https://github.com/juspay/hyperswitch/commit/63857a86a603b3b8b1f6bf77b15cf3451a7f2165))
  - [ADYEN] Add Support for MOTO Payments ([#9134](https://github.com/juspay/hyperswitch/pull/9134)) ([`44ffb4f`](https://github.com/juspay/hyperswitch/commit/44ffb4fe361870729839d66d0b257d2e1fbe9684))
- **core:** Add subscription table ([#9133](https://github.com/juspay/hyperswitch/pull/9133)) ([`144f385`](https://github.com/juspay/hyperswitch/commit/144f38527e4bf5278947705f9fbbb707bf822109))
- **crypto:** Add RSA-SHA-256 signature verification ([#9080](https://github.com/juspay/hyperswitch/pull/9080)) ([`3fcbe40`](https://github.com/juspay/hyperswitch/commit/3fcbe40e5c15b80539f07c60d0c846cf5a22b46a))
- **migration:** Perform card validation only for allowed payment methods ([#9131](https://github.com/juspay/hyperswitch/pull/9131)) ([`0457880`](https://github.com/juspay/hyperswitch/commit/04578801b62245144c029accb6de5c23042480cb))
- **nuvei:** Add mandate support for integ,prod,and sbx for googlepay applepay ([#9256](https://github.com/juspay/hyperswitch/pull/9256)) ([`80d5afc`](https://github.com/juspay/hyperswitch/commit/80d5afc4e9139f0876bf08cd2ec0fd1cdccef106))
- **payouts:** Mapped Default Values to Unified Error Code and Message ([#9262](https://github.com/juspay/hyperswitch/pull/9262)) ([`4ab54f3`](https://github.com/juspay/hyperswitch/commit/4ab54f3c835de59cc098c8518938c746f4895bd1))

### Bug Fixes

- **core:**
  - Add extended authorization to profile update and add supported payment method types ([#9237](https://github.com/juspay/hyperswitch/pull/9237)) ([`e308428`](https://github.com/juspay/hyperswitch/commit/e30842892a48ff6e4bc0e7c0a4990d3e5fc50027))
  - Change condition for updating payment method status ([#9259](https://github.com/juspay/hyperswitch/pull/9259)) ([`0543e51`](https://github.com/juspay/hyperswitch/commit/0543e5155524626e1adbab6b10e1308cf479e412))
- **payment-link:** Decode details before using them in secure links ([#9260](https://github.com/juspay/hyperswitch/pull/9260)) ([`ef778bb`](https://github.com/juspay/hyperswitch/commit/ef778bbc8a25d9b09b536701ac8b6804f3da0efd))
- **payments:** Override payment_method_type only for cards ([#9241](https://github.com/juspay/hyperswitch/pull/9241)) ([`50a8013`](https://github.com/juspay/hyperswitch/commit/50a8013de13e0d6a1652f77889266d71c72679fa))
- Change precedence order in address that we send to ucs ([#9269](https://github.com/juspay/hyperswitch/pull/9269)) ([`a2b180e`](https://github.com/juspay/hyperswitch/commit/a2b180e393680a527d84199f20e8851f78eba8da))

### Refactors

- **routing:** Receive json value instead of string ([#8965](https://github.com/juspay/hyperswitch/pull/8965)) ([`db4413c`](https://github.com/juspay/hyperswitch/commit/db4413cef3695d4f833ef1f9870158711e9af199))

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

- - -

## 2025.09.03.0

### Features

- **core:** Update payment methods api ([#9075](https://github.com/juspay/hyperswitch/pull/9075)) ([`6451136`](https://github.com/juspay/hyperswitch/commit/645113654a15525b4bb33ecc8427a7faa71c89ed))
- **cypress:** Cypress test for ucs ([#8967](https://github.com/juspay/hyperswitch/pull/8967)) ([`7ad5cd5`](https://github.com/juspay/hyperswitch/commit/7ad5cd5531eee612d72c938f324c08682359a313))
- **ucs:** Add event logging for UCS operations ([#9058](https://github.com/juspay/hyperswitch/pull/9058)) ([`10cf161`](https://github.com/juspay/hyperswitch/commit/10cf161d14810cc9c6320933909e9cd3bfdc41ca))

### Bug Fixes

- **connector:** [NETCETERA] Add fix for netcetera authentication response ([#9077](https://github.com/juspay/hyperswitch/pull/9077)) ([`e2d72be`](https://github.com/juspay/hyperswitch/commit/e2d72bef7dc07540ee4d54bfc79b934bc303dca7))
- **metadata:** Fix nordea metadata again ([#9126](https://github.com/juspay/hyperswitch/pull/9126)) ([`a0900d9`](https://github.com/juspay/hyperswitch/commit/a0900d994b80818934341e7c88de767836dcaf44))
- Disable UCS PSync call for Cashtocode ([#9093](https://github.com/juspay/hyperswitch/pull/9093)) ([`7c6e5f6`](https://github.com/juspay/hyperswitch/commit/7c6e5f6ed050bc9739bafbaf7b7170b4234ea5b4))

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

- - -

## 2025.09.02.0

### Features

- **connector:**
  - Introduce setup mandate flow for payload ([#9110](https://github.com/juspay/hyperswitch/pull/9110)) ([`4e33e97`](https://github.com/juspay/hyperswitch/commit/4e33e9741a183f3a142742deb7c8c87e6ec1a294))
  - Add Connector Specifications ([#8797](https://github.com/juspay/hyperswitch/pull/8797)) ([`861e023`](https://github.com/juspay/hyperswitch/commit/861e023f607099c845b36099a4aa9a0622fbc7ee))
- **core:**
  - Add Merchant Tax ID in Merchant Profile ([#8992](https://github.com/juspay/hyperswitch/pull/8992)) ([`ff14b7c`](https://github.com/juspay/hyperswitch/commit/ff14b7cac84f23133f314a1ac337d04e5e845fce))
  - [proxy payments] send external vault proxy metadata to UCS ([#9108](https://github.com/juspay/hyperswitch/pull/9108)) ([`c02d8b9`](https://github.com/juspay/hyperswitch/commit/c02d8b9ba9e204fa163b61c419811eaa65fdbcb4))
- **injector:** Adding tracing to injector for dependency issues ([#9124](https://github.com/juspay/hyperswitch/pull/9124)) ([`aae1994`](https://github.com/juspay/hyperswitch/commit/aae1994ea147fa1ebdc9555f00b072bb9ed57abc))
- **router:** Payment Intent and MCA changes for split payments ([#9049](https://github.com/juspay/hyperswitch/pull/9049)) ([`81b340d`](https://github.com/juspay/hyperswitch/commit/81b340d3a9e20bf278d97d76f9457d29a5bff8e2))

### Bug Fixes

- **authentication:** Fixed ctp confirmation bug ([#9050](https://github.com/juspay/hyperswitch/pull/9050)) ([`4413751`](https://github.com/juspay/hyperswitch/commit/44137517de318c6578602fd3213f571f3c9c50cd))
- **core:** Fix net_amount on Incremental Authorization Update ([#9073](https://github.com/juspay/hyperswitch/pull/9073)) ([`24beaa2`](https://github.com/juspay/hyperswitch/commit/24beaa224a08301b40ee18c617cbf116049eb3d5))
- **payment_method:** Add validation for proxy card details ([#9136](https://github.com/juspay/hyperswitch/pull/9136)) ([`ccdae87`](https://github.com/juspay/hyperswitch/commit/ccdae87789ff53ef60f8adc21dd874c7659f78dc))
- **payments:** Automatic connector_payment_id hashing in v2 if length > 128 ([#9017](https://github.com/juspay/hyperswitch/pull/9017)) ([`97ae320`](https://github.com/juspay/hyperswitch/commit/97ae32035e7f607fa1fffe6c6e694fb39a9be9ae))

### Refactors

- **dynamic_routing:** Remove delete_merchant for DE ([#9076](https://github.com/juspay/hyperswitch/pull/9076)) ([`ec5a955`](https://github.com/juspay/hyperswitch/commit/ec5a955158d1028024787fcf1997f1cce7fdbcda))

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

- - -

## 2025.09.01.0

### Features

- **celero:** CIT-MIT for celero (ALPHA CONNECTOR) ([#9026](https://github.com/juspay/hyperswitch/pull/9026)) ([`55d4d7b`](https://github.com/juspay/hyperswitch/commit/55d4d7b894dc0e88c5bbe2f701dc7536db139502))
- **connector:** [AMAZONPAY] add Payment flows for Amazon Pay Wallet ([#7062](https://github.com/juspay/hyperswitch/pull/7062)) ([`23cf437`](https://github.com/juspay/hyperswitch/commit/23cf4376f5691e9c56cb0346bfd06c1e88ff38a8))
- **connectors:** [Stripe] add extended authorization for cards ([#9084](https://github.com/juspay/hyperswitch/pull/9084)) ([`8cfa966`](https://github.com/juspay/hyperswitch/commit/8cfa966d34e914c8df06f000b59fdad53d3f9902))
- **nuvei:** NTID Support + googlepay & applepay mandate support ([#9081](https://github.com/juspay/hyperswitch/pull/9081)) ([`a589e22`](https://github.com/juspay/hyperswitch/commit/a589e2246447b7096fd335d444a24b2450c3e7c2))

### Bug Fixes

- **connector:** [AUTHORIZEDOTNET] customer id population fixed in authorize flow ([#9079](https://github.com/juspay/hyperswitch/pull/9079)) ([`60ef06e`](https://github.com/juspay/hyperswitch/commit/60ef06e8ffdb83e6ae2d3b12a263d7b27bb606f3))
- Resolved issue of webhooks response from ucs ([#9021](https://github.com/juspay/hyperswitch/pull/9021)) ([`4a60b07`](https://github.com/juspay/hyperswitch/commit/4a60b079544815fcddcf0694e2aeb44a03002b25))

### Refactors

- **revenue_recovery:** Add configs for calculate job ([#9106](https://github.com/juspay/hyperswitch/pull/9106)) ([`8ce36a2`](https://github.com/juspay/hyperswitch/commit/8ce36a2fd513034755a1bf1aacbd3210083e07c9))

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

- - -

## 2025.08.29.0

### Features

- **router:** Add payment_type to Get Intent response (v2) ([#9066](https://github.com/juspay/hyperswitch/pull/9066)) ([`26930a4`](https://github.com/juspay/hyperswitch/commit/26930a47e905baf647f218328e81529951d4f563))

### Bug Fixes

- **connector:**
  - Use SignatureKey for VGS auth_type ([#9068](https://github.com/juspay/hyperswitch/pull/9068)) ([`1ff2ed9`](https://github.com/juspay/hyperswitch/commit/1ff2ed9477a691b23fd2882e2e53dd20078199d4))
  - [Airwallex] Remove optional fields for Ideal and Klarna pm ([#9060](https://github.com/juspay/hyperswitch/pull/9060)) ([`d112179`](https://github.com/juspay/hyperswitch/commit/d112179f2efee77057f53297774f5c27b1e99f83))
  - [CYBERSOURCE] Fix transaction_type for Googlepay payments ([#9071](https://github.com/juspay/hyperswitch/pull/9071)) ([`47f6885`](https://github.com/juspay/hyperswitch/commit/47f68859b3c4c5558b57f5cb6d52dcc5427ef76c))
- **core:** Send whole_connector_response even when Connector sends an Error ([#9043](https://github.com/juspay/hyperswitch/pull/9043)) ([`3aa38ba`](https://github.com/juspay/hyperswitch/commit/3aa38bac1b4aac265a9de142e20c261087be7127))
- **revenue_recovery:** Populate payment method data in record attempt flow for V2 ([#9061](https://github.com/juspay/hyperswitch/pull/9061)) ([`594fae1`](https://github.com/juspay/hyperswitch/commit/594fae1459c1bba05ef9fcd3a6827f9cb026bfa5))
- **wasm:** [dwolla] fix WASM changes ([#9051](https://github.com/juspay/hyperswitch/pull/9051)) ([`a71acbf`](https://github.com/juspay/hyperswitch/commit/a71acbf8ac0f901983a7664bbb472fce60b797a4))

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

- - -

## 2025.08.28.0

### Features

- **core:** Added support for unified_connector_service CardNumber and Secret<String> Type ([#9044](https://github.com/juspay/hyperswitch/pull/9044)) ([`cf64d2a`](https://github.com/juspay/hyperswitch/commit/cf64d2a9dcef422e7d080e21a7e8644694337a51))

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

- - -

## 2025.08.27.0

### Features

- **connector:**
  - [Paysafe] add connector template code ([#9011](https://github.com/juspay/hyperswitch/pull/9011)) ([`ce0159b`](https://github.com/juspay/hyperswitch/commit/ce0159b6d098dba1e00622d18b0f1b29f2151b9f))
  - [Nuvei] Implement setup mandate flow for cards ([#9012](https://github.com/juspay/hyperswitch/pull/9012)) ([`58ff01b`](https://github.com/juspay/hyperswitch/commit/58ff01bab25b167c5f6d8614b02b10d16154e461))
- **payment-link:** Handle locale in payment links ([#9064](https://github.com/juspay/hyperswitch/pull/9064)) ([`aaf5ea9`](https://github.com/juspay/hyperswitch/commit/aaf5ea9886cd11222ee2eced21e098182c0388ac))

### Bug Fixes

- **connector:** [nordea] pass valid connector metadata fields and properly throw error unsupported flows ([#9041](https://github.com/juspay/hyperswitch/pull/9041)) ([`70b426e`](https://github.com/juspay/hyperswitch/commit/70b426e3cac2dffee30fe99cdaa7040d74d92312))
- **routing:** Activating multiple configs in decision engine ([#9036](https://github.com/juspay/hyperswitch/pull/9036)) ([`8446ffb`](https://github.com/juspay/hyperswitch/commit/8446ffbf5992a97d79d129cade997effc60fcd85))

### Refactors

- **connector:** Implement amount converter framework for authorizedotnet, bankofamerica ([#8878](https://github.com/juspay/hyperswitch/pull/8878)) ([`30925ca`](https://github.com/juspay/hyperswitch/commit/30925ca5dd51be93e33ac4492b85c2322263b3fc))

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

- - -

## 2025.08.26.0

### Features

- **connector:**
  - Add VGS connector impls ([#7942](https://github.com/juspay/hyperswitch/pull/7942)) ([`ddf1a6a`](https://github.com/juspay/hyperswitch/commit/ddf1a6a5f36fcc8fd8e6a0497ee7f7ff7a5b98fa))
  - [Adyenplatform] process payouts using PSP tokens ([#9040](https://github.com/juspay/hyperswitch/pull/9040)) ([`b8c5bf3`](https://github.com/juspay/hyperswitch/commit/b8c5bf3f6d0809ba318694d69db1e4431b2c4c0b))
- **injector:** Add support for new crate - injector for external vault proxy ([#8959](https://github.com/juspay/hyperswitch/pull/8959)) ([`d18a941`](https://github.com/juspay/hyperswitch/commit/d18a94188ea93d07c4581888880088a9e676b1fd))
- **revenue_recovery:**
  - Add redis-based payment processor token tracking for revenue recovery ([#8846](https://github.com/juspay/hyperswitch/pull/8846)) ([`0b59b90`](https://github.com/juspay/hyperswitch/commit/0b59b9086cd68eef346413a27d03fa61fb6bb1f7))
  - Introducing new calculate job for card switching and invoice queueing ([#8848](https://github.com/juspay/hyperswitch/pull/8848)) ([`5efe4d9`](https://github.com/juspay/hyperswitch/commit/5efe4d9751bd88861bbe3e609bb815983fca9778))

### Bug Fixes

- **connector:** Add Support for CUSTOMERREQUEST Refund Reason in Adyen ([#9035](https://github.com/juspay/hyperswitch/pull/9035)) ([`2da681a`](https://github.com/juspay/hyperswitch/commit/2da681a265ef85e90f5282acda3140e78aa847d5))

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

- - -

## 2025.08.25.0

### Features

- **connector:**
  - [BHN] Add BHN GiftCard Flow( Alpha) ([#8701](https://github.com/juspay/hyperswitch/pull/8701)) ([`cb34ec5`](https://github.com/juspay/hyperswitch/commit/cb34ec51e0f2b4ba071602f5fe974429de542b80))
  - [AFFIRM] BNPL flow added (Alpha) ([#8795](https://github.com/juspay/hyperswitch/pull/8795)) ([`ad247b7`](https://github.com/juspay/hyperswitch/commit/ad247b7663e46e9e53c0c7011f1a3acee1ebe36c))
- **core:** Add network_transaction_id in payments Response ([#9032](https://github.com/juspay/hyperswitch/pull/9032)) ([`7047c3f`](https://github.com/juspay/hyperswitch/commit/7047c3faa1e71c7ad13592cd41b77be3cdc1a8a2))
- **nuvei:** Googlepay , applepay and partial authorization integration for nuvei ([#8985](https://github.com/juspay/hyperswitch/pull/8985)) ([`049e6b5`](https://github.com/juspay/hyperswitch/commit/049e6b572d88d141fccf09e12854988daf146e67))
- **payment_methods:** Add payment-methods/filter endpoint to list countries and currencies for connector payment method in v2 ([#8978](https://github.com/juspay/hyperswitch/pull/8978)) ([`6016377`](https://github.com/juspay/hyperswitch/commit/6016377ad8e424c58bb91af5c145741ba84e059f))
- **router:**
  - Verify service for applepay merchant registration v2 ([#8999](https://github.com/juspay/hyperswitch/pull/8999)) ([`0ba5d54`](https://github.com/juspay/hyperswitch/commit/0ba5d543496de040f637c5bf8d2529667d25e8a5))
  - Add support to use signature_network and is_issuer_regulated as filters ([#9033](https://github.com/juspay/hyperswitch/pull/9033)) ([`ad05dc4`](https://github.com/juspay/hyperswitch/commit/ad05dc4176114dad3420a78af238d3842160e464))

### Bug Fixes

- **connectors:** [worldpayvantiv] map cnp_txn_id to connector_txn_id and enforce max length for customer_id and connector_request_reference_id ([#9031](https://github.com/juspay/hyperswitch/pull/9031)) ([`b1486f8`](https://github.com/juspay/hyperswitch/commit/b1486f8bd31dbe741ac01f36053bc4a2f3eaaa78))


pub fn get_acs_trans_id(&self) -> Option<String> {
        if let Self::Challenge(challenge_params) = self {
            challenge_params.acs_trans_id.clone()
        } else {
            None
        }
    }


pub struct Comparison {
    /// The left hand side which will always be a domain input identifier like "payment.method.cardtype"
    pub lhs: String,
    /// The comparison operator
    pub comparison: ComparisonType,
    /// The value to compare against
    pub value: ValueType,
    /// Additional metadata that the Static Analyzer and Backend does not touch.
    /// This can be used to store useful information for the frontend and is required for communication
    /// between the static analyzer and the frontend.
    // #[schema(value_type=HashMap<String, serde_json::Value>)]
    pub metadata: Metadata,
}

/// Represents all the conditions of an IF statement
/// eg:
///
/// ```text
/// payment.method = card & payment.method.cardtype = debit & payment.method.network = diners
/// ```
pub type IfCondition = Vec<Comparison>;

/// Represents an IF statement with conditions and optional nested IF statements
///
/// ```text
/// payment.method = card {
///     payment.method.cardtype = (credit, debit) {
///         payment.method.network = (amex, rupay, diners)
///     }
/// }
/// ```
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct IfStatement {
    // #[schema(value_type=Vec<Comparison>)]
    pub condition: IfCondition,
    pub nested: Option<Vec<IfStatement>>,
}

/// Represents a rule
///
/// ```text
/// rule_name: [stripe, adyen, checkout]
/// {
///     payment.method = card {
///         payment.method.cardtype = (credit, debit) {
///             payment.method.network = (amex, rupay, diners)
///         }
///
///         payment.method.cardtype = credit
///     }
/// }
/// ```
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
// #[aliases(RuleConnectorSelection = Rule<ConnectorSelection>)]
pub struct Rule {
    pub name: String,
    #[serde(alias = "routingType")]
    pub routing_type: RoutingType,
    #[serde(alias = "routingOutput")]
    pub output: Output,
    pub statements: Vec<IfStatement>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum RoutingType {
    Priority,
    VolumeSplit,
    VolumeSplitPriority,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct VolumeSplit<T> {
    pub split: u8,
    pub output: T,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct ConnectorInfo {
    pub gateway_name: String,
    pub gateway_id: Option<String>,
}

impl TryFrom<ConnectorInfo> for DeRoutableConnectorChoice {
    type Error = error_stack::Report<errors::RoutingError>;

    fn try_from(c: ConnectorInfo) -> Result<Self, Self::Error> {
        let gateway_id = c
            .gateway_id
            .map(|mca| {
                id_type::MerchantConnectorAccountId::wrap(mca)
                    .change_context(errors::RoutingError::GenericConversionError {
                        from: "String".to_string(),
                        to: "MerchantConnectorAccountId".to_string(),
                    })
                    .attach_printable("unable to convert MerchantConnectorAccountId from string")
            })
            .transpose()?;

        let gateway_name = RoutableConnectors::from_str(&c.gateway_name)
            .map_err(|_| errors::RoutingError::GenericConversionError {
                from: "String".to_string(),
                to: "RoutableConnectors".to_string(),
            })
            .attach_printable("unable to convert connector name to RoutableConnectors")?;

        Ok(Self {
            gateway_name,
            gateway_id,
        })
    }
}

impl ConnectorInfo {
    pub fn new(gateway_name: String, gateway_id: Option<String>) -> Self {
        Self {
            gateway_name,
            gateway_id,
        }
    }
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Output {
    Single(ConnectorInfo),
    Priority(Vec<ConnectorInfo>),
    VolumeSplit(Vec<VolumeSplit<ConnectorInfo>>),
    VolumeSplitPriority(Vec<VolumeSplit<Vec<ConnectorInfo>>>),
}

pub type Globals = HashMap<String, HashSet<ValueType>>;

/// The program, having a default connector selection and
/// a bunch of rules. Also can hold arbitrary metadata.
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
// #[aliases(ProgramConnectorSelection = Program<ConnectorSelection>)]
pub struct Program {
    pub globals: Globals,
    pub default_selection: Output,
    // #[schema(value_type=RuleConnectorSelection)]
    pub rules: Vec<Rule>,
    // #[schema(value_type=HashMap<String, serde_json::Value>)]
    pub metadata: Option<Metadata>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct RoutingRule {
    pub rule_id: Option<String>,
    pub name: String,
    pub description: Option<String>,
    pub metadata: Option<RoutingMetadata>,
    pub created_by: String,
    #[serde(default)]
    pub algorithm_for: AlgorithmType,
    pub algorithm: StaticRoutingAlgorithm,
}
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize, strum::Display)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum AlgorithmType {
    #[default]
    Payment,
    Payout,
    ThreeDsAuthentication,
}

impl From<TransactionType> for AlgorithmType {
    fn from(transaction_type: TransactionType) -> Self {
        match transaction_type {
            TransactionType::Payment => Self::Payment,
            TransactionType::Payout => Self::Payout,
            TransactionType::ThreeDsAuthentication => Self::ThreeDsAuthentication,
        }
    }
}

impl From<RoutableConnectorChoice> for ConnectorInfo {
    fn from(c: RoutableConnectorChoice) -> Self {
        Self {
            gateway_name: c.connector.to_string(),
            gateway_id: c
                .merchant_connector_id
                .map(|mca_id| mca_id.get_string_repr().to_string()),
        }
    }
}

impl From<Box<RoutableConnectorChoice>> for ConnectorInfo {
    fn from(c: Box<RoutableConnectorChoice>) -> Self {
        Self {
            gateway_name: c.connector.to_string(),
            gateway_id: c
                .merchant_connector_id
                .map(|mca_id| mca_id.get_string_repr().to_string()),
        }
    }
}

impl From<ConnectorVolumeSplit> for VolumeSplit<ConnectorInfo> {
    fn from(v: ConnectorVolumeSplit) -> Self {
        Self {
            split: v.split,
            output: ConnectorInfo {
                gateway_name: v.connector.connector.to_string(),
                gateway_id: v
                    .connector
                    .merchant_connector_id
                    .map(|mca_id| mca_id.get_string_repr().to_string()),
            },
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", content = "data", rename_all = "snake_case")]
pub enum StaticRoutingAlgorithm {
    Single(Box<ConnectorInfo>),
    Priority(Vec<ConnectorInfo>),
    VolumeSplit(Vec<VolumeSplit<ConnectorInfo>>),
    Advanced(Program),
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct RoutingMetadata {
    pub kind: enums::RoutingAlgorithmKind,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct RoutingDictionaryRecord {
    pub rule_id: String,
    pub name: String,
    pub created_at: time::PrimitiveDateTime,
    pub modified_at: time::PrimitiveDateTime,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct RoutingAlgorithmRecord {
    pub id: id_type::RoutingId,
    pub name: String,
    pub description: Option<String>,
    pub created_by: id_type::ProfileId,
    pub algorithm_data: StaticRoutingAlgorithm,
    pub algorithm_for: TransactionType,
    pub metadata: Option<RoutingMetadata>,
    pub created_at: time::PrimitiveDateTime,
    pub modified_at: time::PrimitiveDateTime,
}

impl From<RoutingAlgorithmRecord> for routing_algorithm::RoutingProfileMetadata {
    fn from(record: RoutingAlgorithmRecord) -> Self {
        let kind = match record.algorithm_data {
            StaticRoutingAlgorithm::Single(_) => enums::RoutingAlgorithmKind::Single,
            StaticRoutingAlgorithm::Priority(_) => enums::RoutingAlgorithmKind::Priority,
            StaticRoutingAlgorithm::VolumeSplit(_) => enums::RoutingAlgorithmKind::VolumeSplit,
            StaticRoutingAlgorithm::Advanced(_) => enums::RoutingAlgorithmKind::Advanced,
        };
        Self {
            profile_id: record.created_by,
            algorithm_id: record.id,
            name: record.name,
            description: record.description,
            kind,
            created_at: record.created_at,
            modified_at: record.modified_at,
            algorithm_for: record.algorithm_for,
        }
    }
}

impl TryFrom<ast::Program<ConnectorSelection>> for Program {
    type Error = error_stack::Report<errors::RoutingError>;

    fn try_from(p: ast::Program<ConnectorSelection>) -> Result<Self, Self::Error> {
        let rules = p
            .rules
            .into_iter()
            .map(convert_rule)
            .collect::<Result<Vec<_>, _>>()?;

        Ok(Self {
            globals: HashMap::new(),
            default_selection: convert_output(p.default_selection),
            rules,
            metadata: Some(p.metadata),
        })
    }
}

impl TryFrom<ast::Program<ConnectorSelection>> for StaticRoutingAlgorithm {
    type Error = error_stack::Report<errors::RoutingError>;

    fn try_from(p: ast::Program<ConnectorSelection>) -> Result<Self, Self::Error> {
        let internal_program: Program = p.try_into()?;
        Ok(Self::Advanced(internal_program))
    }
}

fn convert_rule(rule: ast::Rule<ConnectorSelection>) -> RoutingResult<Rule> {
    let routing_type = match &rule.connector_selection {
        ConnectorSelection::Priority(_) => RoutingType::Priority,
        ConnectorSelection::VolumeSplit(_) => RoutingType::VolumeSplit,
    };

    Ok(Rule {
        name: rule.name,
        routing_type,
        output: convert_output(rule.connector_selection),
        statements: rule
            .statements
            .into_iter()
            .map(convert_if_stmt)
            .collect::<RoutingResult<Vec<IfStatement>>>()?,
    })
}

fn convert_if_stmt(stmt: ast::IfStatement) -> RoutingResult<IfStatement> {
    Ok(IfStatement {
        condition: stmt
            .condition
            .into_iter()
            .map(convert_comparison)
            .collect::<RoutingResult<Vec<Comparison>>>()?,

        nested: stmt
            .nested
            .map(|v| {
                v.into_iter()
                    .map(convert_if_stmt)
                    .collect::<RoutingResult<Vec<IfStatement>>>()
            })
            .transpose()?,
    })
}

fn convert_comparison(c: ast::Comparison) -> RoutingResult<Comparison> {
    Ok(Comparison {
        lhs: c.lhs,
        comparison: convert_comparison_type(c.comparison),
        value: convert_value(c.value)?,
        metadata: c.metadata,
    })
}

fn convert_comparison_type(ct: ast::ComparisonType) -> ComparisonType {
    match ct {
        ast::ComparisonType::Equal => ComparisonType::Equal,
        ast::ComparisonType::NotEqual => ComparisonType::NotEqual,
        ast::ComparisonType::LessThan => ComparisonType::LessThan,
        ast::ComparisonType::LessThanEqual => ComparisonType::LessThanEqual,
        ast::ComparisonType::GreaterThan => ComparisonType::GreaterThan,
        ast::ComparisonType::GreaterThanEqual => ComparisonType::GreaterThanEqual,
    }
}

fn convert_value(v: ast::ValueType) -> RoutingResult<ValueType> {
    use ast::ValueType::*;
    match v {
        Number(n) => Ok(ValueType::Number(
            n.get_amount_as_i64().try_into().unwrap_or_default(),
        )),
        EnumVariant(e) => Ok(ValueType::EnumVariant(e)),
        MetadataVariant(m) => Ok(ValueType::MetadataVariant(MetadataValue {
            key: m.key,
            value: m.value,
        })),
        StrValue(s) => Ok(ValueType::StrValue(s)),

        NumberArray(arr) => Ok(ValueType::NumberArray(
            arr.into_iter()
                .map(|n| n.get_amount_as_i64().try_into().unwrap_or_default())
                .collect(),
        )),
        EnumVariantArray(arr) => Ok(ValueType::EnumVariantArray(arr)),
        NumberComparisonArray(arr) => Ok(ValueType::NumberComparisonArray(
            arr.into_iter()
                .map(|nc| NumberComparison {
                    comparison_type: convert_comparison_type(nc.comparison_type),
                    number: nc.number.get_amount_as_i64().try_into().unwrap_or_default(),
                })
                .collect(),
        )),
    }
}

fn convert_output(sel: ConnectorSelection) -> Output {
    match sel {
        ConnectorSelection::Priority(choices) => {
            Output::Priority(choices.into_iter().map(stringify_choice).collect())
        }
        ConnectorSelection::VolumeSplit(vs) => Output::VolumeSplit(
            vs.into_iter()
                .map(|v| VolumeSplit {
                    split: v.split,
                    output: stringify_choice(v.connector),
                })
                .collect(),
        ),
    }
}

fn stringify_choice(c: RoutableConnectorChoice) -> ConnectorInfo {
    ConnectorInfo::new(
        c.connector.to_string(),
        c.merchant_connector_id
            .map(|mca_id| mca_id.get_string_repr().to_string()),
    )
}

pub async fn select_routing_result<T>(
    state: &SessionState,
    business_profile: &business_profile::Profile,
    hyperswitch_result: T,
    de_result: T,
) -> T {
    let routing_result_source: Option<api_routing::RoutingResultSource> = state
        .store
        .find_config_by_key(&format!(
            "routing_result_source_{0}",
            business_profile.get_id().get_string_repr()
        ))
        .await
        .map(|c| c.config.parse_enum("RoutingResultSource").ok())
        .unwrap_or(None); //Ignore errors so that we can use the hyperswitch result as a fallback
    if let Some(api_routing::RoutingResultSource::DecisionEngine) = routing_result_source {
        logger::debug!(business_profile_id=?business_profile.get_id(), "Using Decision Engine routing result");
        de_result
    } else {
        logger::debug!(business_profile_id=?business_profile.get_id(), "Using Hyperswitch routing result");
        hyperswitch_result
    }
}
pub trait DecisionEngineErrorsInterface {
    fn get_error_message(&self) -> String;
    fn get_error_code(&self) -> String;
    fn get_error_data(&self) -> Option<String>;
}

#[derive(Debug)]
pub struct RoutingEventsWrapper<Req>
where
    Req: Serialize + Clone,
{
    pub tenant_id: id_type::TenantId,
    pub request_id: Option<RequestId>,
    pub payment_id: String,
    pub profile_id: id_type::ProfileId,
    pub merchant_id: id_type::MerchantId,
    pub flow: String,
    pub request: Option<Req>,
    pub parse_response: bool,
    pub log_event: bool,
    pub routing_event: Option<routing_events::RoutingEvent>,
}

#[derive(Debug)]
pub enum EventResponseType<Res>
where
    Res: Serialize + serde::de::DeserializeOwned + Clone,
{
    Structured(Res),
    String(String),
}

#[derive(Debug, Serialize)]
pub struct RoutingEventsResponse<Res>
where
    Res: Serialize + serde::de::DeserializeOwned + Clone,
{
    pub event: Option<routing_events::RoutingEvent>,
    pub response: Option<Res>,
}

impl<Res> RoutingEventsResponse<Res>
where
    Res: Serialize + serde::de::DeserializeOwned + Clone,
{
    pub fn new(event: Option<routing_events::RoutingEvent>, response: Option<Res>) -> Self {
        Self { event, response }
    }

    pub fn set_response(&mut self, response: Res) {
        self.response = Some(response);
    }

    pub fn set_event(&mut self, event: routing_events::RoutingEvent) {
        self.event = Some(event);
    }
}

impl<Req> RoutingEventsWrapper<Req>
where
    Req: Serialize + Clone,
{
    #[allow(clippy::too_many_arguments)]
    pub fn new(
        tenant_id: id_type::TenantId,
        request_id: Option<RequestId>,
        payment_id: String,
        profile_id: id_type::ProfileId,
        merchant_id: id_type::MerchantId,
        flow: String,
        request: Option<Req>,
        parse_response: bool,
        log_event: bool,
    ) -> Self {
        Self {
            tenant_id,
            request_id,
            payment_id,
            profile_id,
            merchant_id,
            flow,
            request,
            parse_response,
            log_event,
            routing_event: None,
        }
    }

    pub fn construct_event_builder(
        self,
        url: String,
        routing_engine: routing_events::RoutingEngine,
        method: routing_events::ApiMethod,
    ) -> RoutingResult<Self> {
        let mut wrapper = self;
        let request = wrapper
            .request
            .clone()
            .ok_or(errors::RoutingError::RoutingEventsError {
                message: "Request body is missing".to_string(),
                status_code: 400,
            })?;

        let serialized_request = serde_json::to_value(&request)
            .change_context(errors::RoutingError::RoutingEventsError {
                message: "Failed to serialize RoutingRequest".to_string(),
                status_code: 500,
            })
            .attach_printable("Failed to serialize request body")?;

        let routing_event = routing_events::RoutingEvent::new(
            wrapper.tenant_id.clone(),
            "".to_string(),
            &wrapper.flow,
            serialized_request,
            url,
            method,
            wrapper.payment_id.clone(),
            wrapper.profile_id.clone(),
            wrapper.merchant_id.clone(),
            wrapper.request_id,
            routing_engine,
        );

        wrapper.set_routing_event(routing_event);

        Ok(wrapper)
    }

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

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

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

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

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

        response.set_event(routing_event);

        Ok(response)
    }

    pub fn set_log_event(&mut self, log_event: bool) {
        self.log_event = log_event;
    }

    pub fn set_request_body(&mut self, request: Req) {
        self.request = Some(request);
    }

    pub fn set_routing_event(&mut self, routing_event: routing_events::RoutingEvent) {
        self.routing_event = Some(routing_event);
    }
}

pub trait RoutingEventsInterface {
    fn get_routable_connectors(&self) -> Option<Vec<RoutableConnectorChoice>>;
    fn get_payment_connector(&self) -> Option<RoutableConnectorChoice>;
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct CalSuccessRateConfigEventRequest {
    pub min_aggregates_size: Option<u32>,
    pub default_success_rate: Option<f64>,
    pub specificity_level: api_routing::SuccessRateSpecificityLevel,
    pub exploration_percent: Option<f64>,
}

impl From<&api_routing::SuccessBasedRoutingConfigBody> for CalSuccessRateConfigEventRequest {
    fn from(value: &api_routing::SuccessBasedRoutingConfigBody) -> Self {
        Self {
            min_aggregates_size: value.min_aggregates_size,
            default_success_rate: value.default_success_rate,
            specificity_level: value.specificity_level,
            exploration_percent: value.exploration_percent,
        }
    }
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct CalSuccessRateEventRequest {
    pub id: String,
    pub params: String,
    pub labels: Vec<String>,
    pub config: Option<CalSuccessRateConfigEventRequest>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct EliminationRoutingEventBucketConfig {
    pub bucket_size: Option<u64>,
    pub bucket_leak_interval_in_secs: Option<u64>,
}

impl From<&api_routing::EliminationAnalyserConfig> for EliminationRoutingEventBucketConfig {
    fn from(value: &api_routing::EliminationAnalyserConfig) -> Self {
        Self {
            bucket_size: value.bucket_size,
            bucket_leak_interval_in_secs: value.bucket_leak_interval_in_secs,
        }
    }
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct EliminationRoutingEventRequest {
    pub id: String,
    pub params: String,
    pub labels: Vec<String>,
    pub config: Option<EliminationRoutingEventBucketConfig>,
}

/// API-1 types
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct CalContractScoreEventRequest {
    pub id: String,
    pub params: String,
    pub labels: Vec<String>,
    pub config: Option<api_routing::ContractBasedRoutingConfig>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct LabelWithScoreEventResponse {
    pub score: f64,
    pub label: String,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct CalSuccessRateEventResponse {
    pub labels_with_score: Vec<LabelWithScoreEventResponse>,
    pub routing_approach: RoutingApproach,
}

#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
impl TryFrom<&ir_client::success_rate_client::CalSuccessRateResponse>
    for CalSuccessRateEventResponse
{
    type Error = errors::RoutingError;

    fn try_from(
        value: &ir_client::success_rate_client::CalSuccessRateResponse,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            labels_with_score: value
                .labels_with_score
                .iter()
                .map(|l| LabelWithScoreEventResponse {
                    score: l.score,
                    label: l.label.clone(),
                })
                .collect(),
            routing_approach: match value.routing_approach {
                0 => RoutingApproach::Exploration,
                1 => RoutingApproach::Exploitation,
                _ => {
                    return Err(errors::RoutingError::GenericNotFoundError {
                        field: "unknown routing approach from dynamic routing service".to_string(),
                    })
                }
            },
        })
    }
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum RoutingApproach {
    Exploitation,
    Exploration,
    Elimination,
    ContractBased,
    StaticRouting,
    Default,
}

impl RoutingApproach {
    pub fn from_decision_engine_approach(approach: &str) -> Self {
        match approach {
            "SR_SELECTION_V3_ROUTING" => Self::Exploitation,
            "SR_V3_HEDGING" => Self::Exploration,
            _ => Self::Default,
        }
    }
}

impl From<RoutingApproach> for common_enums::RoutingApproach {
    fn from(approach: RoutingApproach) -> Self {
        match approach {
            RoutingApproach::Exploitation => Self::SuccessRateExploitation,
            RoutingApproach::Exploration => Self::SuccessRateExploration,
            RoutingApproach::ContractBased => Self::ContractBasedRouting,
            RoutingApproach::StaticRouting => Self::RuleBasedRouting,
            _ => Self::DefaultFallback,
        }
    }
}

impl std::fmt::Display for RoutingApproach {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::Exploitation => write!(f, "Exploitation"),
            Self::Exploration => write!(f, "Exploration"),
            Self::Elimination => write!(f, "Elimination"),
            Self::ContractBased => write!(f, "ContractBased"),
            Self::StaticRouting => write!(f, "StaticRouting"),
            Self::Default => write!(f, "Default"),
        }
    }
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct BucketInformationEventResponse {
    pub is_eliminated: bool,
    pub bucket_name: Vec<String>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct EliminationInformationEventResponse {
    pub entity: Option<BucketInformationEventResponse>,
    pub global: Option<BucketInformationEventResponse>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct LabelWithStatusEliminationEventResponse {
    pub label: String,
    pub elimination_information: Option<EliminationInformationEventResponse>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct EliminationEventResponse {
    pub labels_with_status: Vec<LabelWithStatusEliminationEventResponse>,
}

#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
impl From<&ir_client::elimination_based_client::EliminationResponse> for EliminationEventResponse {
    fn from(value: &ir_client::elimination_based_client::EliminationResponse) -> Self {
        Self {
            labels_with_status: value
                .labels_with_status
                .iter()
                .map(
                    |label_with_status| LabelWithStatusEliminationEventResponse {
                        label: label_with_status.label.clone(),
                        elimination_information: label_with_status
                            .elimination_information
                            .as_ref()
                            .map(|info| EliminationInformationEventResponse {
                                entity: info.entity.as_ref().map(|entity_info| {
                                    BucketInformationEventResponse {
                                        is_eliminated: entity_info.is_eliminated,
                                        bucket_name: entity_info.bucket_name.clone(),
                                    }
                                }),
                                global: info.global.as_ref().map(|global_info| {
                                    BucketInformationEventResponse {
                                        is_eliminated: global_info.is_eliminated,
                                        bucket_name: global_info.bucket_name.clone(),
                                    }
                                }),
                            }),
                    },
                )
                .collect(),
        }
    }
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct ScoreDataEventResponse {
    pub score: f64,
    pub label: String,
    pub current_count: u64,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct CalContractScoreEventResponse {
    pub labels_with_score: Vec<ScoreDataEventResponse>,
}

#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
impl From<&ir_client::contract_routing_client::CalContractScoreResponse>
    for CalContractScoreEventResponse
{
    fn from(value: &ir_client::contract_routing_client::CalContractScoreResponse) -> Self {
        Self {
            labels_with_score: value
                .labels_with_score
                .iter()
                .map(|label_with_score| ScoreDataEventResponse {
                    score: label_with_score.score,
                    label: label_with_score.label.clone(),
                    current_count: label_with_score.current_count,
                })
                .collect(),
        }
    }
}

#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct CalGlobalSuccessRateConfigEventRequest {
    pub entity_min_aggregates_size: u32,
    pub entity_default_success_rate: f64,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct CalGlobalSuccessRateEventRequest {
    pub entity_id: String,
    pub entity_params: String,
    pub entity_labels: Vec<String>,
    pub global_labels: Vec<String>,
    pub config: Option<CalGlobalSuccessRateConfigEventRequest>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct UpdateSuccessRateWindowConfig {
    pub max_aggregates_size: Option<u32>,
    pub current_block_threshold: Option<api_routing::CurrentBlockThreshold>,
}

impl From<&api_routing::SuccessBasedRoutingConfigBody> for UpdateSuccessRateWindowConfig {
    fn from(value: &api_routing::SuccessBasedRoutingConfigBody) -> Self {
        Self {
            max_aggregates_size: value.max_aggregates_size,
            current_block_threshold: value.current_block_threshold.clone(),
        }
    }
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct UpdateLabelWithStatusEventRequest {
    pub label: String,
    pub status: bool,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct UpdateSuccessRateWindowEventRequest {
    pub id: String,
    pub params: String,
    pub labels_with_status: Vec<UpdateLabelWithStatusEventRequest>,
    pub config: Option<UpdateSuccessRateWindowConfig>,
    pub global_labels_with_status: Vec<UpdateLabelWithStatusEventRequest>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct UpdateSuccessRateWindowEventResponse {
    pub status: UpdationStatusEventResponse,
}

#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
impl TryFrom<&ir_client::success_rate_client::UpdateSuccessRateWindowResponse>
    for UpdateSuccessRateWindowEventResponse
{
    type Error = errors::RoutingError;

    fn try_from(
        value: &ir_client::success_rate_client::UpdateSuccessRateWindowResponse,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            status: match value.status {
                0 => UpdationStatusEventResponse::WindowUpdationSucceeded,
                1 => UpdationStatusEventResponse::WindowUpdationFailed,
                _ => {
                    return Err(errors::RoutingError::GenericNotFoundError {
                        field: "unknown updation status from dynamic routing service".to_string(),
                    })
                }
            },
        })
    }
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum UpdationStatusEventResponse {
    WindowUpdationSucceeded,
    WindowUpdationFailed,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct LabelWithBucketNameEventRequest {
    pub label: String,
    pub bucket_name: String,
}

impl From<&api_routing::RoutableConnectorChoiceWithBucketName> for LabelWithBucketNameEventRequest {
    fn from(value: &api_routing::RoutableConnectorChoiceWithBucketName) -> Self {
        Self {
            label: value.routable_connector_choice.to_string(),
            bucket_name: value.bucket_name.clone(),
        }
    }
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct UpdateEliminationBucketEventRequest {
    pub id: String,
    pub params: String,
    pub labels_with_bucket_name: Vec<LabelWithBucketNameEventRequest>,
    pub config: Option<EliminationRoutingEventBucketConfig>,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct UpdateEliminationBucketEventResponse {
    pub status: EliminationUpdationStatusEventResponse,
}

#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
impl TryFrom<&ir_client::elimination_based_client::UpdateEliminationBucketResponse>
    for UpdateEliminationBucketEventResponse
{
    type Error = errors::RoutingError;

    fn try_from(
        value: &ir_client::elimination_based_client::UpdateEliminationBucketResponse,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            status: match value.status {
                0 => EliminationUpdationStatusEventResponse::BucketUpdationSucceeded,
                1 => EliminationUpdationStatusEventResponse::BucketUpdationFailed,
                _ => {
                    return Err(errors::RoutingError::GenericNotFoundError {
                        field: "unknown updation status from dynamic routing service".to_string(),
                    })
                }
            },
        })
    }
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum EliminationUpdationStatusEventResponse {
    BucketUpdationSucceeded,
    BucketUpdationFailed,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct ContractLabelInformationEventRequest {
    pub label: String,
    pub target_count: u64,
    pub target_time: u64,
    pub current_count: u64,
}

impl From<&api_routing::LabelInformation> for ContractLabelInformationEventRequest {
    fn from(value: &api_routing::LabelInformation) -> Self {
        Self {
            label: value.label.clone(),
            target_count: value.target_count,
            target_time: value.target_time,
            current_count: 1,
        }
    }
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct UpdateContractRequestEventRequest {
    pub id: String,
    pub params: String,
    pub labels_information: Vec<ContractLabelInformationEventRequest>,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct UpdateContractEventResponse {
    pub status: ContractUpdationStatusEventResponse,
}

#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
impl TryFrom<&ir_client::contract_routing_client::UpdateContractResponse>
    for UpdateContractEventResponse
{
    type Error = errors::RoutingError;

    fn try_from(
        value: &ir_client::contract_routing_client::UpdateContractResponse,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            status: match value.status {
                0 => ContractUpdationStatusEventResponse::ContractUpdationSucceeded,
                1 => ContractUpdationStatusEventResponse::ContractUpdationFailed,
                _ => {
                    return Err(errors::RoutingError::GenericNotFoundError {
                        field: "unknown updation status from dynamic routing service".to_string(),
                    })
                }
            },
        })
    }
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ContractUpdationStatusEventResponse {
    ContractUpdationSucceeded,
    ContractUpdationFailed,
}


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

use api_models::payments::{Address, AddressDetails};
use masking::Secret;
use router::types::{self, api, storage::enums, PaymentAddress};
use test_utils::connector_auth;

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

#[derive(Clone, Copy)]
struct NomupayTest;
impl ConnectorActions for NomupayTest {}

impl utils::Connector for NomupayTest {
    fn get_data(&self) -> api::ConnectorData {
        use router::connector::Nomupay;
        utils::construct_connector_data_old(
            Box::new(Nomupay::new()),
            types::Connector::Plaid,
            api::GetToken::Connector,
            None,
        )
    }

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

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

impl NomupayTest {
    fn get_payout_info() -> Option<PaymentInfo> {
        Some(PaymentInfo {
            currency: Some(enums::Currency::GBP),
            address: Some(PaymentAddress::new(
                None,
                Some(
                    Address {
                        address: Some(AddressDetails {
                            country: Some(api_models::enums::CountryAlpha2::GB),
                            city: Some("London".to_string()),
                            zip: Some(Secret::new("10025".to_string())),
                            line1: Some(Secret::new("50 Branson Ave".to_string())),
                            ..Default::default()
                        }),
                        phone: None,
                        email: None,
                    }
                    .into(),
                ),
                None,
                None,
            )),
            payout_method_data: Some(api::PayoutMethodData::Bank(api::payouts::BankPayout::Sepa(
                api::SepaBankTransfer {
                    bank_name: Some("Deutsche Bank".to_string()),
                    bank_country_code: Some(enums::CountryAlpha2::DE),
                    bank_city: Some("Munich".to_string()),
                    iban: Secret::new("DE57331060435647542639".to_string()),
                    bic: Some(Secret::new("DEUTDE5M551".to_string())),
                },
            ))),
            ..Default::default()
        })
    }
}

static CONNECTOR: NomupayTest = NomupayTest {};

/******************** Payouts test cases ********************/
// Creates a recipient at connector's end

#[actix_web::test]
async fn should_create_payout_recipient() {
    let payout_type = enums::PayoutType::Bank;
    let payment_info = NomupayTest::get_payout_info();
    let response = CONNECTOR
        .create_payout_recipient(payout_type, payment_info)
        .await
        .expect("Payout recipient creation response");
    assert_eq!(
        response.status.unwrap(),
        enums::PayoutStatus::RequiresCreation
    );
}

// Create SEPA payout

#[actix_web::test]
async fn should_create_bacs_payout() {
    let payout_type = enums::PayoutType::Bank;
    let payout_info = NomupayTest::get_payout_info();
    // Create recipient
    let recipient_res = CONNECTOR
        .create_payout_recipient(payout_type.to_owned(), payout_info.to_owned())
        .await
        .expect("Payout recipient response");
    assert_eq!(
        recipient_res.status.unwrap(),
        enums::PayoutStatus::RequiresCreation
    );

    // Create payout
    let create_res: types::PayoutsResponseData = CONNECTOR
        .create_payout(recipient_res.connector_payout_id, payout_type, payout_info)
        .await
        .expect("Payout bank creation response");
    assert_eq!(
        create_res.status.unwrap(),
        enums::PayoutStatus::RequiresFulfillment
    );
}

// Create and fulfill SEPA payout

#[actix_web::test]
async fn should_create_and_fulfill_bacs_payout() {
    let payout_type = enums::PayoutType::Bank;
    let payout_info = NomupayTest::get_payout_info();
    // Create recipient
    let recipient_res = CONNECTOR
        .create_payout_recipient(payout_type.to_owned(), payout_info.to_owned())
        .await
        .expect("Payout recipient response");
    assert_eq!(
        recipient_res.status.unwrap(),
        enums::PayoutStatus::RequiresCreation
    );
    let response = CONNECTOR
        .create_and_fulfill_payout(recipient_res.connector_payout_id, payout_type, payout_info)
        .await
        .expect("Payout bank creation and fulfill response");
    assert_eq!(response.status.unwrap(), enums::PayoutStatus::Success);
}


pub struct IatapayAuthType {
    pub(super) client_id: Secret<String>,
    pub(super) merchant_id: Secret<String>,
    pub(super) client_secret: Secret<String>,
}


pub struct DisputesAggregateResponse {
    /// Different status of disputes with their count
    pub status_with_count: HashMap<DisputeStatus, i64>,
}


pub struct KlarnaMeta {
    capture_id: Option<String>,
}


Documentation: api-reference/v2/merchant-account/merchant-account--create.mdx
# Type: Doc File

---
openapi: post /v2/merchant-accounts
---


impl<Secret: ZeroizableSecret, MaskingStrategy> StrongSecret<Secret, MaskingStrategy> {
    /// Take ownership of a secret value
    pub fn new(secret: Secret) -> Self {
        Self {
            inner_secret: secret,
            masking_strategy: PhantomData,
        }
    }
}


OpenAPI Block Path: components.schemas.OnlineMandate
{
  "type": "object",
  "description": "Details of online mandate",
  "required": [
    "ip_address",
    "user_agent"
  ],
  "properties": {
    "ip_address": {
      "type": "string",
      "description": "Ip address of the customer machine from which the mandate was created",
      "example": "123.32.25.123"
    },
    "user_agent": {
      "type": "string",
      "description": "The user-agent of the customer's browser"
    }
  },
  "additionalProperties": false
}


impl Parse for OperationsEnumMeta {
    fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
        let lookahead = input.lookahead1();
        if lookahead.peek(operations_keyword::operations) {
            let keyword = input.parse()?;
            input.parse::<syn::Token![=]>()?;
            let value = get_conversions(input)?;
            Ok(Self::Operations { keyword, value })
        } else if lookahead.peek(operations_keyword::flow) {
            let keyword = input.parse()?;
            input.parse::<syn::Token![=]>()?;
            let value = get_derives(input)?;
            Ok(Self::Flow { keyword, value })
        } else {
            Err(lookahead.error())
        }
    }
}


OpenAPI Block Path: components.schemas.PaymentsSessionResponse
{
  "type": "object",
  "required": [
    "payment_id",
    "client_secret",
    "session_token"
  ],
  "properties": {
    "payment_id": {
      "type": "string",
      "description": "The identifier for the payment"
    },
    "client_secret": {
      "type": "string",
      "description": "This is a token which expires after 15 minutes, used from the client to authenticate and create sessions from the SDK"
    },
    "session_token": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/SessionToken"
      },
      "description": "The list of session token object"
    }
  }
}


pub struct EbanxAuthType {
    pub integration_key: Secret<String>,
}


File: crates/analytics/src/auth_events.rs

pub mod accumulator;
mod core;
pub mod filters;
pub mod metrics;
pub mod sankey;
pub mod types;
pub use accumulator::{AuthEventMetricAccumulator, AuthEventMetricsAccumulator};

pub use self::core::{get_filters, get_metrics, get_sankey};


pub struct ExtendedAuthorizationResponseData {
    pub extended_authentication_applied:
        Option<primitive_wrappers::ExtendedAuthorizationAppliedBool>,
    pub capture_before: Option<time::PrimitiveDateTime>,
}


pub struct PaymentMethodsEnabledForConnector {
    pub payment_methods_enabled: common_types::payment_methods::RequestPaymentMethodTypes,
    pub payment_method: common_enums::PaymentMethod,
    pub connector: common_enums::connector_enums::Connector,
    pub merchant_connector_id: id_type::MerchantConnectorAccountId,
}


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

    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.facilitapay.base_url.as_ref()
    }

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

impl ConnectorCommon for for Facilitapay


impl BatchSampleDataInterface for KafkaStore {
    #[cfg(feature = "v1")]
    async fn insert_payment_intents_batch_for_sample_data(
        &self,
        state: &KeyManagerState,
        batch: Vec<hyperswitch_domain_models::payments::PaymentIntent>,
        key_store: &hyperswitch_domain_models::merchant_key_store::MerchantKeyStore,
    ) -> CustomResult<
        Vec<hyperswitch_domain_models::payments::PaymentIntent>,
        storage_impl::errors::StorageError,
    > {
        let payment_intents_list = self
            .diesel_store
            .insert_payment_intents_batch_for_sample_data(state, batch, key_store)
            .await?;

        for payment_intent in payment_intents_list.iter() {
            let _ = self
                .kafka_producer
                .log_payment_intent(
                    payment_intent,
                    None,
                    self.tenant_id.clone(),
                    state.infra_values.clone(),
                )
                .await;
        }
        Ok(payment_intents_list)
    }

    #[cfg(feature = "v1")]
    async fn insert_payment_attempts_batch_for_sample_data(
        &self,
        batch: Vec<diesel_models::user::sample_data::PaymentAttemptBatchNew>,
    ) -> CustomResult<
        Vec<hyperswitch_domain_models::payments::payment_attempt::PaymentAttempt>,
        storage_impl::errors::StorageError,
    > {
        let payment_attempts_list = self
            .diesel_store
            .insert_payment_attempts_batch_for_sample_data(batch)
            .await?;

        for payment_attempt in payment_attempts_list.iter() {
            let _ = self
                .kafka_producer
                .log_payment_attempt(payment_attempt, None, self.tenant_id.clone())
                .await;
        }
        Ok(payment_attempts_list)
    }

    #[cfg(feature = "v1")]
    async fn insert_refunds_batch_for_sample_data(
        &self,
        batch: Vec<diesel_models::RefundNew>,
    ) -> CustomResult<Vec<diesel_models::Refund>, storage_impl::errors::StorageError> {
        let refunds_list = self
            .diesel_store
            .insert_refunds_batch_for_sample_data(batch)
            .await?;

        for refund in refunds_list.iter() {
            let _ = self
                .kafka_producer
                .log_refund(refund, None, self.tenant_id.clone())
                .await;
        }
        Ok(refunds_list)
    }

    #[cfg(feature = "v1")]
    async fn insert_disputes_batch_for_sample_data(
        &self,
        batch: Vec<diesel_models::DisputeNew>,
    ) -> CustomResult<Vec<diesel_models::Dispute>, storage_impl::errors::StorageError> {
        let disputes_list = self
            .diesel_store
            .insert_disputes_batch_for_sample_data(batch)
            .await?;

        for dispute in disputes_list.iter() {
            let _ = self
                .kafka_producer
                .log_dispute(dispute, None, self.tenant_id.clone())
                .await;
        }
        Ok(disputes_list)
    }

    #[cfg(feature = "v1")]
    async fn delete_payment_intents_for_sample_data(
        &self,
        state: &KeyManagerState,
        merchant_id: &id_type::MerchantId,
        key_store: &hyperswitch_domain_models::merchant_key_store::MerchantKeyStore,
    ) -> CustomResult<
        Vec<hyperswitch_domain_models::payments::PaymentIntent>,
        storage_impl::errors::StorageError,
    > {
        let payment_intents_list = self
            .diesel_store
            .delete_payment_intents_for_sample_data(state, merchant_id, key_store)
            .await?;

        for payment_intent in payment_intents_list.iter() {
            let _ = self
                .kafka_producer
                .log_payment_intent_delete(
                    payment_intent,
                    self.tenant_id.clone(),
                    state.infra_values.clone(),
                )
                .await;
        }
        Ok(payment_intents_list)
    }

    #[cfg(feature = "v1")]
    async fn delete_payment_attempts_for_sample_data(
        &self,
        merchant_id: &id_type::MerchantId,
    ) -> CustomResult<
        Vec<hyperswitch_domain_models::payments::payment_attempt::PaymentAttempt>,
        storage_impl::errors::StorageError,
    > {
        let payment_attempts_list = self
            .diesel_store
            .delete_payment_attempts_for_sample_data(merchant_id)
            .await?;

        for payment_attempt in payment_attempts_list.iter() {
            let _ = self
                .kafka_producer
                .log_payment_attempt_delete(payment_attempt, self.tenant_id.clone())
                .await;
        }

        Ok(payment_attempts_list)
    }

    #[cfg(feature = "v1")]
    async fn delete_refunds_for_sample_data(
        &self,
        merchant_id: &id_type::MerchantId,
    ) -> CustomResult<Vec<diesel_models::Refund>, storage_impl::errors::StorageError> {
        let refunds_list = self
            .diesel_store
            .delete_refunds_for_sample_data(merchant_id)
            .await?;

        for refund in refunds_list.iter() {
            let _ = self
                .kafka_producer
                .log_refund_delete(refund, self.tenant_id.clone())
                .await;
        }

        Ok(refunds_list)
    }

    #[cfg(feature = "v1")]
    async fn delete_disputes_for_sample_data(
        &self,
        merchant_id: &id_type::MerchantId,
    ) -> CustomResult<Vec<diesel_models::Dispute>, storage_impl::errors::StorageError> {
        let disputes_list = self
            .diesel_store
            .delete_disputes_for_sample_data(merchant_id)
            .await?;

        for dispute in disputes_list.iter() {
            let _ = self
                .kafka_producer
                .log_dispute_delete(dispute, self.tenant_id.clone())
                .await;
        }

        Ok(disputes_list)
    }
}

impl BatchSampleDataInterface for for KafkaStore


pub struct KlarnaCheckoutSyncResponse {
    pub order_id: String,
    pub status: KlarnaCheckoutStatus,
    pub options: CheckoutOptions,
}


pub struct GooglePayPaymentInformation {
    fluid_data: FluidData,
}


impl api::PaymentsCompleteAuthorize for Deutschebank {}

impl api::PaymentsCompleteAuthorize for for Deutschebank


#[macro_export]
macro_rules! unimplemented_payment_method {
    ($payment_method:expr, $connector:expr) => {
        errors::ConnectorError::NotImplemented(format!(
            "{} through {}",
            $payment_method, $connector
        ))
    };
    ($payment_method:expr, $flow:expr, $connector:expr) => {
        errors::ConnectorError::NotImplemented(format!(
            "{} {} through {}",
            $payment_method, $flow, $connector
        ))
    };
}


pub struct ApiKeyExpiryTrackingData {
    pub key_id: common_utils::id_type::ApiKeyId,
    pub merchant_id: common_utils::id_type::MerchantId,
    pub api_key_name: String,
    pub prefix: String,
    pub api_key_expiry: Option<PrimitiveDateTime>,
    // Days on which email reminder about api_key expiry has to be sent, prior to it's expiry.
    pub expiry_reminder_days: Vec<u8>,
}


pub struct PaypalAccessTokenErrorResponse {
    pub error: String,
    pub error_description: String,
}


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

pub use diesel_models::merchant_account::{
    MerchantAccount, MerchantAccountNew, MerchantAccountUpdateInternal,
};

pub use crate::types::domain::MerchantAccountUpdate;


pub struct AuthorizedotnetVoidResponse {
    pub transaction_response: Option<VoidResponse>,
    pub messages: ResponseMessages,
}


pub struct RiskifiedFulfilmentResponse {
    order: OrderFulfilmentResponse,
}


impl Config {
    /// Default constructor.
    pub fn new() -> Result<Self, config::ConfigError> {
        Self::new_with_config_path(None)
    }

    /// Constructor expecting config path set explicitly.
    pub fn new_with_config_path(
        explicit_config_path: Option<PathBuf>,
    ) -> Result<Self, config::ConfigError> {
        // Configuration values are picked up in the following priority order (1 being least
        // priority):
        // 1. Defaults from the implementation of the `Default` trait.
        // 2. Values from config file. The config file accessed depends on the environment
        //    specified by the `RUN_ENV` environment variable. `RUN_ENV` can be one of
        //    `development`, `sandbox` or `production`. If nothing is specified for `RUN_ENV`,
        //    `/config/development.toml` file is read.
        // 3. Environment variables prefixed with `ROUTER` and each level separated by double
        //    underscores.
        //
        // Values in config file override the defaults in `Default` trait, and the values set using
        // environment variables override both the defaults and the config file values.

        let environment = crate::env::which();
        let config_path = Self::config_path(&environment.to_string(), explicit_config_path);

        let config = Self::builder(&environment.to_string())?
            .add_source(config::File::from(config_path).required(false))
            .add_source(config::Environment::with_prefix("ROUTER").separator("__"))
            .build()?;

        // The logger may not yet be initialized when constructing the application configuration
        #[allow(clippy::print_stderr)]
        serde_path_to_error::deserialize(config).map_err(|error| {
            crate::error!(%error, "Unable to deserialize configuration");
            eprintln!("Unable to deserialize application configuration: {error}");
            error.into_inner()
        })
    }

    /// Construct config builder extending it by fall-back defaults and setting config file to load.
    pub fn builder(
        environment: &str,
    ) -> Result<config::ConfigBuilder<config::builder::DefaultState>, config::ConfigError> {
        config::Config::builder()
            // Here, it should be `set_override()` not `set_default()`.
            // "env" can't be altered by config field.
            // Should be single source of truth.
            .set_override("env", environment)
    }

    /// Config path.
    pub fn config_path(environment: &str, explicit_config_path: Option<PathBuf>) -> PathBuf {
        let mut config_path = PathBuf::new();
        if let Some(explicit_config_path_val) = explicit_config_path {
            config_path.push(explicit_config_path_val);
        } else {
            let config_file_name = match environment {
                "production" => "production.toml",
                "sandbox" => "sandbox.toml",
                _ => "development.toml",
            };

            let config_directory = Self::get_config_directory();
            config_path.push(config_directory);
            config_path.push(config_file_name);
        }
        config_path
    }

    /// Get the Directory for the config file
    /// Read the env variable `CONFIG_DIR` or fallback to `config`
    pub fn get_config_directory() -> PathBuf {
        let mut config_path = PathBuf::new();

        let config_directory =
            std::env::var(crate::env::vars::CONFIG_DIR).unwrap_or_else(|_| "config".into());

        config_path.push(crate::env::workspace_path());
        config_path.push(config_directory);
        config_path
    }
}


pub async fn update_by_merchant_id_key_id(
        conn: &PgPooledConn,
        merchant_id: common_utils::id_type::MerchantId,
        key_id: common_utils::id_type::ApiKeyId,
        api_key_update: ApiKeyUpdate,
    ) -> StorageResult<Self> {
        match generics::generic_update_with_unique_predicate_get_result::<
            <Self as HasTable>::Table,
            _,
            _,
            _,
        >(
            conn,
            dsl::merchant_id
                .eq(merchant_id.to_owned())
                .and(dsl::key_id.eq(key_id.to_owned())),
            ApiKeyUpdateInternal::from(api_key_update),
        )
        .await
        {
            Err(error) => match error.current_context() {
                errors::DatabaseError::NotFound => {
                    Err(error.attach_printable("API key with the given key ID does not exist"))
                }
                errors::DatabaseError::NoFieldsToUpdate => {
                    generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
                        conn,
                        dsl::merchant_id
                            .eq(merchant_id.to_owned())
                            .and(dsl::key_id.eq(key_id.to_owned())),
                    )
                    .await
                }
                _ => Err(error),
            },
            result => result,
        }
    }


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

#[cfg(feature = "email")]
pub use diesel_models::api_keys::ApiKeyExpiryTrackingData;
pub use diesel_models::api_keys::{ApiKey, ApiKeyNew, ApiKeyUpdate, HashedApiKey};


impl api::MandateSetup for Cashtocode {}

impl api::MandateSetup for for Cashtocode


pub fn new(org_new: OrganizationNew) -> Self {
        let OrganizationNew {
            org_id,
            org_name,
            organization_details,
            metadata,
            created_at,
            modified_at,
            id: _,
            organization_name: _,
            version,
            organization_type,
            platform_merchant_id,
        } = org_new;
        Self {
            id: Some(org_id.clone()),
            organization_name: org_name.clone(),
            org_id,
            org_name,
            organization_details,
            metadata,
            created_at,
            modified_at,
            version,
            organization_type: Some(organization_type),
            platform_merchant_id,
        }
    }


pub async fn find_by_theme_id(conn: &PgPooledConn, theme_id: String) -> StorageResult<Self> {
        generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::theme_id.eq(theme_id),
        )
        .await
    }


impl PaymentAttempt {
    #[cfg(feature = "v1")]
    pub async fn update_with_attempt_id(
        self,
        conn: &PgPooledConn,
        payment_attempt: PaymentAttemptUpdate,
    ) -> StorageResult<Self> {
        match generics::generic_update_with_unique_predicate_get_result::<
            <Self as HasTable>::Table,
            _,
            _,
            _,
        >(
            conn,
            dsl::attempt_id
                .eq(self.attempt_id.to_owned())
                .and(dsl::merchant_id.eq(self.merchant_id.to_owned())),
            PaymentAttemptUpdateInternal::from(payment_attempt).populate_derived_fields(&self),
        )
        .await
        {
            Err(error) => match error.current_context() {
                DatabaseError::NoFieldsToUpdate => Ok(self),
                _ => Err(error),
            },
            result => result,
        }
    }

    #[cfg(feature = "v2")]
    pub async fn update_with_attempt_id(
        self,
        conn: &PgPooledConn,
        payment_attempt: PaymentAttemptUpdateInternal,
    ) -> StorageResult<Self> {
        match generics::generic_update_with_unique_predicate_get_result::<
            <Self as HasTable>::Table,
            _,
            _,
            _,
        >(conn, dsl::id.eq(self.id.to_owned()), payment_attempt)
        .await
        {
            Err(error) => match error.current_context() {
                DatabaseError::NoFieldsToUpdate => Ok(self),
                _ => Err(error),
            },
            result => result,
        }
    }

    #[cfg(feature = "v1")]
    pub async fn find_optional_by_payment_id_merchant_id(
        conn: &PgPooledConn,
        payment_id: &common_utils::id_type::PaymentId,
        merchant_id: &common_utils::id_type::MerchantId,
    ) -> StorageResult<Option<Self>> {
        generics::generic_find_one_optional::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::merchant_id
                .eq(merchant_id.to_owned())
                .and(dsl::payment_id.eq(payment_id.to_owned())),
        )
        .await
    }

    #[cfg(feature = "v1")]
    pub async fn find_by_connector_transaction_id_payment_id_merchant_id(
        conn: &PgPooledConn,
        connector_transaction_id: &common_utils::types::ConnectorTransactionId,
        payment_id: &common_utils::id_type::PaymentId,
        merchant_id: &common_utils::id_type::MerchantId,
    ) -> StorageResult<Self> {
        generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::connector_transaction_id
                .eq(connector_transaction_id.get_id().to_owned())
                .and(dsl::payment_id.eq(payment_id.to_owned()))
                .and(dsl::merchant_id.eq(merchant_id.to_owned())),
        )
        .await
    }

    #[cfg(feature = "v1")]
    pub async fn find_last_successful_attempt_by_payment_id_merchant_id(
        conn: &PgPooledConn,
        payment_id: &common_utils::id_type::PaymentId,
        merchant_id: &common_utils::id_type::MerchantId,
    ) -> StorageResult<Self> {
        // perform ordering on the application level instead of database level
        generics::generic_filter::<<Self as HasTable>::Table, _, _, Self>(
            conn,
            dsl::payment_id
                .eq(payment_id.to_owned())
                .and(dsl::merchant_id.eq(merchant_id.to_owned()))
                .and(dsl::status.eq(enums::AttemptStatus::Charged)),
            Some(1),
            None,
            Some(dsl::modified_at.desc()),
        )
        .await?
        .into_iter()
        .nth(0)
        .ok_or(report!(DatabaseError::NotFound))
    }

    #[cfg(feature = "v1")]
    pub async fn find_last_successful_or_partially_captured_attempt_by_payment_id_merchant_id(
        conn: &PgPooledConn,
        payment_id: &common_utils::id_type::PaymentId,
        merchant_id: &common_utils::id_type::MerchantId,
    ) -> StorageResult<Self> {
        // perform ordering on the application level instead of database level
        generics::generic_filter::<<Self as HasTable>::Table, _, _, Self>(
            conn,
            dsl::payment_id
                .eq(payment_id.to_owned())
                .and(dsl::merchant_id.eq(merchant_id.to_owned()))
                .and(
                    dsl::status
                        .eq(enums::AttemptStatus::Charged)
                        .or(dsl::status.eq(enums::AttemptStatus::PartialCharged)),
                ),
            Some(1),
            None,
            Some(dsl::modified_at.desc()),
        )
        .await?
        .into_iter()
        .nth(0)
        .ok_or(report!(DatabaseError::NotFound))
    }

    #[cfg(feature = "v2")]
    pub async fn find_last_successful_or_partially_captured_attempt_by_payment_id(
        conn: &PgPooledConn,
        payment_id: &common_utils::id_type::GlobalPaymentId,
    ) -> StorageResult<Self> {
        // perform ordering on the application level instead of database level
        generics::generic_filter::<<Self as HasTable>::Table, _, _, Self>(
            conn,
            dsl::payment_id.eq(payment_id.to_owned()).and(
                dsl::status
                    .eq(enums::AttemptStatus::Charged)
                    .or(dsl::status.eq(enums::AttemptStatus::PartialCharged)),
            ),
            Some(1),
            None,
            Some(dsl::modified_at.desc()),
        )
        .await?
        .into_iter()
        .nth(0)
        .ok_or(report!(DatabaseError::NotFound))
    }

    #[cfg(feature = "v1")]
    pub async fn find_by_merchant_id_connector_txn_id(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        connector_txn_id: &str,
    ) -> StorageResult<Self> {
        let (txn_id, txn_data) = common_utils::types::ConnectorTransactionId::form_id_and_data(
            connector_txn_id.to_string(),
        );
        let connector_transaction_id = txn_id
            .get_txn_id(txn_data.as_ref())
            .change_context(DatabaseError::Others)
            .attach_printable_lazy(|| {
                format!("Failed to retrieve txn_id for ({txn_id:?}, {txn_data:?})")
            })?;
        generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::merchant_id
                .eq(merchant_id.to_owned())
                .and(dsl::connector_transaction_id.eq(connector_transaction_id.to_owned())),
        )
        .await
    }

    #[cfg(feature = "v2")]
    pub async fn find_by_profile_id_connector_transaction_id(
        conn: &PgPooledConn,
        profile_id: &common_utils::id_type::ProfileId,
        connector_txn_id: &str,
    ) -> StorageResult<Self> {
        let (txn_id, txn_data) = common_utils::types::ConnectorTransactionId::form_id_and_data(
            connector_txn_id.to_string(),
        );
        let connector_transaction_id = txn_id
            .get_txn_id(txn_data.as_ref())
            .change_context(DatabaseError::Others)
            .attach_printable_lazy(|| {
                format!("Failed to retrieve txn_id for ({txn_id:?}, {txn_data:?})")
            })?;
        generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::profile_id
                .eq(profile_id.to_owned())
                .and(dsl::connector_payment_id.eq(connector_transaction_id.to_owned())),
        )
        .await
    }

    #[cfg(feature = "v1")]
    pub async fn find_by_merchant_id_attempt_id(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        attempt_id: &str,
    ) -> StorageResult<Self> {
        generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::merchant_id
                .eq(merchant_id.to_owned())
                .and(dsl::attempt_id.eq(attempt_id.to_owned())),
        )
        .await
    }

    #[cfg(feature = "v2")]
    pub async fn find_by_id(
        conn: &PgPooledConn,
        id: &common_utils::id_type::GlobalAttemptId,
    ) -> StorageResult<Self> {
        generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::id.eq(id.to_owned()),
        )
        .await
    }

    #[cfg(feature = "v2")]
    pub async fn find_by_payment_id(
        conn: &PgPooledConn,
        payment_id: &common_utils::id_type::GlobalPaymentId,
    ) -> StorageResult<Vec<Self>> {
        generics::generic_filter::<<Self as HasTable>::Table, _, _, _>(
            conn,
            dsl::payment_id.eq(payment_id.to_owned()),
            None,
            None,
            Some(dsl::created_at.asc()),
        )
        .await
    }

    #[cfg(feature = "v1")]
    pub async fn find_by_merchant_id_preprocessing_id(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        preprocessing_id: &str,
    ) -> StorageResult<Self> {
        generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::merchant_id
                .eq(merchant_id.to_owned())
                .and(dsl::preprocessing_step_id.eq(preprocessing_id.to_owned())),
        )
        .await
    }

    #[cfg(feature = "v1")]
    pub async fn find_by_payment_id_merchant_id_attempt_id(
        conn: &PgPooledConn,
        payment_id: &common_utils::id_type::PaymentId,
        merchant_id: &common_utils::id_type::MerchantId,
        attempt_id: &str,
    ) -> StorageResult<Self> {
        generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::payment_id.eq(payment_id.to_owned()).and(
                dsl::merchant_id
                    .eq(merchant_id.to_owned())
                    .and(dsl::attempt_id.eq(attempt_id.to_owned())),
            ),
        )
        .await
    }

    #[cfg(feature = "v1")]
    pub async fn find_by_merchant_id_payment_id(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        payment_id: &common_utils::id_type::PaymentId,
    ) -> StorageResult<Vec<Self>> {
        generics::generic_filter::<
            <Self as HasTable>::Table,
            _,
            <<Self as HasTable>::Table as Table>::PrimaryKey,
            _,
        >(
            conn,
            dsl::merchant_id
                .eq(merchant_id.to_owned())
                .and(dsl::payment_id.eq(payment_id.to_owned())),
            None,
            None,
            None,
        )
        .await
    }

    #[cfg(feature = "v1")]
    pub async fn get_filters_for_payments(
        conn: &PgPooledConn,
        pi: &[PaymentIntent],
        merchant_id: &common_utils::id_type::MerchantId,
    ) -> StorageResult<(
        Vec<String>,
        Vec<enums::Currency>,
        Vec<IntentStatus>,
        Vec<enums::PaymentMethod>,
        Vec<enums::PaymentMethodType>,
        Vec<enums::AuthenticationType>,
    )> {
        let active_attempts: Vec<String> = pi
            .iter()
            .map(|payment_intent| payment_intent.clone().active_attempt_id)
            .collect();

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

        let intent_status: Vec<IntentStatus> = pi
            .iter()
            .map(|payment_intent| payment_intent.status)
            .collect::<HashSet<IntentStatus>>()
            .into_iter()
            .collect();

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

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

        let filter_payment_method = filter
            .clone()
            .select(dsl::payment_method)
            .distinct()
            .get_results_async::<Option<enums::PaymentMethod>>(conn)
            .await
            .change_context(DatabaseError::Others)
            .attach_printable("Error filtering records by payment method")?
            .into_iter()
            .flatten()
            .collect::<Vec<enums::PaymentMethod>>();

        let filter_payment_method_type = filter
            .clone()
            .select(dsl::payment_method_type)
            .distinct()
            .get_results_async::<Option<enums::PaymentMethodType>>(conn)
            .await
            .change_context(DatabaseError::Others)
            .attach_printable("Error filtering records by payment method type")?
            .into_iter()
            .flatten()
            .collect::<Vec<enums::PaymentMethodType>>();

        let filter_authentication_type = filter
            .clone()
            .select(dsl::authentication_type)
            .distinct()
            .get_results_async::<Option<enums::AuthenticationType>>(conn)
            .await
            .change_context(DatabaseError::Others)
            .attach_printable("Error filtering records by authentication type")?
            .into_iter()
            .flatten()
            .collect::<Vec<enums::AuthenticationType>>();

        Ok((
            filter_connector,
            filter_currency,
            intent_status,
            filter_payment_method,
            filter_payment_method_type,
            filter_authentication_type,
        ))
    }

    #[cfg(feature = "v2")]
    #[allow(clippy::too_many_arguments)]
    pub async fn get_total_count_of_attempts(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        active_attempt_ids: &[String],
        connector: Option<Vec<String>>,
        payment_method_type: Option<Vec<enums::PaymentMethod>>,
        payment_method_subtype: Option<Vec<enums::PaymentMethodType>>,
        authentication_type: Option<Vec<enums::AuthenticationType>>,
        merchant_connector_id: Option<Vec<common_utils::id_type::MerchantConnectorAccountId>>,
        card_network: Option<Vec<enums::CardNetwork>>,
    ) -> StorageResult<i64> {
        let mut filter = <Self as HasTable>::table()
            .count()
            .filter(dsl::merchant_id.eq(merchant_id.to_owned()))
            .filter(dsl::id.eq_any(active_attempt_ids.to_owned()))
            .into_boxed();

        if let Some(connectors) = connector {
            filter = filter.filter(dsl::connector.eq_any(connectors));
        }

        if let Some(payment_method_types) = payment_method_type {
            filter = filter.filter(dsl::payment_method_type_v2.eq_any(payment_method_types));
        }
        if let Some(payment_method_subtypes) = payment_method_subtype {
            filter = filter.filter(dsl::payment_method_subtype.eq_any(payment_method_subtypes));
        }
        if let Some(authentication_types) = authentication_type {
            filter = filter.filter(dsl::authentication_type.eq_any(authentication_types));
        }
        if let Some(merchant_connector_ids) = merchant_connector_id {
            filter = filter.filter(dsl::merchant_connector_id.eq_any(merchant_connector_ids));
        }
        if let Some(card_networks) = card_network {
            filter = filter.filter(dsl::card_network.eq_any(card_networks));
        }

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

        // TODO: Remove these logs after debugging the issue for delay in count query
        let start_time = std::time::Instant::now();
        router_env::logger::debug!("Executing count query start_time: {:?}", start_time);
        let result = db_metrics::track_database_call::<<Self as HasTable>::Table, _, _>(
            filter.get_result_async::<i64>(conn),
            db_metrics::DatabaseOperation::Filter,
        )
        .await
        .change_context(DatabaseError::Others)
        .attach_printable("Error filtering count of payments");

        let duration = start_time.elapsed();
        router_env::logger::debug!("Completed count query in {:?}", duration);

        result
    }

    #[cfg(feature = "v1")]
    #[allow(clippy::too_many_arguments)]
    pub async fn get_total_count_of_attempts(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        active_attempt_ids: &[String],
        connector: Option<Vec<String>>,
        payment_method: Option<Vec<enums::PaymentMethod>>,
        payment_method_type: Option<Vec<enums::PaymentMethodType>>,
        authentication_type: Option<Vec<enums::AuthenticationType>>,
        merchant_connector_id: Option<Vec<common_utils::id_type::MerchantConnectorAccountId>>,
        card_network: Option<Vec<enums::CardNetwork>>,
        card_discovery: Option<Vec<enums::CardDiscovery>>,
    ) -> StorageResult<i64> {
        let mut filter = <Self as HasTable>::table()
            .count()
            .filter(dsl::merchant_id.eq(merchant_id.to_owned()))
            .filter(dsl::attempt_id.eq_any(active_attempt_ids.to_owned()))
            .into_boxed();

        if let Some(connector) = connector {
            filter = filter.filter(dsl::connector.eq_any(connector));
        }

        if let Some(payment_method) = payment_method {
            filter = filter.filter(dsl::payment_method.eq_any(payment_method));
        }
        if let Some(payment_method_type) = payment_method_type {
            filter = filter.filter(dsl::payment_method_type.eq_any(payment_method_type));
        }
        if let Some(authentication_type) = authentication_type {
            filter = filter.filter(dsl::authentication_type.eq_any(authentication_type));
        }
        if let Some(merchant_connector_id) = merchant_connector_id {
            filter = filter.filter(dsl::merchant_connector_id.eq_any(merchant_connector_id))
        }
        if let Some(card_network) = card_network {
            filter = filter.filter(dsl::card_network.eq_any(card_network))
        }
        if let Some(card_discovery) = card_discovery {
            filter = filter.filter(dsl::card_discovery.eq_any(card_discovery))
        }

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

        // TODO: Remove these logs after debugging the issue for delay in count query
        let start_time = std::time::Instant::now();
        router_env::logger::debug!("Executing count query start_time: {:?}", start_time);
        let result = db_metrics::track_database_call::<<Self as HasTable>::Table, _, _>(
            filter.get_result_async::<i64>(conn),
            db_metrics::DatabaseOperation::Filter,
        )
        .await
        .change_context(DatabaseError::Others)
        .attach_printable("Error filtering count of payments");

        let duration = start_time.elapsed();
        router_env::logger::debug!("Completed count query in {:?}", duration);

        result
    }
}


pub async fn update_by_profile_id(
        self,
        conn: &PgPooledConn,
        business_profile: ProfileUpdateInternal,
    ) -> StorageResult<Self> {
        match generics::generic_update_by_id::<<Self as HasTable>::Table, _, _, _>(
            conn,
            self.get_id().to_owned(),
            business_profile,
        )
        .await
        {
            Err(error) => match error.current_context() {
                errors::DatabaseError::NoFieldsToUpdate => Ok(self),
                _ => Err(error),
            },
            result => result,
        }
    }


OpenAPI Block Path: components.schemas.MinorUnit
{
  "type": "integer",
  "format": "int64",
  "description": "This Unit struct represents MinorUnit in which core amount works"
}