  - RSync voided status mapping in AuthorizeDotNet ([#7566](https://github.com/juspay/hyperswitch/pull/7566)) ([`8512bf3`](https://github.com/juspay/hyperswitch/commit/8512bf3ea54907887de9b9bcfcf7f522b97ebf2b))
  - [globalpay] address 5xx due to deserialization issue when `account_name` is not passed ([#7540](https://github.com/juspay/hyperswitch/pull/7540)) ([`451d193`](https://github.com/juspay/hyperswitch/commit/451d193137b7c26a8c726633295dbb92a622de59))

### Refactors

- **webhook:** Add jwt authenticated endpoint to list unique webhook events for a profile ([#7325](https://github.com/juspay/hyperswitch/pull/7325)) ([`7010799`](https://github.com/juspay/hyperswitch/commit/7010799087787492d9c4177d56d5edd5d12dc546))

### Miscellaneous Tasks

- Update production endpoint url for globalPay ([#7588](https://github.com/juspay/hyperswitch/pull/7588)) ([`74090f6`](https://github.com/juspay/hyperswitch/commit/74090f6cfead76818424b6f0bad6720596d69a76))

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

- - -

## 2025.03.24.0

### Features

- **coingate:** Add Payment webhooks ([#7489](https://github.com/juspay/hyperswitch/pull/7489)) ([`fb8cba0`](https://github.com/juspay/hyperswitch/commit/fb8cba07012bb4387f38a5e735365853a7716a24))
- **connector:** [Coingate] implement refunds ([#7513](https://github.com/juspay/hyperswitch/pull/7513)) ([`4af8652`](https://github.com/juspay/hyperswitch/commit/4af86523980cb6260c3e86646feeac19ee8f82e1))
- **connectors:**
  - [Redsys] add 3D secure card payment support, including transaction capture, cancellation, and refunds ([#7508](https://github.com/juspay/hyperswitch/pull/7508)) ([`a1ecce8`](https://github.com/juspay/hyperswitch/commit/a1ecce8f256081da1127f0ca4ed06356856c0e8b))
  - [Redsys] add Psync and Rsync support ([#7586](https://github.com/juspay/hyperswitch/pull/7586)) ([`3f18c94`](https://github.com/juspay/hyperswitch/commit/3f18c944117007528093799ffcd779c6d31839f9))
- **router:** [V2] Return connector customer reference IDs in `CustomerResponse` ([#7319](https://github.com/juspay/hyperswitch/pull/7319)) ([`aedf460`](https://github.com/juspay/hyperswitch/commit/aedf460c701611a887b05023059711b9c2d41152))
- **webhook:** Return events list and total_count on list initial delivery attempt call ([#7243](https://github.com/juspay/hyperswitch/pull/7243)) ([`4d57f5e`](https://github.com/juspay/hyperswitch/commit/4d57f5e62d69117cba7422a1cfdb6988674b7dfa))
- Core changes for extended authorization ([#6766](https://github.com/juspay/hyperswitch/pull/6766)) ([`c3c4f50`](https://github.com/juspay/hyperswitch/commit/c3c4f50fcf46354c3240890f6625e9bd37c6c86b))

### Refactors

- **connector:** [BRAINTREE] Pass email in payment requests ([#7591](https://github.com/juspay/hyperswitch/pull/7591)) ([`d59c2fa`](https://github.com/juspay/hyperswitch/commit/d59c2fa026227a9a88329c3a47d4e7faa93ef52b))
- **dynamic_routing:** Change insert operation to upsert for dynamic_routing_stats ([#7398](https://github.com/juspay/hyperswitch/pull/7398)) ([`68aac34`](https://github.com/juspay/hyperswitch/commit/68aac34eb38189427f34753a108478f7e8f8b420))
- **router:** Make error_type generic in domain_models inorder to avoid conversion of errors in storage_impl ([#7537](https://github.com/juspay/hyperswitch/pull/7537)) ([`80218d0`](https://github.com/juspay/hyperswitch/commit/80218d0f2713be27b2249fb72f21a9db2a6e7f40))

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

- - -

## 2025.03.21.0

### Features

- **router:** Return psp_tokenization_enabled in Customer PML ([#7519](https://github.com/juspay/hyperswitch/pull/7519)) ([`a341e82`](https://github.com/juspay/hyperswitch/commit/a341e82d4a2c7d3f3e44ce30cfe137b43b7993ee))

### Bug Fixes

- **analytics:** Retry implementation for forex crate call ([#7280](https://github.com/juspay/hyperswitch/pull/7280)) ([`e93fce2`](https://github.com/juspay/hyperswitch/commit/e93fce26b60b24301aa3fc3a0e87e56ec347816a))
- **connector:**
  - [CYBERSOURCE] send xid as none for external 3ds payments ([#7577](https://github.com/juspay/hyperswitch/pull/7577)) ([`a5be114`](https://github.com/juspay/hyperswitch/commit/a5be11498feb6553b995e35dee41d3908e18aebc))
  - [SHIFT4, WORLDLINE] removed currencies CUC, STD, VEF from sandbox configs ([#7572](https://github.com/juspay/hyperswitch/pull/7572)) ([`97629ad`](https://github.com/juspay/hyperswitch/commit/97629ad18323976ab939f52afbe97c66f4e02949))

### Refactors

- **dynamic_fields:** Multiple SDK queries fixed ([#7380](https://github.com/juspay/hyperswitch/pull/7380)) ([`cbc262f`](https://github.com/juspay/hyperswitch/commit/cbc262fbdfe59a3984864be91325f3bb942736ae))

### Miscellaneous Tasks

- Update payment method configs for globalpay ([#7512](https://github.com/juspay/hyperswitch/pull/7512)) ([`23a2a0c`](https://github.com/juspay/hyperswitch/commit/23a2a0cf27d87fade674f342550a3ade4a2174ce))

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

- - -

## 2025.03.20.0

### Features

- **payment-link:** Emit intent status to parent before rendering payment link UI ([#7531](https://github.com/juspay/hyperswitch/pull/7531)) ([`6f35899`](https://github.com/juspay/hyperswitch/commit/6f35899b6d56c8584e297ff691d15dd3995fb43d))

### Bug Fixes

- **connector:** WASM fixed in Getnet ([#7564](https://github.com/juspay/hyperswitch/pull/7564)) ([`5ee2171`](https://github.com/juspay/hyperswitch/commit/5ee217175c13d24cd0365f8622209cdca5a7d2eb))

### Build System / Dependencies

- **deps:** Update aws dependencies ([#4194](https://github.com/juspay/hyperswitch/pull/4194)) ([`afca506`](https://github.com/juspay/hyperswitch/commit/afca506369685ab63d1b6bccd5f2e2dbae50ebf9))

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

- - -

## 2025.03.19.0

### Features

- **analytics:** Add issuer as filter for authentication analytics ([#7535](https://github.com/juspay/hyperswitch/pull/7535)) ([`a87471c`](https://github.com/juspay/hyperswitch/commit/a87471c45dee50e7b9c9674aa8c9bb668c892917))
- **connector:**
  - [Chargebee] Add record back support for chargebee ([#7505](https://github.com/juspay/hyperswitch/pull/7505)) ([`e77bb71`](https://github.com/juspay/hyperswitch/commit/e77bb71c6c8c13a343d877481fe2e75cdd98e18c))
  - [Nexixpay] add setup mandate flow ([#7532](https://github.com/juspay/hyperswitch/pull/7532)) ([`f6633a7`](https://github.com/juspay/hyperswitch/commit/f6633a72d5f826804c6a24317f8e5c836b0bc95a))

### Bug Fixes

- **connector:**
  - [BRAINTREE] Added fix for Braintree Creds Identifier ([#7501](https://github.com/juspay/hyperswitch/pull/7501)) ([`a161f24`](https://github.com/juspay/hyperswitch/commit/a161f24773abd38435b40b41540d1cab1a722b7c))
  - Handle Sequential Automatic case in MIT payments ([#6833](https://github.com/juspay/hyperswitch/pull/6833)) ([`e4c7ab8`](https://github.com/juspay/hyperswitch/commit/e4c7ab8e683d969092910b3fcc046c2e65e18df9))

### Refactors

- **core:** Revert "filter default routing config response based on connector type" ([#7556](https://github.com/juspay/hyperswitch/pull/7556)) ([`66e507e`](https://github.com/juspay/hyperswitch/commit/66e507e017fa7416f33aeae5f1ee60fd65d1d8b0))

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

- - -

## 2025.03.18.0

### Features

- **connector:**
  - [PAYSTACK] Electronic Fund Transfer(EFT) Payment Flows ([#7440](https://github.com/juspay/hyperswitch/pull/7440)) ([`c39ecda`](https://github.com/juspay/hyperswitch/commit/c39ecda79a9b1df1ccb4e469111e0dfb92a3d82c))
  - Recurly incoming webhook support ([#7439](https://github.com/juspay/hyperswitch/pull/7439)) ([`2d17dad`](https://github.com/juspay/hyperswitch/commit/2d17dad25d0966fc95f17e0ee91598ea445d4dc9))
  - [GETNET] Add cards payment flow ([#7256](https://github.com/juspay/hyperswitch/pull/7256)) ([`d346d38`](https://github.com/juspay/hyperswitch/commit/d346d38faf3858ec1e3360fb9e3a2a82e7d330fb))
  - [Stripebilling] add incoming webhook support ([#7417](https://github.com/juspay/hyperswitch/pull/7417)) ([`3282444`](https://github.com/juspay/hyperswitch/commit/32824441327fce00ecf5dca21bbf48e17910f2df))
- **payment-link:** Add config for enabling form button only when form is complete ([#7517](https://github.com/juspay/hyperswitch/pull/7517)) ([`0be1f87`](https://github.com/juspay/hyperswitch/commit/0be1f878ed58b18068dccc430d17b612e01e9fe7))
- Scheme error code and messages in payments api response ([#7528](https://github.com/juspay/hyperswitch/pull/7528)) ([`c702535`](https://github.com/juspay/hyperswitch/commit/c702535e91cb6c489d8c24d1ab0e6be3025d015a))

### Bug Fixes

- **connector:**
  - [INESPAY] Added iban as required field in Inespay ([#7350](https://github.com/juspay/hyperswitch/pull/7350)) ([`2db2738`](https://github.com/juspay/hyperswitch/commit/2db2738cf8c84704d0765f90cd421f5b5db4b7d8))
  - Fix incorrect mapping of attempt status in AuthorizeDotNet connector ([#7523](https://github.com/juspay/hyperswitch/pull/7523)) ([`04ede12`](https://github.com/juspay/hyperswitch/commit/04ede122e45a02325b881f8772127ad3d1389895))
- **connectors:** [Nexixpay] handle optional fields in nexixpay payments requests. ([#7465](https://github.com/juspay/hyperswitch/pull/7465)) ([`fc596ea`](https://github.com/juspay/hyperswitch/commit/fc596eaf1c45cb8ffafca3ad41945134b32b2bb4))

### Refactors

- **currency_conversion:** Add support for expiring forex data in redis ([#7455](https://github.com/juspay/hyperswitch/pull/7455)) ([`480e8c3`](https://github.com/juspay/hyperswitch/commit/480e8c3dcf59d6e4052aa0077dd06278fba973ff))
- **process_tracker:** Integrate proxy_payments api to process tracker workflow ([#7480](https://github.com/juspay/hyperswitch/pull/7480)) ([`ba3ad87`](https://github.com/juspay/hyperswitch/commit/ba3ad87e06d63910d78fdb217db47064b4d926be))

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

- - -

## 2025.03.17.0

### Features

- **connector:** [BRAINTREE] Add revoke mandate flow ([#7525](https://github.com/juspay/hyperswitch/pull/7525)) ([`2f55be4`](https://github.com/juspay/hyperswitch/commit/2f55be45fba43484b5d1ae36864028388b02e67a))

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

- - -

## 2025.03.14.0

### Features

- **connector:** [Hipay] Add Card Payments Flow ([#7475](https://github.com/juspay/hyperswitch/pull/7475)) ([`3e7db57`](https://github.com/juspay/hyperswitch/commit/3e7db573211ed61eadde3b601575f3472bda2b1f))
- **router:** Add connector field to PaymentRevenueRecoveryMetadata and implement schedule_failed_payment ([#7462](https://github.com/juspay/hyperswitch/pull/7462)) ([`aa337ee`](https://github.com/juspay/hyperswitch/commit/aa337eee9cae96056a5ce6d8a9eb9c84f4d376fe))

### Refactors

- **connector:** [PAYPAL, ADYEN] Move to crate hyperswitch_connectors ([#7431](https://github.com/juspay/hyperswitch/pull/7431)) ([`cf36419`](https://github.com/juspay/hyperswitch/commit/cf364192f10c286082ab8395cc3a8298be0c3df0))

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

- - -

## 2025.03.13.0

### Features

- **analytics:** Modified authentication queries and added generate report for authentications ([#7483](https://github.com/juspay/hyperswitch/pull/7483)) ([`9683b2a`](https://github.com/juspay/hyperswitch/commit/9683b2a8955f876d99625cd5cf70c4bdf3836e9e))
- **connector:** Add record back connector integration flow ([#7416](https://github.com/juspay/hyperswitch/pull/7416)) ([`13a2749`](https://github.com/juspay/hyperswitch/commit/13a274909962872f1d663d082af33fc44205d419))
- **core:** Add V2 Authentication to all available endpoints ([#7487](https://github.com/juspay/hyperswitch/pull/7487)) ([`3667a7f`](https://github.com/juspay/hyperswitch/commit/3667a7ffd216e165e1f51ad1ceac05d6901bb187))
- **payment_methods_v2:** Add total-payment-method-count api ([#7479](https://github.com/juspay/hyperswitch/pull/7479)) ([`4f6174d`](https://github.com/juspay/hyperswitch/commit/4f6174d1bf6dd0713b0a3d8e005671c884555144))
- **users:** Add V2 User APIs to Support Modularity for Merchant Accounts ([#7386](https://github.com/juspay/hyperswitch/pull/7386)) ([`d1f5303`](https://github.com/juspay/hyperswitch/commit/d1f53036c75771d8387a9579a544c1e2b3c17353))

### Bug Fixes

- **connector:** Fix mapping of feature matrix for coinbase ([#7454](https://github.com/juspay/hyperswitch/pull/7454)) ([`e949600`](https://github.com/juspay/hyperswitch/commit/e9496007889350f78af5875566c806f79c397544))
- **payment_methods:** Payment method type not being stored in payment method ([#7411](https://github.com/juspay/hyperswitch/pull/7411)) ([`833da1c`](https://github.com/juspay/hyperswitch/commit/833da1c3c5a0f006a689b05554c547205774c823))
- **routing:** Enable filtering of default connectors for contract based routing ([#7420](https://github.com/juspay/hyperswitch/pull/7420)) ([`c0c08d0`](https://github.com/juspay/hyperswitch/commit/c0c08d05ef04d914e07d49f163e43bdddf5c885b))

### Refactors

- **connector:** [FISERV, HELCIM] Add amount conversion framework to Fiserv, Helcim ([#7336](https://github.com/juspay/hyperswitch/pull/7336)) ([`4352101`](https://github.com/juspay/hyperswitch/commit/4352101555f77bc0c18a2b83e5dbb432fae4e0c9))
- **cypress:** Fiuu connector configuration changes ([#7297](https://github.com/juspay/hyperswitch/pull/7297)) ([`a387ae2`](https://github.com/juspay/hyperswitch/commit/a387ae290ef899c252049f270cbf9b716dbe1c55))
- **payment_methods_v2:** Refactor network tokenization flow for v2 ([#7309](https://github.com/juspay/hyperswitch/pull/7309)) ([`bba414c`](https://github.com/juspay/hyperswitch/commit/bba414cd198bef0f2fb8bfbd077f7f775a2eb8a5))

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

- - -

## 2025.03.12.0

### Features

- **connector:**
  - Rapyd, Bamboraapac, Gocardless, Powertranz, Shift4 and Worldline added in feature matrix ([#7136](https://github.com/juspay/hyperswitch/pull/7136)) ([`9fe9e9e`](https://github.com/juspay/hyperswitch/commit/9fe9e9e0bc59980014254d925add8adb5ee028ad))
  - [BILLWERK, FISERVEMEA, TSYS] add in feature matrix api ([#7165](https://github.com/juspay/hyperswitch/pull/7165)) ([`e15c814`](https://github.com/juspay/hyperswitch/commit/e15c8146aa29430423e884677ac0e7c6834b72fa))
  - Implement wallet mandates for authorizedotnet ([#7412](https://github.com/juspay/hyperswitch/pull/7412)) ([`07733a5`](https://github.com/juspay/hyperswitch/commit/07733a5c5eb94c318626421f2b4420dce0ddd39b))
- **process_tracker:** Add a version column in process tracker table ([#7389](https://github.com/juspay/hyperswitch/pull/7389)) ([`dbecdcf`](https://github.com/juspay/hyperswitch/commit/dbecdcf1cfae9e6f81a0b48a4a7fb273ad314071))

### Bug Fixes

- **connector:** [BRAINTREE] Changed Attempt Status Mapping for Braintree ([#7471](https://github.com/juspay/hyperswitch/pull/7471)) ([`69d8f35`](https://github.com/juspay/hyperswitch/commit/69d8f351b6fb7cb7ba800e6b4d9a541f7f374787))
- **postman:** Update expected status from `processing` to `requires_capture` in payment confirmation tests ([#7458](https://github.com/juspay/hyperswitch/pull/7458)) ([`ce84bb9`](https://github.com/juspay/hyperswitch/commit/ce84bb9245d6bdb4fb9627abec639754c868d54a))
- **router:** Pass card network for external 3DS from additional_payment_method_data ([#7473](https://github.com/juspay/hyperswitch/pull/7473)) ([`5cdfe83`](https://github.com/juspay/hyperswitch/commit/5cdfe8325481909a8a1596f967303eff44ffbcec))

### Refactors

- **core:** Filter default routing config response based on connector type ([#7199](https://github.com/juspay/hyperswitch/pull/7199)) ([`18db67c`](https://github.com/juspay/hyperswitch/commit/18db67c755ebb793223b476e556a4a05617c9a87))
- **pm:** Create new crate for payment methods ([#7355](https://github.com/juspay/hyperswitch/pull/7355)) ([`a31d140`](https://github.com/juspay/hyperswitch/commit/a31d1403eedbe7abd88139e2a9e9c96047324edb))

### Miscellaneous Tasks

- **postman:** Update Postman collection files ([`fd51beb`](https://github.com/juspay/hyperswitch/commit/fd51bebc52835875919ccae1f31f32bc0eb4d657))
- **wasm:** Show allowed auth methods option in google pay ([#7425](https://github.com/juspay/hyperswitch/pull/7425)) ([`2f0005a`](https://github.com/juspay/hyperswitch/commit/2f0005ae382fcbcb941d179f0a0d2dd5f93f724a))
- Update wise connector cypress creds ([#7477](https://github.com/juspay/hyperswitch/pull/7477)) ([`cb256dc`](https://github.com/juspay/hyperswitch/commit/cb256dcdac0533af51a6c77eef0039121a8ee110))

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

- - -

## 2025.03.11.0

### Refactors

- **connector:** [Rapyd] fix amount conversion framework ([#7466](https://github.com/juspay/hyperswitch/pull/7466)) ([`8f1bab6`](https://github.com/juspay/hyperswitch/commit/8f1bab62faa25a8a5f50346adce078911f22eb4a))

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

- - -

## 2025.03.10.0

### Features

- **core:**
  - Add bulk tokenization flows ([#7066](https://github.com/juspay/hyperswitch/pull/7066)) ([`2ff0d4f`](https://github.com/juspay/hyperswitch/commit/2ff0d4f956112ea71f4ecb2a48fd756a12578dab))
  - Add record attempt operation for revenue recovery webhooks ([#7236](https://github.com/juspay/hyperswitch/pull/7236)) ([`24aa003`](https://github.com/juspay/hyperswitch/commit/24aa00341f907e7b77df9348f62d1416cc098691))


OpenAPI Block Path: components.schemas.WalletResponse
{
  "allOf": [
    {
      "allOf": [
        {
          "$ref": "#/components/schemas/WalletResponseData"
        }
      ],
      "nullable": true
    },
    {
      "type": "object"
    }
  ]
}


/// Get the four-digit expiration year from the Google Pay pre-decrypt data
    pub fn get_four_digit_expiry_year(&self) -> Result<Secret<String>, errors::ValidationError> {
        let mut year = self.card_exp_year.peek().clone();

        // If it's a 2-digit year, convert to 4-digit
        if year.len() == 2 {
            year = format!("20{year}");
        } else if year.len() != 4 {
            return Err(errors::ValidationError::InvalidValue {
                message: format!(
                    "Invalid expiry year length: {}. Must be 2 or 4 digits",
                    year.len()
                ),
            }
            .into());
        }
        Ok(Secret::new(year))
    }


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


File: crates/api_models/src/analytics/routing_events.rs
 Public structs: 1

#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
pub struct RoutingEventsRequest {
    pub payment_id: common_utils::id_type::PaymentId,
    pub refund_id: Option<String>,
    pub dispute_id: Option<String>,
}


maud::html! {
            (maud::DOCTYPE)
            html {
                head {
                    meta name="viewport" content="width=device-width, initial-scale=1";
                }
                    body style="background-color: #ffffff; padding: 20px; font-family: Arial, Helvetica, Sans-Serif;" {

                        div id="loader1" class="lottie" style="height: 150px; display: block; position: relative; margin-top: 150px; margin-left: auto; margin-right: auto;" { "" }

                        (PreEscaped(r#""#))

                        (PreEscaped(r#"

                            "#))

                        h3 style="text-align: center;" { "Please wait while we process your payment..." }
                    }

                (PreEscaped(r#""#))
                (PreEscaped(format!("<form id=\"cardinal_collection_form\" method=\"POST\" target=\"collectionIframe\" action=\"{ddc_url}\">
                <input id=\"cardinal_collection_form_input\" type=\"hidden\" name=\"JWT\" value=\"{access_token}\">
              </form>")))
              (PreEscaped(r#""#))
              (PreEscaped(format!("
                ")))
            }}
        }
        RedirectForm::CybersourceConsumerAuth {
            access_token,
            step_up_url,
        } => {
            maud::html! {
            (maud::DOCTYPE)
            html {
                head {
                    meta name="viewport" content="width=device-width, initial-scale=1";
                }
                    body style="background-color: #ffffff; padding: 20px; font-family: Arial, Helvetica, Sans-Serif;" {

                        div id="loader1" class="lottie" style="height: 150px; display: block; position: relative; margin-top: 150px; margin-left: auto; margin-right: auto;" { "" }

                        (PreEscaped(r#""#))

                        (PreEscaped(r#"

                            "#))

                        h3 style="text-align: center;" { "Please wait while we process your payment..." }
                    }

                // This is the iframe recommended by cybersource but the redirection happens inside this iframe once otp
                // is received and we lose control of the redirection on user client browser, so to avoid that we have removed this iframe and directly consumed it.
                // (PreEscaped(r#""#))
                (PreEscaped(format!("<form id=\"step-up-form\" method=\"POST\" action=\"{step_up_url}\">
                <input type=\"hidden\" name=\"JWT\" value=\"{access_token}\">
              </form>")))
              (PreEscaped(format!("")))
            }}
        }
        RedirectForm::DeutschebankThreeDSChallengeFlow { acs_url, creq } => {
            maud::html! {
                (maud::DOCTYPE)
                html {
                    head {
                        meta name="viewport" content="width=device-width, initial-scale=1";
                    }

                    body style="background-color: #ffffff; padding: 20px; font-family: Arial, Helvetica, Sans-Serif;" {
                        div id="loader1" class="lottie" style="height: 150px; display: block; position: relative; margin-top: 150px; margin-left: auto; margin-right: auto;" { "" }

                        (PreEscaped(r#""#))

                        (PreEscaped(r#"


impl api::ConnectorAccessToken for Facilitapay {}

impl api::ConnectorAccessToken for for Facilitapay


    KlarnaRedirect,
    KlarnaSdk,
    AffirmRedirect,
    AfterpayClearpayRedirect,
    PayBrightRedirect,
    WalleyRedirect,
    AlmaRedirect,
    AtomeRedirect,
    Breadpay,
    FlexitiRedirect,
    BancontactCard,
    Bizum,
    Blik,
    Eft,
    Eps,
    Giropay,
    Ideal,
    Interac,
    LocalBankRedirect,
    OnlineBankingCzechRepublic,
    OnlineBankingFinland,
    OnlineBankingPoland,
    OnlineBankingSlovakia,
    OpenBankingUk,
    Przelewy24,
    Sofort,
    Trustly,
    OnlineBankingFpx,
    OnlineBankingThailand,
    AchBankDebit,
    SepaBankDebit,
    BecsBankDebit,
    BacsBankDebit,
    AchBankTransfer,
    SepaBankTransfer,
    BacsBankTransfer,
    MultibancoBankTransfer,
    PermataBankTransfer,
    BcaBankTransfer,
    BniVaBankTransfer,
    BhnCardNetwork,
    BriVaBankTransfer,
    CimbVaBankTransfer,
    DanamonVaBankTransfer,
    MandiriVaBankTransfer,
    Pix,
    Pse,
    Crypto,
    MandatePayment,
    Reward,
    Upi,
    Boleto,
    Efecty,
    PagoEfectivo,
    RedCompra,
    RedPagos,
    Alfamart,
    Indomaret,
    Oxxo,
    SevenEleven,
    Lawson,
    MiniStop,
    FamilyMart,
    Seicomart,
    PayEasy,
    Givex,
    PaySafeCar,
    CardToken,
    LocalBankTransfer,
    Mifinity,
    Fps,
    PromptPay,
    VietQr,
    OpenBanking,
    NetworkToken,
    NetworkTransactionIdAndCardDetails,
    DirectCarrierBilling,
    InstantBankTransfer,
    InstantBankTransferFinland,
    InstantBankTransferPoland,
    RevolutPay,
    IndonesianBankTransfer,
}

impl From<PaymentMethodData> for PaymentMethodDataType {
    fn from(pm_data: PaymentMethodData) -> Self {
        match pm_data {
            PaymentMethodData::Card(_) => Self::Card,
            PaymentMethodData::NetworkToken(_) => Self::NetworkToken,
            PaymentMethodData::CardDetailsForNetworkTransactionId(_) => {
                Self::NetworkTransactionIdAndCardDetails
            }
            PaymentMethodData::CardRedirect(card_redirect_data) => match card_redirect_data {
                payment_method_data::CardRedirectData::Knet {} => Self::Knet,
                payment_method_data::CardRedirectData::Benefit {} => Self::Benefit,
                payment_method_data::CardRedirectData::MomoAtm {} => Self::MomoAtm,
                payment_method_data::CardRedirectData::CardRedirect {} => Self::CardRedirect,
            },
            PaymentMethodData::Wallet(wallet_data) => match wallet_data {
                payment_method_data::WalletData::AliPayQr(_) => Self::AliPayQr,
                payment_method_data::WalletData::AliPayRedirect(_) => Self::AliPayRedirect,
                payment_method_data::WalletData::AliPayHkRedirect(_) => Self::AliPayHkRedirect,
                payment_method_data::WalletData::AmazonPayRedirect(_) => Self::AmazonPayRedirect,
                payment_method_data::WalletData::Skrill(_) => Self::Skrill,
                payment_method_data::WalletData::Paysera(_) => Self::Paysera,
                payment_method_data::WalletData::MomoRedirect(_) => Self::MomoRedirect,
                payment_method_data::WalletData::KakaoPayRedirect(_) => Self::KakaoPayRedirect,
                payment_method_data::WalletData::GoPayRedirect(_) => Self::GoPayRedirect,
                payment_method_data::WalletData::GcashRedirect(_) => Self::GcashRedirect,
                payment_method_data::WalletData::AmazonPay(_) => Self::AmazonPay,
                payment_method_data::WalletData::ApplePay(_) => Self::ApplePay,
                payment_method_data::WalletData::ApplePayRedirect(_) => Self::ApplePayRedirect,
                payment_method_data::WalletData::ApplePayThirdPartySdk(_) => {
                    Self::ApplePayThirdPartySdk
                }
                payment_method_data::WalletData::DanaRedirect {} => Self::DanaRedirect,
                payment_method_data::WalletData::GooglePay(_) => Self::GooglePay,
                payment_method_data::WalletData::BluecodeRedirect {} => Self::Bluecode,
                payment_method_data::WalletData::GooglePayRedirect(_) => Self::GooglePayRedirect,
                payment_method_data::WalletData::GooglePayThirdPartySdk(_) => {
                    Self::GooglePayThirdPartySdk
                }
                payment_method_data::WalletData::MbWayRedirect(_) => Self::MbWayRedirect,
                payment_method_data::WalletData::MobilePayRedirect(_) => Self::MobilePayRedirect,
                payment_method_data::WalletData::PaypalRedirect(_) => Self::PaypalRedirect,
                payment_method_data::WalletData::PaypalSdk(_) => Self::PaypalSdk,
                payment_method_data::WalletData::Paze(_) => Self::Paze,
                payment_method_data::WalletData::SamsungPay(_) => Self::SamsungPay,
                payment_method_data::WalletData::TwintRedirect {} => Self::TwintRedirect,
                payment_method_data::WalletData::VippsRedirect {} => Self::VippsRedirect,
                payment_method_data::WalletData::TouchNGoRedirect(_) => Self::TouchNGoRedirect,
                payment_method_data::WalletData::WeChatPayRedirect(_) => Self::WeChatPayRedirect,
                payment_method_data::WalletData::WeChatPayQr(_) => Self::WeChatPayQr,
                payment_method_data::WalletData::CashappQr(_) => Self::CashappQr,
                payment_method_data::WalletData::SwishQr(_) => Self::SwishQr,
                payment_method_data::WalletData::Mifinity(_) => Self::Mifinity,
                payment_method_data::WalletData::RevolutPay(_) => Self::RevolutPay,
            },
            PaymentMethodData::PayLater(pay_later_data) => match pay_later_data {
                payment_method_data::PayLaterData::KlarnaRedirect { .. } => Self::KlarnaRedirect,
                payment_method_data::PayLaterData::KlarnaSdk { .. } => Self::KlarnaSdk,
                payment_method_data::PayLaterData::AffirmRedirect {} => Self::AffirmRedirect,
                payment_method_data::PayLaterData::AfterpayClearpayRedirect { .. } => {
                    Self::AfterpayClearpayRedirect
                }
                payment_method_data::PayLaterData::FlexitiRedirect { .. } => Self::FlexitiRedirect,
                payment_method_data::PayLaterData::PayBrightRedirect {} => Self::PayBrightRedirect,
                payment_method_data::PayLaterData::WalleyRedirect {} => Self::WalleyRedirect,
                payment_method_data::PayLaterData::AlmaRedirect {} => Self::AlmaRedirect,
                payment_method_data::PayLaterData::AtomeRedirect {} => Self::AtomeRedirect,
                payment_method_data::PayLaterData::BreadpayRedirect {} => Self::Breadpay,
            },
            PaymentMethodData::BankRedirect(bank_redirect_data) => match bank_redirect_data {
                payment_method_data::BankRedirectData::BancontactCard { .. } => {
                    Self::BancontactCard
                }
                payment_method_data::BankRedirectData::Bizum {} => Self::Bizum,
                payment_method_data::BankRedirectData::Blik { .. } => Self::Blik,
                payment_method_data::BankRedirectData::Eft { .. } => Self::Eft,
                payment_method_data::BankRedirectData::Eps { .. } => Self::Eps,
                payment_method_data::BankRedirectData::Giropay { .. } => Self::Giropay,
                payment_method_data::BankRedirectData::Ideal { .. } => Self::Ideal,
                payment_method_data::BankRedirectData::Interac { .. } => Self::Interac,
                payment_method_data::BankRedirectData::OnlineBankingCzechRepublic { .. } => {
                    Self::OnlineBankingCzechRepublic
                }
                payment_method_data::BankRedirectData::OnlineBankingFinland { .. } => {
                    Self::OnlineBankingFinland
                }
                payment_method_data::BankRedirectData::OnlineBankingPoland { .. } => {
                    Self::OnlineBankingPoland
                }
                payment_method_data::BankRedirectData::OnlineBankingSlovakia { .. } => {
                    Self::OnlineBankingSlovakia
                }
                payment_method_data::BankRedirectData::OpenBankingUk { .. } => Self::OpenBankingUk,
                payment_method_data::BankRedirectData::Przelewy24 { .. } => Self::Przelewy24,
                payment_method_data::BankRedirectData::Sofort { .. } => Self::Sofort,
                payment_method_data::BankRedirectData::Trustly { .. } => Self::Trustly,
                payment_method_data::BankRedirectData::OnlineBankingFpx { .. } => {
                    Self::OnlineBankingFpx
                }
                payment_method_data::BankRedirectData::OnlineBankingThailand { .. } => {
                    Self::OnlineBankingThailand
                }
                payment_method_data::BankRedirectData::LocalBankRedirect {} => {
                    Self::LocalBankRedirect
                }
            },
            PaymentMethodData::BankDebit(bank_debit_data) => match bank_debit_data {
                payment_method_data::BankDebitData::AchBankDebit { .. } => Self::AchBankDebit,
                payment_method_data::BankDebitData::SepaBankDebit { .. } => Self::SepaBankDebit,
                payment_method_data::BankDebitData::BecsBankDebit { .. } => Self::BecsBankDebit,
                payment_method_data::BankDebitData::BacsBankDebit { .. } => Self::BacsBankDebit,
            },
            PaymentMethodData::BankTransfer(bank_transfer_data) => match *bank_transfer_data {
                payment_method_data::BankTransferData::AchBankTransfer { .. } => {
                    Self::AchBankTransfer
                }
                payment_method_data::BankTransferData::SepaBankTransfer { .. } => {
                    Self::SepaBankTransfer
                }
                payment_method_data::BankTransferData::BacsBankTransfer { .. } => {
                    Self::BacsBankTransfer
                }
                payment_method_data::BankTransferData::MultibancoBankTransfer { .. } => {
                    Self::MultibancoBankTransfer
                }
                payment_method_data::BankTransferData::PermataBankTransfer { .. } => {
                    Self::PermataBankTransfer
                }
                payment_method_data::BankTransferData::BcaBankTransfer { .. } => {
                    Self::BcaBankTransfer
                }
                payment_method_data::BankTransferData::BniVaBankTransfer { .. } => {
                    Self::BniVaBankTransfer
                }
                payment_method_data::BankTransferData::BriVaBankTransfer { .. } => {
                    Self::BriVaBankTransfer
                }
                payment_method_data::BankTransferData::CimbVaBankTransfer { .. } => {
                    Self::CimbVaBankTransfer
                }
                payment_method_data::BankTransferData::DanamonVaBankTransfer { .. } => {
                    Self::DanamonVaBankTransfer
                }
                payment_method_data::BankTransferData::MandiriVaBankTransfer { .. } => {
                    Self::MandiriVaBankTransfer
                }
                payment_method_data::BankTransferData::Pix { .. } => Self::Pix,
                payment_method_data::BankTransferData::Pse {} => Self::Pse,
                payment_method_data::BankTransferData::LocalBankTransfer { .. } => {
                    Self::LocalBankTransfer
                }
                payment_method_data::BankTransferData::InstantBankTransfer {} => {
                    Self::InstantBankTransfer
                }
                payment_method_data::BankTransferData::InstantBankTransferFinland {} => {
                    Self::InstantBankTransferFinland
                }
                payment_method_data::BankTransferData::InstantBankTransferPoland {} => {
                    Self::InstantBankTransferPoland
                }
                payment_method_data::BankTransferData::IndonesianBankTransfer { .. } => {
                    Self::IndonesianBankTransfer
                }
            },
            PaymentMethodData::Crypto(_) => Self::Crypto,
            PaymentMethodData::MandatePayment => Self::MandatePayment,
            PaymentMethodData::Reward => Self::Reward,
            PaymentMethodData::Upi(_) => Self::Upi,
            PaymentMethodData::Voucher(voucher_data) => match voucher_data {
                payment_method_data::VoucherData::Boleto(_) => Self::Boleto,
                payment_method_data::VoucherData::Efecty => Self::Efecty,
                payment_method_data::VoucherData::PagoEfectivo => Self::PagoEfectivo,
                payment_method_data::VoucherData::RedCompra => Self::RedCompra,
                payment_method_data::VoucherData::RedPagos => Self::RedPagos,
                payment_method_data::VoucherData::Alfamart(_) => Self::Alfamart,
                payment_method_data::VoucherData::Indomaret(_) => Self::Indomaret,
                payment_method_data::VoucherData::Oxxo => Self::Oxxo,
                payment_method_data::VoucherData::SevenEleven(_) => Self::SevenEleven,
                payment_method_data::VoucherData::Lawson(_) => Self::Lawson,
                payment_method_data::VoucherData::MiniStop(_) => Self::MiniStop,
                payment_method_data::VoucherData::FamilyMart(_) => Self::FamilyMart,
                payment_method_data::VoucherData::Seicomart(_) => Self::Seicomart,
                payment_method_data::VoucherData::PayEasy(_) => Self::PayEasy,
            },
            PaymentMethodData::RealTimePayment(real_time_payment_data) => {
                match *real_time_payment_data {
                    payment_method_data::RealTimePaymentData::DuitNow {} => Self::DuitNow,
                    payment_method_data::RealTimePaymentData::Fps {} => Self::Fps,
                    payment_method_data::RealTimePaymentData::PromptPay {} => Self::PromptPay,
                    payment_method_data::RealTimePaymentData::VietQr {} => Self::VietQr,
                }
            }
            PaymentMethodData::GiftCard(gift_card_data) => match *gift_card_data {
                payment_method_data::GiftCardData::Givex(_) => Self::Givex,
                payment_method_data::GiftCardData::PaySafeCard {} => Self::PaySafeCar,
                payment_method_data::GiftCardData::BhnCardNetwork(_) => Self::BhnCardNetwork,
            },
            PaymentMethodData::CardToken(_) => Self::CardToken,
            PaymentMethodData::OpenBanking(data) => match data {
                payment_method_data::OpenBankingData::OpenBankingPIS {} => Self::OpenBanking,
            },
            PaymentMethodData::MobilePayment(mobile_payment_data) => match mobile_payment_data {
                payment_method_data::MobilePaymentData::DirectCarrierBilling { .. } => {
                    Self::DirectCarrierBilling
                }
            },
        }
    }
}
pub trait GooglePay {
    fn get_googlepay_encrypted_payment_data(&self) -> Result<Secret<String>, Error>;
}

impl GooglePay for payment_method_data::GooglePayWalletData {
    fn get_googlepay_encrypted_payment_data(&self) -> Result<Secret<String>, Error> {
        let encrypted_data = self
            .tokenization_data
            .get_encrypted_google_pay_payment_data_mandatory()
            .change_context(errors::ConnectorError::InvalidWalletToken {
                wallet_name: "Google Pay".to_string(),
            })?;

        Ok(Secret::new(encrypted_data.token.clone()))
    }
}
pub trait ApplePay {
    fn get_applepay_decoded_payment_data(&self) -> Result<Secret<String>, Error>;
}

impl ApplePay for payment_method_data::ApplePayWalletData {
    fn get_applepay_decoded_payment_data(&self) -> Result<Secret<String>, Error> {
        let apple_pay_encrypted_data = self
            .payment_data
            .get_encrypted_apple_pay_payment_data_mandatory()
            .change_context(errors::ConnectorError::MissingRequiredField {
                field_name: "Apple pay encrypted data",
            })?;
        let token = Secret::new(
            String::from_utf8(
                BASE64_ENGINE
                    .decode(apple_pay_encrypted_data)
                    .change_context(errors::ConnectorError::InvalidWalletToken {
                        wallet_name: "Apple Pay".to_string(),
                    })?,
            )
            .change_context(errors::ConnectorError::InvalidWalletToken {
                wallet_name: "Apple Pay".to_string(),
            })?,
        );
        Ok(token)
    }
}

pub trait WalletData {
    fn get_wallet_token(&self) -> Result<Secret<String>, Error>;
    fn get_wallet_token_as_json<T>(&self, wallet_name: String) -> Result<T, Error>
    where
        T: serde::de::DeserializeOwned;
    fn get_encoded_wallet_token(&self) -> Result<String, Error>;
}

impl WalletData for payment_method_data::WalletData {
    fn get_wallet_token(&self) -> Result<Secret<String>, Error> {
        match self {
            Self::GooglePay(data) => Ok(data.get_googlepay_encrypted_payment_data()?),
            Self::ApplePay(data) => Ok(data.get_applepay_decoded_payment_data()?),
            Self::PaypalSdk(data) => Ok(Secret::new(data.token.clone())),
            _ => Err(errors::ConnectorError::InvalidWallet.into()),
        }
    }
    fn get_wallet_token_as_json<T>(&self, wallet_name: String) -> Result<T, Error>
    where
        T: serde::de::DeserializeOwned,
    {
        serde_json::from_str::<T>(self.get_wallet_token()?.peek())
            .change_context(errors::ConnectorError::InvalidWalletToken { wallet_name })
    }

    fn get_encoded_wallet_token(&self) -> Result<String, Error> {
        match self {
            Self::GooglePay(_) => {
                let json_token: Value = self.get_wallet_token_as_json("Google Pay".to_owned())?;
                let token_as_vec = serde_json::to_vec(&json_token).change_context(
                    errors::ConnectorError::InvalidWalletToken {
                        wallet_name: "Google Pay".to_string(),
                    },
                )?;
                let encoded_token = BASE64_ENGINE.encode(token_as_vec);
                Ok(encoded_token)
            }
            _ => Err(
                errors::ConnectorError::NotImplemented("SELECTED PAYMENT METHOD".to_owned()).into(),
            ),
        }
    }
}

pub fn deserialize_xml_to_struct<T: serde::de::DeserializeOwned>(
    xml_data: &[u8],
) -> Result<T, errors::ConnectorError> {
    let response_str = std::str::from_utf8(xml_data)
        .map_err(|e| {
            router_env::logger::error!("Error converting response data to UTF-8: {:?}", e);
            errors::ConnectorError::ResponseDeserializationFailed
        })?
        .trim();
    let result: T = quick_xml::de::from_str(response_str).map_err(|e| {
        router_env::logger::error!("Error deserializing XML response: {:?}", e);
        errors::ConnectorError::ResponseDeserializationFailed
    })?;

    Ok(result)
}

pub fn is_html_response(response: &str) -> bool {
    response.starts_with("<html>") || response.starts_with("<!DOCTYPE html>")
}

#[cfg(feature = "payouts")]
pub trait PayoutsData {
    fn get_transfer_id(&self) -> Result<String, Error>;
    fn get_customer_details(
        &self,
    ) -> Result<hyperswitch_domain_models::router_request_types::CustomerDetails, Error>;
    fn get_vendor_details(&self) -> Result<PayoutVendorAccountDetails, Error>;
    #[cfg(feature = "payouts")]
    fn get_payout_type(&self) -> Result<enums::PayoutType, Error>;
}

#[cfg(feature = "payouts")]
impl PayoutsData for hyperswitch_domain_models::router_request_types::PayoutsData {
    fn get_transfer_id(&self) -> Result<String, Error> {
        self.connector_payout_id
            .clone()
            .ok_or_else(missing_field_err("transfer_id"))
    }
    fn get_customer_details(
        &self,
    ) -> Result<hyperswitch_domain_models::router_request_types::CustomerDetails, Error> {
        self.customer_details
            .clone()
            .ok_or_else(missing_field_err("customer_details"))
    }
    fn get_vendor_details(&self) -> Result<PayoutVendorAccountDetails, Error> {
        self.vendor_details
            .clone()
            .ok_or_else(missing_field_err("vendor_details"))
    }
    #[cfg(feature = "payouts")]
    fn get_payout_type(&self) -> Result<enums::PayoutType, Error> {
        self.payout_type
            .to_owned()
            .ok_or_else(missing_field_err("payout_type"))
    }
}
pub trait RevokeMandateRequestData {
    fn get_connector_mandate_id(&self) -> Result<String, Error>;
}

impl RevokeMandateRequestData for MandateRevokeRequestData {
    fn get_connector_mandate_id(&self) -> Result<String, Error> {
        self.connector_mandate_id
            .clone()
            .ok_or_else(missing_field_err("connector_mandate_id"))
    }
}
pub trait RecurringMandateData {
    fn get_original_payment_amount(&self) -> Result<i64, Error>;
    fn get_original_payment_currency(&self) -> Result<enums::Currency, Error>;
}

impl RecurringMandateData for RecurringMandatePaymentData {
    fn get_original_payment_amount(&self) -> Result<i64, Error> {
        self.original_payment_authorized_amount
            .ok_or_else(missing_field_err("original_payment_authorized_amount"))
    }
    fn get_original_payment_currency(&self) -> Result<enums::Currency, Error> {
        self.original_payment_authorized_currency
            .ok_or_else(missing_field_err("original_payment_authorized_currency"))
    }
}

#[cfg(feature = "payouts")]
impl CardData for api_models::payouts::CardPayout {
    fn get_card_expiry_year_2_digit(&self) -> Result<Secret<String>, errors::ConnectorError> {
        let binding = self.expiry_year.clone();
        let year = binding.peek();
        Ok(Secret::new(
            year.get(year.len() - 2..)
                .ok_or(errors::ConnectorError::RequestEncodingFailed)?
                .to_string(),
        ))
    }
    fn get_card_expiry_month_2_digit(&self) -> Result<Secret<String>, errors::ConnectorError> {
        let exp_month = self
            .expiry_month
            .peek()
            .to_string()
            .parse::<u8>()
            .map_err(|_| errors::ConnectorError::InvalidDataFormat {
                field_name: "payment_method_data.card.card_exp_month",
            })?;
        let month = ::cards::CardExpirationMonth::try_from(exp_month).map_err(|_| {
            errors::ConnectorError::InvalidDataFormat {
                field_name: "payment_method_data.card.card_exp_month",
            }
        })?;
        Ok(Secret::new(month.two_digits()))
    }
    fn get_card_issuer(&self) -> Result<CardIssuer, Error> {
        get_card_issuer(self.card_number.peek())
    }
    fn get_card_expiry_month_year_2_digit_with_delimiter(
        &self,
        delimiter: String,
    ) -> Result<Secret<String>, errors::ConnectorError> {
        let year = self.get_card_expiry_year_2_digit()?;
        Ok(Secret::new(format!(
            "{}{}{}",
            self.expiry_month.peek(),
            delimiter,
            year.peek()
        )))
    }
    fn get_expiry_date_as_yyyymm(&self, delimiter: &str) -> Secret<String> {
        let year = self.get_expiry_year_4_digit();
        Secret::new(format!(
            "{}{}{}",
            year.peek(),
            delimiter,
            self.expiry_month.peek()
        ))
    }
    fn get_expiry_date_as_mmyyyy(&self, delimiter: &str) -> Secret<String> {
        let year = self.get_expiry_year_4_digit();
        Secret::new(format!(
            "{}{}{}",
            self.expiry_month.peek(),
            delimiter,
            year.peek()
        ))
    }
    fn get_expiry_year_4_digit(&self) -> Secret<String> {
        let mut year = self.expiry_year.peek().clone();
        if year.len() == 2 {
            year = format!("20{year}");
        }
        Secret::new(year)
    }
    fn get_expiry_date_as_yymm(&self) -> Result<Secret<String>, errors::ConnectorError> {
        let year = self.get_card_expiry_year_2_digit()?.expose();
        let month = self.expiry_month.clone().expose();
        Ok(Secret::new(format!("{year}{month}")))
    }
    fn get_expiry_month_as_i8(&self) -> Result<Secret<i8>, Error> {
        self.expiry_month
            .peek()
            .clone()
            .parse::<i8>()
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)
            .map(Secret::new)
    }
    fn get_expiry_year_as_i32(&self) -> Result<Secret<i32>, Error> {
        self.expiry_year
            .peek()
            .clone()
            .parse::<i32>()
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)
            .map(Secret::new)
    }

    fn get_expiry_date_as_mmyy(&self) -> Result<Secret<String>, errors::ConnectorError> {
        let year = self.get_card_expiry_year_2_digit()?.expose();
        let month = self.expiry_month.clone().expose();
        Ok(Secret::new(format!("{month}{year}")))
    }

    fn get_expiry_year_as_4_digit_i32(&self) -> Result<Secret<i32>, Error> {
        self.get_expiry_year_4_digit()
            .peek()
            .clone()
            .parse::<i32>()
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)
            .map(Secret::new)
    }
    fn get_cardholder_name(&self) -> Result<Secret<String>, Error> {
        self.card_holder_name
            .clone()
            .ok_or_else(missing_field_err("card.card_holder_name"))
    }
}

pub trait NetworkTokenData {
    fn get_card_issuer(&self) -> Result<CardIssuer, Error>;
    fn get_expiry_year_4_digit(&self) -> Secret<String>;
    fn get_network_token(&self) -> NetworkTokenNumber;
    fn get_network_token_expiry_month(&self) -> Secret<String>;
    fn get_network_token_expiry_year(&self) -> Secret<String>;
    fn get_cryptogram(&self) -> Option<Secret<String>>;
    fn get_token_expiry_year_2_digit(&self) -> Result<Secret<String>, errors::ConnectorError>;
    fn get_token_expiry_month_year_2_digit_with_delimiter(
        &self,
        delimiter: String,
    ) -> Result<Secret<String>, errors::ConnectorError>;
}

impl NetworkTokenData for payment_method_data::NetworkTokenData {
    #[cfg(feature = "v1")]
    fn get_card_issuer(&self) -> Result<CardIssuer, Error> {
        get_card_issuer(self.token_number.peek())
    }

    #[cfg(feature = "v2")]
    fn get_card_issuer(&self) -> Result<CardIssuer, Error> {
        get_card_issuer(self.network_token.peek())
    }

    #[cfg(feature = "v1")]
    fn get_expiry_year_4_digit(&self) -> Secret<String> {
        let mut year = self.token_exp_year.peek().clone();
        if year.len() == 2 {
            year = format!("20{year}");
        }
        Secret::new(year)
    }

    #[cfg(feature = "v2")]
    fn get_expiry_year_4_digit(&self) -> Secret<String> {
        let mut year = self.network_token_exp_year.peek().clone();
        if year.len() == 2 {
            year = format!("20{year}");
        }
        Secret::new(year)
    }

    #[cfg(feature = "v1")]
    fn get_network_token(&self) -> NetworkTokenNumber {
        self.token_number.clone()
    }

    #[cfg(feature = "v2")]
    fn get_network_token(&self) -> NetworkTokenNumber {
        self.network_token.clone()
    }

    #[cfg(feature = "v1")]
    fn get_network_token_expiry_month(&self) -> Secret<String> {
        self.token_exp_month.clone()
    }

    #[cfg(feature = "v2")]
    fn get_network_token_expiry_month(&self) -> Secret<String> {
        self.network_token_exp_month.clone()
    }

    #[cfg(feature = "v1")]
    fn get_network_token_expiry_year(&self) -> Secret<String> {
        self.token_exp_year.clone()
    }

    #[cfg(feature = "v2")]
    fn get_network_token_expiry_year(&self) -> Secret<String> {
        self.network_token_exp_year.clone()
    }

    #[cfg(feature = "v1")]
    fn get_cryptogram(&self) -> Option<Secret<String>> {
        self.token_cryptogram.clone()
    }

    #[cfg(feature = "v2")]
    fn get_cryptogram(&self) -> Option<Secret<String>> {
        self.cryptogram.clone()
    }

    #[cfg(feature = "v1")]
    fn get_token_expiry_year_2_digit(&self) -> Result<Secret<String>, errors::ConnectorError> {
        let binding = self.token_exp_year.clone();
        let year = binding.peek();
        Ok(Secret::new(
            year.get(year.len() - 2..)
                .ok_or(errors::ConnectorError::RequestEncodingFailed)?
                .to_string(),
        ))
    }

    #[cfg(feature = "v2")]
    fn get_token_expiry_year_2_digit(&self) -> Result<Secret<String>, errors::ConnectorError> {
        let binding = self.network_token_exp_year.clone();
        let year = binding.peek();
        Ok(Secret::new(
            year.get(year.len() - 2..)
                .ok_or(errors::ConnectorError::RequestEncodingFailed)?
                .to_string(),
        ))
    }

    #[cfg(feature = "v1")]
    fn get_token_expiry_month_year_2_digit_with_delimiter(
        &self,
        delimiter: String,
    ) -> Result<Secret<String>, errors::ConnectorError> {
        let year = self.get_token_expiry_year_2_digit()?;
        Ok(Secret::new(format!(
            "{}{}{}",
            self.token_exp_month.peek(),
            delimiter,
            year.peek()
        )))
    }

    #[cfg(feature = "v2")]
    fn get_token_expiry_month_year_2_digit_with_delimiter(
        &self,
        delimiter: String,
    ) -> Result<Secret<String>, errors::ConnectorError> {
        let year = self.get_token_expiry_year_2_digit()?;
        Ok(Secret::new(format!(
            "{}{}{}",
            self.network_token_exp_month.peek(),
            delimiter,
            year.peek()
        )))
    }
}

pub fn convert_uppercase<'de, D, T>(v: D) -> Result<T, D::Error>
where
    D: serde::Deserializer<'de>,
    T: FromStr,
    <T as FromStr>::Err: std::fmt::Debug + std::fmt::Display + std::error::Error,
{
    use serde::de::Error;
    let output = <&str>::deserialize(v)?;
    output.to_uppercase().parse::<T>().map_err(D::Error::custom)
}

pub(crate) fn convert_setup_mandate_router_data_to_authorize_router_data(
    data: &SetupMandateRouterData,
) -> PaymentsAuthorizeData {
    PaymentsAuthorizeData {
        currency: data.request.currency,
        payment_method_data: data.request.payment_method_data.clone(),
        confirm: data.request.confirm,
        statement_descriptor_suffix: data.request.statement_descriptor_suffix.clone(),
        mandate_id: data.request.mandate_id.clone(),
        setup_future_usage: data.request.setup_future_usage,
        off_session: data.request.off_session,
        setup_mandate_details: data.request.setup_mandate_details.clone(),
        router_return_url: data.request.router_return_url.clone(),
        email: data.request.email.clone(),
        customer_name: data.request.customer_name.clone(),
        amount: 0,
        order_tax_amount: Some(MinorUnit::zero()),
        minor_amount: MinorUnit::new(0),
        statement_descriptor: None,
        capture_method: data.request.capture_method,
        webhook_url: None,
        complete_authorize_url: None,
        browser_info: data.request.browser_info.clone(),
        order_details: None,
        order_category: None,
        session_token: None,
        enrolled_for_3ds: true,
        related_transaction_id: None,
        payment_experience: None,
        payment_method_type: None,
        customer_id: None,
        surcharge_details: None,
        request_extended_authorization: None,
        request_incremental_authorization: data.request.request_incremental_authorization,
        metadata: None,
        authentication_data: None,
        customer_acceptance: data.request.customer_acceptance.clone(),
        split_payments: None, // TODO: allow charges on mandates?
        merchant_order_reference_id: None,
        integrity_object: None,
        additional_payment_method_data: None,
        shipping_cost: data.request.shipping_cost,
        merchant_account_id: None,
        merchant_config_currency: None,
        connector_testing_data: data.request.connector_testing_data.clone(),
        order_id: None,
        locale: None,
        payment_channel: None,
        enable_partial_authorization: data.request.enable_partial_authorization,
    }
}

pub(crate) fn convert_payment_authorize_router_response<F1, F2, T1, T2>(
    item: (&ConnectorRouterData<F1, T1, PaymentsResponseData>, T2),
) -> ConnectorRouterData<F2, T2, PaymentsResponseData> {
    let data = item.0;
    let request = item.1;
    ConnectorRouterData {
        flow: PhantomData,
        request,
        merchant_id: data.merchant_id.clone(),
        connector: data.connector.clone(),
        attempt_id: data.attempt_id.clone(),
        tenant_id: data.tenant_id.clone(),
        status: data.status,
        payment_method: data.payment_method,
        connector_auth_type: data.connector_auth_type.clone(),
        description: data.description.clone(),
        address: data.address.clone(),
        auth_type: data.auth_type,
        connector_meta_data: data.connector_meta_data.clone(),
        connector_wallets_details: data.connector_wallets_details.clone(),
        amount_captured: data.amount_captured,
        minor_amount_captured: data.minor_amount_captured,
        access_token: data.access_token.clone(),
        response: data.response.clone(),
        payment_id: data.payment_id.clone(),
        session_token: data.session_token.clone(),
        reference_id: data.reference_id.clone(),
        customer_id: data.customer_id.clone(),
        payment_method_token: None,
        preprocessing_id: None,
        connector_customer: data.connector_customer.clone(),
        recurring_mandate_payment_data: data.recurring_mandate_payment_data.clone(),
        connector_request_reference_id: data.connector_request_reference_id.clone(),
        #[cfg(feature = "payouts")]
        payout_method_data: data.payout_method_data.clone(),
        #[cfg(feature = "payouts")]
        quote_id: data.quote_id.clone(),
        test_mode: data.test_mode,
        payment_method_status: None,
        payment_method_balance: data.payment_method_balance.clone(),
        connector_api_version: data.connector_api_version.clone(),
        connector_http_status_code: data.connector_http_status_code,
        external_latency: data.external_latency,
        apple_pay_flow: data.apple_pay_flow.clone(),
        frm_metadata: data.frm_metadata.clone(),
        dispute_id: data.dispute_id.clone(),
        refund_id: data.refund_id.clone(),
        connector_response: data.connector_response.clone(),
        integrity_check: Ok(()),
        additional_merchant_data: data.additional_merchant_data.clone(),
        header_payload: data.header_payload.clone(),
        connector_mandate_request_reference_id: data.connector_mandate_request_reference_id.clone(),
        authentication_id: data.authentication_id.clone(),
        psd2_sca_exemption_type: data.psd2_sca_exemption_type,
        raw_connector_response: data.raw_connector_response.clone(),
        is_payment_id_from_merchant: data.is_payment_id_from_merchant,
        l2_l3_data: data.l2_l3_data.clone(),
        minor_amount_capturable: data.minor_amount_capturable,
    }
}

pub fn generate_12_digit_number() -> u64 {
    let mut rng = rand::thread_rng();
    rng.gen_range(100_000_000_000..=999_999_999_999)
}

/// Normalizes a string by converting to lowercase, performing NFKD normalization(https://unicode.org/reports/tr15/#Description_Norm),and removing special characters and spaces.
pub fn normalize_string(value: String) -> Result<String, regex::Error> {
    let nfkd_value = value.nfkd().collect::<String>();
    let lowercase_value = nfkd_value.to_lowercase();
    static REGEX: LazyLock<Result<Regex, regex::Error>> =
        LazyLock::new(|| Regex::new(r"[^a-z0-9]"));
    let regex = REGEX.as_ref().map_err(|e| e.clone())?;
    let normalized = regex.replace_all(&lowercase_value, "").to_string();
    Ok(normalized)
}
#[cfg(feature = "frm")]
pub trait FrmTransactionRouterDataRequest {
    fn is_payment_successful(&self) -> Option<bool>;
}


impl PayoutAttemptUpdate {
    pub fn apply_changeset(self, source: PayoutAttempt) -> PayoutAttempt {
        let PayoutAttemptUpdateInternal {
            payout_token,
            connector_payout_id,
            status,
            error_message,
            error_code,
            is_eligible,
            business_country,
            business_label,
            connector,
            routing_info,
            last_modified_at,
            address_id,
            customer_id,
            merchant_connector_id,
            unified_code,
            unified_message,
            additional_payout_method_data,
            merchant_order_reference_id,
        } = self.into();
        PayoutAttempt {
            payout_token: payout_token.or(source.payout_token),
            connector_payout_id: connector_payout_id.or(source.connector_payout_id),
            status: status.unwrap_or(source.status),
            error_message: error_message.or(source.error_message),
            error_code: error_code.or(source.error_code),
            is_eligible: is_eligible.or(source.is_eligible),
            business_country: business_country.or(source.business_country),
            business_label: business_label.or(source.business_label),
            connector: connector.or(source.connector),
            routing_info: routing_info.or(source.routing_info),
            last_modified_at,
            address_id: address_id.or(source.address_id),
            customer_id: customer_id.or(source.customer_id),
            merchant_connector_id: merchant_connector_id.or(source.merchant_connector_id),
            unified_code: unified_code.or(source.unified_code),
            unified_message: unified_message.or(source.unified_message),
            additional_payout_method_data: additional_payout_method_data
                .or(source.additional_payout_method_data),
            merchant_order_reference_id: merchant_order_reference_id
                .or(source.merchant_order_reference_id),
            ..source
        }
    }
}


impl Responder {
    let flow = Flow::PaymentsRedirect;
    let (payment_id, merchant_id, connector, creds_identifier) = path.into_inner();
    let param_string = req.query_string();

    tracing::Span::current().record("payment_id", payment_id.get_string_repr());

    let payload = payments::PaymentsRedirectResponseData {
        resource_id: payment_types::PaymentIdType::PaymentIntentId(payment_id),
        merchant_id: Some(merchant_id.clone()),
        param: Some(param_string.to_string()),
        json_payload: json_payload.map(|s| s.0),
        force_sync: false,
        connector: Some(connector),
        creds_identifier: Some(creds_identifier),
    };
    let locking_action = payload.get_locking_input(flow.clone());
    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        payload,
        |state, auth: auth::AuthenticationData, req, req_state| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            <payments::PaymentRedirectCompleteAuthorize as PaymentRedirectFlow>::handle_payments_redirect_response(
                &payments::PaymentRedirectCompleteAuthorize {},
                state,
                req_state,
                merchant_context,
                req,
            )
        },
        &auth::MerchantIdAuth(merchant_id),
        locking_action,
    ))
    .await
}


impl DecisionEngineApiHandler for SRApiClient {
    async fn send_decision_engine_request<Req, Res>(
        state: &SessionState,
        http_method: services::Method,
        path: &str,
        request_body: Option<Req>,
        timeout: Option<u64>,
        events_wrapper: Option<RoutingEventsWrapper<Req>>,
    ) -> RoutingResult<RoutingEventsResponse<Res>>
    where
        Req: Serialize + Send + Sync + 'static + Clone,
        Res: Serialize + serde::de::DeserializeOwned + Send + 'static + std::fmt::Debug + Clone,
    {
        let events_response =
            build_and_send_decision_engine_http_request::<_, _, or_types::ErrorResponse>(
                state,
                http_method,
                path,
                request_body,
                timeout,
                "parsing response",
                events_wrapper,
            )
            .await?;

        let parsed_response =
            events_response
                .response
                .as_ref()
                .ok_or(errors::RoutingError::OpenRouterError(
                    "Response from decision engine API is empty".to_string(),
                ))?;
        logger::debug!(parsed_response = ?parsed_response, response_type = %std::any::type_name::<Res>(), decision_engine_request_path = %path, "decision_engine_config: Successfully parsed response from Decision Engine config API");
        Ok(events_response)
    }
}

impl DecisionEngineApiHandler for for SRApiClient


pub async fn retrieve_apple_pay_verified_domains(
    state: web::Data<AppState>,
    req: HttpRequest,
    params: web::Query<verifications::ApplepayGetVerifiedDomainsParam>,
) -> impl Responder {
    let flow = Flow::Verification;
    let merchant_id = &params.merchant_id;
    let mca_id = &params.merchant_connector_account_id;

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        merchant_id.clone(),
        |state, _: auth::AuthenticationData, _, _| {
            verification::get_verified_apple_domains_with_mid_mca_id(
                state,
                merchant_id.to_owned(),
                mca_id.clone(),
            )
        },
        auth::auth_type(
            &auth::HeaderAuth(auth::ApiKeyAuth {
                is_connected_allowed: false,
                is_platform_allowed: false,
            }),
            &auth::JWTAuth {
                permission: Permission::MerchantAccountRead,
            },
            req.headers(),
        ),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


pub async fn organization_update(
    state: web::Data<AppState>,
    req: HttpRequest,
    org_id: web::Path<common_utils::id_type::OrganizationId>,
    json_payload: web::Json<admin::OrganizationUpdateRequest>,
) -> HttpResponse {
    let flow = Flow::OrganizationUpdate;
    let organization_id = org_id.into_inner();
    let org_id = admin::OrganizationId {
        organization_id: organization_id.clone(),
    };
    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        json_payload.into_inner(),
        |state, _, req, _| update_organization(state, org_id.clone(), req),
        auth::auth_type(
            &auth::V2AdminApiAuth,
            &auth::JWTAuthOrganizationFromRoute {
                organization_id,
                required_permission: Permission::OrganizationAccountWrite,
            },
            req.headers(),
        ),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


pub struct GetTokenizePayloadResponse {
    pub lookup_key: String,
    pub get_value2: Option<bool>,
}


OpenAPI Block Path: components.schemas.AuthenticationCreateRequest
{
  "type": "object",
  "required": [
    "amount",
    "currency"
  ],
  "properties": {
    "authentication_id": {
      "type": "string",
      "description": "The unique identifier for this authentication.",
      "example": "auth_mbabizu24mvu3mela5njyhpit4",
      "nullable": true
    },
    "profile_id": {
      "type": "string",
      "description": "The business profile that is associated with this authentication",
      "nullable": true
    },
    "customer": {
      "allOf": [
        {
          "$ref": "#/components/schemas/CustomerDetails"
        }
      ],
      "nullable": true
    },
    "amount": {
      "$ref": "#/components/schemas/MinorUnit"
    },
    "authentication_connector": {
      "allOf": [
        {
          "$ref": "#/components/schemas/AuthenticationConnectors"
        }
      ],
      "nullable": true
    },
    "currency": {
      "$ref": "#/components/schemas/Currency"
    },
    "return_url": {
      "type": "string",
      "description": "The URL to which the user should be redirected after authentication.",
      "example": "https://example.com/redirect",
      "nullable": true
    },
    "force_3ds_challenge": {
      "type": "boolean",
      "description": "Force 3DS challenge.",
      "nullable": true
    },
    "psd2_sca_exemption_type": {
      "allOf": [
        {
          "$ref": "#/components/schemas/ScaExemptionType"
        }
      ],
      "nullable": true
    },
    "profile_acquirer_id": {
      "type": "string",
      "description": "Profile Acquirer ID get from profile acquirer configuration",
      "nullable": true
    },
    "acquirer_details": {
      "allOf": [
        {
          "$ref": "#/components/schemas/AcquirerDetails"
        }
      ],
      "nullable": true
    }
  }
}


pub fn new(identifier: String) -> Self {
        Self(identifier)
    }


pub struct FrmMetricsBucketIdentifier {
    pub frm_status: Option<String>,
    pub frm_name: Option<String>,
    pub frm_transaction_type: Option<String>,
    #[serde(rename = "time_range")]
    pub time_bucket: TimeRange,
    #[serde(rename = "time_bucket")]
    #[serde(with = "common_utils::custom_serde::iso8601custom")]
    pub start_time: time::PrimitiveDateTime,
}


pub struct PowertranzBaseResponse {
    transaction_type: u8,
    approved: bool,
    transaction_identifier: String,
    original_trxn_identifier: Option<String>,
    errors: Option<Vec<Error>>,
    iso_response_code: String,
    redirect_data: Option<Secret<String>>,
    response_message: String,
    order_identifier: String,
}


pub struct PayeezyPaymentsMetadata {
    transaction_tag: String,
}


pub async fn perform_eligibility_analysis_with_fallback(
    state: &SessionState,
    key_store: &domain::MerchantKeyStore,
    chosen: Vec<routing_types::RoutableConnectorChoice>,
    transaction_data: &routing::TransactionData<'_>,
    eligible_connectors: Option<Vec<api_enums::RoutableConnectors>>,
    business_profile: &domain::Profile,
) -> RoutingResult<Vec<routing_types::RoutableConnectorChoice>> {
    logger::debug!("euclid_routing: performing eligibility");
    let mut final_selection = perform_eligibility_analysis(
        state,
        key_store,
        chosen,
        transaction_data,
        eligible_connectors.as_ref(),
        business_profile.get_id(),
    )
    .await?;

    let fallback_selection = perform_fallback_routing(
        state,
        key_store,
        transaction_data,
        eligible_connectors.as_ref(),
        business_profile,
    )
    .await;

    final_selection.append(
        &mut fallback_selection
            .unwrap_or_default()
            .iter()
            .filter(|&routable_connector_choice| {
                !final_selection.contains(routable_connector_choice)
            })
            .cloned()
            .collect::<Vec<_>>(),
    );

    let final_selected_connectors = final_selection
        .iter()
        .map(|item| item.connector)
        .collect::<Vec<_>>();
    logger::debug!(final_selected_connectors_for_routing=?final_selected_connectors, "euclid_routing: List of final selected connectors for routing");

    Ok(final_selection)
}


impl TraceAssertion {
    /// Should the provided url be traced
    fn should_trace_url(&self, url: &str) -> bool {
        match &self.clauses {
            Some(clauses) => clauses.iter().all(|cur| cur.compare_url(url)),
            None => self.default,
        }
    }
}


impl api::PaymentCapture for Fiservemea {}

impl api::PaymentCapture for for Fiservemea


impl PaymentAttemptBatchNew {
//     // Used to verify compatibility with PaymentAttemptTable
//     fn convert_into_normal_attempt_insert(self) -> PaymentAttemptNew {
//         // PaymentAttemptNew {
//         //     payment_id: self.payment_id,
//         //     merchant_id: self.merchant_id,
//         //     status: self.status,
//         //     error_message: self.error_message,
//         //     surcharge_amount: self.surcharge_amount,
//         //     tax_amount: self.tax_amount,
//         //     payment_method_id: self.payment_method_id,
//         //     confirm: self.confirm,
//         //     authentication_type: self.authentication_type,
//         //     created_at: self.created_at,
//         //     modified_at: self.modified_at,
//         //     last_synced: self.last_synced,
//         //     cancellation_reason: self.cancellation_reason,
//         //     browser_info: self.browser_info,
//         //     payment_token: self.payment_token,
//         //     error_code: self.error_code,
//         //     connector_metadata: self.connector_metadata,
//         //     payment_experience: self.payment_experience,
//         //     card_network: self
//         //         .payment_method_data
//         //         .as_ref()
//         //         .and_then(|data| data.as_object())
//         //         .and_then(|card| card.get("card"))
//         //         .and_then(|v| v.as_object())
//         //         .and_then(|v| v.get("card_network"))
//         //         .and_then(|network| network.as_str())
//         //         .map(|network| network.to_string()),
//         //     payment_method_data: self.payment_method_data,
//         //     straight_through_algorithm: self.straight_through_algorithm,
//         //     preprocessing_step_id: self.preprocessing_step_id,
//         //     error_reason: self.error_reason,
//         //     multiple_capture_count: self.multiple_capture_count,
//         //     connector_response_reference_id: self.connector_response_reference_id,
//         //     amount_capturable: self.amount_capturable,
//         //     updated_by: self.updated_by,
//         //     merchant_connector_id: self.merchant_connector_id,
//         //     authentication_data: self.authentication_data,
//         //     encoded_data: self.encoded_data,
//         //     unified_code: self.unified_code,
//         //     unified_message: self.unified_message,
//         //     net_amount: self.net_amount,
//         //     external_three_ds_authentication_attempted: self
//         //         .external_three_ds_authentication_attempted,
//         //     authentication_connector: self.authentication_connector,
//         //     authentication_id: self.authentication_id,
//         //     payment_method_billing_address_id: self.payment_method_billing_address_id,
//         //     fingerprint_id: self.fingerprint_id,
//         //     charge_id: self.charge_id,
//         //     client_source: self.client_source,
//         //     client_version: self.client_version,
//         //     customer_acceptance: self.customer_acceptance,
//         //     profile_id: self.profile_id,
//         //     organization_id: self.organization_id,
//         // }
//         todo!()
//     }
// }


File: crates/router/src/core/verification/utils.rs
 Public functions: 4

use common_utils::{errors::CustomResult, id_type::PaymentId};
use error_stack::{Report, ResultExt};

use crate::{
    core::{
        errors::{self, utils::StorageErrorExt},
        utils,
    },
    logger,
    routes::SessionState,
    services::authentication::AuthenticationData,
    types::{self, storage},
};

pub async fn check_existence_and_add_domain_to_db(
    state: &SessionState,
    merchant_id: common_utils::id_type::MerchantId,
    profile_id_from_auth_layer: Option<common_utils::id_type::ProfileId>,
    merchant_connector_id: common_utils::id_type::MerchantConnectorAccountId,
    domain_from_req: Vec<String>,
) -> CustomResult<Vec<String>, errors::ApiErrorResponse> {
    let key_manager_state = &state.into();
    let key_store = state
        .store
        .get_merchant_key_store_by_merchant_id(
            key_manager_state,
            &merchant_id,
            &state.store.get_master_key().to_vec().into(),
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::InternalServerError)?;

    #[cfg(feature = "v1")]
    let merchant_connector_account = state
        .store
        .find_by_merchant_connector_account_merchant_id_merchant_connector_id(
            key_manager_state,
            &merchant_id,
            &merchant_connector_id,
            &key_store,
        )
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)?;

    #[cfg(feature = "v2")]
    let merchant_connector_account = state
        .store
        .find_merchant_connector_account_by_id(
            key_manager_state,
            &merchant_connector_id,
            &key_store,
        )
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)?;
    utils::validate_profile_id_from_auth_layer(
        profile_id_from_auth_layer,
        &merchant_connector_account,
    )?;
    let mut already_verified_domains = merchant_connector_account
        .applepay_verified_domains
        .clone()
        .unwrap_or_default();

    let mut new_verified_domains: Vec<String> = domain_from_req
        .into_iter()
        .filter(|req_domain| !already_verified_domains.contains(req_domain))
        .collect();

    already_verified_domains.append(&mut new_verified_domains);
    #[cfg(feature = "v1")]
    let updated_mca = storage::MerchantConnectorAccountUpdate::Update {
        connector_type: None,
        connector_name: None,
        connector_account_details: Box::new(None),
        test_mode: None,
        disabled: None,
        merchant_connector_id: None,
        payment_methods_enabled: None,
        metadata: None,
        frm_configs: None,
        connector_webhook_details: Box::new(None),
        applepay_verified_domains: Some(already_verified_domains.clone()),
        pm_auth_config: Box::new(None),
        connector_label: None,
        status: None,
        connector_wallets_details: Box::new(None),
        additional_merchant_data: Box::new(None),
    };
    #[cfg(feature = "v2")]
    let updated_mca = storage::MerchantConnectorAccountUpdate::Update {
        connector_type: None,
        connector_account_details: Box::new(None),
        disabled: None,
        payment_methods_enabled: None,
        metadata: None,
        frm_configs: None,
        connector_webhook_details: Box::new(None),
        applepay_verified_domains: Some(already_verified_domains.clone()),
        pm_auth_config: Box::new(None),
        connector_label: None,
        status: None,
        connector_wallets_details: Box::new(None),
        additional_merchant_data: Box::new(None),
        feature_metadata: Box::new(None),
    };
    state
        .store
        .update_merchant_connector_account(
            key_manager_state,
            merchant_connector_account,
            updated_mca.into(),
            &key_store,
        )
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable_lazy(|| {
            format!(
                "Failed while updating MerchantConnectorAccount: id: {merchant_connector_id:?}",

            )
        })?;

    Ok(already_verified_domains.clone())
}

pub fn log_applepay_verification_response_if_error(
    response: &Result<Result<types::Response, types::Response>, Report<errors::ApiClientError>>,
) {
    if let Err(error) = response.as_ref() {
        logger::error!(applepay_domain_verification_error= ?error);
    };
    response.as_ref().ok().map(|res| {
        res.as_ref()
            .map_err(|error| logger::error!(applepay_domain_verification_error= ?error))
    });
}

#[cfg(feature = "v2")]
pub async fn check_if_profile_id_is_present_in_payment_intent(
    payment_id: PaymentId,
    state: &SessionState,
    auth_data: &AuthenticationData,
) -> CustomResult<(), errors::ApiErrorResponse> {
    todo!()
}

#[cfg(feature = "v1")]
pub async fn check_if_profile_id_is_present_in_payment_intent(
    payment_id: PaymentId,
    state: &SessionState,
    auth_data: &AuthenticationData,
) -> CustomResult<(), errors::ApiErrorResponse> {
    let db = &*state.store;
    let payment_intent = db
        .find_payment_intent_by_payment_id_merchant_id(
            &state.into(),
            &payment_id,
            auth_data.merchant_account.get_id(),
            &auth_data.key_store,
            auth_data.merchant_account.storage_scheme,
        )
        .await
        .change_context(errors::ApiErrorResponse::Unauthorized)?;

    utils::validate_profile_id_from_auth_layer(auth_data.profile_id.clone(), &payment_intent)
}


impl api::revenue_recovery_v2::BillingConnectorPaymentsSyncIntegrationV2 for Recurly {}

impl api::revenue_recovery_v2::BillingConnectorPaymentsSyncIntegrationV2 for for Recurly


impl api::RefundSync for Powertranz {}


File: crates/router/src/routes/feature_matrix.rs
 Public functions: 2

use actix_web::{web, HttpRequest, Responder};
use api_models::{connector_enums::Connector, feature_matrix};
use common_enums::enums;
use hyperswitch_domain_models::{
    api::ApplicationResponse, router_response_types::PaymentMethodTypeMetadata,
};
use hyperswitch_interfaces::api::ConnectorSpecifications;
use router_env::{instrument, tracing, Flow};
use strum::IntoEnumIterator;

use crate::{
    self as app,
    core::{api_locking::LockAction, errors::RouterResponse},
    services::{api, authentication as auth, connector_integration_interface::ConnectorEnum},
    settings,
    types::api::{self as api_types, payments as payment_types},
};

#[instrument(skip_all)]
pub async fn fetch_feature_matrix(
    state: web::Data<app::AppState>,
    req: HttpRequest,
    json_payload: Option<web::Json<payment_types::FeatureMatrixRequest>>,
) -> impl Responder {
    let flow: Flow = Flow::FeatureMatrix;
    let payload = json_payload
        .map(|json_request| json_request.into_inner())
        .unwrap_or_else(|| payment_types::FeatureMatrixRequest { connectors: None });

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        payload,
        |state, (), req, _| generate_feature_matrix(state, req),
        &auth::NoAuth,
        LockAction::NotApplicable,
    ))
    .await
}

pub async fn generate_feature_matrix(
    state: app::SessionState,
    req: payment_types::FeatureMatrixRequest,
) -> RouterResponse<feature_matrix::FeatureMatrixListResponse> {
    let connector_list = req
        .connectors
        .unwrap_or_else(|| Connector::iter().collect());

    let feature_matrix_response: Vec<payment_types::ConnectorFeatureMatrixResponse> =
        connector_list
            .into_iter()
            .filter_map(|connector_name| {
                api_types::feature_matrix::FeatureMatrixConnectorData::convert_connector(
                    &connector_name.to_string(),
                )
                .inspect_err(|_| {
                    router_env::logger::warn!("Failed to fetch {:?} details", connector_name)
                })
                .ok()
                .and_then(|connector| {
                    build_connector_feature_details(&state, connector, connector_name.to_string())
                })
            })
            .collect();

    Ok(ApplicationResponse::Json(
        payment_types::FeatureMatrixListResponse {
            connector_count: feature_matrix_response.len(),
            connectors: feature_matrix_response,
        },
    ))
}

fn build_connector_feature_details(
    state: &app::SessionState,
    connector: ConnectorEnum,
    connector_name: String,
) -> Option<feature_matrix::ConnectorFeatureMatrixResponse> {
    let connector_integration_features = connector.get_supported_payment_methods();
    let supported_payment_methods =
        connector_integration_features.map(|connector_integration_feature_data| {
            connector_integration_feature_data
                .iter()
                .flat_map(|(payment_method, supported_payment_method_types)| {
                    build_payment_method_wise_feature_details(
                        state,
                        &connector_name,
                        *payment_method,
                        supported_payment_method_types,
                    )
                })
                .collect::<Vec<feature_matrix::SupportedPaymentMethod>>()
        });
    let supported_webhook_flows = connector
        .get_supported_webhook_flows()
        .map(|webhook_flows| webhook_flows.to_vec());
    let connector_about = connector.get_connector_about();

    connector_about.map(
        |connector_about| feature_matrix::ConnectorFeatureMatrixResponse {
            name: connector_name.to_uppercase(),
            display_name: connector_about.display_name.to_string(),
            description: connector_about.description.to_string(),
            integration_status: connector_about.integration_status,
            category: connector_about.connector_type,
            supported_webhook_flows,
            supported_payment_methods,
        },
    )
}

fn build_payment_method_wise_feature_details(
    state: &app::SessionState,
    connector_name: &str,
    payment_method: enums::PaymentMethod,
    supported_payment_method_types: &PaymentMethodTypeMetadata,
) -> Vec<feature_matrix::SupportedPaymentMethod> {
    supported_payment_method_types
        .iter()
        .map(|(payment_method_type, feature_metadata)| {
            let payment_method_type_config =
                state
                    .conf
                    .pm_filters
                    .0
                    .get(connector_name)
                    .and_then(|selected_connector| {
                        selected_connector.0.get(
                            &settings::PaymentMethodFilterKey::PaymentMethodType(
                                *payment_method_type,
                            ),
                        )
                    });

            let supported_countries = payment_method_type_config.and_then(|config| {
                config.country.clone().map(|set| {
                    set.into_iter()
                        .map(common_enums::CountryAlpha2::from_alpha2_to_alpha3)
                        .collect::<std::collections::HashSet<_>>()
                })
            });

            let supported_currencies =
                payment_method_type_config.and_then(|config| config.currency.clone());

            feature_matrix::SupportedPaymentMethod {
                payment_method,
                payment_method_type: *payment_method_type,
                payment_method_type_display_name: payment_method_type.to_display_name(),
                mandates: feature_metadata.mandates,
                refunds: feature_metadata.refunds,
                supported_capture_methods: feature_metadata.supported_capture_methods.clone(),
                payment_method_specific_features: feature_metadata.specific_features.clone(),
                supported_countries,
                supported_currencies,
            }
        })
        .collect()
}


impl WebhookEvents {
    pub fn server(config: AppState) -> Scope {
        web::scope("/events")
            .app_data(web::Data::new(config))
            .service(web::scope("/profile/list").service(web::resource("").route(
                web::post().to(webhook_events::list_initial_webhook_delivery_attempts_with_jwtauth),
            )))
            .service(
                web::scope("/{merchant_id}")
                    .service(web::resource("").route(
                        web::post().to(webhook_events::list_initial_webhook_delivery_attempts),
                    ))
                    .service(
                        web::scope("/{event_id}")
                            .service(web::resource("attempts").route(
                                web::get().to(webhook_events::list_webhook_delivery_attempts),
                            ))
                            .service(web::resource("retry").route(
                                web::post().to(webhook_events::retry_webhook_delivery_attempt),
                            )),
                    ),
            )
    }
}


pub struct PaymentIntentCore {
    pub payment_id: common_utils::id_type::PaymentId,
}


pub async fn construct_mandate_revoke_router_data(
    state: &SessionState,
    merchant_connector_account: helpers::MerchantConnectorAccountType,
    merchant_context: &domain::MerchantContext,
    mandate: Mandate,
) -> CustomResult<types::MandateRevokeRouterData, errors::ApiErrorResponse> {
    let auth_type: types::ConnectorAuthType = merchant_connector_account
        .get_connector_account_details()
        .parse_value("ConnectorAuthType")
        .change_context(errors::ApiErrorResponse::InternalServerError)?;
    let router_data = types::RouterData {
        flow: PhantomData,
        merchant_id: merchant_context.get_merchant_account().get_id().clone(),
        customer_id: Some(mandate.customer_id),
        tenant_id: state.tenant.tenant_id.clone(),
        connector_customer: None,
        connector: mandate.connector,
        payment_id: mandate
            .original_payment_id
            .unwrap_or_else(|| {
                common_utils::id_type::PaymentId::get_irrelevant_id("mandate_revoke")
            })
            .get_string_repr()
            .to_owned(),
        attempt_id: IRRELEVANT_ATTEMPT_ID_IN_MANDATE_REVOKE_FLOW.to_string(),
        status: diesel_models::enums::AttemptStatus::default(),
        payment_method: diesel_models::enums::PaymentMethod::default(),
        connector_auth_type: auth_type,
        description: None,
        address: PaymentAddress::default(),
        auth_type: diesel_models::enums::AuthenticationType::default(),
        connector_meta_data: None,
        connector_wallets_details: None,
        amount_captured: None,
        minor_amount_captured: None,
        access_token: None,
        session_token: None,
        reference_id: None,
        payment_method_token: None,
        recurring_mandate_payment_data: None,
        preprocessing_id: None,
        payment_method_balance: None,
        connector_api_version: None,
        payment_method_status: None,
        request: types::MandateRevokeRequestData {
            mandate_id: mandate.mandate_id,
            connector_mandate_id: mandate.connector_mandate_id,
        },
        response: Err(types::ErrorResponse::get_not_implemented()),
        connector_request_reference_id:
            IRRELEVANT_CONNECTOR_REQUEST_REFERENCE_ID_IN_MANDATE_REVOKE_FLOW.to_string(),
        test_mode: None,
        connector_http_status_code: None,
        external_latency: None,
        apple_pay_flow: None,
        frm_metadata: None,
        #[cfg(feature = "payouts")]
        payout_method_data: None,
        #[cfg(feature = "payouts")]
        quote_id: None,
        refund_id: None,
        dispute_id: None,
        connector_response: None,
        integrity_check: Ok(()),
        additional_merchant_data: None,
        header_payload: None,
        connector_mandate_request_reference_id: None,
        authentication_id: None,
        psd2_sca_exemption_type: None,
        raw_connector_response: None,
        is_payment_id_from_merchant: None,
        l2_l3_data: None,
        minor_amount_capturable: None,
    };

    Ok(router_data)
}


impl PaymentMethodSession {
    pub fn apply_changeset(self, update_session: PaymentMethodsSessionUpdateInternal) -> Self {
        let Self {
            id,
            customer_id,
            billing,
            psp_tokenization,
            network_tokenization,
            tokenization_data,
            expires_at,
            return_url,
            associated_payment_methods,
            associated_payment,
            associated_token_id,
        } = self;

        Self {
            id,
            customer_id,
            billing: update_session.billing.or(billing),
            psp_tokenization: update_session.psp_tokenization.or(psp_tokenization),
            network_tokenization: update_session.network_tokenization.or(network_tokenization),
            tokenization_data: update_session.tokenzation_data.or(tokenization_data),
            expires_at,
            return_url,
            associated_payment_methods,
            associated_payment,
            associated_token_id,
        }
    }
}


Documentation: api-reference/v2/payment-method-session/payment-method-session--create.mdx
# Type: Doc File

---
openapi: post /v2/payment-method-sessions
---


pub struct MetricsBucketResponse {
    #[serde(flatten)]
    pub values: SdkEventMetricsBucketValue,
    #[serde(flatten)]
    pub dimensions: SdkEventMetricsBucketIdentifier,
}


File: crates/router_derive/src/macros/diesel.rs
 Public functions: 1

use proc_macro2::{Span, TokenStream};
use quote::{format_ident, quote, ToTokens};
use syn::{parse::Parse, Data, DeriveInput, ItemEnum};

use crate::macros::helpers;

pub(crate) fn diesel_enum_text_derive_inner(ast: &DeriveInput) -> syn::Result<TokenStream> {
    let name = &ast.ident;
    let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();

    match &ast.data {
        Data::Enum(_) => (),
        _ => return Err(helpers::non_enum_error()),
    };

    Ok(quote! {

        #[automatically_derived]
        impl #impl_generics ::diesel::serialize::ToSql<::diesel::sql_types::Text, ::diesel::pg::Pg> for #name #ty_generics
        #where_clause
        {
            fn to_sql<'b>(&'b self, out: &mut ::diesel::serialize::Output<'b, '_, ::diesel::pg::Pg>) -> ::diesel::serialize::Result {
                use ::std::io::Write;

                out.write_all(self.to_string().as_bytes())?;
                Ok(::diesel::serialize::IsNull::No)
            }
        }

        #[automatically_derived]
        impl #impl_generics ::diesel::deserialize::FromSql<::diesel::sql_types::Text, ::diesel::pg::Pg> for #name #ty_generics
        #where_clause
        {
            fn from_sql(value: ::diesel::pg::PgValue) -> diesel::deserialize::Result<Self> {
                use ::core::str::FromStr;

                Self::from_str(::core::str::from_utf8(value.as_bytes())?)
                    .map_err(|_| "Unrecognized enum variant".into())
            }
        }
    })
}

pub(crate) fn diesel_enum_db_enum_derive_inner(ast: &DeriveInput) -> syn::Result<TokenStream> {
    let name = &ast.ident;
    let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();

    match &ast.data {
        Data::Enum(_) => (),
        _ => return Err(helpers::non_enum_error()),
    };

    let struct_name = format_ident!("Db{name}");
    let type_name = format!("{name}");

    Ok(quote! {

        #[derive(::core::clone::Clone, ::core::marker::Copy, ::core::fmt::Debug, ::diesel::QueryId, ::diesel::SqlType)]
        #[diesel(postgres_type(name = #type_name))]
        pub struct #struct_name;

        #[automatically_derived]
        impl #impl_generics ::diesel::serialize::ToSql<#struct_name, ::diesel::pg::Pg> for #name #ty_generics
        #where_clause
        {
            fn to_sql<'b>(&'b self, out: &mut ::diesel::serialize::Output<'b, '_, ::diesel::pg::Pg>) -> ::diesel::serialize::Result {
                use ::std::io::Write;

                out.write_all(self.to_string().as_bytes())?;
                Ok(::diesel::serialize::IsNull::No)
            }
        }

        #[automatically_derived]
        impl #impl_generics ::diesel::deserialize::FromSql<#struct_name, ::diesel::pg::Pg> for #name #ty_generics
        #where_clause
        {
            fn from_sql(value: ::diesel::pg::PgValue) -> diesel::deserialize::Result<Self> {
                use ::core::str::FromStr;

                Self::from_str(::core::str::from_utf8(value.as_bytes())?)
                    .map_err(|_| "Unrecognized enum variant".into())
            }
        }
    })
}

mod diesel_keyword {
    use syn::custom_keyword;

    custom_keyword!(storage_type);
    custom_keyword!(db_enum);
    custom_keyword!(text);
}

#[derive(Debug, strum::EnumString, strum::EnumIter, strum::Display)]
#[strum(serialize_all = "snake_case")]
pub enum StorageType {
    /// Store the Enum as Text value in the database
    Text,
    /// Store the Enum as Enum in the database. This requires a corresponding enum to be created
    /// in the database with the same name
    DbEnum,
}

#[derive(Debug)]
pub enum DieselEnumMeta {
    StorageTypeEnum {
        keyword: diesel_keyword::storage_type,
        value: StorageType,
    },
}

impl Parse for StorageType {
    fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
        let text = input.parse::<syn::LitStr>()?;
        let value = text.value();

        value.as_str().parse().map_err(|_| {
            syn::Error::new_spanned(
                &text,
                format!(
                    "Unexpected value for storage_type: `{value}`. Possible values are `{}`",
                    helpers::get_possible_values_for_enum::<Self>()
                ),
            )
        })
    }
}

impl DieselEnumMeta {
    pub fn get_storage_type(&self) -> &StorageType {
        match self {
            Self::StorageTypeEnum { value, .. } => value,
        }
    }
}

impl Parse for DieselEnumMeta {
    fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
        let lookahead = input.lookahead1();
        if lookahead.peek(diesel_keyword::storage_type) {
            let keyword = input.parse()?;
            input.parse::<syn::Token![=]>()?;
            let value = input.parse()?;
            Ok(Self::StorageTypeEnum { keyword, value })
        } else {
            Err(lookahead.error())
        }
    }
}

impl ToTokens for DieselEnumMeta {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        match self {
            Self::StorageTypeEnum { keyword, .. } => keyword.to_tokens(tokens),
        }
    }
}

trait DieselDeriveInputExt {
    /// Get all the error metadata associated with an enum.
    fn get_metadata(&self) -> syn::Result<Vec<DieselEnumMeta>>;
}

impl DieselDeriveInputExt for DeriveInput {
    fn get_metadata(&self) -> syn::Result<Vec<DieselEnumMeta>> {
        helpers::get_metadata_inner("storage_type", &self.attrs)
    }
}

pub(crate) fn diesel_enum_derive_inner(ast: &DeriveInput) -> syn::Result<TokenStream> {
    let storage_type = ast.get_metadata()?;

    match storage_type
        .first()
        .ok_or(syn::Error::new(
            Span::call_site(),
            "Storage type must be specified",
        ))?
        .get_storage_type()
    {
        StorageType::Text => diesel_enum_text_derive_inner(ast),
        StorageType::DbEnum => diesel_enum_db_enum_derive_inner(ast),
    }
}

/// Based on the storage type, derive appropriate diesel traits
/// This will add the appropriate #[diesel(sql_type)]
/// Since the `FromSql` and `ToSql` have to be derived for all the enums, this will add the
/// `DieselEnum` derive trait.
pub(crate) fn diesel_enum_attribute_macro(
    diesel_enum_meta: DieselEnumMeta,
    item: &ItemEnum,
) -> syn::Result<TokenStream> {
    let diesel_derives =
        quote!(#[derive(diesel::AsExpression, diesel::FromSqlRow, router_derive::DieselEnum) ]);

    match diesel_enum_meta {
        DieselEnumMeta::StorageTypeEnum {
            value: storage_type,
            ..
        } => match storage_type {
            StorageType::Text => Ok(quote! {
                #diesel_derives
                #[diesel(sql_type = ::diesel::sql_types::Text)]
                #[storage_type(storage_type = "text")]
                #item
            }),
            StorageType::DbEnum => {
                let name = &item.ident;
                let type_name = format_ident!("Db{name}");
                Ok(quote! {
                    #diesel_derives
                    #[diesel(sql_type = #type_name)]
                    #[storage_type(storage_type= "db_enum")]
                    #item
                })
            }
        },
    }
}


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


impl api::RefundExecute for Redsys {}


pub struct DomainId(usize);


impl api::Refund for Santander {}