fix: Resolved issue of webhooks response from ucs (#9021)

This commit is contained in:
Amitsingh Tanwar
2025-08-29 20:27:22 +05:30
committed by GitHub
parent a589e22464
commit 4a60b07954
6 changed files with 147 additions and 13 deletions

View File

@ -4263,7 +4263,13 @@ where
services::api::ConnectorIntegration<F, RouterDReq, router_types::PaymentsResponseData>,
{
record_time_taken_with(|| async {
if should_call_unified_connector_service(
if !matches!(
call_connector_action,
CallConnectorAction::UCSHandleResponse(_)
) && !matches!(
call_connector_action,
CallConnectorAction::HandleResponse(_),
) && should_call_unified_connector_service(
state,
merchant_context,
&router_data,

View File

@ -1220,6 +1220,7 @@ impl ForeignTryFrom<&hyperswitch_interfaces::webhooks::IncomingWebhookRequestDet
}
/// Webhook transform data structure containing UCS response information
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct WebhookTransformData {
pub event_type: api_models::webhooks::IncomingWebhookEvent,
pub source_verified: bool,
@ -1231,16 +1232,8 @@ pub struct WebhookTransformData {
pub fn transform_ucs_webhook_response(
response: PaymentServiceTransformResponse,
) -> Result<WebhookTransformData, error_stack::Report<errors::ApiErrorResponse>> {
let event_type = match response.event_type {
0 => api_models::webhooks::IncomingWebhookEvent::PaymentIntentSuccess,
1 => api_models::webhooks::IncomingWebhookEvent::PaymentIntentFailure,
2 => api_models::webhooks::IncomingWebhookEvent::PaymentIntentProcessing,
3 => api_models::webhooks::IncomingWebhookEvent::PaymentIntentCancelled,
4 => api_models::webhooks::IncomingWebhookEvent::RefundSuccess,
5 => api_models::webhooks::IncomingWebhookEvent::RefundFailure,
6 => api_models::webhooks::IncomingWebhookEvent::MandateRevoked,
_ => api_models::webhooks::IncomingWebhookEvent::EventNotSupported,
};
let event_type =
api_models::webhooks::IncomingWebhookEvent::from_ucs_event_type(response.event_type);
Ok(WebhookTransformData {
event_type,

View File

@ -695,6 +695,7 @@ async fn process_webhook_business_logic(
connector,
request_details,
event_type,
webhook_transform_data,
))
.await
.attach_printable("Incoming webhook flow for payments failed"),
@ -931,9 +932,22 @@ async fn payments_incoming_webhook_flow(
connector: &ConnectorEnum,
request_details: &IncomingWebhookRequestDetails<'_>,
event_type: webhooks::IncomingWebhookEvent,
webhook_transform_data: &Option<Box<unified_connector_service::WebhookTransformData>>,
) -> CustomResult<WebhookResponseTracker, errors::ApiErrorResponse> {
let consume_or_trigger_flow = if source_verified {
payments::CallConnectorAction::HandleResponse(webhook_details.resource_object)
// Determine the appropriate action based on UCS availability
let resource_object = webhook_details.resource_object;
match webhook_transform_data.as_ref() {
Some(transform_data) => {
// Serialize the transform data to pass to UCS handler
let transform_data_bytes = serde_json::to_vec(transform_data.as_ref())
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed to serialize UCS webhook transform data")?;
payments::CallConnectorAction::UCSHandleResponse(transform_data_bytes)
}
None => payments::CallConnectorAction::HandleResponse(resource_object),
}
} else {
payments::CallConnectorAction::Trigger
};

View File

@ -61,7 +61,7 @@ use crate::{
core::{
api_locking,
errors::{self, CustomResult},
payments,
payments, unified_connector_service,
},
events::{
api_logs::{ApiEvent, ApiEventMetric, ApiEventsType},
@ -127,6 +127,62 @@ pub type BoxedBillingConnectorPaymentsSyncIntegrationInterface<T, Req, Res> =
pub type BoxedVaultConnectorIntegrationInterface<T, Req, Res> =
BoxedConnectorIntegrationInterface<T, common_types::VaultConnectorFlowData, Req, Res>;
/// Handle UCS webhook response processing
fn handle_ucs_response<T, Req, Resp>(
router_data: types::RouterData<T, Req, Resp>,
transform_data_bytes: Vec<u8>,
) -> CustomResult<types::RouterData<T, Req, Resp>, errors::ConnectorError>
where
T: Clone + Debug + 'static,
Req: Debug + Clone + 'static,
Resp: Debug + Clone + 'static,
{
let webhook_transform_data: unified_connector_service::WebhookTransformData =
serde_json::from_slice(&transform_data_bytes)
.change_context(errors::ConnectorError::ResponseDeserializationFailed)
.attach_printable("Failed to deserialize UCS webhook transform data")?;
let webhook_content = webhook_transform_data
.webhook_content
.ok_or(errors::ConnectorError::ResponseDeserializationFailed)
.attach_printable("UCS webhook transform data missing webhook_content")?;
let payment_get_response = match webhook_content.content {
Some(unified_connector_service_client::payments::webhook_response_content::Content::PaymentsResponse(payments_response)) => {
Ok(payments_response)
},
Some(unified_connector_service_client::payments::webhook_response_content::Content::RefundsResponse(_)) => {
Err(errors::ConnectorError::ProcessingStepFailed(Some("UCS webhook contains refund response but payment processing was expected".to_string().into())).into())
},
Some(unified_connector_service_client::payments::webhook_response_content::Content::DisputesResponse(_)) => {
Err(errors::ConnectorError::ProcessingStepFailed(Some("UCS webhook contains dispute response but payment processing was expected".to_string().into())).into())
},
None => {
Err(errors::ConnectorError::ResponseDeserializationFailed)
.attach_printable("UCS webhook content missing payments_response")
}
}?;
let (status, router_data_response, status_code) =
unified_connector_service::handle_unified_connector_service_response_for_payment_get(
payment_get_response.clone(),
)
.change_context(errors::ConnectorError::ProcessingStepFailed(None))
.attach_printable("Failed to process UCS webhook response using PSync handler")?;
let mut updated_router_data = router_data;
updated_router_data.status = status;
let _ = router_data_response.map_err(|error_response| {
updated_router_data.response = Err(error_response);
});
updated_router_data.raw_connector_response =
payment_get_response.raw_connector_response.map(Secret::new);
updated_router_data.connector_http_status_code = Some(status_code);
Ok(updated_router_data)
}
fn store_raw_connector_response_if_required<T, Req, Resp>(
return_raw_connector_response: Option<bool>,
router_data: &mut types::RouterData<T, Req, Resp>,
@ -186,6 +242,9 @@ where
};
connector_integration.handle_response(req, None, response)
}
payments::CallConnectorAction::UCSHandleResponse(transform_data_bytes) => {
handle_ucs_response(router_data, transform_data_bytes)
}
payments::CallConnectorAction::Avoid => Ok(router_data),
payments::CallConnectorAction::StatusUpdate {
status,