- **connector:**
  - [Stax] Use connector_response_reference_id as reference to merchant ([#2415](https://github.com/juspay/hyperswitch/pull/2415)) ([`099b241`](https://github.com/juspay/hyperswitch/commit/099b241096c69879a805ca81b1c5a23118e10b52))
  - [PowerTranz] Use connector_response_reference_id as reference to merchant ([#2413](https://github.com/juspay/hyperswitch/pull/2413)) ([`0d703c7`](https://github.com/juspay/hyperswitch/commit/0d703c7ab85c68f433767a70a0feabe8daa4f24c))
  - [Payeezy] Use connector_response_reference_id as reference to merchant ([#2410](https://github.com/juspay/hyperswitch/pull/2410)) ([`485c09d`](https://github.com/juspay/hyperswitch/commit/485c09d16743d73b446d6313f0ee6462c8a77028))
  - [Square] Use reference_id as reference to merchant ([#2434](https://github.com/juspay/hyperswitch/pull/2434)) ([`591c9b7`](https://github.com/juspay/hyperswitch/commit/591c9b70d9b7f8df5d7f5d2cb2d19cfaa1457fe1))
- **router:**
  - Remove unnecessary lookups in refund and payment_attempt kv flow ([#2425](https://github.com/juspay/hyperswitch/pull/2425)) ([`f720aec`](https://github.com/juspay/hyperswitch/commit/f720aecf1fb676cec71e636b877a46f9791d713a))
  - [OpenNode] response reference id ([#2416](https://github.com/juspay/hyperswitch/pull/2416)) ([`3bfea72`](https://github.com/juspay/hyperswitch/commit/3bfea72df34f4ce0ffdb61e49960fdf09b96eb5a))
  - Add profile id and extra filters in lists ([#2379](https://github.com/juspay/hyperswitch/pull/2379)) ([`ab2cde7`](https://github.com/juspay/hyperswitch/commit/ab2cde799371a66eb045cf8b20431b3b108dac44))

### Bug Fixes

- **CI:** Fix spell check for CI pull request ([#2439](https://github.com/juspay/hyperswitch/pull/2439)) ([`04f2e11`](https://github.com/juspay/hyperswitch/commit/04f2e11cd4f3fd327408cddec36ccf4fb486b935))
- **router:** Merchant account delete does not delete the merchant_key_store ([#2367](https://github.com/juspay/hyperswitch/pull/2367)) ([`35f7ce0`](https://github.com/juspay/hyperswitch/commit/35f7ce0f4d9e16184e2bb94360d3ced60f8b5af2))

### Refactors

- **config:** Update payment method filter for apple pay ([#2423](https://github.com/juspay/hyperswitch/pull/2423)) ([`d177b4d`](https://github.com/juspay/hyperswitch/commit/d177b4d94f08fb8ef44b5c07ec1bdc771baa016d))
- **payment_methods:** Add `requires_cvv` config while creating merchant account ([#2431](https://github.com/juspay/hyperswitch/pull/2431)) ([`6e5ab0d`](https://github.com/juspay/hyperswitch/commit/6e5ab0d121d6345f18bccc7f917064caa2737475))
- **webhook:** Add a function to retrieve payment_id ([#2447](https://github.com/juspay/hyperswitch/pull/2447)) ([`409913f`](https://github.com/juspay/hyperswitch/commit/409913fd75076e4ee1dac1e4dc5b2f164528bc23))

### Build System / Dependencies

- **deps:** Address `undeclared crate or module` errors on Windows for `scheduler` crate ([#2411](https://github.com/juspay/hyperswitch/pull/2411)) ([`4225238`](https://github.com/juspay/hyperswitch/commit/422523848e6516643a6beef1ba15af4e967f0c5b))

**Full Changelog:** [`v1.49.0...v1.50.0`](https://github.com/juspay/hyperswitch/compare/v1.49.0...v1.50.0)

- - -

## 1.49.0 (2023-10-03)

### Features

- **connector:** [Nuvei] Add order id as the reference id ([#2408](https://github.com/juspay/hyperswitch/pull/2408)) ([`d5d876b`](https://github.com/juspay/hyperswitch/commit/d5d876b821187648994ea53c358467966e99cd23))
- **pm_auth:** Added pm_auth_config to merchant_connector_account ([#2183](https://github.com/juspay/hyperswitch/pull/2183)) ([`abfdea2`](https://github.com/juspay/hyperswitch/commit/abfdea20b06a8804ec83fe9431f9a034465bb924))
- **pm_list:** [Trustpay] add bank_redirect -  blik pm type required field info for trustpay ([#2390](https://github.com/juspay/hyperswitch/pull/2390)) ([`d81762a`](https://github.com/juspay/hyperswitch/commit/d81762a8b430ca1f197d7dabb26167f54e235735))
- **webhooks:** Webhooks effect tracker ([#2260](https://github.com/juspay/hyperswitch/pull/2260)) ([`5048d24`](https://github.com/juspay/hyperswitch/commit/5048d248e59b8ecaf8585ffd5134953cf62e74ef))

### Bug Fixes

- **CI:** Fix spell check for CI pull request  ([#2420](https://github.com/juspay/hyperswitch/pull/2420)) ([`3b10b1c`](https://github.com/juspay/hyperswitch/commit/3b10b1c473209e36183271a81eb9014a8f5cddfa))
- **cards:** Allow card cvc 000 ([#2387](https://github.com/juspay/hyperswitch/pull/2387)) ([`f0dc374`](https://github.com/juspay/hyperswitch/commit/f0dc37438b7a6c4b25acff941aca13545217d307))
- **configs:** Add `lock_settings` in `docker_compose.toml` ([#2396](https://github.com/juspay/hyperswitch/pull/2396)) ([`14fec5c`](https://github.com/juspay/hyperswitch/commit/14fec5c3980397079fe8861caca589157a8ba242))
- **connector:** [noon] add connector_auth params and update description ([#2429](https://github.com/juspay/hyperswitch/pull/2429)) ([`0aa6b30`](https://github.com/juspay/hyperswitch/commit/0aa6b30d2c9056e9a21a88bdc064daa7e8659bd6))
- **payment_methods:** prioritized `apple_pay_combined` deserialization over `apple_pay` ([#2393](https://github.com/juspay/hyperswitch/pull/2393)) ([`f12ce9c`](https://github.com/juspay/hyperswitch/commit/f12ce9c72d94674e0ae0ec7f1c91d8b5c43481e8))
- Temp support for ach gocardless with existing api contracts ([#2395](https://github.com/juspay/hyperswitch/pull/2395)) ([`d43fbcc`](https://github.com/juspay/hyperswitch/commit/d43fbccd54011d0de6f8d39adbd264d9ada77e7e))

### Refactors

- **connector:**
  - [Klarna] Expand wildcard match arms ([#2403](https://github.com/juspay/hyperswitch/pull/2403)) ([`89cb63b`](https://github.com/juspay/hyperswitch/commit/89cb63be3328010d26b5f6322449fc50e80593e4))
  - [Klarna] Enhance currency Mapping with ConnectorCurrencyCommon Trait ([#2414](https://github.com/juspay/hyperswitch/pull/2414)) ([`ee7efd0`](https://github.com/juspay/hyperswitch/commit/ee7efd05adbe14bab1d2862d7ab2bf244c226433))
  - [Cryptopay] Update PSync with connector_request_reference_id  ([#2388](https://github.com/juspay/hyperswitch/pull/2388)) ([`3680541`](https://github.com/juspay/hyperswitch/commit/36805411772da00719a716d05c650f10ca990d49))
- **router:** Add `#[cfg(not(feature = "kms"))]` feature flag to test the simplified apple pay flow locally ([#2200](https://github.com/juspay/hyperswitch/pull/2200)) ([`e5ad9c5`](https://github.com/juspay/hyperswitch/commit/e5ad9c5c35f386486afedded90c46793196a17d0))

### Testing

- **postman:** Update postman collection files ([`34099ba`](https://github.com/juspay/hyperswitch/commit/34099baa2ec2f73598c4433b0a481dec3fde8c05))

### Documentation

- **README:**
  - Include Hacktoberfest information ([#2386](https://github.com/juspay/hyperswitch/pull/2386)) ([`e8eb929`](https://github.com/juspay/hyperswitch/commit/e8eb929d5b4d99d09940532e3abbca2b811bcf36))
  - Fixed TOC links ([#2402](https://github.com/juspay/hyperswitch/pull/2402)) ([`c81d8e9`](https://github.com/juspay/hyperswitch/commit/c81d8e9a180da8f71d156d39c9f85847f6d7a572))

### Miscellaneous Tasks

- **deps:** Bump webpki from 0.22.0 to 0.22.2 ([#2419](https://github.com/juspay/hyperswitch/pull/2419)) ([`6bf0e75`](https://github.com/juspay/hyperswitch/commit/6bf0e75b69608ea07fd7601906982a19cdc81400))

**Full Changelog:** [`v1.48.1+hotfix.1...v1.49.0`](https://github.com/juspay/hyperswitch/compare/v1.48.1+hotfix.1...v1.49.0)

- - -

## 1.48.1 (2023-09-28)

### Bug Fixes

- [Gocardless] add region in customer create request based on country ([#2389](https://github.com/juspay/hyperswitch/pull/2389)) ([`c293cb6`](https://github.com/juspay/hyperswitch/commit/c293cb6ffafd61702ee16233cf06a206c0093f3d))

**Full Changelog:** [`v1.48.0...v1.48.1`](https://github.com/juspay/hyperswitch/compare/v1.48.0...v1.48.1)

- - -

## 1.48.0 (2023-09-27)

### Features

- **core:** Create surcharge_metadata field in payment attempt ([#2371](https://github.com/juspay/hyperswitch/pull/2371)) ([`934542e`](https://github.com/juspay/hyperswitch/commit/934542e92625620d71b940e99d4ae58239a60ce4))
- **router:**
  - Append payment_id to secondary key for payment_intent in kv flow ([#2378](https://github.com/juspay/hyperswitch/pull/2378)) ([`ee91552`](https://github.com/juspay/hyperswitch/commit/ee9155208d6c0a3d5d5422b469bfa7a80671cd86))
  - Pass customers address in retrieve customer ([#2376](https://github.com/juspay/hyperswitch/pull/2376)) ([`f6cfb05`](https://github.com/juspay/hyperswitch/commit/f6cfb05fa042b5f68a5cb6fa17090d2beb91303b))

### Bug Fixes

- **db:** Merchant_account cache invalidation based on publishable_key ([#2365](https://github.com/juspay/hyperswitch/pull/2365)) ([`22a8291`](https://github.com/juspay/hyperswitch/commit/22a8291ea66bc564218af0a4a2695eef70ce6790))
- **router:** Allow address updates in payments update flow ([#2375](https://github.com/juspay/hyperswitch/pull/2375)) ([`0d3dd00`](https://github.com/juspay/hyperswitch/commit/0d3dd0033c5ec9eabc967cb1872f0699546aba89))

### Refactors

- **connector:**
  - [Payme]Enhance currency Mapping with ConnectorCurrencyCommon Trait  ([#2194](https://github.com/juspay/hyperswitch/pull/2194)) ([`77b51d5`](https://github.com/juspay/hyperswitch/commit/77b51d5cbe531526f2f20a0ee4a78e95b00d87de))
  - [bluesnap] add refund status and webhooks ([#2374](https://github.com/juspay/hyperswitch/pull/2374)) ([`fe43458`](https://github.com/juspay/hyperswitch/commit/fe43458ddc0fa1cc31f2b326056baea54af57136))
- Insert requires cvv config to configs table if not found in db ([#2208](https://github.com/juspay/hyperswitch/pull/2208)) ([`68b3310`](https://github.com/juspay/hyperswitch/commit/68b3310993c5196f9f9038f27c5cd7dad82b24d1))

**Full Changelog:** [`v1.47.0...v1.48.0`](https://github.com/juspay/hyperswitch/compare/v1.47.0...v1.48.0)

- - -

## 1.47.0 (2023-09-27)

### Features

- **connector_response:** Kv for connector response table ([#2207](https://github.com/juspay/hyperswitch/pull/2207)) ([`cefa291`](https://github.com/juspay/hyperswitch/commit/cefa291c00c7d4a40213cc6c6087946c031ae0b5))

### Bug Fixes

- **connector:**
  - Make webhook source verification mandatory for adyen ([#2360](https://github.com/juspay/hyperswitch/pull/2360)) ([`3d7e22a`](https://github.com/juspay/hyperswitch/commit/3d7e22a4f106e4d7c4224fecf455e2f2aa417cd0))
  - [noon] Create psync struct from webhook resource object ([#2370](https://github.com/juspay/hyperswitch/pull/2370)) ([`f12a438`](https://github.com/juspay/hyperswitch/commit/f12a43817787faedfdca26ec7f956bf5734c5ee3))
- **merchant_connector_account:** Use appropriate key when redacting ([#2363](https://github.com/juspay/hyperswitch/pull/2363)) ([`54645cd`](https://github.com/juspay/hyperswitch/commit/54645cdbf422d59b8751fa9dbb9a61cd72770b0a))
- **router:**
  - Fix refunds and payment_attempts kv flow ([#2362](https://github.com/juspay/hyperswitch/pull/2362)) ([`ef0df71`](https://github.com/juspay/hyperswitch/commit/ef0df7195d9a7c7cd384f6df9eb5a8b886914e2d))
  - Removed dynamic error messages ([#2168](https://github.com/juspay/hyperswitch/pull/2168)) ([`9c9d453`](https://github.com/juspay/hyperswitch/commit/9c9d45353596edb5dc5c19e1a6d8d42d05bae78c))
- [stripe] Add customer balance in StripePaymentMethodDetailsResponse ([#2369](https://github.com/juspay/hyperswitch/pull/2369)) ([`67a3e8f`](https://github.com/juspay/hyperswitch/commit/67a3e8f534aa98a7331cb20a3877579efed6a348))

### Refactors

- **connector:**
  - [bluesnap]Enhance currency Mapping with ConnectorCurrencyCommon Trait ([#2193](https://github.com/juspay/hyperswitch/pull/2193)) ([`6db60b8`](https://github.com/juspay/hyperswitch/commit/6db60b8cd4319d0246c72494fa65082108ffd06e))
  - [Zen] Enhance currency Mapping with ConnectorCurrencyCommon Trait ([#2196](https://github.com/juspay/hyperswitch/pull/2196)) ([`7fd79e0`](https://github.com/juspay/hyperswitch/commit/7fd79e05d54e6f135fbd4151d6638060660e6c85))
  - [Paypal]Enhance currency Mapping with ConnectorCurrencyCommon Trait  ([#2191](https://github.com/juspay/hyperswitch/pull/2191)) ([`2e97869`](https://github.com/juspay/hyperswitch/commit/2e97869fa0e284e1ab3bcaf940b627acf47d98e3))
  - [Cryptopay]Enhance currency Mapping with ConnectorCurrencyCommon Trait ([#2195](https://github.com/juspay/hyperswitch/pull/2195)) ([`d8c3845`](https://github.com/juspay/hyperswitch/commit/d8c384573e1f31ed4c8fd252b8d753a04a4df75d))

### Miscellaneous Tasks

- **config:** [Multisafepay] Add configs for card mandates for Multisafepay ([#2372](https://github.com/juspay/hyperswitch/pull/2372)) ([`af3b9e9`](https://github.com/juspay/hyperswitch/commit/af3b9e90dbc733b436f84e47ebd62ef0b467c39c))

**Full Changelog:** [`v1.46.0...v1.47.0`](https://github.com/juspay/hyperswitch/compare/v1.46.0...v1.47.0)

- - -

## 1.46.0 (2023-09-25)

### Features

- **payment_attempt:** Add kv for find last successful attempt ([#2206](https://github.com/juspay/hyperswitch/pull/2206)) ([`d3157f0`](https://github.com/juspay/hyperswitch/commit/d3157f0bd6a0246c28182c88335d95ed6ae298a9))
- **payments:** Add api locking for payments core ([#1898](https://github.com/juspay/hyperswitch/pull/1898)) ([`5d66156`](https://github.com/juspay/hyperswitch/commit/5d661561322a21f792e2cdb2ae8c30de96ce7d02))

### Bug Fixes

- **compatibility:** Update BillingDetails mappings in SCL ([#1926](https://github.com/juspay/hyperswitch/pull/1926)) ([`a48f986`](https://github.com/juspay/hyperswitch/commit/a48f9865bcd29d5c3fc5c380dde34b11c6bb254f))
- **connector:** [stripe] use display impl for expiry date  ([#2359](https://github.com/juspay/hyperswitch/pull/2359)) ([`35622af`](https://github.com/juspay/hyperswitch/commit/35622aff7a042764729565db1ed5aca2257603ba))
- **drainer:** Ignore errors in case the stream is empty ([#2261](https://github.com/juspay/hyperswitch/pull/2261)) ([`53de86f`](https://github.com/juspay/hyperswitch/commit/53de86f60d14981087626e1a2a5856089b6f3899))
- Add health metric to drainer ([#2217](https://github.com/juspay/hyperswitch/pull/2217)) ([`4e8471b`](https://github.com/juspay/hyperswitch/commit/4e8471be501806ceeb96c7683be00600c3c1a0d2))

### Refactors

- Enable `logs` feature flag in router crate ([#2358](https://github.com/juspay/hyperswitch/pull/2358)) ([`e4af381`](https://github.com/juspay/hyperswitch/commit/e4af3812d55689aefb5bb8ed6f12a6c9c0643a51))

### Testing

- **postman:** Update postman collection files ([`d7affab`](https://github.com/juspay/hyperswitch/commit/d7affab455adf1eeccaca3005797a81e51c902ac))

**Full Changelog:** [`v1.45.0...v1.46.0`](https://github.com/juspay/hyperswitch/compare/v1.45.0...v1.46.0)

- - -

## 1.45.0 (2023-09-22)

### Features

- **router:** Add mertics to apple pay flow ([#2235](https://github.com/juspay/hyperswitch/pull/2235)) ([`b9f25c4`](https://github.com/juspay/hyperswitch/commit/b9f25c4a4ee540fe13257df193f9f921233156a6))

### Bug Fixes

- **router:** Fix attempt status for technical failures in psync flow ([#2252](https://github.com/juspay/hyperswitch/pull/2252)) ([`2b8bd03`](https://github.com/juspay/hyperswitch/commit/2b8bd03a7243c887c17be658f1d9e9faa462b0c7))

### Refactors

- **connector:**
  - [Checkout]Enhance currency Mapping with ConnectorCurrencyCommon Trait ([#2192](https://github.com/juspay/hyperswitch/pull/2192)) ([`aa8d0dd`](https://github.com/juspay/hyperswitch/commit/aa8d0ddda17adb7c87cea9ff5fbf83b8d0e7fde1))
  - [Trustpay] Enhance currency Mapping with ConnectorCurrencyCommon Trait ([#2197](https://github.com/juspay/hyperswitch/pull/2197)) ([`583b9aa`](https://github.com/juspay/hyperswitch/commit/583b9aa33b15f09cf8ea61b4e6dee002fb562e03))
- **core:** Eliminate business profile database queries in payments confirm flow ([#2190](https://github.com/juspay/hyperswitch/pull/2190)) ([`90e4392`](https://github.com/juspay/hyperswitch/commit/90e43929a0c05e39feac4f13d75b2eea60b858a0))

**Full Changelog:** [`v1.44.0...v1.45.0`](https://github.com/juspay/hyperswitch/compare/v1.44.0...v1.45.0)

- - -

## 1.44.0 (2023-09-22)

### Features

- **connector:** [Trustpay] Add descriptor for card payment method for trustpay ([#2256](https://github.com/juspay/hyperswitch/pull/2256)) ([`b9ddc4f`](https://github.com/juspay/hyperswitch/commit/b9ddc4fb69396a2ced73bc24e3d947eb8c4e091a))
- **db:** Add find_config_by_key_unwrap_or ([#2214](https://github.com/juspay/hyperswitch/pull/2214)) ([`2bd2526`](https://github.com/juspay/hyperswitch/commit/2bd25261b43b8b89ff2042e944ffa6008cc77c8f))

### Bug Fixes

- **connector:** Fix dispute webhook failure bug in checkout during get_webhook_resource_object ([#2257](https://github.com/juspay/hyperswitch/pull/2257)) ([`1d73be0`](https://github.com/juspay/hyperswitch/commit/1d73be08fb3a747ab22ee42eed9f396d78a949dd))

### Refactors

- **connector:**
  - [Stripe] refactor stripe payment method not implemented errors ([#1927](https://github.com/juspay/hyperswitch/pull/1927)) ([`417f793`](https://github.com/juspay/hyperswitch/commit/417f793284a11218fc520319ed717759f60e3934))
  - [Adyen] Enhance currency Mapping with ConnectorCurrencyCommon Trait  ([#2209](https://github.com/juspay/hyperswitch/pull/2209)) ([`3d18f20`](https://github.com/juspay/hyperswitch/commit/3d18f2062e5d7c14fc5725547eeaf80d7b2a86da))

### Miscellaneous Tasks

- **CODEOWNERS:** Update CODEOWNERS ([#2254](https://github.com/juspay/hyperswitch/pull/2254)) ([`7af4c92`](https://github.com/juspay/hyperswitch/commit/7af4c92ef25b8e2b71a6839fcd80925c09897779))
- **deps:** Bump phonenumber from 0.3.2+8.13.9 to 0.3.3+8.13.9 ([#2255](https://github.com/juspay/hyperswitch/pull/2255)) ([`8f3721d`](https://github.com/juspay/hyperswitch/commit/8f3721d16b27962923bff0968f7074cef2471e36))

**Full Changelog:** [`v1.43.1...v1.44.0`](https://github.com/juspay/hyperswitch/compare/v1.43.1...v1.44.0)

- - -

## 1.43.1 (2023-09-21)

### Bug Fixes

- Add flow_name setter ([#2234](https://github.com/juspay/hyperswitch/pull/2234)) ([`30e2c90`](https://github.com/juspay/hyperswitch/commit/30e2c906724a610ec5072e3a103eb3ce21a5ef0e))

**Full Changelog:** [`v1.43.0...v1.43.1`](https://github.com/juspay/hyperswitch/compare/v1.43.0...v1.43.1)

- - -

## 1.43.0 (2023-09-21)

### Features


pub struct DebitRoutingRequestData {
    pub co_badged_card_networks_info: Vec<common_enums::CardNetwork>,
    pub issuer_country: common_enums::CountryAlpha2,
    pub is_regulated: bool,
    pub regulated_name: Option<common_enums::RegulatedName>,
    pub card_type: common_enums::CardType,
}


pub fn get_merchant_details_as_secret(
        &self,
    ) -> CustomResult<Option<pii::SecretSerdeValue>, errors::ParsingError> {
        self.merchant_details
            .as_ref()
            .map(|merchant_details| merchant_details.encode_to_value().map(Secret::new))
            .transpose()
    }


impl api::Payment for Wellsfargo {}


pub struct ThemeData {
    settings: Settings,
    urls: Option<Urls>,
}


impl api::RefundExecute for Paytm {}


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


pub async fn trigger_payouts_webhook(
    state: &SessionState,
    merchant_context: &domain::MerchantContext,
    payout_response: &api_models::payouts::PayoutCreateResponse,
) -> RouterResult<()> {
    let key_manager_state = &(state).into();
    let profile_id = &payout_response.profile_id;
    let business_profile = state
        .store
        .find_business_profile_by_profile_id(
            key_manager_state,
            merchant_context.get_merchant_key_store(),
            profile_id,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound {
            id: profile_id.get_string_repr().to_owned(),
        })?;

    let status = &payout_response.status;
    let should_trigger_webhook = business_profile
        .get_payout_webhook_statuses()
        .contains(status);

    if should_trigger_webhook {
        let event_type = (*status).into();
        if let Some(event_type) = event_type {
            let cloned_merchant_context = merchant_context.clone();
            let cloned_state = state.clone();
            let cloned_response = payout_response.clone();

            // This spawns this futures in a background thread, the exception inside this future won't affect
            // the current thread and the lifecycle of spawn thread is not handled by runtime.
            // So when server shutdown won't wait for this thread's completion.
            tokio::spawn(
                async move {
                    let primary_object_created_at = cloned_response.created;
                    Box::pin(webhooks_core::create_event_and_trigger_outgoing_webhook(
                        cloned_state,
                        cloned_merchant_context,
                        business_profile,
                        event_type,
                        diesel_models::enums::EventClass::Payouts,
                        cloned_response.payout_id.get_string_repr().to_owned(),
                        diesel_models::enums::EventObjectType::PayoutDetails,
                        webhooks::OutgoingWebhookContent::PayoutDetails(Box::new(cloned_response)),
                        primary_object_created_at,
                    ))
                    .await
                }
                .in_current_span(),
            );
        } else {
            logger::warn!("Outgoing webhook not sent because of missing event type status mapping");
        }
    }
    Ok(())
}


impl Default for EliminationRoutingConfig {
    fn default() -> Self {
        Self {
            params: Some(vec![DynamicRoutingConfigParams::PaymentMethod]),
            elimination_analyser_config: Some(EliminationAnalyserConfig {
                bucket_size: Some(5),
                bucket_leak_interval_in_secs: Some(60),
            }),
            decision_engine_configs: None,
        }
    }
}

impl Default for for EliminationRoutingConfig


pub struct SignifydFrmMetadata {
    pub total_shipping_cost: Option<i64>,
    pub fulfillment_method: Option<FulfillmentMethod>,
    pub coverage_request: Option<CoverageRequests>,
    pub order_channel: OrderChannel,
}


File: crates/hyperswitch_domain_models/src/payouts/payout_attempt.rs
 Public structs: 4

use api_models::enums::PayoutConnectors;
use common_enums as storage_enums;
use common_utils::{
    id_type, payout_method_utils,
    types::{UnifiedCode, UnifiedMessage},
};
use serde::{Deserialize, Serialize};
use storage_enums::MerchantStorageScheme;
use time::PrimitiveDateTime;

use super::payouts::Payouts;

#[async_trait::async_trait]
pub trait PayoutAttemptInterface {
    type Error;
    async fn insert_payout_attempt(
        &self,
        _payout_attempt: PayoutAttemptNew,
        _payouts: &Payouts,
        _storage_scheme: MerchantStorageScheme,
    ) -> error_stack::Result<PayoutAttempt, Self::Error>;

    async fn update_payout_attempt(
        &self,
        _this: &PayoutAttempt,
        _payout_attempt_update: PayoutAttemptUpdate,
        _payouts: &Payouts,
        _storage_scheme: MerchantStorageScheme,
    ) -> error_stack::Result<PayoutAttempt, Self::Error>;

    async fn find_payout_attempt_by_merchant_id_payout_attempt_id(
        &self,
        _merchant_id: &id_type::MerchantId,
        _payout_attempt_id: &str,
        _storage_scheme: MerchantStorageScheme,
    ) -> error_stack::Result<PayoutAttempt, Self::Error>;

    async fn find_payout_attempt_by_merchant_id_connector_payout_id(
        &self,
        _merchant_id: &id_type::MerchantId,
        _connector_payout_id: &str,
        _storage_scheme: MerchantStorageScheme,
    ) -> error_stack::Result<PayoutAttempt, Self::Error>;

    async fn find_payout_attempt_by_merchant_id_merchant_order_reference_id(
        &self,
        _merchant_id: &id_type::MerchantId,
        _merchant_order_reference_id: &str,
        _storage_scheme: MerchantStorageScheme,
    ) -> error_stack::Result<PayoutAttempt, Self::Error>;

    async fn get_filters_for_payouts(
        &self,
        _payout: &[Payouts],
        _merchant_id: &id_type::MerchantId,
        _storage_scheme: MerchantStorageScheme,
    ) -> error_stack::Result<PayoutListFilters, Self::Error>;
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct PayoutListFilters {
    pub connector: Vec<PayoutConnectors>,
    pub currency: Vec<storage_enums::Currency>,
    pub status: Vec<storage_enums::PayoutStatus>,
    pub payout_method: Vec<storage_enums::PayoutType>,
}

#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct PayoutAttempt {
    pub payout_attempt_id: String,
    pub payout_id: id_type::PayoutId,
    pub customer_id: Option<id_type::CustomerId>,
    pub merchant_id: id_type::MerchantId,
    pub address_id: Option<String>,
    pub connector: Option<String>,
    pub connector_payout_id: Option<String>,
    pub payout_token: Option<String>,
    pub status: storage_enums::PayoutStatus,
    pub is_eligible: Option<bool>,
    pub error_message: Option<String>,
    pub error_code: Option<String>,
    pub business_country: Option<storage_enums::CountryAlpha2>,
    pub business_label: Option<String>,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub created_at: PrimitiveDateTime,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub last_modified_at: PrimitiveDateTime,
    pub profile_id: id_type::ProfileId,
    pub merchant_connector_id: Option<id_type::MerchantConnectorAccountId>,
    pub routing_info: Option<serde_json::Value>,
    pub unified_code: Option<UnifiedCode>,
    pub unified_message: Option<UnifiedMessage>,
    pub additional_payout_method_data: Option<payout_method_utils::AdditionalPayoutMethodData>,
    pub merchant_order_reference_id: Option<String>,
}

#[derive(Clone, Debug, PartialEq)]
pub struct PayoutAttemptNew {
    pub payout_attempt_id: String,
    pub payout_id: id_type::PayoutId,
    pub customer_id: Option<id_type::CustomerId>,
    pub merchant_id: id_type::MerchantId,
    pub address_id: Option<String>,
    pub connector: Option<String>,
    pub connector_payout_id: Option<String>,
    pub payout_token: Option<String>,
    pub status: storage_enums::PayoutStatus,
    pub is_eligible: Option<bool>,
    pub error_message: Option<String>,
    pub error_code: Option<String>,
    pub business_country: Option<storage_enums::CountryAlpha2>,
    pub business_label: Option<String>,
    pub created_at: PrimitiveDateTime,
    pub last_modified_at: PrimitiveDateTime,
    pub profile_id: id_type::ProfileId,
    pub merchant_connector_id: Option<id_type::MerchantConnectorAccountId>,
    pub routing_info: Option<serde_json::Value>,
    pub unified_code: Option<UnifiedCode>,
    pub unified_message: Option<UnifiedMessage>,
    pub additional_payout_method_data: Option<payout_method_utils::AdditionalPayoutMethodData>,
    pub merchant_order_reference_id: Option<String>,
}

#[derive(Debug, Clone)]
pub enum PayoutAttemptUpdate {
    StatusUpdate {
        connector_payout_id: Option<String>,
        status: storage_enums::PayoutStatus,
        error_message: Option<String>,
        error_code: Option<String>,
        is_eligible: Option<bool>,

        unified_code: Option<UnifiedCode>,
        unified_message: Option<UnifiedMessage>,
    },
    PayoutTokenUpdate {
        payout_token: String,
    },
    BusinessUpdate {
        business_country: Option<storage_enums::CountryAlpha2>,
        business_label: Option<String>,
        address_id: Option<String>,
        customer_id: Option<id_type::CustomerId>,
    },
    UpdateRouting {
        connector: String,
        routing_info: Option<serde_json::Value>,
        merchant_connector_id: Option<id_type::MerchantConnectorAccountId>,
    },
    AdditionalPayoutMethodDataUpdate {
        additional_payout_method_data: Option<payout_method_utils::AdditionalPayoutMethodData>,
    },
}

#[derive(Clone, Debug, Default)]
pub struct PayoutAttemptUpdateInternal {
    pub payout_token: Option<String>,
    pub connector_payout_id: Option<String>,
    pub status: Option<storage_enums::PayoutStatus>,
    pub error_message: Option<String>,
    pub error_code: Option<String>,
    pub is_eligible: Option<bool>,
    pub business_country: Option<storage_enums::CountryAlpha2>,
    pub business_label: Option<String>,
    pub connector: Option<String>,
    pub routing_info: Option<serde_json::Value>,
    pub address_id: Option<String>,
    pub customer_id: Option<id_type::CustomerId>,
    pub merchant_connector_id: Option<id_type::MerchantConnectorAccountId>,
    pub unified_code: Option<UnifiedCode>,
    pub unified_message: Option<UnifiedMessage>,
    pub additional_payout_method_data: Option<payout_method_utils::AdditionalPayoutMethodData>,
}

impl From<PayoutAttemptUpdate> for PayoutAttemptUpdateInternal {
    fn from(payout_update: PayoutAttemptUpdate) -> Self {
        match payout_update {
            PayoutAttemptUpdate::PayoutTokenUpdate { payout_token } => Self {
                payout_token: Some(payout_token),
                ..Default::default()
            },
            PayoutAttemptUpdate::StatusUpdate {
                connector_payout_id,
                status,
                error_message,
                error_code,
                is_eligible,
                unified_code,
                unified_message,
            } => Self {
                connector_payout_id,
                status: Some(status),
                error_message,
                error_code,
                is_eligible,
                unified_code,
                unified_message,
                ..Default::default()
            },
            PayoutAttemptUpdate::BusinessUpdate {
                business_country,
                business_label,
                address_id,
                customer_id,
            } => Self {
                business_country,
                business_label,
                address_id,
                customer_id,
                ..Default::default()
            },
            PayoutAttemptUpdate::UpdateRouting {
                connector,
                routing_info,
                merchant_connector_id,
            } => Self {
                connector: Some(connector),
                routing_info,
                merchant_connector_id,
                ..Default::default()
            },
            PayoutAttemptUpdate::AdditionalPayoutMethodDataUpdate {
                additional_payout_method_data,
            } => Self {
                additional_payout_method_data,
                ..Default::default()
            },
        }
    }
}


pub struct SquareRefundResponseDetails {
    status: RefundStatus,
    id: String,
}


pub fn create_identity_from_certificate_and_key(
    encoded_certificate: masking::Secret<String>,
    encoded_certificate_key: masking::Secret<String>,
) -> Result<reqwest::Identity, error_stack::Report<HttpClientError>> {
    let decoded_certificate = BASE64_ENGINE
        .decode(encoded_certificate.expose())
        .change_context(HttpClientError::CertificateDecodeFailed)?;

    let decoded_certificate_key = BASE64_ENGINE
        .decode(encoded_certificate_key.expose())
        .change_context(HttpClientError::CertificateDecodeFailed)?;

    let certificate = String::from_utf8(decoded_certificate)
        .change_context(HttpClientError::CertificateDecodeFailed)?;

    let certificate_key = String::from_utf8(decoded_certificate_key)
        .change_context(HttpClientError::CertificateDecodeFailed)?;

    let key_chain = format!("{certificate_key}{certificate}");
    reqwest::Identity::from_pem(key_chain.as_bytes())
        .change_context(HttpClientError::CertificateDecodeFailed)
}


pub async fn refunds_create(
    state: web::Data<AppState>,
    req: HttpRequest,
    json_payload: web::Json<refunds::RefundsCreateRequest>,
) -> HttpResponse {
    let flow = Flow::RefundsCreate;

    let global_refund_id =
        common_utils::id_type::GlobalRefundId::generate(&state.conf.cell_information.id);
    let payload = json_payload.into_inner();

    let internal_refund_create_payload =
        internal_payload_types::RefundsGenericRequestWithResourceId {
            global_refund_id: global_refund_id.clone(),
            payment_id: Some(payload.payment_id.clone()),
            payload,
        };

    let auth_type = if state.conf.merchant_id_auth.merchant_id_auth_enabled {
        &auth::MerchantIdAuth
    } else {
        auth::auth_type(
            &auth::V2ApiKeyAuth {
                is_connected_allowed: false,
                is_platform_allowed: false,
            },
            &auth::JWTAuth {
                permission: Permission::ProfileRefundWrite,
            },
            req.headers(),
        )
    };

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        internal_refund_create_payload,
        |state, auth: auth::AuthenticationData, req, _| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            refund_create_core(
                state,
                merchant_context,
                req.payload,
                global_refund_id.clone(),
            )
        },
        auth_type,
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


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

pub use diesel_models::authentication::{Authentication, AuthenticationNew, AuthenticationUpdate};


pub struct SquareWebhookBody {
    #[serde(rename = "type")]
    pub webhook_type: String,
    pub data: SquareWebhookData,
}


impl api::PaymentSession for Tokenio {}

impl api::PaymentSession for for Tokenio


File: crates/router/src/db/api_keys.rs

use error_stack::report;
use router_env::{instrument, tracing};
#[cfg(feature = "accounts_cache")]
use storage_impl::redis::cache::{self, CacheKind, ACCOUNTS_CACHE};

use super::{MockDb, Store};
use crate::{
    connection,
    core::errors::{self, CustomResult},
    types::storage,
};

#[async_trait::async_trait]
pub trait ApiKeyInterface {
    async fn insert_api_key(
        &self,
        api_key: storage::ApiKeyNew,
    ) -> CustomResult<storage::ApiKey, errors::StorageError>;

    async fn update_api_key(
        &self,
        merchant_id: common_utils::id_type::MerchantId,
        key_id: common_utils::id_type::ApiKeyId,
        api_key: storage::ApiKeyUpdate,
    ) -> CustomResult<storage::ApiKey, errors::StorageError>;

    async fn revoke_api_key(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        key_id: &common_utils::id_type::ApiKeyId,
    ) -> CustomResult<bool, errors::StorageError>;

    async fn find_api_key_by_merchant_id_key_id_optional(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        key_id: &common_utils::id_type::ApiKeyId,
    ) -> CustomResult<Option<storage::ApiKey>, errors::StorageError>;

    async fn find_api_key_by_hash_optional(
        &self,
        hashed_api_key: storage::HashedApiKey,
    ) -> CustomResult<Option<storage::ApiKey>, errors::StorageError>;

    async fn list_api_keys_by_merchant_id(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        limit: Option<i64>,
        offset: Option<i64>,
    ) -> CustomResult<Vec<storage::ApiKey>, errors::StorageError>;
}

#[async_trait::async_trait]
impl ApiKeyInterface for Store {
    #[instrument(skip_all)]
    async fn insert_api_key(
        &self,
        api_key: storage::ApiKeyNew,
    ) -> CustomResult<storage::ApiKey, errors::StorageError> {
        let conn = connection::pg_connection_write(self).await?;
        api_key
            .insert(&conn)
            .await
            .map_err(|error| report!(errors::StorageError::from(error)))
    }

    #[instrument(skip_all)]
    async fn update_api_key(
        &self,
        merchant_id: common_utils::id_type::MerchantId,
        key_id: common_utils::id_type::ApiKeyId,
        api_key: storage::ApiKeyUpdate,
    ) -> CustomResult<storage::ApiKey, errors::StorageError> {
        let conn = connection::pg_connection_write(self).await?;
        let _merchant_id = merchant_id.clone();
        let _key_id = key_id.clone();
        let update_call = || async {
            storage::ApiKey::update_by_merchant_id_key_id(&conn, merchant_id, key_id, api_key)
                .await
                .map_err(|error| report!(errors::StorageError::from(error)))
        };

        #[cfg(not(feature = "accounts_cache"))]
        {
            update_call().await
        }

        #[cfg(feature = "accounts_cache")]
        {
            use error_stack::report;

            // We need to fetch api_key here because the key that's saved in cache in HashedApiKey.
            // Used function from storage model to reuse the connection that made here instead of
            // creating new.
            let api_key = storage::ApiKey::find_optional_by_merchant_id_key_id(
                &conn,
                &_merchant_id,
                &_key_id,
            )
            .await
            .map_err(|error| report!(errors::StorageError::from(error)))?
            .ok_or(report!(errors::StorageError::ValueNotFound(format!(
                "ApiKey of {} not found",
                _key_id.get_string_repr()
            ))))?;

            cache::publish_and_redact(
                self,
                CacheKind::Accounts(api_key.hashed_api_key.into_inner().into()),
                update_call,
            )
            .await
        }
    }

    #[instrument(skip_all)]
    async fn revoke_api_key(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        key_id: &common_utils::id_type::ApiKeyId,
    ) -> CustomResult<bool, errors::StorageError> {
        let conn = connection::pg_connection_write(self).await?;
        let delete_call = || async {
            storage::ApiKey::revoke_by_merchant_id_key_id(&conn, merchant_id, key_id)
                .await
                .map_err(|error| report!(errors::StorageError::from(error)))
        };
        #[cfg(not(feature = "accounts_cache"))]
        {
            delete_call().await
        }

        #[cfg(feature = "accounts_cache")]
        {
            use error_stack::report;

            // We need to fetch api_key here because the key that's saved in cache in HashedApiKey.
            // Used function from storage model to reuse the connection that made here instead of
            // creating new.

            let api_key =
                storage::ApiKey::find_optional_by_merchant_id_key_id(&conn, merchant_id, key_id)
                    .await
                    .map_err(|error| report!(errors::StorageError::from(error)))?
                    .ok_or(report!(errors::StorageError::ValueNotFound(format!(
                        "ApiKey of {} not found",
                        key_id.get_string_repr()
                    ))))?;

            cache::publish_and_redact(
                self,
                CacheKind::Accounts(api_key.hashed_api_key.into_inner().into()),
                delete_call,
            )
            .await
        }
    }

    #[instrument(skip_all)]
    async fn find_api_key_by_merchant_id_key_id_optional(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        key_id: &common_utils::id_type::ApiKeyId,
    ) -> CustomResult<Option<storage::ApiKey>, errors::StorageError> {
        let conn = connection::pg_connection_read(self).await?;
        storage::ApiKey::find_optional_by_merchant_id_key_id(&conn, merchant_id, key_id)
            .await
            .map_err(|error| report!(errors::StorageError::from(error)))
    }

    #[instrument(skip_all)]
    async fn find_api_key_by_hash_optional(
        &self,
        hashed_api_key: storage::HashedApiKey,
    ) -> CustomResult<Option<storage::ApiKey>, errors::StorageError> {
        let _hashed_api_key = hashed_api_key.clone();
        let find_call = || async {
            let conn = connection::pg_connection_read(self).await?;
            storage::ApiKey::find_optional_by_hashed_api_key(&conn, hashed_api_key)
                .await
                .map_err(|error| report!(errors::StorageError::from(error)))
        };

        #[cfg(not(feature = "accounts_cache"))]
        {
            find_call().await
        }

        #[cfg(feature = "accounts_cache")]
        {
            cache::get_or_populate_in_memory(
                self,
                &_hashed_api_key.into_inner(),
                find_call,
                &ACCOUNTS_CACHE,
            )
            .await
        }
    }

    #[instrument(skip_all)]
    async fn list_api_keys_by_merchant_id(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        limit: Option<i64>,
        offset: Option<i64>,
    ) -> CustomResult<Vec<storage::ApiKey>, errors::StorageError> {
        let conn = connection::pg_connection_read(self).await?;
        storage::ApiKey::find_by_merchant_id(&conn, merchant_id, limit, offset)
            .await
            .map_err(|error| report!(errors::StorageError::from(error)))
    }
}

#[async_trait::async_trait]
impl ApiKeyInterface for MockDb {
    async fn insert_api_key(
        &self,
        api_key: storage::ApiKeyNew,
    ) -> CustomResult<storage::ApiKey, errors::StorageError> {
        let mut locked_api_keys = self.api_keys.lock().await;
        // don't allow duplicate key_ids, a those would be a unique constraint violation in the
        // real db as it is used as the primary key
        if locked_api_keys.iter().any(|k| k.key_id == api_key.key_id) {
            Err(errors::StorageError::MockDbError)?;
        }
        let stored_key = storage::ApiKey {
            key_id: api_key.key_id,
            merchant_id: api_key.merchant_id,
            name: api_key.name,
            description: api_key.description,
            hashed_api_key: api_key.hashed_api_key,
            prefix: api_key.prefix,
            created_at: api_key.created_at,
            expires_at: api_key.expires_at,
            last_used: api_key.last_used,
        };
        locked_api_keys.push(stored_key.clone());

        Ok(stored_key)
    }

    async fn update_api_key(
        &self,
        merchant_id: common_utils::id_type::MerchantId,
        key_id: common_utils::id_type::ApiKeyId,
        api_key: storage::ApiKeyUpdate,
    ) -> CustomResult<storage::ApiKey, errors::StorageError> {
        let mut locked_api_keys = self.api_keys.lock().await;
        // find a key with the given merchant_id and key_id and update, otherwise return an error
        let key_to_update = locked_api_keys
            .iter_mut()
            .find(|k| k.merchant_id == merchant_id && k.key_id == key_id)
            .ok_or(errors::StorageError::MockDbError)?;

        match api_key {
            storage::ApiKeyUpdate::Update {
                name,
                description,
                expires_at,
                last_used,
            } => {
                if let Some(name) = name {
                    key_to_update.name = name;
                }
                // only update these fields if the value was Some(_)
                if description.is_some() {
                    key_to_update.description = description;
                }
                if let Some(expires_at) = expires_at {
                    key_to_update.expires_at = expires_at;
                }
                if last_used.is_some() {
                    key_to_update.last_used = last_used
                }
            }
            storage::ApiKeyUpdate::LastUsedUpdate { last_used } => {
                key_to_update.last_used = Some(last_used);
            }
        }

        Ok(key_to_update.clone())
    }

    async fn revoke_api_key(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        key_id: &common_utils::id_type::ApiKeyId,
    ) -> CustomResult<bool, errors::StorageError> {
        let mut locked_api_keys = self.api_keys.lock().await;
        // find the key to remove, if it exists
        if let Some(pos) = locked_api_keys
            .iter()
            .position(|k| k.merchant_id == *merchant_id && k.key_id == *key_id)
        {
            // use `remove` instead of `swap_remove` so we have a consistent order, which might
            // matter to someone using limit/offset in `list_api_keys_by_merchant_id`
            locked_api_keys.remove(pos);
            Ok(true)
        } else {
            Ok(false)
        }
    }

    async fn find_api_key_by_merchant_id_key_id_optional(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        key_id: &common_utils::id_type::ApiKeyId,
    ) -> CustomResult<Option<storage::ApiKey>, errors::StorageError> {
        Ok(self
            .api_keys
            .lock()
            .await
            .iter()
            .find(|k| k.merchant_id == *merchant_id && k.key_id == *key_id)
            .cloned())
    }

    async fn find_api_key_by_hash_optional(
        &self,
        hashed_api_key: storage::HashedApiKey,
    ) -> CustomResult<Option<storage::ApiKey>, errors::StorageError> {
        Ok(self
            .api_keys
            .lock()
            .await
            .iter()
            .find(|k| k.hashed_api_key == hashed_api_key)
            .cloned())
    }

    async fn list_api_keys_by_merchant_id(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        limit: Option<i64>,
        offset: Option<i64>,
    ) -> CustomResult<Vec<storage::ApiKey>, errors::StorageError> {
        // mimic the SQL limit/offset behavior
        let offset: usize = if let Some(offset) = offset {
            if offset < 0 {
                Err(errors::StorageError::MockDbError)?;
            }
            offset
                .try_into()
                .map_err(|_| errors::StorageError::MockDbError)?
        } else {
            0
        };

        let limit: usize = if let Some(limit) = limit {
            if limit < 0 {
                Err(errors::StorageError::MockDbError)?;
            }
            limit
                .try_into()
                .map_err(|_| errors::StorageError::MockDbError)?
        } else {
            usize::MAX
        };

        let keys_for_merchant_id: Vec<storage::ApiKey> = self
            .api_keys
            .lock()
            .await
            .iter()
            .filter(|k| k.merchant_id == *merchant_id)
            .skip(offset)
            .take(limit)
            .cloned()
            .collect();

        Ok(keys_for_merchant_id)
    }
}

#[cfg(test)]
mod tests {
    use std::borrow::Cow;

    use storage_impl::redis::{
        cache::{self, CacheKey, CacheKind, ACCOUNTS_CACHE},
        kv_store::RedisConnInterface,
        pub_sub::PubSubInterface,
    };
    use time::macros::datetime;

    use crate::{
        db::{api_keys::ApiKeyInterface, MockDb},
        types::storage,
    };

    #[allow(clippy::unwrap_used)]
    #[tokio::test]
    async fn test_mockdb_api_key_interface() {
        #[allow(clippy::expect_used)]
        let mockdb = MockDb::new(&redis_interface::RedisSettings::default())
            .await
            .expect("Failed to create Mock store");

        let merchant_id =
            common_utils::id_type::MerchantId::try_from(Cow::from("merchant1")).unwrap();

        let key_id1 = common_utils::id_type::ApiKeyId::try_from(Cow::from("key_id1")).unwrap();

        let key_id2 = common_utils::id_type::ApiKeyId::try_from(Cow::from("key_id2")).unwrap();

        let non_existent_key_id =
            common_utils::id_type::ApiKeyId::try_from(Cow::from("does_not_exist")).unwrap();

        let key1 = mockdb
            .insert_api_key(storage::ApiKeyNew {
                key_id: key_id1.clone(),
                merchant_id: merchant_id.clone(),
                name: "Key 1".into(),
                description: None,
                hashed_api_key: "hashed_key1".to_string().into(),
                prefix: "abc".into(),
                created_at: datetime!(2023-02-01 0:00),
                expires_at: Some(datetime!(2023-03-01 0:00)),
                last_used: None,
            })
            .await
            .unwrap();

        mockdb
            .insert_api_key(storage::ApiKeyNew {
                key_id: key_id2.clone(),
                merchant_id: merchant_id.clone(),
                name: "Key 2".into(),
                description: None,
                hashed_api_key: "hashed_key2".to_string().into(),
                prefix: "abc".into(),
                created_at: datetime!(2023-03-01 0:00),
                expires_at: None,
                last_used: None,
            })
            .await
            .unwrap();

        let found_key1 = mockdb
            .find_api_key_by_merchant_id_key_id_optional(&merchant_id, &key_id1)
            .await
            .unwrap()
            .unwrap();
        assert_eq!(found_key1.key_id, key1.key_id);
        assert!(mockdb
            .find_api_key_by_merchant_id_key_id_optional(&merchant_id, &non_existent_key_id)
            .await
            .unwrap()
            .is_none());

        mockdb
            .update_api_key(
                merchant_id.clone(),
                key_id1.clone(),
                storage::ApiKeyUpdate::LastUsedUpdate {
                    last_used: datetime!(2023-02-04 1:11),
                },
            )
            .await
            .unwrap();
        let updated_key1 = mockdb
            .find_api_key_by_merchant_id_key_id_optional(&merchant_id, &key_id1)
            .await
            .unwrap()
            .unwrap();
        assert_eq!(updated_key1.last_used, Some(datetime!(2023-02-04 1:11)));

        assert_eq!(
            mockdb
                .list_api_keys_by_merchant_id(&merchant_id, None, None)
                .await
                .unwrap()
                .len(),
            2
        );
        mockdb.revoke_api_key(&merchant_id, &key_id1).await.unwrap();
        assert_eq!(
            mockdb
                .list_api_keys_by_merchant_id(&merchant_id, None, None)
                .await
                .unwrap()
                .len(),
            1
        );
    }

    #[allow(clippy::unwrap_used)]
    #[tokio::test]
    async fn test_api_keys_cache() {
        let merchant_id =
            common_utils::id_type::MerchantId::try_from(Cow::from("test_merchant")).unwrap();

        #[allow(clippy::expect_used)]
        let db = MockDb::new(&redis_interface::RedisSettings::default())
            .await
            .expect("Failed to create Mock store");

        let redis_conn = db.get_redis_conn().unwrap();
        redis_conn
            .subscribe("hyperswitch_invalidate")
            .await
            .unwrap();

        let test_key = common_utils::id_type::ApiKeyId::try_from(Cow::from("test_ey")).unwrap();

        let api = storage::ApiKeyNew {
            key_id: test_key.clone(),
            merchant_id: merchant_id.clone(),
            name: "My test key".into(),
            description: None,
            hashed_api_key: "a_hashed_key".to_string().into(),
            prefix: "pre".into(),
            created_at: datetime!(2023-06-01 0:00),
            expires_at: None,
            last_used: None,
        };

        let api = db.insert_api_key(api).await.unwrap();

        let hashed_api_key = api.hashed_api_key.clone();
        let find_call = || async {
            db.find_api_key_by_hash_optional(hashed_api_key.clone())
                .await
        };
        let _: Option<storage::ApiKey> = cache::get_or_populate_in_memory(
            &db,
            &format!(
                "{}_{}",
                merchant_id.get_string_repr(),
                hashed_api_key.clone().into_inner()
            ),
            find_call,
            &ACCOUNTS_CACHE,
        )
        .await
        .unwrap();

        let delete_call = || async { db.revoke_api_key(&merchant_id, &api.key_id).await };

        cache::publish_and_redact(
            &db,
            CacheKind::Accounts(
                format!(
                    "{}_{}",
                    merchant_id.get_string_repr(),
                    hashed_api_key.clone().into_inner()
                )
                .into(),
            ),
            delete_call,
        )
        .await
        .unwrap();

        assert!(ACCOUNTS_CACHE
            .get_val::<storage::ApiKey>(CacheKey {
                key: format!(
                    "{}_{}",
                    merchant_id.get_string_repr(),
                    hashed_api_key.into_inner()
                ),
                prefix: String::default(),
            },)
            .await
            .is_none())
    }
}


pub struct TrustpayAuthUpdateRequest {
    pub grant_type: String,
}


pub async fn insert_user_in_blacklist(state: &SessionState, user_id: &str) -> UserResult<()> {
    let user_blacklist_key = format!("{USER_BLACKLIST_PREFIX}{user_id}");
    let expiry =
        expiry_to_i64(JWT_TOKEN_TIME_IN_SECS).change_context(UserErrors::InternalServerError)?;
    let redis_conn = get_redis_connection_for_global_tenant(state)
        .change_context(UserErrors::InternalServerError)?;
    redis_conn
        .set_key_with_expiry(
            &user_blacklist_key.as_str().into(),
            date_time::now_unix_timestamp(),
            expiry,
        )
        .await
        .change_context(UserErrors::InternalServerError)
}


pub async fn update(
        self,
        conn: &PgPooledConn,
        process: ProcessTrackerUpdate,
    ) -> StorageResult<Self> {
        match generics::generic_update_by_id::<<Self as HasTable>::Table, _, _, _>(
            conn,
            self.id.clone(),
            ProcessTrackerUpdateInternal::from(process),
        )
        .await
        {
            Err(error) => match error.current_context() {
                errors::DatabaseError::NoFieldsToUpdate => Ok(self),
                _ => Err(error),
            },
            result => result,
        }
    }


pub fn get_metadata_as_secret(
        &self,
    ) -> CustomResult<Option<pii::SecretSerdeValue>, errors::ParsingError> {
        self.metadata
            .as_ref()
            .map(|metadata| metadata.encode_to_value().map(Secret::new))
            .transpose()
    }


impl std::fmt::Debug for RequestContent {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str(match self {
            Self::Json(_) => "JsonRequestBody",
            Self::FormUrlEncoded(_) => "FormUrlEncodedRequestBody",
            Self::FormData(_) => "FormDataRequestBody",
            Self::Xml(_) => "XmlRequestBody",
            Self::RawBytes(_) => "RawBytesRequestBody",
        })
    }
}

impl std::fmt::Debug for for RequestContent


pub fn construct_router_data<F: Clone, Req, Res>(
    state: &SessionState,
    authentication_connector_name: String,
    payment_method: PaymentMethod,
    merchant_id: common_utils::id_type::MerchantId,
    address: types::PaymentAddress,
    request_data: Req,
    merchant_connector_account: &payments_helpers::MerchantConnectorAccountType,
    psd2_sca_exemption_type: Option<common_enums::ScaExemptionType>,
    payment_id: common_utils::id_type::PaymentId,
) -> RouterResult<types::RouterData<F, Req, Res>> {
    let test_mode: Option<bool> = merchant_connector_account.is_test_mode_on();
    let auth_type: types::ConnectorAuthType = merchant_connector_account
        .get_connector_account_details()
        .parse_value("ConnectorAuthType")
        .change_context(errors::ApiErrorResponse::InternalServerError)?;
    Ok(types::RouterData {
        flow: PhantomData,
        merchant_id,
        customer_id: None,
        tenant_id: state.tenant.tenant_id.clone(),
        connector_customer: None,
        connector: authentication_connector_name,
        payment_id: payment_id.get_string_repr().to_owned(),
        attempt_id: IRRELEVANT_ATTEMPT_ID_IN_AUTHENTICATION_FLOW.to_owned(),
        status: common_enums::AttemptStatus::default(),
        payment_method,
        connector_auth_type: auth_type,
        description: None,
        address,
        auth_type: common_enums::AuthenticationType::NoThreeDs,
        connector_meta_data: merchant_connector_account.get_metadata(),
        connector_wallets_details: merchant_connector_account.get_connector_wallets_details(),
        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,
        request: request_data,
        response: Err(types::ErrorResponse::default()),
        connector_request_reference_id:
            IRRELEVANT_CONNECTOR_REQUEST_REFERENCE_ID_IN_AUTHENTICATION_FLOW.to_owned(),
        #[cfg(feature = "payouts")]
        payout_method_data: None,
        #[cfg(feature = "payouts")]
        quote_id: None,
        test_mode,
        connector_http_status_code: None,
        external_latency: None,
        apple_pay_flow: None,
        frm_metadata: None,
        dispute_id: None,
        refund_id: None,
        payment_method_status: 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,
        raw_connector_response: None,
        is_payment_id_from_merchant: None,
        l2_l3_data: None,
        minor_amount_capturable: None,
    })
}


pub struct BlackhawknetworkAuthType {
    pub(super) client_id: Secret<String>,
    pub(super) client_secret: Secret<String>,
    pub(super) product_line_id: Secret<String>,
}


/// Verifies that the client configuration is usable
    pub fn validate(&self) -> Result<(), &'static str> {
        match self {
            #[cfg(feature = "aws_kms")]
            Self::AwsKms { aws_kms } => aws_kms.validate(),

            Self::NoEncryption => Ok(()),
        }
    }


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


File: crates/hyperswitch_domain_models/src/router_flow_types/refunds.rs
 Public structs: 2

#[derive(Debug, Clone)]
pub struct Execute;
#[derive(Debug, Clone)]
pub struct RSync;


pub fn new(
        tenant_id: id_type::TenantId,
        request_id: Option<RequestId>,
        payment_id: String,
        profile_id: id_type::ProfileId,
        merchant_id: id_type::MerchantId,
        flow: String,
        request: Option<Req>,
        parse_response: bool,
        log_event: bool,
    ) -> Self {
        Self {
            tenant_id,
            request_id,
            payment_id,
            profile_id,
            merchant_id,
            flow,
            request,
            parse_response,
            log_event,
            routing_event: None,
        }
    }


pub async fn migrate_payment_method(
    _state: &state::PaymentMethodsState,
    _req: pm_api::PaymentMethodMigrate,
    _merchant_id: &id_type::MerchantId,
    _merchant_context: &merchant_context::MerchantContext,
    _controller: &dyn PaymentMethodsController,
) -> CustomResult<ApplicationResponse<pm_api::PaymentMethodMigrateResponse>, errors::ApiErrorResponse>
{
    todo!()
}


pub struct MerchantAccountSetter {
    pub merchant_id: common_utils::id_type::MerchantId,
    pub return_url: Option<String>,
    pub enable_payment_response_hash: bool,
    pub payment_response_hash_key: Option<String>,
    pub redirect_to_merchant_with_http_post: bool,
    pub merchant_name: Option<Encryption>,
    pub merchant_details: Option<Encryption>,
    pub webhook_details: Option<crate::business_profile::WebhookDetails>,
    pub sub_merchants_enabled: Option<bool>,
    pub parent_merchant_id: Option<common_utils::id_type::MerchantId>,
    pub publishable_key: Option<String>,
    pub storage_scheme: storage_enums::MerchantStorageScheme,
    pub locker_id: Option<String>,
    pub metadata: Option<pii::SecretSerdeValue>,
    pub routing_algorithm: Option<serde_json::Value>,
    pub primary_business_details: serde_json::Value,
    pub intent_fulfillment_time: Option<i64>,
    pub created_at: time::PrimitiveDateTime,
    pub modified_at: time::PrimitiveDateTime,
    pub frm_routing_algorithm: Option<serde_json::Value>,
    pub payout_routing_algorithm: Option<serde_json::Value>,
    pub organization_id: common_utils::id_type::OrganizationId,
    pub is_recon_enabled: bool,
    pub default_profile: Option<common_utils::id_type::ProfileId>,
    pub recon_status: storage_enums::ReconStatus,
    pub payment_link_config: Option<serde_json::Value>,
    pub pm_collect_link_config: Option<serde_json::Value>,
    pub version: common_enums::ApiVersion,
    pub is_platform_account: bool,
    pub product_type: Option<common_enums::MerchantProductType>,
    pub merchant_account_type: common_enums::MerchantAccountType,
}


pub struct ChargebackUpdateRequest {
    #[serde(rename = "@xmlns")]
    xmlns: String,
    activity_type: ActivityType,
}


pub async fn can_user_access_theme(
    user: &UserFromToken,
    user_entity_type: &EntityType,
    theme: &Theme,
) -> UserResult<()> {
    if user_entity_type < &theme.entity_type {
        return Err(UserErrors::ThemeNotFound.into());
    }

    match theme.entity_type {
        EntityType::Tenant => {
            if user.tenant_id.as_ref() == Some(&theme.tenant_id)
                && theme.org_id.is_none()
                && theme.merchant_id.is_none()
                && theme.profile_id.is_none()
            {
                Ok(())
            } else {
                Err(UserErrors::ThemeNotFound.into())
            }
        }
        EntityType::Organization => {
            if user.tenant_id.as_ref() == Some(&theme.tenant_id)
                && theme.org_id.as_ref() == Some(&user.org_id)
                && theme.merchant_id.is_none()
                && theme.profile_id.is_none()
            {
                Ok(())
            } else {
                Err(UserErrors::ThemeNotFound.into())
            }
        }
        EntityType::Merchant => {
            if user.tenant_id.as_ref() == Some(&theme.tenant_id)
                && theme.org_id.as_ref() == Some(&user.org_id)
                && theme.merchant_id.as_ref() == Some(&user.merchant_id)
                && theme.profile_id.is_none()
            {
                Ok(())
            } else {
                Err(UserErrors::ThemeNotFound.into())
            }
        }
        EntityType::Profile => {
            if user.tenant_id.as_ref() == Some(&theme.tenant_id)
                && theme.org_id.as_ref() == Some(&user.org_id)
                && theme.merchant_id.as_ref() == Some(&user.merchant_id)
                && theme.profile_id.as_ref() == Some(&user.profile_id)
            {
                Ok(())
            } else {
                Err(UserErrors::ThemeNotFound.into())
            }
        }
    }
}


/// If the country code is invalid, it returns a `ValidationError` with the appropriate error message.
    pub fn validate_and_get_country_from_merchant_country_code(
        &self,
    ) -> errors::CustomResult<common_enums::Country, errors::ValidationError> {
        let country_code = self.get_country_code();
        let code = country_code
            .parse::<u32>()
            .map_err(Report::from)
            .change_context(errors::ValidationError::IncorrectValueProvided {
                field_name: "merchant_country_code",
            })
            .attach_printable_lazy(|| {
                format!("Country code {country_code} is negative or too large")
            })?;

        common_enums::Country::from_numeric(code)
            .map_err(|_| errors::ValidationError::IncorrectValueProvided {
                field_name: "merchant_country_code",
            })
            .attach_printable_lazy(|| format!("Invalid country code {code}"))
    }


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


pub struct NetworkTokenizationBuilder<'a, S: State> {
    /// Current state
    state: std::marker::PhantomData<S>,

    /// Customer details
    pub customer: Option<&'a api::CustomerDetails>,

    /// Card details
    pub card: Option<domain::CardDetail>,

    /// CVC
    pub card_cvc: Option<Secret<String>>,

    /// Network token details
    pub network_token: Option<&'a pm_types::CardNetworkTokenResponsePayload>,

    /// Stored card details
    pub stored_card: Option<&'a pm_transformers::StoreCardRespPayload>,

    /// Stored token details
    pub stored_token: Option<&'a pm_transformers::StoreCardRespPayload>,

    /// Payment method response
    pub payment_method_response: Option<api::PaymentMethodResponse>,

    /// Card network tokenization status
    pub card_tokenized: bool,

    /// Error code
    pub error_code: Option<&'a String>,

    /// Error message
    pub error_message: Option<&'a String>,
}


impl std::ops::DerefMut for PaymentsTokenReference {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}

impl std::ops::DerefMut for for PaymentsTokenReference


        key_manager_state: &KeyManagerState,
        merchant_key_store: &domain::MerchantKeyStore,
        algorithm_id: id_type::RoutingId,
        transaction_type: &storage::enums::TransactionType,
    ) -> RouterResult<()> {
        let routing_cache_key = self.clone().get_routing_config_cache_key();

        let (routing_algorithm_id, payout_routing_algorithm_id) = match transaction_type {
            storage::enums::TransactionType::Payment => (Some(algorithm_id), None),
            #[cfg(feature = "payouts")]
            storage::enums::TransactionType::Payout => (None, Some(algorithm_id)),
            //TODO: Handle ThreeDsAuthentication Transaction Type for Three DS Decision Rule Algorithm configuration
            storage::enums::TransactionType::ThreeDsAuthentication => todo!(),
        };

        let profile_update = domain::ProfileUpdate::RoutingAlgorithmUpdate {
            routing_algorithm_id,
            payout_routing_algorithm_id,
        };

        let profile = self.profile;

        db.update_profile_by_profile_id(
            key_manager_state,
            merchant_key_store,
            profile,
            profile_update,
        )
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to update routing algorithm ref in business profile")?;

        storage_impl::redis::cache::redact_from_redis_and_publish(
            db.get_cache_store().as_ref(),
            [routing_cache_key],
        )
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to invalidate routing cache")?;
        Ok(())
    }

    pub fn get_routing_algorithm_id<'a>(
        &'a self,
        transaction_data: &'a routing::TransactionData<'_>,
    ) -> Option<id_type::RoutingId> {
        match transaction_data {
            routing::TransactionData::Payment(_) => self.profile.routing_algorithm_id.clone(),
            #[cfg(feature = "payouts")]
            routing::TransactionData::Payout(_) => self.profile.payout_routing_algorithm_id.clone(),
        }
    }
    pub fn get_default_fallback_list_of_connector_under_profile(
        &self,
    ) -> RouterResult<Vec<routing_types::RoutableConnectorChoice>> {
        let fallback_connectors =
            if let Some(default_fallback_routing) = self.profile.default_fallback_routing.clone() {
                default_fallback_routing
                    .expose()
                    .parse_value::<Vec<routing_types::RoutableConnectorChoice>>(
                        "Vec<RoutableConnectorChoice>",
                    )
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("Business Profile default config has invalid structure")?
            } else {
                Vec::new()
            };
        Ok(fallback_connectors)
    }
    pub fn get_default_routing_configs_from_profile(
        &self,
    ) -> RouterResult<routing_types::ProfileDefaultRoutingConfig> {
        let profile_id = self.profile.get_id().to_owned();
        let connectors = self.get_default_fallback_list_of_connector_under_profile()?;

        Ok(routing_types::ProfileDefaultRoutingConfig {
            profile_id,
            connectors,
        })
    }

    pub async fn update_default_fallback_routing_of_connectors_under_profile(
        self,
        db: &dyn StorageInterface,
        updated_config: &Vec<routing_types::RoutableConnectorChoice>,
        key_manager_state: &KeyManagerState,
        merchant_key_store: &domain::MerchantKeyStore,
    ) -> RouterResult<()> {
        let default_fallback_routing = Secret::from(
            updated_config
                .encode_to_value()
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Failed to convert routing ref to value")?,
        );
        let profile_update = domain::ProfileUpdate::DefaultRoutingFallbackUpdate {
            default_fallback_routing: Some(default_fallback_routing),
        };

        db.update_profile_by_profile_id(
            key_manager_state,
            merchant_key_store,
            self.profile,
            profile_update,
        )
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to update routing algorithm ref in business profile")?;
        Ok(())
    }
    pub async fn update_revenue_recovery_algorithm_under_profile(
        self,
        db: &dyn StorageInterface,
        key_manager_state: &KeyManagerState,
        merchant_key_store: &domain::MerchantKeyStore,
        revenue_recovery_retry_algorithm_type: common_enums::RevenueRecoveryAlgorithmType,
    ) -> RouterResult<()> {
        let recovery_algorithm_data =
            diesel_models::business_profile::RevenueRecoveryAlgorithmData {
                monitoring_configured_timestamp: date_time::now(),
            };
        let profile_update = domain::ProfileUpdate::RevenueRecoveryAlgorithmUpdate {
            revenue_recovery_retry_algorithm_type,
            revenue_recovery_retry_algorithm_data: Some(recovery_algorithm_data),
        };

        db.update_profile_by_profile_id(
            key_manager_state,
            merchant_key_store,
            self.profile,
            profile_update,
        )
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable(
            "Failed to update revenue recovery retry algorithm in business profile",
        )?;
        Ok(())
    }
}

pub async fn extended_card_info_toggle(
    state: SessionState,
    merchant_id: &id_type::MerchantId,
    profile_id: &id_type::ProfileId,
    ext_card_info_choice: admin_types::ExtendedCardInfoChoice,
) -> RouterResponse<admin_types::ExtendedCardInfoChoice> {
    let db = state.store.as_ref();
    let key_manager_state = &(&state).into();

    let key_store = db
        .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::MerchantAccountNotFound)
        .attach_printable("Error while fetching the key store by merchant_id")?;

    let business_profile = db
        .find_business_profile_by_profile_id(key_manager_state, &key_store, profile_id)
        .await
        .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound {
            id: profile_id.get_string_repr().to_owned(),
        })?;

    if business_profile.is_extended_card_info_enabled.is_none()
        || business_profile
            .is_extended_card_info_enabled
            .is_some_and(|existing_config| existing_config != ext_card_info_choice.enabled)
    {
        let profile_update = domain::ProfileUpdate::ExtendedCardInfoUpdate {
            is_extended_card_info_enabled: ext_card_info_choice.enabled,
        };

        db.update_profile_by_profile_id(
            key_manager_state,
            &key_store,
            business_profile,
            profile_update,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound {
            id: profile_id.get_string_repr().to_owned(),
        })?;
    }

    Ok(service_api::ApplicationResponse::Json(ext_card_info_choice))
}

pub async fn connector_agnostic_mit_toggle(
    state: SessionState,
    merchant_id: &id_type::MerchantId,
    profile_id: &id_type::ProfileId,
    connector_agnostic_mit_choice: admin_types::ConnectorAgnosticMitChoice,
) -> RouterResponse<admin_types::ConnectorAgnosticMitChoice> {
    let db = state.store.as_ref();
    let key_manager_state = &(&state).into();

    let key_store = db
        .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::MerchantAccountNotFound)
        .attach_printable("Error while fetching the key store by merchant_id")?;

    let business_profile = db
        .find_business_profile_by_profile_id(key_manager_state, &key_store, profile_id)
        .await
        .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound {
            id: profile_id.get_string_repr().to_owned(),
        })?;

    if business_profile.merchant_id != *merchant_id {
        Err(errors::ApiErrorResponse::AccessForbidden {
            resource: profile_id.get_string_repr().to_owned(),
        })?
    }

    if business_profile.is_connector_agnostic_mit_enabled
        != Some(connector_agnostic_mit_choice.enabled)
    {
        let profile_update = domain::ProfileUpdate::ConnectorAgnosticMitUpdate {
            is_connector_agnostic_mit_enabled: connector_agnostic_mit_choice.enabled,
        };

        db.update_profile_by_profile_id(
            key_manager_state,
            &key_store,
            business_profile,
            profile_update,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound {
            id: profile_id.get_string_repr().to_owned(),
        })?;
    }

    Ok(service_api::ApplicationResponse::Json(
        connector_agnostic_mit_choice,
    ))
}

pub async fn transfer_key_store_to_key_manager(
    state: SessionState,
    req: admin_types::MerchantKeyTransferRequest,
) -> RouterResponse<admin_types::TransferKeyResponse> {
    let resp = transfer_encryption_key(&state, req).await?;

    Ok(service_api::ApplicationResponse::Json(
        admin_types::TransferKeyResponse {
            total_transferred: resp,
        },
    ))
}

async fn process_open_banking_connectors(
    state: &SessionState,
    merchant_id: &id_type::MerchantId,
    auth: &types::ConnectorAuthType,
    connector_type: &api_enums::ConnectorType,
    connector: &api_enums::Connector,
    additional_merchant_data: types::AdditionalMerchantData,
    key_store: &domain::MerchantKeyStore,
) -> RouterResult<types::MerchantRecipientData> {
    let new_merchant_data = match additional_merchant_data {
        types::AdditionalMerchantData::OpenBankingRecipientData(merchant_data) => {
            if connector_type != &api_enums::ConnectorType::PaymentProcessor {
                return Err(errors::ApiErrorResponse::InvalidConnectorConfiguration {
                    config:
                        "OpenBanking connector for Payment Initiation should be a payment processor"
                            .to_string(),
                }
                .into());
            }
            match &merchant_data {
                types::MerchantRecipientData::AccountData(acc_data) => {
                    core_utils::validate_bank_account_data(acc_data)?;

                    let connector_name = api_enums::Connector::to_string(connector);

                    let recipient_creation_not_supported = state
                        .conf
                        .locker_based_open_banking_connectors
                        .connector_list
                        .contains(connector_name.as_str());
                    let recipient_id = if recipient_creation_not_supported {
                        locker_recipient_create_call(state, merchant_id, acc_data, key_store).await
                    } else {
                        connector_recipient_create_call(
                            state,
                            merchant_id,
                            connector_name,
                            auth,
                            acc_data,
                        )
                        .await
                    }
                    .attach_printable("failed to get recipient_id")?;

                    let conn_recipient_id = if recipient_creation_not_supported {
                        Some(types::RecipientIdType::LockerId(Secret::new(recipient_id)))
                    } else {
                        Some(types::RecipientIdType::ConnectorId(Secret::new(
                            recipient_id,
                        )))
                    };

                    let account_data = match &acc_data {
                        types::MerchantAccountData::Iban { iban, name, .. } => {
                            types::MerchantAccountData::Iban {
                                iban: iban.clone(),
                                name: name.clone(),
                                connector_recipient_id: conn_recipient_id.clone(),
                            }
                        }
                        types::MerchantAccountData::Bacs {
                            account_number,
                            sort_code,
                            name,
                            ..
                        } => types::MerchantAccountData::Bacs {
                            account_number: account_number.clone(),
                            sort_code: sort_code.clone(),
                            name: name.clone(),
                            connector_recipient_id: conn_recipient_id.clone(),
                        },
                        types::MerchantAccountData::FasterPayments {
                            account_number,
                            sort_code,
                            name,
                            ..
                        } => types::MerchantAccountData::FasterPayments {
                            account_number: account_number.clone(),
                            sort_code: sort_code.clone(),
                            name: name.clone(),
                            connector_recipient_id: conn_recipient_id.clone(),
                        },
                        types::MerchantAccountData::Sepa { iban, name, .. } => {
                            types::MerchantAccountData::Sepa {
                                iban: iban.clone(),
                                name: name.clone(),
                                connector_recipient_id: conn_recipient_id.clone(),
                            }
                        }
                        types::MerchantAccountData::SepaInstant { iban, name, .. } => {
                            types::MerchantAccountData::SepaInstant {
                                iban: iban.clone(),
                                name: name.clone(),
                                connector_recipient_id: conn_recipient_id.clone(),
                            }
                        }
                        types::MerchantAccountData::Elixir {
                            account_number,
                            iban,
                            name,
                            ..
                        } => types::MerchantAccountData::Elixir {
                            account_number: account_number.clone(),
                            iban: iban.clone(),
                            name: name.clone(),
                            connector_recipient_id: conn_recipient_id.clone(),
                        },
                        types::MerchantAccountData::Bankgiro { number, name, .. } => {
                            types::MerchantAccountData::Bankgiro {
                                number: number.clone(),
                                name: name.clone(),
                                connector_recipient_id: conn_recipient_id.clone(),
                            }
                        }
                        types::MerchantAccountData::Plusgiro { number, name, .. } => {
                            types::MerchantAccountData::Plusgiro {
                                number: number.clone(),
                                name: name.clone(),
                                connector_recipient_id: conn_recipient_id.clone(),
                            }
                        }
                    };

                    types::MerchantRecipientData::AccountData(account_data)
                }
                _ => merchant_data.clone(),
            }
        }
    };

    Ok(new_merchant_data)
}

async fn connector_recipient_create_call(
    state: &SessionState,
    merchant_id: &id_type::MerchantId,
    connector_name: String,
    auth: &types::ConnectorAuthType,
    data: &types::MerchantAccountData,
) -> RouterResult<String> {
    let connector = pm_auth_types::api::PaymentAuthConnectorData::get_connector_by_name(
        connector_name.as_str(),
    )?;

    let auth = pm_auth_types::ConnectorAuthType::foreign_try_from(auth.clone())
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed while converting ConnectorAuthType")?;

    let connector_integration: pm_auth_types::api::BoxedConnectorIntegration<
        '_,
        pm_auth_types::api::auth_service::RecipientCreate,
        pm_auth_types::RecipientCreateRequest,
        pm_auth_types::RecipientCreateResponse,
    > = connector.connector.get_connector_integration();

    let req = pm_auth_types::RecipientCreateRequest::from(data);
    let router_data = pm_auth_types::RecipientCreateRouterData {
        flow: std::marker::PhantomData,
        merchant_id: Some(merchant_id.to_owned()),
        connector: Some(connector_name),
        request: req,
        response: Err(pm_auth_types::ErrorResponse {
            status_code: http::StatusCode::INTERNAL_SERVER_ERROR.as_u16(),
            code: consts::NO_ERROR_CODE.to_string(),
            message: consts::UNSUPPORTED_ERROR_MESSAGE.to_string(),
            reason: None,
        }),
        connector_http_status_code: None,
        connector_auth_type: auth,
    };

    let resp = payment_initiation_service::execute_connector_processing_step(
        state,
        connector_integration,
        &router_data,
        &connector.connector_name,
    )
    .await
    .change_context(errors::ApiErrorResponse::InternalServerError)
    .attach_printable("Failed while calling recipient create connector api")?;

    let recipient_create_resp =
        resp.response
            .map_err(|err| errors::ApiErrorResponse::ExternalConnectorError {
                code: err.code,
                message: err.message,
                connector: connector.connector_name.to_string(),
                status_code: err.status_code,
                reason: err.reason,
            })?;

    let recipient_id = recipient_create_resp.recipient_id;

    Ok(recipient_id)
}
async fn locker_recipient_create_call(
    state: &SessionState,
    merchant_id: &id_type::MerchantId,
    data: &types::MerchantAccountData,
    key_store: &domain::MerchantKeyStore,
) -> RouterResult<String> {
    let key_manager_state = &state.into();
    let key = key_store.key.get_inner().peek();
    let identifier = km_types::Identifier::Merchant(key_store.merchant_id.clone());

    let data_json = serde_json::to_string(data)
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to serialize MerchantAccountData to JSON")?;

    let encrypted_data = domain_types::crypto_operation(
        key_manager_state,
        type_name!(payment_method::PaymentMethod),
        domain_types::CryptoOperation::Encrypt(Secret::<String, masking::WithType>::new(data_json)),
        identifier,
        key,
    )
    .await
    .and_then(|val| val.try_into_operation())
    .change_context(errors::ApiErrorResponse::InternalServerError)
    .attach_printable("Failed to encrypt merchant account data")?;

    let enc_data = hex::encode(encrypted_data.into_encrypted().expose());
    let merchant_id_string = merchant_id.get_string_repr().to_owned();

    let cust_id = id_type::CustomerId::try_from(std::borrow::Cow::from(merchant_id_string))
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to convert to CustomerId")?;

    let payload = transformers::StoreLockerReq::LockerGeneric(transformers::StoreGenericReq {
        merchant_id: merchant_id.to_owned(),
        merchant_customer_id: cust_id.clone(),
        enc_data,
        ttl: state.conf.locker.ttl_for_storage_in_secs,
    });

    let store_resp = cards::add_card_to_hs_locker(
        state,
        &payload,
        &cust_id,
        api_enums::LockerChoice::HyperswitchCardVault,
    )
    .await
    .change_context(errors::ApiErrorResponse::InternalServerError)
    .attach_printable("Failed to encrypt merchant bank account data")?;

    Ok(store_resp.card_reference)
}

pub async fn enable_platform_account(
    state: SessionState,
    merchant_id: id_type::MerchantId,
) -> RouterResponse<()> {
    let db = state.store.as_ref();
    let key_manager_state = &(&state).into();
    let key_store = db
        .get_merchant_key_store_by_merchant_id(
            key_manager_state,
            &merchant_id,
            &db.get_master_key().to_vec().into(),
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?;

    let merchant_account = db
        .find_merchant_account_by_merchant_id(key_manager_state, &merchant_id, &key_store)
        .await
        .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?;

    db.update_merchant(
        key_manager_state,
        merchant_account,
        storage::MerchantAccountUpdate::ToPlatformAccount,
        &key_store,
    )
    .await
    .change_context(errors::ApiErrorResponse::InternalServerError)
    .attach_printable("Error while enabling platform merchant account")
    .map(|_| services::ApplicationResponse::StatusOk)
}

impl From<&types::MerchantAccountData> for pm_auth_types::RecipientCreateRequest {
    fn from(data: &types::MerchantAccountData) -> Self {
        let (name, account_data) = match data {
            types::MerchantAccountData::Iban { iban, name, .. } => (
                name.clone(),
                pm_auth_types::RecipientAccountData::Iban(iban.clone()),
            ),
            types::MerchantAccountData::Bacs {
                account_number,
                sort_code,
                name,
                ..
            } => (
                name.clone(),
                pm_auth_types::RecipientAccountData::Bacs {
                    sort_code: sort_code.clone(),
                    account_number: account_number.clone(),
                },
            ),
            types::MerchantAccountData::FasterPayments {
                account_number,
                sort_code,
                name,
                ..
            } => (
                name.clone(),
                pm_auth_types::RecipientAccountData::FasterPayments {
                    sort_code: sort_code.clone(),
                    account_number: account_number.clone(),
                },
            ),
            types::MerchantAccountData::Sepa { iban, name, .. } => (
                name.clone(),
                pm_auth_types::RecipientAccountData::Sepa(iban.clone()),
            ),
            types::MerchantAccountData::SepaInstant { iban, name, .. } => (
                name.clone(),
                pm_auth_types::RecipientAccountData::SepaInstant(iban.clone()),
            ),
            types::MerchantAccountData::Elixir {
                account_number,
                iban,
                name,
                ..
            } => (
                name.clone(),
                pm_auth_types::RecipientAccountData::Elixir {
                    account_number: account_number.clone(),
                    iban: iban.clone(),
                },
            ),
            types::MerchantAccountData::Bankgiro { number, name, .. } => (
                name.clone(),
                pm_auth_types::RecipientAccountData::Bankgiro(number.clone()),
            ),
            types::MerchantAccountData::Plusgiro { number, name, .. } => (
                name.clone(),
                pm_auth_types::RecipientAccountData::Plusgiro(number.clone()),
            ),
        };

        Self {
            name,
            account_data,
            address: None,
        }
    }
}


pub struct RefundResponse {
    refund: PayuRefundResponseData,
}


pub fn get_webhook_url_from_profile(&self) -> CustomResult<String, ValidationError> {
        self.webhook_details
            .clone()
            .and_then(|details| details.webhook_url)
            .get_required_value("webhook_details.webhook_url")
            .map(ExposeInterface::expose)
    }


pub struct OrganizationUpdateInternal {
    org_name: Option<String>,
    organization_name: Option<String>,
    organization_details: Option<pii::SecretSerdeValue>,
    metadata: Option<pii::SecretSerdeValue>,
    modified_at: time::PrimitiveDateTime,
    platform_merchant_id: Option<id_type::MerchantId>,
}


pub fn update(&mut self, new: Self) {
        if let Some(new_cons) = new.constants {
            self.constants = Some(new_cons)
        }
        if let Some(new_time_scale) = new.time_scale {
            self.time_scale = Some(new_time_scale)
        }
    }


File: crates/euclid_wasm/src/utils.rs

use wasm_bindgen::prelude::*;

pub trait JsResultExt<T> {
    fn err_to_js(self) -> Result<T, JsValue>;
}

impl<T, E> JsResultExt<T> for Result<T, E>
where
    E: serde::Serialize,
{
    fn err_to_js(self) -> Result<T, JsValue> {
        match self {
            Ok(t) => Ok(t),
            Err(e) => Err(serde_wasm_bindgen::to_value(&e)?),
        }
    }
}


pub struct XenditCaptureResponse {
    pub id: String,
    pub status: PaymentStatus,
    pub actions: Option<Vec<Action>>,
    pub payment_method: PaymentMethodInfo,
    pub failure_code: Option<String>,
    pub reference_id: Secret<String>,
}


pub async fn are_all_tokens_hard_declined(
        state: &SessionState,
        connector_customer_id: &str,
    ) -> CustomResult<bool, errors::StorageError> {
        let tokens_map =
            Self::get_connector_customer_payment_processor_tokens(state, connector_customer_id)
                .await?;
        let all_hard_declined = tokens_map
            .values()
            .all(|token| token.is_hard_decline.unwrap_or(false));

        tracing::debug!(
            connector_customer_id = connector_customer_id,
            all_hard_declined,
            "Checked if all tokens are hard declined or no token found for the customer",
        );

        Ok(all_hard_declined)
    }


Web Documentation: Setup MCP Server | Hyperswitch
# Type: Web Doc

This MCP server is only meant for product exploration while using sandbox environment.
DO NOT USE Hyperswitch API Key from the PRODUCTION ENVIRONMENT.
Prerequisites
: Ensure you have an MCP client installed (Cursor, Claude Desktop, Cline, etc.)
Step 1: Install the MCP Server
Open terminal and run the command given below
npx
mint-mcp
add
api-reference.hyperswitch.io
Step 2: Configure your MCP Server
Enter your Hyperswitch API key (You can generate one from the developer section in
Hyperswitch Control Center
)
Select your preferred MCP client from the prompt.
Step 3: Run the Server
On completing step 2, you will get an output similar to
You can then just copy the command and run it:
node <path to your local folder>/.mcp/api-reference.hyperswitch.io/src/index.js
Once done, you will see this as output:
Step 4: Enable in your MCP Client
Go to integrations (or MCP tools) section in your MCP client
Look for the locally available MCP tool:
api.reference.hyperswitch.io
Eg. in Claude Desktop go to Search and Tools button in the chat box, or in Cursor go to the settings and then MCP tools section.
Step 5: Make your First Payment
Paste this prompt in your MCP Client:
Make a payment of 100 USD via Hyperswitch.
Output:
You can head on to payment section in
Hyperswitch Control Center
and verify the payment with the time stamp and status as
REQUIRES_PAYMENT_METHOD
.
Responses from AI tools can be incorrect, use with caution.
and reach out to us on
slack
for any questions or further support.
4 months ago
Was this helpful?

Setup MCP Server | Hyperswitch


/// Get the unique identifier of MerchantAccount
    pub fn get_id(&self) -> &common_utils::id_type::MerchantId {
        &self.merchant_id
    }


/// This function transforms the decision_engine response in a way that's usable for further flows:
/// It places evaluated_output connectors first, followed by remaining output connectors (no duplicates).
pub fn transform_de_output_for_router(
    de_output: Vec<ConnectorInfo>,
    de_evaluated_output: Vec<RoutableConnectorChoice>,
) -> RoutingResult<Vec<RoutableConnectorChoice>> {
    let mut seen = HashSet::new();

    // evaluated connectors on top, to ensure the fallback is based on other connectors.
    let mut ordered = Vec::with_capacity(de_output.len() + de_evaluated_output.len());
    for eval_conn in de_evaluated_output {
        if seen.insert(eval_conn.connector) {
            ordered.push(eval_conn);
        }
    }

    // Add remaining connectors from de_output (only if not already seen), for fallback
    for conn in de_output {
        let key = RoutableConnectors::from_str(&conn.gateway_name).map_err(|_| {
            errors::RoutingError::GenericConversionError {
                from: "String".to_string(),
                to: "RoutableConnectors".to_string(),
            }
        })?;
        if seen.insert(key) {
            let de_choice = DeRoutableConnectorChoice::try_from(conn)?;
            ordered.push(RoutableConnectorChoice::from(de_choice));
        }
    }
    Ok(ordered)
}