refactor(access_token): handle timeout errors gracefully (#1882)

This commit is contained in:
Narayan Bhat
2023-08-08 15:18:23 +05:30
committed by GitHub
parent c205f064b9
commit cc4136f85f
4 changed files with 55 additions and 8 deletions

View File

@ -11,6 +11,8 @@ pub(crate) const ALPHABETS: [char; 62] = [
];
/// API client request timeout (in seconds)
pub const REQUEST_TIME_OUT: u64 = 30;
pub const REQUEST_TIMEOUT_ERROR_CODE: &str = "TIMEOUT";
pub const REQUEST_TIMEOUT_ERROR_MESSAGE: &str = "Connector did not respond in specified time";
///Payment intent fulfillment default timeout (in seconds)
pub const DEFAULT_FULFILLMENT_TIME: i64 = 15 * 60;

View File

@ -186,7 +186,7 @@ pub fn http_not_implemented() -> actix_web::HttpResponse<BoxBody> {
.error_response()
}
#[derive(Debug, thiserror::Error)]
#[derive(Debug, thiserror::Error, PartialEq)]
pub enum ApiClientError {
#[error("Header map construction failed")]
HeaderMapConstructionFailed,
@ -315,6 +315,8 @@ pub enum ConnectorError {
MissingPaymentMethodType,
#[error("Balance in the payment method is low")]
InSufficientBalanceInPaymentMethod,
#[error("Server responded with Request Timeout")]
RequestTimeoutReceived,
}
#[derive(Debug, thiserror::Error)]
@ -490,6 +492,18 @@ pub enum WebhooksFlowError {
MissingRequiredField { field_name: &'static str },
}
impl ApiClientError {
pub fn is_upstream_timeout(&self) -> bool {
self == &Self::RequestTimeoutReceived
}
}
impl ConnectorError {
pub fn is_connector_timeout(&self) -> bool {
self == &Self::RequestTimeoutReceived
}
}
#[cfg(feature = "detailed_errors")]
pub mod error_stack_parsing {

View File

@ -4,6 +4,7 @@ use common_utils::ext_traits::AsyncExt;
use error_stack::{IntoReport, ResultExt};
use crate::{
consts,
core::{
errors::{self, RouterResult},
payments,
@ -151,16 +152,38 @@ pub async fn refresh_connector_auth(
types::AccessToken,
> = connector.connector.get_connector_integration();
let access_token_router_data = services::execute_connector_processing_step(
let access_token_router_data_result = services::execute_connector_processing_step(
state,
connector_integration,
router_data,
payments::CallConnectorAction::Trigger,
None,
)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Could not refresh access token")?;
.await;
let access_token_router_data = match access_token_router_data_result {
Ok(router_data) => Ok(router_data.response),
Err(connector_error) => {
// If we receive a timeout error from the connector, then
// the error has to be handled gracefully by updating the payment status to failed.
// further payment flow will not be continued
if connector_error.current_context().is_connector_timeout() {
let error_response = types::ErrorResponse {
code: consts::REQUEST_TIMEOUT_ERROR_CODE.to_string(),
message: consts::REQUEST_TIMEOUT_ERROR_MESSAGE.to_string(),
reason: Some(consts::REQUEST_TIMEOUT_ERROR_MESSAGE.to_string()),
status_code: 200,
};
Ok(Err(error_response))
} else {
Err(connector_error
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Could not refresh access token"))
}
}
}?;
metrics::ACCESS_TOKEN_CREATION.add(
&metrics::CONTEXT,
1,
@ -169,5 +192,5 @@ pub async fn refresh_connector_auth(
connector.connector_name.to_string(),
)],
);
Ok(access_token_router_data.response)
Ok(access_token_router_data)
}

View File

@ -325,8 +325,16 @@ where
};
Ok(response)
}
Err(error) => Err(error
.change_context(errors::ConnectorError::ProcessingStepFailed(None))),
Err(error) => {
if error.current_context().is_upstream_timeout() {
Err(error
.change_context(errors::ConnectorError::RequestTimeoutReceived))
} else {
Err(error.change_context(
errors::ConnectorError::ProcessingStepFailed(None),
))
}
}
}
}
None => Ok(router_data),