pub struct EncryptedDataGroup(pub FxHashMap<String, EncryptedData>);


pub struct AUBankAccount {
    country_code: CountryAlpha2,
    account_number: Secret<String>,
    branch_code: Secret<String>,
    account_holder_name: Secret<String>,
}


Web Documentation: Exploration Guide | Hyperswitch
# Type: Web Doc

Hyperswitch gives you full control over your payments infrastructure without the complexity of building from scratch. Whether you're validating a prototype or scaling globally, you can get started in minutes.
This guide helps you:
Quickly install Hyperswitch with minimal setup
Try your first hello world by making a payment
Learn about Hyperswitch’s modular architecture
Improve and streamline your checkout experience
See how Hyperswitch embeds security, reliability, and scalability into its core architecture
Try Hyperswitch Quickly
Get a Sandbox Ready in under 10 Minutes
Spin up a working integration with minimal effort. Choose the path that fits your needs:
Cloud Sandbox
Try Hyperswitch →
Launch a ready-to-use Control Center test environment. No setup required, just log in and run your first transaction.
Local Deployment (Docker)
Set up Docker Locally →
Perfect for developers who want local control and flexibility. You'll launch a ready-to-use Control Center environment.
API-first Integration
API Reference →
Use Postman Collection →
Build a custom backend integration from scratch.
⚠ To authenticate API requests, you’ll need a username and password from either:
• the
Cloud Sandbox
• or your
Docker setup
Launch a Scalable Hyperswitch Instance on AWS
AWS Deployment →
Launch Hyperswitch on your own AWS infrastructure with our step-by-step deployment guide. Ideal for teams looking for flexibility, control, and production-ready scalability.
Scalable, Self-Hosted Deployment | Helm Charts for GCP & Azure
Deploy on GCP or Azure →
Install Hyperswitch on your cloud infrastructure using Helm charts for Kubernetes. This method gives you full control over your environment and is ideal for teams deploying on GCP, Azure, or any Kubernetes-compatible platform.
Your first payment
See Hyperswitch in action by sending your first test transaction via the cloud sandbox, local Docker setup or directly via API.
Test a payment | Your First Hello World with Hyperswitch
Control Center
| Accessible through cloud sandbox or Docker
API
| Accessible through the Postman Collection
Learn how the SDK, control center and backend work together
Hyperswitch Capabilities Overview
Power only what you need with Hyperswitch’s modular architecture. You can pick and integrate the components that solve your specific payment challenges without unnecessary overhead.
Power Only What You Need with Hyperswitch’s Modular Architecture
Intelligent Routing →
Dynamically route transactions based on geography, cost, or success rate to reduce failures and fees.
Revenue Recovery →
Recover failed payments using machine learning–based retry logic that adapts to card network behavior.
Vault (Tokenization) →
Securely store and reuse payment credentials across providers — ideal for subscriptions and saved cards.
Cost Observability →
Gain real-time visibility into your processing costs and optimize spend across processors.
Reconciliation →
Automatically match transaction data across banks, PSPs, and internal systems to reduce manual effort.
3DS Decision Manager →
Apply 3DS only when necessary, minimizing friction while keeping fraud under control.
Payment Orchestration →
Automate disbursements to vendors or sellers with rule-based routing logic.
Explore Core Payment Flows and Capabilities
Power only what you need with Hyperswitch’s modular architecture. Integrate just the components that solve your payment challenges, without the extra overhead.
Build Smarter Payment Flows
Payment Orchestration →
Automate and optimize how payments are routed, authorized, and split across providers with Hyperswitch’s flexible payment orchestration engine.
Tokenization and Card Vault →
Securely store and reuse customer payment credentials across processors to reduce friction and improve retention.
Get Started with Hyperswitch's Vault
.
Routing →
Control how transactions flow across payment providers with configurable routing logic and fallback options
Intelligent Routing →
Automatically route transactions based on geography, success rate, or cost to maximize authorization rates.
Smart Retries →
Recover failed payments using ML-driven retry strategies optimized for timing, issuer behavior, and card type.
Payouts →
Easily manage and automate disbursements to sellers, vendors, or partners with flexible payout logic.
Subscriptions →
Handle recurring payments seamlessly with built-in support for subscription billing and invoicing.
Split Payments →
Divide transactions between multiple parties or accounts with precision and control.
Improve Your Checkout and Payment Experience
Deliver seamless, flexible, and localized payment flows that drive higher conversion and customer trust.
Create Seamless Checkout Experiences That Convert
Customizable Checkout SDK (Web) →
Embed a native, responsive checkout experience into your website with full control over styling and flow.
Click to Pay →
Enable frictionless, one-click payments for returning users using wallets and saved cards.
Payment Methods Management →
Dynamically configure and prioritize payment methods based on geography, currency, and user preference.
Alternate Payment Methods (APMs) →
Offer support for UPI, wallets, and local payment options to meet your customers where they are.
Integration Guide Overview →
Explore the full set of tools and options to deliver a branded and consistent payment experience across platforms.
Manage and Monitor Your Payment Operations
Operate at scale with tools to manage accounts, monitor transactions, handle disputes, and apply business rules.
Operate and Monitor Your Payment Stack
Manage Accounts and Profiles →
Create, manage, and operate across multiple merchant accounts and profiles with full multi-tenancy support.
Analytics and Operations →
Gain real-time visibility into transaction performance, routing behavior, and operational metrics.
Disputes and Chargebacks →
Monitor, respond to, and manage disputes or chargebacks from a centralized operations interface.
Surcharge Management →
Apply dynamic surcharges or convenience fees based on card type, geography, or business logic.
Full Operations Overview →
Explore the complete set of tools available for scaling your payment operations with confidence.
Scalability, Relability, and Security
Take your Hyperswitch integration to production with confidence. Set up environments, secure credentials, monitor performance, and scale seamlessly as your business grows.
Explore Security, Reliability, and Scalability
Build with confidence on an architecture designed for compliance, low-latency scaling, and enterprise-grade uptime.
Security and Compliance →
Protect sensitive data and meet global compliance standards like PCI DSS with secure-by-default components.
Latency →
Scale effortlessly with a stateless architecture designed to handle high-throughput payment workloads with near-zero overhead.
Here's how
Hyperswitch handles horizontal scaling under high throughput
Reliability →
Achieve consistent uptime and resiliency through modular design and built-in fault tolerance.
Here's how
Hyperswitch handles idempotency and message ordering
.
Go Live with Hyperswitch
Take Hyperswitch into production
How to Go Live with Hyperswitch →
Follow our go-live checklist to launch with confidence — covering setup, credentials, security, and monitoring.
Need Help?
Join our Slack Community →
Contact Us →
Prefer direct support? We’re happy to help.
Developer Resources
API Reference →
SDK Documentation →
Postman Collection →
FAQs →
GitHub Repository →
2 months ago
Was this helpful?

Exploration Guide | Hyperswitch


impl super::KafkaMessage for KafkaAuthentication<'_> {
    fn key(&self) -> String {
        format!(
            "{}_{}",
            self.merchant_id.get_string_repr(),
            self.authentication_id.get_string_repr()
        )
    }

    fn event_type(&self) -> crate::events::EventType {
        crate::events::EventType::Authentication
    }
}

impl super::KafkaMessage for for KafkaAuthentication


pub struct RefundSyncResponse {
    refunds: Vec<PayuRefundResponseData>,
}


impl api::PaymentSession for Bluesnap {}

impl api::PaymentSession for for Bluesnap


pub struct IncrementalAuthorizationDetails {
    pub additional_amount: MinorUnit,
    pub total_amount: MinorUnit,
    pub reason: Option<String>,
    pub authorization_id: Option<String>,
}


pub fn get_application_builder(
    request_body_limit: usize,
    cors: settings::CorsSettings,
) -> actix_web::App<
    impl ServiceFactory<
        ServiceRequest,
        Config = (),
        Response = actix_web::dev::ServiceResponse<impl MessageBody>,
        Error = actix_web::Error,
        InitError = (),
    >,
> {
    let json_cfg = actix_web::web::JsonConfig::default()
        .limit(request_body_limit)
        .content_type_required(true)
        .error_handler(utils::error_parser::custom_json_error_handler);

    actix_web::App::new()
        .app_data(json_cfg)
        .wrap(ErrorHandlers::new().handler(
            StatusCode::NOT_FOUND,
            errors::error_handlers::custom_error_handlers,
        ))
        .wrap(ErrorHandlers::new().handler(
            StatusCode::METHOD_NOT_ALLOWED,
            errors::error_handlers::custom_error_handlers,
        ))
        .wrap(middleware::default_response_headers())
        .wrap(middleware::RequestId)
        .wrap(cors::cors(cors))
        // this middleware works only for Http1.1 requests
        .wrap(middleware::Http400RequestDetailsLogger)
        .wrap(middleware::AddAcceptLanguageHeader)
        .wrap(middleware::RequestResponseMetrics)
        .wrap(middleware::LogSpanInitializer)
        .wrap(router_env::tracing_actix_web::TracingLogger::default())
}


pub struct Unpacker;


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

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

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


pub struct DisputeId {
    pub dispute_id: String,
}


pub struct NetworkToken {
    #[serde(rename = "type")]
    pub auth_type: CustomerAuthType,
    pub authentication_value: Secret<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub eci: Option<String>,
}


File: crates/pm_auth/src/lib.rs

pub mod connector;
pub mod consts;
pub mod core;
pub mod types;


Documentation: api-reference/v2/payment-methods/payment-method--confirm-intent.mdx
# Type: Doc File

---
openapi: post /v2/payment-methods/{id}/confirm-intent
---


pub struct UserAuthenticationMethodResponse {
    pub id: String,
    pub auth_id: String,
    pub auth_method: AuthMethodDetails,
    pub allow_signup: bool,
}


pub struct NmiWebhookBody {
    pub event_body: NmiWebhookObject,
}


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


pub fn get_card_data(&self) -> Option<&Card> {
        if let Self::Card(card) = self {
            Some(card)
        } else {
            None
        }
    }


pub async fn delete_payment_token(
    state: &routes::SessionState,
    key_for_token: &str,
    intent_status: enums::IntentStatus,
) -> RouterResult<()> {
    if ![
        enums::IntentStatus::RequiresCustomerAction,
        enums::IntentStatus::RequiresMerchantAction,
    ]
    .contains(&intent_status)
    {
        utils::delete_payment_token_data(state, key_for_token)
            .await
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Unable to delete payment_token")?;
    }
    Ok(())
}


/// Extract webhook content from UCS response for further processing
/// This provides a helper function to extract specific data from UCS responses
pub fn extract_webhook_content_from_ucs_response(
    transform_data: &WebhookTransformData,
) -> Option<&unified_connector_service_client::payments::WebhookResponseContent> {
    transform_data.webhook_content.as_ref()
}


impl ConnectorValidation for Flexiti {
    fn validate_mandate_payment(
        &self,
        _pm_type: Option<enums::PaymentMethodType>,
        pm_data: PaymentMethodData,
    ) -> CustomResult<(), errors::ConnectorError> {
        match pm_data {
            PaymentMethodData::Card(_) => Err(errors::ConnectorError::NotImplemented(
                "validate_mandate_payment does not support cards".to_string(),
            )
            .into()),
            _ => Ok(()),
        }
    }

    fn validate_connector_against_payment_request(
        &self,
        capture_method: Option<common_enums::CaptureMethod>,
        _payment_method: common_enums::PaymentMethod,
        pmt: Option<common_enums::PaymentMethodType>,
    ) -> CustomResult<(), errors::ConnectorError> {
        let capture_method = capture_method.unwrap_or_default();

        match capture_method {
            enums::CaptureMethod::Manual => Ok(()),
            enums::CaptureMethod::Automatic
            | enums::CaptureMethod::SequentialAutomatic
            | enums::CaptureMethod::ManualMultiple
            | enums::CaptureMethod::Scheduled => {
                let connector = self.id();
                match pmt {
                    Some(payment_method_type) => {
                        capture_method_not_supported!(
                            connector,
                            capture_method,
                            payment_method_type
                        )
                    }
                    None => capture_method_not_supported!(connector, capture_method),
                }
            }
        }
    }

    fn validate_psync_reference_id(
        &self,
        _data: &PaymentsSyncData,
        _is_three_ds: bool,
        _status: enums::AttemptStatus,
        _connector_meta_data: Option<common_utils::pii::SecretSerdeValue>,
    ) -> CustomResult<(), errors::ConnectorError> {
        Ok(())
    }
}

impl ConnectorValidation for for Flexiti


pub trait RoutingEq<T> {
    fn is_equal(a: &T, b: &T) -> bool;
}


pub fn new(three_d_s_info: RedsysThreeDsInfo) -> Self {
        Self {
            three_d_s_info,
            protocol_version: None,
            browser_accept_header: None,
            browser_user_agent: None,
            browser_java_enabled: None,
            browser_javascript_enabled: None,
            browser_language: None,
            browser_color_depth: None,
            browser_screen_height: None,
            browser_screen_width: None,
            browser_t_z: None,
            browser_i_p: None,
            three_d_s_server_trans_i_d: None,
            notification_u_r_l: None,
            three_d_s_comp_ind: None,
            cres: None,
            billing_data: None,
            shipping_data: None,
        }
    }


pub struct CashtocodePaymentsSyncResponse {
    pub transaction_id: String,
    pub amount: FloatMajorUnit,
}


pub struct RSyncNodeData {
    id: String,
    status: BraintreeRefundStatus,
}


pub struct PaymentFilters {
    #[serde(default)]
    pub currency: Vec<Currency>,
    #[serde(default)]
    pub status: Vec<AttemptStatus>,
    #[serde(default)]
    pub connector: Vec<Connector>,
    #[serde(default)]
    pub auth_type: Vec<AuthenticationType>,
    #[serde(default)]
    pub payment_method: Vec<PaymentMethod>,
    #[serde(default)]
    pub payment_method_type: Vec<PaymentMethodType>,
    #[serde(default)]
    pub client_source: Vec<String>,
    #[serde(default)]
    pub client_version: Vec<String>,
    #[serde(default)]
    pub card_network: Vec<CardNetwork>,
    #[serde(default)]
    pub profile_id: Vec<id_type::ProfileId>,
    #[serde(default)]
    pub merchant_id: Vec<id_type::MerchantId>,
    #[serde(default)]
    pub card_last_4: Vec<String>,
    #[serde(default)]
    pub card_issuer: Vec<String>,
    #[serde(default)]
    pub error_reason: Vec<String>,
    #[serde(default)]
    pub first_attempt: Vec<bool>,
    #[serde(default)]
    pub routing_approach: Vec<RoutingApproach>,
    #[serde(default)]
    pub signature_network: Vec<String>,
    #[serde(default)]
    pub is_issuer_regulated: Vec<bool>,
    #[serde(default)]
    pub is_debit_routed: Vec<bool>,
}


OpenAPI Block Path: components.schemas.DecisionEngineSRSubLevelInputConfig
{
  "type": "object",
  "properties": {
    "paymentMethodType": {
      "type": "string",
      "nullable": true
    },
    "paymentMethod": {
      "type": "string",
      "nullable": true
    },
    "latencyThreshold": {
      "type": "number",
      "format": "double",
      "nullable": true
    },
    "bucketSize": {
      "type": "integer",
      "format": "int32",
      "nullable": true
    },
    "hedgingPercent": {
      "type": "number",
      "format": "double",
      "nullable": true
    },
    "lowerResetFactor": {
      "type": "number",
      "format": "double",
      "nullable": true
    },
    "upperResetFactor": {
      "type": "number",
      "format": "double",
      "nullable": true
    },
    "gatewayExtraScore": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/DecisionEngineGatewayWiseExtraScore"
      },
      "nullable": true
    }
  }
}


pub struct ToggleKVRequest {
    #[serde(skip_deserializing)]
    #[schema(value_type = String)]
    pub merchant_id: id_type::MerchantId,
    /// Status of KV for the specific merchant
    #[schema(example = true)]
    pub kv_enabled: bool,
}


impl api::PaymentCapture for Nuvei {}


impl<'a> KafkaAuthenticationEvent<'a> {
    pub fn from_storage(authentication: &'a Authentication) -> Self {
        Self {
            created_at: authentication.created_at.assume_utc(),
            modified_at: authentication.modified_at.assume_utc(),
            authentication_id: &authentication.authentication_id,
            merchant_id: &authentication.merchant_id,
            authentication_status: authentication.authentication_status,
            authentication_connector: authentication.authentication_connector.as_ref(),
            connector_authentication_id: authentication.connector_authentication_id.as_ref(),
            authentication_data: authentication.authentication_data.clone(),
            payment_method_id: &authentication.payment_method_id,
            authentication_type: authentication.authentication_type,
            authentication_lifecycle_status: authentication.authentication_lifecycle_status,
            error_code: authentication.error_code.as_ref(),
            error_message: authentication.error_message.as_ref(),
            connector_metadata: authentication.connector_metadata.clone(),
            maximum_supported_version: authentication.maximum_supported_version.clone(),
            threeds_server_transaction_id: authentication.threeds_server_transaction_id.as_ref(),
            cavv: authentication.cavv.as_ref(),
            authentication_flow_type: authentication.authentication_flow_type.as_ref(),
            message_version: authentication.message_version.clone(),
            eci: authentication.eci.as_ref(),
            trans_status: authentication.trans_status.clone(),
            acquirer_bin: authentication.acquirer_bin.as_ref(),
            acquirer_merchant_id: authentication.acquirer_merchant_id.as_ref(),
            three_ds_method_data: authentication.three_ds_method_data.as_ref(),
            three_ds_method_url: authentication.three_ds_method_url.as_ref(),
            acs_url: authentication.acs_url.as_ref(),
            challenge_request: authentication.challenge_request.as_ref(),
            acs_reference_number: authentication.acs_reference_number.as_ref(),
            acs_trans_id: authentication.acs_trans_id.as_ref(),
            acs_signed_content: authentication.acs_signed_content.as_ref(),
            profile_id: &authentication.profile_id,
            payment_id: authentication.payment_id.as_ref(),
            merchant_connector_id: authentication.merchant_connector_id.as_ref(),
            ds_trans_id: authentication.ds_trans_id.as_ref(),
            directory_server_id: authentication.directory_server_id.as_ref(),
            acquirer_country_code: authentication.acquirer_country_code.as_ref(),
            organization_id: &authentication.organization_id,
        }
    }
}


pub struct CybersourceStandardErrorResponse {
    pub error_information: Option<ErrorInformation>,
    pub status: Option<String>,
    pub message: Option<String>,
    pub reason: Option<String>,
    pub details: Option<Vec<Details>>,
}


impl Default for PayoutsUpdateInternal {
    fn default() -> Self {
        Self {
            amount: None,
            destination_currency: None,
            source_currency: None,
            description: None,
            recurring: None,
            auto_fulfill: None,
            return_url: None,
            entity_type: None,
            metadata: None,
            payout_method_id: None,
            profile_id: None,
            status: None,
            last_modified_at: common_utils::date_time::now(),
            attempt_count: None,
            confirm: None,
            payout_type: None,
            address_id: None,
            customer_id: None,
        }
    }
}

impl Default for for PayoutsUpdateInternal


File: crates/drainer/src/handler.rs
 Public functions: 5
 Public structs: 2

use std::{
    collections::HashMap,
    sync::{atomic, Arc},
};

use common_utils::id_type;
use router_env::tracing::Instrument;
use tokio::{
    sync::{mpsc, oneshot},
    time::{self, Duration},
};

use crate::{
    errors, instrument, logger, metrics, query::ExecuteQuery, tracing, utils, DrainerSettings,
    Store, StreamData,
};

/// Handler handles the spawning and closing of drainer
/// Arc is used to enable creating a listener for graceful shutdown
#[derive(Clone)]
pub struct Handler {
    inner: Arc<HandlerInner>,
}

impl std::ops::Deref for Handler {
    type Target = HandlerInner;

    fn deref(&self) -> &Self::Target {
        &self.inner
    }
}

pub struct HandlerInner {
    shutdown_interval: Duration,
    loop_interval: Duration,
    active_tasks: Arc<atomic::AtomicU64>,
    conf: DrainerSettings,
    stores: HashMap<id_type::TenantId, Arc<Store>>,
    running: Arc<atomic::AtomicBool>,
}

impl Handler {
    pub fn from_conf(
        conf: DrainerSettings,
        stores: HashMap<id_type::TenantId, Arc<Store>>,
    ) -> Self {
        let shutdown_interval = Duration::from_millis(conf.shutdown_interval.into());
        let loop_interval = Duration::from_millis(conf.loop_interval.into());

        let active_tasks = Arc::new(atomic::AtomicU64::new(0));

        let running = Arc::new(atomic::AtomicBool::new(true));

        let handler = HandlerInner {
            shutdown_interval,
            loop_interval,
            active_tasks,
            conf,
            stores,
            running,
        };

        Self {
            inner: Arc::new(handler),
        }
    }

    pub fn close(&self) {
        self.running.store(false, atomic::Ordering::SeqCst);
    }

    pub async fn spawn(&self) -> errors::DrainerResult<()> {
        let mut stream_index: u8 = 0;
        let jobs_picked = Arc::new(atomic::AtomicU8::new(0));

        while self.running.load(atomic::Ordering::SeqCst) {
            metrics::DRAINER_HEALTH.add(1, &[]);
            for store in self.stores.values() {
                if store.is_stream_available(stream_index).await {
                    let _task_handle = tokio::spawn(
                        drainer_handler(
                            store.clone(),
                            stream_index,
                            self.conf.max_read_count,
                            self.active_tasks.clone(),
                            jobs_picked.clone(),
                        )
                        .in_current_span(),
                    );
                }
            }
            stream_index = utils::increment_stream_index(
                (stream_index, jobs_picked.clone()),
                self.conf.num_partitions,
            )
            .await;
            time::sleep(self.loop_interval).await;
        }

        Ok(())
    }

    pub(crate) async fn shutdown_listener(&self, mut rx: mpsc::Receiver<()>) {
        while let Some(_c) = rx.recv().await {
            logger::info!("Awaiting shutdown!");
            metrics::SHUTDOWN_SIGNAL_RECEIVED.add(1, &[]);
            let shutdown_started = time::Instant::now();
            rx.close();

            //Check until the active tasks are zero. This does not include the tasks in the stream.
            while self.active_tasks.load(atomic::Ordering::SeqCst) != 0 {
                time::sleep(self.shutdown_interval).await;
            }
            logger::info!("Terminating drainer");
            metrics::SUCCESSFUL_SHUTDOWN.add(1, &[]);
            let shutdown_ended = shutdown_started.elapsed().as_secs_f64() * 1000f64;
            metrics::CLEANUP_TIME.record(shutdown_ended, &[]);
            self.close();
        }
        logger::info!(
            tasks_remaining = self.active_tasks.load(atomic::Ordering::SeqCst),
            "Drainer shutdown successfully"
        )
    }

    pub fn spawn_error_handlers(&self, tx: mpsc::Sender<()>) -> errors::DrainerResult<()> {
        let (redis_error_tx, redis_error_rx) = oneshot::channel();
        let redis_conn_clone = self
            .stores
            .values()
            .next()
            .map(|store| store.redis_conn.clone());
        match redis_conn_clone {
            None => {
                logger::error!("No redis connection found");
                Err(
                    errors::DrainerError::UnexpectedError("No redis connection found".to_string())
                        .into(),
                )
            }
            Some(redis_conn_clone) => {
                // Spawn a task to monitor if redis is down or not
                let _task_handle = tokio::spawn(
                    async move { redis_conn_clone.on_error(redis_error_tx).await }
                        .in_current_span(),
                );

                //Spawns a task to send shutdown signal if redis goes down
                let _task_handle =
                    tokio::spawn(redis_error_receiver(redis_error_rx, tx).in_current_span());

                Ok(())
            }
        }
    }
}

pub async fn redis_error_receiver(rx: oneshot::Receiver<()>, shutdown_channel: mpsc::Sender<()>) {
    match rx.await {
        Ok(_) => {
            logger::error!("The redis server failed");
            let _ = shutdown_channel.send(()).await.map_err(|err| {
                logger::error!("Failed to send signal to the shutdown channel {err}")
            });
        }
        Err(err) => {
            logger::error!("Channel receiver error {err}");
        }
    }
}

#[router_env::instrument(skip_all)]
async fn drainer_handler(
    store: Arc<Store>,
    stream_index: u8,
    max_read_count: u64,
    active_tasks: Arc<atomic::AtomicU64>,
    jobs_picked: Arc<atomic::AtomicU8>,
) -> errors::DrainerResult<()> {
    active_tasks.fetch_add(1, atomic::Ordering::Release);

    let stream_name = store.get_drainer_stream_name(stream_index);

    let drainer_result = Box::pin(drainer(
        store.clone(),
        max_read_count,
        stream_name.as_str(),
        jobs_picked,
    ))
    .await;

    if let Err(error) = drainer_result {
        logger::error!(?error)
    }

    let flag_stream_name = store.get_stream_key_flag(stream_index);

    let output = store.make_stream_available(flag_stream_name.as_str()).await;
    active_tasks.fetch_sub(1, atomic::Ordering::Release);
    output.inspect_err(|err| logger::error!(operation = "unlock_stream", err=?err))
}

#[instrument(skip_all, fields(global_id, request_id, session_id))]
async fn drainer(
    store: Arc<Store>,
    max_read_count: u64,
    stream_name: &str,
    jobs_picked: Arc<atomic::AtomicU8>,
) -> errors::DrainerResult<()> {
    let stream_read = match store.read_from_stream(stream_name, max_read_count).await {
        Ok(result) => {
            jobs_picked.fetch_add(1, atomic::Ordering::SeqCst);
            result
        }
        Err(error) => {
            if let errors::DrainerError::RedisError(redis_err) = error.current_context() {
                if let redis_interface::errors::RedisError::StreamEmptyOrNotAvailable =
                    redis_err.current_context()
                {
                    metrics::STREAM_EMPTY.add(1, &[]);
                    return Ok(());
                } else {
                    return Err(error);
                }
            } else {
                return Err(error);
            }
        }
    };

    // parse_stream_entries returns error if no entries is found, handle it
    let entries = utils::parse_stream_entries(
        &stream_read,
        store.redis_conn.add_prefix(stream_name).as_str(),
    )?;
    let read_count = entries.len();

    metrics::JOBS_PICKED_PER_STREAM.add(
        u64::try_from(read_count).unwrap_or(u64::MIN),
        router_env::metric_attributes!(("stream", stream_name.to_owned())),
    );

    let session_id = common_utils::generate_id_with_default_len("drainer_session");

    let mut last_processed_id = String::new();

    for (entry_id, entry) in entries.clone() {
        let data = match StreamData::from_hashmap(entry) {
            Ok(data) => data,
            Err(err) => {
                logger::error!(operation = "deserialization", err=?err);
                metrics::STREAM_PARSE_FAIL.add(
                    1,
                    router_env::metric_attributes!(("operation", "deserialization")),
                );

                // break from the loop in case of a deser error
                break;
            }
        };

        tracing::Span::current().record("request_id", data.request_id);
        tracing::Span::current().record("global_id", data.global_id);
        tracing::Span::current().record("session_id", &session_id);

        match data.typed_sql.execute_query(&store, data.pushed_at).await {
            Ok(_) => {
                last_processed_id = entry_id;
            }
            Err(err) => match err.current_context() {
                // In case of Uniqueviolation we can't really do anything to fix it so just clear
                // it from the stream
                diesel_models::errors::DatabaseError::UniqueViolation => {
                    last_processed_id = entry_id;
                }
                // break from the loop in case of an error in query
                _ => break,
            },
        }

        if store.use_legacy_version() {
            store
                .delete_from_stream(stream_name, &last_processed_id)
                .await?;
        }
    }

    if !(last_processed_id.is_empty() || store.use_legacy_version()) {
        let entries_trimmed = store
            .trim_from_stream(stream_name, &last_processed_id)
            .await?;
        if read_count != entries_trimmed {
            logger::error!(
                read_entries = %read_count,
                trimmed_entries = %entries_trimmed,
                ?entries,
                "Assertion Failed no. of entries read from the stream doesn't match no. of entries trimmed"
            );
        }
    } else {
        logger::error!(read_entries = %read_count,?entries,"No streams were processed in this session");
    }

    Ok(())
}


pub struct TrustpayRefundRequestBankRedirect {
    pub merchant_identification: MerchantIdentification,
    pub payment_information: BankPaymentInformation,
}


pub struct BrowserInfoCollected {
    pub browser_accept_header: Option<String>,
    pub browser_color_depth: Option<String>,
    #[serde(rename = "browserIP")]
    pub browser_ip: Option<Secret<String, common_utils::pii::IpAddress>>,
    pub browser_javascript_enabled: Option<bool>,
    pub browser_java_enabled: Option<bool>,
    pub browser_language: Option<String>,
    pub browser_screen_height: Option<String>,
    pub browser_screen_width: Option<String>,
    #[serde(rename = "browserTZ")]
    pub browser_tz: Option<String>,
    pub browser_user_agent: Option<String>,
}


- - -

## 2025.07.30.0

### Features

- **connector:**
  - [BLUECODE] Added Template Code ([#8756](https://github.com/juspay/hyperswitch/pull/8756)) ([`9f6182b`](https://github.com/juspay/hyperswitch/commit/9f6182b7fbb334447f2dda77f70d780ebbcde0ea))
  - [FISERV] Added GooglePay Payment Method - Connector Decryption Flow ([#8658](https://github.com/juspay/hyperswitch/pull/8658)) ([`b5586b6`](https://github.com/juspay/hyperswitch/commit/b5586b68d8e7380ee57ebcaf93a68ecd59c686fc))
  - [Adyen] receive incoming webhooks for pix expiry ([#8720](https://github.com/juspay/hyperswitch/pull/8720)) ([`4587564`](https://github.com/juspay/hyperswitch/commit/4587564824dff214744425d8e6dbfac8850e8416))
- **routing:** Add api-refs for new decision engine endpoints ([#8709](https://github.com/juspay/hyperswitch/pull/8709)) ([`4dea30f`](https://github.com/juspay/hyperswitch/commit/4dea30ffa0d9bf636af3757c998e12a23f4769bd))

### Bug Fixes

- **connector:**
  - [Worldpay] handle multiple ddc submission for CompleteAuthorize ([#8741](https://github.com/juspay/hyperswitch/pull/8741)) ([`f6cdddc`](https://github.com/juspay/hyperswitch/commit/f6cdddcb98e08f97dc518c8c14569fe67426bd6f))
  - [GLOBALPAY] Added Tokenization Flow for CITs ([#8568](https://github.com/juspay/hyperswitch/pull/8568)) ([`f7bc33c`](https://github.com/juspay/hyperswitch/commit/f7bc33cd0f5537e7be05af4064df8715ed47011b))
- Replace xtrim with xdel to support older redis version ([#8515](https://github.com/juspay/hyperswitch/pull/8515)) ([`3d085ab`](https://github.com/juspay/hyperswitch/commit/3d085abf3853c1658e208a733bf69715f44ba0bb))

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

- - -

## 2025.07.29.0

### Documentation

- Add initial authentication page ([#8719](https://github.com/juspay/hyperswitch/pull/8719)) ([`5ca0fb5`](https://github.com/juspay/hyperswitch/commit/5ca0fb5f654c9b32186dab8a2a2d475f2eb7df6a))

### Miscellaneous Tasks

- Updated open api spec documentation for create platform account ([#8692](https://github.com/juspay/hyperswitch/pull/8692)) ([`3bf4c49`](https://github.com/juspay/hyperswitch/commit/3bf4c490563d836685157351b66fc662bca0debd))

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

- - -

## 2025.07.28.1

### Features

- **core:** Hyperswitch <|> UCS Mandate flow integration ([#8738](https://github.com/juspay/hyperswitch/pull/8738)) ([`f94f39e`](https://github.com/juspay/hyperswitch/commit/f94f39ef0cae800a8ecde21e8d4a95b14c074f88))
- **themes:** Create user APIs for managing themes ([#8387](https://github.com/juspay/hyperswitch/pull/8387)) ([`20049d5`](https://github.com/juspay/hyperswitch/commit/20049d52fa3d3123c9942109c3ee3acc011d5978))

### Refactors

- Add routing_approach other variant to handle unknown data ([#8754](https://github.com/juspay/hyperswitch/pull/8754)) ([`7682cd4`](https://github.com/juspay/hyperswitch/commit/7682cd445eccdc8c213e0013a1fd025b21a4414d))

### Miscellaneous Tasks

- Update devDependencies for cypress ([#8735](https://github.com/juspay/hyperswitch/pull/8735)) ([`b30a7b8`](https://github.com/juspay/hyperswitch/commit/b30a7b809382a0119788e6807bd2eb8918d6edac))

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

- - -

## 2025.07.28.0

### Features

- **authentication:** Add authentication api for modular authentication ([#8459](https://github.com/juspay/hyperswitch/pull/8459)) ([`dbdf757`](https://github.com/juspay/hyperswitch/commit/dbdf7579d278bff37ffbbe9e10028dca7cc61369))
- **connector:**
  - [Flexiti]template code for flexiti connector ([#8714](https://github.com/juspay/hyperswitch/pull/8714)) ([`0f66692`](https://github.com/juspay/hyperswitch/commit/0f66692edeb26fb7092024410370d066e88ff74f))
  - [MPGS] template code ([#8544](https://github.com/juspay/hyperswitch/pull/8544)) ([`98d924b`](https://github.com/juspay/hyperswitch/commit/98d924bf870b1ffdd24181deb2cea2e2778b7708))
- **recovery-events:** Add revenue recovery topic and vector config to push these events to s3 ([#8285](https://github.com/juspay/hyperswitch/pull/8285)) ([`17d34a2`](https://github.com/juspay/hyperswitch/commit/17d34a29e4a286ca1acc0169f00322c2eab217d1))

### Bug Fixes

- **chat:** Append request id to headers for chat request ([#8680](https://github.com/juspay/hyperswitch/pull/8680)) ([`3113d2b`](https://github.com/juspay/hyperswitch/commit/3113d2b5dc3ab78a17ffc047337170b6806779c2))

### Refactors

- **connector:** [facilitapay] move destination bank account number to connector metadata ([#8704](https://github.com/juspay/hyperswitch/pull/8704)) ([`38c19f3`](https://github.com/juspay/hyperswitch/commit/38c19f30fce7515869d800bd5cabf3a7dd804e55))
- **schema:** Add a new column for storing large customer user agents in mandate table ([#8616](https://github.com/juspay/hyperswitch/pull/8616)) ([`b74c1e9`](https://github.com/juspay/hyperswitch/commit/b74c1e918833d938c1d3ecdbad895c3fb84ac2d1))

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

- - -

## 2025.07.25.0

### Features

- **connector:**
  - [SILVERFLOW] Integrate cards non 3ds payments ([#8591](https://github.com/juspay/hyperswitch/pull/8591)) ([`b521951`](https://github.com/juspay/hyperswitch/commit/b521951942dd653f6a78729926d91e8e793cd999))
  - [TRUSTPAYMENTS] Add Template Code ([#8672](https://github.com/juspay/hyperswitch/pull/8672)) ([`3159071`](https://github.com/juspay/hyperswitch/commit/3159071839f2fb14a11d3af7f657d7c62b53105b))
  - [AIRWALLEX] Added Ideal & Skrill payment methods ([#8535](https://github.com/juspay/hyperswitch/pull/8535)) ([`15d589d`](https://github.com/juspay/hyperswitch/commit/15d589d27bf8375367c0f922ed76783ad210b5b0))
  - [Breadpay]Add support for Breadpay connector ([#8676](https://github.com/juspay/hyperswitch/pull/8676)) ([`0d9750c`](https://github.com/juspay/hyperswitch/commit/0d9750cd2405a8da9132d24ba8770a416935fa47))
- **core:** Consuming locale in PaymentsAuthorizeData from SessionState ([#8731](https://github.com/juspay/hyperswitch/pull/8731)) ([`be03dd2`](https://github.com/juspay/hyperswitch/commit/be03dd2ae9a4df9b592aa3fc482e5356843a7163))

### Bug Fixes

- **core:** Fixed invalid merchant country code error object ([#8665](https://github.com/juspay/hyperswitch/pull/8665)) ([`20b37bd`](https://github.com/juspay/hyperswitch/commit/20b37bdb7a25ec053e7671332d4837a0ad743983))
- **feature_matrix:** Refunds are supported by jpmorgan ([#8699](https://github.com/juspay/hyperswitch/pull/8699)) ([`5c78abd`](https://github.com/juspay/hyperswitch/commit/5c78abd77ef886b2c81addbd8094132ec7aecf5f))
- **payment-methods:** Fetch payment method details in payouts flow ([#8729](https://github.com/juspay/hyperswitch/pull/8729)) ([`56d8ed7`](https://github.com/juspay/hyperswitch/commit/56d8ed72458f189084bf09450276bbce3a80e62a))
- **router:** Add payment method Wallet check for decrypted wallet payment method token logic ([#8744](https://github.com/juspay/hyperswitch/pull/8744)) ([`be00272`](https://github.com/juspay/hyperswitch/commit/be002728a365b92df52125d445e1e8a4c3181f0b))

### Refactors

- **core:** Remove hardcoded timeout limit of 5s for outgoing webhook requests ([#8725](https://github.com/juspay/hyperswitch/pull/8725)) ([`8c7ddaa`](https://github.com/juspay/hyperswitch/commit/8c7ddaa46d484973056e63471b0ec4fdf2e4f412))

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

- - -

## 2025.07.23.0

### Features

- **connector:**
  - Add template code for breadpay ([#8655](https://github.com/juspay/hyperswitch/pull/8655)) ([`b2ab927`](https://github.com/juspay/hyperswitch/commit/b2ab92771307a0a2bc7c208ddbff50f9bf381d8a))
  - [AUTHORIZEDOTNET] customer_id length validated and server-too-busy error handled ([#8696](https://github.com/juspay/hyperswitch/pull/8696)) ([`422724c`](https://github.com/juspay/hyperswitch/commit/422724cfe9b38db5a314b2cb3d4ec5365774f3b7))
- **core:**
  - Implement UCS kill switch for emergency fallback ([#8651](https://github.com/juspay/hyperswitch/pull/8651)) ([`0c5bbad`](https://github.com/juspay/hyperswitch/commit/0c5bbad9c2bdba44011b36213b446740c78cb842))
  - Add authentication date in Authentication Flow ([#8661](https://github.com/juspay/hyperswitch/pull/8661)) ([`a93ebda`](https://github.com/juspay/hyperswitch/commit/a93ebda7b520b6df82216bf93ab67ff2ba562002))

### Bug Fixes

- **connector:** Add Trustpay in Authentication Providers Config ([#8622](https://github.com/juspay/hyperswitch/pull/8622)) ([`12ac508`](https://github.com/juspay/hyperswitch/commit/12ac5086a0a67b43f00844a041814bfcef93bb20))
- **openapi:** Added Error Response Schema for Status Code 400 ([#8684](https://github.com/juspay/hyperswitch/pull/8684)) ([`a01d608`](https://github.com/juspay/hyperswitch/commit/a01d6083b00404b6b1cb3380c10cb6a676cda476))

### Refactors

- **connector:** [Adyen] map ssn and session validity for Pix ([#8702](https://github.com/juspay/hyperswitch/pull/8702)) ([`9bf1e95`](https://github.com/juspay/hyperswitch/commit/9bf1e95f06af4dd0620133cdadca6b2522b27e6e))

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

- - -

## 2025.07.21.1

### Features

- **authentication:** Added eligibility flow for modular authentication ([#8431](https://github.com/juspay/hyperswitch/pull/8431)) ([`82eb3ca`](https://github.com/juspay/hyperswitch/commit/82eb3ca7b4b0f513ed3d8281523d7d646c8129a9))
- **connector:** [BLACKHAWKNETWORK] Add Template Code ([#8632](https://github.com/juspay/hyperswitch/pull/8632)) ([`7f6a126`](https://github.com/juspay/hyperswitch/commit/7f6a1266a1868d336a0ce9a85e5b8a5a9c736adb))
- **routing:** Add API key auth for decision engine endpoints ([#8640](https://github.com/juspay/hyperswitch/pull/8640)) ([`f4da057`](https://github.com/juspay/hyperswitch/commit/f4da057ca8252719a18d5eb804828184bc3c1b00))

### Bug Fixes

- **connector:**
  - [Access Worldpay] correct enum deserialization for payment responses for ([#8689](https://github.com/juspay/hyperswitch/pull/8689)) ([`58f8429`](https://github.com/juspay/hyperswitch/commit/58f8429f507c32e75fea7365504ae8573103c912))
  - [Cybersource] Add type_selection_indicator as 1 for all cards ([#8663](https://github.com/juspay/hyperswitch/pull/8663)) ([`2bac3e0`](https://github.com/juspay/hyperswitch/commit/2bac3e0a65283326a3050b0ce2af2e54aa1ea496))
- **router:** Make v2 endpoints follow standard naming conventions ([#8630](https://github.com/juspay/hyperswitch/pull/8630)) ([`db14764`](https://github.com/juspay/hyperswitch/commit/db14764671d18fbbd8d699f0c7c97a327bc1d0db))
- Remove straight through routing from routing approach ([#8695](https://github.com/juspay/hyperswitch/pull/8695)) ([`53e7e7f`](https://github.com/juspay/hyperswitch/commit/53e7e7fa819183eaad1e8321f7e2b89b22867163))

### Refactors

- **payments:** Fetch payment method information in attempts list api v2 and add custom billing connector template ([#8681](https://github.com/juspay/hyperswitch/pull/8681)) ([`110beaf`](https://github.com/juspay/hyperswitch/commit/110beaff9a809f30a704b5ef9cb7dc75cc31a7bf))

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

- - -

## 2025.07.21.0

### Features

- **debit_routing:** Add debit routing support for apple pay ([#8673](https://github.com/juspay/hyperswitch/pull/8673)) ([`d42fad7`](https://github.com/juspay/hyperswitch/commit/d42fad73f502933ecf50215503f0bea41813662c))
- **routing:** Add routing evaluation rule endpoint and related flow ([#8656](https://github.com/juspay/hyperswitch/pull/8656)) ([`126018e`](https://github.com/juspay/hyperswitch/commit/126018e217bfbd8bdd44043ace8a815046193818))

### Bug Fixes

- **connector:** [AUTHORIZEDOTNET] Added Invoice Number Fix ([#8685](https://github.com/juspay/hyperswitch/pull/8685)) ([`0016849`](https://github.com/juspay/hyperswitch/commit/00168496b7283a4278c72418c7f33e622598980b))

### Refactors

- **router:** Decrypt the wallet token before the debit routing call ([#8598](https://github.com/juspay/hyperswitch/pull/8598)) ([`bf8dc49`](https://github.com/juspay/hyperswitch/commit/bf8dc4959eab4e2a2ca8120055ff80900418fe0b))

### Miscellaneous Tasks

- Update org retrieve api response to include org type ([#8660](https://github.com/juspay/hyperswitch/pull/8660)) ([`a75af8b`](https://github.com/juspay/hyperswitch/commit/a75af8b3e6ae342c66e690ab79dad1b8a7ff0a8e))

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

- - -

## 2025.07.17.0

### Features

- **ai:** Add endpoints to chat with ai service ([#8585](https://github.com/juspay/hyperswitch/pull/8585)) ([`3d60e6c`](https://github.com/juspay/hyperswitch/commit/3d60e6c4c807f65fed93a341ef1d8063f818b31d))
- **connector:**
  - Added Supported Country and Currencies ([#8383](https://github.com/juspay/hyperswitch/pull/8383)) ([`26b3a96`](https://github.com/juspay/hyperswitch/commit/26b3a96c9b5e0f40769c1439e056da17614afe91))
  - [AFFIRM] add Connector Template Code ([#8650](https://github.com/juspay/hyperswitch/pull/8650)) ([`9bc0251`](https://github.com/juspay/hyperswitch/commit/9bc02516d6ca4ce1a998f0a7c800ca1eaa078ad7))
- **payments:** Add feature_metadata in the payments response for v2 ([#8667](https://github.com/juspay/hyperswitch/pull/8667)) ([`dcf867d`](https://github.com/juspay/hyperswitch/commit/dcf867d96ed899886b5246f0f53aa2ddbb2ed0cf))

### Refactors

- **core:** Make raw_connector_response as secret ([#8565](https://github.com/juspay/hyperswitch/pull/8565)) ([`fb33937`](https://github.com/juspay/hyperswitch/commit/fb33937c21dd481c78fa2f99222dc02320399677))
- **payment-links:** Use top redirection in status page ([#8641](https://github.com/juspay/hyperswitch/pull/8641)) ([`30a4a34`](https://github.com/juspay/hyperswitch/commit/30a4a340c331bc84de1f76d2f5d154b61f08fa36))

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

- - -

## 2025.07.16.0

### Features

- **config:** Enabled platform feature on sandbox ([#8580](https://github.com/juspay/hyperswitch/pull/8580)) ([`11205b4`](https://github.com/juspay/hyperswitch/commit/11205b4b8fe672a155696fb73ed992fef3e0f26c))
- **connector:**
  - Add apple pay decrypt support for Adyen ([#8605](https://github.com/juspay/hyperswitch/pull/8605)) ([`064113a`](https://github.com/juspay/hyperswitch/commit/064113a4c96c77d83e1e0230a56678863dd8f7db))
  - [AUTHORIZEDOTNET] custom invoice number populated ([#8635](https://github.com/juspay/hyperswitch/pull/8635)) ([`7fb03a9`](https://github.com/juspay/hyperswitch/commit/7fb03a9bfd25fe21ec5e820251160707490e072f))
- **payment_methods:** [Paysera, Skrill] Add support to paysera and skrill wallets and in shift4 ([#8487](https://github.com/juspay/hyperswitch/pull/8487)) ([`45e2d5c`](https://github.com/juspay/hyperswitch/commit/45e2d5c1447bf7ff7c4c31d4c12dfc08d95da457))
- **routing:** Add support to pass fallback connectors to decision engine ([#8615](https://github.com/juspay/hyperswitch/pull/8615)) ([`d9e1d88`](https://github.com/juspay/hyperswitch/commit/d9e1d88183c85fe0960e297fd34af818822de2ae))

### Bug Fixes

- **connector:** [Worldpayvantiv] Psync status mapping and add address lines to billing data ([#8602](https://github.com/juspay/hyperswitch/pull/8602)) ([`a4ec03f`](https://github.com/juspay/hyperswitch/commit/a4ec03f922751774f73bdbc08e6ced6f7b2c3288))

### Refactors

- **nix:** Migrate from cargo2nix to rust-flake ([#8503](https://github.com/juspay/hyperswitch/pull/8503)) ([`871c082`](https://github.com/juspay/hyperswitch/commit/871c08249d8006b54fe46d78f835452a39242ba8))

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

- - -

## 2025.07.15.0

### Features

- **business_profile:** Added merchant country code in business profile ([#8529](https://github.com/juspay/hyperswitch/pull/8529)) ([`44f8964`](https://github.com/juspay/hyperswitch/commit/44f8964a05dcc61026988b19d7297ca1287e7b9f))
- **connector:**
  - [Multisafepay] Integrate EPS, MBWAY and SOFORT ([#8506](https://github.com/juspay/hyperswitch/pull/8506)) ([`8a9d7d2`](https://github.com/juspay/hyperswitch/commit/8a9d7d22108b649ea998b84995603d61915fde2a))
  - Add Incremental Authorization flow for Paypal ([#8517](https://github.com/juspay/hyperswitch/pull/8517)) ([`fd6de7c`](https://github.com/juspay/hyperswitch/commit/fd6de7cc24850cda2c97ca01e5bf69e341790bdb))
- **router:** Deduplicate PML response and populate bank details (V2) ([#8583](https://github.com/juspay/hyperswitch/pull/8583)) ([`7d54ce8`](https://github.com/juspay/hyperswitch/commit/7d54ce87f0dbf66b774a08545cae48bcfc606512))

### Bug Fixes

- **users:** Fallback to fresh lineage context if cached role_id no longer matches ([#8593](https://github.com/juspay/hyperswitch/pull/8593)) ([`aaa4fca`](https://github.com/juspay/hyperswitch/commit/aaa4fca58d8eaab0eb0163aad226427eebf7c7cd))

### Refactors

- **dynamic_routing:** Make the dynamo configs optional ([#8589](https://github.com/juspay/hyperswitch/pull/8589)) ([`4048aa1`](https://github.com/juspay/hyperswitch/commit/4048aa16cadd98605b81aebc7543da50c36454b9))

### Miscellaneous Tasks

- **stripe:** Eps bank_name should be mandatory ([#8577](https://github.com/juspay/hyperswitch/pull/8577)) ([`fffaa53`](https://github.com/juspay/hyperswitch/commit/fffaa53a5fe4859bea607f2eb6bf866b4a20b949))

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

- - -

## 2025.07.14.1

### Features


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

---
openapi: put /v2/merchant-accounts/{id}
---


pub struct VaultDeleteResponse {
    pub entity_id: id_type::GlobalCustomerId,
    pub vault_id: domain::VaultId,
}


pub struct ListOrgsForUserResponse {
    pub org_id: id_type::OrganizationId,
    pub org_name: Option<String>,
    pub org_type: common_enums::OrganizationType,
}


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

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

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

    fn get_auth_header(
        &self,
        auth_type: &ConnectorAuthType,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        let auth = powertranz::PowertranzAuthType::try_from(auth_type)
            .change_context(errors::ConnectorError::FailedToObtainAuthType)?;
        Ok(vec![
            (
                POWER_TRANZ_ID.to_string(),
                auth.power_tranz_id.expose().into_masked(),
            ),
            (
                POWER_TRANZ_PASSWORD.to_string(),
                auth.power_tranz_password.expose().into_masked(),
            ),
        ])
    }

    fn build_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        // For error scenarios connector respond with 200 http status code and error response object in response
        // For http status code other than 200 they send empty response back
        event_builder.map(|i: &mut ConnectorEvent| i.set_error_response_body(&serde_json::json!({"error_response": std::str::from_utf8(&res.response).unwrap_or("")})));

        Ok(ErrorResponse {
            status_code: res.status_code,
            code: consts::NO_ERROR_CODE.to_string(),
            message: consts::NO_ERROR_MESSAGE.to_string(),
            reason: None,
            attempt_status: None,
            connector_transaction_id: None,
            network_advice_code: None,
            network_decline_code: None,
            network_error_message: None,
            connector_metadata: None,
        })
    }
}


impl ConnectorPreAuthenticationVersionCall for Gpayments {}

impl ConnectorPreAuthenticationVersionCall for for Gpayments


impl api::PaymentSync for Flexiti {}


pub struct WorldpayPaymentsResponse {
    pub outcome: PaymentOutcome,
    pub transaction_reference: Option<String>,
    #[serde(flatten)]
    pub other_fields: Option<WorldpayPaymentResponseFields>,
}


impl RoutingAlgorithmUpdate {
    pub fn create_new_routing_algorithm(
        request: &routing_types::RoutingConfigRequest,
        merchant_id: &common_utils::id_type::MerchantId,
        profile_id: common_utils::id_type::ProfileId,
        transaction_type: enums::TransactionType,
    ) -> Self {
        let algorithm_id = common_utils::generate_routing_id_of_default_length();
        let timestamp = common_utils::date_time::now();
        let algo = RoutingAlgorithm {
            algorithm_id,
            profile_id,
            merchant_id: merchant_id.clone(),
            name: request.name.clone(),
            description: Some(request.description.clone()),
            kind: request.algorithm.get_kind().foreign_into(),
            algorithm_data: serde_json::json!(request.algorithm),
            created_at: timestamp,
            modified_at: timestamp,
            algorithm_for: transaction_type,
            decision_engine_routing_id: None,
        };
        Self(algo)
    }
    pub async fn fetch_routing_algo(
        merchant_id: &common_utils::id_type::MerchantId,
        algorithm_id: &common_utils::id_type::RoutingId,
        db: &dyn StorageInterface,
    ) -> RouterResult<Self> {
        let routing_algo = db
            .find_routing_algorithm_by_algorithm_id_merchant_id(algorithm_id, merchant_id)
            .await
            .change_context(errors::ApiErrorResponse::ResourceIdNotFound)?;
        Ok(Self(routing_algo))
    }
}


impl IncomingWebhook for Helcim {
    fn get_webhook_object_reference_id(
        &self,
        _request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<api_models::webhooks::ObjectReferenceId, errors::ConnectorError> {
        Err(report!(errors::ConnectorError::WebhooksNotImplemented))
    }

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

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


pub struct FrmMetricsAccumulator {
    pub frm_triggered_attempts: TriggeredAttemptsAccumulator,
    pub frm_blocked_rate: BlockedRateAccumulator,
}