diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index f49417c5840..e4c662840e4 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -716,7 +716,7 @@ pub struct RevenueRecoveryGetIntentResponse { pub status: common_enums::RecoveryStatus, /// The amount details for the payment - pub amount_details: AmountDetailsResponse, + pub amount_details: PaymentAmountDetailsResponse, /// It's a token used for client side verification. #[schema(value_type = String, example = "cs_0195b34da95d75239c6a4bf514458896")] @@ -832,6 +832,10 @@ pub struct RevenueRecoveryGetIntentResponse { #[serde(with = "common_utils::custom_serde::iso8601")] pub expires_on: PrimitiveDateTime, + /// Time when the payment was created + #[serde(with = "common_utils::custom_serde::iso8601")] + pub created_at: PrimitiveDateTime, // Add this new field + /// Additional data related to some frm(Fraud Risk Management) connectors #[schema(value_type = Option, example = r#"{ "coverage_request" : "fraud", "fulfillment_method" : "delivery" }"#)] pub frm_metadata: Option, diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index b5f6d63ecb9..7fbf90c0c4b 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -2727,10 +2727,38 @@ pub async fn revenue_recovery_get_intent_core( 0 }; + let payment_attempt = state + .store + .find_payment_attempts_by_payment_intent_id( + &payment_intent.id, + platform.get_processor().get_key_store(), + platform.get_processor().get_account().storage_scheme, + ) + .await + .ok() + .and_then(|attempts| { + payment_intent + .active_attempt_id + .as_ref() + .and_then(|active_attempt_id| { + attempts + .iter() + .find(|attempt| attempt.id == *active_attempt_id) + .cloned() + }) + .or_else(|| { + attempts + .iter() + .max_by_key(|attempt| attempt.created_at) + .cloned() + }) + }); + let response = transformers::generate_revenue_recovery_get_intent_response( payment_data, recovery_status, card_attached, + payment_attempt.as_ref(), ); Ok(services::ApplicationResponse::Json(response)) @@ -8458,25 +8486,64 @@ pub async fn revenue_recovery_list_payments( let workflow_results = join_all(workflow_futures).await; let billing_connector_results = join_all(billing_connector_futures).await; + let attempt_futures: Vec<_> = list + .iter() + .map(|(payment_intent, payment_attempt)| { + let platform_clone = platform.clone(); + + async move { + if payment_attempt.is_some() { + payment_attempt.clone() + } else { + db.find_payment_attempts_by_payment_intent_id( + &payment_intent.id.clone(), + platform_clone.get_processor().get_key_store(), + platform_clone.get_processor().get_account().storage_scheme, + ) + .await + .ok() + .and_then(|attempts| { + payment_intent + .active_attempt_id + .as_ref() + .and_then(|active_attempt_id| { + attempts + .iter() + .find(|attempt| attempt.id == *active_attempt_id) + .cloned() + }) + .or_else(|| { + attempts + .iter() + .max_by_key(|attempt| attempt.created_at) + .cloned() + }) + }) + } + } + }) + .collect(); + + let attempt_results = join_all(attempt_futures).await; + let data: Vec = list .into_iter() .zip(workflow_results.into_iter()) .zip(billing_connector_results.into_iter()) + .zip(attempt_results.into_iter()) .map( |( - ((payment_intent, payment_attempt), workflow_result), - billing_connector_account, + (((payment_intent, _), workflow_result), billing_connector_account), + payment_attempt, )| { let (calculate_workflow, execute_workflow) = workflow_result.unwrap_or((None, None)); - // Get retry threshold from billing connector account let max_retry_threshold = billing_connector_account .as_ref() .and_then(|mca| mca.get_retry_threshold()) - .unwrap_or(0); // Default fallback + .unwrap_or(0); - // Use custom mapping function map_to_recovery_payment_item( payment_intent, payment_attempt, diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs index f8303e8e633..32185576701 100644 --- a/crates/router/src/core/payments/transformers.rs +++ b/crates/router/src/core/payments/transformers.rs @@ -2177,6 +2177,7 @@ pub fn generate_revenue_recovery_get_intent_response( payment_data: D, recovery_status: common_enums::RecoveryStatus, card_attached: u32, + payment_attempt: Option<&storage::PaymentAttempt>, ) -> RevenueRecoveryGetIntentResponse where F: Clone, @@ -2188,10 +2189,12 @@ where RevenueRecoveryGetIntentResponse { id: payment_intent.id.clone(), profile_id: payment_intent.profile_id.clone(), - status: recovery_status, // Note: field is named 'status' not 'recovery_status' - amount_details: api_models::payments::AmountDetailsResponse::foreign_from( - payment_intent.amount_details.clone(), - ), + status: recovery_status, + amount_details: api_models::payments::PaymentAmountDetailsResponse::foreign_from(( + &payment_intent.amount_details, + payment_attempt.map(|pa| &pa.amount_details), // Same pattern as list API + )), + created_at: payment_intent.created_at, client_secret: client_secret.clone(), merchant_reference_id: payment_intent.merchant_reference_id.clone(), routing_algorithm_id: payment_intent.routing_algorithm_id.clone(),