diff --git a/crates/api_models/src/webhooks.rs b/crates/api_models/src/webhooks.rs index edb96b4c20..b9052a754e 100644 --- a/crates/api_models/src/webhooks.rs +++ b/crates/api_models/src/webhooks.rs @@ -91,5 +91,8 @@ pub enum OutgoingWebhookContent { DisputeDetails(Box), } -pub trait OutgoingWebhookType: Serialize + From + Sync + Send {} +pub trait OutgoingWebhookType: + Serialize + From + Sync + Send + std::fmt::Debug +{ +} impl OutgoingWebhookType for OutgoingWebhook {} diff --git a/crates/router/src/compatibility/stripe/payment_intents/types.rs b/crates/router/src/compatibility/stripe/payment_intents/types.rs index 8c2796b434..0206cf9600 100644 --- a/crates/router/src/compatibility/stripe/payment_intents/types.rs +++ b/crates/router/src/compatibility/stripe/payment_intents/types.rs @@ -37,7 +37,7 @@ impl From for payments::Address { } } -#[derive(Default, Serialize, PartialEq, Eq, Deserialize, Clone)] +#[derive(Default, Serialize, PartialEq, Eq, Deserialize, Clone, Debug)] pub struct StripeCard { pub number: cards::CardNumber, pub exp_month: pii::Secret, @@ -221,7 +221,7 @@ impl TryFrom for payments::PaymentsRequest { } } -#[derive(Clone, Default, Eq, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Default, Eq, PartialEq, Serialize, Deserialize, Debug)] #[serde(rename_all = "snake_case")] pub enum StripePaymentStatus { Succeeded, @@ -288,7 +288,7 @@ pub struct StripeCaptureRequest { pub amount_to_capture: Option, } -#[derive(Default, Eq, PartialEq, Serialize)] +#[derive(Default, Eq, PartialEq, Serialize, Debug)] pub struct StripePaymentIntentResponse { pub id: Option, pub object: &'static str, @@ -328,7 +328,7 @@ pub struct StripePaymentIntentResponse { pub last_payment_error: Option, } -#[derive(Default, Eq, PartialEq, Serialize)] +#[derive(Default, Eq, PartialEq, Serialize, Debug)] pub struct LastPaymentError { charge: Option, code: Option, @@ -402,7 +402,7 @@ impl From for StripePaymentIntentResponse { } } -#[derive(Default, Eq, PartialEq, Serialize)] +#[derive(Default, Eq, PartialEq, Serialize, Debug)] pub struct StripePaymentMethod { #[serde(rename = "id")] payment_method_id: String, @@ -414,7 +414,7 @@ pub struct StripePaymentMethod { livemode: bool, } -#[derive(Default, Eq, PartialEq, Serialize)] +#[derive(Default, Eq, PartialEq, Serialize, Debug)] pub struct Charges { object: &'static str, data: Vec, @@ -604,13 +604,13 @@ impl ForeignFrom> for api_models::enums::AuthenticationType { } } -#[derive(Default, Eq, PartialEq, Serialize)] +#[derive(Default, Eq, PartialEq, Serialize, Debug)] pub struct RedirectUrl { pub return_url: Option, pub url: Option, } -#[derive(Eq, PartialEq, Serialize)] +#[derive(Eq, PartialEq, Serialize, Debug)] pub struct StripeNextAction { #[serde(rename = "type")] stype: payments::NextActionType, diff --git a/crates/router/src/compatibility/stripe/refunds/types.rs b/crates/router/src/compatibility/stripe/refunds/types.rs index c45ba9e6e0..e148618649 100644 --- a/crates/router/src/compatibility/stripe/refunds/types.rs +++ b/crates/router/src/compatibility/stripe/refunds/types.rs @@ -20,7 +20,7 @@ pub struct StripeUpdateRefundRequest { pub metadata: Option, } -#[derive(Clone, Serialize, PartialEq, Eq)] +#[derive(Clone, Serialize, PartialEq, Eq, Debug)] pub struct StripeRefundResponse { pub id: String, pub amount: i64, @@ -31,7 +31,7 @@ pub struct StripeRefundResponse { pub metadata: pii::SecretSerdeValue, } -#[derive(Clone, Serialize, Deserialize, Eq, PartialEq)] +#[derive(Clone, Serialize, Deserialize, Eq, PartialEq, Debug)] #[serde(rename_all = "snake_case")] pub enum StripeRefundStatus { Succeeded, diff --git a/crates/router/src/compatibility/stripe/webhooks.rs b/crates/router/src/compatibility/stripe/webhooks.rs index 23952f2aef..3e4ebf5b08 100644 --- a/crates/router/src/compatibility/stripe/webhooks.rs +++ b/crates/router/src/compatibility/stripe/webhooks.rs @@ -8,7 +8,7 @@ use super::{ payment_intents::types::StripePaymentIntentResponse, refunds::types::StripeRefundResponse, }; -#[derive(Serialize)] +#[derive(Serialize, Debug)] pub struct StripeOutgoingWebhook { id: Option, #[serde(rename = "type")] @@ -18,7 +18,7 @@ pub struct StripeOutgoingWebhook { impl api::OutgoingWebhookType for StripeOutgoingWebhook {} -#[derive(Serialize)] +#[derive(Serialize, Debug)] #[serde(tag = "type", content = "object", rename_all = "snake_case")] pub enum StripeWebhookObject { PaymentIntent(StripePaymentIntentResponse), @@ -26,7 +26,7 @@ pub enum StripeWebhookObject { Dispute(StripeDisputeResponse), } -#[derive(Serialize)] +#[derive(Serialize, Debug)] pub struct StripeDisputeResponse { pub id: String, pub amount: String, @@ -36,7 +36,7 @@ pub struct StripeDisputeResponse { pub status: StripeDisputeStatus, } -#[derive(Serialize)] +#[derive(Serialize, Debug)] #[serde(rename_all = "snake_case")] pub enum StripeDisputeStatus { WarningNeedsResponse, diff --git a/crates/router/src/core/errors.rs b/crates/router/src/core/errors.rs index 356580a520..d1556cb26a 100644 --- a/crates/router/src/core/errors.rs +++ b/crates/router/src/core/errors.rs @@ -460,6 +460,8 @@ pub enum WebhooksFlowError { NotImplemented, #[error("Dispute webhook status validation failed")] DisputeWebhookValidationFailed, + #[error("Outgoing webhook body encoding failed")] + OutgoingWebhookEncodingFailed, } #[cfg(feature = "detailed_errors")] diff --git a/crates/router/src/core/webhooks.rs b/crates/router/src/core/webhooks.rs index 4f7fb9a5fe..234128e6dc 100644 --- a/crates/router/src/core/webhooks.rs +++ b/crates/router/src/core/webhooks.rs @@ -12,7 +12,6 @@ use crate::{ errors::{self, CustomResult, RouterResponse}, payments, refunds, }, - db::StorageInterface, logger, routes::AppState, services, @@ -24,7 +23,7 @@ use crate::{ utils::{generate_id, Encode, OptionExt, ValueExt}, }; -const OUTGOING_WEBHOOK_TIMEOUT_MS: u64 = 5000; +const OUTGOING_WEBHOOK_TIMEOUT_SECS: u64 = 5; #[instrument(skip_all)] async fn payments_incoming_webhook_flow( @@ -403,8 +402,7 @@ async fn create_event_and_trigger_outgoing_webhook( arbiter.spawn(async move { let result = - trigger_webhook_to_merchant::(merchant_account, outgoing_webhook, state.store) - .await; + trigger_webhook_to_merchant::(merchant_account, outgoing_webhook, &state).await; if let Err(e) = result { logger::error!(?e); @@ -418,7 +416,7 @@ async fn create_event_and_trigger_outgoing_webhook( async fn trigger_webhook_to_merchant( merchant_account: storage::MerchantAccount, webhook: api::OutgoingWebhook, - db: Box, + state: &AppState, ) -> CustomResult<(), errors::WebhooksFlowError> { let webhook_details_json = merchant_account .webhook_details @@ -440,29 +438,38 @@ async fn trigger_webhook_to_merchant( let transformed_outgoing_webhook = W::from(webhook); - let response = reqwest::Client::new() - .post(&webhook_url) - .header(reqwest::header::CONTENT_TYPE, "application/json") - .json(&transformed_outgoing_webhook) - .timeout(core::time::Duration::from_millis( - OUTGOING_WEBHOOK_TIMEOUT_MS, - )) - .send() - .await; + let transformed_outgoing_webhook_string = + Encode::::encode_to_string_of_json(&transformed_outgoing_webhook) + .change_context(errors::WebhooksFlowError::OutgoingWebhookEncodingFailed) + .attach_printable("There was an issue when encoding the outgoing webhook body")?; + + let request = services::RequestBuilder::new() + .method(services::Method::Post) + .url(&webhook_url) + .attach_default_headers() + .headers(vec![( + reqwest::header::CONTENT_TYPE.to_string(), + "application/json".into(), + )]) + .body(Some(transformed_outgoing_webhook_string)) + .build(); + + let response = + services::api::send_request(state, request, Some(OUTGOING_WEBHOOK_TIMEOUT_SECS)).await; match response { Err(e) => { // [#217]: Schedule webhook for retry. - Err(e) - .into_report() - .change_context(errors::WebhooksFlowError::CallToMerchantFailed)?; + Err(e).change_context(errors::WebhooksFlowError::CallToMerchantFailed)?; } Ok(res) => { if res.status().is_success() { let update_event = storage::EventUpdate::UpdateWebhookNotified { is_webhook_notified: Some(true), }; - db.update_event(outgoing_webhook_event_id, update_event) + state + .store + .update_event(outgoing_webhook_event_id, update_event) .await .change_context(errors::WebhooksFlowError::WebhookEventUpdationFailed)?; } else { diff --git a/crates/router/src/services/api.rs b/crates/router/src/services/api.rs index ca0e860284..c2340ab106 100644 --- a/crates/router/src/services/api.rs +++ b/crates/router/src/services/api.rs @@ -287,7 +287,7 @@ pub async fn call_connector_api( ) -> CustomResult, errors::ApiClientError> { let current_time = Instant::now(); - let response = send_request(state, request).await; + let response = send_request(state, request, None).await; let elapsed_time = current_time.elapsed(); logger::info!(request_time=?elapsed_time); @@ -296,9 +296,10 @@ pub async fn call_connector_api( } #[instrument(skip_all)] -async fn send_request( +pub async fn send_request( state: &AppState, request: Request, + option_timeout_secs: Option, ) -> CustomResult { logger::debug!(method=?request.method, headers=?request.headers, payload=?request.payload, ?request); let url = &request.url; @@ -356,7 +357,9 @@ async fn send_request( Method::Delete => client.delete(url), } .add_headers(headers) - .timeout(Duration::from_secs(crate::consts::REQUEST_TIME_OUT)) + .timeout(Duration::from_secs( + option_timeout_secs.unwrap_or(crate::consts::REQUEST_TIME_OUT), + )) .send() .await .map_err(|error| match error {