feat(stripe): get error message for failed redirect payments (#615)

This commit is contained in:
Narayan Bhat
2023-02-26 16:46:59 +05:30
committed by GitHub
parent f3224cc4df
commit 12f25f057d
4 changed files with 85 additions and 7 deletions

View File

@ -130,7 +130,7 @@ fn get_payer_name(address: &AddressDetails) -> Option<Secret<String>> {
.last_name
.clone()
.map_or("".to_string(), |last_name| last_name.peek().to_string());
let name: String = format!("{} {}", first_name, last_name).trim().to_string();
let name: String = format!("{first_name} {last_name}").trim().to_string();
if !name.is_empty() {
Some(Secret::new(name))
} else {

View File

@ -266,7 +266,7 @@ impl
{
let response: stripe::PaymentIntentResponse = res
.response
.parse_struct("PaymentIntentResponse")
.parse_struct("PaymentSyncResponse")
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
types::RouterData::try_from(types::ResponseRouterData {
response,
@ -982,7 +982,12 @@ impl services::ConnectorRedirectResponse for Stripe {
Ok(query
.redirect_status
.map_or(payments::CallConnectorAction::Trigger, |status| {
payments::CallConnectorAction::StatusUpdate(status.into())
// Get failed error message by triggering call to connector
if status == transformers::StripePaymentStatus::Failed {
payments::CallConnectorAction::Trigger
} else {
payments::CallConnectorAction::StatusUpdate(status.into())
}
}))
}
}

View File

@ -1,4 +1,4 @@
use std::str::FromStr;
use std::{ops::Deref, str::FromStr};
use api_models::{self, payments};
use common_utils::{fp_utils, pii::Email};
@ -459,7 +459,7 @@ impl From<StripePaymentStatus> for enums::AttemptStatus {
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize)]
#[derive(Debug, Default, Eq, PartialEq, Deserialize)]
pub struct PaymentIntentResponse {
pub id: String,
pub object: String,
@ -477,6 +477,22 @@ pub struct PaymentIntentResponse {
pub metadata: StripeMetadata,
pub next_action: Option<StripeNextActionResponse>,
pub payment_method_options: Option<StripePaymentMethodOptions>,
pub last_payment_error: Option<ErrorDetails>,
}
#[derive(Debug, Default, Eq, PartialEq, Deserialize)]
pub struct PaymentSyncResponse {
#[serde(flatten)]
pub intent_fields: PaymentIntentResponse,
pub last_payment_error: Option<ErrorDetails>,
}
impl Deref for PaymentSyncResponse {
type Target = PaymentIntentResponse;
fn deref(&self) -> &Self::Target {
&self.intent_fields
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize)]
@ -538,6 +554,63 @@ impl<F, T>
}
}
impl<F, T>
TryFrom<types::ResponseRouterData<F, PaymentSyncResponse, T, types::PaymentsResponseData>>
for types::RouterData<F, T, types::PaymentsResponseData>
{
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(
item: types::ResponseRouterData<F, PaymentSyncResponse, T, types::PaymentsResponseData>,
) -> Result<Self, Self::Error> {
let redirection_data = item.response.next_action.as_ref().map(
|StripeNextActionResponse::RedirectToUrl(response)| {
services::RedirectForm::from((response.url.clone(), services::Method::Get))
},
);
let mandate_reference =
item.response
.payment_method_options
.to_owned()
.and_then(|payment_method_options| match payment_method_options {
StripePaymentMethodOptions::Card {
mandate_options, ..
} => mandate_options.map(|mandate_options| mandate_options.reference),
StripePaymentMethodOptions::Klarna {}
| StripePaymentMethodOptions::Affirm {}
| StripePaymentMethodOptions::AfterpayClearpay {} => None,
});
let error_res =
item.response
.last_payment_error
.as_ref()
.map(|error| types::ErrorResponse {
code: error.code.to_owned().unwrap_or_default(),
message: error.message.to_owned().unwrap_or_default(),
reason: None,
status_code: item.http_code,
});
let response = error_res.map_or(
Ok(types::PaymentsResponseData::TransactionResponse {
resource_id: types::ResponseId::ConnectorTransactionId(item.response.id.clone()),
redirection_data,
mandate_reference,
connector_metadata: None,
}),
Err,
);
Ok(Self {
status: enums::AttemptStatus::from(item.response.status.to_owned()),
response,
amount_captured: Some(item.response.amount_received),
..item.data
})
}
}
impl<F, T>
TryFrom<types::ResponseRouterData<F, SetupIntentResponse, T, types::PaymentsResponseData>>
for types::RouterData<F, T, types::PaymentsResponseData>

View File

@ -273,7 +273,7 @@ async fn payment_response_update_tracker<F: Clone, T>(
Err(err) => (
Some(storage::PaymentAttemptUpdate::ErrorUpdate {
connector: Some(router_data.connector.clone()),
status: storage::enums::AttemptStatus::Failure,
status: router_data.status.foreign_into(),
error_message: Some(err.message),
error_code: Some(err.code),
}),
@ -364,7 +364,7 @@ async fn payment_response_update_tracker<F: Clone, T>(
let payment_intent_update = match router_data.response {
Err(_) => storage::PaymentIntentUpdate::PGStatusUpdate {
status: enums::IntentStatus::Failed,
status: router_data.status.foreign_into(),
},
Ok(_) => storage::PaymentIntentUpdate::ResponseUpdate {
status: router_data.status.foreign_into(),