mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 17:19:15 +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
|
.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 {
|
||||||
|
|||||||
@ -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())
|
||||||
|
}
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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(),
|
||||||
|
|||||||
Reference in New Issue
Block a user