Bug: [FEATURE] add payment_expired outgoing webhook



### Feature Description

Payment methods related to QR code which generally has an expiration time / date associated comes to end of its lifecycle if they're not fulfilled. For such payments, underlying connector can send an incoming webhook when they expire.

HS needs to provision a status (or if `Cancelled` can be reused?) and a variant for `EventType` which specifies they type of outgoing webhook event being generated.

These are needed to be consumed for the supported connectors (Adyen for now) and also send an outgoing webhook once they expire.

### Possible Implementation

- Integrate OFFER_CLOSED from Adyen as a part of incoming webhooks - https://docs.adyen.com/development-resources/webhooks/webhook-types/#non-default-event-codes
- ** Map it to `cancelled` in `IntentStatus` (map to `voided` in `AttemptStatus`)
diff --git a/crates/api_models/src/webhooks.rs b/crates/api_models/src/webhooks.rs
index 75af0b19cf2..fcab9461a00 100644
--- a/crates/api_models/src/webhooks.rs
+++ b/crates/api_models/src/webhooks.rs
@@ -22,6 +22,7 @@ pub enum IncomingWebhookEvent {
     PaymentIntentAuthorizationFailure,
     PaymentIntentCaptureSuccess,
     PaymentIntentCaptureFailure,
+    PaymentIntentExpired,
     PaymentActionRequired,
     EventNotSupported,
     SourceChargeable,
@@ -199,7 +200,8 @@ impl From<IncomingWebhookEvent> for WebhookFlow {
             | IncomingWebhookEvent::PaymentIntentAuthorizationSuccess
             | IncomingWebhookEvent::PaymentIntentAuthorizationFailure
             | IncomingWebhookEvent::PaymentIntentCaptureSuccess
-            | IncomingWebhookEvent::PaymentIntentCaptureFailure => Self::Payment,
+            | IncomingWebhookEvent::PaymentIntentCaptureFailure
+            | IncomingWebhookEvent::PaymentIntentExpired => Self::Payment,
             IncomingWebhookEvent::EventNotSupported => Self::ReturnResponse,
             IncomingWebhookEvent::RefundSuccess | IncomingWebhookEvent::RefundFailure => {
                 Self::Refund
diff --git a/crates/common_enums/src/enums.rs b/crates/common_enums/src/enums.rs
index 12f1fe5581f..a2912e73c45 100644
--- a/crates/common_enums/src/enums.rs
+++ b/crates/common_enums/src/enums.rs
@@ -156,6 +156,7 @@ pub enum AttemptStatus {
     ConfirmationAwaited,
     DeviceDataCollectionPending,
     IntegrityFailure,
+    Expired,
 }
 
 impl AttemptStatus {
@@ -168,7 +169,8 @@ impl AttemptStatus {
             | Self::VoidFailed
             | Self::CaptureFailed
             | Self::Failure
-            | Self::PartialCharged => true,
+            | Self::PartialCharged
+            | Self::Expired => true,
             Self::Started
             | Self::AuthenticationFailed
             | Self::AuthenticationPending
@@ -1461,6 +1463,7 @@ impl EventClass {
                 EventType::PaymentCancelled,
                 EventType::PaymentAuthorized,
                 EventType::PaymentCaptured,
+                EventType::PaymentExpired,
                 EventType::ActionRequired,
             ]),
             Self::Refunds => HashSet::from([EventType::RefundSucceeded, EventType::RefundFailed]),
@@ -1514,6 +1517,7 @@ pub enum EventType {
     PaymentCancelled,
     PaymentAuthorized,
     PaymentCaptured,
+    PaymentExpired,
     ActionRequired,
     RefundSucceeded,
     RefundFailed,
@@ -1635,13 +1639,19 @@ pub enum IntentStatus {
     PartiallyCapturedAndCapturable,
     /// There has been a discrepancy between the amount/currency sent in the request and the amount/currency received by the processor
     Conflicted,
+    /// The payment expired before it could be captured.
+    Expired,
 }
 
 impl IntentStatus {
     /// Indicates whether the payment intent is in terminal state or not
     pub fn is_in_terminal_state(self) -> bool {
         match self {
-            Self::Succeeded | Self::Failed | Self::Cancelled | Self::PartiallyCaptured => true,
+            Self::Succeeded
+            | Self::Failed
+            | Self::Cancelled
+            | Self::PartiallyCaptured
+            | Self::Expired => true,
             Self::Processing
             | Self::RequiresCustomerAction
             | Self::RequiresMerchantAction
@@ -1664,7 +1674,7 @@ impl IntentStatus {
             | Self::Failed
             | Self::Cancelled
             |  Self::PartiallyCaptured
-            |  Self::RequiresCapture | Self::Conflicted => false,
+            |  Self::RequiresCapture | Self::Conflicted | Self::Expired=> false,
             Self::Processing
             | Self::RequiresCustomerAction
             | Self::RequiresMerchantAction
@@ -1796,7 +1806,8 @@ impl From<AttemptStatus> for PaymentMethodStatus {
             | AttemptStatus::PartialChargedAndChargeable
             | AttemptStatus::ConfirmationAwaited
             | AttemptStatus::DeviceDataCollectionPending
-            | AttemptStatus::IntegrityFailure => Self::Inactive,
+            | AttemptStatus::IntegrityFailure
+            | AttemptStatus::Expired => Self::Inactive,
             AttemptStatus::Charged | AttemptStatus::Authorized => Self::Active,
         }
     }
diff --git a/crates/common_enums/src/transformers.rs b/crates/common_enums/src/transformers.rs
index 0dcef36ac97..549be0e0dc6 100644
--- a/crates/common_enums/src/transformers.rs
+++ b/crates/common_enums/src/transformers.rs
@@ -2121,6 +2121,7 @@ impl From<AttemptStatus> for IntentStatus {
             | AttemptStatus::CaptureFailed
             | AttemptStatus::Failure => Self::Failed,
             AttemptStatus::Voided => Self::Cancelled,
+            AttemptStatus::Expired => Self::Expired,
         }
     }
 }
@@ -2135,6 +2136,7 @@ impl From<IntentStatus> for Option<EventType> {
             | IntentStatus::RequiresCustomerAction
             | IntentStatus::Conflicted => Some(EventType::ActionRequired),
             IntentStatus::Cancelled => Some(EventType::PaymentCancelled),
+            IntentStatus::Expired => Some(EventType::PaymentExpired),
             IntentStatus::PartiallyCaptured | IntentStatus::PartiallyCapturedAndCapturable => {
                 Some(EventType::PaymentCaptured)
             }
diff --git a/crates/hyperswitch_connectors/src/connectors/adyen/transformers.rs b/crates/hyperswitch_connectors/src/connectors/adyen/transformers.rs
index 6a0cc9c4610..5b424ec5d4d 100644
--- a/crates/hyperswitch_connectors/src/connectors/adyen/transformers.rs
+++ b/crates/hyperswitch_connectors/src/connectors/adyen/transformers.rs
@@ -428,6 +428,7 @@ impl ForeignTryFrom<(bool, AdyenWebhookStatus)> for storage_enums::AttemptStatus
             AdyenWebhookStatus::CancelFailed => Ok(Self::VoidFailed),
             AdyenWebhookStatus::Captured => Ok(Self::Charged),
             AdyenWebhookStatus::CaptureFailed => Ok(Self::CaptureFailed),
+            AdyenWebhookStatus::Expired => Ok(Self::Expired),
             //If Unexpected Event is received, need to understand how it reached this point
             //Webhooks with Payment Events only should try to conume this resource object.
             AdyenWebhookStatus::UnexpectedEvent | AdyenWebhookStatus::Reversed => {
@@ -513,6 +514,7 @@ pub enum AdyenWebhookStatus {
     CaptureFailed,
     Reversed,
     UnexpectedEvent,
+    Expired,
 }
 
 //Creating custom struct which can be consumed in Psync Handler triggered from Webhooks
@@ -4798,6 +4800,7 @@ pub enum WebhookEventCode {
     SecondChargeback,
     PrearbitrationWon,
     PrearbitrationLost,
+    OfferClosed,
     #[cfg(feature = "payouts")]
     PayoutThirdparty,
     #[cfg(feature = "payouts")]
@@ -4811,7 +4814,10 @@ pub enum WebhookEventCode {
 }
 
 pub fn is_transaction_event(event_code: &WebhookEventCode) -> bool {
-    matches!(event_code, WebhookEventCode::Authorisation)
+    matches!(
+        event_code,
+        WebhookEventCode::Authorisation | WebhookEventCode::OfferClosed
+    )
 }
 
 pub fn is_capture_or_cancel_event(event_code: &WebhookEventCode) -> bool {
@@ -4928,6 +4934,9 @@ pub(crate) fn get_adyen_webhook_event(
         WebhookEventCode::CaptureFailed => {
             api_models::webhooks::IncomingWebhookEvent::PaymentIntentCaptureFailure
         }
+        WebhookEventCode::OfferClosed => {
+            api_models::webhooks::IncomingWebhookEvent::PaymentIntentExpired
+        }
         #[cfg(feature = "payouts")]
         WebhookEventCode::PayoutThirdparty => {
             api_models::webhooks::IncomingWebhookEvent::PayoutCreated
@@ -5015,6 +5024,7 @@ impl From<AdyenNotificationRequestItemWH> for AdyenWebhookResponse {
                         AdyenWebhookStatus::CancelFailed
                     }
                 }
+                WebhookEventCode::OfferClosed => AdyenWebhookStatus::Expired,
                 WebhookEventCode::Capture => {
                     if is_success_scenario(notif.success) {
                         AdyenWebhookStatus::Captured
diff --git a/crates/hyperswitch_connectors/src/connectors/chargebee/transformers.rs b/crates/hyperswitch_connectors/src/connectors/chargebee/transformers.rs
index 5a3ec2f96f9..6bb4cf345f7 100644
--- a/crates/hyperswitch_connectors/src/connectors/chargebee/transformers.rs
+++ b/crates/hyperswitch_connectors/src/connectors/chargebee/transformers.rs
@@ -706,7 +706,8 @@ impl TryFrom<enums::AttemptStatus> for ChargebeeRecordStatus {
             | enums::AttemptStatus::PaymentMethodAwaited
             | enums::AttemptStatus::ConfirmationAwaited
             | enums::AttemptStatus::DeviceDataCollectionPending
-            | enums::AttemptStatus::IntegrityFailure => Err(errors::ConnectorError::NotSupported {
+            | enums::AttemptStatus::IntegrityFailure
+            | enums::AttemptStatus::Expired => Err(errors::ConnectorError::NotSupported {
                 message: "Record back flow is only supported for terminal status".to_string(),
                 connector: "chargebee",
             }
diff --git a/crates/hyperswitch_connectors/src/connectors/paypal/transformers.rs b/crates/hyperswitch_connectors/src/connectors/paypal/transformers.rs
index 289fee48d99..9ffc5a0c13a 100644
--- a/crates/hyperswitch_connectors/src/connectors/paypal/transformers.rs
+++ b/crates/hyperswitch_connectors/src/connectors/paypal/transformers.rs
@@ -2701,7 +2701,8 @@ impl TryFrom<PaymentsCaptureResponseRouterData<PaypalCaptureResponse>>
             | storage_enums::AttemptStatus::PaymentMethodAwaited
             | storage_enums::AttemptStatus::ConfirmationAwaited
             | storage_enums::AttemptStatus::DeviceDataCollectionPending
-            | storage_enums::AttemptStatus::Voided => 0,
+            | storage_enums::AttemptStatus::Voided
+            | storage_enums::AttemptStatus::Expired => 0,
             storage_enums::AttemptStatus::Charged
             | storage_enums::AttemptStatus::PartialCharged
             | storage_enums::AttemptStatus::PartialChargedAndChargeable
diff --git a/crates/hyperswitch_connectors/src/connectors/recurly/transformers.rs b/crates/hyperswitch_connectors/src/connectors/recurly/transformers.rs
index b7f7d3d39da..4aef739b979 100644
--- a/crates/hyperswitch_connectors/src/connectors/recurly/transformers.rs
+++ b/crates/hyperswitch_connectors/src/connectors/recurly/transformers.rs
@@ -278,7 +278,8 @@ impl TryFrom<enums::AttemptStatus> for RecurlyRecordStatus {
             | enums::AttemptStatus::PaymentMethodAwaited
             | enums::AttemptStatus::ConfirmationAwaited
             | enums::AttemptStatus::DeviceDataCollectionPending
-            | enums::AttemptStatus::IntegrityFailure => Err(errors::ConnectorError::NotSupported {
+            | enums::AttemptStatus::IntegrityFailure
+            | enums::AttemptStatus::Expired => Err(errors::ConnectorError::NotSupported {
                 message: "Record back flow is only supported for terminal status".to_string(),
                 connector: "recurly",
             }
diff --git a/crates/hyperswitch_connectors/src/utils.rs b/crates/hyperswitch_connectors/src/utils.rs
index 90a650a2b1f..7b9b314ce5a 100644
--- a/crates/hyperswitch_connectors/src/utils.rs
+++ b/crates/hyperswitch_connectors/src/utils.rs
@@ -425,7 +425,8 @@ pub(crate) fn is_payment_failure(status: AttemptStatus) -> bool {
         | AttemptStatus::AuthorizationFailed
         | AttemptStatus::CaptureFailed
         | AttemptStatus::VoidFailed
-        | AttemptStatus::Failure => true,
+        | AttemptStatus::Failure
+        | AttemptStatus::Expired => true,
         AttemptStatus::Started
         | AttemptStatus::RouterDeclined
         | AttemptStatus::AuthenticationPending
@@ -6295,7 +6296,8 @@ impl FrmTransactionRouterDataRequest for FrmTransactionRouterData {
             | AttemptStatus::Voided
             | AttemptStatus::CaptureFailed
             | AttemptStatus::Failure
-            | AttemptStatus::AutoRefunded => Some(false),
+            | AttemptStatus::AutoRefunded
+            | AttemptStatus::Expired => Some(false),
 
             AttemptStatus::AuthenticationSuccessful
             | AttemptStatus::PartialChargedAndChargeable
diff --git a/crates/hyperswitch_domain_models/src/revenue_recovery.rs b/crates/hyperswitch_domain_models/src/revenue_recovery.rs
index 6481a81d536..f9506e2ed3d 100644
--- a/crates/hyperswitch_domain_models/src/revenue_recovery.rs
+++ b/crates/hyperswitch_domain_models/src/revenue_recovery.rs
@@ -150,6 +150,7 @@ impl RecoveryAction {
             | webhooks::IncomingWebhookEvent::PaymentIntentAuthorizationFailure
             | webhooks::IncomingWebhookEvent::PaymentIntentCaptureSuccess
             | webhooks::IncomingWebhookEvent::PaymentIntentCaptureFailure
+            | webhooks::IncomingWebhookEvent::PaymentIntentExpired
             | webhooks::IncomingWebhookEvent::PaymentActionRequired
             | webhooks::IncomingWebhookEvent::EventNotSupported
             | webhooks::IncomingWebhookEvent::SourceChargeable
diff --git a/crates/hyperswitch_domain_models/src/router_data.rs b/crates/hyperswitch_domain_models/src/router_data.rs
index 259f280d49b..8cdcfe7e456 100644
--- a/crates/hyperswitch_domain_models/src/router_data.rs
+++ b/crates/hyperswitch_domain_models/src/router_data.rs
@@ -702,7 +702,8 @@ impl
             common_enums::IntentStatus::Succeeded
             | common_enums::IntentStatus::Failed
             | common_enums::IntentStatus::Cancelled
-            | common_enums::IntentStatus::Conflicted => Some(MinorUnit::zero()),
+            | common_enums::IntentStatus::Conflicted
+            | common_enums::IntentStatus::Expired => Some(MinorUnit::zero()),
             // For these statuses, update the capturable amount when it reaches terminal / capturable state
             common_enums::IntentStatus::RequiresCustomerAction
             | common_enums::IntentStatus::RequiresMerchantAction
@@ -737,9 +738,9 @@ impl
                 Some(total_amount)
             }
             // No amount is captured
-            common_enums::IntentStatus::Cancelled | common_enums::IntentStatus::Failed => {
-                Some(MinorUnit::zero())
-            }
+            common_enums::IntentStatus::Cancelled
+            | common_enums::IntentStatus::Failed
+            | common_enums::IntentStatus::Expired => Some(MinorUnit::zero()),
             // For these statuses, update the amount captured when it reaches terminal state
             common_enums::IntentStatus::RequiresCustomerAction
             | common_enums::IntentStatus::RequiresMerchantAction
@@ -913,7 +914,8 @@ impl
             common_enums::IntentStatus::Succeeded
             | common_enums::IntentStatus::Failed
             | common_enums::IntentStatus::Cancelled
-            | common_enums::IntentStatus::Conflicted => Some(MinorUnit::zero()),
+            | common_enums::IntentStatus::Conflicted
+            | common_enums::IntentStatus::Expired => Some(MinorUnit::zero()),
             // For these statuses, update the capturable amount when it reaches terminal / capturable state
             common_enums::IntentStatus::RequiresCustomerAction
             | common_enums::IntentStatus::RequiresMerchantAction
@@ -951,9 +953,9 @@ impl
                 Some(amount_captured)
             }
             // No amount is captured
-            common_enums::IntentStatus::Cancelled | common_enums::IntentStatus::Failed => {
-                Some(MinorUnit::zero())
-            }
+            common_enums::IntentStatus::Cancelled
+            | common_enums::IntentStatus::Failed
+            | common_enums::IntentStatus::Expired => Some(MinorUnit::zero()),
             common_enums::IntentStatus::RequiresCapture => {
                 let total_amount = payment_data.payment_attempt.amount_details.get_net_amount();
                 Some(total_amount)
@@ -1149,7 +1151,8 @@ impl
             common_enums::IntentStatus::Succeeded
             | common_enums::IntentStatus::Failed
             | common_enums::IntentStatus::Cancelled
-            | common_enums::IntentStatus::Conflicted => Some(MinorUnit::zero()),
+            | common_enums::IntentStatus::Conflicted
+            | common_enums::IntentStatus::Expired => Some(MinorUnit::zero()),
             // For these statuses, update the capturable amount when it reaches terminal / capturable state
             common_enums::IntentStatus::RequiresCustomerAction
             | common_enums::IntentStatus::RequiresMerchantAction
@@ -1186,9 +1189,9 @@ impl
                 Some(amount_captured)
             }
             // No amount is captured
-            common_enums::IntentStatus::Cancelled | common_enums::IntentStatus::Failed => {
-                Some(MinorUnit::zero())
-            }
+            common_enums::IntentStatus::Cancelled
+            | common_enums::IntentStatus::Failed
+            | common_enums::IntentStatus::Expired => Some(MinorUnit::zero()),
             // For these statuses, update the amount captured when it reaches terminal state
             common_enums::IntentStatus::RequiresCustomerAction
             | common_enums::IntentStatus::RequiresMerchantAction
@@ -1382,7 +1385,8 @@ impl
             common_enums::IntentStatus::Succeeded
             | common_enums::IntentStatus::Failed
             | common_enums::IntentStatus::Cancelled
-            | common_enums::IntentStatus::Conflicted => Some(MinorUnit::zero()),
+            | common_enums::IntentStatus::Conflicted
+            | common_enums::IntentStatus::Expired => Some(MinorUnit::zero()),
             // For these statuses, update the capturable amount when it reaches terminal / capturable state
             common_enums::IntentStatus::RequiresCustomerAction
             | common_enums::IntentStatus::RequiresMerchantAction
@@ -1417,9 +1421,9 @@ impl
                 Some(total_amount)
             }
             // No amount is captured
-            common_enums::IntentStatus::Cancelled | common_enums::IntentStatus::Failed => {
-                Some(MinorUnit::zero())
-            }
+            common_enums::IntentStatus::Cancelled
+            | common_enums::IntentStatus::Failed
+            | common_enums::IntentStatus::Expired => Some(MinorUnit::zero()),
             // For these statuses, update the amount captured when it reaches terminal state
             common_enums::IntentStatus::RequiresCustomerAction
             | common_enums::IntentStatus::RequiresMerchantAction
diff --git a/crates/router/src/compatibility/stripe/payment_intents/types.rs b/crates/router/src/compatibility/stripe/payment_intents/types.rs
index e8813fad688..acf2f5e03ab 100644
--- a/crates/router/src/compatibility/stripe/payment_intents/types.rs
+++ b/crates/router/src/compatibility/stripe/payment_intents/types.rs
@@ -421,7 +421,7 @@ impl From<api_enums::IntentStatus> for StripePaymentStatus {
             api_enums::IntentStatus::Succeeded | api_enums::IntentStatus::PartiallyCaptured => {
                 Self::Succeeded
             }
-            api_enums::IntentStatus::Failed => Self::Canceled,
+            api_enums::IntentStatus::Failed | api_enums::IntentStatus::Expired => Self::Canceled,
             api_enums::IntentStatus::Processing => Self::Processing,
             api_enums::IntentStatus::RequiresCustomerAction
             | api_enums::IntentStatus::RequiresMerchantAction
diff --git a/crates/router/src/compatibility/stripe/setup_intents/types.rs b/crates/router/src/compatibility/stripe/setup_intents/types.rs
index 3cf996685ce..90df416ec76 100644
--- a/crates/router/src/compatibility/stripe/setup_intents/types.rs
+++ b/crates/router/src/compatibility/stripe/setup_intents/types.rs
@@ -312,7 +312,7 @@ impl From<api_enums::IntentStatus> for StripeSetupStatus {
             api_enums::IntentStatus::Succeeded | api_enums::IntentStatus::PartiallyCaptured => {
                 Self::Succeeded
             }
-            api_enums::IntentStatus::Failed => Self::Canceled,
+            api_enums::IntentStatus::Failed | api_enums::IntentStatus::Expired => Self::Canceled,
             api_enums::IntentStatus::Processing => Self::Processing,
             api_enums::IntentStatus::RequiresCustomerAction => Self::RequiresAction,
             api_enums::IntentStatus::RequiresMerchantAction
diff --git a/crates/router/src/compatibility/stripe/webhooks.rs b/crates/router/src/compatibility/stripe/webhooks.rs
index 3bd0f83b7db..5dad60120ec 100644
--- a/crates/router/src/compatibility/stripe/webhooks.rs
+++ b/crates/router/src/compatibility/stripe/webhooks.rs
@@ -270,7 +270,8 @@ fn get_stripe_event_type(event_type: api_models::enums::EventType) -> &'static s
         api_models::enums::EventType::PaymentSucceeded => "payment_intent.succeeded",
         api_models::enums::EventType::PaymentFailed => "payment_intent.payment_failed",
         api_models::enums::EventType::PaymentProcessing => "payment_intent.processing",
-        api_models::enums::EventType::PaymentCancelled => "payment_intent.canceled",
+        api_models::enums::EventType::PaymentCancelled
+        | api_models::enums::EventType::PaymentExpired => "payment_intent.canceled",
 
         // the below are not really stripe compatible because stripe doesn't provide this
         api_models::enums::EventType::ActionRequired => "action.required",
diff --git a/crates/router/src/connector/utils.rs b/crates/router/src/connector/utils.rs
index 525a9d678ae..02839ee8d78 100644
--- a/crates/router/src/connector/utils.rs
+++ b/crates/router/src/connector/utils.rs
@@ -2195,7 +2195,8 @@ impl FrmTransactionRouterDataRequest for fraud_check::FrmTransactionRouterData {
             | storage_enums::AttemptStatus::Voided
             | storage_enums::AttemptStatus::CaptureFailed
             | storage_enums::AttemptStatus::Failure
-            | storage_enums::AttemptStatus::AutoRefunded => Some(false),
+            | storage_enums::AttemptStatus::AutoRefunded
+            | storage_enums::AttemptStatus::Expired => Some(false),
 
             storage_enums::AttemptStatus::AuthenticationSuccessful
             | storage_enums::AttemptStatus::PartialChargedAndChargeable
@@ -2226,7 +2227,8 @@ pub fn is_payment_failure(status: enums::AttemptStatus) -> bool {
         | common_enums::AttemptStatus::AuthorizationFailed
         | common_enums::AttemptStatus::CaptureFailed
         | common_enums::AttemptStatus::VoidFailed
-        | common_enums::AttemptStatus::Failure => true,
+        | common_enums::AttemptStatus::Failure
+        | common_enums::AttemptStatus::Expired => true,
         common_enums::AttemptStatus::Started
         | common_enums::AttemptStatus::RouterDeclined
         | common_enums::AttemptStatus::AuthenticationPending
diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs
index fd8615ff8af..5651dae946d 100644
--- a/crates/router/src/core/payments.rs
+++ b/crates/router/src/core/payments.rs
@@ -3143,7 +3143,8 @@ impl ValidateStatusForOperation for &PaymentRedirectSync {
             | common_enums::IntentStatus::RequiresCapture
             | common_enums::IntentStatus::PartiallyCaptured
             | common_enums::IntentStatus::RequiresConfirmation
-            | common_enums::IntentStatus::PartiallyCapturedAndCapturable => {
+            | common_enums::IntentStatus::PartiallyCapturedAndCapturable
+            | common_enums::IntentStatus::Expired => {
                 Err(errors::ApiErrorResponse::PaymentUnexpectedState {
                     current_flow: format!("{self:?}"),
                     field_name: "status".to_string(),
diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs
index 39eeba8223a..9c9d1d69053 100644
--- a/crates/router/src/core/payments/helpers.rs
+++ b/crates/router/src/core/payments/helpers.rs
@@ -4276,7 +4276,8 @@ pub fn get_attempt_type(
                     | enums::AttemptStatus::AutoRefunded
                     | enums::AttemptStatus::PaymentMethodAwaited
                     | enums::AttemptStatus::DeviceDataCollectionPending
-                    | enums::AttemptStatus::IntegrityFailure => {
+                    | enums::AttemptStatus::IntegrityFailure
+                    | enums::AttemptStatus::Expired => {
                         metrics::MANUAL_RETRY_VALIDATION_FAILED.add(
                             1,
                             router_env::metric_attributes!((
@@ -4332,7 +4333,8 @@ pub fn get_attempt_type(
         | enums::IntentStatus::PartiallyCapturedAndCapturable
         | enums::IntentStatus::Processing
         | enums::IntentStatus::Succeeded
-        | enums::IntentStatus::Conflicted => {
+        | enums::IntentStatus::Conflicted
+        | enums::IntentStatus::Expired => {
             Err(report!(errors::ApiErrorResponse::PreconditionFailed {
                 message: format!(
                     "You cannot {action} this payment because it has status {}",
@@ -4579,18 +4581,19 @@ pub fn is_manual_retry_allowed(
             | enums::AttemptStatus::AutoRefunded
             | enums::AttemptStatus::PaymentMethodAwaited
             | enums::AttemptStatus::DeviceDataCollectionPending
-            | storage_enums::AttemptStatus::IntegrityFailure => {
+            | enums::AttemptStatus::IntegrityFailure
+            | enums::AttemptStatus::Expired => {
                 logger::error!("Payment Attempt should not be in this state because Attempt to Intent status mapping doesn't allow it");
                 None
             }
 
-            storage_enums::AttemptStatus::VoidFailed
-            | storage_enums::AttemptStatus::RouterDeclined
-            | storage_enums::AttemptStatus::CaptureFailed => Some(false),
+            enums::AttemptStatus::VoidFailed
+            | enums::AttemptStatus::RouterDeclined
+            | enums::AttemptStatus::CaptureFailed => Some(false),
 
-            storage_enums::AttemptStatus::AuthenticationFailed
-            | storage_enums::AttemptStatus::AuthorizationFailed
-            | storage_enums::AttemptStatus::Failure => Some(true),
+            enums::AttemptStatus::AuthenticationFailed
+            | enums::AttemptStatus::AuthorizationFailed
+            | enums::AttemptStatus::Failure => Some(true),
         },
         enums::IntentStatus::Cancelled
         | enums::IntentStatus::RequiresCapture
@@ -4598,7 +4601,8 @@ pub fn is_manual_retry_allowed(
         | enums::IntentStatus::PartiallyCapturedAndCapturable
         | enums::IntentStatus::Processing
         | enums::IntentStatus::Succeeded
-        | enums::IntentStatus::Conflicted => Some(false),
+        | enums::IntentStatus::Conflicted
+        | enums::IntentStatus::Expired => Some(false),
 
         enums::IntentStatus::RequiresCustomerAction
         | enums::IntentStatus::RequiresMerchantAction
diff --git a/crates/router/src/core/payments/operations/payment_attempt_record.rs b/crates/router/src/core/payments/operations/payment_attempt_record.rs
index b2834f0bfad..3c5259b6bf1 100644
--- a/crates/router/src/core/payments/operations/payment_attempt_record.rs
+++ b/crates/router/src/core/payments/operations/payment_attempt_record.rs
@@ -87,7 +87,8 @@ impl ValidateStatusForOperation for PaymentAttemptRecord {
             | common_enums::IntentStatus::RequiresCapture
             | common_enums::IntentStatus::PartiallyCaptured
             | common_enums::IntentStatus::RequiresConfirmation
-            | common_enums::IntentStatus::PartiallyCapturedAndCapturable => {
+            | common_enums::IntentStatus::PartiallyCapturedAndCapturable
+            | common_enums::IntentStatus::Expired => {
                 Err(errors::ApiErrorResponse::PaymentUnexpectedState {
                     current_flow: format!("{self:?}"),
                     field_name: "status".to_string(),
diff --git a/crates/router/src/core/payments/operations/payment_capture_v2.rs b/crates/router/src/core/payments/operations/payment_capture_v2.rs
index 1b46cb85d4a..13100e7762f 100644
--- a/crates/router/src/core/payments/operations/payment_capture_v2.rs
+++ b/crates/router/src/core/payments/operations/payment_capture_v2.rs
@@ -43,7 +43,8 @@ impl ValidateStatusForOperation for PaymentsCapture {
             | common_enums::IntentStatus::RequiresMerchantAction
             | common_enums::IntentStatus::RequiresPaymentMethod
             | common_enums::IntentStatus::PartiallyCaptured
-            | common_enums::IntentStatus::RequiresConfirmation => {
+            | common_enums::IntentStatus::RequiresConfirmation
+            | common_enums::IntentStatus::Expired => {
                 Err(errors::ApiErrorResponse::PaymentUnexpectedState {
                     current_flow: format!("{self:?}"),
                     field_name: "status".to_string(),
diff --git a/crates/router/src/core/payments/operations/payment_confirm_intent.rs b/crates/router/src/core/payments/operations/payment_confirm_intent.rs
index 143d6f7d342..a3b81afb4f8 100644
--- a/crates/router/src/core/payments/operations/payment_confirm_intent.rs
+++ b/crates/router/src/core/payments/operations/payment_confirm_intent.rs
@@ -53,7 +53,8 @@ impl ValidateStatusForOperation for PaymentIntentConfirm {
             | common_enums::IntentStatus::RequiresCapture
             | common_enums::IntentStatus::PartiallyCaptured
             | common_enums::IntentStatus::RequiresConfirmation
-            | common_enums::IntentStatus::PartiallyCapturedAndCapturable => {
+            | common_enums::IntentStatus::PartiallyCapturedAndCapturable
+            | common_enums::IntentStatus::Expired => {
                 Err(errors::ApiErrorResponse::PaymentUnexpectedState {
                     current_flow: format!("{self:?}"),
                     field_name: "status".to_string(),
diff --git a/crates/router/src/core/payments/operations/payment_get.rs b/crates/router/src/core/payments/operations/payment_get.rs
index 402c11e2f86..621a6e71a70 100644
--- a/crates/router/src/core/payments/operations/payment_get.rs
+++ b/crates/router/src/core/payments/operations/payment_get.rs
@@ -42,7 +42,8 @@ impl ValidateStatusForOperation for PaymentGet {
             | common_enums::IntentStatus::PartiallyCapturedAndCapturable
             | common_enums::IntentStatus::PartiallyCaptured
             | common_enums::IntentStatus::Cancelled
-            | common_enums::IntentStatus::Conflicted => Ok(()),
+            | common_enums::IntentStatus::Conflicted
+            | common_enums::IntentStatus::Expired => Ok(()),
             // These statuses are not valid for this operation
             common_enums::IntentStatus::RequiresConfirmation
             | common_enums::IntentStatus::RequiresPaymentMethod => {
diff --git a/crates/router/src/core/payments/operations/payment_response.rs b/crates/router/src/core/payments/operations/payment_response.rs
index 7fa8a4858f8..425173d4441 100644
--- a/crates/router/src/core/payments/operations/payment_response.rs
+++ b/crates/router/src/core/payments/operations/payment_response.rs
@@ -2565,7 +2565,8 @@ impl<F: Clone> PostUpdateTracker<F, PaymentConfirmData<F>, types::PaymentsAuthor
                 | common_enums::AttemptStatus::AutoRefunded
                 | common_enums::AttemptStatus::Unresolved
                 | common_enums::AttemptStatus::Pending
-                | common_enums::AttemptStatus::Failure => (),
+                | common_enums::AttemptStatus::Failure
+                | common_enums::AttemptStatus::Expired => (),
 
                 common_enums::AttemptStatus::Started
                 | common_enums::AttemptStatus::AuthenticationPending
diff --git a/crates/router/src/core/payments/operations/payment_session_intent.rs b/crates/router/src/core/payments/operations/payment_session_intent.rs
index 8ae5d2cd19d..d713c5175b2 100644
--- a/crates/router/src/core/payments/operations/payment_session_intent.rs
+++ b/crates/router/src/core/payments/operations/payment_session_intent.rs
@@ -41,7 +41,7 @@ impl ValidateStatusForOperation for PaymentSessionIntent {
             | common_enums::IntentStatus::RequiresConfirmation
             | common_enums::IntentStatus::PartiallyCapturedAndCapturable
             | common_enums::IntentStatus::Succeeded
-            | common_enums::IntentStatus::Failed | common_enums::IntentStatus::Conflicted => {
+            | common_enums::IntentStatus::Failed | common_enums::IntentStatus::Conflicted | common_enums::IntentStatus::Expired => {
                 Err(errors::ApiErrorResponse::PreconditionFailed {
                     message: format!(
                         "You cannot create session token for this payment because it has status {intent_status}. Expected status is requires_payment_method.",
diff --git a/crates/router/src/core/payments/operations/payment_update_intent.rs b/crates/router/src/core/payments/operations/payment_update_intent.rs
index 2c7b2ea4e8b..6e500e3eb0b 100644
--- a/crates/router/src/core/payments/operations/payment_update_intent.rs
+++ b/crates/router/src/core/payments/operations/payment_update_intent.rs
@@ -59,7 +59,8 @@ impl ValidateStatusForOperation for PaymentUpdateIntent {
             | common_enums::IntentStatus::RequiresCapture
             | common_enums::IntentStatus::PartiallyCaptured
             | common_enums::IntentStatus::RequiresConfirmation
-            | common_enums::IntentStatus::PartiallyCapturedAndCapturable => {
+            | common_enums::IntentStatus::PartiallyCapturedAndCapturable
+            | common_enums::IntentStatus::Expired => {
                 Err(errors::ApiErrorResponse::PaymentUnexpectedState {
                     current_flow: format!("{self:?}"),
                     field_name: "status".to_string(),
diff --git a/crates/router/src/core/payments/operations/proxy_payments_intent.rs b/crates/router/src/core/payments/operations/proxy_payments_intent.rs
index 30ea6913c6d..3994fd8ef05 100644
--- a/crates/router/src/core/payments/operations/proxy_payments_intent.rs
+++ b/crates/router/src/core/payments/operations/proxy_payments_intent.rs
@@ -54,7 +54,8 @@ impl ValidateStatusForOperation for PaymentProxyIntent {
             | common_enums::IntentStatus::RequiresCapture
             | common_enums::IntentStatus::PartiallyCaptured
             | common_enums::IntentStatus::RequiresConfirmation
-            | common_enums::IntentStatus::PartiallyCapturedAndCapturable => {
+            | common_enums::IntentStatus::PartiallyCapturedAndCapturable
+            | common_enums::IntentStatus::Expired => {
                 Err(errors::ApiErrorResponse::PaymentUnexpectedState {
                     current_flow: format!("{self:?}"),
                     field_name: "status".to_string(),
diff --git a/crates/router/src/core/payments/payment_methods.rs b/crates/router/src/core/payments/payment_methods.rs
index e21b3b96105..9bf9d3cc921 100644
--- a/crates/router/src/core/payments/payment_methods.rs
+++ b/crates/router/src/core/payments/payment_methods.rs
@@ -370,7 +370,8 @@ fn validate_payment_status_for_payment_method_list(
         | common_enums::IntentStatus::RequiresCapture
         | common_enums::IntentStatus::PartiallyCaptured
         | common_enums::IntentStatus::RequiresConfirmation
-        | common_enums::IntentStatus::PartiallyCapturedAndCapturable => {
+        | common_enums::IntentStatus::PartiallyCapturedAndCapturable
+        | common_enums::IntentStatus::Expired => {
             Err(errors::ApiErrorResponse::PaymentUnexpectedState {
                 current_flow: "list_payment_methods".to_string(),
                 field_name: "status".to_string(),
diff --git a/crates/router/src/core/payments/retry.rs b/crates/router/src/core/payments/retry.rs
index 8fe9c565e08..aafe25aea67 100644
--- a/crates/router/src/core/payments/retry.rs
+++ b/crates/router/src/core/payments/retry.rs
@@ -808,7 +808,8 @@ impl<F: Send + Clone + Sync, FData: Send + Sync>
                 | storage_enums::AttemptStatus::ConfirmationAwaited
                 | storage_enums::AttemptStatus::Unresolved
                 | storage_enums::AttemptStatus::DeviceDataCollectionPending
-                | storage_enums::AttemptStatus::IntegrityFailure => false,
+                | storage_enums::AttemptStatus::IntegrityFailure
+                | storage_enums::AttemptStatus::Expired => false,
 
                 storage_enums::AttemptStatus::AuthenticationFailed
                 | storage_enums::AttemptStatus::AuthorizationFailed
diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs
index 6f5689ed1cb..0cb84fc75cf 100644
--- a/crates/router/src/core/payments/transformers.rs
+++ b/crates/router/src/core/payments/transformers.rs
@@ -1943,17 +1943,21 @@ where
         let next_action_containing_wait_screen =
             wait_screen_next_steps_check(payment_attempt.clone())?;
 
-        let next_action = payment_attempt
-            .redirection_data
-            .as_ref()
-            .map(|_| api_models::payments::NextActionData::RedirectToUrl { redirect_to_url })
-            .or(next_action_containing_wait_screen.map(|wait_screen_data| {
-                api_models::payments::NextActionData::WaitScreenInformation {
-                    display_from_timestamp: wait_screen_data.display_from_timestamp,
-                    display_to_timestamp: wait_screen_data.display_to_timestamp,
-                    poll_config: wait_screen_data.poll_config,
-                }
-            }));
+        let next_action = if payment_intent.status.is_in_terminal_state() {
+            None
+        } else {
+            payment_attempt
+                .redirection_data
+                .as_ref()
+                .map(|_| api_models::payments::NextActionData::RedirectToUrl { redirect_to_url })
+                .or(next_action_containing_wait_screen.map(|wait_screen_data| {
+                    api_models::payments::NextActionData::WaitScreenInformation {
+                        display_from_timestamp: wait_screen_data.display_from_timestamp,
+                        display_to_timestamp: wait_screen_data.display_to_timestamp,
+                        poll_config: wait_screen_data.poll_config,
+                    }
+                }))
+        };
 
         let connector_token_details = payment_attempt
             .connector_token_details
@@ -2641,136 +2645,142 @@ where
     } else {
         let mut next_action_response = None;
 
-        let bank_transfer_next_steps = bank_transfer_next_steps_check(payment_attempt.clone())?;
+        // Early exit for terminal payment statuses - don't evaluate next_action at all
+        if payment_intent.status.is_in_terminal_state() {
+            next_action_response = None;
+        } else {
+            let bank_transfer_next_steps = bank_transfer_next_steps_check(payment_attempt.clone())?;
 
-        let next_action_voucher = voucher_next_steps_check(payment_attempt.clone())?;
+            let next_action_voucher = voucher_next_steps_check(payment_attempt.clone())?;
 
-        let next_action_mobile_payment = mobile_payment_next_steps_check(&payment_attempt)?;
+            let next_action_mobile_payment = mobile_payment_next_steps_check(&payment_attempt)?;
 
-        let next_action_containing_qr_code_url = qr_code_next_steps_check(payment_attempt.clone())?;
+            let next_action_containing_qr_code_url =
+                qr_code_next_steps_check(payment_attempt.clone())?;
 
-        let papal_sdk_next_action = paypal_sdk_next_steps_check(payment_attempt.clone())?;
+            let papal_sdk_next_action = paypal_sdk_next_steps_check(payment_attempt.clone())?;
 
-        let next_action_containing_fetch_qr_code_url =
-            fetch_qr_code_url_next_steps_check(payment_attempt.clone())?;
+            let next_action_containing_fetch_qr_code_url =
+                fetch_qr_code_url_next_steps_check(payment_attempt.clone())?;
 
-        let next_action_containing_wait_screen =
-            wait_screen_next_steps_check(payment_attempt.clone())?;
+            let next_action_containing_wait_screen =
+                wait_screen_next_steps_check(payment_attempt.clone())?;
 
-        let next_action_invoke_hidden_frame = next_action_invoke_hidden_frame(&payment_attempt)?;
+            let next_action_invoke_hidden_frame =
+                next_action_invoke_hidden_frame(&payment_attempt)?;
 
-        if payment_intent.status == enums::IntentStatus::RequiresCustomerAction
-            || bank_transfer_next_steps.is_some()
-            || next_action_voucher.is_some()
-            || next_action_containing_qr_code_url.is_some()
-            || next_action_containing_wait_screen.is_some()
-            || papal_sdk_next_action.is_some()
-            || next_action_containing_fetch_qr_code_url.is_some()
-            || payment_data.get_authentication().is_some()
-        {
-            next_action_response = bank_transfer_next_steps
-                        .map(|bank_transfer| {
-                            api_models::payments::NextActionData::DisplayBankTransferInformation {
-                                bank_transfer_steps_and_charges_details: bank_transfer,
-                            }
-                        })
-                        .or(next_action_voucher.map(|voucher_data| {
-                            api_models::payments::NextActionData::DisplayVoucherInformation {
-                                voucher_details: voucher_data,
-                            }
-                        }))
-                        .or(next_action_mobile_payment.map(|mobile_payment_data| {
-                            api_models::payments::NextActionData::CollectOtp {
-                                consent_data_required: mobile_payment_data.consent_data_required,
-                            }
-                        }))
-                        .or(next_action_containing_qr_code_url.map(|qr_code_data| {
-                            api_models::payments::NextActionData::foreign_from(qr_code_data)
-                        }))
-                        .or(next_action_containing_fetch_qr_code_url.map(|fetch_qr_code_data| {
-                            api_models::payments::NextActionData::FetchQrCodeInformation {
-                                qr_code_fetch_url: fetch_qr_code_data.qr_code_fetch_url
-                            }
-                        }))
-                        .or(papal_sdk_next_action.map(|paypal_next_action_data| {
-                            api_models::payments::NextActionData::InvokeSdkClient{
-                                next_action_data: paypal_next_action_data
-                            }
-                        }))
-                        .or(next_action_containing_wait_screen.map(|wait_screen_data| {
-                            api_models::payments::NextActionData::WaitScreenInformation {
-                                display_from_timestamp: wait_screen_data.display_from_timestamp,
-                                display_to_timestamp: wait_screen_data.display_to_timestamp,
-                                poll_config: wait_screen_data.poll_config,
-                            }
-                        }))
-                        .or(payment_attempt.authentication_data.as_ref().map(|_| {
-                            // Check if iframe redirection is enabled in the business profile
-                            let redirect_url = helpers::create_startpay_url(
-                                base_url,
-                                &payment_attempt,
-                                &payment_intent,
-                            );
-                            // Check if redirection inside popup is enabled in the payment intent
-                            if payment_intent.is_iframe_redirection_enabled.unwrap_or(false) {
-                                api_models::payments::NextActionData::RedirectInsidePopup {
-                                    popup_url: redirect_url,
-                                    redirect_response_url:router_return_url
+            if payment_intent.status == enums::IntentStatus::RequiresCustomerAction
+                || bank_transfer_next_steps.is_some()
+                || next_action_voucher.is_some()
+                || next_action_containing_qr_code_url.is_some()
+                || next_action_containing_wait_screen.is_some()
+                || papal_sdk_next_action.is_some()
+                || next_action_containing_fetch_qr_code_url.is_some()
+                || payment_data.get_authentication().is_some()
+            {
+                next_action_response = bank_transfer_next_steps
+                            .map(|bank_transfer| {
+                                api_models::payments::NextActionData::DisplayBankTransferInformation {
+                                    bank_transfer_steps_and_charges_details: bank_transfer,
                                 }
-                            } else {
-                                api_models::payments::NextActionData::RedirectToUrl {
-                                    redirect_to_url: redirect_url,
+                            })
+                            .or(next_action_voucher.map(|voucher_data| {
+                                api_models::payments::NextActionData::DisplayVoucherInformation {
+                                    voucher_details: voucher_data,
                                 }
-                            }
-                        }))
-                        .or(match payment_data.get_authentication(){
-                            Some(authentication_store) => {
-                                let authentication = &authentication_store.authentication;
-                                if payment_intent.status == common_enums::IntentStatus::RequiresCustomerAction && authentication_store.cavv.is_none() && authentication.is_separate_authn_required(){
-                                    // if preAuthn and separate authentication needed.
-                                    let poll_config = payment_data.get_poll_config().unwrap_or_default();
-                                    let request_poll_id = core_utils::get_external_authentication_request_poll_id(&payment_intent.payment_id);
-                                    let payment_connector_name = payment_attempt.connector
-                                        .as_ref()
-                                        .get_required_value("connector")?;
-                                    Some(api_models::payments::NextActionData::ThreeDsInvoke {
-                                        three_ds_data: api_models::payments::ThreeDsData {
-                                            three_ds_authentication_url: helpers::create_authentication_url(base_url, &payment_attempt),
-                                            three_ds_authorize_url: helpers::create_authorize_url(
-                                                base_url,
-                                                &payment_attempt,
-                                                payment_connector_name,
-                                            ),
-                                            three_ds_method_details: authentication.three_ds_method_url.as_ref().zip(authentication.three_ds_method_data.as_ref()).map(|(three_ds_method_url,three_ds_method_data )|{
-                                                api_models::payments::ThreeDsMethodData::AcsThreeDsMethodData {
-                                                    three_ds_method_data_submission: true,
-                                                    three_ds_method_data: Some(three_ds_method_data.clone()),
-                                                    three_ds_method_url: Some(three_ds_method_url.to_owned()),
-                                                }
-                                            }).unwrap_or(api_models::payments::ThreeDsMethodData::AcsThreeDsMethodData {
-                                                    three_ds_method_data_submission: false,
-                                                    three_ds_method_data: None,
-                                                    three_ds_method_url: None,
-                                            }),
-                                            poll_config: api_models::payments::PollConfigResponse {poll_id: request_poll_id, delay_in_secs: poll_config.delay_in_secs, frequency: poll_config.frequency},
-                                            message_version: authentication.message_version.as_ref()
-                                            .map(|version| version.to_string()),
-                                            directory_server_id: authentication.directory_server_id.clone(),
-                                        },
-                                    })
-                                }else{
-                                    None
+                            }))
+                            .or(next_action_mobile_payment.map(|mobile_payment_data| {
+                                api_models::payments::NextActionData::CollectOtp {
+                                    consent_data_required: mobile_payment_data.consent_data_required,
                                 }
-                            },
-                            None => None
-                        })
-                        .or(match next_action_invoke_hidden_frame{
-                            Some(threeds_invoke_data) => Some(construct_connector_invoke_hidden_frame(
-                                threeds_invoke_data,
-                            )?),
-                            None => None
-
-        });
+                            }))
+                            .or(next_action_containing_qr_code_url.map(|qr_code_data| {
+                                api_models::payments::NextActionData::foreign_from(qr_code_data)
+                            }))
+                            .or(next_action_containing_fetch_qr_code_url.map(|fetch_qr_code_data| {
+                                api_models::payments::NextActionData::FetchQrCodeInformation {
+                                    qr_code_fetch_url: fetch_qr_code_data.qr_code_fetch_url
+                                }
+                            }))
+                            .or(papal_sdk_next_action.map(|paypal_next_action_data| {
+                                api_models::payments::NextActionData::InvokeSdkClient{
+                                    next_action_data: paypal_next_action_data
+                                }
+                            }))
+                            .or(next_action_containing_wait_screen.map(|wait_screen_data| {
+                                api_models::payments::NextActionData::WaitScreenInformation {
+                                    display_from_timestamp: wait_screen_data.display_from_timestamp,
+                                    display_to_timestamp: wait_screen_data.display_to_timestamp,
+                                    poll_config: wait_screen_data.poll_config,
+                                }
+                            }))
+                            .or(payment_attempt.authentication_data.as_ref().map(|_| {
+                                // Check if iframe redirection is enabled in the business profile
+                                let redirect_url = helpers::create_startpay_url(
+                                    base_url,
+                                    &payment_attempt,
+                                    &payment_intent,
+                                );
+                                // Check if redirection inside popup is enabled in the payment intent
+                                if payment_intent.is_iframe_redirection_enabled.unwrap_or(false) {
+                                    api_models::payments::NextActionData::RedirectInsidePopup {
+                                        popup_url: redirect_url,
+                                        redirect_response_url:router_return_url
+                                    }
+                                } else {
+                                    api_models::payments::NextActionData::RedirectToUrl {
+                                        redirect_to_url: redirect_url,
+                                    }
+                                }
+                            }))
+                            .or(match payment_data.get_authentication(){
+                                Some(authentication_store) => {
+                                    let authentication = &authentication_store.authentication;
+                                    if payment_intent.status == common_enums::IntentStatus::RequiresCustomerAction && authentication_store.cavv.is_none() && authentication.is_separate_authn_required(){
+                                        // if preAuthn and separate authentication needed.
+                                        let poll_config = payment_data.get_poll_config().unwrap_or_default();
+                                        let request_poll_id = core_utils::get_external_authentication_request_poll_id(&payment_intent.payment_id);
+                                        let payment_connector_name = payment_attempt.connector
+                                            .as_ref()
+                                            .get_required_value("connector")?;
+                                        Some(api_models::payments::NextActionData::ThreeDsInvoke {
+                                            three_ds_data: api_models::payments::ThreeDsData {
+                                                three_ds_authentication_url: helpers::create_authentication_url(base_url, &payment_attempt),
+                                                three_ds_authorize_url: helpers::create_authorize_url(
+                                                    base_url,
+                                                    &payment_attempt,
+                                                    payment_connector_name,
+                                                ),
+                                                three_ds_method_details: authentication.three_ds_method_url.as_ref().zip(authentication.three_ds_method_data.as_ref()).map(|(three_ds_method_url,three_ds_method_data )|{
+                                                    api_models::payments::ThreeDsMethodData::AcsThreeDsMethodData {
+                                                        three_ds_method_data_submission: true,
+                                                        three_ds_method_data: Some(three_ds_method_data.clone()),
+                                                        three_ds_method_url: Some(three_ds_method_url.to_owned()),
+                                                    }
+                                                }).unwrap_or(api_models::payments::ThreeDsMethodData::AcsThreeDsMethodData {
+                                                        three_ds_method_data_submission: false,
+                                                        three_ds_method_data: None,
+                                                        three_ds_method_url: None,
+                                                }),
+                                                poll_config: api_models::payments::PollConfigResponse {poll_id: request_poll_id, delay_in_secs: poll_config.delay_in_secs, frequency: poll_config.frequency},
+                                                message_version: authentication.message_version.as_ref()
+                                                .map(|version| version.to_string()),
+                                                directory_server_id: authentication.directory_server_id.clone(),
+                                            },
+                                        })
+                                    }else{
+                                        None
+                                    }
+                                },
+                                None => None
+                            })
+                            .or(match next_action_invoke_hidden_frame{
+                                Some(threeds_invoke_data) => Some(construct_connector_invoke_hidden_frame(
+                                    threeds_invoke_data,
+                                )?),
+                                None => None
+                            });
+            }
         };
 
         // next action check for third party sdk session (for ex: Apple pay through trustpay has third party sdk session response)
diff --git a/crates/router/src/core/revenue_recovery/transformers.rs b/crates/router/src/core/revenue_recovery/transformers.rs
index a4e40c69b72..76f987673da 100644
--- a/crates/router/src/core/revenue_recovery/transformers.rs
+++ b/crates/router/src/core/revenue_recovery/transformers.rs
@@ -35,7 +35,8 @@ impl ForeignFrom<AttemptStatus> for RevenueRecoveryPaymentsAttemptStatus {
             | AttemptStatus::AuthenticationPending
             | AttemptStatus::DeviceDataCollectionPending
             | AttemptStatus::Unresolved
-            | AttemptStatus::IntegrityFailure => Self::InvalidStatus(s.to_string()),
+            | AttemptStatus::IntegrityFailure
+            | AttemptStatus::Expired => Self::InvalidStatus(s.to_string()),
         }
     }
 }
diff --git a/crates/router/src/core/routing/helpers.rs b/crates/router/src/core/routing/helpers.rs
index 32a7b560566..5d5a16912e2 100644
--- a/crates/router/src/core/routing/helpers.rs
+++ b/crates/router/src/core/routing/helpers.rs
@@ -1714,9 +1714,8 @@ fn get_desired_payment_status_for_dynamic_routing_metrics(
         | common_enums::AttemptStatus::IntegrityFailure
         | common_enums::AttemptStatus::PaymentMethodAwaited
         | common_enums::AttemptStatus::ConfirmationAwaited
-        | common_enums::AttemptStatus::DeviceDataCollectionPending => {
-            common_enums::AttemptStatus::Pending
-        }
+        | common_enums::AttemptStatus::DeviceDataCollectionPending
+        | common_enums::AttemptStatus::Expired => common_enums::AttemptStatus::Pending,
     }
 }
 
@@ -1734,7 +1733,9 @@ impl ForeignFrom<common_enums::AttemptStatus> for open_router::TxnStatus {
             common_enums::AttemptStatus::Charged => Self::Charged,
             common_enums::AttemptStatus::Authorizing => Self::Authorizing,
             common_enums::AttemptStatus::CodInitiated => Self::CODInitiated,
-            common_enums::AttemptStatus::Voided => Self::Voided,
+            common_enums::AttemptStatus::Voided | common_enums::AttemptStatus::Expired => {
+                Self::Voided
+            }
             common_enums::AttemptStatus::VoidInitiated => Self::VoidInitiated,
             common_enums::AttemptStatus::CaptureInitiated => Self::CaptureInitiated,
             common_enums::AttemptStatus::CaptureFailed => Self::CaptureFailed,
diff --git a/crates/router/src/types.rs b/crates/router/src/types.rs
index 717140cc0ed..4887a80f31a 100644
--- a/crates/router/src/types.rs
+++ b/crates/router/src/types.rs
@@ -296,12 +296,15 @@ impl Capturable for PaymentsAuthorizeData {
     {
         match payment_data.get_capture_method().unwrap_or_default()
         {
-            common_enums::CaptureMethod::Automatic|common_enums::CaptureMethod::SequentialAutomatic  => {
+            common_enums::CaptureMethod::Automatic
+            | common_enums::CaptureMethod::SequentialAutomatic => {
                 let intent_status = common_enums::IntentStatus::foreign_from(attempt_status);
                 match intent_status {
                     common_enums::IntentStatus::Succeeded
                     | common_enums::IntentStatus::Failed
-                    | common_enums::IntentStatus::Processing | common_enums::IntentStatus::Conflicted => Some(0),
+                    | common_enums::IntentStatus::Processing
+                    | common_enums::IntentStatus::Conflicted
+                    | common_enums::IntentStatus::Expired => Some(0),
                     common_enums::IntentStatus::Cancelled
                     | common_enums::IntentStatus::PartiallyCaptured
                     | common_enums::IntentStatus::RequiresCustomerAction
@@ -341,7 +344,8 @@ impl Capturable for PaymentsCaptureData {
         match intent_status {
             common_enums::IntentStatus::Succeeded
             | common_enums::IntentStatus::PartiallyCaptured
-            | common_enums::IntentStatus::Conflicted => Some(0),
+            | common_enums::IntentStatus::Conflicted
+            | common_enums::IntentStatus::Expired => Some(0),
             common_enums::IntentStatus::Processing
             | common_enums::IntentStatus::Cancelled
             | common_enums::IntentStatus::Failed
@@ -383,9 +387,11 @@ impl Capturable for CompleteAuthorizeData {
             common_enums::CaptureMethod::Automatic | common_enums::CaptureMethod::SequentialAutomatic => {
                 let intent_status = common_enums::IntentStatus::foreign_from(attempt_status);
                 match intent_status {
-                    common_enums::IntentStatus::Succeeded|
-                    common_enums::IntentStatus::Failed|
-                    common_enums::IntentStatus::Processing | common_enums::IntentStatus::Conflicted => Some(0),
+                    common_enums::IntentStatus::Succeeded
+                    | common_enums::IntentStatus::Failed
+                    | common_enums::IntentStatus::Processing
+                    | common_enums::IntentStatus::Conflicted
+                    | common_enums::IntentStatus::Expired => Some(0),
                     common_enums::IntentStatus::Cancelled | common_enums::IntentStatus::PartiallyCaptured
                     | common_enums::IntentStatus::RequiresCustomerAction
                     | common_enums::IntentStatus::RequiresMerchantAction
@@ -433,7 +439,8 @@ impl Capturable for PaymentsCancelData {
             common_enums::IntentStatus::Cancelled
             | common_enums::IntentStatus::Processing
             | common_enums::IntentStatus::PartiallyCaptured
-            | common_enums::IntentStatus::Conflicted => Some(0),
+            | common_enums::IntentStatus::Conflicted
+            | common_enums::IntentStatus::Expired => Some(0),
             common_enums::IntentStatus::Succeeded
             | common_enums::IntentStatus::Failed
             | common_enums::IntentStatus::RequiresCustomerAction
diff --git a/crates/router/src/types/transformers.rs b/crates/router/src/types/transformers.rs
index 5cd1c3d3bd1..daf630e6894 100644
--- a/crates/router/src/types/transformers.rs
+++ b/crates/router/src/types/transformers.rs
@@ -168,7 +168,7 @@ impl ForeignTryFrom<storage_enums::AttemptStatus> for storage_enums::CaptureStat
             | storage_enums::AttemptStatus::PaymentMethodAwaited
             | storage_enums::AttemptStatus::ConfirmationAwaited
             | storage_enums::AttemptStatus::DeviceDataCollectionPending
-            | storage_enums::AttemptStatus::PartialChargedAndChargeable => {
+            | storage_enums::AttemptStatus::PartialChargedAndChargeable | storage_enums::AttemptStatus::Expired => {
                 Err(errors::ApiErrorResponse::PreconditionFailed {
                     message: "AttemptStatus must be one of these for multiple partial captures [Charged, PartialCharged, Pending, CaptureInitiated, Failure, CaptureFailed]".into(),
                 }.into())
diff --git a/migrations/2025-07-24-214849_add_expired_to_intent_and_attempt_status_and_event/down.sql b/migrations/2025-07-24-214849_add_expired_to_intent_and_attempt_status_and_event/down.sql
new file mode 100644
index 00000000000..e0ac49d1ecf
--- /dev/null
+++ b/migrations/2025-07-24-214849_add_expired_to_intent_and_attempt_status_and_event/down.sql
@@ -0,0 +1 @@
+SELECT 1;
diff --git a/migrations/2025-07-24-214849_add_expired_to_intent_and_attempt_status_and_event/up.sql b/migrations/2025-07-24-214849_add_expired_to_intent_and_attempt_status_and_event/up.sql
new file mode 100644
index 00000000000..ae322e833e4
--- /dev/null
+++ b/migrations/2025-07-24-214849_add_expired_to_intent_and_attempt_status_and_event/up.sql
@@ -0,0 +1,5 @@
+ALTER TYPE "IntentStatus" ADD VALUE IF NOT EXISTS 'expired';
+
+ALTER TYPE "AttemptStatus" ADD VALUE IF NOT EXISTS 'expired';
+
+ALTER TYPE "EventType" ADD VALUE IF NOT EXISTS 'payment_expired';
\ No newline at end of file

## Type of Change
- [x] New feature

## Description

This PR implements payment expiration handling for QR code-based payments through Adyen's OFFER_CLOSED webhook and fixes next_action display for terminal payment states.

### Changes:
1. Adyen `OFFER_CLOSED` webhook:
   - Added `OfferClosed` to `WebhookEventCode` enum
   - Handle `OfferClosed` events as `Expired` status in AttemptStatus
   - Maps `OFFER_CLOSED` events to `PaymentIntentExpired` webhook

2. Added `Expired` status support:
   - Added to both `AttemptStatus` and `IntentStatus` enums
   - Database migration for new enum values
   - Add `PaymentExpired` event type for outgoing webhooks

3. Fixed next_action for terminal payments
   - Prevents showing QR codes/redirects data for terminal payments

## Motivation and Context

QR code payments (like PIX) have expiration times. When they expire, Adyen sends OFFER_CLOSED webhooks, but Hyperswitch wasn't handling these properly. Additionally, cancelled payments were still showing actionable `next_action` content.

## How did you test it?

<details>
    <summary>1. Create a Pix Payment (with expiry)</summary>

cURL

    curl --location --request POST 'http://localhost:8080/payments' \
        --header 'Content-Type: application/json' \
        --header 'Accept: application/json' \
        --header 'api-key: dev_W8rGDzCct9xjU2Rqd8xxZubP9rRJ4u1Y42J1im7qplUxjOgWmJS4ZPq9lRfvOCnd' \
        --data-raw '{"amount":4500,"currency":"BRL","confirm":true,"profile_id":"pro_HvmsPUKxoCtI58mtCUhg","capture_on":"2022-09-10T10:11:12Z","connector":["adyen"],"customer_id":"cus_HXi1vEcMXQ74qsaNq57p","email":"abc@example.com","return_url":"https://google.com","payment_method":"bank_transfer","payment_method_type":"pix","payment_method_data":{"bank_transfer":{"pix":{"pix_key":"test","cpf":"test","cnpj":"test","expiry_date":"2025-07-22T14:58:00Z"}}},"billing":{"address":{"line1":"1467","line2":"Harrison Street","line3":"Harrison Street","city":"San Fransico","state":"California","zip":"94122","country":"BR","first_name":"joseph","last_name":"Doe"},"phone":{"number":"8056594427","country_code":"+91"},"email":"guest@example.com"},"metadata":{"Force-PSP":"Adyen","udf1":"value1","new_customer":"true","login_date":"2019-09-10T10:11:12Z"},"browser_info":{"user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36","accept_header":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8","language":"en-US","color_depth":32,"screen_height":1117,"screen_width":1728,"time_zone":-330,"java_enabled":true,"java_script_enabled":true,"ip_address":"127.0.0.1"},"customer_acceptance":{"acceptance_type":"online","accepted_at":"1963-05-03T04:07:52.723Z","online":{"ip_address":"127.0.0.1","user_agent":"amet irure esse"}},"session_expiry":60}'

Response

    {"payment_id":"pay_8P2b5MuSkplj8d3U06Nj","merchant_id":"merchant_1753193103","status":"requires_customer_action","amount":4500,"net_amount":4500,"shipping_cost":null,"amount_capturable":4500,"amount_received":null,"connector":"adyen","client_secret":"pay_8P2b5MuSkplj8d3U06Nj_secret_px8ZGtda34okt5ngtFdb","created":"2025-07-22T14:57:52.108Z","currency":"BRL","customer_id":"cus_HXi1vEcMXQ74qsaNq57p","customer":{"id":"cus_HXi1vEcMXQ74qsaNq57p","name":"John Nether","email":"abc@example.com","phone":"6168205362","phone_country_code":"+1"},"description":null,"refunds":null,"disputes":null,"mandate_id":null,"mandate_data":null,"setup_future_usage":null,"off_session":null,"capture_on":null,"capture_method":null,"payment_method":"bank_transfer","payment_method_data":{"bank_transfer":{"pix":{"pix_key":"test","cpf":"test","cnpj":"test","source_bank_account_id":null,"destination_bank_account_id":null,"expiry_date":"2025-07-22T14:58:00.000Z"}},"billing":null},"payment_token":null,"shipping":null,"billing":{"address":{"city":"San Fransico","country":"BR","line1":"1467","line2":"Harrison Street","line3":"Harrison Street","zip":"94122","state":"California","first_name":"joseph","last_name":"Doe"},"phone":{"number":"8056594427","country_code":"+91"},"email":"guest@example.com"},"order_details":null,"email":"abc@example.com","name":"John Nether","phone":"6168205362","return_url":"https://google.com/","authentication_type":"no_three_ds","statement_descriptor_name":null,"statement_descriptor_suffix":null,"next_action":{"type":"qr_code_information","image_data_url":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQgAAAEICAAAAACGnTUjAAAGJElEQVR4Ae3gAZAkSZIkSRKLqpm7R0REZmZmVlVVVVV3d3d3d/fMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMdHd3d3dXV1VVVVVmZkZGRIS7m5kKz0xmV3d1d3dPz8zMzMxMYjVXAVSuAqByFQCVqwCoXAVA5SoAKlcBULkKgMpVAFSuAqByFQCVqwCoXAVA5SoAKlcBULkKgMpVAFSuAqByFQCVqwCoXAVA5SoAKlcBULkKgMpVAFSuAqByFQCVqwCoXAVA5SoAKlcBULkKgMq/QPzbmCvEFeY5iedkrhDPn7lC/NuYF4rKVQBUrgKgchUAlasAqFwFQOUqACpXAVB5EZkXjXhO5gpxhXnhzHMSz5950YgXCZWrAKhcBUDlKgAqVwFQuQqAylUAVK4CoPKvJJ4/88KZK8QV5grx/IkrzAsnnj/zr0LlKgAqVwFQuQqAylUAVK4CoHIVAJWrAKj8JxPPn7jCvHDiCvOfispVAFSuAqByFQCVqwCoXAVA5SoAKlcBUPkvZq4Qz5/4b0HlKgAqVwFQuQqAylUAVK4CoHIVAJWrAKj8K5l/H/GcxBXmCvOcxAtn/kNQuQqAylUAVK4CoHIVAJWrAKhcBUDlKgAqLyLx72OuEFeYK8RzEleYF078h6JyFQCVqwCoXAVA5SoAKlcBULkKgMpVAFT+Bea/hrjCvHDmPwWVqwCoXAVA5SoAKlcBULkKgMpVAFSuAqDyLxBXmOdPXGGuEM+fuMK8aMQV5grxojHPSVxhXigqVwFQuQqAylUAVK4CoHIVAJWrAKhcBYDMv494TuYK8fyZK8S/jnn+xBXmOYkrzIuEylUAVK4CoHIVAJWrAKhcBUDlKgAqVwFQ+VcSz5+5QlxhrhAvGvOcxBXmhTPPn/lXoXIVAJWrAKhcBUDlKgAqVwFQuQqAylUAVP4F4oUzV4grzPNnXjTiCvPCiSvMcxJXmH8VKlcBULkKgMpVAFSuAqByFQCVqwCoXAWAzAsnrjBXiBeNeU7iOZnnJJ4/c4V4/swV4oUzLxSVqwCoXAVA5SoAKlcBULkKgMpVAFSuAqDyIhJXmOdPXGGuEFeYF05cYZ6TuEJcYa4QV5grxHMy/yZUrgKgchUAlasAqFwFQOUqACpXAVC5CgCZfxtxhXlO4grznMQV5grxr2Oek3jhzL8KlasAqFwFQOUqACpXAVC5CoDKVQBUrgKg8m9krhDPyTwncYV5/swV4gpzhbjCvGjMFeIKcYV5kVC5CoDKVQBUrgKgchUAlasAqFwFQOUqAGReOPH8medPPCfznMQV5grxnMxzEv865grxnMwLReUqACpXAVC5CoDKVQBUrgKgchUAlasAkPnPJa4wL5x44czzJ64wz0k8J/NCUbkKgMpVAFSuAqByFQCVqwCoXAVA5SoAKv8C8W9j/nXMcxLPn7jCPH/iCvOvQuUqACpXAVC5CoDKVQBUrgKgchUAlasAqLyIzItGPCdzhbjCPCdxhblCXGGuEM/JvGjEczIvFJWrAKhcBUDlKgAqVwFQuQqAylUAVK4CoPKvJJ4/8/yJK8wV4jmZK8QLJ144cYV5TuJFQuUqACpXAVC5CoDKVQBUrgKgchUAlasAqPwnM1eI52SuEFeYfxvz/Il/FSpXAVC5CoDKVQBUrgKgchUAlasAqFwFQOW/iXhO4jmZ589cIZ6TuMI8J/MioXIVAJWrAKhcBUDlKgAqVwFQuQqAylUAVP6VzL+NeU7iCnOFuMI8f+YK8ZzEFeYK8W9C5SoAKlcBULkKgMpVAFSuAqByFQCVqwCovIjEv424wlwhrjBXiOdPXGGuEC8ac4W4QlxhXigqVwFQuQqAylUAVK4CoHIVAJWrAKhcBYDMVQCVqwCoXAVA5SoAKlcBULkKgMpVAFSuAqByFQCVqwCoXAVA5SoAKlcBULkKgMpVAFSuAqByFQCVqwCoXAVA5SoAKlcBULkKgMpVAFSuAqByFQCVqwCoXAVA5SoAKlcBULkKgMpVAFSuAuAfAfHRhAhvVmeEAAAAAElFTkSuQmCC","display_to_timestamp":1753196280000,"qr_code_url":"https://test.adyen.com/hpp/generateQRCodeImage.shtml?url=TestQRCodeEMVToken","display_text":null,"border_color":null},"cancellation_reason":null,"error_code":null,"error_message":null,"unified_code":null,"unified_message":null,"payment_experience":null,"payment_method_type":"pix","connector_label":null,"business_country":null,"business_label":"default","business_sub_label":null,"allowed_payment_method_types":null,"ephemeral_key":{"customer_id":"cus_HXi1vEcMXQ74qsaNq57p","created_at":1753196272,"expires":1753199872,"secret":"epk_7b539192a86b4d3baf83c0dcc93da9a6"},"manual_retry_allowed":null,"connector_transaction_id":"BGC4GRDTH5V3NDV5","frm_message":null,"metadata":{"udf1":"value1","Force-PSP":"Adyen","login_date":"2019-09-10T10:11:12Z","new_customer":"true"},"connector_metadata":null,"feature_metadata":null,"reference_id":"BGC4GRDTH5V3NDV5","payment_link":null,"profile_id":"pro_HvmsPUKxoCtI58mtCUhg","surcharge_details":null,"attempt_count":1,"merchant_decision":null,"merchant_connector_id":"mca_c5mj7ZUwHtsNKo5EfexQ","incremental_authorization_allowed":null,"authorization_count":null,"incremental_authorizations":null,"external_authentication_details":null,"external_3ds_authentication_attempted":false,"expires_on":"2025-07-22T14:58:52.108Z","fingerprint":null,"browser_info":{"language":"en-US","time_zone":-330,"ip_address":"127.0.0.1","user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36","color_depth":32,"java_enabled":true,"screen_width":1728,"accept_header":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8","screen_height":1117,"java_script_enabled":true},"payment_method_id":null,"payment_method_status":null,"updated":"2025-07-22T14:57:53.084Z","split_payments":null,"frm_metadata":null,"extended_authorization_applied":null,"capture_before":null,"merchant_order_reference_id":null,"order_tax_amount":null,"connector_mandate_id":null,"card_discovery":null,"force_3ds_challenge":false,"force_3ds_challenge_trigger":false,"issuer_error_code":null,"issuer_error_message":null,"is_iframe_redirection_enabled":null,"whole_connector_response":null}

Wait for it to expire
Receive incoming webhooks for OFFER_CLOSED

</details>

<details>
    <summary>2. Verify outgoing webhook</summary>

    {"merchant_id":"merchant_1753397156","event_id":"evt_019841219a907f8084065fb9d9e2842e","event_type":"payment_expired","content":{"type":"payment_details","object":{"payment_id":"pay_GE6MyVzON0EXIcyOFh5p","merchant_id":"merchant_1753397156","status":"expired","amount":4500,"net_amount":4500,"shipping_cost":null,"amount_capturable":0,"amount_received":null,"connector":"adyen","client_secret":"pay_GE6MyVzON0EXIcyOFh5p_secret_44s26p5R8wAp9cXOQ3Qt","created":"2025-07-25T10:26:23.600Z","currency":"BRL","customer_id":"cus_J2NWO1RbnwNdiEO6KdIt","customer":{"id":"cus_J2NWO1RbnwNdiEO6KdIt","name":null,"email":"abc@example.com","phone":null,"phone_country_code":null},"description":null,"refunds":null,"disputes":null,"mandate_id":null,"mandate_data":null,"setup_future_usage":null,"off_session":null,"capture_on":null,"capture_method":null,"payment_method":"bank_transfer","payment_method_data":{"bank_transfer":{"pix":{"pix_key":"test","cpf":"test","cnpj":"test","source_bank_account_id":null,"destination_bank_account_id":null,"expiry_date":"2025-07-25T10:28:00.000Z"}},"billing":null},"payment_token":null,"shipping":null,"billing":{"address":{"city":"San Fransico","country":"BR","line1":"1467","line2":"Harrison Street","line3":"Harrison Street","zip":"94122","state":"California","first_name":"joseph","last_name":"Doe"},"phone":{"number":"8056594427","country_code":"+91"},"email":"guest@example.com"},"order_details":null,"email":"abc@example.com","name":null,"phone":null,"return_url":"https://google.com/","authentication_type":"no_three_ds","statement_descriptor_name":null,"statement_descriptor_suffix":null,"next_action":null,"cancellation_reason":null,"error_code":null,"error_message":null,"unified_code":null,"unified_message":null,"payment_experience":null,"payment_method_type":"pix","connector_label":null,"business_country":null,"business_label":"default","business_sub_label":null,"allowed_payment_method_types":null,"ephemeral_key":null,"manual_retry_allowed":false,"connector_transaction_id":"BXSX5X9VLW5PB2V5","frm_message":null,"metadata":{"udf1":"value1","Force-PSP":"Adyen","login_date":"2019-09-10T10:11:12Z","new_customer":"true"},"connector_metadata":null,"feature_metadata":null,"reference_id":"pay_GE6MyVzON0EXIcyOFh5p_1","payment_link":null,"profile_id":"pro_zJpGj6xSodQqoBYltbzl","surcharge_details":null,"attempt_count":1,"merchant_decision":null,"merchant_connector_id":"mca_PTFqfuKnofhHIFDFLBsu","incremental_authorization_allowed":null,"authorization_count":null,"incremental_authorizations":null,"external_authentication_details":null,"external_3ds_authentication_attempted":false,"expires_on":"2025-07-25T10:27:23.600Z","fingerprint":null,"browser_info":{"language":"en-US","time_zone":-330,"ip_address":"127.0.0.1","user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36","color_depth":32,"java_enabled":true,"screen_width":1728,"accept_header":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8","screen_height":1117,"java_script_enabled":true},"payment_method_id":"pm_uHViByM1aRfvzf4cCKYH","payment_method_status":"inactive","updated":"2025-07-25T10:29:38.062Z","split_payments":null,"frm_metadata":null,"extended_authorization_applied":null,"capture_before":null,"merchant_order_reference_id":null,"order_tax_amount":null,"connector_mandate_id":null,"card_discovery":null,"force_3ds_challenge":false,"force_3ds_challenge_trigger":false,"issuer_error_code":null,"issuer_error_message":null,"is_iframe_redirection_enabled":null,"whole_connector_response":null}},"timestamp":"2025-07-25T10:29:38.064Z"}

It should have
- event_type as payment_expired
- status as expired
- `next_action` should be null

</details>

<details>
    <summary>3. Verify payments retrieve</summary>

cURL

    curl --location --request GET 'http://localhost:8080/payments/pay_8P2b5MuSkplj8d3U06Nj?force_sync=true' \
        --header 'Accept: application/json' \
        --header 'api-key: dev_W8rGDzCct9xjU2Rqd8xxZubP9rRJ4u1Y42J1im7qplUxjOgWmJS4ZPq9lRfvOCnd'

Response

    {"payment_id":"pay_cqxjoA0Drk0NBgmCpbQf","merchant_id":"merchant_1753397156","status":"expired","amount":4500,"net_amount":4500,"shipping_cost":null,"amount_capturable":0,"amount_received":null,"connector":"adyen","client_secret":"pay_cqxjoA0Drk0NBgmCpbQf_secret_S3VytS4Ym1KOhpKRQvtV","created":"2025-07-24T22:51:17.081Z","currency":"BRL","customer_id":"cus_J2NWO1RbnwNdiEO6KdIt","customer":{"id":"cus_J2NWO1RbnwNdiEO6KdIt","name":null,"email":"abc@example.com","phone":null,"phone_country_code":null},"description":null,"refunds":null,"disputes":null,"mandate_id":null,"mandate_data":null,"setup_future_usage":null,"off_session":null,"capture_on":null,"capture_method":null,"payment_method":"bank_transfer","payment_method_data":{"bank_transfer":{"pix":{"pix_key":"test","cpf":"test","cnpj":"test","source_bank_account_id":null,"destination_bank_account_id":null,"expiry_date":"2025-07-24T22:54:00.000Z"}},"billing":null},"payment_token":null,"shipping":null,"billing":{"address":{"city":"San Fransico","country":"BR","line1":"1467","line2":"Harrison Street","line3":"Harrison Street","zip":"94122","state":"California","first_name":"joseph","last_name":"Doe"},"phone":{"number":"8056594427","country_code":"+91"},"email":"guest@example.com"},"order_details":null,"email":"abc@example.com","name":null,"phone":null,"return_url":"https://google.com/","authentication_type":"no_three_ds","statement_descriptor_name":null,"statement_descriptor_suffix":null,"next_action":null,"cancellation_reason":null,"error_code":null,"error_message":null,"unified_code":null,"unified_message":null,"payment_experience":null,"payment_method_type":"pix","connector_label":null,"business_country":null,"business_label":"default","business_sub_label":null,"allowed_payment_method_types":null,"ephemeral_key":null,"manual_retry_allowed":false,"connector_transaction_id":"BD7QBLW3MDSQNS65","frm_message":null,"metadata":{"udf1":"value1","Force-PSP":"Adyen","login_date":"2019-09-10T10:11:12Z","new_customer":"true"},"connector_metadata":null,"feature_metadata":null,"reference_id":"pay_cqxjoA0Drk0NBgmCpbQf_1","payment_link":null,"profile_id":"pro_zJpGj6xSodQqoBYltbzl","surcharge_details":null,"attempt_count":1,"merchant_decision":null,"merchant_connector_id":"mca_PTFqfuKnofhHIFDFLBsu","incremental_authorization_allowed":null,"authorization_count":null,"incremental_authorizations":null,"external_authentication_details":null,"external_3ds_authentication_attempted":false,"expires_on":"2025-07-24T22:52:17.081Z","fingerprint":null,"browser_info":{"language":"en-US","time_zone":-330,"ip_address":"127.0.0.1","user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36","color_depth":32,"java_enabled":true,"screen_width":1728,"accept_header":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8","screen_height":1117,"java_script_enabled":true},"payment_method_id":"pm_EN1YZr7i5sD4qHXH2uV5","payment_method_status":"inactive","updated":"2025-07-24T22:55:13.196Z","split_payments":null,"frm_metadata":null,"extended_authorization_applied":null,"capture_before":null,"merchant_order_reference_id":null,"order_tax_amount":null,"connector_mandate_id":null,"card_discovery":null,"force_3ds_challenge":false,"force_3ds_challenge_trigger":false,"issuer_error_code":null,"issuer_error_message":null,"is_iframe_redirection_enabled":null,"whole_connector_response":null}

</details>

## Checklist
- [x] I formatted the code `cargo +nightly fmt --all`
- [x] I addressed lints thrown by `cargo clippy`
- [x] I reviewed the submitted code
