mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 17:19:15 +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:
@ -363,7 +363,7 @@ pub async fn payouts_create_core(
|
||||
.await?
|
||||
};
|
||||
|
||||
response_handler(&state, &merchant_context, &payout_data).await
|
||||
trigger_webhook_and_handle_response(&state, &merchant_context, &payout_data).await
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
@ -426,7 +426,7 @@ pub async fn payouts_confirm_core(
|
||||
)
|
||||
.await?;
|
||||
|
||||
response_handler(&state, &merchant_context, &payout_data).await
|
||||
trigger_webhook_and_handle_response(&state, &merchant_context, &payout_data).await
|
||||
}
|
||||
|
||||
pub async fn payouts_update_core(
|
||||
@ -498,7 +498,7 @@ pub async fn payouts_update_core(
|
||||
.await?;
|
||||
}
|
||||
|
||||
response_handler(&state, &merchant_context, &payout_data).await
|
||||
trigger_webhook_and_handle_response(&state, &merchant_context, &payout_data).await
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "payouts", feature = "v1"))]
|
||||
@ -541,7 +541,9 @@ pub async fn payouts_retrieve_core(
|
||||
.await?;
|
||||
}
|
||||
|
||||
response_handler(&state, &merchant_context, &payout_data).await
|
||||
Ok(services::ApplicationResponse::Json(
|
||||
response_handler(&state, &merchant_context, &payout_data).await?,
|
||||
))
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
@ -632,7 +634,9 @@ pub async fn payouts_cancel_core(
|
||||
.attach_printable("Payout cancellation failed for given Payout request")?;
|
||||
}
|
||||
|
||||
response_handler(&state, &merchant_context, &payout_data).await
|
||||
Ok(services::ApplicationResponse::Json(
|
||||
response_handler(&state, &merchant_context, &payout_data).await?,
|
||||
))
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
@ -722,7 +726,7 @@ pub async fn payouts_fulfill_core(
|
||||
}));
|
||||
}
|
||||
|
||||
response_handler(&state, &merchant_context, &payout_data).await
|
||||
trigger_webhook_and_handle_response(&state, &merchant_context, &payout_data).await
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "olap", feature = "v2"))]
|
||||
@ -2481,11 +2485,21 @@ pub async fn fulfill_payout(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn response_handler(
|
||||
pub async fn trigger_webhook_and_handle_response(
|
||||
state: &SessionState,
|
||||
merchant_context: &domain::MerchantContext,
|
||||
payout_data: &PayoutData,
|
||||
) -> RouterResponse<payouts::PayoutCreateResponse> {
|
||||
let response = response_handler(state, merchant_context, payout_data).await?;
|
||||
utils::trigger_payouts_webhook(state, merchant_context, &response).await?;
|
||||
Ok(services::ApplicationResponse::Json(response))
|
||||
}
|
||||
|
||||
pub async fn response_handler(
|
||||
state: &SessionState,
|
||||
merchant_context: &domain::MerchantContext,
|
||||
payout_data: &PayoutData,
|
||||
) -> RouterResult<payouts::PayoutCreateResponse> {
|
||||
let payout_attempt = payout_data.payout_attempt.to_owned();
|
||||
let payouts = payout_data.payouts.to_owned();
|
||||
|
||||
@ -2574,7 +2588,8 @@ pub async fn response_handler(
|
||||
.attach_printable("Failed to parse payout link's URL")?,
|
||||
payout_method_id,
|
||||
};
|
||||
Ok(services::ApplicationResponse::Json(response))
|
||||
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
#[cfg(feature = "v2")]
|
||||
|
||||
@ -864,7 +864,7 @@ async fn payments_incoming_webhook_flow(
|
||||
|
||||
let status = payments_response.status;
|
||||
|
||||
let event_type: Option<enums::EventType> = payments_response.status.foreign_into();
|
||||
let event_type: Option<enums::EventType> = payments_response.status.into();
|
||||
|
||||
// If event is NOT an UnsupportedEvent, trigger Outgoing Webhook
|
||||
if let Some(outgoing_event_type) = event_type {
|
||||
@ -984,20 +984,13 @@ async fn payouts_incoming_webhook_flow(
|
||||
)
|
||||
})?;
|
||||
|
||||
let event_type: Option<enums::EventType> = updated_payout_attempt.status.foreign_into();
|
||||
let event_type: Option<enums::EventType> = updated_payout_attempt.status.into();
|
||||
|
||||
// If event is NOT an UnsupportedEvent, trigger Outgoing Webhook
|
||||
if let Some(outgoing_event_type) = event_type {
|
||||
let router_response =
|
||||
let payout_create_response =
|
||||
payouts::response_handler(&state, &merchant_context, &payout_data).await?;
|
||||
|
||||
let payout_create_response: payout_models::PayoutCreateResponse = match router_response
|
||||
{
|
||||
services::ApplicationResponse::Json(response) => response,
|
||||
_ => Err(errors::ApiErrorResponse::WebhookResourceNotFound)
|
||||
.attach_printable("Failed to fetch the payout create response")?,
|
||||
};
|
||||
|
||||
Box::pin(super::create_event_and_trigger_outgoing_webhook(
|
||||
state,
|
||||
merchant_context,
|
||||
@ -1192,7 +1185,7 @@ async fn refunds_incoming_webhook_flow(
|
||||
.await
|
||||
.attach_printable_lazy(|| format!("Failed while updating refund: refund_id: {refund_id}"))?
|
||||
};
|
||||
let event_type: Option<enums::EventType> = updated_refund.refund_status.foreign_into();
|
||||
let event_type: Option<enums::EventType> = updated_refund.refund_status.into();
|
||||
|
||||
// If event is NOT an UnsupportedEvent, trigger Outgoing Webhook
|
||||
if let Some(outgoing_event_type) = event_type {
|
||||
@ -1508,8 +1501,7 @@ async fn external_authentication_incoming_webhook_flow(
|
||||
let payment_id = payments_response.payment_id.clone();
|
||||
|
||||
let status = payments_response.status;
|
||||
let event_type: Option<enums::EventType> =
|
||||
payments_response.status.foreign_into();
|
||||
let event_type: Option<enums::EventType> = payments_response.status.into();
|
||||
// Set poll_id as completed in redis to allow the fetch status of poll through retrieve_poll_status api from client
|
||||
let poll_id = core_utils::get_poll_id(
|
||||
merchant_context.get_merchant_account().get_id(),
|
||||
@ -1627,7 +1619,7 @@ async fn mandates_incoming_webhook_flow(
|
||||
)
|
||||
.await?,
|
||||
);
|
||||
let event_type: Option<enums::EventType> = updated_mandate.mandate_status.foreign_into();
|
||||
let event_type: Option<enums::EventType> = updated_mandate.mandate_status.into();
|
||||
if let Some(outgoing_event_type) = event_type {
|
||||
Box::pin(super::create_event_and_trigger_outgoing_webhook(
|
||||
state,
|
||||
@ -1730,7 +1722,7 @@ async fn frm_incoming_webhook_flow(
|
||||
services::ApplicationResponse::JsonWithHeaders((payments_response, _)) => {
|
||||
let payment_id = payments_response.payment_id.clone();
|
||||
let status = payments_response.status;
|
||||
let event_type: Option<enums::EventType> = payments_response.status.foreign_into();
|
||||
let event_type: Option<enums::EventType> = payments_response.status.into();
|
||||
if let Some(outgoing_event_type) = event_type {
|
||||
let primary_object_created_at = payments_response.created;
|
||||
Box::pin(super::create_event_and_trigger_outgoing_webhook(
|
||||
@ -1804,7 +1796,7 @@ async fn disputes_incoming_webhook_flow(
|
||||
)
|
||||
.await?;
|
||||
let disputes_response = Box::new(dispute_object.clone().foreign_into());
|
||||
let event_type: enums::EventType = dispute_object.dispute_status.foreign_into();
|
||||
let event_type: enums::EventType = dispute_object.dispute_status.into();
|
||||
|
||||
Box::pin(super::create_event_and_trigger_outgoing_webhook(
|
||||
state,
|
||||
@ -1886,7 +1878,7 @@ async fn bank_transfer_webhook_flow(
|
||||
services::ApplicationResponse::JsonWithHeaders((payments_response, _)) => {
|
||||
let payment_id = payments_response.payment_id.clone();
|
||||
|
||||
let event_type: Option<enums::EventType> = payments_response.status.foreign_into();
|
||||
let event_type: Option<enums::EventType> = payments_response.status.into();
|
||||
let status = payments_response.status;
|
||||
|
||||
// If event is NOT an UnsupportedEvent, trigger Outgoing Webhook
|
||||
|
||||
@ -535,7 +535,7 @@ async fn payments_incoming_webhook_flow(
|
||||
|
||||
let status = payments_response.status;
|
||||
|
||||
let event_type: Option<enums::EventType> = payments_response.status.foreign_into();
|
||||
let event_type: Option<enums::EventType> = payments_response.status.into();
|
||||
|
||||
// If event is NOT an UnsupportedEvent, trigger Outgoing Webhook
|
||||
if let Some(outgoing_event_type) = event_type {
|
||||
|
||||
@ -3,7 +3,7 @@ use router_env::{instrument, tracing, Flow};
|
||||
|
||||
use super::app::AppState;
|
||||
use crate::{
|
||||
core::{admin::*, api_locking},
|
||||
core::{admin::*, api_locking, errors},
|
||||
services::{api, authentication as auth, authorization::permissions::Permission},
|
||||
types::{api::admin, domain},
|
||||
};
|
||||
@ -186,11 +186,25 @@ pub async fn merchant_account_create(
|
||||
json_payload: web::Json<admin::MerchantAccountCreate>,
|
||||
) -> HttpResponse {
|
||||
let flow = Flow::MerchantsAccountCreate;
|
||||
let payload = json_payload.into_inner();
|
||||
if let Err(api_error) = payload
|
||||
.webhook_details
|
||||
.as_ref()
|
||||
.map(|details| {
|
||||
details
|
||||
.validate()
|
||||
.map_err(|message| errors::ApiErrorResponse::InvalidRequestData { message })
|
||||
})
|
||||
.transpose()
|
||||
{
|
||||
return api::log_and_return_error_response(api_error.into());
|
||||
}
|
||||
|
||||
Box::pin(api::server_wrap(
|
||||
flow,
|
||||
state,
|
||||
&req,
|
||||
json_payload.into_inner(),
|
||||
payload,
|
||||
|state, auth, req, _| create_merchant_account(state, req, auth),
|
||||
&auth::PlatformOrgAdminAuth {
|
||||
is_admin_auth_allowed: true,
|
||||
|
||||
@ -3,7 +3,7 @@ use router_env::{instrument, tracing, Flow};
|
||||
|
||||
use super::app::AppState;
|
||||
use crate::{
|
||||
core::{admin::*, api_locking},
|
||||
core::{admin::*, api_locking, errors},
|
||||
services::{api, authentication as auth, authorization::permissions},
|
||||
types::{api::admin, domain},
|
||||
};
|
||||
@ -19,6 +19,18 @@ pub async fn profile_create(
|
||||
let flow = Flow::ProfileCreate;
|
||||
let payload = json_payload.into_inner();
|
||||
let merchant_id = path.into_inner();
|
||||
if let Err(api_error) = payload
|
||||
.webhook_details
|
||||
.as_ref()
|
||||
.map(|details| {
|
||||
details
|
||||
.validate()
|
||||
.map_err(|message| errors::ApiErrorResponse::InvalidRequestData { message })
|
||||
})
|
||||
.transpose()
|
||||
{
|
||||
return api::log_and_return_error_response(api_error.into());
|
||||
}
|
||||
|
||||
Box::pin(api::server_wrap(
|
||||
flow,
|
||||
@ -53,6 +65,18 @@ pub async fn profile_create(
|
||||
) -> HttpResponse {
|
||||
let flow = Flow::ProfileCreate;
|
||||
let payload = json_payload.into_inner();
|
||||
if let Err(api_error) = payload
|
||||
.webhook_details
|
||||
.as_ref()
|
||||
.map(|details| {
|
||||
details
|
||||
.validate()
|
||||
.map_err(|message| errors::ApiErrorResponse::InvalidRequestData { message })
|
||||
})
|
||||
.transpose()
|
||||
{
|
||||
return api::log_and_return_error_response(api_error.into());
|
||||
}
|
||||
|
||||
Box::pin(api::server_wrap(
|
||||
flow,
|
||||
@ -158,12 +182,25 @@ pub async fn profile_update(
|
||||
) -> HttpResponse {
|
||||
let flow = Flow::ProfileUpdate;
|
||||
let (merchant_id, profile_id) = path.into_inner();
|
||||
let payload = json_payload.into_inner();
|
||||
if let Err(api_error) = payload
|
||||
.webhook_details
|
||||
.as_ref()
|
||||
.map(|details| {
|
||||
details
|
||||
.validate()
|
||||
.map_err(|message| errors::ApiErrorResponse::InvalidRequestData { message })
|
||||
})
|
||||
.transpose()
|
||||
{
|
||||
return api::log_and_return_error_response(api_error.into());
|
||||
}
|
||||
|
||||
Box::pin(api::server_wrap(
|
||||
flow,
|
||||
state,
|
||||
&req,
|
||||
json_payload.into_inner(),
|
||||
payload,
|
||||
|state, auth_data, req, _| update_profile(state, &profile_id, auth_data.key_store, req),
|
||||
auth::auth_type(
|
||||
&auth::HeaderAuth(auth::ApiKeyAuthWithMerchantIdFromRoute(merchant_id.clone())),
|
||||
@ -189,12 +226,25 @@ pub async fn profile_update(
|
||||
) -> HttpResponse {
|
||||
let flow = Flow::ProfileUpdate;
|
||||
let profile_id = path.into_inner();
|
||||
let payload = json_payload.into_inner();
|
||||
if let Err(api_error) = payload
|
||||
.webhook_details
|
||||
.as_ref()
|
||||
.map(|details| {
|
||||
details
|
||||
.validate()
|
||||
.map_err(|message| errors::ApiErrorResponse::InvalidRequestData { message })
|
||||
})
|
||||
.transpose()
|
||||
{
|
||||
return api::log_and_return_error_response(api_error.into());
|
||||
}
|
||||
|
||||
Box::pin(api::server_wrap(
|
||||
flow,
|
||||
state,
|
||||
&req,
|
||||
json_payload.into_inner(),
|
||||
payload,
|
||||
|state, auth::AuthenticationDataWithoutProfile { key_store, .. }, req, _| {
|
||||
update_profile(state, &profile_id, key_store, req)
|
||||
},
|
||||
|
||||
@ -443,31 +443,6 @@ impl ForeignFrom<payments::MandateAmountData> for storage_enums::MandateAmountDa
|
||||
}
|
||||
}
|
||||
|
||||
impl ForeignFrom<api_enums::IntentStatus> for Option<storage_enums::EventType> {
|
||||
fn foreign_from(value: api_enums::IntentStatus) -> Self {
|
||||
match value {
|
||||
api_enums::IntentStatus::Succeeded => Some(storage_enums::EventType::PaymentSucceeded),
|
||||
api_enums::IntentStatus::Failed => Some(storage_enums::EventType::PaymentFailed),
|
||||
api_enums::IntentStatus::Processing => {
|
||||
Some(storage_enums::EventType::PaymentProcessing)
|
||||
}
|
||||
api_enums::IntentStatus::RequiresMerchantAction
|
||||
| api_enums::IntentStatus::RequiresCustomerAction
|
||||
| api_enums::IntentStatus::Conflicted => Some(storage_enums::EventType::ActionRequired),
|
||||
api_enums::IntentStatus::Cancelled => Some(storage_enums::EventType::PaymentCancelled),
|
||||
api_enums::IntentStatus::PartiallyCaptured
|
||||
| api_enums::IntentStatus::PartiallyCapturedAndCapturable => {
|
||||
Some(storage_enums::EventType::PaymentCaptured)
|
||||
}
|
||||
api_enums::IntentStatus::RequiresCapture => {
|
||||
Some(storage_enums::EventType::PaymentAuthorized)
|
||||
}
|
||||
api_enums::IntentStatus::RequiresPaymentMethod
|
||||
| api_enums::IntentStatus::RequiresConfirmation => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ForeignFrom<api_enums::PaymentMethodType> for api_enums::PaymentMethod {
|
||||
fn foreign_from(payment_method_type: api_enums::PaymentMethodType) -> Self {
|
||||
match payment_method_type {
|
||||
@ -614,66 +589,6 @@ impl ForeignTryFrom<payments::PaymentMethodData> for api_enums::PaymentMethod {
|
||||
}
|
||||
}
|
||||
|
||||
impl ForeignFrom<storage_enums::RefundStatus> for Option<storage_enums::EventType> {
|
||||
fn foreign_from(value: storage_enums::RefundStatus) -> Self {
|
||||
match value {
|
||||
storage_enums::RefundStatus::Success => Some(storage_enums::EventType::RefundSucceeded),
|
||||
storage_enums::RefundStatus::Failure => Some(storage_enums::EventType::RefundFailed),
|
||||
api_enums::RefundStatus::ManualReview
|
||||
| api_enums::RefundStatus::Pending
|
||||
| api_enums::RefundStatus::TransactionFailure => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ForeignFrom<storage_enums::PayoutStatus> for Option<storage_enums::EventType> {
|
||||
fn foreign_from(value: storage_enums::PayoutStatus) -> Self {
|
||||
match value {
|
||||
storage_enums::PayoutStatus::Success => Some(storage_enums::EventType::PayoutSuccess),
|
||||
storage_enums::PayoutStatus::Failed => Some(storage_enums::EventType::PayoutFailed),
|
||||
storage_enums::PayoutStatus::Cancelled => {
|
||||
Some(storage_enums::EventType::PayoutCancelled)
|
||||
}
|
||||
storage_enums::PayoutStatus::Initiated => {
|
||||
Some(storage_enums::EventType::PayoutInitiated)
|
||||
}
|
||||
storage_enums::PayoutStatus::Expired => Some(storage_enums::EventType::PayoutExpired),
|
||||
storage_enums::PayoutStatus::Reversed => Some(storage_enums::EventType::PayoutReversed),
|
||||
storage_enums::PayoutStatus::Ineligible
|
||||
| storage_enums::PayoutStatus::Pending
|
||||
| storage_enums::PayoutStatus::RequiresCreation
|
||||
| storage_enums::PayoutStatus::RequiresFulfillment
|
||||
| storage_enums::PayoutStatus::RequiresPayoutMethodData
|
||||
| storage_enums::PayoutStatus::RequiresVendorAccountCreation
|
||||
| storage_enums::PayoutStatus::RequiresConfirmation => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ForeignFrom<storage_enums::DisputeStatus> for storage_enums::EventType {
|
||||
fn foreign_from(value: storage_enums::DisputeStatus) -> Self {
|
||||
match value {
|
||||
storage_enums::DisputeStatus::DisputeOpened => Self::DisputeOpened,
|
||||
storage_enums::DisputeStatus::DisputeExpired => Self::DisputeExpired,
|
||||
storage_enums::DisputeStatus::DisputeAccepted => Self::DisputeAccepted,
|
||||
storage_enums::DisputeStatus::DisputeCancelled => Self::DisputeCancelled,
|
||||
storage_enums::DisputeStatus::DisputeChallenged => Self::DisputeChallenged,
|
||||
storage_enums::DisputeStatus::DisputeWon => Self::DisputeWon,
|
||||
storage_enums::DisputeStatus::DisputeLost => Self::DisputeLost,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ForeignFrom<storage_enums::MandateStatus> for Option<storage_enums::EventType> {
|
||||
fn foreign_from(value: storage_enums::MandateStatus) -> Self {
|
||||
match value {
|
||||
storage_enums::MandateStatus::Active => Some(storage_enums::EventType::MandateActive),
|
||||
storage_enums::MandateStatus::Revoked => Some(storage_enums::EventType::MandateRevoked),
|
||||
storage_enums::MandateStatus::Inactive | storage_enums::MandateStatus::Pending => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ForeignTryFrom<api_models::webhooks::IncomingWebhookEvent> for storage_enums::RefundStatus {
|
||||
type Error = errors::ValidationError;
|
||||
|
||||
@ -2135,8 +2050,11 @@ impl ForeignFrom<api_models::admin::WebhookDetails>
|
||||
webhook_password: item.webhook_password,
|
||||
webhook_url: item.webhook_url,
|
||||
payment_created_enabled: item.payment_created_enabled,
|
||||
payment_succeeded_enabled: item.payment_succeeded_enabled,
|
||||
payment_failed_enabled: item.payment_failed_enabled,
|
||||
payment_succeeded_enabled: item.payment_succeeded_enabled,
|
||||
payment_statuses_enabled: item.payment_statuses_enabled,
|
||||
refund_statuses_enabled: item.refund_statuses_enabled,
|
||||
payout_statuses_enabled: item.payout_statuses_enabled,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2151,8 +2069,11 @@ impl ForeignFrom<diesel_models::business_profile::WebhookDetails>
|
||||
webhook_password: item.webhook_password,
|
||||
webhook_url: item.webhook_url,
|
||||
payment_created_enabled: item.payment_created_enabled,
|
||||
payment_succeeded_enabled: item.payment_succeeded_enabled,
|
||||
payment_failed_enabled: item.payment_failed_enabled,
|
||||
payment_succeeded_enabled: item.payment_succeeded_enabled,
|
||||
payment_statuses_enabled: item.payment_statuses_enabled,
|
||||
refund_statuses_enabled: item.refund_statuses_enabled,
|
||||
payout_statuses_enabled: item.payout_statuses_enabled,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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!()
|
||||
}
|
||||
|
||||
@ -438,7 +438,7 @@ async fn get_outgoing_webhook_content_and_event_type(
|
||||
})
|
||||
}
|
||||
}?;
|
||||
let event_type = Option::<EventType>::foreign_from(payments_response.status);
|
||||
let event_type: Option<EventType> = payments_response.status.into();
|
||||
logger::debug!(current_resource_status=%payments_response.status);
|
||||
|
||||
Ok((
|
||||
@ -462,7 +462,7 @@ async fn get_outgoing_webhook_content_and_event_type(
|
||||
request,
|
||||
))
|
||||
.await?;
|
||||
let event_type = Option::<EventType>::foreign_from(refund.refund_status);
|
||||
let event_type: Option<EventType> = refund.refund_status.into();
|
||||
logger::debug!(current_resource_status=%refund.refund_status);
|
||||
let refund_response = RefundResponse::foreign_from(refund);
|
||||
|
||||
@ -495,7 +495,7 @@ async fn get_outgoing_webhook_content_and_event_type(
|
||||
}
|
||||
}
|
||||
.map(Box::new)?;
|
||||
let event_type = Some(EventType::foreign_from(dispute_response.dispute_status));
|
||||
let event_type = Some(EventType::from(dispute_response.dispute_status));
|
||||
logger::debug!(current_resource_status=%dispute_response.dispute_status);
|
||||
|
||||
Ok((
|
||||
@ -527,7 +527,7 @@ async fn get_outgoing_webhook_content_and_event_type(
|
||||
}
|
||||
}
|
||||
.map(Box::new)?;
|
||||
let event_type = Option::<EventType>::foreign_from(mandate_response.status);
|
||||
let event_type: Option<EventType> = mandate_response.status.into();
|
||||
logger::debug!(current_resource_status=%mandate_response.status);
|
||||
|
||||
Ok((
|
||||
@ -551,17 +551,10 @@ async fn get_outgoing_webhook_content_and_event_type(
|
||||
))
|
||||
.await?;
|
||||
|
||||
let router_response =
|
||||
let payout_create_response =
|
||||
payouts::response_handler(&state, &merchant_context, &payout_data).await?;
|
||||
|
||||
let payout_create_response: payout_models::PayoutCreateResponse = match router_response
|
||||
{
|
||||
ApplicationResponse::Json(response) => response,
|
||||
_ => Err(errors::ApiErrorResponse::WebhookResourceNotFound)
|
||||
.attach_printable("Failed to fetch the payout create response")?,
|
||||
};
|
||||
|
||||
let event_type = Option::<EventType>::foreign_from(payout_data.payout_attempt.status);
|
||||
let event_type: Option<EventType> = payout_data.payout_attempt.status.into();
|
||||
logger::debug!(current_resource_status=%payout_data.payout_attempt.status);
|
||||
|
||||
Ok((
|
||||
|
||||
Reference in New Issue
Block a user