mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-27 19:46:48 +08:00
feat(stripe): get error message for failed redirect payments (#615)
This commit is contained in:
@ -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 {
|
||||
|
||||
@ -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())
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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(),
|
||||
|
||||
Reference in New Issue
Block a user