mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-28 04:04:55 +08:00
feat(connector): add 3ds for Bambora and Support Html 3ds response (#817)
Co-authored-by: Narayan Bhat <narayan.bhat@juspay.in> Co-authored-by: shankar singh <shankar.singh@shankar.singh-MacBookPro> Co-authored-by: Jagan Elavarasan <jaganelavarasan@gmail.com> Co-authored-by: Arun Raj M <jarnura47@gmail.com>
This commit is contained in:
@ -1324,7 +1324,7 @@ pub fn get_redirection_response(
|
||||
.map(|(key, value)| (key.to_string(), value.to_string())),
|
||||
)
|
||||
});
|
||||
services::RedirectForm {
|
||||
services::RedirectForm::Form {
|
||||
endpoint: url.to_string(),
|
||||
method: response.action.method.unwrap_or(services::Method::Get),
|
||||
form_fields,
|
||||
|
||||
@ -150,7 +150,7 @@ pub struct AirwallexCompleteRequest {
|
||||
|
||||
#[derive(Default, Debug, Serialize, Eq, PartialEq)]
|
||||
pub struct AirwallexThreeDsData {
|
||||
acs_response: Option<common_utils::pii::SecretSerdeValue>,
|
||||
acs_response: Option<Secret<String>>,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Serialize, Eq, PartialEq)]
|
||||
@ -166,7 +166,11 @@ impl TryFrom<&types::PaymentsCompleteAuthorizeRouterData> for AirwallexCompleteR
|
||||
Ok(Self {
|
||||
request_id: Uuid::new_v4().to_string(),
|
||||
three_ds: AirwallexThreeDsData {
|
||||
acs_response: item.request.payload.clone().map(Secret::new),
|
||||
acs_response: item
|
||||
.request
|
||||
.payload
|
||||
.as_ref()
|
||||
.map(|data| Secret::new(serde_json::Value::to_string(data))),
|
||||
},
|
||||
three_ds_type: AirwallexThreeDsType::ThreeDSContinue,
|
||||
})
|
||||
@ -285,7 +289,7 @@ pub struct AirwallexPaymentsResponse {
|
||||
fn get_redirection_form(
|
||||
response_url_data: AirwallexPaymentsNextAction,
|
||||
) -> Option<services::RedirectForm> {
|
||||
Some(services::RedirectForm {
|
||||
Some(services::RedirectForm::Form {
|
||||
endpoint: response_url_data.url.to_string(),
|
||||
method: response_url_data.method,
|
||||
form_fields: std::collections::HashMap::from([
|
||||
|
||||
@ -8,8 +8,11 @@ use transformers as bambora;
|
||||
use super::utils::RefundsRequestData;
|
||||
use crate::{
|
||||
configs::settings,
|
||||
connector::utils::{PaymentsAuthorizeRequestData, PaymentsSyncRequestData},
|
||||
core::errors::{self, CustomResult},
|
||||
connector::utils::{to_connector_meta, PaymentsAuthorizeRequestData, PaymentsSyncRequestData},
|
||||
core::{
|
||||
errors::{self, CustomResult},
|
||||
payments,
|
||||
},
|
||||
headers, logger,
|
||||
services::{self, ConnectorIntegration},
|
||||
types::{
|
||||
@ -166,7 +169,7 @@ impl ConnectorIntegration<api::Void, types::PaymentsCancelData, types::PaymentsR
|
||||
data: &types::PaymentsCancelRouterData,
|
||||
res: Response,
|
||||
) -> CustomResult<types::PaymentsCancelRouterData, errors::ConnectorError> {
|
||||
let response: bambora::BamboraPaymentsResponse = res
|
||||
let response: bambora::BamboraResponse = res
|
||||
.response
|
||||
.parse_struct("bambora PaymentsResponse")
|
||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||
@ -257,7 +260,7 @@ impl ConnectorIntegration<api::PSync, types::PaymentsSyncData, types::PaymentsRe
|
||||
data: &types::PaymentsSyncRouterData,
|
||||
res: Response,
|
||||
) -> CustomResult<types::PaymentsSyncRouterData, errors::ConnectorError> {
|
||||
let response: bambora::BamboraPaymentsResponse = res
|
||||
let response: bambora::BamboraResponse = res
|
||||
.response
|
||||
.parse_struct("bambora PaymentsResponse")
|
||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||
@ -336,7 +339,7 @@ impl ConnectorIntegration<api::Capture, types::PaymentsCaptureData, types::Payme
|
||||
data: &types::PaymentsCaptureRouterData,
|
||||
res: Response,
|
||||
) -> CustomResult<types::PaymentsCaptureRouterData, errors::ConnectorError> {
|
||||
let response: bambora::BamboraPaymentsResponse = res
|
||||
let response: bambora::BamboraResponse = res
|
||||
.response
|
||||
.parse_struct("Bambora PaymentsResponse")
|
||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||
@ -388,9 +391,9 @@ impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::P
|
||||
fn get_url(
|
||||
&self,
|
||||
_req: &types::PaymentsAuthorizeRouterData,
|
||||
_connectors: &settings::Connectors,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
Ok(format!("{}{}", self.base_url(_connectors), "/v1/payments"))
|
||||
Ok(format!("{}{}", self.base_url(connectors), "/v1/payments"))
|
||||
}
|
||||
|
||||
fn get_request_body(
|
||||
@ -429,7 +432,7 @@ impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::P
|
||||
data: &types::PaymentsAuthorizeRouterData,
|
||||
res: Response,
|
||||
) -> CustomResult<types::PaymentsAuthorizeRouterData, errors::ConnectorError> {
|
||||
let response: bambora::BamboraPaymentsResponse = res
|
||||
let response: bambora::BamboraResponse = res
|
||||
.response
|
||||
.parse_struct("PaymentIntentResponse")
|
||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||
@ -638,3 +641,111 @@ pub fn get_payment_flow(is_auto_capture: bool) -> bambora::PaymentFlow {
|
||||
bambora::PaymentFlow::Authorize
|
||||
}
|
||||
}
|
||||
|
||||
impl services::ConnectorRedirectResponse for Bambora {
|
||||
fn get_flow_type(
|
||||
&self,
|
||||
_query_params: &str,
|
||||
_json_payload: Option<serde_json::Value>,
|
||||
_action: services::PaymentAction,
|
||||
) -> CustomResult<payments::CallConnectorAction, errors::ConnectorError> {
|
||||
Ok(payments::CallConnectorAction::Trigger)
|
||||
}
|
||||
}
|
||||
|
||||
impl api::PaymentsCompleteAuthorize for Bambora {}
|
||||
|
||||
impl
|
||||
ConnectorIntegration<
|
||||
api::CompleteAuthorize,
|
||||
types::CompleteAuthorizeData,
|
||||
types::PaymentsResponseData,
|
||||
> for Bambora
|
||||
{
|
||||
fn get_headers(
|
||||
&self,
|
||||
req: &types::PaymentsCompleteAuthorizeRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<Vec<(String, String)>, errors::ConnectorError> {
|
||||
self.build_headers(req, connectors)
|
||||
}
|
||||
|
||||
fn get_content_type(&self) -> &'static str {
|
||||
self.common_get_content_type()
|
||||
}
|
||||
|
||||
fn get_url(
|
||||
&self,
|
||||
req: &types::PaymentsCompleteAuthorizeRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
let meta: bambora::BamboraMeta = to_connector_meta(req.request.connector_meta.clone())?;
|
||||
Ok(format!(
|
||||
"{}/v1/payments/{}{}",
|
||||
self.base_url(connectors),
|
||||
meta.three_d_session_data,
|
||||
"/continue"
|
||||
))
|
||||
}
|
||||
|
||||
fn get_request_body(
|
||||
&self,
|
||||
req: &types::PaymentsCompleteAuthorizeRouterData,
|
||||
) -> CustomResult<Option<String>, errors::ConnectorError> {
|
||||
let request = bambora::BamboraThreedsContinueRequest::try_from(&req.request)?;
|
||||
let bambora_req =
|
||||
utils::Encode::<bambora::BamboraThreedsContinueRequest>::encode_to_string_of_json(
|
||||
&request,
|
||||
)
|
||||
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
|
||||
Ok(Some(bambora_req))
|
||||
}
|
||||
|
||||
fn build_request(
|
||||
&self,
|
||||
req: &types::PaymentsCompleteAuthorizeRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
|
||||
let request = services::RequestBuilder::new()
|
||||
.method(services::Method::Post)
|
||||
.url(&types::PaymentsCompleteAuthorizeType::get_url(
|
||||
self, req, connectors,
|
||||
)?)
|
||||
.headers(types::PaymentsCompleteAuthorizeType::get_headers(
|
||||
self, req, connectors,
|
||||
)?)
|
||||
.body(types::PaymentsCompleteAuthorizeType::get_request_body(
|
||||
self, req,
|
||||
)?)
|
||||
.build();
|
||||
Ok(Some(request))
|
||||
}
|
||||
|
||||
fn handle_response(
|
||||
&self,
|
||||
data: &types::PaymentsCompleteAuthorizeRouterData,
|
||||
res: Response,
|
||||
) -> CustomResult<types::PaymentsCompleteAuthorizeRouterData, errors::ConnectorError> {
|
||||
let response: bambora::BamboraResponse = res
|
||||
.response
|
||||
.parse_struct("Bambora PaymentsResponse")
|
||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||
logger::debug!(bamborapayments_create_response=?response);
|
||||
types::RouterData::try_from((
|
||||
types::ResponseRouterData {
|
||||
response,
|
||||
data: data.clone(),
|
||||
http_code: res.status_code,
|
||||
},
|
||||
bambora::PaymentFlow::Capture,
|
||||
))
|
||||
.change_context(errors::ConnectorError::ResponseHandlingFailed)
|
||||
}
|
||||
|
||||
fn get_error_response(
|
||||
&self,
|
||||
res: Response,
|
||||
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
|
||||
self.build_error_response(res)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
use base64::Engine;
|
||||
use common_utils::ext_traits::ValueExt;
|
||||
use error_stack::{IntoReport, ResultExt};
|
||||
use masking::Secret;
|
||||
use serde::{Deserialize, Deserializer, Serialize};
|
||||
|
||||
@ -6,6 +8,7 @@ use crate::{
|
||||
connector::utils::PaymentsAuthorizeRequestData,
|
||||
consts,
|
||||
core::errors,
|
||||
services,
|
||||
types::{self, api, storage::enums},
|
||||
};
|
||||
|
||||
@ -24,19 +27,21 @@ pub struct BamboraCard {
|
||||
|
||||
#[derive(Default, Debug, Serialize, Eq, PartialEq)]
|
||||
pub struct ThreeDSecure {
|
||||
// browser: Option<Browser>, //Needed only in case of 3Ds 2.0. Need to update request for this.
|
||||
browser: Option<BamboraBrowserInfo>, //Needed only in case of 3Ds 2.0. Need to update request for this.
|
||||
enabled: bool,
|
||||
version: Option<i64>,
|
||||
auth_required: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Serialize, Eq, PartialEq)]
|
||||
pub struct Browser {
|
||||
pub struct BamboraBrowserInfo {
|
||||
accept_header: String,
|
||||
java_enabled: String,
|
||||
java_enabled: bool,
|
||||
language: String,
|
||||
color_depth: String,
|
||||
screen_height: i64,
|
||||
screen_width: i64,
|
||||
time_zone: i64,
|
||||
color_depth: u8,
|
||||
screen_height: u32,
|
||||
screen_width: u32,
|
||||
time_zone: i32,
|
||||
user_agent: String,
|
||||
javascript_enabled: bool,
|
||||
}
|
||||
@ -45,16 +50,63 @@ pub struct Browser {
|
||||
pub struct BamboraPaymentsRequest {
|
||||
amount: i64,
|
||||
payment_method: PaymentMethod,
|
||||
customer_ip: Option<std::net::IpAddr>,
|
||||
term_url: Option<String>,
|
||||
card: BamboraCard,
|
||||
}
|
||||
|
||||
fn get_browser_info(item: &types::PaymentsAuthorizeRouterData) -> Option<BamboraBrowserInfo> {
|
||||
if matches!(item.auth_type, enums::AuthenticationType::ThreeDs) {
|
||||
item.request
|
||||
.browser_info
|
||||
.as_ref()
|
||||
.map(|info| BamboraBrowserInfo {
|
||||
accept_header: info.accept_header.clone(),
|
||||
java_enabled: info.java_enabled,
|
||||
language: info.language.clone(),
|
||||
color_depth: info.color_depth,
|
||||
screen_height: info.screen_height,
|
||||
screen_width: info.screen_width,
|
||||
time_zone: info.time_zone,
|
||||
user_agent: info.user_agent.clone(),
|
||||
javascript_enabled: info.java_script_enabled,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&types::CompleteAuthorizeData> for BamboraThreedsContinueRequest {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(value: &types::CompleteAuthorizeData) -> Result<Self, Self::Error> {
|
||||
let card_response: CardResponse = value
|
||||
.payload
|
||||
.clone()
|
||||
.ok_or(errors::ConnectorError::MissingRequiredField {
|
||||
field_name: "payload",
|
||||
})?
|
||||
.parse_value("CardResponse")
|
||||
.change_context(errors::ConnectorError::ParsingFailed)?;
|
||||
let bambora_req = Self {
|
||||
payment_method: "credit_card".to_string(),
|
||||
card_response,
|
||||
};
|
||||
Ok(bambora_req)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&types::PaymentsAuthorizeRouterData> for BamboraPaymentsRequest {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(item: &types::PaymentsAuthorizeRouterData) -> Result<Self, Self::Error> {
|
||||
match item.request.payment_method_data.clone() {
|
||||
api::PaymentMethodData::Card(req_card) => {
|
||||
let three_ds = match item.auth_type {
|
||||
enums::AuthenticationType::ThreeDs => Some(ThreeDSecure { enabled: true }),
|
||||
enums::AuthenticationType::ThreeDs => Some(ThreeDSecure {
|
||||
enabled: true,
|
||||
browser: get_browser_info(item),
|
||||
version: Some(2),
|
||||
auth_required: Some(true),
|
||||
}),
|
||||
enums::AuthenticationType::NoThreeDs => None,
|
||||
};
|
||||
let bambora_card = BamboraCard {
|
||||
@ -66,10 +118,13 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for BamboraPaymentsRequest {
|
||||
three_d_secure: three_ds,
|
||||
complete: item.request.is_auto_capture()?,
|
||||
};
|
||||
let browser_info = item.request.get_browser_info()?;
|
||||
Ok(Self {
|
||||
amount: item.request.amount,
|
||||
payment_method: PaymentMethod::Card,
|
||||
card: bambora_card,
|
||||
customer_ip: browser_info.ip_address,
|
||||
term_url: item.request.complete_authorize_url.clone(),
|
||||
})
|
||||
}
|
||||
_ => Err(errors::ConnectorError::NotImplemented("Payment methods".to_string()).into()),
|
||||
@ -115,42 +170,68 @@ pub enum PaymentFlow {
|
||||
// PaymentsResponse
|
||||
impl<F, T>
|
||||
TryFrom<(
|
||||
types::ResponseRouterData<F, BamboraPaymentsResponse, T, types::PaymentsResponseData>,
|
||||
types::ResponseRouterData<F, BamboraResponse, T, types::PaymentsResponseData>,
|
||||
PaymentFlow,
|
||||
)> for types::RouterData<F, T, types::PaymentsResponseData>
|
||||
{
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(
|
||||
data: (
|
||||
types::ResponseRouterData<F, BamboraPaymentsResponse, T, types::PaymentsResponseData>,
|
||||
types::ResponseRouterData<F, BamboraResponse, T, types::PaymentsResponseData>,
|
||||
PaymentFlow,
|
||||
),
|
||||
) -> Result<Self, Self::Error> {
|
||||
let flow = data.1;
|
||||
let item = data.0;
|
||||
let pg_response = item.response;
|
||||
Ok(Self {
|
||||
status: match pg_response.approved.as_str() {
|
||||
"0" => match flow {
|
||||
PaymentFlow::Authorize => enums::AttemptStatus::AuthorizationFailed,
|
||||
PaymentFlow::Capture => enums::AttemptStatus::Failure,
|
||||
PaymentFlow::Void => enums::AttemptStatus::VoidFailed,
|
||||
match item.response {
|
||||
BamboraResponse::NormalTransaction(pg_response) => Ok(Self {
|
||||
status: match pg_response.approved.as_str() {
|
||||
"0" => match flow {
|
||||
PaymentFlow::Authorize => enums::AttemptStatus::AuthorizationFailed,
|
||||
PaymentFlow::Capture => enums::AttemptStatus::Failure,
|
||||
PaymentFlow::Void => enums::AttemptStatus::VoidFailed,
|
||||
},
|
||||
"1" => match flow {
|
||||
PaymentFlow::Authorize => enums::AttemptStatus::Authorized,
|
||||
PaymentFlow::Capture => enums::AttemptStatus::Charged,
|
||||
PaymentFlow::Void => enums::AttemptStatus::Voided,
|
||||
},
|
||||
&_ => Err(errors::ConnectorError::ResponseDeserializationFailed)?,
|
||||
},
|
||||
"1" => match flow {
|
||||
PaymentFlow::Authorize => enums::AttemptStatus::Authorized,
|
||||
PaymentFlow::Capture => enums::AttemptStatus::Charged,
|
||||
PaymentFlow::Void => enums::AttemptStatus::Voided,
|
||||
},
|
||||
&_ => Err(errors::ConnectorError::ResponseDeserializationFailed)?,
|
||||
},
|
||||
response: Ok(types::PaymentsResponseData::TransactionResponse {
|
||||
resource_id: types::ResponseId::ConnectorTransactionId(pg_response.id.to_string()),
|
||||
redirection_data: None,
|
||||
mandate_reference: None,
|
||||
connector_metadata: None,
|
||||
response: Ok(types::PaymentsResponseData::TransactionResponse {
|
||||
resource_id: types::ResponseId::ConnectorTransactionId(
|
||||
pg_response.id.to_string(),
|
||||
),
|
||||
redirection_data: None,
|
||||
mandate_reference: None,
|
||||
connector_metadata: None,
|
||||
}),
|
||||
..item.data
|
||||
}),
|
||||
..item.data
|
||||
})
|
||||
|
||||
BamboraResponse::ThreeDsResponse(response) => {
|
||||
let value = url::form_urlencoded::parse(response.contents.as_bytes())
|
||||
.map(|(key, val)| [key, val].concat())
|
||||
.collect();
|
||||
let redirection_data = Some(services::RedirectForm::Html { html_data: value });
|
||||
Ok(Self {
|
||||
status: enums::AttemptStatus::AuthenticationPending,
|
||||
response: Ok(types::PaymentsResponseData::TransactionResponse {
|
||||
resource_id: types::ResponseId::NoResponseId,
|
||||
redirection_data,
|
||||
mandate_reference: None,
|
||||
connector_metadata: Some(
|
||||
serde_json::to_value(BamboraMeta {
|
||||
three_d_session_data: response.three_d_session_data,
|
||||
})
|
||||
.into_report()
|
||||
.change_context(errors::ConnectorError::ResponseHandlingFailed)?,
|
||||
),
|
||||
}),
|
||||
..item.data
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -173,7 +254,14 @@ where
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum BamboraResponse {
|
||||
NormalTransaction(Box<BamboraPaymentsResponse>),
|
||||
ThreeDsResponse(Box<Bambora3DsResponse>),
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, Deserialize, PartialEq)]
|
||||
pub struct BamboraPaymentsResponse {
|
||||
#[serde(deserialize_with = "str_or_i32")]
|
||||
id: String,
|
||||
@ -203,7 +291,30 @@ pub struct BamboraPaymentsResponse {
|
||||
risk_score: Option<f32>,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct Bambora3DsResponse {
|
||||
#[serde(rename = "3d_session_data")]
|
||||
three_d_session_data: String,
|
||||
contents: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Default, Deserialize)]
|
||||
pub struct BamboraMeta {
|
||||
pub three_d_session_data: String,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Serialize, Eq, PartialEq)]
|
||||
pub struct BamboraThreedsContinueRequest {
|
||||
pub(crate) payment_method: String,
|
||||
pub card_response: CardResponse,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Deserialize, Serialize, Eq, PartialEq)]
|
||||
pub struct CardResponse {
|
||||
pub(crate) cres: Option<common_utils::pii::SecretSerdeValue>,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, Deserialize, PartialEq)]
|
||||
pub struct CardData {
|
||||
name: Option<String>,
|
||||
expiry_month: Option<String>,
|
||||
@ -337,34 +448,34 @@ impl From<RefundStatus> for enums::RefundStatus {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
|
||||
#[derive(Default, Debug, Clone, Deserialize)]
|
||||
pub struct RefundResponse {
|
||||
#[serde(deserialize_with = "str_or_i32")]
|
||||
id: String,
|
||||
authorizing_merchant_id: i32,
|
||||
pub id: String,
|
||||
pub authorizing_merchant_id: i32,
|
||||
#[serde(deserialize_with = "str_or_i32")]
|
||||
approved: String,
|
||||
pub approved: String,
|
||||
#[serde(deserialize_with = "str_or_i32")]
|
||||
message_id: String,
|
||||
message: String,
|
||||
auth_code: String,
|
||||
created: String,
|
||||
amount: f32,
|
||||
order_number: String,
|
||||
pub message_id: String,
|
||||
pub message: String,
|
||||
pub auth_code: String,
|
||||
pub created: String,
|
||||
pub amount: f32,
|
||||
pub order_number: String,
|
||||
#[serde(rename = "type")]
|
||||
payment_type: String,
|
||||
comments: Option<String>,
|
||||
batch_number: Option<String>,
|
||||
total_refunds: Option<f32>,
|
||||
total_completions: Option<f32>,
|
||||
payment_method: String,
|
||||
card: CardData,
|
||||
billing: Option<AddressData>,
|
||||
shipping: Option<AddressData>,
|
||||
custom: CustomData,
|
||||
adjusted_by: Option<Vec<AdjustedBy>>,
|
||||
links: Vec<Links>,
|
||||
risk_score: Option<f32>,
|
||||
pub payment_type: String,
|
||||
pub comments: Option<String>,
|
||||
pub batch_number: Option<String>,
|
||||
pub total_refunds: Option<f32>,
|
||||
pub total_completions: Option<f32>,
|
||||
pub payment_method: String,
|
||||
pub card: CardData,
|
||||
pub billing: Option<AddressData>,
|
||||
pub shipping: Option<AddressData>,
|
||||
pub custom: CustomData,
|
||||
pub adjusted_by: Option<Vec<AdjustedBy>>,
|
||||
pub links: Vec<Links>,
|
||||
pub risk_score: Option<f32>,
|
||||
}
|
||||
|
||||
impl TryFrom<types::RefundsResponseRouterData<api::Execute, RefundResponse>>
|
||||
|
||||
@ -124,7 +124,7 @@ impl<F, T>
|
||||
>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
let form_fields = HashMap::new();
|
||||
let redirection_data = services::RedirectForm {
|
||||
let redirection_data = services::RedirectForm::Form {
|
||||
endpoint: item.response.data.hosted_url.to_string(),
|
||||
method: services::Method::Get,
|
||||
form_fields,
|
||||
|
||||
@ -1121,7 +1121,7 @@ where
|
||||
.and_then(|o| o.card.clone())
|
||||
.and_then(|card| card.three_d)
|
||||
.and_then(|three_ds| three_ds.acs_url.zip(three_ds.c_req))
|
||||
.map(|(base_url, creq)| services::RedirectForm {
|
||||
.map(|(base_url, creq)| services::RedirectForm::Form {
|
||||
endpoint: base_url,
|
||||
method: services::Method::Post,
|
||||
form_fields: std::collections::HashMap::from([("creq".to_string(), creq)]),
|
||||
|
||||
@ -97,7 +97,7 @@ impl<F, T>
|
||||
>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
let form_fields = HashMap::new();
|
||||
let redirection_data = services::RedirectForm {
|
||||
let redirection_data = services::RedirectForm::Form {
|
||||
endpoint: item.response.data.hosted_checkout_url.to_string(),
|
||||
method: services::Method::Get,
|
||||
form_fields,
|
||||
|
||||
@ -558,11 +558,13 @@ fn handle_cards_response(
|
||||
response.redirect_url.clone(),
|
||||
)?;
|
||||
let form_fields = response.redirect_params.unwrap_or_default();
|
||||
let redirection_data = response.redirect_url.map(|url| services::RedirectForm {
|
||||
endpoint: url.to_string(),
|
||||
method: services::Method::Post,
|
||||
form_fields,
|
||||
});
|
||||
let redirection_data = response
|
||||
.redirect_url
|
||||
.map(|url| services::RedirectForm::Form {
|
||||
endpoint: url.to_string(),
|
||||
method: services::Method::Post,
|
||||
form_fields,
|
||||
});
|
||||
let error = if msg.is_some() {
|
||||
Some(types::ErrorResponse {
|
||||
code: response.payment_status,
|
||||
|
||||
@ -89,7 +89,6 @@ default_imp_for_complete_authorize!(
|
||||
connector::Aci,
|
||||
connector::Adyen,
|
||||
connector::Authorizedotnet,
|
||||
connector::Bambora,
|
||||
connector::Bluesnap,
|
||||
connector::Braintree,
|
||||
connector::Checkout,
|
||||
@ -132,7 +131,6 @@ default_imp_for_connector_redirect_response!(
|
||||
connector::Aci,
|
||||
connector::Adyen,
|
||||
connector::Authorizedotnet,
|
||||
connector::Bambora,
|
||||
connector::Bluesnap,
|
||||
connector::Braintree,
|
||||
connector::Coinbase,
|
||||
|
||||
@ -701,7 +701,7 @@ impl<F: Clone> TryFrom<PaymentAdditionalData<'_, F>> for types::CompleteAuthoriz
|
||||
let json_payload = payment_data
|
||||
.connector_response
|
||||
.encoded_data
|
||||
.map(serde_json::to_value)
|
||||
.map(|s| serde_json::from_str::<serde_json::Value>(&s))
|
||||
.transpose()
|
||||
.into_report()
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)?;
|
||||
|
||||
@ -467,10 +467,15 @@ pub struct ApplicationRedirectResponse {
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct RedirectForm {
|
||||
pub endpoint: String,
|
||||
pub method: Method,
|
||||
pub form_fields: HashMap<String, String>,
|
||||
pub enum RedirectForm {
|
||||
Form {
|
||||
endpoint: String,
|
||||
method: Method,
|
||||
form_fields: HashMap<String, String>,
|
||||
},
|
||||
Html {
|
||||
html_data: String,
|
||||
},
|
||||
}
|
||||
|
||||
impl From<(url::Url, Method)> for RedirectForm {
|
||||
@ -484,7 +489,7 @@ impl From<(url::Url, Method)> for RedirectForm {
|
||||
// Do not include query params in the endpoint
|
||||
redirect_url.set_query(None);
|
||||
|
||||
Self {
|
||||
Self::Form {
|
||||
endpoint: redirect_url.to_string(),
|
||||
method,
|
||||
form_fields,
|
||||
@ -681,7 +686,12 @@ impl Authenticate for api_models::payment_methods::PaymentMethodListRequest {
|
||||
pub fn build_redirection_form(form: &RedirectForm) -> maud::Markup {
|
||||
use maud::PreEscaped;
|
||||
|
||||
maud::html! {
|
||||
match form {
|
||||
RedirectForm::Form {
|
||||
endpoint,
|
||||
method,
|
||||
form_fields,
|
||||
} => maud::html! {
|
||||
(maud::DOCTYPE)
|
||||
html {
|
||||
meta name="viewport" content="width=device-width, initial-scale=1";
|
||||
@ -726,8 +736,8 @@ pub fn build_redirection_form(form: &RedirectForm) -> maud::Markup {
|
||||
|
||||
|
||||
h3 style="text-align: center;" { "Please wait while we process your payment..." }
|
||||
form action=(PreEscaped(&form.endpoint)) method=(form.method.to_string()) #payment_form {
|
||||
@for (field, value) in &form.form_fields {
|
||||
form action=(PreEscaped(endpoint)) method=(method.to_string()) #payment_form {
|
||||
@for (field, value) in form_fields {
|
||||
input type="hidden" name=(field) value=(value);
|
||||
}
|
||||
}
|
||||
@ -735,6 +745,8 @@ pub fn build_redirection_form(form: &RedirectForm) -> maud::Markup {
|
||||
(PreEscaped(r#"<script type="text/javascript"> var frm = document.getElementById("payment_form"); window.setTimeout(function () { frm.submit(); }, 300); </script>"#))
|
||||
}
|
||||
}
|
||||
},
|
||||
RedirectForm::Html { html_data } => PreEscaped(html_data.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user