Bug: [FEATURE]: Add refunds list feature in v2 apis



Add refunds list feature in v2 apis
diff --git a/api-reference-v2/openapi_spec.json b/api-reference-v2/openapi_spec.json
index b6d6fb87e27..59d5f5437c2 100644
--- a/api-reference-v2/openapi_spec.json
+++ b/api-reference-v2/openapi_spec.json
@@ -3306,6 +3306,43 @@
         ]
       }
     },
+    "/v2/refunds/list": {
+      "get": {
+        "tags": [
+          "Refunds"
+        ],
+        "summary": "Refunds - List",
+        "description": "To list the refunds associated with a payment_id or with the merchant, if payment_id is not provided",
+        "operationId": "List all Refunds",
+        "requestBody": {
+          "content": {
+            "application/json": {
+              "schema": {
+                "$ref": "#/components/schemas/RefundListRequest"
+              }
+            }
+          },
+          "required": true
+        },
+        "responses": {
+          "200": {
+            "description": "List of refunds",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/RefundListResponse"
+                }
+              }
+            }
+          }
+        },
+        "security": [
+          {
+            "api_key": []
+          }
+        ]
+      }
+    },
     "/v2/process_tracker/revenue_recovery_workflow/{revenue_recovery_id}": {
       "get": {
         "tags": [
@@ -20386,13 +20423,11 @@
                 "nullable": true
               },
               "refund_id": {
-                "type": "string",
-                "description": "The identifier for the refund",
-                "nullable": true
-              },
-              "profile_id": {
-                "type": "string",
-                "description": "The identifier for business profile",
+                "allOf": [
+                  {
+                    "$ref": "#/components/schemas/common_utils.id_type.GlobalRefundId"
+                  }
+                ],
                 "nullable": true
               },
               "limit": {
@@ -20423,7 +20458,7 @@
                 "description": "The list of connectors to filter refunds list",
                 "nullable": true
               },
-              "merchant_connector_id": {
+              "connector_id_list": {
                 "type": "array",
                 "items": {
                   "type": "string"
diff --git a/crates/api_models/src/refunds.rs b/crates/api_models/src/refunds.rs
index 775e0dfeef7..bb20978e0d2 100644
--- a/crates/api_models/src/refunds.rs
+++ b/crates/api_models/src/refunds.rs
@@ -330,6 +330,7 @@ pub struct RefundErrorDetails {
     pub message: String,
 }
 
+#[cfg(feature = "v1")]
 #[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize, ToSchema)]
 pub struct RefundListRequest {
     /// The identifier for the payment
@@ -361,7 +362,35 @@ pub struct RefundListRequest {
     #[schema(value_type = Option<Vec<RefundStatus>>)]
     pub refund_status: Option<Vec<enums::RefundStatus>>,
 }
-
+#[cfg(feature = "v2")]
+#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize, ToSchema)]
+pub struct RefundListRequest {
+    /// The identifier for the payment
+    #[schema(value_type = Option<String>)]
+    pub payment_id: Option<common_utils::id_type::GlobalPaymentId>,
+    /// The identifier for the refund
+    pub refund_id: Option<common_utils::id_type::GlobalRefundId>,
+    /// Limit on the number of objects to return
+    pub limit: Option<i64>,
+    /// The starting point within a list of objects
+    pub offset: Option<i64>,
+    /// The time range for which objects are needed. TimeRange has two fields start_time and end_time from which objects can be filtered as per required scenarios (created_at, time less than, greater than etc)
+    #[serde(flatten)]
+    pub time_range: Option<TimeRange>,
+    /// The amount to filter reufnds list. Amount takes two option fields start_amount and end_amount from which objects can be filtered as per required scenarios (less_than, greater_than, equal_to and range)
+    pub amount_filter: Option<AmountFilter>,
+    /// The list of connectors to filter refunds list
+    pub connector: Option<Vec<String>>,
+    /// The list of merchant connector ids to filter the refunds list for selected label
+    #[schema(value_type = Option<Vec<String>>)]
+    pub connector_id_list: Option<Vec<common_utils::id_type::MerchantConnectorAccountId>>,
+    /// The list of currencies to filter refunds list
+    #[schema(value_type = Option<Vec<Currency>>)]
+    pub currency: Option<Vec<enums::Currency>>,
+    /// The list of refund statuses to filter refunds list
+    #[schema(value_type = Option<Vec<RefundStatus>>)]
+    pub refund_status: Option<Vec<enums::RefundStatus>>,
+}
 #[derive(Debug, Clone, Eq, PartialEq, Serialize, ToSchema)]
 pub struct RefundListResponse {
     /// The number of refunds included in the list
diff --git a/crates/hyperswitch_domain_models/Cargo.toml b/crates/hyperswitch_domain_models/Cargo.toml
index 39aadb90db6..e889d2c16c3 100644
--- a/crates/hyperswitch_domain_models/Cargo.toml
+++ b/crates/hyperswitch_domain_models/Cargo.toml
@@ -17,6 +17,7 @@ v2 = ["api_models/v2", "diesel_models/v2", "common_utils/v2", "common_types/v2"]
 v1 = ["api_models/v1", "diesel_models/v1", "common_utils/v1", "common_types/v1"]
 customer_v2 = ["api_models/customer_v2", "diesel_models/customer_v2"]
 payment_methods_v2 = ["api_models/payment_methods_v2", "diesel_models/payment_methods_v2"]
+refunds_v2 = ["api_models/refunds_v2"]
 dummy_connector = []
 revenue_recovery= []
 
diff --git a/crates/hyperswitch_domain_models/src/refunds.rs b/crates/hyperswitch_domain_models/src/refunds.rs
index 4016754510a..f8e7e0bad11 100644
--- a/crates/hyperswitch_domain_models/src/refunds.rs
+++ b/crates/hyperswitch_domain_models/src/refunds.rs
@@ -1,5 +1,9 @@
+#[cfg(all(feature = "v2", feature = "refunds_v2"))]
+use crate::business_profile::Profile;
+#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "refunds_v2")))]
 use crate::errors;
 
+#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "refunds_v2")))]
 pub struct RefundListConstraints {
     pub payment_id: Option<common_utils::id_type::PaymentId>,
     pub refund_id: Option<String>,
@@ -14,6 +18,22 @@ pub struct RefundListConstraints {
     pub refund_status: Option<Vec<common_enums::RefundStatus>>,
 }
 
+#[cfg(all(feature = "v2", feature = "refunds_v2"))]
+pub struct RefundListConstraints {
+    pub payment_id: Option<common_utils::id_type::GlobalPaymentId>,
+    pub refund_id: Option<common_utils::id_type::GlobalRefundId>,
+    pub profile_id: common_utils::id_type::ProfileId,
+    pub limit: Option<i64>,
+    pub offset: Option<i64>,
+    pub time_range: Option<common_utils::types::TimeRange>,
+    pub amount_filter: Option<api_models::payments::AmountFilter>,
+    pub connector: Option<Vec<String>>,
+    pub connector_id_list: Option<Vec<common_utils::id_type::MerchantConnectorAccountId>>,
+    pub currency: Option<Vec<common_enums::Currency>>,
+    pub refund_status: Option<Vec<common_enums::RefundStatus>>,
+}
+
+#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "refunds_v2")))]
 impl
     TryFrom<(
         api_models::refunds::RefundListRequest,
@@ -80,3 +100,35 @@ impl
         })
     }
 }
+
+#[cfg(all(feature = "v2", feature = "refunds_v2"))]
+impl From<(api_models::refunds::RefundListRequest, Profile)> for RefundListConstraints {
+    fn from((value, profile): (api_models::refunds::RefundListRequest, Profile)) -> Self {
+        let api_models::refunds::RefundListRequest {
+            payment_id,
+            refund_id,
+            connector,
+            currency,
+            refund_status,
+            limit,
+            offset,
+            time_range,
+            amount_filter,
+            connector_id_list,
+        } = value;
+
+        Self {
+            payment_id,
+            refund_id,
+            profile_id: profile.get_id().to_owned(),
+            limit,
+            offset,
+            time_range,
+            amount_filter,
+            connector,
+            connector_id_list,
+            currency,
+            refund_status,
+        }
+    }
+}
diff --git a/crates/openapi/src/openapi_v2.rs b/crates/openapi/src/openapi_v2.rs
index bb48871190e..e9338af8d81 100644
--- a/crates/openapi/src/openapi_v2.rs
+++ b/crates/openapi/src/openapi_v2.rs
@@ -149,6 +149,7 @@ Never share your secret api keys. Keep them guarded and secure.
         //Routes for refunds
         routes::refunds::refunds_create,
         routes::refunds::refunds_retrieve,
+        routes::refunds::refunds_list,
 
         // Routes for Revenue Recovery flow under Process Tracker
         routes::revenue_recovery::revenue_recovery_pt_retrieve_api
diff --git a/crates/openapi/src/routes/refunds.rs b/crates/openapi/src/routes/refunds.rs
index 5c55de2d177..8fb6d4f1835 100644
--- a/crates/openapi/src/routes/refunds.rs
+++ b/crates/openapi/src/routes/refunds.rs
@@ -128,6 +128,7 @@ pub async fn refunds_update() {}
     operation_id = "List all Refunds",
     security(("api_key" = []))
 )]
+#[cfg(feature = "v1")]
 pub fn refunds_list() {}
 
 /// Refunds - List For the Given profiles
@@ -233,3 +234,20 @@ pub async fn refunds_create() {}
 )]
 #[cfg(feature = "v2")]
 pub async fn refunds_retrieve() {}
+
+/// Refunds - List
+///
+/// To list the refunds associated with a payment_id or with the merchant, if payment_id is not provided
+#[utoipa::path(
+    get,
+    path = "/v2/refunds/list",
+    request_body=RefundListRequest,
+    responses(
+        (status = 200, description = "List of refunds", body = RefundListResponse),
+    ),
+    tag = "Refunds",
+    operation_id = "List all Refunds",
+    security(("api_key" = []))
+)]
+#[cfg(feature = "v2")]
+pub fn refunds_list() {}
diff --git a/crates/router/Cargo.toml b/crates/router/Cargo.toml
index e80b9672f23..d14eaaf83d5 100644
--- a/crates/router/Cargo.toml
+++ b/crates/router/Cargo.toml
@@ -39,7 +39,7 @@ customer_v2 = ["api_models/customer_v2", "diesel_models/customer_v2", "hyperswit
 payment_methods_v2 = ["api_models/payment_methods_v2", "diesel_models/payment_methods_v2", "hyperswitch_domain_models/payment_methods_v2", "storage_impl/payment_methods_v2", "common_utils/payment_methods_v2"]
 dynamic_routing = ["external_services/dynamic_routing", "storage_impl/dynamic_routing", "api_models/dynamic_routing"]
 revenue_recovery =["api_models/revenue_recovery","hyperswitch_interfaces/revenue_recovery","hyperswitch_domain_models/revenue_recovery","hyperswitch_connectors/revenue_recovery"]
-refunds_v2 = ["api_models/refunds_v2", "diesel_models/refunds_v2", "storage_impl/refunds_v2"]
+refunds_v2 = ["api_models/refunds_v2", "diesel_models/refunds_v2", "storage_impl/refunds_v2", "hyperswitch_domain_models/refunds_v2"]
 
 # Partial Auth
 # The feature reduces the overhead of the router authenticating the merchant for every request, and trusts on `x-merchant-id` header to be present in the request.
diff --git a/crates/router/src/core/refunds_v2.rs b/crates/router/src/core/refunds_v2.rs
index b6ccfe8376b..8d113d5a329 100644
--- a/crates/router/src/core/refunds_v2.rs
+++ b/crates/router/src/core/refunds_v2.rs
@@ -4,6 +4,7 @@ use api_models::{enums::Connector, refunds::RefundErrorDetails};
 use common_utils::{id_type, types as common_utils_types};
 use error_stack::{report, ResultExt};
 use hyperswitch_domain_models::{
+    refunds::RefundListConstraints,
     router_data::{ErrorResponse, RouterData},
     router_data_v2::RefundFlowData,
 };
@@ -734,6 +735,56 @@ pub fn build_refund_update_for_rsync(
     }
 }
 
+// ********************************************** Refund list **********************************************
+
+///   If payment_id is provided, lists all the refunds associated with that particular payment_id
+///   If payment_id is not provided, lists the refunds associated with that particular merchant - to the limit specified,if no limits given, it is 10 by default
+#[instrument(skip_all)]
+#[cfg(feature = "olap")]
+pub async fn refund_list(
+    state: SessionState,
+    merchant_account: domain::MerchantAccount,
+    profile: domain::Profile,
+    req: refunds::RefundListRequest,
+) -> errors::RouterResponse<refunds::RefundListResponse> {
+    let db = state.store;
+    let limit = refunds_validator::validate_refund_list(req.limit)?;
+    let offset = req.offset.unwrap_or_default();
+
+    let refund_list = db
+        .filter_refund_by_constraints(
+            merchant_account.get_id(),
+            RefundListConstraints::from((req.clone(), profile.clone())),
+            merchant_account.storage_scheme,
+            limit,
+            offset,
+        )
+        .await
+        .to_not_found_response(errors::ApiErrorResponse::RefundNotFound)?;
+
+    let data: Vec<refunds::RefundResponse> = refund_list
+        .into_iter()
+        .map(refunds::RefundResponse::foreign_try_from)
+        .collect::<Result<_, _>>()?;
+
+    let total_count = db
+        .get_total_count_of_refunds(
+            merchant_account.get_id(),
+            RefundListConstraints::from((req, profile)),
+            merchant_account.storage_scheme,
+        )
+        .await
+        .to_not_found_response(errors::ApiErrorResponse::InternalServerError)?;
+
+    Ok(services::ApplicationResponse::Json(
+        api_models::refunds::RefundListResponse {
+            count: data.len(),
+            total_count,
+            data,
+        },
+    ))
+}
+
 // ********************************************** VALIDATIONS **********************************************
 
 #[instrument(skip_all)]
diff --git a/crates/router/src/db/kafka_store.rs b/crates/router/src/db/kafka_store.rs
index 6e59e0c95d7..13f0cb857ed 100644
--- a/crates/router/src/db/kafka_store.rs
+++ b/crates/router/src/db/kafka_store.rs
@@ -2843,6 +2843,26 @@ impl RefundInterface for KafkaStore {
             .await
     }
 
+    #[cfg(all(feature = "v2", feature = "refunds_v2"))]
+    async fn filter_refund_by_constraints(
+        &self,
+        merchant_id: &id_type::MerchantId,
+        refund_details: refunds::RefundListConstraints,
+        storage_scheme: MerchantStorageScheme,
+        limit: i64,
+        offset: i64,
+    ) -> CustomResult<Vec<storage::Refund>, errors::StorageError> {
+        self.diesel_store
+            .filter_refund_by_constraints(
+                merchant_id,
+                refund_details,
+                storage_scheme,
+                limit,
+                offset,
+            )
+            .await
+    }
+
     #[cfg(all(
         any(feature = "v1", feature = "v2"),
         not(feature = "refunds_v2"),
@@ -2891,6 +2911,18 @@ impl RefundInterface for KafkaStore {
             .get_total_count_of_refunds(merchant_id, refund_details, storage_scheme)
             .await
     }
+
+    #[cfg(all(feature = "v2", feature = "refunds_v2"))]
+    async fn get_total_count_of_refunds(
+        &self,
+        merchant_id: &id_type::MerchantId,
+        refund_details: refunds::RefundListConstraints,
+        storage_scheme: MerchantStorageScheme,
+    ) -> CustomResult<i64, errors::StorageError> {
+        self.diesel_store
+            .get_total_count_of_refunds(merchant_id, refund_details, storage_scheme)
+            .await
+    }
 }
 
 #[async_trait::async_trait]
diff --git a/crates/router/src/db/refund.rs b/crates/router/src/db/refund.rs
index fbbe0a6d159..7d9a8703169 100644
--- a/crates/router/src/db/refund.rs
+++ b/crates/router/src/db/refund.rs
@@ -91,6 +91,16 @@ pub trait RefundInterface {
         offset: i64,
     ) -> CustomResult<Vec<diesel_models::refund::Refund>, errors::StorageError>;
 
+    #[cfg(all(feature = "v2", feature = "refunds_v2"))]
+    async fn filter_refund_by_constraints(
+        &self,
+        merchant_id: &common_utils::id_type::MerchantId,
+        refund_details: refunds::RefundListConstraints,
+        storage_scheme: enums::MerchantStorageScheme,
+        limit: i64,
+        offset: i64,
+    ) -> CustomResult<Vec<diesel_models::refund::Refund>, errors::StorageError>;
+
     #[cfg(all(
         any(feature = "v1", feature = "v2"),
         not(feature = "refunds_v2"),
@@ -127,6 +137,14 @@ pub trait RefundInterface {
         refund_details: &refunds::RefundListConstraints,
         storage_scheme: enums::MerchantStorageScheme,
     ) -> CustomResult<i64, errors::StorageError>;
+
+    #[cfg(all(feature = "v2", feature = "refunds_v2"))]
+    async fn get_total_count_of_refunds(
+        &self,
+        merchant_id: &common_utils::id_type::MerchantId,
+        refund_details: refunds::RefundListConstraints,
+        storage_scheme: enums::MerchantStorageScheme,
+    ) -> CustomResult<i64, errors::StorageError>;
 }
 
 #[cfg(not(feature = "kv_store"))]
@@ -925,6 +943,28 @@ mod storage {
             .map_err(|error| report!(errors::StorageError::from(error)))
         }
 
+        #[cfg(all(feature = "v2", feature = "refunds_v2"))]
+        #[instrument(skip_all)]
+        async fn filter_refund_by_constraints(
+            &self,
+            merchant_id: &common_utils::id_type::MerchantId,
+            refund_details: refunds::RefundListConstraints,
+            _storage_scheme: enums::MerchantStorageScheme,
+            limit: i64,
+            offset: i64,
+        ) -> CustomResult<Vec<diesel_models::refund::Refund>, errors::StorageError> {
+            let conn = connection::pg_connection_read(self).await?;
+            <diesel_models::refund::Refund as storage_types::RefundDbExt>::filter_by_constraints(
+                &conn,
+                merchant_id,
+                refund_details,
+                limit,
+                offset,
+            )
+            .await
+            .map_err(|error| report!(errors::StorageError::from(error)))
+        }
+
         #[cfg(all(
             any(feature = "v1", feature = "v2"),
             not(feature = "refunds_v2"),
@@ -983,6 +1023,24 @@ mod storage {
             .await
             .map_err(|error| report!(errors::StorageError::from(error)))
         }
+
+        #[cfg(all(feature = "v2", feature = "refunds_v2"))]
+        #[instrument(skip_all)]
+        async fn get_total_count_of_refunds(
+            &self,
+            merchant_id: &common_utils::id_type::MerchantId,
+            refund_details: refunds::RefundListConstraints,
+            _storage_scheme: enums::MerchantStorageScheme,
+        ) -> CustomResult<i64, errors::StorageError> {
+            let conn = connection::pg_connection_read(self).await?;
+            <diesel_models::refund::Refund as storage_types::RefundDbExt>::get_refunds_count(
+                &conn,
+                merchant_id,
+                refund_details,
+            )
+            .await
+            .map_err(|error| report!(errors::StorageError::from(error)))
+        }
     }
 }
 
@@ -1369,6 +1427,115 @@ impl RefundInterface for MockDb {
         Ok(filtered_refunds)
     }
 
+    #[cfg(all(feature = "v2", feature = "refunds_v2", feature = "olap"))]
+    async fn filter_refund_by_constraints(
+        &self,
+        merchant_id: &common_utils::id_type::MerchantId,
+        refund_details: refunds::RefundListConstraints,
+        _storage_scheme: enums::MerchantStorageScheme,
+        limit: i64,
+        offset: i64,
+    ) -> CustomResult<Vec<diesel_models::refund::Refund>, errors::StorageError> {
+        let mut unique_connectors = HashSet::new();
+        let mut unique_connector_ids = HashSet::new();
+        let mut unique_currencies = HashSet::new();
+        let mut unique_statuses = HashSet::new();
+
+        // Fill the hash sets with data from refund_details
+        if let Some(connectors) = &refund_details.connector {
+            connectors.iter().for_each(|connector| {
+                unique_connectors.insert(connector);
+            });
+        }
+
+        if let Some(connector_id_list) = &refund_details.connector_id_list {
+            connector_id_list.iter().for_each(|unique_connector_id| {
+                unique_connector_ids.insert(unique_connector_id);
+            });
+        }
+
+        if let Some(currencies) = &refund_details.currency {
+            currencies.iter().for_each(|currency| {
+                unique_currencies.insert(currency);
+            });
+        }
+
+        if let Some(refund_statuses) = &refund_details.refund_status {
+            refund_statuses.iter().for_each(|refund_status| {
+                unique_statuses.insert(refund_status);
+            });
+        }
+
+        let refunds = self.refunds.lock().await;
+        let filtered_refunds = refunds
+            .iter()
+            .filter(|refund| refund.merchant_id == *merchant_id)
+            .filter(|refund| {
+                refund_details
+                    .payment_id
+                    .clone()
+                    .map_or(true, |id| id == refund.payment_id)
+            })
+            .filter(|refund| {
+                refund_details
+                    .refund_id
+                    .clone()
+                    .map_or(true, |id| id == refund.id)
+            })
+            .filter(|refund| {
+                refund
+                    .profile_id
+                    .as_ref()
+                    .is_some_and(|profile_id| profile_id == &refund_details.profile_id)
+            })
+            .filter(|refund| {
+                refund.created_at
+                    >= refund_details.time_range.map_or(
+                        common_utils::date_time::now() - time::Duration::days(60),
+                        |range| range.start_time,
+                    )
+                    && refund.created_at
+                        <= refund_details
+                            .time_range
+                            .map_or(common_utils::date_time::now(), |range| {
+                                range.end_time.unwrap_or_else(common_utils::date_time::now)
+                            })
+            })
+            .filter(|refund| {
+                refund_details
+                    .amount_filter
+                    .as_ref()
+                    .map_or(true, |amount| {
+                        refund.refund_amount
+                            >= MinorUnit::new(amount.start_amount.unwrap_or(i64::MIN))
+                            && refund.refund_amount
+                                <= MinorUnit::new(amount.end_amount.unwrap_or(i64::MAX))
+                    })
+            })
+            .filter(|refund| {
+                unique_connectors.is_empty() || unique_connectors.contains(&refund.connector)
+            })
+            .filter(|refund| {
+                unique_connector_ids.is_empty()
+                    || refund
+                        .connector_id
+                        .as_ref()
+                        .is_some_and(|id| unique_connector_ids.contains(id))
+            })
+            .filter(|refund| {
+                unique_currencies.is_empty() || unique_currencies.contains(&refund.currency)
+            })
+            .filter(|refund| {
+                unique_statuses.is_empty() || unique_statuses.contains(&refund.refund_status)
+            })
+            .skip(usize::try_from(offset).unwrap_or_default())
+            .take(usize::try_from(limit).unwrap_or(MAX_LIMIT))
+            .cloned()
+            .collect::<Vec<_>>();
+
+        Ok(filtered_refunds)
+    }
+
     #[cfg(all(
         any(feature = "v1", feature = "v2"),
         not(feature = "refunds_v2"),
@@ -1586,4 +1753,111 @@ impl RefundInterface for MockDb {
 
         Ok(filtered_refunds_count)
     }
+
+    #[cfg(all(feature = "v2", feature = "refunds_v2", feature = "olap"))]
+    async fn get_total_count_of_refunds(
+        &self,
+        merchant_id: &common_utils::id_type::MerchantId,
+        refund_details: refunds::RefundListConstraints,
+        _storage_scheme: enums::MerchantStorageScheme,
+    ) -> CustomResult<i64, errors::StorageError> {
+        let mut unique_connectors = HashSet::new();
+        let mut unique_connector_ids = HashSet::new();
+        let mut unique_currencies = HashSet::new();
+        let mut unique_statuses = HashSet::new();
+
+        // Fill the hash sets with data from refund_details
+        if let Some(connectors) = &refund_details.connector {
+            connectors.iter().for_each(|connector| {
+                unique_connectors.insert(connector);
+            });
+        }
+
+        if let Some(connector_id_list) = &refund_details.connector_id_list {
+            connector_id_list.iter().for_each(|unique_connector_id| {
+                unique_connector_ids.insert(unique_connector_id);
+            });
+        }
+
+        if let Some(currencies) = &refund_details.currency {
+            currencies.iter().for_each(|currency| {
+                unique_currencies.insert(currency);
+            });
+        }
+
+        if let Some(refund_statuses) = &refund_details.refund_status {
+            refund_statuses.iter().for_each(|refund_status| {
+                unique_statuses.insert(refund_status);
+            });
+        }
+
+        let refunds = self.refunds.lock().await;
+        let filtered_refunds = refunds
+            .iter()
+            .filter(|refund| refund.merchant_id == *merchant_id)
+            .filter(|refund| {
+                refund_details
+                    .payment_id
+                    .clone()
+                    .map_or(true, |id| id == refund.payment_id)
+            })
+            .filter(|refund| {
+                refund_details
+                    .refund_id
+                    .clone()
+                    .map_or(true, |id| id == refund.id)
+            })
+            .filter(|refund| {
+                refund
+                    .profile_id
+                    .as_ref()
+                    .is_some_and(|profile_id| profile_id == &refund_details.profile_id)
+            })
+            .filter(|refund| {
+                refund.created_at
+                    >= refund_details.time_range.map_or(
+                        common_utils::date_time::now() - time::Duration::days(60),
+                        |range| range.start_time,
+                    )
+                    && refund.created_at
+                        <= refund_details
+                            .time_range
+                            .map_or(common_utils::date_time::now(), |range| {
+                                range.end_time.unwrap_or_else(common_utils::date_time::now)
+                            })
+            })
+            .filter(|refund| {
+                refund_details
+                    .amount_filter
+                    .as_ref()
+                    .map_or(true, |amount| {
+                        refund.refund_amount
+                            >= MinorUnit::new(amount.start_amount.unwrap_or(i64::MIN))
+                            && refund.refund_amount
+                                <= MinorUnit::new(amount.end_amount.unwrap_or(i64::MAX))
+                    })
+            })
+            .filter(|refund| {
+                unique_connectors.is_empty() || unique_connectors.contains(&refund.connector)
+            })
+            .filter(|refund| {
+                unique_connector_ids.is_empty()
+                    || refund
+                        .connector_id
+                        .as_ref()
+                        .is_some_and(|id| unique_connector_ids.contains(id))
+            })
+            .filter(|refund| {
+                unique_currencies.is_empty() || unique_currencies.contains(&refund.currency)
+            })
+            .filter(|refund| {
+                unique_statuses.is_empty() || unique_statuses.contains(&refund.refund_status)
+            })
+            .cloned()
+            .collect::<Vec<_>>();
+
+        let filtered_refunds_count = filtered_refunds.len().try_into().unwrap_or_default();
+
+        Ok(filtered_refunds_count)
+    }
 }
diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs
index f75fb9ad7f6..57a1c74893d 100644
--- a/crates/router/src/routes/app.rs
+++ b/crates/router/src/routes/app.rs
@@ -1169,14 +1169,26 @@ impl Refunds {
     }
 }
 
-#[cfg(all(feature = "v2", feature = "refunds_v2", feature = "oltp"))]
+#[cfg(all(
+    feature = "v2",
+    feature = "refunds_v2",
+    any(feature = "olap", feature = "oltp")
+))]
 impl Refunds {
     pub fn server(state: AppState) -> Scope {
         let mut route = web::scope("/v2/refunds").app_data(web::Data::new(state));
 
-        route = route
-            .service(web::resource("").route(web::post().to(refunds::refunds_create)))
-            .service(web::resource("/{id}").route(web::get().to(refunds::refunds_retrieve)));
+        #[cfg(feature = "olap")]
+        {
+            route =
+                route.service(web::resource("/list").route(web::get().to(refunds::refunds_list)));
+        }
+        #[cfg(feature = "oltp")]
+        {
+            route = route
+                .service(web::resource("").route(web::post().to(refunds::refunds_create)))
+                .service(web::resource("/{id}").route(web::get().to(refunds::refunds_retrieve)));
+        }
 
         route
     }
diff --git a/crates/router/src/routes/refunds.rs b/crates/router/src/routes/refunds.rs
index 65ea8720f99..dc92545841e 100644
--- a/crates/router/src/routes/refunds.rs
+++ b/crates/router/src/routes/refunds.rs
@@ -375,6 +375,37 @@ pub async fn refunds_list(
     .await
 }
 
+#[cfg(all(feature = "v2", feature = "refunds_v2", feature = "olap"))]
+#[instrument(skip_all, fields(flow = ?Flow::RefundsList))]
+pub async fn refunds_list(
+    state: web::Data<AppState>,
+    req: HttpRequest,
+    payload: web::Json<api_models::refunds::RefundListRequest>,
+) -> HttpResponse {
+    let flow = Flow::RefundsList;
+    Box::pin(api::server_wrap(
+        flow,
+        state,
+        &req,
+        payload.into_inner(),
+        |state, auth: auth::AuthenticationData, req, _| {
+            refund_list(state, auth.merchant_account, auth.profile, req)
+        },
+        auth::auth_type(
+            &auth::V2ApiKeyAuth {
+                is_connected_allowed: false,
+                is_platform_allowed: false,
+            },
+            &auth::JWTAuth {
+                permission: Permission::MerchantRefundRead,
+            },
+            req.headers(),
+        ),
+        api_locking::LockAction::NotApplicable,
+    ))
+    .await
+}
+
 #[cfg(all(
     any(feature = "v1", feature = "v2"),
     not(feature = "refunds_v2"),
diff --git a/crates/router/src/types/api/refunds.rs b/crates/router/src/types/api/refunds.rs
index 8beaf7010f9..935cb14c1f7 100644
--- a/crates/router/src/types/api/refunds.rs
+++ b/crates/router/src/types/api/refunds.rs
@@ -3,8 +3,8 @@ pub use api_models::refunds::RefundRequest;
 #[cfg(all(feature = "v2", feature = "refunds_v2"))]
 pub use api_models::refunds::RefundsCreateRequest;
 pub use api_models::refunds::{
-    RefundResponse, RefundStatus, RefundType, RefundUpdateRequest, RefundsRetrieveBody,
-    RefundsRetrieveRequest,
+    RefundListRequest, RefundListResponse, RefundResponse, RefundStatus, RefundType,
+    RefundUpdateRequest, RefundsRetrieveBody, RefundsRetrieveRequest,
 };
 pub use hyperswitch_domain_models::router_flow_types::refunds::{Execute, RSync};
 pub use hyperswitch_interfaces::api::refunds::{Refund, RefundExecute, RefundSync};
diff --git a/crates/router/src/types/storage/refund.rs b/crates/router/src/types/storage/refund.rs
index 646b2742d5b..8f0472bb8f5 100644
--- a/crates/router/src/types/storage/refund.rs
+++ b/crates/router/src/types/storage/refund.rs
@@ -5,11 +5,14 @@ use diesel::{associations::HasTable, BoolExpressionMethods, ExpressionMethods, Q
 pub use diesel_models::refund::{
     Refund, RefundCoreWorkflow, RefundNew, RefundUpdate, RefundUpdateInternal,
 };
+#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "refunds_v2")))]
+use diesel_models::schema::refund::dsl;
+#[cfg(all(feature = "v2", feature = "refunds_v2"))]
+use diesel_models::schema_v2::refund::dsl;
 use diesel_models::{
     enums::{Currency, RefundStatus},
     errors,
     query::generics::db_metrics,
-    schema::refund::dsl,
 };
 use error_stack::ResultExt;
 use hyperswitch_domain_models::refunds;
@@ -27,6 +30,15 @@ pub trait RefundDbExt: Sized {
         offset: i64,
     ) -> CustomResult<Vec<Self>, errors::DatabaseError>;
 
+    #[cfg(all(feature = "v2", feature = "refunds_v2"))]
+    async fn filter_by_constraints(
+        conn: &PgPooledConn,
+        merchant_id: &common_utils::id_type::MerchantId,
+        refund_list_details: refunds::RefundListConstraints,
+        limit: i64,
+        offset: i64,
+    ) -> CustomResult<Vec<Self>, errors::DatabaseError>;
+
     #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "refunds_v2")))]
     async fn filter_by_meta_constraints(
         conn: &PgPooledConn,
@@ -48,6 +60,13 @@ pub trait RefundDbExt: Sized {
         profile_id_list: Option<Vec<common_utils::id_type::ProfileId>>,
         time_range: &common_utils::types::TimeRange,
     ) -> CustomResult<Vec<(RefundStatus, i64)>, errors::DatabaseError>;
+
+    #[cfg(all(feature = "v2", feature = "refunds_v2"))]
+    async fn get_refunds_count(
+        conn: &PgPooledConn,
+        merchant_id: &common_utils::id_type::MerchantId,
+        refund_list_details: refunds::RefundListConstraints,
+    ) -> CustomResult<i64, errors::DatabaseError>;
 }
 
 #[async_trait::async_trait]
@@ -164,6 +183,82 @@ impl RefundDbExt for Refund {
         .attach_printable_lazy(|| "Error filtering records by predicate")
     }
 
+    #[cfg(all(feature = "v2", feature = "refunds_v2"))]
+    async fn filter_by_constraints(
+        conn: &PgPooledConn,
+        merchant_id: &common_utils::id_type::MerchantId,
+        refund_list_details: refunds::RefundListConstraints,
+        limit: i64,
+        offset: i64,
+    ) -> CustomResult<Vec<Self>, errors::DatabaseError> {
+        let mut filter = <Self as HasTable>::table()
+            .filter(dsl::merchant_id.eq(merchant_id.to_owned()))
+            .order(dsl::modified_at.desc())
+            .into_boxed();
+
+        if let Some(payment_id) = &refund_list_details.payment_id {
+            filter = filter.filter(dsl::payment_id.eq(payment_id.to_owned()));
+        }
+
+        if let Some(refund_id) = &refund_list_details.refund_id {
+            filter = filter.filter(dsl::id.eq(refund_id.to_owned()));
+        }
+
+        if let Some(time_range) = &refund_list_details.time_range {
+            filter = filter.filter(dsl::created_at.ge(time_range.start_time));
+
+            if let Some(end_time) = time_range.end_time {
+                filter = filter.filter(dsl::created_at.le(end_time));
+            }
+        }
+
+        filter = match refund_list_details.amount_filter {
+            Some(AmountFilter {
+                start_amount: Some(start),
+                end_amount: Some(end),
+            }) => filter.filter(dsl::refund_amount.between(start, end)),
+            Some(AmountFilter {
+                start_amount: Some(start),
+                end_amount: None,
+            }) => filter.filter(dsl::refund_amount.ge(start)),
+            Some(AmountFilter {
+                start_amount: None,
+                end_amount: Some(end),
+            }) => filter.filter(dsl::refund_amount.le(end)),
+            _ => filter,
+        };
+
+        if let Some(connector) = refund_list_details.connector {
+            filter = filter.filter(dsl::connector.eq_any(connector));
+        }
+
+        if let Some(connector_id_list) = refund_list_details.connector_id_list {
+            filter = filter.filter(dsl::connector_id.eq_any(connector_id_list));
+        }
+
+        if let Some(filter_currency) = refund_list_details.currency {
+            filter = filter.filter(dsl::currency.eq_any(filter_currency));
+        }
+
+        if let Some(filter_refund_status) = refund_list_details.refund_status {
+            filter = filter.filter(dsl::refund_status.eq_any(filter_refund_status));
+        }
+
+        filter = filter.limit(limit).offset(offset);
+
+        logger::debug!(query = %diesel::debug_query::<diesel::pg::Pg, _>(&filter).to_string());
+
+        db_metrics::track_database_call::<<Self as HasTable>::Table, _, _>(
+            filter.get_results_async(conn),
+            db_metrics::DatabaseOperation::Filter,
+        )
+        .await
+        .change_context(errors::DatabaseError::NotFound)
+        .attach_printable_lazy(|| "Error filtering records by predicate")
+
+        // todo!()
+    }
+
     #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "refunds_v2")))]
     async fn filter_by_meta_constraints(
         conn: &PgPooledConn,
@@ -309,6 +404,74 @@ impl RefundDbExt for Refund {
             .attach_printable_lazy(|| "Error filtering count of refunds")
     }
 
+    #[cfg(all(feature = "v2", feature = "refunds_v2"))]
+    async fn get_refunds_count(
+        conn: &PgPooledConn,
+        merchant_id: &common_utils::id_type::MerchantId,
+        refund_list_details: refunds::RefundListConstraints,
+    ) -> CustomResult<i64, errors::DatabaseError> {
+        let mut filter = <Self as HasTable>::table()
+            .count()
+            .filter(dsl::merchant_id.eq(merchant_id.to_owned()))
+            .into_boxed();
+
+        if let Some(payment_id) = &refund_list_details.payment_id {
+            filter = filter.filter(dsl::payment_id.eq(payment_id.to_owned()));
+        }
+
+        if let Some(refund_id) = &refund_list_details.refund_id {
+            filter = filter.filter(dsl::id.eq(refund_id.to_owned()));
+        }
+
+        if let Some(time_range) = refund_list_details.time_range {
+            filter = filter.filter(dsl::created_at.ge(time_range.start_time));
+
+            if let Some(end_time) = time_range.end_time {
+                filter = filter.filter(dsl::created_at.le(end_time));
+            }
+        }
+
+        filter = match refund_list_details.amount_filter {
+            Some(AmountFilter {
+                start_amount: Some(start),
+                end_amount: Some(end),
+            }) => filter.filter(dsl::refund_amount.between(start, end)),
+            Some(AmountFilter {
+                start_amount: Some(start),
+                end_amount: None,
+            }) => filter.filter(dsl::refund_amount.ge(start)),
+            Some(AmountFilter {
+                start_amount: None,
+                end_amount: Some(end),
+            }) => filter.filter(dsl::refund_amount.le(end)),
+            _ => filter,
+        };
+
+        if let Some(connector) = refund_list_details.connector {
+            filter = filter.filter(dsl::connector.eq_any(connector));
+        }
+
+        if let Some(connector_id_list) = refund_list_details.connector_id_list {
+            filter = filter.filter(dsl::connector_id.eq_any(connector_id_list));
+        }
+
+        if let Some(filter_currency) = refund_list_details.currency {
+            filter = filter.filter(dsl::currency.eq_any(filter_currency));
+        }
+
+        if let Some(filter_refund_status) = refund_list_details.refund_status {
+            filter = filter.filter(dsl::refund_status.eq_any(filter_refund_status));
+        }
+
+        logger::debug!(query = %diesel::debug_query::<diesel::pg::Pg, _>(&filter).to_string());
+
+        filter
+            .get_result_async::<i64>(conn)
+            .await
+            .change_context(errors::DatabaseError::NotFound)
+            .attach_printable_lazy(|| "Error filtering count of refunds")
+    }
+
     #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "refunds_v2")))]
     async fn get_refund_status_with_count(
         conn: &PgPooledConn,

## Type of Change
<!-- Put an `x` in the boxes that apply -->

- [ ] Bugfix
- [x] New feature
- [ ] Enhancement
- [ ] Refactoring
- [ ] Dependency updates
- [ ] Documentation
- [ ] CI/CD

## Description
<!-- Describe your changes in detail -->
This PR introduces support for listing refunds in v2 apis.

Available filters for listing are as follows
- `payment_id`: Filter by a specific payment identifier.
- `refund_id`: Filter by a specific refund identifier.
- `limit`: Maximum number of refund records to return.
- `offset`: Starting index for pagination.
- `time_range`: Filter refunds by a range of creation timestamps (start_time, end_time).
- `amount_filter`: Filter by refund amount (start_amount, end_amount).
- `connector`: Filter by a list of connector names.
- `connector_id_list`: Filter by specific merchant connector account IDs.
- `currency`: Filter by a list of currency codes.
- `refund_status`: Filter by a list of refund statuses.

### Additional Changes

- [x] This PR modifies the API contract
- [ ] This PR modifies the database schema
- [ ] This PR modifies application configuration/environment variables

<!--
Provide links to the files with corresponding changes.

Following are the paths where you can find config files:
1. `config`
2. `crates/router/src/configs`
3. `loadtest/config`
-->


## Motivation and Context
<!--
Why is this change required? What problem does it solve?
If it fixes an open issue, please link to the issue here.

If you don't have an issue, we'd recommend starting with one first so the PR
can focus on the implementation (unless it is an obvious bug or documentation fix
that will have little conversation).
-->
Merchants will be able to list refunds upon merging this PR.

## How did you test it?
<!--
Did you write an integration/unit/API test to verify the code changes?
Or did you test this change manually (provide relevant screenshots)?
-->
Create some payments and refunds before hand.

- List all refunds API call
```
curl --location --request GET 'http://localhost:8080/v2/refunds/list' \
--header 'x-profile-id: pro_PiIU0MSYNuU5kS96H0kV' \
--header 'Content-Type: application/json' \
--header 'Authorization: api-key=dev_nG2oG32R2dnadMOvkwfRyl0j1YB8uNf3t1FcDqhFgFxO7TtVS1kPQ8m4F3DEngVb' \
--data '{
    
}'
```
- Response from the above call
```
{
    "count": 4,
    "total_count": 4,
    "data": [
        {
            "id": "12345_ref_0196a4badf2f7ed2b16c4ecaf1c814db",
            "payment_id": "12345_pay_0196a4baa48772909f72eac2cd863d7b",
            "merchant_reference_id": "1746520432",
            "amount": 70,
            "currency": "EUR",
            "status": "succeeded",
            "reason": "Paid by mistake",
            "metadata": {
                "foo": "bar"
            },
            "error_details": {
                "code": "",
                "message": ""
            },
            "created_at": "2025-05-06T08:33:52.433Z",
            "updated_at": "2025-05-06T08:33:53.474Z",
            "connector": "stripe",
            "profile_id": "pro_PiIU0MSYNuU5kS96H0kV",
            "merchant_connector_id": "mca_jQBjGAAN1BgqzXa0Gpoy",
            "connector_refund_reference_id": null
        },
        {
            "id": "12345_ref_0196a4ba41147d138621293a283b9d3e",
            "payment_id": "12345_pay_0196a4ba295477d0a4156e65135d8fb2",
            "merchant_reference_id": "1746520392",
            "amount": 1000,
            "currency": "BBD",
            "status": "succeeded",
            "reason": "Paid by mistake",
            "metadata": {
                "foo": "bar"
            },
            "error_details": {
                "code": "",
                "message": ""
            },
            "created_at": "2025-05-06T08:33:11.959Z",
            "updated_at": "2025-05-06T08:33:13.047Z",
            "connector": "stripe",
            "profile_id": "pro_PiIU0MSYNuU5kS96H0kV",
            "merchant_connector_id": "mca_jQBjGAAN1BgqzXa0Gpoy",
            "connector_refund_reference_id": null
        },
        {
            "id": "12345_ref_0196a4b978637810b17d2f855afb0305",
            "payment_id": "12345_pay_0196a4b9609179429e3e0decc9892a55",
            "merchant_reference_id": "1746520341",
            "amount": 100,
            "currency": "USD",
            "status": "succeeded",
            "reason": "Paid by mistake",
            "metadata": {
                "foo": "bar"
            },
            "error_details": {
                "code": "",
                "message": ""
            },
            "created_at": "2025-05-06T08:32:20.588Z",
            "updated_at": "2025-05-06T08:32:21.669Z",
            "connector": "stripe",
            "profile_id": "pro_PiIU0MSYNuU5kS96H0kV",
            "merchant_connector_id": "mca_jQBjGAAN1BgqzXa0Gpoy",
            "connector_refund_reference_id": null
        },
        {
            "id": "12345_ref_01969f96f27e7213b4257eb238ec3a46",
            "payment_id": "12345_pay_01969f96e02a7dd3a183592c24c6693e",
            "merchant_reference_id": "1746434192",
            "amount": 100,
            "currency": "USD",
            "status": "succeeded",
            "reason": "Paid by mistake",
            "metadata": {
                "foo": "bar"
            },
            "error_details": {
                "code": "",
                "message": ""
            },
            "created_at": "2025-05-05T08:36:32.015Z",
            "updated_at": "2025-05-05T08:36:46.580Z",
            "connector": "stripe",
            "profile_id": "pro_PiIU0MSYNuU5kS96H0kV",
            "merchant_connector_id": "mca_jQBjGAAN1BgqzXa0Gpoy",
            "connector_refund_reference_id": null
        }
    ]
}
```
- List refund by payment id API Call
```
curl --location --request GET 'http://localhost:8080/v2/refunds/list' \
--header 'x-profile-id: pro_PiIU0MSYNuU5kS96H0kV' \
--header 'Content-Type: application/json' \
--header 'Authorization: api-key=dev_nG2oG32R2dnadMOvkwfRyl0j1YB8uNf3t1FcDqhFgFxO7TtVS1kPQ8m4F3DEngVb' \
--data '{
    "payment_id":"12345_pay_0196a4baa48772909f72eac2cd863d7b"
}'
```
- Response from the above call
```
{
    "count": 1,
    "total_count": 1,
    "data": [
        {
            "id": "12345_ref_0196a4badf2f7ed2b16c4ecaf1c814db",
            "payment_id": "12345_pay_0196a4baa48772909f72eac2cd863d7b",
            "merchant_reference_id": "1746520432",
            "amount": 70,
            "currency": "EUR",
            "status": "succeeded",
            "reason": "Paid by mistake",
            "metadata": {
                "foo": "bar"
            },
            "error_details": {
                "code": "",
                "message": ""
            },
            "created_at": "2025-05-06T08:33:52.433Z",
            "updated_at": "2025-05-06T08:33:53.474Z",
            "connector": "stripe",
            "profile_id": "pro_PiIU0MSYNuU5kS96H0kV",
            "merchant_connector_id": "mca_jQBjGAAN1BgqzXa0Gpoy",
            "connector_refund_reference_id": null
        }
    ]
}
```
- List refund by refund id API Call
```
curl --location --request GET 'http://localhost:8080/v2/refunds/list' \
--header 'x-profile-id: pro_PiIU0MSYNuU5kS96H0kV' \
--header 'Content-Type: application/json' \
--header 'Authorization: api-key=dev_nG2oG32R2dnadMOvkwfRyl0j1YB8uNf3t1FcDqhFgFxO7TtVS1kPQ8m4F3DEngVb' \
--data '{
    "refund_id":"12345_ref_0196a4ba41147d138621293a283b9d3e"
}'
```
- Response from the above call
```
{
    "count": 1,
    "total_count": 1,
    "data": [
        {
            "id": "12345_ref_0196a4ba41147d138621293a283b9d3e",
            "payment_id": "12345_pay_0196a4ba295477d0a4156e65135d8fb2",
            "merchant_reference_id": "1746520392",
            "amount": 1000,
            "currency": "BBD",
            "status": "succeeded",
            "reason": "Paid by mistake",
            "metadata": {
                "foo": "bar"
            },
            "error_details": {
                "code": "",
                "message": ""
            },
            "created_at": "2025-05-06T08:33:11.959Z",
            "updated_at": "2025-05-06T08:33:13.047Z",
            "connector": "stripe",
            "profile_id": "pro_PiIU0MSYNuU5kS96H0kV",
            "merchant_connector_id": "mca_jQBjGAAN1BgqzXa0Gpoy",
            "connector_refund_reference_id": null
        }
    ]
}
```
- List refund by currencies API Call
```
curl --location --request GET 'http://localhost:8080/v2/refunds/list' \
--header 'x-profile-id: pro_PiIU0MSYNuU5kS96H0kV' \
--header 'Content-Type: application/json' \
--header 'Authorization: api-key=dev_nG2oG32R2dnadMOvkwfRyl0j1YB8uNf3t1FcDqhFgFxO7TtVS1kPQ8m4F3DEngVb' \
--data '{
    "currency":["EUR","USD"]
}'
```
- Response from the above call
```
{
    "count": 3,
    "total_count": 3,
    "data": [
        {
            "id": "12345_ref_0196a4badf2f7ed2b16c4ecaf1c814db",
            "payment_id": "12345_pay_0196a4baa48772909f72eac2cd863d7b",
            "merchant_reference_id": "1746520432",
            "amount": 70,
            "currency": "EUR",
            "status": "succeeded",
            "reason": "Paid by mistake",
            "metadata": {
                "foo": "bar"
            },
            "error_details": {
                "code": "",
                "message": ""
            },
            "created_at": "2025-05-06T08:33:52.433Z",
            "updated_at": "2025-05-06T08:33:53.474Z",
            "connector": "stripe",
            "profile_id": "pro_PiIU0MSYNuU5kS96H0kV",
            "merchant_connector_id": "mca_jQBjGAAN1BgqzXa0Gpoy",
            "connector_refund_reference_id": null
        },
        {
            "id": "12345_ref_0196a4b978637810b17d2f855afb0305",
            "payment_id": "12345_pay_0196a4b9609179429e3e0decc9892a55",
            "merchant_reference_id": "1746520341",
            "amount": 100,
            "currency": "USD",
            "status": "succeeded",
            "reason": "Paid by mistake",
            "metadata": {
                "foo": "bar"
            },
            "error_details": {
                "code": "",
                "message": ""
            },
            "created_at": "2025-05-06T08:32:20.588Z",
            "updated_at": "2025-05-06T08:32:21.669Z",
            "connector": "stripe",
            "profile_id": "pro_PiIU0MSYNuU5kS96H0kV",
            "merchant_connector_id": "mca_jQBjGAAN1BgqzXa0Gpoy",
            "connector_refund_reference_id": null
        },
        {
            "id": "12345_ref_01969f96f27e7213b4257eb238ec3a46",
            "payment_id": "12345_pay_01969f96e02a7dd3a183592c24c6693e",
            "merchant_reference_id": "1746434192",
            "amount": 100,
            "currency": "USD",
            "status": "succeeded",
            "reason": "Paid by mistake",
            "metadata": {
                "foo": "bar"
            },
            "error_details": {
                "code": "",
                "message": ""
            },
            "created_at": "2025-05-05T08:36:32.015Z",
            "updated_at": "2025-05-05T08:36:46.580Z",
            "connector": "stripe",
            "profile_id": "pro_PiIU0MSYNuU5kS96H0kV",
            "merchant_connector_id": "mca_jQBjGAAN1BgqzXa0Gpoy",
            "connector_refund_reference_id": null
        }
    ]
}
```

## Checklist
<!-- Put an `x` in the boxes that apply -->

- [x] I formatted the code `cargo +nightly fmt --all`
- [x] I addressed lints thrown by `cargo clippy`
- [x] I reviewed the submitted code
- [ ] I added unit tests for my changes where possible
