mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-11-02 04:04:43 +08:00
refactor(redirection): From impl for redirection data for ease of use (#613)
This commit is contained in:
@ -1,6 +1,3 @@
|
|||||||
use std::{collections::HashMap, str::FromStr};
|
|
||||||
|
|
||||||
use error_stack::{IntoReport, ResultExt};
|
|
||||||
use masking::PeekInterface;
|
use masking::PeekInterface;
|
||||||
use reqwest::Url;
|
use reqwest::Url;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@ -195,11 +192,11 @@ pub struct AdyenRedirectionResponse {
|
|||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct AdyenRedirectionAction {
|
pub struct AdyenRedirectionAction {
|
||||||
payment_method_type: String,
|
payment_method_type: String,
|
||||||
url: String,
|
url: Url,
|
||||||
method: String,
|
method: services::Method,
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
type_of_response: String,
|
type_of_response: String,
|
||||||
data: Option<HashMap<String, String>>,
|
data: Option<std::collections::HashMap<String, String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
|
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
|
||||||
@ -755,26 +752,20 @@ pub fn get_redirection_response(
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let redirection_url_response = Url::parse(&response.action.url)
|
let form_fields = response.action.data.unwrap_or_else(|| {
|
||||||
.into_report()
|
std::collections::HashMap::from_iter(
|
||||||
.change_context(errors::ConnectorError::ResponseHandlingFailed)
|
response
|
||||||
.attach_printable("Failed to parse redirection url")?;
|
.action
|
||||||
|
.url
|
||||||
let form_field_for_redirection = match response.action.data {
|
|
||||||
Some(data) => data,
|
|
||||||
None => std::collections::HashMap::from_iter(
|
|
||||||
redirection_url_response
|
|
||||||
.query_pairs()
|
.query_pairs()
|
||||||
.map(|(k, v)| (k.to_string(), v.to_string())),
|
.map(|(key, value)| (key.to_string(), value.to_string())),
|
||||||
),
|
)
|
||||||
};
|
});
|
||||||
|
|
||||||
let redirection_data = services::RedirectForm {
|
let redirection_data = services::RedirectForm {
|
||||||
url: redirection_url_response.to_string(),
|
endpoint: response.action.url.to_string(),
|
||||||
method: services::Method::from_str(&response.action.method)
|
method: response.action.method,
|
||||||
.into_report()
|
form_fields,
|
||||||
.change_context(errors::ConnectorError::ResponseHandlingFailed)?,
|
|
||||||
form_fields: form_field_for_redirection,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// We don't get connector transaction id for redirections in Adyen.
|
// We don't get connector transaction id for redirections in Adyen.
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
use error_stack::{IntoReport, ResultExt};
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
@ -167,15 +166,18 @@ impl From<transformers::Foreign<(CheckoutPaymentStatus, Option<enums::CaptureMet
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<transformers::Foreign<(CheckoutPaymentStatus, Balances)>>
|
impl From<transformers::Foreign<(CheckoutPaymentStatus, Option<Balances>)>>
|
||||||
for transformers::Foreign<enums::AttemptStatus>
|
for transformers::Foreign<enums::AttemptStatus>
|
||||||
{
|
{
|
||||||
fn from(item: transformers::Foreign<(CheckoutPaymentStatus, Balances)>) -> Self {
|
fn from(item: transformers::Foreign<(CheckoutPaymentStatus, Option<Balances>)>) -> Self {
|
||||||
let (status, balances) = item.0;
|
let (status, balances) = item.0;
|
||||||
|
|
||||||
match status {
|
match status {
|
||||||
CheckoutPaymentStatus::Authorized => {
|
CheckoutPaymentStatus::Authorized => {
|
||||||
if balances.available_to_capture == 0 {
|
if let Some(Balances {
|
||||||
|
available_to_capture: 0,
|
||||||
|
}) = balances
|
||||||
|
{
|
||||||
enums::AttemptStatus::Charged
|
enums::AttemptStatus::Charged
|
||||||
} else {
|
} else {
|
||||||
enums::AttemptStatus::Authorized
|
enums::AttemptStatus::Authorized
|
||||||
@ -190,9 +192,10 @@ impl From<transformers::Foreign<(CheckoutPaymentStatus, Balances)>>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize)]
|
#[derive(Clone, Debug, Eq, PartialEq, Deserialize)]
|
||||||
pub struct Href {
|
pub struct Href {
|
||||||
href: String,
|
#[serde(rename = "href")]
|
||||||
|
redirection_url: Url,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize)]
|
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize)]
|
||||||
@ -206,7 +209,7 @@ pub struct PaymentsResponse {
|
|||||||
status: CheckoutPaymentStatus,
|
status: CheckoutPaymentStatus,
|
||||||
#[serde(rename = "_links")]
|
#[serde(rename = "_links")]
|
||||||
links: Links,
|
links: Links,
|
||||||
balances: Balances,
|
balances: Option<Balances>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize)]
|
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize)]
|
||||||
@ -221,24 +224,10 @@ impl TryFrom<types::PaymentsResponseRouterData<PaymentsResponse>>
|
|||||||
fn try_from(
|
fn try_from(
|
||||||
item: types::PaymentsResponseRouterData<PaymentsResponse>,
|
item: types::PaymentsResponseRouterData<PaymentsResponse>,
|
||||||
) -> Result<Self, Self::Error> {
|
) -> Result<Self, Self::Error> {
|
||||||
let redirection_url = item
|
let redirection_data = item.response.links.redirect.map(|href| {
|
||||||
.response
|
services::RedirectForm::from((href.redirection_url, services::Method::Get))
|
||||||
.links
|
|
||||||
.redirect
|
|
||||||
.map(|data| Url::parse(&data.href))
|
|
||||||
.transpose()
|
|
||||||
.into_report()
|
|
||||||
.change_context(errors::ConnectorError::ResponseHandlingFailed)
|
|
||||||
.attach_printable("Could not parse the redirection data")?;
|
|
||||||
|
|
||||||
let redirection_data = redirection_url.map(|url| services::RedirectForm {
|
|
||||||
url: url.to_string(),
|
|
||||||
method: services::Method::Get,
|
|
||||||
form_fields: std::collections::HashMap::from_iter(
|
|
||||||
url.query_pairs()
|
|
||||||
.map(|(k, v)| (k.to_string(), v.to_string())),
|
|
||||||
),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
status: enums::AttemptStatus::foreign_from((
|
status: enums::AttemptStatus::foreign_from((
|
||||||
item.response.status,
|
item.response.status,
|
||||||
@ -262,23 +251,8 @@ impl TryFrom<types::PaymentsSyncResponseRouterData<PaymentsResponse>>
|
|||||||
fn try_from(
|
fn try_from(
|
||||||
item: types::PaymentsSyncResponseRouterData<PaymentsResponse>,
|
item: types::PaymentsSyncResponseRouterData<PaymentsResponse>,
|
||||||
) -> Result<Self, Self::Error> {
|
) -> Result<Self, Self::Error> {
|
||||||
let redirection_url = item
|
let redirection_data = item.response.links.redirect.map(|href| {
|
||||||
.response
|
services::RedirectForm::from((href.redirection_url, services::Method::Get))
|
||||||
.links
|
|
||||||
.redirect
|
|
||||||
.map(|data| Url::parse(&data.href))
|
|
||||||
.transpose()
|
|
||||||
.into_report()
|
|
||||||
.change_context(errors::ConnectorError::ResponseHandlingFailed)
|
|
||||||
.attach_printable("Could not parse the redirection data")?;
|
|
||||||
|
|
||||||
let redirection_data = redirection_url.map(|url| services::RedirectForm {
|
|
||||||
url: url.to_string(),
|
|
||||||
method: services::Method::Get,
|
|
||||||
form_fields: std::collections::HashMap::from_iter(
|
|
||||||
url.query_pairs()
|
|
||||||
.map(|(k, v)| (k.to_string(), v.to_string())),
|
|
||||||
),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
|
|||||||
@ -179,40 +179,39 @@ pub enum RapydPaymentStatus {
|
|||||||
New,
|
New,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<transformers::Foreign<(RapydPaymentStatus, String)>>
|
impl From<transformers::Foreign<(RapydPaymentStatus, NextAction)>>
|
||||||
for transformers::Foreign<enums::AttemptStatus>
|
for transformers::Foreign<enums::AttemptStatus>
|
||||||
{
|
{
|
||||||
fn from(item: transformers::Foreign<(RapydPaymentStatus, String)>) -> Self {
|
fn from(item: transformers::Foreign<(RapydPaymentStatus, NextAction)>) -> Self {
|
||||||
let (status, next_action) = item.0;
|
let (status, next_action) = item.0;
|
||||||
match status {
|
match (status, next_action) {
|
||||||
RapydPaymentStatus::Closed => enums::AttemptStatus::Charged,
|
(RapydPaymentStatus::Closed, _) => enums::AttemptStatus::Charged,
|
||||||
RapydPaymentStatus::Active => {
|
(RapydPaymentStatus::Active, NextAction::ThreedsVerification) => {
|
||||||
if next_action == "3d_verification" {
|
enums::AttemptStatus::AuthenticationPending
|
||||||
enums::AttemptStatus::AuthenticationPending
|
|
||||||
} else if next_action == "pending_capture" {
|
|
||||||
enums::AttemptStatus::Authorized
|
|
||||||
} else {
|
|
||||||
enums::AttemptStatus::Pending
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
RapydPaymentStatus::CanceledByClientOrBank
|
(RapydPaymentStatus::Active, NextAction::PendingCapture) => {
|
||||||
| RapydPaymentStatus::Expired
|
enums::AttemptStatus::Authorized
|
||||||
| RapydPaymentStatus::ReversedByRapyd => enums::AttemptStatus::Voided,
|
}
|
||||||
RapydPaymentStatus::Error => enums::AttemptStatus::Failure,
|
(
|
||||||
|
RapydPaymentStatus::CanceledByClientOrBank
|
||||||
RapydPaymentStatus::New => enums::AttemptStatus::Authorizing,
|
| RapydPaymentStatus::Expired
|
||||||
|
| RapydPaymentStatus::ReversedByRapyd,
|
||||||
|
_,
|
||||||
|
) => enums::AttemptStatus::Voided,
|
||||||
|
(RapydPaymentStatus::Error, _) => enums::AttemptStatus::Failure,
|
||||||
|
(RapydPaymentStatus::New, _) => enums::AttemptStatus::Authorizing,
|
||||||
}
|
}
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||||
pub struct RapydPaymentsResponse {
|
pub struct RapydPaymentsResponse {
|
||||||
pub status: Status,
|
pub status: Status,
|
||||||
pub data: Option<ResponseData>,
|
pub data: Option<ResponseData>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||||
pub struct Status {
|
pub struct Status {
|
||||||
pub error_code: String,
|
pub error_code: String,
|
||||||
pub status: Option<String>,
|
pub status: Option<String>,
|
||||||
@ -221,13 +220,21 @@ pub struct Status {
|
|||||||
pub operation_id: Option<String>,
|
pub operation_id: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||||
|
pub enum NextAction {
|
||||||
|
#[serde(rename = "3d_verification")]
|
||||||
|
ThreedsVerification,
|
||||||
|
#[serde(rename = "pending_capture")]
|
||||||
|
PendingCapture,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||||
pub struct ResponseData {
|
pub struct ResponseData {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub amount: i64,
|
pub amount: i64,
|
||||||
pub status: RapydPaymentStatus,
|
pub status: RapydPaymentStatus,
|
||||||
pub next_action: String,
|
pub next_action: NextAction,
|
||||||
pub redirect_url: Option<String>,
|
pub redirect_url: Option<Url>,
|
||||||
pub original_amount: Option<i64>,
|
pub original_amount: Option<i64>,
|
||||||
pub is_partial: Option<bool>,
|
pub is_partial: Option<bool>,
|
||||||
pub currency_code: Option<enums::Currency>,
|
pub currency_code: Option<enums::Currency>,
|
||||||
@ -277,13 +284,13 @@ impl From<RefundStatus> for enums::RefundStatus {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct RefundResponse {
|
pub struct RefundResponse {
|
||||||
pub status: Status,
|
pub status: Status,
|
||||||
pub data: Option<RefundResponseData>,
|
pub data: Option<RefundResponseData>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||||
pub struct RefundResponseData {
|
pub struct RefundResponseData {
|
||||||
//Some field related to forign exchange and split payment can be added as and when implemented
|
//Some field related to forign exchange and split payment can be added as and when implemented
|
||||||
pub id: String,
|
pub id: String,
|
||||||
@ -389,25 +396,12 @@ impl<F, T>
|
|||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
_ => {
|
_ => {
|
||||||
let redirection_data =
|
let redirection_data = data.redirect_url.as_ref().map(|redirect_url| {
|
||||||
match (data.next_action.as_str(), data.redirect_url.to_owned()) {
|
services::RedirectForm::from((
|
||||||
("3d_verification", Some(url)) => {
|
redirect_url.to_owned(),
|
||||||
let url = Url::parse(&url).into_report().change_context(
|
services::Method::Get,
|
||||||
errors::ConnectorError::ResponseHandlingFailed,
|
))
|
||||||
)?;
|
});
|
||||||
let mut base_url = url.clone();
|
|
||||||
base_url.set_query(None);
|
|
||||||
Some(services::RedirectForm {
|
|
||||||
url: base_url.to_string(),
|
|
||||||
method: services::Method::Get,
|
|
||||||
form_fields: std::collections::HashMap::from_iter(
|
|
||||||
url.query_pairs()
|
|
||||||
.map(|(k, v)| (k.to_string(), v.to_string())),
|
|
||||||
),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
(_, _) => None,
|
|
||||||
};
|
|
||||||
(
|
(
|
||||||
attempt_status,
|
attempt_status,
|
||||||
Ok(types::PaymentsResponseData::TransactionResponse {
|
Ok(types::PaymentsResponseData::TransactionResponse {
|
||||||
|
|||||||
@ -501,22 +501,12 @@ impl<F, T>
|
|||||||
fn try_from(
|
fn try_from(
|
||||||
item: types::ResponseRouterData<F, PaymentIntentResponse, T, types::PaymentsResponseData>,
|
item: types::ResponseRouterData<F, PaymentIntentResponse, T, types::PaymentsResponseData>,
|
||||||
) -> Result<Self, Self::Error> {
|
) -> Result<Self, Self::Error> {
|
||||||
let redirection_data = item.response.next_action.as_ref().map(
|
let redirection_data =
|
||||||
|StripeNextActionResponse::RedirectToUrl(response)| {
|
item.response
|
||||||
let mut base_url = response.url.clone();
|
.next_action
|
||||||
base_url.set_query(None);
|
.map(|StripeNextActionResponse::RedirectToUrl(response)| {
|
||||||
services::RedirectForm {
|
services::RedirectForm::from((response.url, services::Method::Get))
|
||||||
url: base_url.to_string(),
|
});
|
||||||
method: services::Method::Get,
|
|
||||||
form_fields: std::collections::HashMap::from_iter(
|
|
||||||
response
|
|
||||||
.url
|
|
||||||
.query_pairs()
|
|
||||||
.map(|(k, v)| (k.to_string(), v.to_string())),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
let mandate_reference =
|
let mandate_reference =
|
||||||
item.response
|
item.response
|
||||||
@ -556,22 +546,12 @@ impl<F, T>
|
|||||||
fn try_from(
|
fn try_from(
|
||||||
item: types::ResponseRouterData<F, SetupIntentResponse, T, types::PaymentsResponseData>,
|
item: types::ResponseRouterData<F, SetupIntentResponse, T, types::PaymentsResponseData>,
|
||||||
) -> Result<Self, Self::Error> {
|
) -> Result<Self, Self::Error> {
|
||||||
let redirection_data = item.response.next_action.as_ref().map(
|
let redirection_data =
|
||||||
|StripeNextActionResponse::RedirectToUrl(response)| {
|
item.response
|
||||||
let mut base_url = response.url.clone();
|
.next_action
|
||||||
base_url.set_query(None);
|
.map(|StripeNextActionResponse::RedirectToUrl(response)| {
|
||||||
services::RedirectForm {
|
services::RedirectForm::from((response.url, services::Method::Get))
|
||||||
url: base_url.to_string(),
|
});
|
||||||
method: services::Method::Get,
|
|
||||||
form_fields: std::collections::HashMap::from_iter(
|
|
||||||
response
|
|
||||||
.url
|
|
||||||
.query_pairs()
|
|
||||||
.map(|(k, v)| (k.to_string(), v.to_string())),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
let mandate_reference =
|
let mandate_reference =
|
||||||
item.response
|
item.response
|
||||||
|
|||||||
@ -363,15 +363,24 @@ impl From<&storage::PaymentAttempt> for ApplicationRedirectResponse {
|
|||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq, Clone, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, Eq, PartialEq, Clone, serde::Serialize, serde::Deserialize)]
|
||||||
pub struct RedirectForm {
|
pub struct RedirectForm {
|
||||||
pub url: String,
|
pub endpoint: String,
|
||||||
pub method: Method,
|
pub method: Method,
|
||||||
pub form_fields: HashMap<String, String>,
|
pub form_fields: HashMap<String, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RedirectForm {
|
impl From<(url::Url, Method)> for RedirectForm {
|
||||||
pub fn new(url: String, method: Method, form_fields: HashMap<String, String>) -> Self {
|
fn from((mut redirect_url, method): (url::Url, Method)) -> Self {
|
||||||
|
let form_fields = std::collections::HashMap::from_iter(
|
||||||
|
redirect_url
|
||||||
|
.query_pairs()
|
||||||
|
.map(|(key, value)| (key.to_string(), value.to_string())),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Do not include query params in the endpoint
|
||||||
|
redirect_url.set_query(None);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
url,
|
endpoint: redirect_url.to_string(),
|
||||||
method,
|
method,
|
||||||
form_fields,
|
form_fields,
|
||||||
}
|
}
|
||||||
@ -574,18 +583,18 @@ pub fn build_redirection_form(form: &RedirectForm) -> maud::Markup {
|
|||||||
head {
|
head {
|
||||||
style {
|
style {
|
||||||
r##"
|
r##"
|
||||||
|
|
||||||
"##
|
"##
|
||||||
}
|
}
|
||||||
(PreEscaped(r##"
|
(PreEscaped(r##"
|
||||||
<style>
|
<style>
|
||||||
#loader1 {
|
#loader1 {
|
||||||
width: 500px,
|
width: 500px,
|
||||||
}
|
}
|
||||||
@media max-width: 600px {
|
@media max-width: 600px {
|
||||||
#loader1 {
|
#loader1 {
|
||||||
width: 200px
|
width: 200px
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
"##))
|
"##))
|
||||||
@ -612,7 +621,7 @@ pub fn build_redirection_form(form: &RedirectForm) -> maud::Markup {
|
|||||||
|
|
||||||
|
|
||||||
h3 style="text-align: center;" { "Please wait while we process your payment..." }
|
h3 style="text-align: center;" { "Please wait while we process your payment..." }
|
||||||
form action=(PreEscaped(&form.url)) method=(form.method.to_string()) #payment_form {
|
form action=(PreEscaped(&form.endpoint)) method=(form.method.to_string()) #payment_form {
|
||||||
@for (field, value) in &form.form_fields {
|
@for (field, value) in &form.form_fields {
|
||||||
input type="hidden" name=(field) value=(value);
|
input type="hidden" name=(field) value=(value);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user