Bug: [FEATURE] allow payout_id to be configurable



### Feature Description

`payout_id` is an unique identifier for payout resource which is auto generated during payout creation. This field needs to be configurable during payout creation by accepting in the API.

Along with this, a `merchant_order_reference_id` needs to be added which is an unique identifier for the consumer of the API.

### Possible Implementation

Below changes are needed in payout flow across different components -

- Add `payout_id` to API models
- Add `PayoutId` domain type
- Add `merchant_order_reference_id` to API and diesel models
- Add DB queries for fetching orders using `merchant_order_reference_id`
- Add `merchant_order_reference_id` as a filter for OLAP APIs
- Use uuidv4 for payout ID creation for Wise

### Have you spent some time checking if this feature request has been raised before?

- [x] I checked and didn't find a similar issue

### Have you read the Contributing Guidelines?

- [x] I have read the [Contributing Guidelines](https://github.com/juspay/hyperswitch/blob/main/docs/CONTRIBUTING.md)

### Are you willing to submit a PR?

None
diff --git a/crates/api_models/src/events/payouts.rs b/crates/api_models/src/events/payouts.rs
index a08f3ed94a8..b7eded681a3 100644
--- a/crates/api_models/src/events/payouts.rs
+++ b/crates/api_models/src/events/payouts.rs
@@ -9,7 +9,7 @@ use crate::payouts::{
 impl ApiEventMetric for PayoutRetrieveRequest {
     fn get_api_event_type(&self) -> Option<ApiEventsType> {
         Some(ApiEventsType::Payout {
-            payout_id: self.payout_id.clone(),
+            payout_id: self.payout_id.to_owned(),
         })
     }
 }
@@ -17,7 +17,7 @@ impl ApiEventMetric for PayoutRetrieveRequest {
 impl ApiEventMetric for PayoutCreateRequest {
     fn get_api_event_type(&self) -> Option<ApiEventsType> {
         self.payout_id.as_ref().map(|id| ApiEventsType::Payout {
-            payout_id: id.clone(),
+            payout_id: id.to_owned(),
         })
     }
 }
@@ -25,7 +25,7 @@ impl ApiEventMetric for PayoutCreateRequest {
 impl ApiEventMetric for PayoutCreateResponse {
     fn get_api_event_type(&self) -> Option<ApiEventsType> {
         Some(ApiEventsType::Payout {
-            payout_id: self.payout_id.clone(),
+            payout_id: self.payout_id.to_owned(),
         })
     }
 }
@@ -33,7 +33,7 @@ impl ApiEventMetric for PayoutCreateResponse {
 impl ApiEventMetric for PayoutActionRequest {
     fn get_api_event_type(&self) -> Option<ApiEventsType> {
         Some(ApiEventsType::Payout {
-            payout_id: self.payout_id.clone(),
+            payout_id: self.payout_id.to_owned(),
         })
     }
 }
@@ -65,7 +65,7 @@ impl ApiEventMetric for PayoutListFilters {
 impl ApiEventMetric for PayoutLinkInitiateRequest {
     fn get_api_event_type(&self) -> Option<ApiEventsType> {
         Some(ApiEventsType::Payout {
-            payout_id: self.payout_id.clone(),
+            payout_id: self.payout_id.to_owned(),
         })
     }
 }
diff --git a/crates/api_models/src/payouts.rs b/crates/api_models/src/payouts.rs
index cf07fd9e109..a6fdaa2b6e5 100644
--- a/crates/api_models/src/payouts.rs
+++ b/crates/api_models/src/payouts.rs
@@ -16,7 +16,7 @@ use utoipa::ToSchema;
 
 use crate::{enums as api_enums, payment_methods::RequiredFieldInfo, payments};
 
-#[derive(Debug, Deserialize, Serialize, Clone, ToSchema)]
+#[derive(Debug, Serialize, Clone, ToSchema)]
 pub enum PayoutRequest {
     PayoutActionRequest(PayoutActionRequest),
     PayoutCreateRequest(Box<PayoutCreateRequest>),
@@ -37,13 +37,17 @@ pub struct PayoutCreateRequest {
         example = "187282ab-40ef-47a9-9206-5099ba31e432"
     )]
     #[remove_in(PayoutsCreateRequest, PayoutUpdateRequest, PayoutConfirmRequest)]
-    pub payout_id: Option<String>, // TODO: #1321 https://github.com/juspay/hyperswitch/issues/1321
+    pub payout_id: Option<id_type::PayoutId>,
 
     /// This is an identifier for the merchant account. This is inferred from the API key provided during the request, **not required to be included in the Payout Create/Update Request.**
     #[schema(max_length = 255, value_type = Option<String>, example = "merchant_1668273825")]
     #[remove_in(PayoutsCreateRequest, PayoutUpdateRequest, PayoutConfirmRequest)]
     pub merchant_id: Option<id_type::MerchantId>,
 
+    /// Your unique identifier for this payout or order. This ID helps you reconcile payouts on your system. If provided, it is passed to the connector if supported.
+    #[schema(value_type = Option<String>, max_length = 255, example = "merchant_order_ref_123")]
+    pub merchant_order_reference_id: Option<String>,
+
     /// The payout amount. Amount for the payout in lowest denomination of the currency. (i.e) in cents for USD denomination, in paisa for INR denomination etc.,
     #[schema(value_type = Option<u64>, example = 1000)]
     #[mandatory_in(PayoutsCreateRequest = u64)]
@@ -399,13 +403,17 @@ pub struct PayoutCreateResponse {
         max_length = 30,
         example = "187282ab-40ef-47a9-9206-5099ba31e432"
     )]
-    pub payout_id: String, // TODO: Update this to PayoutIdType similar to PaymentIdType
+    pub payout_id: id_type::PayoutId,
 
     /// This is an identifier for the merchant account. This is inferred from the API key
     /// provided during the request
     #[schema(max_length = 255, value_type = String, example = "merchant_1668273825")]
     pub merchant_id: id_type::MerchantId,
 
+    /// Your unique identifier for this payout or order. This ID helps you reconcile payouts on your system. If provided, it is passed to the connector if supported.
+    #[schema(value_type = Option<String>, max_length = 255, example = "merchant_order_ref_123")]
+    pub merchant_order_reference_id: Option<String>,
+
     /// The payout amount. Amount for the payout in lowest denomination of the currency. (i.e) in cents for USD denomination, in paisa for INR denomination etc.,
     #[schema(value_type = i64, example = 1000)]
     pub amount: common_utils::types::MinorUnit,
@@ -638,7 +646,7 @@ pub struct PayoutRetrieveBody {
     pub merchant_id: Option<id_type::MerchantId>,
 }
 
-#[derive(Default, Debug, Serialize, ToSchema, Clone, Deserialize)]
+#[derive(Debug, Serialize, ToSchema, Clone, Deserialize)]
 pub struct PayoutRetrieveRequest {
     /// Unique identifier for the payout. This ensures idempotency for multiple payouts
     /// that have been done by a single merchant. This field is auto generated and is returned in the API response.
@@ -648,7 +656,7 @@ pub struct PayoutRetrieveRequest {
         max_length = 30,
         example = "187282ab-40ef-47a9-9206-5099ba31e432"
     )]
-    pub payout_id: String,
+    pub payout_id: id_type::PayoutId,
 
     /// `force_sync` with the connector to get payout details
     /// (defaults to false)
@@ -660,9 +668,7 @@ pub struct PayoutRetrieveRequest {
     pub merchant_id: Option<id_type::MerchantId>,
 }
 
-#[derive(
-    Default, Debug, Deserialize, Serialize, Clone, ToSchema, router_derive::PolymorphicSchema,
-)]
+#[derive(Debug, Serialize, Clone, ToSchema, router_derive::PolymorphicSchema)]
 #[generate_schemas(PayoutCancelRequest, PayoutFulfillRequest)]
 pub struct PayoutActionRequest {
     /// Unique identifier for the payout. This ensures idempotency for multiple payouts
@@ -673,8 +679,7 @@ pub struct PayoutActionRequest {
         max_length = 30,
         example = "187282ab-40ef-47a9-9206-5099ba31e432"
     )]
-    #[serde(skip_deserializing)]
-    pub payout_id: String,
+    pub payout_id: id_type::PayoutId,
 }
 
 #[derive(Default, Debug, ToSchema, Clone, Deserialize)]
@@ -722,12 +727,12 @@ pub struct PayoutListConstraints {
     pub customer_id: Option<id_type::CustomerId>,
 
     /// A cursor for use in pagination, fetch the next list after some object
-    #[schema(example = "pay_fafa124123")]
-    pub starting_after: Option<String>,
+    #[schema(example = "payout_fafa124123", value_type = Option<String>,)]
+    pub starting_after: Option<id_type::PayoutId>,
 
     /// A cursor for use in pagination, fetch the previous list before some object
-    #[schema(example = "pay_fafa124123")]
-    pub ending_before: Option<String>,
+    #[schema(example = "payout_fafa124123", value_type = Option<String>,)]
+    pub ending_before: Option<id_type::PayoutId>,
 
     /// limit on the number of objects to return
     #[schema(default = 10, maximum = 100)]
@@ -755,7 +760,10 @@ pub struct PayoutListFilterConstraints {
     max_length = 30,
     example = "187282ab-40ef-47a9-9206-5099ba31e432"
 )]
-    pub payout_id: Option<String>,
+    pub payout_id: Option<id_type::PayoutId>,
+    /// The merchant order reference ID for payout
+    #[schema(value_type = Option<String>, max_length = 255, example = "merchant_order_ref_123")]
+    pub merchant_order_reference_id: Option<String>,
     /// The identifier for business profile
     #[schema(value_type = Option<String>)]
     pub profile_id: Option<id_type::ProfileId>,
@@ -826,7 +834,8 @@ pub struct PayoutLinkResponse {
 pub struct PayoutLinkInitiateRequest {
     #[schema(value_type = String)]
     pub merchant_id: id_type::MerchantId,
-    pub payout_id: String,
+    #[schema(value_type = String)]
+    pub payout_id: id_type::PayoutId,
 }
 
 #[derive(Clone, Debug, serde::Serialize)]
@@ -834,7 +843,7 @@ pub struct PayoutLinkDetails {
     pub publishable_key: Secret<String>,
     pub client_secret: Secret<String>,
     pub payout_link_id: String,
-    pub payout_id: String,
+    pub payout_id: id_type::PayoutId,
     pub customer_id: id_type::CustomerId,
     #[serde(with = "common_utils::custom_serde::iso8601")]
     pub session_expiry: PrimitiveDateTime,
@@ -870,7 +879,7 @@ pub struct RequiredFieldsOverrideRequest {
 #[derive(Clone, Debug, serde::Serialize)]
 pub struct PayoutLinkStatusDetails {
     pub payout_link_id: String,
-    pub payout_id: String,
+    pub payout_id: id_type::PayoutId,
     pub customer_id: id_type::CustomerId,
     #[serde(with = "common_utils::custom_serde::iso8601")]
     pub session_expiry: PrimitiveDateTime,
diff --git a/crates/api_models/src/webhooks.rs b/crates/api_models/src/webhooks.rs
index 4667866073b..177310de18f 100644
--- a/crates/api_models/src/webhooks.rs
+++ b/crates/api_models/src/webhooks.rs
@@ -98,7 +98,7 @@ pub enum WebhookResponseTracker {
     },
     #[cfg(feature = "payouts")]
     Payout {
-        payout_id: String,
+        payout_id: common_utils::id_type::PayoutId,
         status: common_enums::PayoutStatus,
     },
     #[cfg(feature = "v1")]
diff --git a/crates/common_utils/src/events.rs b/crates/common_utils/src/events.rs
index 376b57fbb47..e1964296755 100644
--- a/crates/common_utils/src/events.rs
+++ b/crates/common_utils/src/events.rs
@@ -12,7 +12,7 @@ pub trait ApiEventMetric {
 #[serde(tag = "flow_type", rename_all = "snake_case")]
 pub enum ApiEventsType {
     Payout {
-        payout_id: String,
+        payout_id: id_type::PayoutId,
     },
     #[cfg(feature = "v1")]
     Payment {
diff --git a/crates/common_utils/src/id_type.rs b/crates/common_utils/src/id_type.rs
index d9daa5caf9d..6d73a90eab2 100644
--- a/crates/common_utils/src/id_type.rs
+++ b/crates/common_utils/src/id_type.rs
@@ -11,6 +11,7 @@ mod merchant;
 mod merchant_connector_account;
 mod organization;
 mod payment;
+mod payout;
 mod profile;
 mod profile_acquirer;
 mod refunds;
@@ -27,6 +28,7 @@ use diesel::{
     serialize::{Output, ToSql},
     sql_types,
 };
+pub use payout::PayoutId;
 use serde::{Deserialize, Serialize};
 use thiserror::Error;
 
diff --git a/crates/common_utils/src/id_type/payout.rs b/crates/common_utils/src/id_type/payout.rs
new file mode 100644
index 00000000000..8faa661d363
--- /dev/null
+++ b/crates/common_utils/src/id_type/payout.rs
@@ -0,0 +1,10 @@
+crate::id_type!(
+    PayoutId,
+    "A domain type for payout_id that can be used for payout ids"
+);
+crate::impl_id_type_methods!(PayoutId, "payout_id");
+crate::impl_debug_id_type!(PayoutId);
+crate::impl_try_from_cow_str_id_type!(PayoutId, "payout_id");
+crate::impl_generate_id_id_type!(PayoutId, "payout");
+crate::impl_queryable_id_type!(PayoutId);
+crate::impl_to_sql_from_sql_id_type!(PayoutId);
diff --git a/crates/common_utils/src/link_utils.rs b/crates/common_utils/src/link_utils.rs
index 2201000d062..89a52f44792 100644
--- a/crates/common_utils/src/link_utils.rs
+++ b/crates/common_utils/src/link_utils.rs
@@ -149,7 +149,7 @@ pub struct PayoutLinkData {
     /// Identifier for the customer
     pub customer_id: id_type::CustomerId,
     /// Identifier for the payouts resource
-    pub payout_id: String,
+    pub payout_id: id_type::PayoutId,
     /// Link to render the payout link
     pub link: url::Url,
     /// Client secret generated for authenticating frontend APIs
diff --git a/crates/diesel_models/src/events.rs b/crates/diesel_models/src/events.rs
index cbc39340dcf..bc9f0788113 100644
--- a/crates/diesel_models/src/events.rs
+++ b/crates/diesel_models/src/events.rs
@@ -74,7 +74,7 @@ pub enum EventMetadata {
         payment_id: common_utils::id_type::GlobalPaymentId,
     },
     Payout {
-        payout_id: String,
+        payout_id: common_utils::id_type::PayoutId,
     },
     #[cfg(feature = "v1")]
     Refund {
diff --git a/crates/diesel_models/src/generic_link.rs b/crates/diesel_models/src/generic_link.rs
index 28402692480..d6071268eae 100644
--- a/crates/diesel_models/src/generic_link.rs
+++ b/crates/diesel_models/src/generic_link.rs
@@ -156,7 +156,7 @@ pub struct PaymentMethodCollectLinkData {
 #[diesel(primary_key(link_id))]
 pub struct PayoutLink {
     pub link_id: String,
-    pub primary_reference: String,
+    pub primary_reference: common_utils::id_type::PayoutId,
     pub merchant_id: common_utils::id_type::MerchantId,
     #[serde(with = "common_utils::custom_serde::iso8601")]
     pub created_at: PrimitiveDateTime,
diff --git a/crates/diesel_models/src/payout_attempt.rs b/crates/diesel_models/src/payout_attempt.rs
index 0e12a9ed801..a908ef7bdf5 100644
--- a/crates/diesel_models/src/payout_attempt.rs
+++ b/crates/diesel_models/src/payout_attempt.rs
@@ -14,7 +14,7 @@ use crate::{enums as storage_enums, schema::payout_attempt};
 #[diesel(table_name = payout_attempt, primary_key(payout_attempt_id), check_for_backend(diesel::pg::Pg))]
 pub struct PayoutAttempt {
     pub payout_attempt_id: String,
-    pub payout_id: String,
+    pub payout_id: common_utils::id_type::PayoutId,
     pub customer_id: Option<common_utils::id_type::CustomerId>,
     pub merchant_id: common_utils::id_type::MerchantId,
     pub address_id: Option<String>,
@@ -37,6 +37,7 @@ pub struct PayoutAttempt {
     pub unified_code: Option<UnifiedCode>,
     pub unified_message: Option<UnifiedMessage>,
     pub additional_payout_method_data: Option<payout_method_utils::AdditionalPayoutMethodData>,
+    pub merchant_order_reference_id: Option<String>,
 }
 
 #[derive(
@@ -53,7 +54,7 @@ pub struct PayoutAttempt {
 #[diesel(table_name = payout_attempt)]
 pub struct PayoutAttemptNew {
     pub payout_attempt_id: String,
-    pub payout_id: String,
+    pub payout_id: common_utils::id_type::PayoutId,
     pub customer_id: Option<common_utils::id_type::CustomerId>,
     pub merchant_id: common_utils::id_type::MerchantId,
     pub address_id: Option<String>,
@@ -76,6 +77,7 @@ pub struct PayoutAttemptNew {
     pub unified_code: Option<UnifiedCode>,
     pub unified_message: Option<UnifiedMessage>,
     pub additional_payout_method_data: Option<payout_method_utils::AdditionalPayoutMethodData>,
+    pub merchant_order_reference_id: Option<String>,
 }
 
 #[derive(Debug, Clone, Serialize, Deserialize)]
@@ -128,6 +130,7 @@ pub struct PayoutAttemptUpdateInternal {
     pub unified_code: Option<UnifiedCode>,
     pub unified_message: Option<UnifiedMessage>,
     pub additional_payout_method_data: Option<payout_method_utils::AdditionalPayoutMethodData>,
+    pub merchant_order_reference_id: Option<String>,
 }
 
 impl Default for PayoutAttemptUpdateInternal {
@@ -150,6 +153,7 @@ impl Default for PayoutAttemptUpdateInternal {
             unified_code: None,
             unified_message: None,
             additional_payout_method_data: None,
+            merchant_order_reference_id: None,
         }
     }
 }
@@ -231,6 +235,7 @@ impl PayoutAttemptUpdate {
             unified_code,
             unified_message,
             additional_payout_method_data,
+            merchant_order_reference_id,
         } = self.into();
         PayoutAttempt {
             payout_token: payout_token.or(source.payout_token),
@@ -251,6 +256,8 @@ impl PayoutAttemptUpdate {
             unified_message: unified_message.or(source.unified_message),
             additional_payout_method_data: additional_payout_method_data
                 .or(source.additional_payout_method_data),
+            merchant_order_reference_id: merchant_order_reference_id
+                .or(source.merchant_order_reference_id),
             ..source
         }
     }
diff --git a/crates/diesel_models/src/payouts.rs b/crates/diesel_models/src/payouts.rs
index 8acdc4a0a5a..881875494ab 100644
--- a/crates/diesel_models/src/payouts.rs
+++ b/crates/diesel_models/src/payouts.rs
@@ -11,7 +11,7 @@ use crate::{enums as storage_enums, schema::payouts};
 )]
 #[diesel(table_name = payouts, primary_key(payout_id), check_for_backend(diesel::pg::Pg))]
 pub struct Payouts {
-    pub payout_id: String,
+    pub payout_id: common_utils::id_type::PayoutId,
     pub merchant_id: common_utils::id_type::MerchantId,
     pub customer_id: Option<common_utils::id_type::CustomerId>,
     pub address_id: Option<String>,
@@ -52,7 +52,7 @@ pub struct Payouts {
 )]
 #[diesel(table_name = payouts)]
 pub struct PayoutsNew {
-    pub payout_id: String,
+    pub payout_id: common_utils::id_type::PayoutId,
     pub merchant_id: common_utils::id_type::MerchantId,
     pub customer_id: Option<common_utils::id_type::CustomerId>,
     pub address_id: Option<String>,
diff --git a/crates/diesel_models/src/query/generic_link.rs b/crates/diesel_models/src/query/generic_link.rs
index 75f1fc8ded8..96966e14547 100644
--- a/crates/diesel_models/src/query/generic_link.rs
+++ b/crates/diesel_models/src/query/generic_link.rs
@@ -224,7 +224,11 @@ impl TryFrom<GenericLink> for PayoutLink {
 
         Ok(Self {
             link_id: db_val.link_id,
-            primary_reference: db_val.primary_reference,
+            primary_reference: common_utils::id_type::PayoutId::try_from(std::borrow::Cow::Owned(
+                db_val.primary_reference,
+            ))
+            .change_context(errors::ParsingError::UnknownError)
+            .attach_printable("Failed to parse PayoutId from primary_reference string")?,
             merchant_id: db_val.merchant_id,
             created_at: db_val.created_at,
             last_modified_at: db_val.last_modified_at,
diff --git a/crates/diesel_models/src/query/payout_attempt.rs b/crates/diesel_models/src/query/payout_attempt.rs
index 939336a223a..300b6fdb647 100644
--- a/crates/diesel_models/src/query/payout_attempt.rs
+++ b/crates/diesel_models/src/query/payout_attempt.rs
@@ -56,7 +56,7 @@ impl PayoutAttempt {
     pub async fn find_by_merchant_id_payout_id(
         conn: &PgPooledConn,
         merchant_id: &common_utils::id_type::MerchantId,
-        payout_id: &str,
+        payout_id: &common_utils::id_type::PayoutId,
     ) -> StorageResult<Self> {
         generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
             conn,
@@ -95,10 +95,24 @@ impl PayoutAttempt {
         .await
     }
 
+    pub async fn find_by_merchant_id_merchant_order_reference_id(
+        conn: &PgPooledConn,
+        merchant_id_input: &common_utils::id_type::MerchantId,
+        merchant_order_reference_id_input: &str,
+    ) -> StorageResult<Self> {
+        generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
+            conn,
+            dsl::merchant_id.eq(merchant_id_input.to_owned()).and(
+                dsl::merchant_order_reference_id.eq(merchant_order_reference_id_input.to_owned()),
+            ),
+        )
+        .await
+    }
+
     pub async fn update_by_merchant_id_payout_id(
         conn: &PgPooledConn,
         merchant_id: &common_utils::id_type::MerchantId,
-        payout_id: &str,
+        payout_id: &common_utils::id_type::PayoutId,
         payout: PayoutAttemptUpdate,
     ) -> StorageResult<Self> {
         generics::generic_update_with_results::<<Self as HasTable>::Table, _, _, _>(
@@ -152,7 +166,7 @@ impl PayoutAttempt {
             .map(|payout| {
                 format!(
                     "{}_{}",
-                    payout.payout_id.clone(),
+                    payout.payout_id.get_string_repr(),
                     payout.attempt_count.clone()
                 )
             })
@@ -160,8 +174,8 @@ impl PayoutAttempt {
 
         let active_payout_ids = payouts
             .iter()
-            .map(|payout| payout.payout_id.clone())
-            .collect::<Vec<String>>();
+            .map(|payout| payout.payout_id.to_owned())
+            .collect::<Vec<common_utils::id_type::PayoutId>>();
 
         let filter = <Self as HasTable>::table()
             .filter(dsl::merchant_id.eq(merchant_id.to_owned()))
diff --git a/crates/diesel_models/src/query/payouts.rs b/crates/diesel_models/src/query/payouts.rs
index 25c7bfc4f7e..e7d06d05ffe 100644
--- a/crates/diesel_models/src/query/payouts.rs
+++ b/crates/diesel_models/src/query/payouts.rs
@@ -47,7 +47,7 @@ impl Payouts {
     pub async fn find_by_merchant_id_payout_id(
         conn: &PgPooledConn,
         merchant_id: &common_utils::id_type::MerchantId,
-        payout_id: &str,
+        payout_id: &common_utils::id_type::PayoutId,
     ) -> StorageResult<Self> {
         generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
             conn,
@@ -61,7 +61,7 @@ impl Payouts {
     pub async fn update_by_merchant_id_payout_id(
         conn: &PgPooledConn,
         merchant_id: &common_utils::id_type::MerchantId,
-        payout_id: &str,
+        payout_id: &common_utils::id_type::PayoutId,
         payout: PayoutsUpdate,
     ) -> StorageResult<Self> {
         generics::generic_update_with_results::<<Self as HasTable>::Table, _, _, _>(
@@ -82,7 +82,7 @@ impl Payouts {
     pub async fn find_optional_by_merchant_id_payout_id(
         conn: &PgPooledConn,
         merchant_id: &common_utils::id_type::MerchantId,
-        payout_id: &str,
+        payout_id: &common_utils::id_type::PayoutId,
     ) -> StorageResult<Option<Self>> {
         generics::generic_find_one_optional::<<Self as HasTable>::Table, _, _>(
             conn,
@@ -96,7 +96,7 @@ impl Payouts {
     pub async fn get_total_count_of_payouts(
         conn: &PgPooledConn,
         merchant_id: &common_utils::id_type::MerchantId,
-        active_payout_ids: &[String],
+        active_payout_ids: &[common_utils::id_type::PayoutId],
         connector: Option<Vec<String>>,
         currency: Option<Vec<enums::Currency>>,
         status: Option<Vec<enums::PayoutStatus>>,
@@ -106,7 +106,7 @@ impl Payouts {
             .inner_join(payout_attempt::table.on(payout_attempt::dsl::payout_id.eq(dsl::payout_id)))
             .count()
             .filter(dsl::merchant_id.eq(merchant_id.to_owned()))
-            .filter(dsl::payout_id.eq_any(active_payout_ids.to_owned()))
+            .filter(dsl::payout_id.eq_any(active_payout_ids.to_vec()))
             .into_boxed();
 
         if let Some(connector) = connector {
diff --git a/crates/diesel_models/src/schema.rs b/crates/diesel_models/src/schema.rs
index e303dfc553f..1629780458c 100644
--- a/crates/diesel_models/src/schema.rs
+++ b/crates/diesel_models/src/schema.rs
@@ -1151,7 +1151,7 @@ diesel::table! {
     use diesel::sql_types::*;
     use crate::enums::diesel_exports::*;
 
-    payout_attempt (payout_attempt_id) {
+    payout_attempt (merchant_id, payout_attempt_id) {
         #[max_length = 64]
         payout_attempt_id -> Varchar,
         #[max_length = 64]
@@ -1188,6 +1188,8 @@ diesel::table! {
         #[max_length = 1024]
         unified_message -> Nullable<Varchar>,
         additional_payout_method_data -> Nullable<Jsonb>,
+        #[max_length = 255]
+        merchant_order_reference_id -> Nullable<Varchar>,
     }
 }
 
@@ -1195,7 +1197,7 @@ diesel::table! {
     use diesel::sql_types::*;
     use crate::enums::diesel_exports::*;
 
-    payouts (payout_id) {
+    payouts (merchant_id, payout_id) {
         #[max_length = 64]
         payout_id -> Varchar,
         #[max_length = 64]
diff --git a/crates/diesel_models/src/schema_v2.rs b/crates/diesel_models/src/schema_v2.rs
index 9ba5cdfaff6..c931cec977a 100644
--- a/crates/diesel_models/src/schema_v2.rs
+++ b/crates/diesel_models/src/schema_v2.rs
@@ -1090,7 +1090,7 @@ diesel::table! {
     use diesel::sql_types::*;
     use crate::enums::diesel_exports::*;
 
-    payout_attempt (payout_attempt_id) {
+    payout_attempt (merchant_id, payout_attempt_id) {
         #[max_length = 64]
         payout_attempt_id -> Varchar,
         #[max_length = 64]
@@ -1127,6 +1127,8 @@ diesel::table! {
         #[max_length = 1024]
         unified_message -> Nullable<Varchar>,
         additional_payout_method_data -> Nullable<Jsonb>,
+        #[max_length = 255]
+        merchant_order_reference_id -> Nullable<Varchar>,
     }
 }
 
@@ -1134,7 +1136,7 @@ diesel::table! {
     use diesel::sql_types::*;
     use crate::enums::diesel_exports::*;
 
-    payouts (payout_id) {
+    payouts (merchant_id, payout_id) {
         #[max_length = 64]
         payout_id -> Varchar,
         #[max_length = 64]
diff --git a/crates/hyperswitch_connectors/src/connectors/adyen/transformers.rs b/crates/hyperswitch_connectors/src/connectors/adyen/transformers.rs
index 8eec0abb9fc..8f4d667711b 100644
--- a/crates/hyperswitch_connectors/src/connectors/adyen/transformers.rs
+++ b/crates/hyperswitch_connectors/src/connectors/adyen/transformers.rs
@@ -5351,7 +5351,7 @@ impl<F> TryFrom<&AdyenRouterData<&PayoutsRouterData<F>>> for AdyenPayoutCreateRe
                     },
                     merchant_account,
                     payment_data: PayoutPaymentMethodData::PayoutWalletData(payout_wallet),
-                    reference: item.router_data.request.payout_id.to_owned(),
+                    reference: item.router_data.connector_request_reference_id.clone(),
                     shopper_reference: item.router_data.merchant_id.get_string_repr().to_owned(),
                     shopper_email: customer_email,
                     shopper_name: ShopperName {
diff --git a/crates/hyperswitch_connectors/src/connectors/adyenplatform/transformers/payouts.rs b/crates/hyperswitch_connectors/src/connectors/adyenplatform/transformers/payouts.rs
index 7262ec9f3ff..82c9afadeed 100644
--- a/crates/hyperswitch_connectors/src/connectors/adyenplatform/transformers/payouts.rs
+++ b/crates/hyperswitch_connectors/src/connectors/adyenplatform/transformers/payouts.rs
@@ -361,7 +361,7 @@ impl<F> TryFrom<&AdyenPlatformRouterData<&types::PayoutsRouterData<F>>> for Adye
             counterparty,
             priority,
             reference: item.router_data.connector_request_reference_id.clone(),
-            reference_for_beneficiary: request.payout_id.clone(),
+            reference_for_beneficiary: item.router_data.connector_request_reference_id.clone(),
             description: item.router_data.description.clone(),
         })
     }
diff --git a/crates/hyperswitch_connectors/src/connectors/cybersource/transformers.rs b/crates/hyperswitch_connectors/src/connectors/cybersource/transformers.rs
index 6b4101a15d2..09b468c3caf 100644
--- a/crates/hyperswitch_connectors/src/connectors/cybersource/transformers.rs
+++ b/crates/hyperswitch_connectors/src/connectors/cybersource/transformers.rs
@@ -3958,7 +3958,7 @@ impl TryFrom<&CybersourceRouterData<&PayoutsRouterData<PoFulfill>>>
         match payout_type {
             enums::PayoutType::Card => {
                 let client_reference_information = ClientReferenceInformation {
-                    code: Some(item.router_data.request.payout_id.clone()),
+                    code: Some(item.router_data.connector_request_reference_id.clone()),
                 };
 
                 let order_information = OrderInformation {
@@ -3974,7 +3974,7 @@ impl TryFrom<&CybersourceRouterData<&PayoutsRouterData<PoFulfill>>>
                     CybersourceRecipientInfo::try_from((billing_address, phone_address))?;
 
                 let sender_information = CybersourceSenderInfo {
-                    reference_number: item.router_data.request.payout_id.clone(),
+                    reference_number: item.router_data.connector_request_reference_id.clone(),
                     account: CybersourceAccountInfo {
                         funds_source: CybersourcePayoutFundSourceType::Disbursement,
                     },
diff --git a/crates/hyperswitch_connectors/src/connectors/nomupay/transformers.rs b/crates/hyperswitch_connectors/src/connectors/nomupay/transformers.rs
index 9a1a50e8642..3eab923ff16 100644
--- a/crates/hyperswitch_connectors/src/connectors/nomupay/transformers.rs
+++ b/crates/hyperswitch_connectors/src/connectors/nomupay/transformers.rs
@@ -385,7 +385,7 @@ impl<F> TryFrom<&PayoutsRouterData<F>> for OnboardSubAccountRequest {
         match payout_type {
             Some(common_enums::PayoutType::Bank) => Ok(Self {
                 account_id: nomupay_auth_type.eid,
-                client_sub_account_id: Secret::new(request.payout_id),
+                client_sub_account_id: Secret::new(item.connector_request_reference_id.clone()),
                 profile,
             }),
             _ => Err(errors::ConnectorError::NotImplemented(
@@ -498,7 +498,7 @@ impl<F> TryFrom<(&PayoutsRouterData<F>, FloatMajorUnit)> for NomupayPaymentReque
         Ok(Self {
             source_id: nomupay_auth_type.eid,
             destination_id: Secret::new(destination),
-            payment_reference: item.request.clone().payout_id,
+            payment_reference: item.connector_request_reference_id.clone(),
             amount,
             currency_code: item.request.destination_currency,
             purpose: PURPOSE_OF_PAYMENT_IS_OTHER.to_string(),
diff --git a/crates/hyperswitch_connectors/src/connectors/paypal/transformers.rs b/crates/hyperswitch_connectors/src/connectors/paypal/transformers.rs
index 6b56ccea208..18c7ac02e71 100644
--- a/crates/hyperswitch_connectors/src/connectors/paypal/transformers.rs
+++ b/crates/hyperswitch_connectors/src/connectors/paypal/transformers.rs
@@ -2309,7 +2309,7 @@ impl TryFrom<&PaypalRouterData<&PayoutsRouterData<PoFulfill>>> for PaypalFulfill
         let item_data = PaypalPayoutItem::try_from(item)?;
         Ok(Self {
             sender_batch_header: PayoutBatchHeader {
-                sender_batch_id: item.router_data.request.payout_id.to_owned(),
+                sender_batch_id: item.router_data.connector_request_reference_id.to_owned(),
             },
             items: vec![item_data],
         })
diff --git a/crates/hyperswitch_connectors/src/connectors/stripe/transformers/connect.rs b/crates/hyperswitch_connectors/src/connectors/stripe/transformers/connect.rs
index bb8d4509dac..321aad76551 100644
--- a/crates/hyperswitch_connectors/src/connectors/stripe/transformers/connect.rs
+++ b/crates/hyperswitch_connectors/src/connectors/stripe/transformers/connect.rs
@@ -216,7 +216,7 @@ impl<F> TryFrom<&PayoutsRouterData<F>> for StripeConnectPayoutCreateRequest {
             amount: request.amount,
             currency: request.destination_currency,
             destination: connector_customer_id,
-            transfer_group: request.payout_id,
+            transfer_group: item.connector_request_reference_id.clone(),
         })
     }
 }
diff --git a/crates/hyperswitch_connectors/src/connectors/wise/transformers.rs b/crates/hyperswitch_connectors/src/connectors/wise/transformers.rs
index 1a9ff7bf89f..f844e15a488 100644
--- a/crates/hyperswitch_connectors/src/connectors/wise/transformers.rs
+++ b/crates/hyperswitch_connectors/src/connectors/wise/transformers.rs
@@ -510,7 +510,7 @@ impl<F> TryFrom<&PayoutsRouterData<F>> for WisePayoutCreateRequest {
                 Ok(Self {
                     target_account,
                     quote_uuid,
-                    customer_transaction_id: request.payout_id,
+                    customer_transaction_id: request.payout_id.get_string_repr().to_string(),
                     details: wise_transfer_details,
                 })
             }
diff --git a/crates/hyperswitch_domain_models/src/errors/api_error_response.rs b/crates/hyperswitch_domain_models/src/errors/api_error_response.rs
index 21accfa22c5..2924093da3a 100644
--- a/crates/hyperswitch_domain_models/src/errors/api_error_response.rs
+++ b/crates/hyperswitch_domain_models/src/errors/api_error_response.rs
@@ -79,8 +79,10 @@ pub enum ApiErrorResponse {
     DuplicatePayment {
         payment_id: common_utils::id_type::PaymentId,
     },
-    #[error(error_type = ErrorType::DuplicateRequest, code = "HE_01", message = "The payout with the specified payout_id '{payout_id}' already exists in our records")]
-    DuplicatePayout { payout_id: String },
+    #[error(error_type = ErrorType::DuplicateRequest, code = "HE_01", message = "The payout with the specified payout_id '{payout_id:?}' already exists in our records")]
+    DuplicatePayout {
+        payout_id: common_utils::id_type::PayoutId,
+    },
     #[error(error_type = ErrorType::DuplicateRequest, code = "HE_01", message = "The config with the specified key already exists in our records")]
     DuplicateConfig,
     #[error(error_type = ErrorType::ObjectNotFound, code = "HE_02", message = "Refund does not exist in our records")]
@@ -403,7 +405,7 @@ impl ErrorSwitch<api_models::errors::types::ApiErrorResponse> for ApiErrorRespon
                 AER::BadRequest(ApiError::new("HE", 1, "The payment with the specified payment_id already exists in our records", Some(Extra {reason: Some(format!("{payment_id:?} already exists")), ..Default::default()})))
             }
             Self::DuplicatePayout { payout_id } => {
-                AER::BadRequest(ApiError::new("HE", 1, format!("The payout with the specified payout_id '{payout_id}' already exists in our records"), None))
+                AER::BadRequest(ApiError::new("HE", 1, format!("The payout with the specified payout_id '{payout_id:?}' already exists in our records"), None))
             }
             Self::DuplicateConfig => {
                 AER::BadRequest(ApiError::new("HE", 1, "The config with the specified key already exists in our records", None))
diff --git a/crates/hyperswitch_domain_models/src/payouts.rs b/crates/hyperswitch_domain_models/src/payouts.rs
index 8c6d751ebec..9a40dc119c0 100644
--- a/crates/hyperswitch_domain_models/src/payouts.rs
+++ b/crates/hyperswitch_domain_models/src/payouts.rs
@@ -7,7 +7,7 @@ use common_utils::{consts, id_type};
 use time::PrimitiveDateTime;
 
 pub enum PayoutFetchConstraints {
-    Single { payout_id: String },
+    Single { payout_id: id_type::PayoutId },
     List(Box<PayoutListParams>),
 }
 
@@ -21,10 +21,11 @@ pub struct PayoutListParams {
     pub payout_method: Option<Vec<common_enums::PayoutType>>,
     pub profile_id: Option<id_type::ProfileId>,
     pub customer_id: Option<id_type::CustomerId>,
-    pub starting_after_id: Option<String>,
-    pub ending_before_id: Option<String>,
+    pub starting_after_id: Option<id_type::PayoutId>,
+    pub ending_before_id: Option<id_type::PayoutId>,
     pub entity_type: Option<common_enums::PayoutEntityType>,
     pub limit: Option<u32>,
+    pub merchant_order_reference_id: Option<String>,
 }
 
 impl From<api_models::payouts::PayoutListConstraints> for PayoutFetchConstraints {
@@ -44,6 +45,7 @@ impl From<api_models::payouts::PayoutListConstraints> for PayoutFetchConstraints
             starting_after_id: value.starting_after,
             ending_before_id: value.ending_before,
             entity_type: None,
+            merchant_order_reference_id: None,
             limit: Some(std::cmp::min(
                 value.limit,
                 consts::PAYOUTS_LIST_MAX_LIMIT_GET,
@@ -67,6 +69,7 @@ impl From<common_utils::types::TimeRange> for PayoutFetchConstraints {
             starting_after_id: None,
             ending_before_id: None,
             entity_type: None,
+            merchant_order_reference_id: None,
             limit: None,
         }))
     }
@@ -90,6 +93,7 @@ impl From<api_models::payouts::PayoutListFilterConstraints> for PayoutFetchConst
                 starting_after_id: None,
                 ending_before_id: None,
                 entity_type: value.entity_type,
+                merchant_order_reference_id: value.merchant_order_reference_id,
                 limit: Some(std::cmp::min(
                     value.limit,
                     consts::PAYOUTS_LIST_MAX_LIMIT_POST,
diff --git a/crates/hyperswitch_domain_models/src/payouts/payout_attempt.rs b/crates/hyperswitch_domain_models/src/payouts/payout_attempt.rs
index dcb61bddc97..b6293faa841 100644
--- a/crates/hyperswitch_domain_models/src/payouts/payout_attempt.rs
+++ b/crates/hyperswitch_domain_models/src/payouts/payout_attempt.rs
@@ -42,6 +42,13 @@ pub trait PayoutAttemptInterface {
         _storage_scheme: MerchantStorageScheme,
     ) -> error_stack::Result<PayoutAttempt, Self::Error>;
 
+    async fn find_payout_attempt_by_merchant_id_merchant_order_reference_id(
+        &self,
+        _merchant_id: &id_type::MerchantId,
+        _merchant_order_reference_id: &str,
+        _storage_scheme: MerchantStorageScheme,
+    ) -> error_stack::Result<PayoutAttempt, Self::Error>;
+
     async fn get_filters_for_payouts(
         &self,
         _payout: &[Payouts],
@@ -61,7 +68,7 @@ pub struct PayoutListFilters {
 #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
 pub struct PayoutAttempt {
     pub payout_attempt_id: String,
-    pub payout_id: String,
+    pub payout_id: id_type::PayoutId,
     pub customer_id: Option<id_type::CustomerId>,
     pub merchant_id: id_type::MerchantId,
     pub address_id: Option<String>,
@@ -84,12 +91,13 @@ pub struct PayoutAttempt {
     pub unified_code: Option<UnifiedCode>,
     pub unified_message: Option<UnifiedMessage>,
     pub additional_payout_method_data: Option<payout_method_utils::AdditionalPayoutMethodData>,
+    pub merchant_order_reference_id: Option<String>,
 }
 
 #[derive(Clone, Debug, PartialEq)]
 pub struct PayoutAttemptNew {
     pub payout_attempt_id: String,
-    pub payout_id: String,
+    pub payout_id: id_type::PayoutId,
     pub customer_id: Option<id_type::CustomerId>,
     pub merchant_id: id_type::MerchantId,
     pub address_id: Option<String>,
@@ -110,6 +118,7 @@ pub struct PayoutAttemptNew {
     pub unified_code: Option<UnifiedCode>,
     pub unified_message: Option<UnifiedMessage>,
     pub additional_payout_method_data: Option<payout_method_utils::AdditionalPayoutMethodData>,
+    pub merchant_order_reference_id: Option<String>,
 }
 
 #[derive(Debug, Clone)]
diff --git a/crates/hyperswitch_domain_models/src/payouts/payouts.rs b/crates/hyperswitch_domain_models/src/payouts/payouts.rs
index 7c34b3c26df..1d41a5f9a32 100644
--- a/crates/hyperswitch_domain_models/src/payouts/payouts.rs
+++ b/crates/hyperswitch_domain_models/src/payouts/payouts.rs
@@ -20,7 +20,7 @@ pub trait PayoutsInterface {
     async fn find_payout_by_merchant_id_payout_id(
         &self,
         _merchant_id: &id_type::MerchantId,
-        _payout_id: &str,
+        _payout_id: &id_type::PayoutId,
         _storage_scheme: MerchantStorageScheme,
     ) -> error_stack::Result<Payouts, Self::Error>;
 
@@ -35,7 +35,7 @@ pub trait PayoutsInterface {
     async fn find_optional_payout_by_merchant_id_payout_id(
         &self,
         _merchant_id: &id_type::MerchantId,
-        _payout_id: &str,
+        _payout_id: &id_type::PayoutId,
         _storage_scheme: MerchantStorageScheme,
     ) -> error_stack::Result<Option<Payouts>, Self::Error>;
 
@@ -76,7 +76,7 @@ pub trait PayoutsInterface {
     async fn get_total_count_of_filtered_payouts(
         &self,
         _merchant_id: &id_type::MerchantId,
-        _active_payout_ids: &[String],
+        _active_payout_ids: &[id_type::PayoutId],
         _connector: Option<Vec<api_models::enums::PayoutConnectors>>,
         _currency: Option<Vec<storage_enums::Currency>>,
         _status: Option<Vec<storage_enums::PayoutStatus>>,
@@ -88,12 +88,12 @@ pub trait PayoutsInterface {
         &self,
         _merchant_id: &id_type::MerchantId,
         _constraints: &PayoutFetchConstraints,
-    ) -> error_stack::Result<Vec<String>, Self::Error>;
+    ) -> error_stack::Result<Vec<id_type::PayoutId>, Self::Error>;
 }
 
 #[derive(Clone, Debug, Eq, PartialEq)]
 pub struct Payouts {
-    pub payout_id: String,
+    pub payout_id: id_type::PayoutId,
     pub merchant_id: id_type::MerchantId,
     pub customer_id: Option<id_type::CustomerId>,
     pub address_id: Option<String>,
@@ -121,7 +121,7 @@ pub struct Payouts {
 
 #[derive(Clone, Debug, Eq, PartialEq)]
 pub struct PayoutsNew {
-    pub payout_id: String,
+    pub payout_id: id_type::PayoutId,
     pub merchant_id: id_type::MerchantId,
     pub customer_id: Option<id_type::CustomerId>,
     pub address_id: Option<String>,
diff --git a/crates/hyperswitch_domain_models/src/router_request_types.rs b/crates/hyperswitch_domain_models/src/router_request_types.rs
index e811b872c8c..c7987122ead 100644
--- a/crates/hyperswitch_domain_models/src/router_request_types.rs
+++ b/crates/hyperswitch_domain_models/src/router_request_types.rs
@@ -890,7 +890,7 @@ pub struct UploadFileRequestData {
 #[cfg(feature = "payouts")]
 #[derive(Debug, Clone)]
 pub struct PayoutsData {
-    pub payout_id: String,
+    pub payout_id: id_type::PayoutId,
     pub amount: i64,
     pub connector_payout_id: Option<String>,
     pub destination_currency: storage_enums::Currency,
diff --git a/crates/router/src/compatibility/stripe/errors.rs b/crates/router/src/compatibility/stripe/errors.rs
index 58f707b2db6..83f7fe69053 100644
--- a/crates/router/src/compatibility/stripe/errors.rs
+++ b/crates/router/src/compatibility/stripe/errors.rs
@@ -1,4 +1,4 @@
-use common_utils::errors::ErrorSwitch;
+use common_utils::{errors::ErrorSwitch, id_type};
 use hyperswitch_domain_models::errors::api_error_response as errors;
 
 use crate::core::errors::CustomersErrorResponse;
@@ -133,7 +133,7 @@ pub enum StripeErrorCode {
     EventNotFound,
 
     #[error(error_type = StripeErrorType::InvalidRequestError, code = "token_already_used", message = "Duplicate payout request")]
-    DuplicatePayout { payout_id: String },
+    DuplicatePayout { payout_id: id_type::PayoutId },
 
     #[error(error_type = StripeErrorType::InvalidRequestError, code = "parameter_missing", message = "Return url is not available")]
     ReturnUrlUnavailable,
@@ -209,9 +209,7 @@ pub enum StripeErrorCode {
     PaymentIntentMandateInvalid { message: String },
 
     #[error(error_type = StripeErrorType::InvalidRequestError, code = "", message = "The payment with the specified payment_id already exists in our records.")]
-    DuplicatePayment {
-        payment_id: common_utils::id_type::PaymentId,
-    },
+    DuplicatePayment { payment_id: id_type::PaymentId },
 
     #[error(error_type = StripeErrorType::ConnectorError, code = "", message = "{code}: {message}")]
     ExternalConnectorError {
diff --git a/crates/router/src/compatibility/stripe/webhooks.rs b/crates/router/src/compatibility/stripe/webhooks.rs
index f5dde401b35..3bd0f83b7db 100644
--- a/crates/router/src/compatibility/stripe/webhooks.rs
+++ b/crates/router/src/compatibility/stripe/webhooks.rs
@@ -4,9 +4,12 @@ use api_models::{
     enums::{Currency, DisputeStatus, MandateStatus},
     webhooks::{self as api},
 };
-#[cfg(feature = "payouts")]
-use common_utils::pii::{self, Email};
 use common_utils::{crypto::SignMessage, date_time, ext_traits::Encode};
+#[cfg(feature = "payouts")]
+use common_utils::{
+    id_type,
+    pii::{self, Email},
+};
 use error_stack::ResultExt;
 use router_env::logger;
 use serde::Serialize;
@@ -94,7 +97,7 @@ pub struct StripeDisputeResponse {
     pub id: String,
     pub amount: String,
     pub currency: Currency,
-    pub payment_intent: common_utils::id_type::PaymentId,
+    pub payment_intent: id_type::PaymentId,
     pub reason: Option<String>,
     pub status: StripeDisputeStatus,
 }
@@ -110,7 +113,7 @@ pub struct StripeMandateResponse {
 #[cfg(feature = "payouts")]
 #[derive(Clone, Serialize, Debug)]
 pub struct StripePayoutResponse {
-    pub id: String,
+    pub id: id_type::PayoutId,
     pub amount: i64,
     pub currency: String,
     pub payout_type: Option<common_enums::PayoutType>,
diff --git a/crates/router/src/core/payments/routing.rs b/crates/router/src/core/payments/routing.rs
index 4cc667bdc8a..7126ed9617e 100644
--- a/crates/router/src/core/payments/routing.rs
+++ b/crates/router/src/core/payments/routing.rs
@@ -483,9 +483,11 @@ pub async fn perform_static_routing_v1(
             .get_string_repr()
             .to_string(),
         #[cfg(feature = "payouts")]
-        routing::TransactionData::Payout(payout_data) => {
-            payout_data.payout_attempt.payout_id.clone()
-        }
+        routing::TransactionData::Payout(payout_data) => payout_data
+            .payout_attempt
+            .payout_id
+            .get_string_repr()
+            .to_string(),
     };
 
     let routing_events_wrapper = utils::RoutingEventsWrapper::new(
diff --git a/crates/router/src/core/payout_link.rs b/crates/router/src/core/payout_link.rs
index 8c234889630..1d224ffbd79 100644
--- a/crates/router/src/core/payout_link.rs
+++ b/crates/router/src/core/payout_link.rs
@@ -22,6 +22,7 @@ use crate::{
     routes::{app::StorageInterface, SessionState},
     services,
     types::{api, domain, transformers::ForeignFrom},
+    utils::get_payout_attempt_id,
 };
 
 #[cfg(feature = "v2")]
@@ -56,7 +57,7 @@ pub async fn initiate_payout_link(
     let payout_attempt = db
         .find_payout_attempt_by_merchant_id_payout_attempt_id(
             merchant_id,
-            &format!("{}_{}", payout.payout_id, payout.attempt_count),
+            &get_payout_attempt_id(payout.payout_id.get_string_repr(), payout.attempt_count),
             merchant_context.get_merchant_account().storage_scheme,
         )
         .await
@@ -146,13 +147,11 @@ pub async fn initiate_payout_link(
                 .await
                 .change_context(errors::ApiErrorResponse::InvalidRequestData {
                     message: format!(
-                        "Customer [{}] not found for link_id - {}",
-                        payout_link.primary_reference, payout_link.link_id
+                        "Customer [{:?}] not found for link_id - {}",
+                        customer_id, payout_link.link_id
                     ),
                 })
-                .attach_printable_lazy(|| {
-                    format!("customer [{}] not found", payout_link.primary_reference)
-                })?;
+                .attach_printable_lazy(|| format!("customer [{:?}] not found", customer_id))?;
             let address = payout
                 .address_id
                 .as_ref()
@@ -169,7 +168,7 @@ pub async fn initiate_payout_link(
                 .change_context(errors::ApiErrorResponse::InternalServerError)
                 .attach_printable_lazy(|| {
                     format!(
-                        "Failed while fetching address [id - {:?}] for payout [id - {}]",
+                        "Failed while fetching address [id - {:?}] for payout [id - {:?}]",
                         payout.address_id, payout.payout_id
                     )
                 })?;
@@ -227,7 +226,7 @@ pub async fn initiate_payout_link(
                 ),
                 client_secret: link_data.client_secret.clone(),
                 payout_link_id: payout_link.link_id,
-                payout_id: payout_link.primary_reference,
+                payout_id: payout_link.primary_reference.clone(),
                 customer_id: customer.customer_id,
                 session_expiry: payout_link.expiry,
                 return_url: payout_link
@@ -284,7 +283,7 @@ pub async fn initiate_payout_link(
                 .await?;
             let js_data = payouts::PayoutLinkStatusDetails {
                 payout_link_id: payout_link.link_id,
-                payout_id: payout_link.primary_reference,
+                payout_id: payout_link.primary_reference.clone(),
                 customer_id: link_data.customer_id,
                 session_expiry: payout_link.expiry,
                 return_url: payout_link
diff --git a/crates/router/src/core/payouts.rs b/crates/router/src/core/payouts.rs
index 495bea84eac..3cc996bd9a6 100644
--- a/crates/router/src/core/payouts.rs
+++ b/crates/router/src/core/payouts.rs
@@ -17,7 +17,7 @@ use common_enums::PayoutRetryType;
 use common_utils::{
     consts,
     ext_traits::{AsyncExt, ValueExt},
-    id_type::CustomerId,
+    id_type::{self, GenerateId},
     link_utils::{GenericLinkStatus, GenericLinkUiConfig, PayoutLinkData, PayoutLinkStatus},
     types::{MinorUnit, UnifiedCode, UnifiedMessage},
 };
@@ -73,7 +73,7 @@ pub struct PayoutData {
     pub payouts: storage::Payouts,
     pub payout_attempt: storage::PayoutAttempt,
     pub payout_method_data: Option<payouts::PayoutMethodData>,
-    pub profile_id: common_utils::id_type::ProfileId,
+    pub profile_id: id_type::ProfileId,
     pub should_terminate: bool,
     pub payout_link: Option<PayoutLink>,
     pub current_locale: String,
@@ -451,7 +451,7 @@ pub async fn payouts_update_core(
     if helpers::is_payout_terminal_state(status) || helpers::is_payout_initiated(status) {
         return Err(report!(errors::ApiErrorResponse::InvalidRequestData {
             message: format!(
-                "Payout {} cannot be updated for status {}",
+                "Payout {:?} cannot be updated for status {}",
                 payout_id, status
             ),
         }));
@@ -506,7 +506,7 @@ pub async fn payouts_update_core(
 pub async fn payouts_retrieve_core(
     state: SessionState,
     merchant_context: domain::MerchantContext,
-    profile_id: Option<common_utils::id_type::ProfileId>,
+    profile_id: Option<id_type::ProfileId>,
     req: payouts::PayoutRetrieveRequest,
 ) -> RouterResponse<payouts::PayoutCreateResponse> {
     let mut payout_data = Box::pin(make_payout_data(
@@ -568,7 +568,7 @@ pub async fn payouts_cancel_core(
     if helpers::is_payout_terminal_state(status) {
         return Err(report!(errors::ApiErrorResponse::InvalidRequestData {
             message: format!(
-                "Payout {} cannot be cancelled for status {}",
+                "Payout {:?} cannot be cancelled for status {}",
                 payout_attempt.payout_id, status
             ),
         }));
@@ -663,7 +663,7 @@ pub async fn payouts_fulfill_core(
     {
         return Err(report!(errors::ApiErrorResponse::InvalidRequestData {
             message: format!(
-                "Payout {} cannot be fulfilled for status {}",
+                "Payout {:?} cannot be fulfilled for status {}",
                 payout_attempt.payout_id, status
             ),
         }));
@@ -733,7 +733,7 @@ pub async fn payouts_fulfill_core(
 pub async fn payouts_list_core(
     _state: SessionState,
     _merchant_context: domain::MerchantContext,
-    _profile_id_list: Option<Vec<common_utils::id_type::ProfileId>>,
+    _profile_id_list: Option<Vec<id_type::ProfileId>>,
     _constraints: payouts::PayoutListConstraints,
 ) -> RouterResponse<payouts::PayoutListResponse> {
     todo!()
@@ -743,7 +743,7 @@ pub async fn payouts_list_core(
 pub async fn payouts_list_core(
     state: SessionState,
     merchant_context: domain::MerchantContext,
-    profile_id_list: Option<Vec<common_utils::id_type::ProfileId>>,
+    profile_id_list: Option<Vec<id_type::ProfileId>>,
     constraints: payouts::PayoutListConstraints,
 ) -> RouterResponse<payouts::PayoutListResponse> {
     validator::validate_payout_list_request(&constraints)?;
@@ -765,7 +765,10 @@ pub async fn payouts_list_core(
         match db
             .find_payout_attempt_by_merchant_id_payout_attempt_id(
                 merchant_id,
-                &utils::get_payout_attempt_id(payout.payout_id.clone(), payout.attempt_count),
+                &utils::get_payout_attempt_id(
+                    payout.payout_id.get_string_repr(),
+                    payout.attempt_count,
+                ),
                 storage_enums::MerchantStorageScheme::PostgresOnly,
             )
             .await
@@ -795,7 +798,7 @@ pub async fn payouts_list_core(
                 };
 
                 let payout_id_as_payment_id_type =
-                    common_utils::id_type::PaymentId::wrap(payout.payout_id.clone())
+                    id_type::PaymentId::wrap(payout.payout_id.get_string_repr().to_string())
                         .change_context(errors::ApiErrorResponse::InvalidRequestData {
                             message: "payout_id contains invalid data".to_string(),
                         })
@@ -862,7 +865,7 @@ pub async fn payouts_list_core(
 pub async fn payouts_filtered_list_core(
     state: SessionState,
     merchant_context: domain::MerchantContext,
-    profile_id_list: Option<Vec<common_utils::id_type::ProfileId>>,
+    profile_id_list: Option<Vec<id_type::ProfileId>>,
     filters: payouts::PayoutListFilterConstraints,
 ) -> RouterResponse<payouts::PayoutListResponse> {
     let limit = &filters.limit;
@@ -976,7 +979,7 @@ pub async fn payouts_filtered_list_core(
 pub async fn payouts_list_available_filters_core(
     state: SessionState,
     merchant_context: domain::MerchantContext,
-    profile_id_list: Option<Vec<common_utils::id_type::ProfileId>>,
+    profile_id_list: Option<Vec<id_type::ProfileId>>,
     time_range: common_utils::types::TimeRange,
 ) -> RouterResponse<api::PayoutListFilters> {
     let db = state.store.as_ref();
@@ -2540,6 +2543,7 @@ pub async fn response_handler(
     let response = api::PayoutCreateResponse {
         payout_id: payouts.payout_id.to_owned(),
         merchant_id: merchant_context.get_merchant_account().get_id().to_owned(),
+        merchant_order_reference_id: payout_attempt.merchant_order_reference_id.clone(),
         amount: payouts.amount,
         currency: payouts.destination_currency.to_owned(),
         connector: payout_attempt.connector,
@@ -2615,8 +2619,8 @@ pub async fn payout_create_db_entries(
     state: &SessionState,
     merchant_context: &domain::MerchantContext,
     req: &payouts::PayoutCreateRequest,
-    payout_id: &String,
-    profile_id: &common_utils::id_type::ProfileId,
+    payout_id: &id_type::PayoutId,
+    profile_id: &id_type::ProfileId,
     stored_payout_method_data: Option<&payouts::PayoutMethodData>,
     locale: &str,
     customer: Option<&domain::Customer>,
@@ -2655,12 +2659,13 @@ pub async fn payout_create_db_entries(
 
     // We have to do this because the function that is being used to create / get address is from payments
     // which expects a payment_id
-    let payout_id_as_payment_id_type =
-        common_utils::id_type::PaymentId::try_from(std::borrow::Cow::Owned(payout_id.to_string()))
-            .change_context(errors::ApiErrorResponse::InvalidRequestData {
-                message: "payout_id contains invalid data".to_string(),
-            })
-            .attach_printable("Error converting payout_id to PaymentId type")?;
+    let payout_id_as_payment_id_type = id_type::PaymentId::try_from(std::borrow::Cow::Owned(
+        payout_id.get_string_repr().to_string(),
+    ))
+    .change_context(errors::ApiErrorResponse::InvalidRequestData {
+        message: "payout_id contains invalid data".to_string(),
+    })
+    .attach_printable("Error converting payout_id to PaymentId type")?;
 
     // Get or create address
     let billing_address = payment_helpers::create_or_find_address_for_payment_by_request(
@@ -2696,7 +2701,7 @@ pub async fn payout_create_db_entries(
 
     let client_secret = utils::generate_id(
         consts::ID_LENGTH,
-        format!("payout_{payout_id}_secret").as_str(),
+        format!("payout_{payout_id:?}_secret").as_str(),
     );
     let amount = MinorUnit::from(req.amount.unwrap_or(api::Amount::Zero));
     let status = if req.payout_method_data.is_some()
@@ -2712,7 +2717,7 @@ pub async fn payout_create_db_entries(
     };
 
     let payouts_req = storage::PayoutsNew {
-        payout_id: payout_id.to_string(),
+        payout_id: payout_id.clone(),
         merchant_id: merchant_id.to_owned(),
         customer_id: customer_id.to_owned(),
         address_id: address_id.to_owned(),
@@ -2746,11 +2751,11 @@ pub async fn payout_create_db_entries(
         )
         .await
         .to_duplicate_response(errors::ApiErrorResponse::DuplicatePayout {
-            payout_id: payout_id.to_owned(),
+            payout_id: payout_id.clone(),
         })
         .attach_printable("Error inserting payouts in db")?;
     // Make payout_attempt entry
-    let payout_attempt_id = utils::get_payout_attempt_id(payout_id, 1);
+    let payout_attempt_id = utils::get_payout_attempt_id(payout_id.get_string_repr(), 1);
 
     let additional_pm_data_value = req
         .payout_method_data
@@ -2764,9 +2769,10 @@ pub async fn payout_create_db_entries(
 
     let payout_attempt_req = storage::PayoutAttemptNew {
         payout_attempt_id: payout_attempt_id.to_string(),
-        payout_id: payout_id.to_owned(),
+        payout_id: payout_id.clone(),
         additional_payout_method_data: additional_pm_data_value,
         merchant_id: merchant_id.to_owned(),
+        merchant_order_reference_id: req.merchant_order_reference_id.clone(),
         status,
         business_country: req.business_country.to_owned(),
         business_label: req.business_label.to_owned(),
@@ -2794,7 +2800,7 @@ pub async fn payout_create_db_entries(
         )
         .await
         .to_duplicate_response(errors::ApiErrorResponse::DuplicatePayout {
-            payout_id: payout_id.to_owned(),
+            payout_id: payout_id.clone(),
         })
         .attach_printable("Error inserting payout_attempt in db")?;
 
@@ -2823,7 +2829,7 @@ pub async fn payout_create_db_entries(
 pub async fn make_payout_data(
     _state: &SessionState,
     _merchant_context: &domain::MerchantContext,
-    _auth_profile_id: Option<common_utils::id_type::ProfileId>,
+    _auth_profile_id: Option<id_type::ProfileId>,
     _req: &payouts::PayoutRequest,
     locale: &str,
 ) -> RouterResult<PayoutData> {
@@ -2834,7 +2840,7 @@ pub async fn make_payout_data(
 pub async fn make_payout_data(
     state: &SessionState,
     merchant_context: &domain::MerchantContext,
-    auth_profile_id: Option<common_utils::id_type::ProfileId>,
+    auth_profile_id: Option<id_type::ProfileId>,
     req: &payouts::PayoutRequest,
     locale: &str,
 ) -> RouterResult<PayoutData> {
@@ -2842,7 +2848,9 @@ pub async fn make_payout_data(
     let merchant_id = merchant_context.get_merchant_account().get_id();
     let payout_id = match req {
         payouts::PayoutRequest::PayoutActionRequest(r) => r.payout_id.clone(),
-        payouts::PayoutRequest::PayoutCreateRequest(r) => r.payout_id.clone().unwrap_or_default(),
+        payouts::PayoutRequest::PayoutCreateRequest(r) => {
+            r.payout_id.clone().unwrap_or(id_type::PayoutId::generate())
+        }
         payouts::PayoutRequest::PayoutRetrieveRequest(r) => r.payout_id.clone(),
     };
 
@@ -2856,7 +2864,8 @@ pub async fn make_payout_data(
         .to_not_found_response(errors::ApiErrorResponse::PayoutNotFound)?;
     core_utils::validate_profile_id_from_auth_layer(auth_profile_id, &payouts)?;
 
-    let payout_attempt_id = utils::get_payout_attempt_id(payout_id, payouts.attempt_count);
+    let payout_attempt_id =
+        utils::get_payout_attempt_id(payouts.payout_id.get_string_repr(), payouts.attempt_count);
 
     let mut payout_attempt = db
         .find_payout_attempt_by_merchant_id_payout_attempt_id(
@@ -2871,9 +2880,9 @@ pub async fn make_payout_data(
 
     // We have to do this because the function that is being used to create / get address is from payments
     // which expects a payment_id
-    let payout_id_as_payment_id_type = common_utils::id_type::PaymentId::try_from(
-        std::borrow::Cow::Owned(payouts.payout_id.clone()),
-    )
+    let payout_id_as_payment_id_type = id_type::PaymentId::try_from(std::borrow::Cow::Owned(
+        payouts.payout_id.get_string_repr().to_string(),
+    ))
     .change_context(errors::ApiErrorResponse::InvalidRequestData {
         message: "payout_id contains invalid data".to_string(),
     })
@@ -2906,7 +2915,7 @@ pub async fn make_payout_data(
             .map_err(|err| err.change_context(errors::ApiErrorResponse::InternalServerError))
             .attach_printable_lazy(|| {
                 format!(
-                    "Failed while fetching optional customer [id - {:?}] for payout [id - {}]",
+                    "Failed while fetching optional customer [id - {:?}] for payout [id - {:?}]",
                     customer_id, payout_id
                 )
             })
@@ -3079,8 +3088,8 @@ pub async fn add_external_account_addition_task(
 async fn validate_and_get_business_profile(
     state: &SessionState,
     merchant_key_store: &domain::MerchantKeyStore,
-    profile_id: &common_utils::id_type::ProfileId,
-    merchant_id: &common_utils::id_type::MerchantId,
+    profile_id: &id_type::ProfileId,
+    merchant_id: &id_type::MerchantId,
 ) -> RouterResult<domain::Profile> {
     let db = &*state.store;
     let key_manager_state = &state.into();
@@ -3108,10 +3117,10 @@ async fn validate_and_get_business_profile(
 pub async fn create_payout_link(
     state: &SessionState,
     business_profile: &domain::Profile,
-    customer_id: &CustomerId,
-    merchant_id: &common_utils::id_type::MerchantId,
+    customer_id: &id_type::CustomerId,
+    merchant_id: &id_type::MerchantId,
     req: &payouts::PayoutCreateRequest,
-    payout_id: &str,
+    payout_id: &id_type::PayoutId,
     locale: &str,
 ) -> RouterResult<PayoutLink> {
     let payout_link_config_req = req.payout_link_config.to_owned();
@@ -3175,7 +3184,7 @@ pub async fn create_payout_link(
         .as_ref()
         .map_or(default_config.expiry, |expiry| *expiry);
     let url = format!(
-        "{base_url}/payout_link/{}/{payout_id}?locale={}",
+        "{base_url}/payout_link/{}/{payout_id:?}?locale={}",
         merchant_id.get_string_repr(),
         locale
     );
@@ -3214,7 +3223,7 @@ pub async fn create_payout_link(
     let data = PayoutLinkData {
         payout_link_id: payout_link_id.clone(),
         customer_id: customer_id.clone(),
-        payout_id: payout_id.to_string(),
+        payout_id: payout_id.clone(),
         link,
         client_secret: Secret::new(client_secret),
         session_expiry,
@@ -3232,7 +3241,7 @@ pub async fn create_payout_link(
 
 pub async fn create_payout_link_db_entry(
     state: &SessionState,
-    merchant_id: &common_utils::id_type::MerchantId,
+    merchant_id: &id_type::MerchantId,
     payout_link_data: &PayoutLinkData,
     return_url: Option<String>,
 ) -> RouterResult<PayoutLink> {
@@ -3244,7 +3253,7 @@ pub async fn create_payout_link_db_entry(
 
     let payout_link = GenericLinkNew {
         link_id: payout_link_data.payout_link_id.to_string(),
-        primary_reference: payout_link_data.payout_id.to_string(),
+        primary_reference: payout_link_data.payout_id.get_string_repr().to_string(),
         merchant_id: merchant_id.to_owned(),
         link_type: common_enums::GenericLinkType::PayoutLink,
         link_status: GenericLinkStatus::PayoutLink(PayoutLinkStatus::Initiated),
@@ -3267,9 +3276,9 @@ pub async fn create_payout_link_db_entry(
 pub async fn get_mca_from_profile_id(
     state: &SessionState,
     merchant_context: &domain::MerchantContext,
-    profile_id: &common_utils::id_type::ProfileId,
+    profile_id: &id_type::ProfileId,
     connector_name: &str,
-    merchant_connector_id: Option<&common_utils::id_type::MerchantConnectorAccountId>,
+    merchant_connector_id: Option<&id_type::MerchantConnectorAccountId>,
 ) -> RouterResult<payment_helpers::MerchantConnectorAccountType> {
     let merchant_connector_account = payment_helpers::get_merchant_connector_account(
         state,
diff --git a/crates/router/src/core/payouts/helpers.rs b/crates/router/src/core/payouts/helpers.rs
index 1003ee699f2..9e006ed9ce4 100644
--- a/crates/router/src/core/payouts/helpers.rs
+++ b/crates/router/src/core/payouts/helpers.rs
@@ -1201,7 +1201,8 @@ pub async fn update_payouts_and_payout_attempt(
         return Err(report!(errors::ApiErrorResponse::InvalidRequestData {
             message: format!(
                 "Payout {} cannot be updated for status {}",
-                payout_id, status
+                payout_id.get_string_repr(),
+                status
             ),
         }));
     }
@@ -1226,12 +1227,13 @@ pub async fn update_payouts_and_payout_attempt(
 
     // We have to do this because the function that is being used to create / get address is from payments
     // which expects a payment_id
-    let payout_id_as_payment_id_type =
-        id_type::PaymentId::try_from(std::borrow::Cow::Owned(payout_id.clone()))
-            .change_context(errors::ApiErrorResponse::InvalidRequestData {
-                message: "payout_id contains invalid data".to_string(),
-            })
-            .attach_printable("Error converting payout_id to PaymentId type")?;
+    let payout_id_as_payment_id_type = id_type::PaymentId::try_from(std::borrow::Cow::Owned(
+        payout_id.get_string_repr().to_string(),
+    ))
+    .change_context(errors::ApiErrorResponse::InvalidRequestData {
+        message: "payout_id contains invalid data for PaymentId conversion".to_string(),
+    })
+    .attach_printable("Error converting payout_id to PaymentId type")?;
 
     // Fetch address details from request and create new or else use existing address that was attached
     let billing_address = payment_helpers::create_or_find_address_for_payment_by_request(
diff --git a/crates/router/src/core/payouts/retry.rs b/crates/router/src/core/payouts/retry.rs
index 3b5e93eaf11..30100a580d4 100644
--- a/crates/router/src/core/payouts/retry.rs
+++ b/crates/router/src/core/payouts/retry.rs
@@ -269,12 +269,18 @@ pub async fn modify_trackers(
         .change_context(errors::ApiErrorResponse::InternalServerError)
         .attach_printable("Error updating payouts")?;
 
-    let payout_attempt_id =
-        utils::get_payout_attempt_id(payout_id.to_owned(), payout_data.payouts.attempt_count);
+    let payout_attempt_id = utils::get_payout_attempt_id(
+        payout_id.get_string_repr(),
+        payout_data.payouts.attempt_count,
+    );
 
     let payout_attempt_req = storage::PayoutAttemptNew {
         payout_attempt_id: payout_attempt_id.to_string(),
         payout_id: payout_id.to_owned(),
+        merchant_order_reference_id: payout_data
+            .payout_attempt
+            .merchant_order_reference_id
+            .clone(),
         customer_id: payout_data.payout_attempt.customer_id.to_owned(),
         connector: Some(connector.connector_name.to_string()),
         merchant_id: payout_data.payout_attempt.merchant_id.to_owned(),
diff --git a/crates/router/src/core/payouts/transformers.rs b/crates/router/src/core/payouts/transformers.rs
index a1c92ec3b29..7040559d9f4 100644
--- a/crates/router/src/core/payouts/transformers.rs
+++ b/crates/router/src/core/payouts/transformers.rs
@@ -69,6 +69,7 @@ impl
             payout_id: payout.payout_id,
             merchant_id: payout.merchant_id,
             merchant_connector_id: payout_attempt.merchant_connector_id,
+            merchant_order_reference_id: payout_attempt.merchant_order_reference_id.clone(),
             amount: payout.amount,
             currency: payout.destination_currency,
             connector: payout_attempt.connector,
diff --git a/crates/router/src/core/payouts/validator.rs b/crates/router/src/core/payouts/validator.rs
index 35c4599dbc2..1af3f4d2cb5 100644
--- a/crates/router/src/core/payouts/validator.rs
+++ b/crates/router/src/core/payouts/validator.rs
@@ -3,7 +3,10 @@ use std::collections::HashSet;
 use actix_web::http::header;
 #[cfg(feature = "olap")]
 use common_utils::errors::CustomResult;
-use common_utils::validation::validate_domain_against_allowed_domains;
+use common_utils::{
+    id_type::{self, GenerateId},
+    validation::validate_domain_against_allowed_domains,
+};
 use diesel_models::generic_link::PayoutLink;
 use error_stack::{report, ResultExt};
 use hyperswitch_domain_models::payment_methods::PaymentMethod;
@@ -29,8 +32,8 @@ use crate::{
 #[instrument(skip(db))]
 pub async fn validate_uniqueness_of_payout_id_against_merchant_id(
     db: &dyn StorageInterface,
-    payout_id: &str,
-    merchant_id: &common_utils::id_type::MerchantId,
+    payout_id: &id_type::PayoutId,
+    merchant_id: &id_type::MerchantId,
     storage_scheme: storage::enums::MerchantStorageScheme,
 ) -> RouterResult<Option<storage::Payouts>> {
     let maybe_payouts = db
@@ -75,9 +78,9 @@ pub async fn validate_create_request(
     merchant_context: &domain::MerchantContext,
     req: &payouts::PayoutCreateRequest,
 ) -> RouterResult<(
-    String,
+    id_type::PayoutId,
     Option<payouts::PayoutMethodData>,
-    common_utils::id_type::ProfileId,
+    id_type::ProfileId,
     Option<domain::Customer>,
     Option<PaymentMethod>,
 )> {
@@ -101,7 +104,11 @@ pub async fn validate_create_request(
 
     // Payout ID
     let db: &dyn StorageInterface = &*state.store;
-    let payout_id = core_utils::get_or_generate_uuid("payout_id", req.payout_id.as_ref())?;
+    let payout_id = match req.payout_id.as_ref() {
+        Some(provided_payout_id) => provided_payout_id.clone(),
+        None => id_type::PayoutId::generate(),
+    };
+
     match validate_uniqueness_of_payout_id_against_merchant_id(
         db,
         &payout_id,
@@ -111,13 +118,12 @@ pub async fn validate_create_request(
     .await
     .attach_printable_lazy(|| {
         format!(
-            "Unique violation while checking payout_id: {} against merchant_id: {:?}",
-            payout_id.to_owned(),
-            merchant_id
+            "Unique violation while checking payout_id: {:?} against merchant_id: {:?}",
+            payout_id, merchant_id
         )
     })? {
         Some(_) => Err(report!(errors::ApiErrorResponse::DuplicatePayout {
-            payout_id: payout_id.to_owned()
+            payout_id: payout_id.clone()
         })),
         None => Ok(()),
     }?;
diff --git a/crates/router/src/core/utils.rs b/crates/router/src/core/utils.rs
index 17ac3f9abd0..d0a27406eb5 100644
--- a/crates/router/src/core/utils.rs
+++ b/crates/router/src/core/utils.rs
@@ -34,7 +34,6 @@ use masking::{ExposeInterface, PeekInterface};
 use maud::{html, PreEscaped};
 use regex::Regex;
 use router_env::{instrument, tracing};
-use uuid::Uuid;
 
 use super::payments::helpers;
 #[cfg(feature = "payouts")]
@@ -57,7 +56,7 @@ use crate::{
         storage::{self, enums},
         PollConfig,
     },
-    utils::{generate_id, generate_uuid, OptionExt, ValueExt},
+    utils::{generate_id, OptionExt, ValueExt},
 };
 
 pub const IRRELEVANT_CONNECTOR_REQUEST_REFERENCE_ID_IN_DISPUTE_FLOW: &str =
@@ -188,7 +187,7 @@ pub async fn construct_payout_router_data<'a, F>(
         minor_amount_captured: None,
         payment_method_status: None,
         request: types::PayoutsData {
-            payout_id: payouts.payout_id.to_owned(),
+            payout_id: payouts.payout_id.clone(),
             amount: payouts.amount.get_amount_as_i64(),
             minor_amount: payouts.amount,
             connector_payout_id: payout_attempt.connector_payout_id.clone(),
@@ -622,16 +621,6 @@ pub fn get_or_generate_id(
         .map_or(Ok(generate_id(consts::ID_LENGTH, prefix)), validate_id)
 }
 
-pub fn get_or_generate_uuid(
-    key: &str,
-    provided_id: Option<&String>,
-) -> Result<String, errors::ApiErrorResponse> {
-    let validate_id = |id: String| validate_uuid(id, key);
-    provided_id
-        .cloned()
-        .map_or(Ok(generate_uuid()), validate_id)
-}
-
 fn invalid_id_format_error(key: &str) -> errors::ApiErrorResponse {
     errors::ApiErrorResponse::InvalidDataFormat {
         field_name: key.to_string(),
@@ -650,13 +639,6 @@ pub fn validate_id(id: String, key: &str) -> Result<String, errors::ApiErrorResp
     }
 }
 
-pub fn validate_uuid(uuid: String, key: &str) -> Result<String, errors::ApiErrorResponse> {
-    match (Uuid::parse_str(&uuid), uuid.len() > consts::MAX_ID_LENGTH) {
-        (Ok(_), false) => Ok(uuid),
-        (_, _) => Err(invalid_id_format_error(key)),
-    }
-}
-
 #[cfg(feature = "v1")]
 pub fn get_split_refunds(
     split_refund_input: refunds_transformers::SplitRefundInput,
diff --git a/crates/router/src/core/webhooks/incoming.rs b/crates/router/src/core/webhooks/incoming.rs
index 5fef952532f..cc1dc7bc506 100644
--- a/crates/router/src/core/webhooks/incoming.rs
+++ b/crates/router/src/core/webhooks/incoming.rs
@@ -997,7 +997,10 @@ async fn payouts_incoming_webhook_flow(
                 business_profile,
                 outgoing_event_type,
                 enums::EventClass::Payouts,
-                updated_payout_attempt.payout_id.clone(),
+                updated_payout_attempt
+                    .payout_id
+                    .get_string_repr()
+                    .to_string(),
                 enums::EventObjectType::PayoutDetails,
                 api::OutgoingWebhookContent::PayoutDetails(Box::new(payout_create_response)),
                 Some(updated_payout_attempt.created_at),
diff --git a/crates/router/src/db/kafka_store.rs b/crates/router/src/db/kafka_store.rs
index 48d0d582abb..7f24d2f5c99 100644
--- a/crates/router/src/db/kafka_store.rs
+++ b/crates/router/src/db/kafka_store.rs
@@ -2325,6 +2325,22 @@ impl PayoutAttemptInterface for KafkaStore {}
 #[async_trait::async_trait]
 impl PayoutAttemptInterface for KafkaStore {
     type Error = errors::StorageError;
+
+    async fn find_payout_attempt_by_merchant_id_merchant_order_reference_id(
+        &self,
+        merchant_id: &id_type::MerchantId,
+        merchant_order_reference_id: &str,
+        storage_scheme: MerchantStorageScheme,
+    ) -> CustomResult<storage::PayoutAttempt, errors::StorageError> {
+        self.diesel_store
+            .find_payout_attempt_by_merchant_id_merchant_order_reference_id(
+                merchant_id,
+                merchant_order_reference_id,
+                storage_scheme,
+            )
+            .await
+    }
+
     async fn find_payout_attempt_by_merchant_id_payout_attempt_id(
         &self,
         merchant_id: &id_type::MerchantId,
@@ -2431,7 +2447,7 @@ impl PayoutsInterface for KafkaStore {
     async fn find_payout_by_merchant_id_payout_id(
         &self,
         merchant_id: &id_type::MerchantId,
-        payout_id: &str,
+        payout_id: &id_type::PayoutId,
         storage_scheme: MerchantStorageScheme,
     ) -> CustomResult<storage::Payouts, errors::StorageError> {
         self.diesel_store
@@ -2477,7 +2493,7 @@ impl PayoutsInterface for KafkaStore {
     async fn find_optional_payout_by_merchant_id_payout_id(
         &self,
         merchant_id: &id_type::MerchantId,
-        payout_id: &str,
+        payout_id: &id_type::PayoutId,
         storage_scheme: MerchantStorageScheme,
     ) -> CustomResult<Option<storage::Payouts>, errors::StorageError> {
         self.diesel_store
@@ -2533,7 +2549,7 @@ impl PayoutsInterface for KafkaStore {
     async fn get_total_count_of_filtered_payouts(
         &self,
         merchant_id: &id_type::MerchantId,
-        active_payout_ids: &[String],
+        active_payout_ids: &[id_type::PayoutId],
         connector: Option<Vec<api_models::enums::PayoutConnectors>>,
         currency: Option<Vec<enums::Currency>>,
         status: Option<Vec<enums::PayoutStatus>>,
@@ -2556,7 +2572,7 @@ impl PayoutsInterface for KafkaStore {
         &self,
         merchant_id: &id_type::MerchantId,
         constraints: &hyperswitch_domain_models::payouts::PayoutFetchConstraints,
-    ) -> CustomResult<Vec<String>, errors::StorageError> {
+    ) -> CustomResult<Vec<id_type::PayoutId>, errors::StorageError> {
         self.diesel_store
             .filter_active_payout_ids_by_constraints(merchant_id, constraints)
             .await
diff --git a/crates/router/src/events/outgoing_webhook_logs.rs b/crates/router/src/events/outgoing_webhook_logs.rs
index 7574332f778..43f2ae54be4 100644
--- a/crates/router/src/events/outgoing_webhook_logs.rs
+++ b/crates/router/src/events/outgoing_webhook_logs.rs
@@ -38,7 +38,7 @@ pub enum OutgoingWebhookEventContent {
         content: Value,
     },
     Payout {
-        payout_id: String,
+        payout_id: common_utils::id_type::PayoutId,
         content: Value,
     },
     #[cfg(feature = "v1")]
diff --git a/crates/router/src/routes/payout_link.rs b/crates/router/src/routes/payout_link.rs
index 0727890bb32..71e3439d337 100644
--- a/crates/router/src/routes/payout_link.rs
+++ b/crates/router/src/routes/payout_link.rs
@@ -15,7 +15,10 @@ use crate::{
 pub async fn render_payout_link(
     state: web::Data<AppState>,
     req: actix_web::HttpRequest,
-    path: web::Path<(common_utils::id_type::MerchantId, String)>,
+    path: web::Path<(
+        common_utils::id_type::MerchantId,
+        common_utils::id_type::PayoutId,
+    )>,
 ) -> impl Responder {
     let flow = Flow::PayoutLinkInitiate;
     let (merchant_id, payout_id) = path.into_inner();
diff --git a/crates/router/src/routes/payouts.rs b/crates/router/src/routes/payouts.rs
index be0aa027d70..5bccdd06668 100644
--- a/crates/router/src/routes/payouts.rs
+++ b/crates/router/src/routes/payouts.rs
@@ -2,6 +2,7 @@ use actix_web::{
     body::{BoxBody, MessageBody},
     web, HttpRequest, HttpResponse, Responder,
 };
+use common_utils::id_type;
 use router_env::{instrument, tracing, Flow};
 
 use super::app::AppState;
@@ -50,7 +51,7 @@ pub async fn payouts_create(
 pub async fn payouts_retrieve(
     state: web::Data<AppState>,
     req: HttpRequest,
-    path: web::Path<String>,
+    path: web::Path<id_type::PayoutId>,
     query_params: web::Query<payout_types::PayoutRetrieveBody>,
 ) -> HttpResponse {
     let payout_retrieve_request = payout_types::PayoutRetrieveRequest {
@@ -90,7 +91,7 @@ pub async fn payouts_retrieve(
 pub async fn payouts_update(
     state: web::Data<AppState>,
     req: HttpRequest,
-    path: web::Path<String>,
+    path: web::Path<id_type::PayoutId>,
     json_payload: web::Json<payout_types::PayoutCreateRequest>,
 ) -> HttpResponse {
     let flow = Flow::PayoutsUpdate;
@@ -122,12 +123,12 @@ pub async fn payouts_confirm(
     state: web::Data<AppState>,
     req: HttpRequest,
     json_payload: web::Json<payout_types::PayoutCreateRequest>,
-    path: web::Path<String>,
+    path: web::Path<id_type::PayoutId>,
 ) -> HttpResponse {
     let flow = Flow::PayoutsConfirm;
     let mut payload = json_payload.into_inner();
     let payout_id = path.into_inner();
-    tracing::Span::current().record("payout_id", &payout_id);
+    tracing::Span::current().record("payout_id", payout_id.get_string_repr());
     payload.payout_id = Some(payout_id);
     payload.confirm = Some(true);
     let api_auth = auth::ApiKeyAuth::default();
@@ -160,12 +161,12 @@ pub async fn payouts_confirm(
 pub async fn payouts_cancel(
     state: web::Data<AppState>,
     req: HttpRequest,
-    json_payload: web::Json<payout_types::PayoutActionRequest>,
-    path: web::Path<String>,
+    path: web::Path<id_type::PayoutId>,
 ) -> HttpResponse {
     let flow = Flow::PayoutsCancel;
-    let mut payload = json_payload.into_inner();
-    payload.payout_id = path.into_inner();
+    let payload = payout_types::PayoutActionRequest {
+        payout_id: path.into_inner(),
+    };
 
     Box::pin(api::server_wrap(
         flow,
@@ -191,12 +192,12 @@ pub async fn payouts_cancel(
 pub async fn payouts_fulfill(
     state: web::Data<AppState>,
     req: HttpRequest,
-    json_payload: web::Json<payout_types::PayoutActionRequest>,
-    path: web::Path<String>,
+    path: web::Path<id_type::PayoutId>,
 ) -> HttpResponse {
     let flow = Flow::PayoutsFulfill;
-    let mut payload = json_payload.into_inner();
-    payload.payout_id = path.into_inner();
+    let payload = payout_types::PayoutActionRequest {
+        payout_id: path.into_inner(),
+    };
 
     Box::pin(api::server_wrap(
         flow,
diff --git a/crates/router/src/services/kafka/payout.rs b/crates/router/src/services/kafka/payout.rs
index babf7c1c568..e27009b3989 100644
--- a/crates/router/src/services/kafka/payout.rs
+++ b/crates/router/src/services/kafka/payout.rs
@@ -5,7 +5,7 @@ use time::OffsetDateTime;
 
 #[derive(serde::Serialize, Debug)]
 pub struct KafkaPayout<'a> {
-    pub payout_id: &'a String,
+    pub payout_id: &'a id_type::PayoutId,
     pub payout_attempt_id: &'a String,
     pub merchant_id: &'a id_type::MerchantId,
     pub customer_id: Option<&'a id_type::CustomerId>,
diff --git a/crates/router/src/utils.rs b/crates/router/src/utils.rs
index f89dbbec70a..a733de87656 100644
--- a/crates/router/src/utils.rs
+++ b/crates/router/src/utils.rs
@@ -41,7 +41,6 @@ use nanoid::nanoid;
 use serde::de::DeserializeOwned;
 use serde_json::Value;
 use tracing_futures::Instrument;
-use uuid::Uuid;
 
 pub use self::ext_traits::{OptionExt, ValidateCall};
 use crate::{
@@ -115,11 +114,6 @@ pub fn generate_id(length: usize, prefix: &str) -> String {
     format!("{}_{}", prefix, nanoid!(length, &consts::ALPHABETS))
 }
 
-#[inline]
-pub fn generate_uuid() -> String {
-    Uuid::new_v4().to_string()
-}
-
 pub trait ConnectorResponseExt: Sized {
     fn get_response(self) -> RouterResult<types::Response>;
     fn get_error_response(self) -> RouterResult<types::Response>;
@@ -164,7 +158,7 @@ impl<E> ConnectorResponseExt
 }
 
 #[inline]
-pub fn get_payout_attempt_id(payout_id: impl std::fmt::Display, attempt_count: i16) -> String {
+pub fn get_payout_attempt_id(payout_id: &str, attempt_count: i16) -> String {
     format!("{payout_id}_{attempt_count}")
 }
 
@@ -1356,7 +1350,7 @@ pub async fn trigger_payouts_webhook(
                         business_profile,
                         event_type,
                         diesel_models::enums::EventClass::Payouts,
-                        cloned_response.payout_id.clone(),
+                        cloned_response.payout_id.get_string_repr().to_owned(),
                         diesel_models::enums::EventObjectType::PayoutDetails,
                         webhooks::OutgoingWebhookContent::PayoutDetails(Box::new(cloned_response)),
                         primary_object_created_at,
diff --git a/crates/router/src/workflows/outgoing_webhook_retry.rs b/crates/router/src/workflows/outgoing_webhook_retry.rs
index 8a04094f31d..243c7382023 100644
--- a/crates/router/src/workflows/outgoing_webhook_retry.rs
+++ b/crates/router/src/workflows/outgoing_webhook_retry.rs
@@ -8,6 +8,7 @@ use api_models::{
 use common_utils::{
     consts::DEFAULT_LOCALE,
     ext_traits::{StringExt, ValueExt},
+    id_type,
 };
 use diesel_models::process_tracker::business_status;
 use error_stack::ResultExt;
@@ -283,7 +284,7 @@ impl ProcessTrackerWorkflow<SessionState> for OutgoingWebhookRetryWorkflow {
 #[instrument(skip_all)]
 pub(crate) async fn get_webhook_delivery_retry_schedule_time(
     db: &dyn StorageInterface,
-    merchant_id: &common_utils::id_type::MerchantId,
+    merchant_id: &id_type::MerchantId,
     retry_count: i32,
 ) -> Option<time::PrimitiveDateTime> {
     let key = "pt_mapping_outgoing_webhooks";
@@ -329,7 +330,7 @@ pub(crate) async fn get_webhook_delivery_retry_schedule_time(
 #[instrument(skip_all)]
 pub(crate) async fn retry_webhook_delivery_task(
     db: &dyn StorageInterface,
-    merchant_id: &common_utils::id_type::MerchantId,
+    merchant_id: &id_type::MerchantId,
     process: storage::ProcessTracker,
 ) -> errors::CustomResult<(), errors::StorageError> {
     let schedule_time =
@@ -385,15 +386,14 @@ async fn get_outgoing_webhook_content_and_event_type(
     match tracking_data.event_class {
         diesel_models::enums::EventClass::Payments => {
             let payment_id = tracking_data.primary_object_id.clone();
-            let payment_id =
-                common_utils::id_type::PaymentId::try_from(std::borrow::Cow::Owned(payment_id))
-                    .map_err(|payment_id_parsing_error| {
-                        logger::error!(
-                            ?payment_id_parsing_error,
-                            "Failed to parse payment ID from tracking data"
-                        );
-                        errors::ProcessTrackerError::DeserializationFailed
-                    })?;
+            let payment_id = id_type::PaymentId::try_from(std::borrow::Cow::Owned(payment_id))
+                .map_err(|payment_id_parsing_error| {
+                    logger::error!(
+                        ?payment_id_parsing_error,
+                        "Failed to parse payment ID from tracking data"
+                    );
+                    errors::ProcessTrackerError::DeserializationFailed
+                })?;
             let request = PaymentsRetrieveRequest {
                 resource_id: PaymentIdType::PaymentIntentId(payment_id),
                 merchant_id: Some(tracking_data.merchant_id.clone()),
@@ -539,7 +539,9 @@ async fn get_outgoing_webhook_content_and_event_type(
         diesel_models::enums::EventClass::Payouts => {
             let payout_id = tracking_data.primary_object_id.clone();
             let request = payout_models::PayoutRequest::PayoutActionRequest(
-                payout_models::PayoutActionRequest { payout_id },
+                payout_models::PayoutActionRequest {
+                    payout_id: id_type::PayoutId::try_from(std::borrow::Cow::Owned(payout_id))?,
+                },
             );
 
             let payout_data = Box::pin(payouts::make_payout_data(
diff --git a/crates/router/tests/connectors/utils.rs b/crates/router/tests/connectors/utils.rs
index 4bc234cca24..bf9bc355334 100644
--- a/crates/router/tests/connectors/utils.rs
+++ b/crates/router/tests/connectors/utils.rs
@@ -1,11 +1,9 @@
 use std::{fmt::Debug, marker::PhantomData, str::FromStr, sync::Arc, time::Duration};
 
 use async_trait::async_trait;
-use common_utils::pii::Email;
+use common_utils::{id_type::GenerateId, pii::Email};
 use error_stack::Report;
 use masking::Secret;
-#[cfg(feature = "payouts")]
-use router::core::utils as core_utils;
 use router::{
     configs::settings::Settings,
     core::{errors::ConnectorError, payments},
@@ -454,8 +452,7 @@ pub trait ConnectorActions: Connector {
     ) -> RouterData<Flow, types::PayoutsData, Res> {
         self.generate_data(
             types::PayoutsData {
-                payout_id: core_utils::get_or_generate_uuid("payout_id", None)
-                    .map_or("payout_3154763247".to_string(), |p| p),
+                payout_id: common_utils::id_type::PayoutId::generate(),
                 amount: 1,
                 minor_amount: MinorUnit::new(1),
                 connector_payout_id,
diff --git a/crates/storage_impl/src/lib.rs b/crates/storage_impl/src/lib.rs
index 156ebee2dea..b2a2dba5779 100644
--- a/crates/storage_impl/src/lib.rs
+++ b/crates/storage_impl/src/lib.rs
@@ -427,7 +427,7 @@ impl UniqueConstraints for diesel_models::Payouts {
         vec![format!(
             "po_{}_{}",
             self.merchant_id.get_string_repr(),
-            self.payout_id
+            self.payout_id.get_string_repr()
         )]
     }
     fn table_name(&self) -> &str {
diff --git a/crates/storage_impl/src/mock_db/payout_attempt.rs b/crates/storage_impl/src/mock_db/payout_attempt.rs
index 1fd2058e95a..008e9f921b0 100644
--- a/crates/storage_impl/src/mock_db/payout_attempt.rs
+++ b/crates/storage_impl/src/mock_db/payout_attempt.rs
@@ -65,4 +65,13 @@ impl PayoutAttemptInterface for MockDb {
     > {
         Err(StorageError::MockDbError)?
     }
+
+    async fn find_payout_attempt_by_merchant_id_merchant_order_reference_id(
+        &self,
+        _merchant_id: &common_utils::id_type::MerchantId,
+        _merchant_order_reference_id: &str,
+        _storage_scheme: storage_enums::MerchantStorageScheme,
+    ) -> CustomResult<PayoutAttempt, StorageError> {
+        Err(StorageError::MockDbError)?
+    }
 }
diff --git a/crates/storage_impl/src/mock_db/payouts.rs b/crates/storage_impl/src/mock_db/payouts.rs
index ecba8f88df6..648e09bd5c6 100644
--- a/crates/storage_impl/src/mock_db/payouts.rs
+++ b/crates/storage_impl/src/mock_db/payouts.rs
@@ -13,7 +13,7 @@ impl PayoutsInterface for MockDb {
     async fn find_payout_by_merchant_id_payout_id(
         &self,
         _merchant_id: &common_utils::id_type::MerchantId,
-        _payout_id: &str,
+        _payout_id: &common_utils::id_type::PayoutId,
         _storage_scheme: storage_enums::MerchantStorageScheme,
     ) -> CustomResult<Payouts, StorageError> {
         // TODO: Implement function for `MockDb`
@@ -43,7 +43,7 @@ impl PayoutsInterface for MockDb {
     async fn find_optional_payout_by_merchant_id_payout_id(
         &self,
         _merchant_id: &common_utils::id_type::MerchantId,
-        _payout_id: &str,
+        _payout_id: &common_utils::id_type::PayoutId,
         _storage_scheme: storage_enums::MerchantStorageScheme,
     ) -> CustomResult<Option<Payouts>, StorageError> {
         // TODO: Implement function for `MockDb`
@@ -95,7 +95,7 @@ impl PayoutsInterface for MockDb {
     async fn get_total_count_of_filtered_payouts(
         &self,
         _merchant_id: &common_utils::id_type::MerchantId,
-        _active_payout_ids: &[String],
+        _active_payout_ids: &[common_utils::id_type::PayoutId],
         _connector: Option<Vec<api_models::enums::PayoutConnectors>>,
         _currency: Option<Vec<storage_enums::Currency>>,
         _status: Option<Vec<storage_enums::PayoutStatus>>,
@@ -110,7 +110,7 @@ impl PayoutsInterface for MockDb {
         &self,
         _merchant_id: &common_utils::id_type::MerchantId,
         _constraints: &hyperswitch_domain_models::payouts::PayoutFetchConstraints,
-    ) -> CustomResult<Vec<String>, StorageError> {
+    ) -> CustomResult<Vec<common_utils::id_type::PayoutId>, StorageError> {
         // TODO: Implement function for `MockDb`
         Err(StorageError::MockDbError)?
     }
diff --git a/crates/storage_impl/src/payouts/payout_attempt.rs b/crates/storage_impl/src/payouts/payout_attempt.rs
index e1706fad124..35939c11323 100644
--- a/crates/storage_impl/src/payouts/payout_attempt.rs
+++ b/crates/storage_impl/src/payouts/payout_attempt.rs
@@ -60,7 +60,7 @@ impl<T: DatabaseStore> PayoutAttemptInterface for KVRouterStore<T> {
                 let payout_attempt_id = new_payout_attempt.payout_id.clone();
                 let key = PartitionKey::MerchantIdPayoutAttemptId {
                     merchant_id: &merchant_id,
-                    payout_attempt_id: &payout_attempt_id,
+                    payout_attempt_id: payout_attempt_id.get_string_repr(),
                 };
                 let key_str = key.to_string();
                 let created_attempt = PayoutAttempt {
@@ -88,6 +88,9 @@ impl<T: DatabaseStore> PayoutAttemptInterface for KVRouterStore<T> {
                     routing_info: new_payout_attempt.routing_info.clone(),
                     unified_code: new_payout_attempt.unified_code.clone(),
                     unified_message: new_payout_attempt.unified_message.clone(),
+                    merchant_order_reference_id: new_payout_attempt
+                        .merchant_order_reference_id
+                        .clone(),
                 };
 
                 let redis_entry = kv::TypedSql {
@@ -149,7 +152,7 @@ impl<T: DatabaseStore> PayoutAttemptInterface for KVRouterStore<T> {
     ) -> error_stack::Result<PayoutAttempt, errors::StorageError> {
         let key = PartitionKey::MerchantIdPayoutAttemptId {
             merchant_id: &this.merchant_id,
-            payout_attempt_id: &this.payout_id,
+            payout_attempt_id: this.payout_id.get_string_repr(),
         };
         let field = format!("poa_{}", this.payout_attempt_id);
         let storage_scheme = Box::pin(decide_storage_scheme::<_, DieselPayoutAttempt>(
@@ -378,6 +381,22 @@ impl<T: DatabaseStore> PayoutAttemptInterface for KVRouterStore<T> {
             .get_filters_for_payouts(payouts, merchant_id, storage_scheme)
             .await
     }
+
+    #[instrument(skip_all)]
+    async fn find_payout_attempt_by_merchant_id_merchant_order_reference_id(
+        &self,
+        merchant_id: &common_utils::id_type::MerchantId,
+        merchant_order_reference_id: &str,
+        storage_scheme: MerchantStorageScheme,
+    ) -> error_stack::Result<PayoutAttempt, errors::StorageError> {
+        self.router_store
+            .find_payout_attempt_by_merchant_id_merchant_order_reference_id(
+                merchant_id,
+                merchant_order_reference_id,
+                storage_scheme,
+            )
+            .await
+    }
 }
 
 #[async_trait::async_trait]
@@ -504,6 +523,27 @@ impl<T: DatabaseStore> PayoutAttemptInterface for crate::RouterStore<T> {
                 },
             )
     }
+
+    #[instrument(skip_all)]
+    async fn find_payout_attempt_by_merchant_id_merchant_order_reference_id(
+        &self,
+        merchant_id: &common_utils::id_type::MerchantId,
+        merchant_order_reference_id: &str,
+        _storage_scheme: MerchantStorageScheme,
+    ) -> error_stack::Result<PayoutAttempt, errors::StorageError> {
+        let conn = pg_connection_read(self).await?;
+        DieselPayoutAttempt::find_by_merchant_id_merchant_order_reference_id(
+            &conn,
+            merchant_id,
+            merchant_order_reference_id,
+        )
+        .await
+        .map(PayoutAttempt::from_storage_model)
+        .map_err(|er| {
+            let new_err = diesel_error_to_data_error(*er.current_context());
+            er.change_context(new_err)
+        })
+    }
 }
 
 impl DataModelExt for PayoutAttempt {
@@ -533,6 +573,7 @@ impl DataModelExt for PayoutAttempt {
             unified_code: self.unified_code,
             unified_message: self.unified_message,
             additional_payout_method_data: self.additional_payout_method_data,
+            merchant_order_reference_id: self.merchant_order_reference_id,
         }
     }
 
@@ -560,6 +601,7 @@ impl DataModelExt for PayoutAttempt {
             unified_code: storage_model.unified_code,
             unified_message: storage_model.unified_message,
             additional_payout_method_data: storage_model.additional_payout_method_data,
+            merchant_order_reference_id: storage_model.merchant_order_reference_id,
         }
     }
 }
@@ -590,6 +632,7 @@ impl DataModelExt for PayoutAttemptNew {
             unified_code: self.unified_code,
             unified_message: self.unified_message,
             additional_payout_method_data: self.additional_payout_method_data,
+            merchant_order_reference_id: self.merchant_order_reference_id,
         }
     }
 
@@ -617,6 +660,7 @@ impl DataModelExt for PayoutAttemptNew {
             unified_code: storage_model.unified_code,
             unified_message: storage_model.unified_message,
             additional_payout_method_data: storage_model.additional_payout_method_data,
+            merchant_order_reference_id: storage_model.merchant_order_reference_id,
         }
     }
 }
diff --git a/crates/storage_impl/src/payouts/payouts.rs b/crates/storage_impl/src/payouts/payouts.rs
index 484328aabab..c9d16b7d8a2 100644
--- a/crates/storage_impl/src/payouts/payouts.rs
+++ b/crates/storage_impl/src/payouts/payouts.rs
@@ -80,7 +80,7 @@ impl<T: DatabaseStore> PayoutsInterface for KVRouterStore<T> {
                     payout_id: &payout_id,
                 };
                 let key_str = key.to_string();
-                let field = format!("po_{}", new.payout_id);
+                let field = format!("po_{}", new.payout_id.get_string_repr());
                 let created_payout = Payouts {
                     payout_id: new.payout_id.clone(),
                     merchant_id: new.merchant_id.clone(),
@@ -151,7 +151,7 @@ impl<T: DatabaseStore> PayoutsInterface for KVRouterStore<T> {
             merchant_id: &this.merchant_id,
             payout_id: &this.payout_id,
         };
-        let field = format!("po_{}", this.payout_id);
+        let field = format!("po_{}", this.payout_id.get_string_repr());
         let storage_scheme = Box::pin(decide_storage_scheme::<_, DieselPayouts>(
             self,
             storage_scheme,
@@ -207,7 +207,7 @@ impl<T: DatabaseStore> PayoutsInterface for KVRouterStore<T> {
     async fn find_payout_by_merchant_id_payout_id(
         &self,
         merchant_id: &common_utils::id_type::MerchantId,
-        payout_id: &str,
+        payout_id: &common_utils::id_type::PayoutId,
         storage_scheme: MerchantStorageScheme,
     ) -> error_stack::Result<Payouts, StorageError> {
         let database_call = || async {
@@ -232,7 +232,7 @@ impl<T: DatabaseStore> PayoutsInterface for KVRouterStore<T> {
                     merchant_id,
                     payout_id,
                 };
-                let field = format!("po_{payout_id}");
+                let field = format!("po_{}", payout_id.get_string_repr());
                 Box::pin(utils::try_redis_get_else_try_database_get(
                     async {
                         Box::pin(kv_wrapper::<DieselPayouts, _, _>(
@@ -255,7 +255,7 @@ impl<T: DatabaseStore> PayoutsInterface for KVRouterStore<T> {
     async fn find_optional_payout_by_merchant_id_payout_id(
         &self,
         merchant_id: &common_utils::id_type::MerchantId,
-        payout_id: &str,
+        payout_id: &common_utils::id_type::PayoutId,
         storage_scheme: MerchantStorageScheme,
     ) -> error_stack::Result<Option<Payouts>, StorageError> {
         let database_call = || async {
@@ -276,20 +276,14 @@ impl<T: DatabaseStore> PayoutsInterface for KVRouterStore<T> {
         match storage_scheme {
             MerchantStorageScheme::PostgresOnly => {
                 let maybe_payouts = database_call().await?;
-                Ok(maybe_payouts.and_then(|payout| {
-                    if payout.payout_id == payout_id {
-                        Some(payout)
-                    } else {
-                        None
-                    }
-                }))
+                Ok(maybe_payouts.filter(|payout| &payout.payout_id == payout_id))
             }
             MerchantStorageScheme::RedisKv => {
                 let key = PartitionKey::MerchantIdPayoutId {
                     merchant_id,
                     payout_id,
                 };
-                let field = format!("po_{payout_id}");
+                let field = format!("po_{}", payout_id.get_string_repr());
                 Box::pin(utils::try_redis_get_else_try_database_get(
                     async {
                         Box::pin(kv_wrapper::<DieselPayouts, _, _>(
@@ -360,7 +354,7 @@ impl<T: DatabaseStore> PayoutsInterface for KVRouterStore<T> {
     async fn get_total_count_of_filtered_payouts(
         &self,
         merchant_id: &common_utils::id_type::MerchantId,
-        active_payout_ids: &[String],
+        active_payout_ids: &[common_utils::id_type::PayoutId],
         connector: Option<Vec<PayoutConnectors>>,
         currency: Option<Vec<storage_enums::Currency>>,
         status: Option<Vec<storage_enums::PayoutStatus>>,
@@ -383,7 +377,7 @@ impl<T: DatabaseStore> PayoutsInterface for KVRouterStore<T> {
         &self,
         merchant_id: &common_utils::id_type::MerchantId,
         constraints: &PayoutFetchConstraints,
-    ) -> error_stack::Result<Vec<String>, StorageError> {
+    ) -> error_stack::Result<Vec<common_utils::id_type::PayoutId>, StorageError> {
         self.router_store
             .filter_active_payout_ids_by_constraints(merchant_id, constraints)
             .await
@@ -434,7 +428,7 @@ impl<T: DatabaseStore> PayoutsInterface for crate::RouterStore<T> {
     async fn find_payout_by_merchant_id_payout_id(
         &self,
         merchant_id: &common_utils::id_type::MerchantId,
-        payout_id: &str,
+        payout_id: &common_utils::id_type::PayoutId,
         _storage_scheme: MerchantStorageScheme,
     ) -> error_stack::Result<Payouts, StorageError> {
         let conn = pg_connection_read(self).await?;
@@ -451,7 +445,7 @@ impl<T: DatabaseStore> PayoutsInterface for crate::RouterStore<T> {
     async fn find_optional_payout_by_merchant_id_payout_id(
         &self,
         merchant_id: &common_utils::id_type::MerchantId,
-        payout_id: &str,
+        payout_id: &common_utils::id_type::PayoutId,
         _storage_scheme: MerchantStorageScheme,
     ) -> error_stack::Result<Option<Payouts>, StorageError> {
         let conn = pg_connection_read(self).await?;
@@ -498,7 +492,7 @@ impl<T: DatabaseStore> PayoutsInterface for crate::RouterStore<T> {
                     query = query.filter(po_dsl::profile_id.eq(profile_id.clone()));
                 }
 
-                query = match (params.starting_at, &params.starting_after_id) {
+                query = match (params.starting_at, params.starting_after_id.as_ref()) {
                     (Some(starting_at), _) => query.filter(po_dsl::created_at.ge(starting_at)),
                     (None, Some(starting_after_id)) => {
                         // TODO: Fetch partial columns for this query since we only need some columns
@@ -515,7 +509,7 @@ impl<T: DatabaseStore> PayoutsInterface for crate::RouterStore<T> {
                     (None, None) => query,
                 };
 
-                query = match (params.ending_at, &params.ending_before_id) {
+                query = match (params.ending_at, params.ending_before_id.as_ref()) {
                     (Some(ending_at), _) => query.filter(po_dsl::created_at.le(ending_at)),
                     (None, Some(ending_before_id)) => {
                         // TODO: Fetch partial columns for this query since we only need some columns
@@ -619,10 +613,18 @@ impl<T: DatabaseStore> PayoutsInterface for crate::RouterStore<T> {
                     query = query.filter(po_dsl::profile_id.eq(profile_id.clone()));
                 }
 
-                query = match (params.starting_at, &params.starting_after_id) {
+                if let Some(merchant_order_reference_id_filter) =
+                    &params.merchant_order_reference_id
+                {
+                    query = query.filter(
+                        poa_dsl::merchant_order_reference_id
+                            .eq(merchant_order_reference_id_filter.clone()),
+                    );
+                }
+
+                query = match (params.starting_at, params.starting_after_id.as_ref()) {
                     (Some(starting_at), _) => query.filter(po_dsl::created_at.ge(starting_at)),
                     (None, Some(starting_after_id)) => {
-                        // TODO: Fetch partial columns for this query since we only need some columns
                         let starting_at = self
                             .find_payout_by_merchant_id_payout_id(
                                 merchant_id,
@@ -636,10 +638,9 @@ impl<T: DatabaseStore> PayoutsInterface for crate::RouterStore<T> {
                     (None, None) => query,
                 };
 
-                query = match (params.ending_at, &params.ending_before_id) {
+                query = match (params.ending_at, params.ending_before_id.as_ref()) {
                     (Some(ending_at), _) => query.filter(po_dsl::created_at.le(ending_at)),
                     (None, Some(ending_before_id)) => {
-                        // TODO: Fetch partial columns for this query since we only need some columns
                         let ending_at = self
                             .find_payout_by_merchant_id_payout_id(
                                 merchant_id,
@@ -665,20 +666,24 @@ impl<T: DatabaseStore> PayoutsInterface for crate::RouterStore<T> {
                     .map(|c| c.iter().map(|c| c.to_string()).collect::<Vec<String>>());
 
                 query = match connectors {
-                    Some(connectors) => query.filter(poa_dsl::connector.eq_any(connectors)),
-                    None => query,
+                    Some(conn_filters) if !conn_filters.is_empty() => {
+                        query.filter(poa_dsl::connector.eq_any(conn_filters))
+                    }
+                    _ => query,
                 };
 
                 query = match &params.status {
-                    Some(status) => query.filter(po_dsl::status.eq_any(status.clone())),
-                    None => query,
+                    Some(status_filters) if !status_filters.is_empty() => {
+                        query.filter(po_dsl::status.eq_any(status_filters.clone()))
+                    }
+                    _ => query,
                 };
 
                 query = match &params.payout_method {
-                    Some(payout_method) => {
+                    Some(payout_method) if !payout_method.is_empty() => {
                         query.filter(po_dsl::payout_type.eq_any(payout_method.clone()))
                     }
-                    None => query,
+                    _ => query,
                 };
 
                 query
@@ -760,7 +765,7 @@ impl<T: DatabaseStore> PayoutsInterface for crate::RouterStore<T> {
     async fn get_total_count_of_filtered_payouts(
         &self,
         merchant_id: &common_utils::id_type::MerchantId,
-        active_payout_ids: &[String],
+        active_payout_ids: &[common_utils::id_type::PayoutId],
         connector: Option<Vec<PayoutConnectors>>,
         currency: Option<Vec<storage_enums::Currency>>,
         status: Option<Vec<storage_enums::PayoutStatus>>,
@@ -800,7 +805,7 @@ impl<T: DatabaseStore> PayoutsInterface for crate::RouterStore<T> {
         &self,
         merchant_id: &common_utils::id_type::MerchantId,
         constraints: &PayoutFetchConstraints,
-    ) -> error_stack::Result<Vec<String>, StorageError> {
+    ) -> error_stack::Result<Vec<common_utils::id_type::PayoutId>, StorageError> {
         let conn = connection::pg_connection_read(self).await?;
         let conn = async_bb8_diesel::Connection::as_async_conn(&conn);
         let mut query = DieselPayouts::table()
@@ -868,8 +873,14 @@ impl<T: DatabaseStore> PayoutsInterface for crate::RouterStore<T> {
                 error_stack::report!(diesel_models::errors::DatabaseError::from(er))
                     .attach_printable("Error filtering payout records"),
             )
-            .into()
+        })?
+        .into_iter()
+        .map(|s| {
+            common_utils::id_type::PayoutId::try_from(std::borrow::Cow::Owned(s))
+                .change_context(StorageError::DeserializationFailed)
+                .attach_printable("Failed to deserialize PayoutId from database string")
         })
+        .collect::<error_stack::Result<Vec<_>, _>>()
     }
 
     #[cfg(all(feature = "olap", feature = "v2"))]
@@ -878,7 +889,7 @@ impl<T: DatabaseStore> PayoutsInterface for crate::RouterStore<T> {
         &self,
         _merchant_id: &common_utils::id_type::MerchantId,
         _constraints: &PayoutFetchConstraints,
-    ) -> error_stack::Result<Vec<String>, StorageError> {
+    ) -> error_stack::Result<Vec<common_utils::id_type::PayoutId>, StorageError> {
         todo!()
     }
 }
diff --git a/crates/storage_impl/src/redis/kv_store.rs b/crates/storage_impl/src/redis/kv_store.rs
index fd583f500ed..e7c47c58996 100644
--- a/crates/storage_impl/src/redis/kv_store.rs
+++ b/crates/storage_impl/src/redis/kv_store.rs
@@ -41,7 +41,7 @@ pub enum PartitionKey<'a> {
     },
     MerchantIdPayoutId {
         merchant_id: &'a common_utils::id_type::MerchantId,
-        payout_id: &'a str,
+        payout_id: &'a common_utils::id_type::PayoutId,
     },
     MerchantIdPayoutAttemptId {
         merchant_id: &'a common_utils::id_type::MerchantId,
@@ -93,8 +93,9 @@ impl std::fmt::Display for PartitionKey<'_> {
                 merchant_id,
                 payout_id,
             } => f.write_str(&format!(
-                "mid_{}_po_{payout_id}",
-                merchant_id.get_string_repr()
+                "mid_{}_po_{}",
+                merchant_id.get_string_repr(),
+                payout_id.get_string_repr()
             )),
             PartitionKey::MerchantIdPayoutAttemptId {
                 merchant_id,
diff --git a/flake.nix b/flake.nix
index 35fa05b73a7..05058a75d9e 100644
--- a/flake.nix
+++ b/flake.nix
@@ -43,6 +43,7 @@
           devPackages = base ++ (with pkgs; [
             cargo-watch
             nixd
+            protobuf
             rust-bin.stable.${rustDevVersion}.default
             swagger-cli
           ]);
diff --git a/migrations/2025-06-19-170656_alter_payout_primary_key/down.sql b/migrations/2025-06-19-170656_alter_payout_primary_key/down.sql
new file mode 100644
index 00000000000..e8ff6e1c5a1
--- /dev/null
+++ b/migrations/2025-06-19-170656_alter_payout_primary_key/down.sql
@@ -0,0 +1,7 @@
+ALTER TABLE payout_attempt DROP COLUMN merchant_order_reference_id;
+
+ALTER TABLE payout_attempt DROP CONSTRAINT payout_attempt_pkey;
+ALTER TABLE payout_attempt ADD PRIMARY KEY (payout_attempt_id);
+
+ALTER TABLE payouts DROP CONSTRAINT payouts_pkey;
+ALTER TABLE payouts ADD PRIMARY KEY (payout_id);
diff --git a/migrations/2025-06-19-170656_alter_payout_primary_key/up.sql b/migrations/2025-06-19-170656_alter_payout_primary_key/up.sql
new file mode 100644
index 00000000000..d65c931719f
--- /dev/null
+++ b/migrations/2025-06-19-170656_alter_payout_primary_key/up.sql
@@ -0,0 +1,7 @@
+ALTER TABLE payout_attempt DROP CONSTRAINT payout_attempt_pkey;
+ALTER TABLE payout_attempt ADD PRIMARY KEY (merchant_id, payout_attempt_id);
+
+ALTER TABLE payouts DROP CONSTRAINT payouts_pkey;
+ALTER TABLE payouts ADD PRIMARY KEY (merchant_id, payout_id);
+
+ALTER TABLE payout_attempt ADD COLUMN merchant_order_reference_id VARCHAR(255) NULL;

feat(payouts): allow payout_id to be configurable in API request
feat(payouts): add merchant_order_reference_id for referencing payouts

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

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

## Description
This pull request introduces two main features for payouts:

1.  **Configurable `payout_id` in API Requests**:
    *   The `payout_id` field in payout creation API requests can now be optionally provided by the merchant. If provided, this ID will be used for the payout. If not provided, the system will generate one.
    *   Internally, the usage of `payout_id` has been standardized to use the strongly-typed `common_utils::id_type::PayoutId` instead of `String`. This change has been propagated throughout relevant structs and functionalities like `PayoutsData`, data handling logic in `crates/router/src/core/utils.rs`, test utilities and DB queries.

2.  **`merchant_order_reference_id` for Payout Referencing**:
    *   The `merchant_order_reference_id` field has been added/updated in payout API models (e.g., `PayoutListFilterConstraints` in `crates/api_models/src/payouts.rs`). This allows merchants to use their own unique identifier to reference payouts.

**Refactoring and Connector Updates**:
*   To support the `id_type::PayoutId` change and ensure correct data flow, connector integrations (Adyen, Adyenplatform, Cybersource, Nomupay) have been refactored.
*   Connectors now consistently use `RouterData.connector_request_reference_id` (which typically holds the `payout_attempt_id` as a `String`) when a string-based reference is required by the external service, rather than converting the internal `PayoutsData.payout_id`. This ensures that connectors receive the appropriate string identifier (e.g., `payout_attempt_id`) they expect.

### Additional Changes

- [x] This PR modifies the API contract.
- [x] 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
These changes address the need for greater flexibility and type safety in payout processing, as outlined in GitHub issues #8392 and #1321.

*   **Configurable `payout_id`**: Allows merchants more control over their payout identifiers within the Hyperswitch.
*   **`merchant_order_reference_id`**: Provides a dedicated field for merchants to use their internal order or reference IDs.
*   **`PayoutId` Type Standardization**: Using `common_utils::id_type::PayoutId` enhances code robustness and maintainability by leveraging Rust's type system to prevent errors related to ID handling.
*   **Connector Refactoring**: Ensures that connectors use the correct string-based identifiers (like `payout_attempt_id` via `connector_request_reference_id`) required by external payout processors, maintaining correctness of integrations.

Links to issues:
*   https://github.com/juspay/hyperswitch/issues/8392
*   https://github.com/juspay/hyperswitch/issues/1321

## How did you test it?

<details>
    <summary>1. Card payouts via API</summary>

cURL

    curl --location --request POST 'http://localhost:8080/payouts/create' \
        --header 'Content-Type: application/json' \
        --header 'api-key: dev_lAISInmiO4UNrMmvkJogzRVKEGKlBKQbRCBFOkLJTXmq6nsD5G1H65BjOd4H72H8' \
        --data '{"amount":100,"currency":"EUR","profile_id":"pro_vVSlzCG7M1GSX9SyssY8","customer_id":"cus_WULwykbL8laUbxDUeGEB","connector":["adyenplatform"],"description":"Its my first payout request","payout_type":"card","payout_method_data":{"card":{"card_number":"4111111111111111","expiry_month":"03","expiry_year":"2030"}},"billing":{"address":{"line1":"1467","line2":"Harrison Street","line3":"Harrison Street","city":"San Fransico","state":"CA","zip":"94122","country":"FR","first_name":"John","last_name":"Doe"}},"entity_type":"Individual","recurring":true,"metadata":{"ref":"123"},"confirm":true,"auto_fulfill":true}'

Response

    {"payout_id":"ff21b89d-3cfd-4f0f-b3ff-c468d90d7074","merchant_id":"merchant_1751292662","amount":100,"currency":"EUR","connector":"adyenplatform","payout_type":"card","payout_method_data":{"card":{"card_issuer":null,"card_network":null,"card_type":null,"card_issuing_country":null,"bank_code":null,"last4":"1111","card_isin":"411111","card_extended_bin":null,"card_exp_month":"03","card_exp_year":"2030","card_holder_name":null}},"billing":{"address":{"city":"San Fransico","country":"FR","line1":"1467","line2":"Harrison Street","line3":"Harrison Street","zip":"94122","state":"CA","first_name":"John","last_name":"Doe"},"phone":null,"email":null},"auto_fulfill":true,"customer_id":"cus_WULwykbL8laUbxDUeGEB","customer":{"id":"cus_WULwykbL8laUbxDUeGEB","name":"John Nether","email":null,"phone":"6168205362","phone_country_code":"+1"},"client_secret":"payout_ff21b89d-3cfd-4f0f-b3ff-c468d90d7074_secret_qzFALtOg6ZhYAOAUr2qY","return_url":null,"business_country":null,"business_label":null,"description":"Its my first payout request","entity_type":"Individual","recurring":true,"metadata":{"ref":"123"},"merchant_connector_id":"mca_SfssFvg5RBT2k7iKRRsS","status":"initiated","error_message":null,"error_code":null,"profile_id":"pro_vVSlzCG7M1GSX9SyssY8","created":"2025-06-30T14:12:47.886Z","connector_transaction_id":"38EBIX67HBSYZ30V","priority":null,"payout_link":null,"email":null,"name":"John Nether","phone":"6168205362","phone_country_code":"+1","unified_code":null,"unified_message":null,"payout_method_id":"pm_wScFhCfxELZ7gN13Nbxz"}

</details>

<details>
    <summary>2. Card payouts via Payout Links</summary>

cURL

    curl --location --request POST 'http://localhost:8080/payouts/create' \
        --header 'Content-Type: application/json' \
        --header 'api-key: dev_lAISInmiO4UNrMmvkJogzRVKEGKlBKQbRCBFOkLJTXmq6nsD5G1H65BjOd4H72H8' \
        --data '{"amount":1,"currency":"EUR","customer_id":"cus_WULwykbL8laUbxDUeGEB","description":"Its my first payout request","entity_type":"Individual","confirm":false,"auto_fulfill":true,"session_expiry":1000000,"profile_id":"pro_vVSlzCG7M1GSX9SyssY8","payout_link":true,"return_url":"https://www.google.com","payout_link_config":{"form_layout":"journey","theme":"#0066ff","logo":"https://hyperswitch.io/favicon.ico","merchant_name":"HyperSwitch Inc.","test_mode":true}}'

Response

    {"payout_id":"2371746b-df3d-41da-8fa2-455faf921ea9","merchant_id":"merchant_1751292662","amount":1,"currency":"EUR","connector":null,"payout_type":null,"payout_method_data":null,"billing":null,"auto_fulfill":true,"customer_id":"cus_WULwykbL8laUbxDUeGEB","customer":{"id":"cus_WULwykbL8laUbxDUeGEB","name":"John Nether","email":null,"phone":"6168205362","phone_country_code":"+1"},"client_secret":"payout_2371746b-df3d-41da-8fa2-455faf921ea9_secret_VwDeIScMTLFzG7Iz1DZU","return_url":"https://www.google.com","business_country":null,"business_label":null,"description":"Its my first payout request","entity_type":"Individual","recurring":false,"metadata":{},"merchant_connector_id":null,"status":"requires_payout_method_data","error_message":null,"error_code":null,"profile_id":"pro_vVSlzCG7M1GSX9SyssY8","created":"2025-06-30T14:14:48.897Z","connector_transaction_id":null,"priority":null,"payout_link":{"payout_link_id":"payout_link_vITmtOWGkaUfeXopruRs","link":"http://localhost:8080/payout_link/merchant_1751292662/2371746b-df3d-41da-8fa2-455faf921ea9?locale=en"},"email":null,"name":"John Nether","phone":"6168205362","phone_country_code":"+1","unified_code":null,"unified_message":null,"payout_method_id":null}

<img width="1719" alt="Screenshot 2025-06-30 at 7 45 32 PM" src="https://github.com/user-attachments/assets/e1ace815-74b5-411a-83be-badb2c04bf7d" />
<img width="1721" alt="Screenshot 2025-06-30 at 7 45 48 PM" src="https://github.com/user-attachments/assets/a8233053-9c1e-402d-87af-e59783fefc9d" />
<img width="913" alt="Screenshot 2025-06-30 at 7 45 54 PM" src="https://github.com/user-attachments/assets/586d1aa9-0116-46f3-b597-91b1f46ba5d2" />


</details>

## 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
