fix(connector): [Payme] populate error message in case of 2xx payment failures (#2037)

This commit is contained in:
Arjun Karthik
2023-08-28 22:34:05 +05:30
committed by GitHub
parent f04bee2611
commit aeebc5b525

View File

@ -9,6 +9,7 @@ use crate::{
self, missing_field_err, AddressDetailsData, CardData, PaymentsAuthorizeRequestData, self, missing_field_err, AddressDetailsData, CardData, PaymentsAuthorizeRequestData,
PaymentsPreProcessingData, PaymentsSyncRequestData, RouterData, PaymentsPreProcessingData, PaymentsSyncRequestData, RouterData,
}, },
consts,
core::errors, core::errors,
types::{self, api, storage::enums, MandateReference}, types::{self, api, storage::enums, MandateReference},
}; };
@ -120,30 +121,61 @@ impl<F, T>
fn try_from( fn try_from(
item: types::ResponseRouterData<F, PaymePaySaleResponse, T, types::PaymentsResponseData>, item: types::ResponseRouterData<F, PaymePaySaleResponse, T, types::PaymentsResponseData>,
) -> Result<Self, Self::Error> { ) -> Result<Self, Self::Error> {
let response = if item.response.sale_status == SaleStatus::Failed {
// To populate error message in case of failure
Err(types::ErrorResponse::from((&item.response, item.http_code)))
} else {
Ok(types::PaymentsResponseData::try_from(&item.response)?)
};
Ok(Self { Ok(Self {
status: enums::AttemptStatus::from(item.response.sale_status), status: enums::AttemptStatus::from(item.response.sale_status),
response: Ok(types::PaymentsResponseData::TransactionResponse { response,
resource_id: types::ResponseId::ConnectorTransactionId(item.response.payme_sale_id),
redirection_data: None,
mandate_reference: item.response.buyer_key.map(|buyer_key| MandateReference {
connector_mandate_id: Some(buyer_key.expose()),
payment_method_id: None,
}),
connector_metadata: Some(
serde_json::to_value(PaymeMetadata {
payme_transaction_id: item.response.payme_transaction_id,
})
.into_report()
.change_context(errors::ConnectorError::ResponseHandlingFailed)?,
),
network_txn_id: None,
connector_response_reference_id: None,
}),
..item.data ..item.data
}) })
} }
} }
impl From<(&PaymePaySaleResponse, u16)> for types::ErrorResponse {
fn from((pay_sale_response, http_code): (&PaymePaySaleResponse, u16)) -> Self {
let code = pay_sale_response
.status_error_code
.map(|error_code| error_code.to_string())
.unwrap_or(consts::NO_ERROR_CODE.to_string());
Self {
code,
message: pay_sale_response
.status_error_details
.clone()
.unwrap_or(consts::NO_ERROR_MESSAGE.to_string()),
reason: pay_sale_response.status_error_details.to_owned(),
status_code: http_code,
}
}
}
impl TryFrom<&PaymePaySaleResponse> for types::PaymentsResponseData {
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(value: &PaymePaySaleResponse) -> Result<Self, Self::Error> {
Ok(Self::TransactionResponse {
resource_id: types::ResponseId::ConnectorTransactionId(value.payme_sale_id.clone()),
redirection_data: None,
mandate_reference: value.buyer_key.clone().map(|buyer_key| MandateReference {
connector_mandate_id: Some(buyer_key.expose()),
payment_method_id: None,
}),
connector_metadata: Some(
serde_json::to_value(PaymeMetadata {
payme_transaction_id: value.payme_transaction_id.clone(),
})
.into_report()
.change_context(errors::ConnectorError::ResponseHandlingFailed)?,
),
network_txn_id: None,
connector_response_reference_id: None,
})
}
}
impl<F, T> TryFrom<types::ResponseRouterData<F, SaleQueryResponse, T, types::PaymentsResponseData>> impl<F, T> TryFrom<types::ResponseRouterData<F, SaleQueryResponse, T, types::PaymentsResponseData>>
for types::RouterData<F, T, types::PaymentsResponseData> for types::RouterData<F, T, types::PaymentsResponseData>
{ {
@ -158,24 +190,54 @@ impl<F, T> TryFrom<types::ResponseRouterData<F, SaleQueryResponse, T, types::Pay
.first() .first()
.cloned() .cloned()
.ok_or(errors::ConnectorError::ResponseHandlingFailed)?; .ok_or(errors::ConnectorError::ResponseHandlingFailed)?;
let response = if transaction_response.sale_status == SaleStatus::Failed {
// To populate error message in case of failure
Err(types::ErrorResponse::from((
&transaction_response,
item.http_code,
)))
} else {
Ok(types::PaymentsResponseData::from(&transaction_response))
};
Ok(Self { Ok(Self {
status: enums::AttemptStatus::from(transaction_response.sale_status), status: enums::AttemptStatus::from(transaction_response.sale_status),
response: Ok(types::PaymentsResponseData::TransactionResponse { response,
resource_id: types::ResponseId::ConnectorTransactionId(
transaction_response.sale_payme_id,
),
redirection_data: None,
// mandate reference will be updated with webhooks only. That has been handled with PaymePaySaleResponse struct
mandate_reference: None,
connector_metadata: None,
network_txn_id: None,
connector_response_reference_id: None,
}),
..item.data ..item.data
}) })
} }
} }
impl From<(&SaleQuery, u16)> for types::ErrorResponse {
fn from((sale_query_response, http_code): (&SaleQuery, u16)) -> Self {
Self {
code: sale_query_response
.sale_error_code
.clone()
.unwrap_or(consts::NO_ERROR_CODE.to_string()),
message: sale_query_response
.sale_error_text
.clone()
.unwrap_or(consts::NO_ERROR_MESSAGE.to_string()),
reason: sale_query_response.sale_error_text.clone(),
status_code: http_code,
}
}
}
impl From<&SaleQuery> for types::PaymentsResponseData {
fn from(value: &SaleQuery) -> Self {
Self::TransactionResponse {
resource_id: types::ResponseId::ConnectorTransactionId(value.sale_payme_id.clone()),
redirection_data: None,
// mandate reference will be updated with webhooks only. That has been handled with PaymePaySaleResponse struct
mandate_reference: None,
connector_metadata: None,
network_txn_id: None,
connector_response_reference_id: None,
}
}
}
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
#[serde(rename_all = "lowercase")] #[serde(rename_all = "lowercase")]
pub enum SaleType { pub enum SaleType {
@ -483,7 +545,7 @@ impl TryFrom<&types::PaymentsPreProcessingRouterData> for SaleType {
} }
} }
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")] #[serde(rename_all = "kebab-case")]
pub enum SaleStatus { pub enum SaleStatus {
Initial, Initial,
@ -527,6 +589,8 @@ pub struct SaleQueryResponse {
pub struct SaleQuery { pub struct SaleQuery {
sale_status: SaleStatus, sale_status: SaleStatus,
sale_payme_id: String, sale_payme_id: String,
sale_error_text: Option<String>,
sale_error_code: Option<String>,
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
@ -535,6 +599,8 @@ pub struct PaymePaySaleResponse {
payme_sale_id: String, payme_sale_id: String,
payme_transaction_id: String, payme_transaction_id: String,
buyer_key: Option<Secret<String>>, buyer_key: Option<Secret<String>>,
status_error_details: Option<String>,
status_error_code: Option<u32>,
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
@ -673,7 +739,7 @@ pub struct PaymeErrorResponse {
pub status_code: u16, pub status_code: u16,
pub status_error_details: String, pub status_error_details: String,
pub status_additional_info: serde_json::Value, pub status_additional_info: serde_json::Value,
pub status_error_code: u16, pub status_error_code: u32,
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
@ -695,6 +761,8 @@ pub struct WebhookEventDataResource {
pub notify_type: NotifyType, pub notify_type: NotifyType,
pub payme_sale_id: String, pub payme_sale_id: String,
pub payme_transaction_id: String, pub payme_transaction_id: String,
pub status_error_details: Option<String>,
pub status_error_code: Option<u32>,
} }
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
@ -715,6 +783,8 @@ impl From<WebhookEventDataResource> for PaymePaySaleResponse {
payme_sale_id: value.payme_sale_id, payme_sale_id: value.payme_sale_id,
payme_transaction_id: value.payme_transaction_id, payme_transaction_id: value.payme_transaction_id,
buyer_key: value.buyer_key, buyer_key: value.buyer_key,
status_error_code: value.status_error_code,
status_error_details: value.status_error_details,
} }
} }
} }