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 .last_name
.clone() .clone()
.map_or("".to_string(), |last_name| last_name.peek().to_string()); .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() { if !name.is_empty() {
Some(Secret::new(name)) Some(Secret::new(name))
} else { } else {

View File

@ -266,7 +266,7 @@ impl
{ {
let response: stripe::PaymentIntentResponse = res let response: stripe::PaymentIntentResponse = res
.response .response
.parse_struct("PaymentIntentResponse") .parse_struct("PaymentSyncResponse")
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?; .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
types::RouterData::try_from(types::ResponseRouterData { types::RouterData::try_from(types::ResponseRouterData {
response, response,
@ -982,7 +982,12 @@ impl services::ConnectorRedirectResponse for Stripe {
Ok(query Ok(query
.redirect_status .redirect_status
.map_or(payments::CallConnectorAction::Trigger, |status| { .map_or(payments::CallConnectorAction::Trigger, |status| {
// Get failed error message by triggering call to connector
if status == transformers::StripePaymentStatus::Failed {
payments::CallConnectorAction::Trigger
} else {
payments::CallConnectorAction::StatusUpdate(status.into()) 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 api_models::{self, payments};
use common_utils::{fp_utils, pii::Email}; 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 struct PaymentIntentResponse {
pub id: String, pub id: String,
pub object: String, pub object: String,
@ -477,6 +477,22 @@ pub struct PaymentIntentResponse {
pub metadata: StripeMetadata, pub metadata: StripeMetadata,
pub next_action: Option<StripeNextActionResponse>, pub next_action: Option<StripeNextActionResponse>,
pub payment_method_options: Option<StripePaymentMethodOptions>, 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)] #[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> impl<F, T>
TryFrom<types::ResponseRouterData<F, SetupIntentResponse, T, types::PaymentsResponseData>> TryFrom<types::ResponseRouterData<F, SetupIntentResponse, T, types::PaymentsResponseData>>
for types::RouterData<F, 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) => ( Err(err) => (
Some(storage::PaymentAttemptUpdate::ErrorUpdate { Some(storage::PaymentAttemptUpdate::ErrorUpdate {
connector: Some(router_data.connector.clone()), connector: Some(router_data.connector.clone()),
status: storage::enums::AttemptStatus::Failure, status: router_data.status.foreign_into(),
error_message: Some(err.message), error_message: Some(err.message),
error_code: Some(err.code), 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 { let payment_intent_update = match router_data.response {
Err(_) => storage::PaymentIntentUpdate::PGStatusUpdate { Err(_) => storage::PaymentIntentUpdate::PGStatusUpdate {
status: enums::IntentStatus::Failed, status: router_data.status.foreign_into(),
}, },
Ok(_) => storage::PaymentIntentUpdate::ResponseUpdate { Ok(_) => storage::PaymentIntentUpdate::ResponseUpdate {
status: router_data.status.foreign_into(), status: router_data.status.foreign_into(),