mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-12-10 07:48:07 +08:00
feat(core): allow setting up status across payments, refunds and payouts for triggering webhooks in core resource flows (#8433)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
@@ -55,10 +55,7 @@ use crate::{
|
||||
logger,
|
||||
routes::{metrics, SessionState},
|
||||
services::{self, authentication::get_header_value_by_key},
|
||||
types::{
|
||||
self, domain,
|
||||
transformers::{ForeignFrom, ForeignInto},
|
||||
},
|
||||
types::{self, domain, transformers::ForeignInto},
|
||||
};
|
||||
#[cfg(feature = "v1")]
|
||||
use crate::{core::webhooks as webhooks_core, types::storage};
|
||||
@@ -1156,25 +1153,21 @@ where
|
||||
D: payments_core::OperationSessionGetters<F>,
|
||||
{
|
||||
let status = payment_data.get_payment_intent().status;
|
||||
let payment_id = payment_data.get_payment_intent().get_id().to_owned();
|
||||
let should_trigger_webhook = business_profile
|
||||
.get_payment_webhook_statuses()
|
||||
.contains(&status);
|
||||
|
||||
let captures = payment_data
|
||||
.get_multiple_capture_data()
|
||||
.map(|multiple_capture_data| {
|
||||
multiple_capture_data
|
||||
.get_all_captures()
|
||||
.into_iter()
|
||||
.cloned()
|
||||
.collect()
|
||||
});
|
||||
|
||||
if matches!(
|
||||
status,
|
||||
enums::IntentStatus::Succeeded
|
||||
| enums::IntentStatus::Failed
|
||||
| enums::IntentStatus::PartiallyCaptured
|
||||
| enums::IntentStatus::RequiresMerchantAction
|
||||
) {
|
||||
if should_trigger_webhook {
|
||||
let captures = payment_data
|
||||
.get_multiple_capture_data()
|
||||
.map(|multiple_capture_data| {
|
||||
multiple_capture_data
|
||||
.get_all_captures()
|
||||
.into_iter()
|
||||
.cloned()
|
||||
.collect()
|
||||
});
|
||||
let payment_id = payment_data.get_payment_intent().get_id().to_owned();
|
||||
let payments_response = crate::core::payments::transformers::payments_to_payments_response(
|
||||
payment_data,
|
||||
captures,
|
||||
@@ -1188,7 +1181,7 @@ where
|
||||
None,
|
||||
)?;
|
||||
|
||||
let event_type = ForeignFrom::foreign_from(status);
|
||||
let event_type = status.into();
|
||||
|
||||
if let services::ApplicationResponse::JsonWithHeaders((payments_response_json, _)) =
|
||||
payments_response
|
||||
@@ -1250,27 +1243,28 @@ pub async fn trigger_refund_outgoing_webhook(
|
||||
profile_id: id_type::ProfileId,
|
||||
) -> RouterResult<()> {
|
||||
let refund_status = refund.refund_status;
|
||||
if matches!(
|
||||
refund_status,
|
||||
enums::RefundStatus::Success
|
||||
| enums::RefundStatus::Failure
|
||||
| enums::RefundStatus::TransactionFailure
|
||||
) {
|
||||
let event_type = ForeignFrom::foreign_from(refund_status);
|
||||
|
||||
let key_manager_state = &(state).into();
|
||||
let business_profile = state
|
||||
.store
|
||||
.find_business_profile_by_profile_id(
|
||||
key_manager_state,
|
||||
merchant_context.get_merchant_key_store(),
|
||||
&profile_id,
|
||||
)
|
||||
.await
|
||||
.to_not_found_response(errors::ApiErrorResponse::ProfileNotFound {
|
||||
id: profile_id.get_string_repr().to_owned(),
|
||||
})?;
|
||||
|
||||
let should_trigger_webhook = business_profile
|
||||
.get_refund_webhook_statuses()
|
||||
.contains(&refund_status);
|
||||
|
||||
if should_trigger_webhook {
|
||||
let event_type = refund_status.into();
|
||||
let refund_response: api_models::refunds::RefundResponse = refund.clone().foreign_into();
|
||||
let key_manager_state = &(state).into();
|
||||
let refund_id = refund_response.refund_id.clone();
|
||||
let business_profile = state
|
||||
.store
|
||||
.find_business_profile_by_profile_id(
|
||||
key_manager_state,
|
||||
merchant_context.get_merchant_key_store(),
|
||||
&profile_id,
|
||||
)
|
||||
.await
|
||||
.to_not_found_response(errors::ApiErrorResponse::ProfileNotFound {
|
||||
id: profile_id.get_string_repr().to_owned(),
|
||||
})?;
|
||||
let cloned_state = state.clone();
|
||||
let cloned_merchant_context = merchant_context.clone();
|
||||
let primary_object_created_at = refund_response.created_at;
|
||||
@@ -1317,3 +1311,72 @@ pub fn get_locale_from_header(headers: &actix_web::http::header::HeaderMap) -> S
|
||||
.map(|val| val.to_string())
|
||||
.unwrap_or(common_utils::consts::DEFAULT_LOCALE.to_string())
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "payouts", feature = "v1"))]
|
||||
pub async fn trigger_payouts_webhook(
|
||||
state: &SessionState,
|
||||
merchant_context: &domain::MerchantContext,
|
||||
payout_response: &api_models::payouts::PayoutCreateResponse,
|
||||
) -> RouterResult<()> {
|
||||
let key_manager_state = &(state).into();
|
||||
let profile_id = &payout_response.profile_id;
|
||||
let business_profile = state
|
||||
.store
|
||||
.find_business_profile_by_profile_id(
|
||||
key_manager_state,
|
||||
merchant_context.get_merchant_key_store(),
|
||||
profile_id,
|
||||
)
|
||||
.await
|
||||
.to_not_found_response(errors::ApiErrorResponse::ProfileNotFound {
|
||||
id: profile_id.get_string_repr().to_owned(),
|
||||
})?;
|
||||
|
||||
let status = &payout_response.status;
|
||||
let should_trigger_webhook = business_profile
|
||||
.get_payout_webhook_statuses()
|
||||
.contains(status);
|
||||
|
||||
if should_trigger_webhook {
|
||||
let event_type = (*status).into();
|
||||
if let Some(event_type) = event_type {
|
||||
let cloned_merchant_context = merchant_context.clone();
|
||||
let cloned_state = state.clone();
|
||||
let cloned_response = payout_response.clone();
|
||||
|
||||
// This spawns this futures in a background thread, the exception inside this future won't affect
|
||||
// the current thread and the lifecycle of spawn thread is not handled by runtime.
|
||||
// So when server shutdown won't wait for this thread's completion.
|
||||
tokio::spawn(
|
||||
async move {
|
||||
let primary_object_created_at = cloned_response.created;
|
||||
Box::pin(webhooks_core::create_event_and_trigger_outgoing_webhook(
|
||||
cloned_state,
|
||||
cloned_merchant_context,
|
||||
business_profile,
|
||||
event_type,
|
||||
diesel_models::enums::EventClass::Payouts,
|
||||
cloned_response.payout_id.clone(),
|
||||
diesel_models::enums::EventObjectType::PayoutDetails,
|
||||
webhooks::OutgoingWebhookContent::PayoutDetails(Box::new(cloned_response)),
|
||||
primary_object_created_at,
|
||||
))
|
||||
.await
|
||||
}
|
||||
.in_current_span(),
|
||||
);
|
||||
} else {
|
||||
logger::warn!("Outgoing webhook not sent because of missing event type status mapping");
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "payouts", feature = "v2"))]
|
||||
pub async fn trigger_payouts_webhook(
|
||||
state: &SessionState,
|
||||
merchant_context: &domain::MerchantContext,
|
||||
payout_response: &api_models::payouts::PayoutCreateResponse,
|
||||
) -> RouterResult<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user