diff --git a/api-reference-v2/api-reference/refunds/refunds--create.mdx b/api-reference-v2/api-reference/refunds/refunds--create.mdx new file mode 100644 index 0000000000..fa52f18c28 --- /dev/null +++ b/api-reference-v2/api-reference/refunds/refunds--create.mdx @@ -0,0 +1,3 @@ +--- +openapi: post /v2/refunds +--- \ No newline at end of file diff --git a/api-reference-v2/mint.json b/api-reference-v2/mint.json index 4d990aa5d4..5efdb1ed2a 100644 --- a/api-reference-v2/mint.json +++ b/api-reference-v2/mint.json @@ -107,6 +107,12 @@ "api-reference/customers/customers--delete", "api-reference/customers/customers--list" ] + }, + { + "group": "Refunds", + "pages": [ + "api-reference/refunds/refunds--create" + ] } ], "footerSocials": { diff --git a/api-reference-v2/openapi_spec.json b/api-reference-v2/openapi_spec.json index 41bda6be85..72afa1dc3d 100644 --- a/api-reference-v2/openapi_spec.json +++ b/api-reference-v2/openapi_spec.json @@ -1847,6 +1847,72 @@ } ] } + }, + "/v2/refunds": { + "post": { + "tags": [ + "Refunds" + ], + "summary": "Refunds - Create", + "description": "Creates a refund against an already processed payment. In case of some processors, you can even opt to refund only a partial amount multiple times until the original charge amount has been refunded", + "operationId": "Create a Refund", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RefundsCreateRequest" + }, + "examples": { + "Create an instant refund to refund partial amount": { + "value": { + "amount": 654, + "merchant_reference_id": "ref_123", + "payment_id": "{{payment_id}}", + "refund_type": "instant" + } + }, + "Create an instant refund to refund the whole amount": { + "value": { + "merchant_reference_id": "ref_123", + "payment_id": "{{payment_id}}", + "refund_type": "instant" + } + }, + "Create an instant refund with reason": { + "value": { + "amount": 6540, + "merchant_reference_id": "ref_123", + "payment_id": "{{payment_id}}", + "reason": "Customer returned product", + "refund_type": "instant" + } + } + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Refund created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RefundResponse" + } + } + } + }, + "400": { + "description": "Missing Mandatory fields" + } + }, + "security": [ + { + "api_key": [] + } + ] + } } }, "components": { @@ -18108,6 +18174,21 @@ } } }, + "RefundErrorDetails": { + "type": "object", + "required": [ + "code", + "message" + ], + "properties": { + "code": { + "type": "string" + }, + "message": { + "type": "string" + } + } + }, "RefundListRequest": { "allOf": [ { @@ -18298,89 +18379,89 @@ "RefundResponse": { "type": "object", "required": [ - "refund_id", + "id", "payment_id", "amount", "currency", "status", - "connector" + "created_at", + "updated_at", + "connector", + "profile_id", + "merchant_connector_id" ], "properties": { - "refund_id": { + "id": { "type": "string", - "description": "Unique Identifier for the refund" + "description": "Global Refund Id for the refund" }, "payment_id": { "type": "string", "description": "The payment id against which refund is initiated" }, + "merchant_reference_id": { + "type": "string", + "description": "Unique Identifier for the Refund. This is to ensure idempotency for multiple partial refunds initiated against the same payment.", + "example": "ref_mbabizu24mvu3mela5njyhpit4", + "nullable": true, + "maxLength": 30, + "minLength": 30 + }, "amount": { "type": "integer", "format": "int64", - "description": "The refund amount, which should be less than or equal to the total payment amount. Amount for the payment in lowest denomination of the currency. (i.e) in cents for USD denomination, in paisa for INR denomination etc", + "description": "The refund amount", "example": 6540, "minimum": 100 }, "currency": { - "type": "string", - "description": "The three-letter ISO currency code" + "$ref": "#/components/schemas/Currency" }, "status": { "$ref": "#/components/schemas/RefundStatus" }, "reason": { "type": "string", - "description": "An arbitrary string attached to the object. Often useful for displaying to users and your customer support executive", + "description": "An arbitrary string attached to the object", "nullable": true }, "metadata": { "type": "object", - "description": "You can specify up to 50 keys, with key names up to 40 characters long and values up to 500 characters long. Metadata is useful for storing additional, structured information on an object", + "description": "Metadata is useful for storing additional, unstructured information on an object", "nullable": true }, - "error_message": { - "type": "string", - "description": "The error message", - "nullable": true - }, - "error_code": { - "type": "string", - "description": "The code for the error", + "error_details": { + "allOf": [ + { + "$ref": "#/components/schemas/RefundErrorDetails" + } + ], "nullable": true }, "created_at": { "type": "string", "format": "date-time", - "description": "The timestamp at which refund is created", - "nullable": true + "description": "The timestamp at which refund is created" }, "updated_at": { "type": "string", "format": "date-time", - "description": "The timestamp at which refund is updated", - "nullable": true + "description": "The timestamp at which refund is updated" }, "connector": { - "type": "string", - "description": "The connector used for the refund and the corresponding payment", - "example": "stripe" + "$ref": "#/components/schemas/Connector" }, "profile_id": { "type": "string", - "description": "The id of business profile for this refund", - "nullable": true + "description": "The id of business profile for this refund" }, "merchant_connector_id": { "type": "string", - "description": "The merchant_connector_id of the processor through which this payment went through", - "nullable": true + "description": "The merchant_connector_id of the processor through which this payment went through" }, - "charges": { - "allOf": [ - { - "$ref": "#/components/schemas/ChargeRefunds" - } - ], + "connector_refund_reference_id": { + "type": "string", + "description": "The reference id of the connector for the refund", "nullable": true } } @@ -18421,6 +18502,59 @@ }, "additionalProperties": false }, + "RefundsCreateRequest": { + "type": "object", + "required": [ + "payment_id" + ], + "properties": { + "payment_id": { + "type": "string", + "description": "The payment id against which refund is initiated", + "example": "pay_mbabizu24mvu3mela5njyhpit4", + "maxLength": 30, + "minLength": 30 + }, + "merchant_reference_id": { + "type": "string", + "description": "Unique Identifier for the Refund. This is to ensure idempotency for multiple partial refunds initiated against the same payment.", + "example": "ref_mbabizu24mvu3mela5njyhpit4", + "nullable": true, + "maxLength": 30, + "minLength": 30 + }, + "amount": { + "type": "integer", + "format": "int64", + "description": "Total amount for which the refund is to be initiated. Amount for the payment in lowest denomination of the currency. (i.e) in cents for USD denomination, in paisa for INR denomination etc., If not provided, this will default to the amount_captured of the payment", + "example": 6540, + "nullable": true, + "minimum": 100 + }, + "reason": { + "type": "string", + "description": "Reason for the refund. Often useful for displaying to users and your customer support executive.", + "example": "Customer returned the product", + "nullable": true, + "maxLength": 255 + }, + "refund_type": { + "allOf": [ + { + "$ref": "#/components/schemas/RefundType" + } + ], + "default": "Instant", + "nullable": true + }, + "metadata": { + "type": "object", + "description": "Metadata is useful for storing additional, unstructured information on an object.", + "nullable": true + } + }, + "additionalProperties": false + }, "RequestIncrementalAuthorization": { "type": "string", "enum": [ diff --git a/crates/api_models/src/events/refund.rs b/crates/api_models/src/events/refund.rs index d180753735..e87ae15447 100644 --- a/crates/api_models/src/events/refund.rs +++ b/crates/api_models/src/events/refund.rs @@ -6,6 +6,7 @@ use crate::refunds::{ RefundUpdateRequest, RefundsRetrieveRequest, }; +#[cfg(feature = "v1")] impl ApiEventMetric for RefundRequest { fn get_api_event_type(&self) -> Option { let payment_id = self.payment_id.clone(); @@ -18,6 +19,7 @@ impl ApiEventMetric for RefundRequest { } } +#[cfg(feature = "v1")] impl ApiEventMetric for RefundResponse { fn get_api_event_type(&self) -> Option { Some(ApiEventsType::Refund { @@ -27,6 +29,17 @@ impl ApiEventMetric for RefundResponse { } } +#[cfg(feature = "v2")] +impl ApiEventMetric for RefundResponse { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Refund { + payment_id: self.payment_id.clone(), + refund_id: self.id.clone(), + }) + } +} + +#[cfg(feature = "v1")] impl ApiEventMetric for RefundsRetrieveRequest { fn get_api_event_type(&self) -> Option { Some(ApiEventsType::Refund { @@ -36,6 +49,7 @@ impl ApiEventMetric for RefundsRetrieveRequest { } } +#[cfg(feature = "v1")] impl ApiEventMetric for RefundUpdateRequest { fn get_api_event_type(&self) -> Option { Some(ApiEventsType::Refund { @@ -45,6 +59,7 @@ impl ApiEventMetric for RefundUpdateRequest { } } +#[cfg(feature = "v1")] impl ApiEventMetric for RefundManualUpdateRequest { fn get_api_event_type(&self) -> Option { Some(ApiEventsType::Refund { diff --git a/crates/api_models/src/refunds.rs b/crates/api_models/src/refunds.rs index 237a903d80..f42eb8095b 100644 --- a/crates/api_models/src/refunds.rs +++ b/crates/api_models/src/refunds.rs @@ -61,6 +61,45 @@ pub struct RefundRequest { pub charges: Option, } +#[cfg(feature = "v2")] +#[derive(Debug, ToSchema, Clone, Deserialize, Serialize)] +#[serde(deny_unknown_fields)] +pub struct RefundsCreateRequest { + /// The payment id against which refund is initiated + #[schema( + max_length = 30, + min_length = 30, + example = "pay_mbabizu24mvu3mela5njyhpit4", + value_type = String, + )] + pub payment_id: common_utils::id_type::GlobalPaymentId, + + /// Unique Identifier for the Refund. This is to ensure idempotency for multiple partial refunds initiated against the same payment. + #[schema( + max_length = 30, + min_length = 30, + example = "ref_mbabizu24mvu3mela5njyhpit4", + value_type = Option, + )] + pub merchant_reference_id: Option, + + /// Total amount for which the refund is to be initiated. Amount for the payment in lowest denomination of the currency. (i.e) in cents for USD denomination, in paisa for INR denomination etc., If not provided, this will default to the amount_captured of the payment + #[schema(value_type = Option , minimum = 100, example = 6540)] + pub amount: Option, + + /// Reason for the refund. Often useful for displaying to users and your customer support executive. + #[schema(max_length = 255, example = "Customer returned the product")] + pub reason: Option, + + /// To indicate whether to refund needs to be instant or scheduled. Default value is instant + #[schema(default = "Instant", example = "Instant")] + pub refund_type: Option, + + /// Metadata is useful for storing additional, unstructured information on an object. + #[schema(value_type = Option, example = r#"{ "city": "NY", "unit": "245" }"#)] + pub metadata: Option, +} + #[derive(Default, Debug, Clone, Deserialize)] pub struct RefundsRetrieveBody { pub force_sync: Option, @@ -125,6 +164,7 @@ pub enum RefundType { Instant, } +#[cfg(feature = "v1")] #[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize, ToSchema)] pub struct RefundResponse { /// Unique Identifier for the refund @@ -168,6 +208,78 @@ pub struct RefundResponse { pub charges: Option, } +#[cfg(feature = "v1")] +impl RefundResponse { + pub fn get_refund_id_as_string(&self) -> String { + self.refund_id.clone() + } +} + +#[cfg(feature = "v2")] +#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize, ToSchema)] +pub struct RefundResponse { + /// Global Refund Id for the refund + #[schema(value_type = String)] + pub id: common_utils::id_type::GlobalRefundId, + /// The payment id against which refund is initiated + #[schema(value_type = String)] + pub payment_id: common_utils::id_type::GlobalPaymentId, + /// Unique Identifier for the Refund. This is to ensure idempotency for multiple partial refunds initiated against the same payment. + #[schema( + max_length = 30, + min_length = 30, + example = "ref_mbabizu24mvu3mela5njyhpit4", + value_type = Option, + )] + pub merchant_reference_id: Option, + /// The refund amount + #[schema(value_type = i64 , minimum = 100, example = 6540)] + pub amount: MinorUnit, + /// The three-letter ISO currency code + #[schema(value_type = Currency)] + pub currency: common_enums::Currency, + /// The status for refund + pub status: RefundStatus, + /// An arbitrary string attached to the object + pub reason: Option, + /// Metadata is useful for storing additional, unstructured information on an object + #[schema(value_type = Option)] + pub metadata: Option, + /// The error details for the refund + pub error_details: Option, + /// The timestamp at which refund is created + #[serde(with = "common_utils::custom_serde::iso8601")] + pub created_at: PrimitiveDateTime, + /// The timestamp at which refund is updated + #[serde(with = "common_utils::custom_serde::iso8601")] + pub updated_at: PrimitiveDateTime, + /// The connector used for the refund and the corresponding payment + #[schema(example = "stripe", value_type = Connector)] + pub connector: enums::Connector, + /// The id of business profile for this refund + #[schema(value_type = String)] + pub profile_id: common_utils::id_type::ProfileId, + /// The merchant_connector_id of the processor through which this payment went through + #[schema(value_type = String)] + pub merchant_connector_id: common_utils::id_type::MerchantConnectorAccountId, + /// The reference id of the connector for the refund + pub connector_refund_reference_id: Option, +} + +#[cfg(feature = "v2")] +impl RefundResponse { + pub fn get_refund_id_as_string(&self) -> String { + self.id.get_string_repr().to_owned() + } +} + +#[cfg(feature = "v2")] +#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize, ToSchema)] +pub struct RefundErrorDetails { + pub code: String, + pub message: String, +} + #[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize, ToSchema)] pub struct RefundListRequest { /// The identifier for the payment diff --git a/crates/common_utils/src/events.rs b/crates/common_utils/src/events.rs index e30aa70897..656b299370 100644 --- a/crates/common_utils/src/events.rs +++ b/crates/common_utils/src/events.rs @@ -23,10 +23,16 @@ pub enum ApiEventsType { Payment { payment_id: id_type::GlobalPaymentId, }, + #[cfg(feature = "v1")] Refund { payment_id: Option, refund_id: String, }, + #[cfg(feature = "v2")] + Refund { + payment_id: id_type::GlobalPaymentId, + refund_id: id_type::GlobalRefundId, + }, PaymentMethod { payment_method_id: String, payment_method: Option, diff --git a/crates/common_utils/src/id_type.rs b/crates/common_utils/src/id_type.rs index c256559fc1..859732aed0 100644 --- a/crates/common_utils/src/id_type.rs +++ b/crates/common_utils/src/id_type.rs @@ -10,6 +10,7 @@ mod merchant_connector_account; mod organization; mod payment; mod profile; +mod refunds; mod routing; #[cfg(feature = "v2")] @@ -25,12 +26,16 @@ use diesel::{ sql_types, }; #[cfg(feature = "v2")] -pub use global_id::{payment::GlobalPaymentId, payment_methods::GlobalPaymentMethodId, CellId}; +pub use global_id::{ + payment::GlobalPaymentId, payment_methods::GlobalPaymentMethodId, refunds::GlobalRefundId, + CellId, +}; pub use merchant::MerchantId; pub use merchant_connector_account::MerchantConnectorAccountId; pub use organization::OrganizationId; pub use payment::{PaymentId, PaymentReferenceId}; pub use profile::ProfileId; +pub use refunds::RefundReferenceId; pub use routing::RoutingId; use serde::{Deserialize, Serialize}; use thiserror::Error; diff --git a/crates/common_utils/src/id_type/global_id.rs b/crates/common_utils/src/id_type/global_id.rs index 0709ce84d5..0032c531e4 100644 --- a/crates/common_utils/src/id_type/global_id.rs +++ b/crates/common_utils/src/id_type/global_id.rs @@ -1,6 +1,7 @@ #![allow(unused)] pub mod payment; pub mod payment_methods; +pub mod refunds; use diesel::{backend::Backend, deserialize::FromSql, serialize::ToSql, sql_types}; use error_stack::ResultExt; @@ -24,6 +25,7 @@ pub(crate) enum GlobalEntity { Customer, Payment, PaymentMethod, + Refund, } impl GlobalEntity { @@ -32,6 +34,7 @@ impl GlobalEntity { Self::Customer => "cus", Self::Payment => "pay", Self::PaymentMethod => "pm", + Self::Refund => "ref", } } } diff --git a/crates/common_utils/src/id_type/global_id/refunds.rs b/crates/common_utils/src/id_type/global_id/refunds.rs new file mode 100644 index 0000000000..64e4751611 --- /dev/null +++ b/crates/common_utils/src/id_type/global_id/refunds.rs @@ -0,0 +1,71 @@ +use error_stack::ResultExt; + +use crate::{errors, generate_id_with_default_len, generate_time_ordered_id_without_prefix, types}; + +/// A global id that can be used to identify a refund +#[derive( + Debug, + Clone, + Hash, + PartialEq, + Eq, + serde::Serialize, + serde::Deserialize, + diesel::expression::AsExpression, +)] +#[diesel(sql_type = diesel::sql_types::Text)] +pub struct GlobalRefundId(super::GlobalId); + +// Database related implementations so that this field can be used directly in the database tables +crate::impl_queryable_id_type!(GlobalRefundId); + +impl GlobalRefundId { + /// Get string representation of the id + pub fn get_string_repr(&self) -> &str { + self.0.get_string_repr() + } + + /// Generate a new GlobalRefundId from a cell id + pub fn generate(cell_id: crate::id_type::CellId) -> Self { + let global_id = super::GlobalId::generate(cell_id, super::GlobalEntity::Refund); + Self(global_id) + } +} + +// TODO: refactor the macro to include this id use case as well +impl TryFrom> for GlobalRefundId { + type Error = error_stack::Report; + fn try_from(value: std::borrow::Cow<'static, str>) -> Result { + use error_stack::ResultExt; + let merchant_ref_id = super::GlobalId::from_string(value).change_context( + errors::ValidationError::IncorrectValueProvided { + field_name: "refund_id", + }, + )?; + Ok(Self(merchant_ref_id)) + } +} + +// TODO: refactor the macro to include this id use case as well +impl diesel::serialize::ToSql for GlobalRefundId +where + DB: diesel::backend::Backend, + super::GlobalId: diesel::serialize::ToSql, +{ + fn to_sql<'b>( + &'b self, + out: &mut diesel::serialize::Output<'b, '_, DB>, + ) -> diesel::serialize::Result { + self.0.to_sql(out) + } +} + +impl diesel::deserialize::FromSql for GlobalRefundId +where + DB: diesel::backend::Backend, + super::GlobalId: diesel::deserialize::FromSql, +{ + fn from_sql(value: DB::RawValue<'_>) -> diesel::deserialize::Result { + super::GlobalId::from_sql(value).map(Self) + } +} diff --git a/crates/common_utils/src/id_type/refunds.rs b/crates/common_utils/src/id_type/refunds.rs new file mode 100644 index 0000000000..478ff03380 --- /dev/null +++ b/crates/common_utils/src/id_type/refunds.rs @@ -0,0 +1,10 @@ +crate::id_type!(RefundReferenceId, "A type for refund_reference_id"); +crate::impl_id_type_methods!(RefundReferenceId, "refund_reference_id"); + +// This is to display the `RefundReferenceId` as RefundReferenceId(abcd) +crate::impl_debug_id_type!(RefundReferenceId); +crate::impl_try_from_cow_str_id_type!(RefundReferenceId, "refund_reference_id"); + +// Database related implementations so that this field can be used directly in the database tables +crate::impl_queryable_id_type!(RefundReferenceId); +crate::impl_to_sql_from_sql_id_type!(RefundReferenceId); diff --git a/crates/diesel_models/Cargo.toml b/crates/diesel_models/Cargo.toml index 02d917f3b1..120a606d58 100644 --- a/crates/diesel_models/Cargo.toml +++ b/crates/diesel_models/Cargo.toml @@ -10,8 +10,8 @@ license.workspace = true [features] default = ["kv_store"] kv_store = [] -v1 = [] -v2 = [] +v1 = ["common_utils/v1"] +v2 = ["common_utils/v2"] customer_v2 = [] payment_methods_v2 = [] diff --git a/crates/diesel_models/src/refund.rs b/crates/diesel_models/src/refund.rs index 9e850bc9f1..42a22b9139 100644 --- a/crates/diesel_models/src/refund.rs +++ b/crates/diesel_models/src/refund.rs @@ -317,6 +317,7 @@ pub struct RefundCoreWorkflow { pub connector_transaction_data: Option, } +#[cfg(feature = "v1")] impl common_utils::events::ApiEventMetric for Refund { fn get_api_event_type(&self) -> Option { Some(common_utils::events::ApiEventsType::Refund { diff --git a/crates/openapi/src/openapi_v2.rs b/crates/openapi/src/openapi_v2.rs index bf95fbe743..7c961b2b2b 100644 --- a/crates/openapi/src/openapi_v2.rs +++ b/crates/openapi/src/openapi_v2.rs @@ -123,6 +123,9 @@ Never share your secret api keys. Keep them guarded and secure. //Routes for payments routes::payments::payments_create_intent, + + //Routes for refunds + routes::refunds::refunds_create, ), components(schemas( common_utils::types::MinorUnit, @@ -140,6 +143,8 @@ Never share your secret api keys. Keep them guarded and secure. common_utils::payout_method_utils::PaypalAdditionalData, common_utils::payout_method_utils::VenmoAdditionalData, api_models::refunds::RefundRequest, + api_models::refunds::RefundsCreateRequest, + api_models::refunds::RefundErrorDetails, api_models::refunds::RefundType, api_models::refunds::RefundResponse, api_models::refunds::RefundStatus, diff --git a/crates/openapi/src/routes/refunds.rs b/crates/openapi/src/routes/refunds.rs index 5c72fe8b48..4e096e243a 100644 --- a/crates/openapi/src/routes/refunds.rs +++ b/crates/openapi/src/routes/refunds.rs @@ -44,6 +44,7 @@ operation_id = "Create a Refund", security(("api_key" = [])) )] +#[cfg(feature = "v1")] pub async fn refunds_create() {} /// Refunds - Retrieve @@ -159,3 +160,55 @@ pub fn refunds_list_profile() {} security(("api_key" = [])) )] pub async fn refunds_filter_list() {} + +/// Refunds - Create +/// +/// Creates a refund against an already processed payment. In case of some processors, you can even opt to refund only a partial amount multiple times until the original charge amount has been refunded +#[utoipa::path( + post, + path = "/v2/refunds", + request_body( + content = RefundsCreateRequest, + examples( + ( + "Create an instant refund to refund the whole amount" = ( + value = json!({ + "payment_id": "{{payment_id}}", + "merchant_reference_id": "ref_123", + "refund_type": "instant" + }) + ) + ), + ( + "Create an instant refund to refund partial amount" = ( + value = json!({ + "payment_id": "{{payment_id}}", + "merchant_reference_id": "ref_123", + "refund_type": "instant", + "amount": 654 + }) + ) + ), + ( + "Create an instant refund with reason" = ( + value = json!({ + "payment_id": "{{payment_id}}", + "merchant_reference_id": "ref_123", + "refund_type": "instant", + "amount": 6540, + "reason": "Customer returned product" + }) + ) + ), + ) + ), + responses( + (status = 200, description = "Refund created", body = RefundResponse), + (status = 400, description = "Missing Mandatory fields") + ), + tag = "Refunds", + operation_id = "Create a Refund", + security(("api_key" = [])) +)] +#[cfg(feature = "v2")] +pub async fn refunds_create() {} diff --git a/crates/router/src/events/outgoing_webhook_logs.rs b/crates/router/src/events/outgoing_webhook_logs.rs index b2a8202e0e..db5a40fc6e 100644 --- a/crates/router/src/events/outgoing_webhook_logs.rs +++ b/crates/router/src/events/outgoing_webhook_logs.rs @@ -34,11 +34,18 @@ pub enum OutgoingWebhookEventContent { payout_id: String, content: Value, }, + #[cfg(feature = "v1")] Refund { payment_id: common_utils::id_type::PaymentId, refund_id: String, content: Value, }, + #[cfg(feature = "v2")] + Refund { + payment_id: common_utils::id_type::GlobalPaymentId, + refund_id: String, + content: Value, + }, Dispute { payment_id: common_utils::id_type::PaymentId, attempt_id: String, @@ -64,7 +71,7 @@ impl OutgoingWebhookEventMetric for OutgoingWebhookContent { }), Self::RefundDetails(refund_payload) => Some(OutgoingWebhookEventContent::Refund { payment_id: refund_payload.payment_id.clone(), - refund_id: refund_payload.refund_id.clone(), + refund_id: refund_payload.get_refund_id_as_string(), content: masking::masked_serialize(&refund_payload) .unwrap_or(serde_json::json!({"error":"failed to serialize"})), }),