mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-11-01 19:42:27 +08:00
feat(connector): [cybersource] Implement 3DS flow for cards (#3290)
Co-authored-by: DEEPANSHU BANSAL <deepanshubansal515@gmail.com> Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Co-authored-by: DEEPANSHU BANSAL <41580413+deepanshu-iiitu@users.noreply.github.com>
This commit is contained in:
@ -351,6 +351,7 @@ stripe = { payment_method = "bank_transfer" }
|
||||
nuvei = { payment_method = "card" }
|
||||
shift4 = { payment_method = "card" }
|
||||
bluesnap = { payment_method = "card" }
|
||||
cybersource = {payment_method = "card"}
|
||||
nmi = {payment_method = "card"}
|
||||
|
||||
[dummy_connector]
|
||||
|
||||
@ -428,6 +428,7 @@ stripe = {payment_method = "bank_transfer"}
|
||||
nuvei = {payment_method = "card"}
|
||||
shift4 = {payment_method = "card"}
|
||||
bluesnap = {payment_method = "card"}
|
||||
cybersource = {payment_method = "card"}
|
||||
nmi = {payment_method = "card"}
|
||||
|
||||
[connector_customer]
|
||||
|
||||
@ -241,6 +241,7 @@ stripe = {payment_method = "bank_transfer"}
|
||||
nuvei = {payment_method = "card"}
|
||||
shift4 = {payment_method = "card"}
|
||||
bluesnap = {payment_method = "card"}
|
||||
cybersource = {payment_method = "card"}
|
||||
nmi = {payment_method = "card"}
|
||||
|
||||
[dummy_connector]
|
||||
|
||||
@ -12,6 +12,7 @@ use time::OffsetDateTime;
|
||||
use transformers as cybersource;
|
||||
use url::Url;
|
||||
|
||||
use super::utils::{PaymentsAuthorizeRequestData, RouterData};
|
||||
use crate::{
|
||||
configs::settings,
|
||||
connector::{utils as connector_utils, utils::RefundsRequestData},
|
||||
@ -286,6 +287,8 @@ impl api::PaymentIncrementalAuthorization for Cybersource {}
|
||||
impl api::MandateSetup for Cybersource {}
|
||||
impl api::ConnectorAccessToken for Cybersource {}
|
||||
impl api::PaymentToken for Cybersource {}
|
||||
impl api::PaymentsPreProcessing for Cybersource {}
|
||||
impl api::PaymentsCompleteAuthorize for Cybersource {}
|
||||
impl api::ConnectorMandateRevoke for Cybersource {}
|
||||
|
||||
impl
|
||||
@ -472,6 +475,113 @@ impl ConnectorIntegration<api::Session, types::PaymentsSessionData, types::Payme
|
||||
{
|
||||
}
|
||||
|
||||
impl
|
||||
ConnectorIntegration<
|
||||
api::PreProcessing,
|
||||
types::PaymentsPreProcessingData,
|
||||
types::PaymentsResponseData,
|
||||
> for Cybersource
|
||||
{
|
||||
fn get_headers(
|
||||
&self,
|
||||
req: &types::PaymentsPreProcessingRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<Vec<(String, request::Maskable<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::PaymentsPreProcessingRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
let redirect_response = req.request.redirect_response.clone().ok_or(
|
||||
errors::ConnectorError::MissingRequiredField {
|
||||
field_name: "redirect_response",
|
||||
},
|
||||
)?;
|
||||
match redirect_response.params {
|
||||
Some(param) if !param.clone().peek().is_empty() => Ok(format!(
|
||||
"{}risk/v1/authentications",
|
||||
self.base_url(connectors)
|
||||
)),
|
||||
Some(_) | None => Ok(format!(
|
||||
"{}risk/v1/authentication-results",
|
||||
self.base_url(connectors)
|
||||
)),
|
||||
}
|
||||
}
|
||||
fn get_request_body(
|
||||
&self,
|
||||
req: &types::PaymentsPreProcessingRouterData,
|
||||
_connectors: &settings::Connectors,
|
||||
) -> CustomResult<RequestContent, errors::ConnectorError> {
|
||||
let connector_router_data = cybersource::CybersourceRouterData::try_from((
|
||||
&self.get_currency_unit(),
|
||||
req.request
|
||||
.currency
|
||||
.ok_or(errors::ConnectorError::MissingRequiredField {
|
||||
field_name: "currency",
|
||||
})?,
|
||||
req.request
|
||||
.amount
|
||||
.ok_or(errors::ConnectorError::MissingRequiredField {
|
||||
field_name: "amount",
|
||||
})?,
|
||||
req,
|
||||
))?;
|
||||
let connector_req =
|
||||
cybersource::CybersourcePreProcessingRequest::try_from(&connector_router_data)?;
|
||||
Ok(RequestContent::Json(Box::new(connector_req)))
|
||||
}
|
||||
fn build_request(
|
||||
&self,
|
||||
req: &types::PaymentsPreProcessingRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
|
||||
Ok(Some(
|
||||
services::RequestBuilder::new()
|
||||
.method(services::Method::Post)
|
||||
.url(&types::PaymentsPreProcessingType::get_url(
|
||||
self, req, connectors,
|
||||
)?)
|
||||
.attach_default_headers()
|
||||
.headers(types::PaymentsPreProcessingType::get_headers(
|
||||
self, req, connectors,
|
||||
)?)
|
||||
.set_body(types::PaymentsPreProcessingType::get_request_body(
|
||||
self, req, connectors,
|
||||
)?)
|
||||
.build(),
|
||||
))
|
||||
}
|
||||
|
||||
fn handle_response(
|
||||
&self,
|
||||
data: &types::PaymentsPreProcessingRouterData,
|
||||
res: types::Response,
|
||||
) -> CustomResult<types::PaymentsPreProcessingRouterData, errors::ConnectorError> {
|
||||
let response: cybersource::CybersourcePreProcessingResponse = res
|
||||
.response
|
||||
.parse_struct("Cybersource AuthEnrollmentResponse")
|
||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||
types::RouterData::try_from(types::ResponseRouterData {
|
||||
response,
|
||||
data: data.clone(),
|
||||
http_code: res.status_code,
|
||||
})
|
||||
}
|
||||
|
||||
fn get_error_response(
|
||||
&self,
|
||||
res: types::Response,
|
||||
) -> CustomResult<types::ErrorResponse, errors::ConnectorError> {
|
||||
self.build_error_response(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl ConnectorIntegration<api::Capture, types::PaymentsCaptureData, types::PaymentsResponseData>
|
||||
for Cybersource
|
||||
{
|
||||
@ -672,14 +782,21 @@ impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::P
|
||||
|
||||
fn get_url(
|
||||
&self,
|
||||
_req: &types::PaymentsAuthorizeRouterData,
|
||||
req: &types::PaymentsAuthorizeRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
if req.is_three_ds() && req.request.is_card() {
|
||||
Ok(format!(
|
||||
"{}risk/v1/authentication-setups",
|
||||
api::ConnectorCommon::base_url(self, connectors)
|
||||
))
|
||||
} else {
|
||||
Ok(format!(
|
||||
"{}pts/v2/payments/",
|
||||
api::ConnectorCommon::base_url(self, connectors)
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn get_request_body(
|
||||
&self,
|
||||
@ -692,10 +809,16 @@ impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::P
|
||||
req.request.amount,
|
||||
req,
|
||||
))?;
|
||||
if req.is_three_ds() && req.request.is_card() {
|
||||
let connector_req =
|
||||
cybersource::CybersourceAuthSetupRequest::try_from(&connector_router_data)?;
|
||||
Ok(RequestContent::Json(Box::new(connector_req)))
|
||||
} else {
|
||||
let connector_req =
|
||||
cybersource::CybersourcePaymentsRequest::try_from(&connector_router_data)?;
|
||||
Ok(RequestContent::Json(Box::new(connector_req)))
|
||||
}
|
||||
}
|
||||
|
||||
fn build_request(
|
||||
&self,
|
||||
@ -722,6 +845,106 @@ impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::P
|
||||
data: &types::PaymentsAuthorizeRouterData,
|
||||
res: types::Response,
|
||||
) -> CustomResult<types::PaymentsAuthorizeRouterData, errors::ConnectorError> {
|
||||
if data.is_three_ds() && data.request.is_card() {
|
||||
let response: cybersource::CybersourceAuthSetupResponse = res
|
||||
.response
|
||||
.parse_struct("Cybersource AuthSetupResponse")
|
||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||
types::RouterData::try_from(types::ResponseRouterData {
|
||||
response,
|
||||
data: data.clone(),
|
||||
http_code: res.status_code,
|
||||
})
|
||||
} else {
|
||||
let response: cybersource::CybersourcePaymentsResponse = res
|
||||
.response
|
||||
.parse_struct("Cybersource PaymentResponse")
|
||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||
types::RouterData::try_from(types::ResponseRouterData {
|
||||
response,
|
||||
data: data.clone(),
|
||||
http_code: res.status_code,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn get_error_response(
|
||||
&self,
|
||||
res: types::Response,
|
||||
) -> CustomResult<types::ErrorResponse, errors::ConnectorError> {
|
||||
self.build_error_response(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl
|
||||
ConnectorIntegration<
|
||||
api::CompleteAuthorize,
|
||||
types::CompleteAuthorizeData,
|
||||
types::PaymentsResponseData,
|
||||
> for Cybersource
|
||||
{
|
||||
fn get_headers(
|
||||
&self,
|
||||
req: &types::PaymentsCompleteAuthorizeRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<Vec<(String, request::Maskable<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> {
|
||||
Ok(format!(
|
||||
"{}pts/v2/payments/",
|
||||
api::ConnectorCommon::base_url(self, connectors)
|
||||
))
|
||||
}
|
||||
fn get_request_body(
|
||||
&self,
|
||||
req: &types::PaymentsCompleteAuthorizeRouterData,
|
||||
_connectors: &settings::Connectors,
|
||||
) -> CustomResult<RequestContent, errors::ConnectorError> {
|
||||
let connector_router_data = cybersource::CybersourceRouterData::try_from((
|
||||
&self.get_currency_unit(),
|
||||
req.request.currency,
|
||||
req.request.amount,
|
||||
req,
|
||||
))?;
|
||||
let connector_req =
|
||||
cybersource::CybersourcePaymentsRequest::try_from(&connector_router_data)?;
|
||||
Ok(RequestContent::Json(Box::new(connector_req)))
|
||||
}
|
||||
fn build_request(
|
||||
&self,
|
||||
req: &types::PaymentsCompleteAuthorizeRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
|
||||
Ok(Some(
|
||||
services::RequestBuilder::new()
|
||||
.method(services::Method::Post)
|
||||
.url(&types::PaymentsCompleteAuthorizeType::get_url(
|
||||
self, req, connectors,
|
||||
)?)
|
||||
.attach_default_headers()
|
||||
.headers(types::PaymentsCompleteAuthorizeType::get_headers(
|
||||
self, req, connectors,
|
||||
)?)
|
||||
.set_body(types::PaymentsCompleteAuthorizeType::get_request_body(
|
||||
self, req, connectors,
|
||||
)?)
|
||||
.build(),
|
||||
))
|
||||
}
|
||||
|
||||
fn handle_response(
|
||||
&self,
|
||||
data: &types::PaymentsCompleteAuthorizeRouterData,
|
||||
res: types::Response,
|
||||
) -> CustomResult<types::PaymentsCompleteAuthorizeRouterData, errors::ConnectorError> {
|
||||
let response: cybersource::CybersourcePaymentsResponse = res
|
||||
.response
|
||||
.parse_struct("Cybersource PaymentResponse")
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
use api_models::payments;
|
||||
use base64::Engine;
|
||||
use common_utils::pii;
|
||||
use common_utils::{ext_traits::ValueExt, pii};
|
||||
use error_stack::{IntoReport, ResultExt};
|
||||
use masking::{PeekInterface, Secret};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
@ -8,10 +9,12 @@ use serde_json::Value;
|
||||
use crate::{
|
||||
connector::utils::{
|
||||
self, AddressDetailsData, ApplePayDecrypt, CardData, PaymentsAuthorizeRequestData,
|
||||
PaymentsCompleteAuthorizeRequestData, PaymentsPreProcessingData,
|
||||
PaymentsSetupMandateRequestData, PaymentsSyncRequestData, RouterData,
|
||||
},
|
||||
consts,
|
||||
core::errors,
|
||||
services,
|
||||
types::{
|
||||
self,
|
||||
api::{self, enums as api_enums},
|
||||
@ -200,7 +203,7 @@ impl TryFrom<&types::SetupMandateRouterData> for CybersourceZeroMandateRequest {
|
||||
action_list,
|
||||
action_token_types,
|
||||
authorization_options,
|
||||
commerce_indicator: CybersourceCommerceIndicator::Internet,
|
||||
commerce_indicator: String::from("internet"),
|
||||
payment_solution: solution.map(String::from),
|
||||
};
|
||||
Ok(Self {
|
||||
@ -220,6 +223,8 @@ pub struct CybersourcePaymentsRequest {
|
||||
order_information: OrderInformationWithBill,
|
||||
client_reference_information: ClientReferenceInformation,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
consumer_authentication_information: Option<CybersourceConsumerAuthInformation>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
merchant_defined_information: Option<Vec<MerchantDefinedInformation>>,
|
||||
}
|
||||
|
||||
@ -229,12 +234,22 @@ pub struct ProcessingInformation {
|
||||
action_list: Option<Vec<CybersourceActionsList>>,
|
||||
action_token_types: Option<Vec<CybersourceActionsTokenType>>,
|
||||
authorization_options: Option<CybersourceAuthorizationOptions>,
|
||||
commerce_indicator: CybersourceCommerceIndicator,
|
||||
commerce_indicator: String,
|
||||
capture: Option<bool>,
|
||||
capture_options: Option<CaptureOptions>,
|
||||
payment_solution: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CybersourceConsumerAuthInformation {
|
||||
ucaf_collection_indicator: Option<String>,
|
||||
cavv: Option<String>,
|
||||
ucaf_authentication_data: Option<String>,
|
||||
xid: Option<String>,
|
||||
directory_server_transaction_id: Option<String>,
|
||||
specification_version: Option<String>,
|
||||
}
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct MerchantDefinedInformation {
|
||||
@ -282,12 +297,6 @@ pub enum CybersourcePaymentInitiatorTypes {
|
||||
Customer,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum CybersourceCommerceIndicator {
|
||||
Internet,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CaptureOptions {
|
||||
@ -450,6 +459,16 @@ impl From<&CybersourceRouterData<&types::PaymentsAuthorizeRouterData>>
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&CybersourceRouterData<&types::PaymentsCompleteAuthorizeRouterData>>
|
||||
for ClientReferenceInformation
|
||||
{
|
||||
fn from(item: &CybersourceRouterData<&types::PaymentsCompleteAuthorizeRouterData>) -> Self {
|
||||
Self {
|
||||
code: Some(item.router_data.connector_request_reference_id.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl
|
||||
From<(
|
||||
&CybersourceRouterData<&types::PaymentsAuthorizeRouterData>,
|
||||
@ -489,7 +508,56 @@ impl
|
||||
action_token_types,
|
||||
authorization_options,
|
||||
capture_options: None,
|
||||
commerce_indicator: CybersourceCommerceIndicator::Internet,
|
||||
commerce_indicator: String::from("internet"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl
|
||||
From<(
|
||||
&CybersourceRouterData<&types::PaymentsCompleteAuthorizeRouterData>,
|
||||
Option<PaymentSolution>,
|
||||
&CybersourceConsumerAuthValidateResponse,
|
||||
)> for ProcessingInformation
|
||||
{
|
||||
fn from(
|
||||
(item, solution, three_ds_data): (
|
||||
&CybersourceRouterData<&types::PaymentsCompleteAuthorizeRouterData>,
|
||||
Option<PaymentSolution>,
|
||||
&CybersourceConsumerAuthValidateResponse,
|
||||
),
|
||||
) -> Self {
|
||||
let (action_list, action_token_types, authorization_options) =
|
||||
if item.router_data.request.setup_future_usage.is_some() {
|
||||
(
|
||||
Some(vec![CybersourceActionsList::TokenCreate]),
|
||||
Some(vec![CybersourceActionsTokenType::PaymentInstrument]),
|
||||
Some(CybersourceAuthorizationOptions {
|
||||
initiator: CybersourcePaymentInitiator {
|
||||
initiator_type: Some(CybersourcePaymentInitiatorTypes::Customer),
|
||||
credential_stored_on_file: Some(true),
|
||||
stored_credential_used: None,
|
||||
},
|
||||
merchant_intitiated_transaction: None,
|
||||
}),
|
||||
)
|
||||
} else {
|
||||
(None, None, None)
|
||||
};
|
||||
Self {
|
||||
capture: Some(matches!(
|
||||
item.router_data.request.capture_method,
|
||||
Some(enums::CaptureMethod::Automatic) | None
|
||||
)),
|
||||
payment_solution: solution.map(String::from),
|
||||
action_list,
|
||||
action_token_types,
|
||||
authorization_options,
|
||||
capture_options: None,
|
||||
commerce_indicator: three_ds_data
|
||||
.indicator
|
||||
.to_owned()
|
||||
.unwrap_or(String::from("internet")),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -516,6 +584,28 @@ impl
|
||||
}
|
||||
}
|
||||
|
||||
impl
|
||||
From<(
|
||||
&CybersourceRouterData<&types::PaymentsCompleteAuthorizeRouterData>,
|
||||
BillTo,
|
||||
)> for OrderInformationWithBill
|
||||
{
|
||||
fn from(
|
||||
(item, bill_to): (
|
||||
&CybersourceRouterData<&types::PaymentsCompleteAuthorizeRouterData>,
|
||||
BillTo,
|
||||
),
|
||||
) -> Self {
|
||||
Self {
|
||||
amount_details: Amount {
|
||||
total_amount: item.amount.to_owned(),
|
||||
currency: item.router_data.request.currency,
|
||||
},
|
||||
bill_to: Some(bill_to),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// for cybersource each item in Billing is mandatory
|
||||
fn build_bill_to(
|
||||
address_details: &payments::Address,
|
||||
@ -602,6 +692,84 @@ impl
|
||||
payment_information,
|
||||
order_information,
|
||||
client_reference_information,
|
||||
consumer_authentication_information: None,
|
||||
merchant_defined_information,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl
|
||||
TryFrom<(
|
||||
&CybersourceRouterData<&types::PaymentsCompleteAuthorizeRouterData>,
|
||||
payments::Card,
|
||||
)> for CybersourcePaymentsRequest
|
||||
{
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(
|
||||
(item, ccard): (
|
||||
&CybersourceRouterData<&types::PaymentsCompleteAuthorizeRouterData>,
|
||||
payments::Card,
|
||||
),
|
||||
) -> Result<Self, Self::Error> {
|
||||
let email = item.router_data.request.get_email()?;
|
||||
let bill_to = build_bill_to(item.router_data.get_billing()?, email)?;
|
||||
let order_information = OrderInformationWithBill::from((item, bill_to));
|
||||
|
||||
let card_issuer = ccard.get_card_issuer();
|
||||
let card_type = match card_issuer {
|
||||
Ok(issuer) => Some(String::from(issuer)),
|
||||
Err(_) => None,
|
||||
};
|
||||
|
||||
let payment_information = PaymentInformation::Cards(CardPaymentInformation {
|
||||
card: Card {
|
||||
number: ccard.card_number,
|
||||
expiration_month: ccard.card_exp_month,
|
||||
expiration_year: ccard.card_exp_year,
|
||||
security_code: ccard.card_cvc,
|
||||
card_type,
|
||||
},
|
||||
});
|
||||
let client_reference_information = ClientReferenceInformation::from(item);
|
||||
|
||||
let three_ds_info: CybersourceThreeDSMetadata = item
|
||||
.router_data
|
||||
.request
|
||||
.connector_meta
|
||||
.clone()
|
||||
.ok_or(errors::ConnectorError::MissingRequiredField {
|
||||
field_name: "connector_meta",
|
||||
})?
|
||||
.parse_value("CybersourceThreeDSMetadata")
|
||||
.change_context(errors::ConnectorError::InvalidConnectorConfig {
|
||||
config: "Merchant connector account metadata",
|
||||
})?;
|
||||
|
||||
let processing_information =
|
||||
ProcessingInformation::from((item, None, &three_ds_info.three_ds_data));
|
||||
|
||||
let consumer_authentication_information = Some(CybersourceConsumerAuthInformation {
|
||||
ucaf_collection_indicator: three_ds_info.three_ds_data.ucaf_collection_indicator,
|
||||
cavv: three_ds_info.three_ds_data.cavv,
|
||||
ucaf_authentication_data: three_ds_info.three_ds_data.ucaf_authentication_data,
|
||||
xid: three_ds_info.three_ds_data.xid,
|
||||
directory_server_transaction_id: three_ds_info
|
||||
.three_ds_data
|
||||
.directory_server_transaction_id,
|
||||
specification_version: three_ds_info.three_ds_data.specification_version,
|
||||
});
|
||||
|
||||
let merchant_defined_information =
|
||||
item.router_data.request.metadata.clone().map(|metadata| {
|
||||
Vec::<MerchantDefinedInformation>::foreign_from(metadata.peek().to_owned())
|
||||
});
|
||||
|
||||
Ok(Self {
|
||||
processing_information,
|
||||
payment_information,
|
||||
order_information,
|
||||
client_reference_information,
|
||||
consumer_authentication_information,
|
||||
merchant_defined_information,
|
||||
})
|
||||
}
|
||||
@ -647,6 +815,7 @@ impl
|
||||
payment_information,
|
||||
order_information,
|
||||
client_reference_information,
|
||||
consumer_authentication_information: None,
|
||||
merchant_defined_information,
|
||||
})
|
||||
}
|
||||
@ -689,6 +858,7 @@ impl
|
||||
payment_information,
|
||||
order_information,
|
||||
client_reference_information,
|
||||
consumer_authentication_information: None,
|
||||
merchant_defined_information,
|
||||
})
|
||||
}
|
||||
@ -747,6 +917,7 @@ impl TryFrom<&CybersourceRouterData<&types::PaymentsAuthorizeRouterData>>
|
||||
order_information,
|
||||
client_reference_information,
|
||||
merchant_defined_information,
|
||||
consumer_authentication_information: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -810,6 +981,7 @@ impl TryFrom<&CybersourceRouterData<&types::PaymentsAuthorizeRouterData>>
|
||||
order_information,
|
||||
client_reference_information,
|
||||
merchant_defined_information,
|
||||
consumer_authentication_information: None,
|
||||
})
|
||||
}
|
||||
payments::PaymentMethodData::CardRedirect(_)
|
||||
@ -832,6 +1004,64 @@ impl TryFrom<&CybersourceRouterData<&types::PaymentsAuthorizeRouterData>>
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CybersourceAuthSetupRequest {
|
||||
payment_information: PaymentInformation,
|
||||
client_reference_information: ClientReferenceInformation,
|
||||
}
|
||||
|
||||
impl TryFrom<&CybersourceRouterData<&types::PaymentsAuthorizeRouterData>>
|
||||
for CybersourceAuthSetupRequest
|
||||
{
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(
|
||||
item: &CybersourceRouterData<&types::PaymentsAuthorizeRouterData>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
match item.router_data.request.payment_method_data.clone() {
|
||||
payments::PaymentMethodData::Card(ccard) => {
|
||||
let card_issuer = ccard.get_card_issuer();
|
||||
let card_type = match card_issuer {
|
||||
Ok(issuer) => Some(String::from(issuer)),
|
||||
Err(_) => None,
|
||||
};
|
||||
let payment_information = PaymentInformation::Cards(CardPaymentInformation {
|
||||
card: Card {
|
||||
number: ccard.card_number,
|
||||
expiration_month: ccard.card_exp_month,
|
||||
expiration_year: ccard.card_exp_year,
|
||||
security_code: ccard.card_cvc,
|
||||
card_type,
|
||||
},
|
||||
});
|
||||
let client_reference_information = ClientReferenceInformation::from(item);
|
||||
Ok(Self {
|
||||
payment_information,
|
||||
client_reference_information,
|
||||
})
|
||||
}
|
||||
payments::PaymentMethodData::Wallet(_)
|
||||
| payments::PaymentMethodData::CardRedirect(_)
|
||||
| payments::PaymentMethodData::PayLater(_)
|
||||
| payments::PaymentMethodData::BankRedirect(_)
|
||||
| payments::PaymentMethodData::BankDebit(_)
|
||||
| payments::PaymentMethodData::BankTransfer(_)
|
||||
| payments::PaymentMethodData::Crypto(_)
|
||||
| payments::PaymentMethodData::MandatePayment
|
||||
| payments::PaymentMethodData::Reward
|
||||
| payments::PaymentMethodData::Upi(_)
|
||||
| payments::PaymentMethodData::Voucher(_)
|
||||
| payments::PaymentMethodData::GiftCard(_)
|
||||
| payments::PaymentMethodData::CardToken(_) => {
|
||||
Err(errors::ConnectorError::NotImplemented(
|
||||
utils::get_unimplemented_payment_method_error_message("Cybersource"),
|
||||
)
|
||||
.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CybersourcePaymentsCaptureRequest {
|
||||
@ -870,7 +1100,7 @@ impl TryFrom<&CybersourceRouterData<&types::PaymentsCaptureRouterData>>
|
||||
action_token_types: None,
|
||||
authorization_options: None,
|
||||
capture: None,
|
||||
commerce_indicator: CybersourceCommerceIndicator::Internet,
|
||||
commerce_indicator: String::from("internet"),
|
||||
payment_solution: None,
|
||||
},
|
||||
order_information: OrderInformationWithBill {
|
||||
@ -909,7 +1139,7 @@ impl TryFrom<&CybersourceRouterData<&types::PaymentsIncrementalAuthorizationRout
|
||||
reason: "5".to_owned(),
|
||||
}),
|
||||
}),
|
||||
commerce_indicator: CybersourceCommerceIndicator::Internet,
|
||||
commerce_indicator: String::from("internet"),
|
||||
capture: None,
|
||||
capture_options: None,
|
||||
payment_solution: None,
|
||||
@ -1118,6 +1348,29 @@ pub struct CybersourceErrorInformationResponse {
|
||||
error_information: CybersourceErrorInformation,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CybersourceConsumerAuthInformationResponse {
|
||||
access_token: String,
|
||||
device_data_collection_url: String,
|
||||
reference_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ClientAuthSetupInfoResponse {
|
||||
id: String,
|
||||
client_reference_information: ClientReferenceInformation,
|
||||
consumer_authentication_information: CybersourceConsumerAuthInformationResponse,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum CybersourceAuthSetupResponse {
|
||||
ClientAuthSetupInfo(ClientAuthSetupInfoResponse),
|
||||
ErrorInformation(CybersourceErrorInformationResponse),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CybersourcePaymentsIncrementalAuthorizationResponse {
|
||||
@ -1326,6 +1579,492 @@ impl<F>
|
||||
}
|
||||
}
|
||||
|
||||
impl<F>
|
||||
TryFrom<
|
||||
types::ResponseRouterData<
|
||||
F,
|
||||
CybersourceAuthSetupResponse,
|
||||
types::PaymentsAuthorizeData,
|
||||
types::PaymentsResponseData,
|
||||
>,
|
||||
> for types::RouterData<F, types::PaymentsAuthorizeData, types::PaymentsResponseData>
|
||||
{
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(
|
||||
item: types::ResponseRouterData<
|
||||
F,
|
||||
CybersourceAuthSetupResponse,
|
||||
types::PaymentsAuthorizeData,
|
||||
types::PaymentsResponseData,
|
||||
>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
match item.response {
|
||||
CybersourceAuthSetupResponse::ClientAuthSetupInfo(info_response) => Ok(Self {
|
||||
status: enums::AttemptStatus::AuthenticationPending,
|
||||
response: Ok(types::PaymentsResponseData::TransactionResponse {
|
||||
resource_id: types::ResponseId::NoResponseId,
|
||||
redirection_data: Some(services::RedirectForm::CybersourceAuthSetup {
|
||||
access_token: info_response
|
||||
.consumer_authentication_information
|
||||
.access_token,
|
||||
ddc_url: info_response
|
||||
.consumer_authentication_information
|
||||
.device_data_collection_url,
|
||||
reference_id: info_response
|
||||
.consumer_authentication_information
|
||||
.reference_id,
|
||||
}),
|
||||
mandate_reference: None,
|
||||
connector_metadata: None,
|
||||
network_txn_id: None,
|
||||
connector_response_reference_id: Some(
|
||||
info_response
|
||||
.client_reference_information
|
||||
.code
|
||||
.unwrap_or(info_response.id.clone()),
|
||||
),
|
||||
incremental_authorization_allowed: None,
|
||||
}),
|
||||
..item.data
|
||||
}),
|
||||
CybersourceAuthSetupResponse::ErrorInformation(error_response) => {
|
||||
let error_reason = error_response
|
||||
.error_information
|
||||
.message
|
||||
.unwrap_or(consts::NO_ERROR_MESSAGE.to_string());
|
||||
let error_message = error_response.error_information.reason;
|
||||
Ok(Self {
|
||||
response: Err(types::ErrorResponse {
|
||||
code: error_message
|
||||
.clone()
|
||||
.unwrap_or(consts::NO_ERROR_CODE.to_string()),
|
||||
message: error_message.unwrap_or(consts::NO_ERROR_MESSAGE.to_string()),
|
||||
reason: Some(error_reason),
|
||||
status_code: item.http_code,
|
||||
attempt_status: None,
|
||||
connector_transaction_id: Some(error_response.id.clone()),
|
||||
}),
|
||||
status: enums::AttemptStatus::AuthenticationFailed,
|
||||
..item.data
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CybersourceConsumerAuthInformationRequest {
|
||||
return_url: String,
|
||||
reference_id: String,
|
||||
}
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CybersourceAuthEnrollmentRequest {
|
||||
payment_information: PaymentInformation,
|
||||
client_reference_information: ClientReferenceInformation,
|
||||
consumer_authentication_information: CybersourceConsumerAuthInformationRequest,
|
||||
order_information: OrderInformationWithBill,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "PascalCase")]
|
||||
pub struct CybersourceRedirectionAuthResponse {
|
||||
pub transaction_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CybersourceConsumerAuthInformationValidateRequest {
|
||||
authentication_transaction_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CybersourceAuthValidateRequest {
|
||||
payment_information: PaymentInformation,
|
||||
client_reference_information: ClientReferenceInformation,
|
||||
consumer_authentication_information: CybersourceConsumerAuthInformationValidateRequest,
|
||||
order_information: OrderInformation,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum CybersourcePreProcessingRequest {
|
||||
AuthEnrollment(CybersourceAuthEnrollmentRequest),
|
||||
AuthValidate(CybersourceAuthValidateRequest),
|
||||
}
|
||||
|
||||
impl TryFrom<&CybersourceRouterData<&types::PaymentsPreProcessingRouterData>>
|
||||
for CybersourcePreProcessingRequest
|
||||
{
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(
|
||||
item: &CybersourceRouterData<&types::PaymentsPreProcessingRouterData>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
let client_reference_information = ClientReferenceInformation {
|
||||
code: Some(item.router_data.connector_request_reference_id.clone()),
|
||||
};
|
||||
let payment_method_data = item.router_data.request.payment_method_data.clone().ok_or(
|
||||
errors::ConnectorError::MissingConnectorRedirectionPayload {
|
||||
field_name: "payment_method_data",
|
||||
},
|
||||
)?;
|
||||
let payment_information = match payment_method_data {
|
||||
payments::PaymentMethodData::Card(ccard) => {
|
||||
let card_issuer = ccard.get_card_issuer();
|
||||
let card_type = match card_issuer {
|
||||
Ok(issuer) => Some(String::from(issuer)),
|
||||
Err(_) => None,
|
||||
};
|
||||
Ok(PaymentInformation::Cards(CardPaymentInformation {
|
||||
card: Card {
|
||||
number: ccard.card_number,
|
||||
expiration_month: ccard.card_exp_month,
|
||||
expiration_year: ccard.card_exp_year,
|
||||
security_code: ccard.card_cvc,
|
||||
card_type,
|
||||
},
|
||||
}))
|
||||
}
|
||||
payments::PaymentMethodData::Wallet(_)
|
||||
| payments::PaymentMethodData::CardRedirect(_)
|
||||
| payments::PaymentMethodData::PayLater(_)
|
||||
| payments::PaymentMethodData::BankRedirect(_)
|
||||
| payments::PaymentMethodData::BankDebit(_)
|
||||
| payments::PaymentMethodData::BankTransfer(_)
|
||||
| payments::PaymentMethodData::Crypto(_)
|
||||
| payments::PaymentMethodData::MandatePayment
|
||||
| payments::PaymentMethodData::Reward
|
||||
| payments::PaymentMethodData::Upi(_)
|
||||
| payments::PaymentMethodData::Voucher(_)
|
||||
| payments::PaymentMethodData::GiftCard(_)
|
||||
| payments::PaymentMethodData::CardToken(_) => {
|
||||
Err(errors::ConnectorError::NotImplemented(
|
||||
utils::get_unimplemented_payment_method_error_message("Cybersource"),
|
||||
))
|
||||
}
|
||||
}?;
|
||||
|
||||
let redirect_response = item.router_data.request.redirect_response.clone().ok_or(
|
||||
errors::ConnectorError::MissingRequiredField {
|
||||
field_name: "redirect_response",
|
||||
},
|
||||
)?;
|
||||
|
||||
let amount_details = Amount {
|
||||
total_amount: item.amount.clone(),
|
||||
currency: item.router_data.request.currency.ok_or(
|
||||
errors::ConnectorError::MissingRequiredField {
|
||||
field_name: "currency",
|
||||
},
|
||||
)?,
|
||||
};
|
||||
|
||||
match redirect_response.params {
|
||||
Some(param) if !param.clone().peek().is_empty() => {
|
||||
let reference_id = param
|
||||
.clone()
|
||||
.peek()
|
||||
.split_once('=')
|
||||
.ok_or(errors::ConnectorError::MissingConnectorRedirectionPayload {
|
||||
field_name: "request.redirect_response.params.reference_id",
|
||||
})?
|
||||
.1
|
||||
.to_string();
|
||||
let email = item.router_data.request.get_email()?;
|
||||
let bill_to = build_bill_to(item.router_data.get_billing()?, email)?;
|
||||
let order_information = OrderInformationWithBill {
|
||||
amount_details,
|
||||
bill_to: Some(bill_to),
|
||||
};
|
||||
Ok(Self::AuthEnrollment(CybersourceAuthEnrollmentRequest {
|
||||
payment_information,
|
||||
client_reference_information,
|
||||
consumer_authentication_information:
|
||||
CybersourceConsumerAuthInformationRequest {
|
||||
return_url: item.router_data.request.get_complete_authorize_url()?,
|
||||
reference_id,
|
||||
},
|
||||
order_information,
|
||||
}))
|
||||
}
|
||||
Some(_) | None => {
|
||||
let redirect_payload: CybersourceRedirectionAuthResponse = redirect_response
|
||||
.payload
|
||||
.ok_or(errors::ConnectorError::MissingConnectorRedirectionPayload {
|
||||
field_name: "request.redirect_response.payload",
|
||||
})?
|
||||
.peek()
|
||||
.clone()
|
||||
.parse_value("CybersourceRedirectionAuthResponse")
|
||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||
let order_information = OrderInformation { amount_details };
|
||||
Ok(Self::AuthValidate(CybersourceAuthValidateRequest {
|
||||
payment_information,
|
||||
client_reference_information,
|
||||
consumer_authentication_information:
|
||||
CybersourceConsumerAuthInformationValidateRequest {
|
||||
authentication_transaction_id: redirect_payload.transaction_id,
|
||||
},
|
||||
order_information,
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&CybersourceRouterData<&types::PaymentsCompleteAuthorizeRouterData>>
|
||||
for CybersourcePaymentsRequest
|
||||
{
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(
|
||||
item: &CybersourceRouterData<&types::PaymentsCompleteAuthorizeRouterData>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
let payment_method_data = item.router_data.request.payment_method_data.clone().ok_or(
|
||||
errors::ConnectorError::MissingRequiredField {
|
||||
field_name: "payment_method_data",
|
||||
},
|
||||
)?;
|
||||
match payment_method_data {
|
||||
payments::PaymentMethodData::Card(ccard) => Self::try_from((item, ccard)),
|
||||
payments::PaymentMethodData::Wallet(_)
|
||||
| payments::PaymentMethodData::CardRedirect(_)
|
||||
| payments::PaymentMethodData::PayLater(_)
|
||||
| payments::PaymentMethodData::BankRedirect(_)
|
||||
| payments::PaymentMethodData::BankDebit(_)
|
||||
| payments::PaymentMethodData::BankTransfer(_)
|
||||
| payments::PaymentMethodData::Crypto(_)
|
||||
| payments::PaymentMethodData::MandatePayment
|
||||
| payments::PaymentMethodData::Reward
|
||||
| payments::PaymentMethodData::Upi(_)
|
||||
| payments::PaymentMethodData::Voucher(_)
|
||||
| payments::PaymentMethodData::GiftCard(_)
|
||||
| payments::PaymentMethodData::CardToken(_) => {
|
||||
Err(errors::ConnectorError::NotImplemented(
|
||||
utils::get_unimplemented_payment_method_error_message("Cybersource"),
|
||||
)
|
||||
.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||
pub enum CybersourceAuthEnrollmentStatus {
|
||||
PendingAuthentication,
|
||||
AuthenticationSuccessful,
|
||||
AuthenticationFailed,
|
||||
}
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CybersourceConsumerAuthValidateResponse {
|
||||
ucaf_collection_indicator: Option<String>,
|
||||
cavv: Option<String>,
|
||||
ucaf_authentication_data: Option<String>,
|
||||
xid: Option<String>,
|
||||
specification_version: Option<String>,
|
||||
directory_server_transaction_id: Option<String>,
|
||||
indicator: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct CybersourceThreeDSMetadata {
|
||||
three_ds_data: CybersourceConsumerAuthValidateResponse,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CybersourceConsumerAuthInformationEnrollmentResponse {
|
||||
access_token: Option<String>,
|
||||
step_up_url: Option<String>,
|
||||
//Added to segregate the three_ds_data in a separate struct
|
||||
#[serde(flatten)]
|
||||
validate_response: CybersourceConsumerAuthValidateResponse,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ClientAuthCheckInfoResponse {
|
||||
id: String,
|
||||
client_reference_information: ClientReferenceInformation,
|
||||
consumer_authentication_information: CybersourceConsumerAuthInformationEnrollmentResponse,
|
||||
status: CybersourceAuthEnrollmentStatus,
|
||||
error_information: Option<CybersourceErrorInformation>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum CybersourcePreProcessingResponse {
|
||||
ClientAuthCheckInfo(Box<ClientAuthCheckInfoResponse>),
|
||||
ErrorInformation(CybersourceErrorInformationResponse),
|
||||
}
|
||||
|
||||
impl From<CybersourceAuthEnrollmentStatus> for enums::AttemptStatus {
|
||||
fn from(item: CybersourceAuthEnrollmentStatus) -> Self {
|
||||
match item {
|
||||
CybersourceAuthEnrollmentStatus::PendingAuthentication => Self::AuthenticationPending,
|
||||
CybersourceAuthEnrollmentStatus::AuthenticationSuccessful => {
|
||||
Self::AuthenticationSuccessful
|
||||
}
|
||||
CybersourceAuthEnrollmentStatus::AuthenticationFailed => Self::AuthenticationFailed,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F>
|
||||
TryFrom<
|
||||
types::ResponseRouterData<
|
||||
F,
|
||||
CybersourcePreProcessingResponse,
|
||||
types::PaymentsPreProcessingData,
|
||||
types::PaymentsResponseData,
|
||||
>,
|
||||
> for types::RouterData<F, types::PaymentsPreProcessingData, types::PaymentsResponseData>
|
||||
{
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(
|
||||
item: types::ResponseRouterData<
|
||||
F,
|
||||
CybersourcePreProcessingResponse,
|
||||
types::PaymentsPreProcessingData,
|
||||
types::PaymentsResponseData,
|
||||
>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
match item.response {
|
||||
CybersourcePreProcessingResponse::ClientAuthCheckInfo(info_response) => {
|
||||
let status = enums::AttemptStatus::from(info_response.status);
|
||||
let risk_info: Option<ClientRiskInformation> = None;
|
||||
if utils::is_payment_failure(status) {
|
||||
let response = Err(types::ErrorResponse::from((
|
||||
&info_response.error_information,
|
||||
&risk_info,
|
||||
item.http_code,
|
||||
info_response.id.clone(),
|
||||
)));
|
||||
|
||||
Ok(Self {
|
||||
status,
|
||||
response,
|
||||
..item.data
|
||||
})
|
||||
} else {
|
||||
let connector_response_reference_id = Some(
|
||||
info_response
|
||||
.client_reference_information
|
||||
.code
|
||||
.unwrap_or(info_response.id.clone()),
|
||||
);
|
||||
|
||||
let redirection_data = match (
|
||||
info_response
|
||||
.consumer_authentication_information
|
||||
.access_token,
|
||||
info_response
|
||||
.consumer_authentication_information
|
||||
.step_up_url,
|
||||
) {
|
||||
(Some(access_token), Some(step_up_url)) => {
|
||||
Some(services::RedirectForm::CybersourceConsumerAuth {
|
||||
access_token,
|
||||
step_up_url,
|
||||
})
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
let three_ds_data = serde_json::to_value(
|
||||
info_response
|
||||
.consumer_authentication_information
|
||||
.validate_response,
|
||||
)
|
||||
.into_report()
|
||||
.change_context(errors::ConnectorError::ResponseHandlingFailed)?;
|
||||
Ok(Self {
|
||||
status,
|
||||
response: Ok(types::PaymentsResponseData::TransactionResponse {
|
||||
resource_id: types::ResponseId::NoResponseId,
|
||||
redirection_data,
|
||||
mandate_reference: None,
|
||||
connector_metadata: Some(
|
||||
serde_json::json!({"three_ds_data":three_ds_data}),
|
||||
),
|
||||
network_txn_id: None,
|
||||
connector_response_reference_id,
|
||||
incremental_authorization_allowed: None,
|
||||
}),
|
||||
..item.data
|
||||
})
|
||||
}
|
||||
}
|
||||
CybersourcePreProcessingResponse::ErrorInformation(ref error_response) => {
|
||||
let error_reason = error_response
|
||||
.error_information
|
||||
.message
|
||||
.to_owned()
|
||||
.unwrap_or(consts::NO_ERROR_MESSAGE.to_string());
|
||||
let error_message = error_response.error_information.reason.to_owned();
|
||||
let response = Err(types::ErrorResponse {
|
||||
code: error_message
|
||||
.clone()
|
||||
.unwrap_or(consts::NO_ERROR_CODE.to_string()),
|
||||
message: error_message.unwrap_or(consts::NO_ERROR_MESSAGE.to_string()),
|
||||
reason: Some(error_reason),
|
||||
status_code: item.http_code,
|
||||
attempt_status: None,
|
||||
connector_transaction_id: Some(error_response.id.clone()),
|
||||
});
|
||||
Ok(Self {
|
||||
response,
|
||||
status: enums::AttemptStatus::AuthenticationFailed,
|
||||
..item.data
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F>
|
||||
TryFrom<
|
||||
types::ResponseRouterData<
|
||||
F,
|
||||
CybersourcePaymentsResponse,
|
||||
types::CompleteAuthorizeData,
|
||||
types::PaymentsResponseData,
|
||||
>,
|
||||
> for types::RouterData<F, types::CompleteAuthorizeData, types::PaymentsResponseData>
|
||||
{
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(
|
||||
item: types::ResponseRouterData<
|
||||
F,
|
||||
CybersourcePaymentsResponse,
|
||||
types::CompleteAuthorizeData,
|
||||
types::PaymentsResponseData,
|
||||
>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
match item.response {
|
||||
CybersourcePaymentsResponse::ClientReferenceInformation(info_response) => {
|
||||
let status = enums::AttemptStatus::foreign_from((
|
||||
info_response.status.clone(),
|
||||
item.data.request.is_auto_capture()?,
|
||||
));
|
||||
let response = get_payment_response((&info_response, status, item.http_code));
|
||||
Ok(Self {
|
||||
status,
|
||||
response,
|
||||
..item.data
|
||||
})
|
||||
}
|
||||
CybersourcePaymentsResponse::ErrorInformation(ref error_response) => Ok(Self::from((
|
||||
&error_response.clone(),
|
||||
item,
|
||||
Some(enums::AttemptStatus::Failure),
|
||||
))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F>
|
||||
TryFrom<
|
||||
types::ResponseRouterData<
|
||||
@ -1463,25 +2202,29 @@ impl<F, T>
|
||||
..item.data
|
||||
})
|
||||
}
|
||||
CybersourceSetupMandatesResponse::ErrorInformation(error_response) => Ok(Self {
|
||||
response: {
|
||||
let error_reason = &error_response.error_information.reason;
|
||||
Err(types::ErrorResponse {
|
||||
code: error_reason
|
||||
CybersourceSetupMandatesResponse::ErrorInformation(ref error_response) => {
|
||||
let error_reason = error_response
|
||||
.error_information
|
||||
.message
|
||||
.to_owned()
|
||||
.unwrap_or(consts::NO_ERROR_MESSAGE.to_string());
|
||||
let error_message = error_response.error_information.reason.to_owned();
|
||||
let response = Err(types::ErrorResponse {
|
||||
code: error_message
|
||||
.clone()
|
||||
.unwrap_or(consts::NO_ERROR_CODE.to_string()),
|
||||
message: error_reason
|
||||
.clone()
|
||||
.unwrap_or(consts::NO_ERROR_MESSAGE.to_string()),
|
||||
reason: error_response.error_information.message,
|
||||
message: error_message.unwrap_or(consts::NO_ERROR_MESSAGE.to_string()),
|
||||
reason: Some(error_reason),
|
||||
status_code: item.http_code,
|
||||
attempt_status: None,
|
||||
connector_transaction_id: Some(error_response.id.clone()),
|
||||
})
|
||||
},
|
||||
});
|
||||
Ok(Self {
|
||||
response,
|
||||
status: enums::AttemptStatus::Failure,
|
||||
..item.data
|
||||
}),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -273,6 +273,7 @@ pub trait PaymentsPreProcessingData {
|
||||
fn get_webhook_url(&self) -> Result<String, Error>;
|
||||
fn get_return_url(&self) -> Result<String, Error>;
|
||||
fn get_browser_info(&self) -> Result<BrowserInformation, Error>;
|
||||
fn get_complete_authorize_url(&self) -> Result<String, Error>;
|
||||
}
|
||||
|
||||
impl PaymentsPreProcessingData for types::PaymentsPreProcessingData {
|
||||
@ -317,6 +318,11 @@ impl PaymentsPreProcessingData for types::PaymentsPreProcessingData {
|
||||
.clone()
|
||||
.ok_or_else(missing_field_err("browser_info"))
|
||||
}
|
||||
fn get_complete_authorize_url(&self) -> Result<String, Error> {
|
||||
self.complete_authorize_url
|
||||
.clone()
|
||||
.ok_or_else(missing_field_err("complete_authorize_url"))
|
||||
}
|
||||
}
|
||||
|
||||
pub trait PaymentsCaptureRequestData {
|
||||
@ -592,6 +598,7 @@ pub trait PaymentsCompleteAuthorizeRequestData {
|
||||
fn is_auto_capture(&self) -> Result<bool, Error>;
|
||||
fn get_email(&self) -> Result<Email, Error>;
|
||||
fn get_redirect_response_payload(&self) -> Result<pii::SecretSerdeValue, Error>;
|
||||
fn get_complete_authorize_url(&self) -> Result<String, Error>;
|
||||
}
|
||||
|
||||
impl PaymentsCompleteAuthorizeRequestData for types::CompleteAuthorizeData {
|
||||
@ -616,6 +623,11 @@ impl PaymentsCompleteAuthorizeRequestData for types::CompleteAuthorizeData {
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
fn get_complete_authorize_url(&self) -> Result<String, Error> {
|
||||
self.complete_authorize_url
|
||||
.clone()
|
||||
.ok_or_else(missing_field_err("complete_authorize_url"))
|
||||
}
|
||||
}
|
||||
|
||||
pub trait PaymentsSyncRequestData {
|
||||
|
||||
@ -1489,6 +1489,22 @@ where
|
||||
router_data = router_data.preprocessing_steps(state, connector).await?;
|
||||
|
||||
(router_data, false)
|
||||
} else if connector.connector_name == router_types::Connector::Cybersource
|
||||
&& is_operation_complete_authorize(&operation)
|
||||
&& router_data.auth_type == storage_enums::AuthenticationType::ThreeDs
|
||||
{
|
||||
router_data = router_data.preprocessing_steps(state, connector).await?;
|
||||
|
||||
// Should continue the flow only if no redirection_data is returned else a response with redirection form shall be returned
|
||||
let should_continue = matches!(
|
||||
router_data.response,
|
||||
Ok(router_types::PaymentsResponseData::TransactionResponse {
|
||||
redirection_data: None,
|
||||
..
|
||||
})
|
||||
) && router_data.status
|
||||
!= common_enums::AttemptStatus::AuthenticationFailed;
|
||||
(router_data, should_continue)
|
||||
} else {
|
||||
(router_data, should_continue_payment)
|
||||
}
|
||||
@ -2106,6 +2122,10 @@ pub fn is_operation_confirm<Op: Debug>(operation: &Op) -> bool {
|
||||
matches!(format!("{operation:?}").as_str(), "PaymentConfirm")
|
||||
}
|
||||
|
||||
pub fn is_operation_complete_authorize<Op: Debug>(operation: &Op) -> bool {
|
||||
matches!(format!("{operation:?}").as_str(), "CompleteAuthorize")
|
||||
}
|
||||
|
||||
#[cfg(feature = "olap")]
|
||||
pub async fn list_payments(
|
||||
state: AppState,
|
||||
|
||||
@ -154,7 +154,6 @@ default_imp_for_complete_authorize!(
|
||||
connector::Checkout,
|
||||
connector::Coinbase,
|
||||
connector::Cryptopay,
|
||||
connector::Cybersource,
|
||||
connector::Dlocal,
|
||||
connector::Fiserv,
|
||||
connector::Forte,
|
||||
@ -873,7 +872,6 @@ default_imp_for_pre_processing_steps!(
|
||||
connector::Checkout,
|
||||
connector::Coinbase,
|
||||
connector::Cryptopay,
|
||||
connector::Cybersource,
|
||||
connector::Dlocal,
|
||||
connector::Iatapay,
|
||||
connector::Fiserv,
|
||||
|
||||
@ -412,6 +412,7 @@ impl TryFrom<types::PaymentsAuthorizeData> for types::PaymentsPreProcessingData
|
||||
browser_info: data.browser_info,
|
||||
surcharge_details: data.surcharge_details,
|
||||
connector_transaction_id: None,
|
||||
redirect_response: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -431,10 +432,11 @@ impl TryFrom<types::CompleteAuthorizeData> for types::PaymentsPreProcessingData
|
||||
order_details: None,
|
||||
router_return_url: None,
|
||||
webhook_url: None,
|
||||
complete_authorize_url: None,
|
||||
complete_authorize_url: data.complete_authorize_url,
|
||||
browser_info: data.browser_info,
|
||||
surcharge_details: None,
|
||||
connector_transaction_id: data.connector_transaction_id,
|
||||
redirect_response: data.redirect_response,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -203,10 +203,19 @@ pub async fn complete_authorize_preprocessing_steps<F: Clone>(
|
||||
],
|
||||
);
|
||||
|
||||
let mut router_data_request = router_data.request.to_owned();
|
||||
|
||||
if let Ok(types::PaymentsResponseData::TransactionResponse {
|
||||
connector_metadata, ..
|
||||
}) = &resp.response
|
||||
{
|
||||
router_data_request.connector_meta = connector_metadata.to_owned();
|
||||
};
|
||||
|
||||
let authorize_router_data =
|
||||
payments::helpers::router_data_type_conversion::<_, F, _, _, _, _>(
|
||||
resp.clone(),
|
||||
router_data.request.to_owned(),
|
||||
router_data_request,
|
||||
resp.response,
|
||||
);
|
||||
|
||||
|
||||
@ -1425,6 +1425,9 @@ impl<F: Clone> TryFrom<PaymentAdditionalData<'_, F>> for types::CompleteAuthoriz
|
||||
|
||||
fn try_from(additional_data: PaymentAdditionalData<'_, F>) -> Result<Self, Self::Error> {
|
||||
let payment_data = additional_data.payment_data;
|
||||
let router_base_url = &additional_data.router_base_url;
|
||||
let connector_name = &additional_data.connector_name;
|
||||
let attempt = &payment_data.payment_attempt;
|
||||
let browser_info: Option<types::BrowserInformation> = payment_data
|
||||
.payment_attempt
|
||||
.browser_info
|
||||
@ -1446,7 +1449,11 @@ impl<F: Clone> TryFrom<PaymentAdditionalData<'_, F>> for types::CompleteAuthoriz
|
||||
.as_ref()
|
||||
.map(|surcharge_details| surcharge_details.final_amount)
|
||||
.unwrap_or(payment_data.amount.into());
|
||||
|
||||
let complete_authorize_url = Some(helpers::create_complete_authorize_url(
|
||||
router_base_url,
|
||||
attempt,
|
||||
connector_name,
|
||||
));
|
||||
Ok(Self {
|
||||
setup_future_usage: payment_data.payment_intent.setup_future_usage,
|
||||
mandate_id: payment_data.mandate_id.clone(),
|
||||
@ -1463,6 +1470,8 @@ impl<F: Clone> TryFrom<PaymentAdditionalData<'_, F>> for types::CompleteAuthoriz
|
||||
connector_transaction_id: payment_data.payment_attempt.connector_transaction_id,
|
||||
redirect_response,
|
||||
connector_meta: payment_data.payment_attempt.connector_metadata,
|
||||
complete_authorize_url,
|
||||
metadata: payment_data.payment_intent.metadata,
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -1541,6 +1550,7 @@ impl<F: Clone> TryFrom<PaymentAdditionalData<'_, F>> for types::PaymentsPreProce
|
||||
browser_info,
|
||||
surcharge_details: payment_data.surcharge_details,
|
||||
connector_transaction_id: payment_data.payment_attempt.connector_transaction_id,
|
||||
redirect_response: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -789,6 +789,15 @@ pub enum RedirectForm {
|
||||
BlueSnap {
|
||||
payment_fields_token: String, // payment-field-token
|
||||
},
|
||||
CybersourceAuthSetup {
|
||||
access_token: String,
|
||||
ddc_url: String,
|
||||
reference_id: String,
|
||||
},
|
||||
CybersourceConsumerAuth {
|
||||
access_token: String,
|
||||
step_up_url: String,
|
||||
},
|
||||
Payme,
|
||||
Braintree {
|
||||
client_token: String,
|
||||
@ -1426,6 +1435,105 @@ pub fn build_redirection_form(
|
||||
")))
|
||||
}}
|
||||
}
|
||||
RedirectForm::CybersourceAuthSetup {
|
||||
access_token,
|
||||
ddc_url,
|
||||
reference_id,
|
||||
} => {
|
||||
maud::html! {
|
||||
(maud::DOCTYPE)
|
||||
html {
|
||||
head {
|
||||
meta name="viewport" content="width=device-width, initial-scale=1";
|
||||
}
|
||||
body style="background-color: #ffffff; padding: 20px; font-family: Arial, Helvetica, Sans-Serif;" {
|
||||
|
||||
div id="loader1" class="lottie" style="height: 150px; display: block; position: relative; margin-top: 150px; margin-left: auto; margin-right: auto;" { "" }
|
||||
|
||||
(PreEscaped(r#"<script src="https://cdnjs.cloudflare.com/ajax/libs/bodymovin/5.7.4/lottie.min.js"></script>"#))
|
||||
|
||||
(PreEscaped(r#"
|
||||
<script>
|
||||
var anime = bodymovin.loadAnimation({
|
||||
container: document.getElementById('loader1'),
|
||||
renderer: 'svg',
|
||||
loop: true,
|
||||
autoplay: true,
|
||||
name: 'hyperswitch loader',
|
||||
animationData: {"v":"4.8.0","meta":{"g":"LottieFiles AE 3.1.1","a":"","k":"","d":"","tc":""},"fr":29.9700012207031,"ip":0,"op":31.0000012626559,"w":400,"h":250,"nm":"loader_shape","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"circle 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[278.25,202.671,0],"ix":2},"a":{"a":0,"k":[23.72,23.72,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[12.935,0],[0,-12.936],[-12.935,0],[0,12.935]],"o":[[-12.952,0],[0,12.935],[12.935,0],[0,-12.936]],"v":[[0,-23.471],[-23.47,0.001],[0,23.471],[23.47,0.001]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0.427451010311,0.976470648074,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":10,"s":[10]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":19.99,"s":[100]},{"t":29.9800012211104,"s":[10]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[23.72,23.721],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":48.0000019550801,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"square 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[196.25,201.271,0],"ix":2},"a":{"a":0,"k":[22.028,22.03,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.914,0],[0,0],[0,-1.914],[0,0],[-1.914,0],[0,0],[0,1.914],[0,0]],"o":[[0,0],[-1.914,0],[0,0],[0,1.914],[0,0],[1.914,0],[0,0],[0,-1.914]],"v":[[18.313,-21.779],[-18.312,-21.779],[-21.779,-18.313],[-21.779,18.314],[-18.312,21.779],[18.313,21.779],[21.779,18.314],[21.779,-18.313]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0.427451010311,0.976470648074,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":5,"s":[10]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":14.99,"s":[100]},{"t":24.9800010174563,"s":[10]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[22.028,22.029],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":47.0000019143492,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Triangle 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[116.25,200.703,0],"ix":2},"a":{"a":0,"k":[27.11,21.243,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0.558,-0.879],[0,0],[-1.133,0],[0,0],[0.609,0.947],[0,0]],"o":[[-0.558,-0.879],[0,0],[-0.609,0.947],[0,0],[1.133,0],[0,0],[0,0]],"v":[[1.209,-20.114],[-1.192,-20.114],[-26.251,18.795],[-25.051,20.993],[25.051,20.993],[26.251,18.795],[1.192,-20.114]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0.427451010311,0.976470648074,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[10]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":9.99,"s":[100]},{"t":19.9800008138021,"s":[10]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[27.11,21.243],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":48.0000019550801,"st":0,"bm":0}],"markers":[]}
|
||||
})
|
||||
</script>
|
||||
"#))
|
||||
|
||||
|
||||
h3 style="text-align: center;" { "Please wait while we process your payment..." }
|
||||
}
|
||||
|
||||
(PreEscaped(r#"<iframe id="cardinal_collection_iframe" name="collectionIframe" height="10" width="10" style="display: none;"></iframe>"#))
|
||||
(PreEscaped(format!("<form id=\"cardinal_collection_form\" method=\"POST\" target=\"collectionIframe\" action=\"{ddc_url}\">
|
||||
<input id=\"cardinal_collection_form_input\" type=\"hidden\" name=\"JWT\" value=\"{access_token}\">
|
||||
</form>")))
|
||||
(PreEscaped(r#"<script>
|
||||
window.onload = function() {
|
||||
var cardinalCollectionForm = document.querySelector('#cardinal_collection_form'); if(cardinalCollectionForm) cardinalCollectionForm.submit();
|
||||
}
|
||||
</script>"#))
|
||||
(PreEscaped(format!("<script>
|
||||
window.addEventListener(\"message\", function(event) {{
|
||||
if (event.origin === \"https://centinelapistag.cardinalcommerce.com\" || event.origin === \"https://centinelapi.cardinalcommerce.com\") {{
|
||||
window.location.href = window.location.pathname.replace(/payments\\/redirect\\/(\\w+)\\/(\\w+)\\/\\w+/, \"payments/$1/$2/redirect/complete/cybersource?referenceId={reference_id}\");
|
||||
}}
|
||||
}}, false);
|
||||
</script>
|
||||
")))
|
||||
}}
|
||||
}
|
||||
RedirectForm::CybersourceConsumerAuth {
|
||||
access_token,
|
||||
step_up_url,
|
||||
} => {
|
||||
maud::html! {
|
||||
(maud::DOCTYPE)
|
||||
html {
|
||||
head {
|
||||
meta name="viewport" content="width=device-width, initial-scale=1";
|
||||
}
|
||||
body style="background-color: #ffffff; padding: 20px; font-family: Arial, Helvetica, Sans-Serif;" {
|
||||
|
||||
div id="loader1" class="lottie" style="height: 150px; display: block; position: relative; margin-top: 150px; margin-left: auto; margin-right: auto;" { "" }
|
||||
|
||||
(PreEscaped(r#"<script src="https://cdnjs.cloudflare.com/ajax/libs/bodymovin/5.7.4/lottie.min.js"></script>"#))
|
||||
|
||||
(PreEscaped(r#"
|
||||
<script>
|
||||
var anime = bodymovin.loadAnimation({
|
||||
container: document.getElementById('loader1'),
|
||||
renderer: 'svg',
|
||||
loop: true,
|
||||
autoplay: true,
|
||||
name: 'hyperswitch loader',
|
||||
animationData: {"v":"4.8.0","meta":{"g":"LottieFiles AE 3.1.1","a":"","k":"","d":"","tc":""},"fr":29.9700012207031,"ip":0,"op":31.0000012626559,"w":400,"h":250,"nm":"loader_shape","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"circle 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[278.25,202.671,0],"ix":2},"a":{"a":0,"k":[23.72,23.72,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[12.935,0],[0,-12.936],[-12.935,0],[0,12.935]],"o":[[-12.952,0],[0,12.935],[12.935,0],[0,-12.936]],"v":[[0,-23.471],[-23.47,0.001],[0,23.471],[23.47,0.001]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0.427451010311,0.976470648074,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":10,"s":[10]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":19.99,"s":[100]},{"t":29.9800012211104,"s":[10]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[23.72,23.721],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":48.0000019550801,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"square 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[196.25,201.271,0],"ix":2},"a":{"a":0,"k":[22.028,22.03,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.914,0],[0,0],[0,-1.914],[0,0],[-1.914,0],[0,0],[0,1.914],[0,0]],"o":[[0,0],[-1.914,0],[0,0],[0,1.914],[0,0],[1.914,0],[0,0],[0,-1.914]],"v":[[18.313,-21.779],[-18.312,-21.779],[-21.779,-18.313],[-21.779,18.314],[-18.312,21.779],[18.313,21.779],[21.779,18.314],[21.779,-18.313]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0.427451010311,0.976470648074,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":5,"s":[10]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":14.99,"s":[100]},{"t":24.9800010174563,"s":[10]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[22.028,22.029],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":47.0000019143492,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Triangle 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[116.25,200.703,0],"ix":2},"a":{"a":0,"k":[27.11,21.243,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0.558,-0.879],[0,0],[-1.133,0],[0,0],[0.609,0.947],[0,0]],"o":[[-0.558,-0.879],[0,0],[-0.609,0.947],[0,0],[1.133,0],[0,0],[0,0]],"v":[[1.209,-20.114],[-1.192,-20.114],[-26.251,18.795],[-25.051,20.993],[25.051,20.993],[26.251,18.795],[1.192,-20.114]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0.427451010311,0.976470648074,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[10]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":9.99,"s":[100]},{"t":19.9800008138021,"s":[10]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[27.11,21.243],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":48.0000019550801,"st":0,"bm":0}],"markers":[]}
|
||||
})
|
||||
</script>
|
||||
"#))
|
||||
|
||||
|
||||
h3 style="text-align: center;" { "Please wait while we process your payment..." }
|
||||
}
|
||||
|
||||
// This is the iframe recommended by cybersource but the redirection happens inside this iframe once otp
|
||||
// is received and we lose control of the redirection on user client browser, so to avoid that we have removed this iframe and directly consumed it.
|
||||
// (PreEscaped(r#"<iframe id="step_up_iframe" style="border: none; margin-left: auto; margin-right: auto; display: block" height="800px" width="400px" name="stepUpIframe"></iframe>"#))
|
||||
(PreEscaped(format!("<form id=\"step_up_form\" method=\"POST\" action=\"{step_up_url}\">
|
||||
<input id=\"step_up_form_jwt_input\" type=\"hidden\" name=\"JWT\" value=\"{access_token}\">
|
||||
</form>")))
|
||||
(PreEscaped(r#"<script>
|
||||
window.onload = function() {
|
||||
var stepUpForm = document.querySelector('#step_up_form'); if(stepUpForm) stepUpForm.submit();
|
||||
}
|
||||
</script>"#))
|
||||
}}
|
||||
}
|
||||
RedirectForm::Payme => {
|
||||
maud::html! {
|
||||
(maud::DOCTYPE)
|
||||
|
||||
@ -490,6 +490,7 @@ pub struct PaymentsPreProcessingData {
|
||||
pub surcharge_details: Option<types::SurchargeDetails>,
|
||||
pub browser_info: Option<BrowserInformation>,
|
||||
pub connector_transaction_id: Option<String>,
|
||||
pub redirect_response: Option<CompleteAuthorizeRedirectResponse>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@ -510,6 +511,8 @@ pub struct CompleteAuthorizeData {
|
||||
pub browser_info: Option<BrowserInformation>,
|
||||
pub connector_transaction_id: Option<String>,
|
||||
pub connector_meta: Option<serde_json::Value>,
|
||||
pub complete_authorize_url: Option<String>,
|
||||
pub metadata: Option<pii::SecretSerdeValue>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
||||
Reference in New Issue
Block a user