mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-11-02 04:04:43 +08:00
feat(connector): [Deutschebank] Implement Card 3ds (#6844)
Co-authored-by: Debarshi Gupta <debarshi.gupta@Debarshi-Gupta-CM92YWDXFD.local> Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
@ -3426,6 +3426,10 @@ pub enum RedirectForm {
|
||||
access_token: String,
|
||||
step_up_url: String,
|
||||
},
|
||||
DeutschebankThreeDSChallengeFlow {
|
||||
acs_url: String,
|
||||
creq: String,
|
||||
},
|
||||
Payme,
|
||||
Braintree {
|
||||
client_token: String,
|
||||
|
||||
@ -59,7 +59,7 @@ use crate::{
|
||||
types::ResponseRouterData,
|
||||
utils::{
|
||||
self, PaymentsAuthorizeRequestData, PaymentsCompleteAuthorizeRequestData,
|
||||
RefundsRequestData,
|
||||
RefundsRequestData, RouterData as ConnectorRouterData,
|
||||
},
|
||||
};
|
||||
|
||||
@ -131,7 +131,7 @@ impl ConnectorCommon for Deutschebank {
|
||||
}
|
||||
|
||||
fn get_currency_unit(&self) -> api::CurrencyUnit {
|
||||
api::CurrencyUnit::Base
|
||||
api::CurrencyUnit::Minor
|
||||
}
|
||||
|
||||
fn common_get_content_type(&self) -> &'static str {
|
||||
@ -311,18 +311,30 @@ impl ConnectorIntegration<Authorize, PaymentsAuthorizeData, PaymentsResponseData
|
||||
req: &PaymentsAuthorizeRouterData,
|
||||
connectors: &Connectors,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
if req.request.connector_mandate_id().is_none() {
|
||||
let event_id = req.connector_request_reference_id.clone();
|
||||
let tx_action = if req.request.is_auto_capture()? {
|
||||
"authorization"
|
||||
} else {
|
||||
"preauthorization"
|
||||
};
|
||||
|
||||
if req.is_three_ds() && req.request.is_card() {
|
||||
Ok(format!(
|
||||
"{}/services/v2.1/headless3DSecure/event/{event_id}/{tx_action}/initialize",
|
||||
self.base_url(connectors)
|
||||
))
|
||||
} else if !req.is_three_ds() && req.request.is_card() {
|
||||
Err(errors::ConnectorError::NotSupported {
|
||||
message: "Non-ThreeDs".to_owned(),
|
||||
connector: "deutschebank",
|
||||
}
|
||||
.into())
|
||||
} else if req.request.connector_mandate_id().is_none() {
|
||||
Ok(format!(
|
||||
"{}/services/v2.1/managedmandate",
|
||||
self.base_url(connectors)
|
||||
))
|
||||
} else {
|
||||
let event_id = req.connector_request_reference_id.clone();
|
||||
let tx_action = if req.request.is_auto_capture()? {
|
||||
"authorization"
|
||||
} else {
|
||||
"preauthorization"
|
||||
};
|
||||
Ok(format!(
|
||||
"{}/services/v2.1/payment/event/{event_id}/directdebit/{tx_action}",
|
||||
self.base_url(connectors)
|
||||
@ -375,7 +387,19 @@ impl ConnectorIntegration<Authorize, PaymentsAuthorizeData, PaymentsResponseData
|
||||
event_builder: Option<&mut ConnectorEvent>,
|
||||
res: Response,
|
||||
) -> CustomResult<PaymentsAuthorizeRouterData, errors::ConnectorError> {
|
||||
if data.request.connector_mandate_id().is_none() {
|
||||
if data.is_three_ds() && data.request.is_card() {
|
||||
let response: deutschebank::DeutschebankThreeDSInitializeResponse = res
|
||||
.response
|
||||
.parse_struct("DeutschebankPaymentsAuthorizeResponse")
|
||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||
event_builder.map(|i| i.set_response_body(&response));
|
||||
router_env::logger::info!(connector_response=?response);
|
||||
RouterData::try_from(ResponseRouterData {
|
||||
response,
|
||||
data: data.clone(),
|
||||
http_code: res.status_code,
|
||||
})
|
||||
} else if data.request.connector_mandate_id().is_none() {
|
||||
let response: deutschebank::DeutschebankMandatePostResponse = res
|
||||
.response
|
||||
.parse_struct("DeutschebankMandatePostResponse")
|
||||
@ -437,10 +461,18 @@ impl ConnectorIntegration<CompleteAuthorize, CompleteAuthorizeData, PaymentsResp
|
||||
} else {
|
||||
"preauthorization"
|
||||
};
|
||||
Ok(format!(
|
||||
"{}/services/v2.1/payment/event/{event_id}/directdebit/{tx_action}",
|
||||
self.base_url(connectors)
|
||||
))
|
||||
|
||||
if req.is_three_ds() && matches!(req.payment_method, enums::PaymentMethod::Card) {
|
||||
Ok(format!(
|
||||
"{}/services/v2.1//headless3DSecure/event/{event_id}/final",
|
||||
self.base_url(connectors)
|
||||
))
|
||||
} else {
|
||||
Ok(format!(
|
||||
"{}/services/v2.1/payment/event/{event_id}/directdebit/{tx_action}",
|
||||
self.base_url(connectors)
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn get_request_body(
|
||||
@ -453,10 +485,9 @@ impl ConnectorIntegration<CompleteAuthorize, CompleteAuthorizeData, PaymentsResp
|
||||
req.request.minor_amount,
|
||||
req.request.currency,
|
||||
)?;
|
||||
|
||||
let connector_router_data = deutschebank::DeutschebankRouterData::from((amount, req));
|
||||
let connector_req =
|
||||
deutschebank::DeutschebankDirectDebitRequest::try_from(&connector_router_data)?;
|
||||
deutschebank::DeutschebankCompleteAuthorizeRequest::try_from(&connector_router_data)?;
|
||||
Ok(RequestContent::Json(Box::new(connector_req)))
|
||||
}
|
||||
|
||||
@ -952,10 +983,20 @@ lazy_static! {
|
||||
deutschebank_supported_payment_methods.add(
|
||||
enums::PaymentMethod::BankDebit,
|
||||
enums::PaymentMethodType::Sepa,
|
||||
PaymentMethodDetails{
|
||||
mandates: enums::FeatureStatus::Supported,
|
||||
refunds: enums::FeatureStatus::Supported,
|
||||
supported_capture_methods: supported_capture_methods.clone(),
|
||||
}
|
||||
);
|
||||
|
||||
deutschebank_supported_payment_methods.add(
|
||||
enums::PaymentMethod::Card,
|
||||
enums::PaymentMethodType::Credit,
|
||||
PaymentMethodDetails{
|
||||
mandates: enums::FeatureStatus::NotSupported,
|
||||
refunds: enums::FeatureStatus::NotSupported,
|
||||
supported_capture_methods,
|
||||
refunds: enums::FeatureStatus::Supported,
|
||||
supported_capture_methods: supported_capture_methods.clone(),
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use common_enums::enums;
|
||||
use cards::CardNumber;
|
||||
use common_enums::{enums, PaymentMethod};
|
||||
use common_utils::{ext_traits::ValueExt, pii::Email, types::MinorUnit};
|
||||
use error_stack::ResultExt;
|
||||
use hyperswitch_domain_models::{
|
||||
@ -22,14 +23,14 @@ use hyperswitch_domain_models::{
|
||||
PaymentsCompleteAuthorizeRouterData, RefundsRouterData,
|
||||
},
|
||||
};
|
||||
use hyperswitch_interfaces::errors;
|
||||
use masking::{PeekInterface, Secret};
|
||||
use hyperswitch_interfaces::{consts, errors};
|
||||
use masking::{ExposeInterface, PeekInterface, Secret};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
types::{PaymentsCancelResponseRouterData, RefundsResponseRouterData, ResponseRouterData},
|
||||
utils::{
|
||||
self, AddressDetailsData, PaymentsAuthorizeRequestData,
|
||||
self, AddressDetailsData, CardData, PaymentsAuthorizeRequestData,
|
||||
PaymentsCompleteAuthorizeRequestData, RefundsRequestData, RouterData as OtherRouterData,
|
||||
},
|
||||
};
|
||||
@ -129,6 +130,75 @@ pub struct DeutschebankMandatePostRequest {
|
||||
pub enum DeutschebankPaymentsRequest {
|
||||
MandatePost(DeutschebankMandatePostRequest),
|
||||
DirectDebit(DeutschebankDirectDebitRequest),
|
||||
CreditCard(Box<DeutschebankThreeDSInitializeRequest>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct DeutschebankThreeDSInitializeRequest {
|
||||
means_of_payment: DeutschebankThreeDSInitializeRequestMeansOfPayment,
|
||||
tds_20_data: DeutschebankThreeDSInitializeRequestTds20Data,
|
||||
amount_total: DeutschebankThreeDSInitializeRequestAmountTotal,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct DeutschebankThreeDSInitializeRequestMeansOfPayment {
|
||||
credit_card: DeutschebankThreeDSInitializeRequestCreditCard,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct DeutschebankThreeDSInitializeRequestCreditCard {
|
||||
number: CardNumber,
|
||||
expiry_date: DeutschebankThreeDSInitializeRequestCreditCardExpiry,
|
||||
code: Secret<String>,
|
||||
cardholder: Secret<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct DeutschebankThreeDSInitializeRequestCreditCardExpiry {
|
||||
year: Secret<String>,
|
||||
month: Secret<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct DeutschebankThreeDSInitializeRequestAmountTotal {
|
||||
amount: MinorUnit,
|
||||
currency: api_models::enums::Currency,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct DeutschebankThreeDSInitializeRequestTds20Data {
|
||||
communication_data: DeutschebankThreeDSInitializeRequestCommunicationData,
|
||||
customer_data: DeutschebankThreeDSInitializeRequestCustomerData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct DeutschebankThreeDSInitializeRequestCommunicationData {
|
||||
method_notification_url: String,
|
||||
cres_notification_url: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct DeutschebankThreeDSInitializeRequestCustomerData {
|
||||
billing_address: DeutschebankThreeDSInitializeRequestCustomerBillingData,
|
||||
cardholder_email: Email,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct DeutschebankThreeDSInitializeRequestCustomerBillingData {
|
||||
street: Secret<String>,
|
||||
postal_code: Secret<String>,
|
||||
city: String,
|
||||
state: Secret<String>,
|
||||
country: String,
|
||||
}
|
||||
|
||||
impl TryFrom<&DeutschebankRouterData<&PaymentsAuthorizeRouterData>>
|
||||
@ -148,11 +218,9 @@ impl TryFrom<&DeutschebankRouterData<&PaymentsAuthorizeRouterData>>
|
||||
None => {
|
||||
// To facilitate one-off payments via SEPA with Deutsche Bank, we are considering not storing the connector mandate ID in our system if future usage is on-session.
|
||||
// We will only check for customer acceptance to make a one-off payment. we will be storing the connector mandate details only when setup future usage is off-session.
|
||||
if item.router_data.request.customer_acceptance.is_some() {
|
||||
match item.router_data.request.payment_method_data.clone() {
|
||||
PaymentMethodData::BankDebit(BankDebitData::SepaBankDebit {
|
||||
iban, ..
|
||||
}) => {
|
||||
match item.router_data.request.payment_method_data.clone() {
|
||||
PaymentMethodData::BankDebit(BankDebitData::SepaBankDebit { iban, .. }) => {
|
||||
if item.router_data.request.customer_acceptance.is_some() {
|
||||
let billing_address = item.router_data.get_billing_address()?;
|
||||
Ok(Self::MandatePost(DeutschebankMandatePostRequest {
|
||||
approval_by: DeutschebankSEPAApproval::Click,
|
||||
@ -161,17 +229,60 @@ impl TryFrom<&DeutschebankRouterData<&PaymentsAuthorizeRouterData>>
|
||||
first_name: billing_address.get_first_name()?.clone(),
|
||||
last_name: billing_address.get_last_name()?.clone(),
|
||||
}))
|
||||
} else {
|
||||
Err(errors::ConnectorError::MissingRequiredField {
|
||||
field_name: "customer_acceptance",
|
||||
}
|
||||
.into())
|
||||
}
|
||||
_ => Err(errors::ConnectorError::NotImplemented(
|
||||
utils::get_unimplemented_payment_method_error_message("deutschebank"),
|
||||
)
|
||||
.into()),
|
||||
}
|
||||
} else {
|
||||
Err(errors::ConnectorError::MissingRequiredField {
|
||||
field_name: "customer_acceptance",
|
||||
PaymentMethodData::Card(ccard) => {
|
||||
if !item.router_data.clone().is_three_ds() {
|
||||
Err(errors::ConnectorError::NotSupported {
|
||||
message: "Non-ThreeDs".to_owned(),
|
||||
connector: "deutschebank",
|
||||
}
|
||||
.into())
|
||||
} else {
|
||||
let billing_address = item.router_data.get_billing_address()?;
|
||||
Ok(Self::CreditCard(Box::new(DeutschebankThreeDSInitializeRequest {
|
||||
means_of_payment: DeutschebankThreeDSInitializeRequestMeansOfPayment {
|
||||
credit_card: DeutschebankThreeDSInitializeRequestCreditCard {
|
||||
number: ccard.clone().card_number,
|
||||
expiry_date: DeutschebankThreeDSInitializeRequestCreditCardExpiry {
|
||||
year: ccard.get_expiry_year_4_digit(),
|
||||
month: ccard.card_exp_month,
|
||||
},
|
||||
code: ccard.card_cvc,
|
||||
cardholder: item.router_data.get_billing_full_name()?,
|
||||
}},
|
||||
amount_total: DeutschebankThreeDSInitializeRequestAmountTotal {
|
||||
amount: item.amount,
|
||||
currency: item.router_data.request.currency,
|
||||
},
|
||||
tds_20_data: DeutschebankThreeDSInitializeRequestTds20Data {
|
||||
communication_data: DeutschebankThreeDSInitializeRequestCommunicationData {
|
||||
method_notification_url: item.router_data.request.get_complete_authorize_url()?,
|
||||
cres_notification_url: item.router_data.request.get_complete_authorize_url()?,
|
||||
},
|
||||
customer_data: DeutschebankThreeDSInitializeRequestCustomerData {
|
||||
billing_address: DeutschebankThreeDSInitializeRequestCustomerBillingData {
|
||||
street: billing_address.get_line1()?.clone(),
|
||||
postal_code: billing_address.get_zip()?.clone(),
|
||||
city: billing_address.get_city()?.to_string(),
|
||||
state: billing_address.get_state()?.clone(),
|
||||
country: item.router_data.get_billing_country()?.to_string(),
|
||||
},
|
||||
cardholder_email: item.router_data.request.get_email()?,
|
||||
}
|
||||
}
|
||||
})))
|
||||
}
|
||||
}
|
||||
.into())
|
||||
_ => Err(errors::ConnectorError::NotImplemented(
|
||||
utils::get_unimplemented_payment_method_error_message("deutschebank"),
|
||||
)
|
||||
.into()),
|
||||
}
|
||||
}
|
||||
Some(api_models::payments::MandateReferenceId::ConnectorMandateId(mandate_data)) => {
|
||||
@ -209,6 +320,138 @@ impl TryFrom<&DeutschebankRouterData<&PaymentsAuthorizeRouterData>>
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct DeutschebankThreeDSInitializeResponse {
|
||||
outcome: DeutschebankThreeDSInitializeResponseOutcome,
|
||||
challenge_required: Option<DeutschebankThreeDSInitializeResponseChallengeRequired>,
|
||||
processed: Option<DeutschebankThreeDSInitializeResponseProcessed>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct DeutschebankThreeDSInitializeResponseProcessed {
|
||||
rc: String,
|
||||
message: String,
|
||||
tx_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||
pub enum DeutschebankThreeDSInitializeResponseOutcome {
|
||||
Processed,
|
||||
ChallengeRequired,
|
||||
MethodRequired,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct DeutschebankThreeDSInitializeResponseChallengeRequired {
|
||||
acs_url: String,
|
||||
creq: String,
|
||||
}
|
||||
|
||||
impl
|
||||
TryFrom<
|
||||
ResponseRouterData<
|
||||
Authorize,
|
||||
DeutschebankThreeDSInitializeResponse,
|
||||
PaymentsAuthorizeData,
|
||||
PaymentsResponseData,
|
||||
>,
|
||||
> for RouterData<Authorize, PaymentsAuthorizeData, PaymentsResponseData>
|
||||
{
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(
|
||||
item: ResponseRouterData<
|
||||
Authorize,
|
||||
DeutschebankThreeDSInitializeResponse,
|
||||
PaymentsAuthorizeData,
|
||||
PaymentsResponseData,
|
||||
>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
match item.response.outcome {
|
||||
DeutschebankThreeDSInitializeResponseOutcome::Processed => {
|
||||
match item.response.processed {
|
||||
Some(processed) => Ok(Self {
|
||||
status: if is_response_success(&processed.rc) {
|
||||
match item.data.request.is_auto_capture()? {
|
||||
true => common_enums::AttemptStatus::Charged,
|
||||
false => common_enums::AttemptStatus::Authorized,
|
||||
}
|
||||
} else {
|
||||
common_enums::AttemptStatus::AuthenticationFailed
|
||||
},
|
||||
response: Ok(PaymentsResponseData::TransactionResponse {
|
||||
resource_id: ResponseId::ConnectorTransactionId(
|
||||
processed.tx_id.clone(),
|
||||
),
|
||||
redirection_data: Box::new(None),
|
||||
mandate_reference: Box::new(None),
|
||||
connector_metadata: None,
|
||||
network_txn_id: None,
|
||||
connector_response_reference_id: Some(processed.tx_id.clone()),
|
||||
incremental_authorization_allowed: None,
|
||||
charge_id: None,
|
||||
}),
|
||||
..item.data
|
||||
}),
|
||||
None => {
|
||||
let response_string = format!("{:?}", item.response);
|
||||
Err(
|
||||
errors::ConnectorError::UnexpectedResponseError(bytes::Bytes::from(
|
||||
response_string,
|
||||
))
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
DeutschebankThreeDSInitializeResponseOutcome::ChallengeRequired => {
|
||||
match item.response.challenge_required {
|
||||
Some(challenge) => Ok(Self {
|
||||
status: common_enums::AttemptStatus::AuthenticationPending,
|
||||
response: Ok(PaymentsResponseData::TransactionResponse {
|
||||
resource_id: ResponseId::NoResponseId,
|
||||
redirection_data: Box::new(Some(
|
||||
RedirectForm::DeutschebankThreeDSChallengeFlow {
|
||||
acs_url: challenge.acs_url,
|
||||
creq: challenge.creq,
|
||||
},
|
||||
)),
|
||||
mandate_reference: Box::new(None),
|
||||
connector_metadata: None,
|
||||
network_txn_id: None,
|
||||
connector_response_reference_id: None,
|
||||
incremental_authorization_allowed: None,
|
||||
charge_id: None,
|
||||
}),
|
||||
..item.data
|
||||
}),
|
||||
None => {
|
||||
let response_string = format!("{:?}", item.response);
|
||||
Err(
|
||||
errors::ConnectorError::UnexpectedResponseError(bytes::Bytes::from(
|
||||
response_string,
|
||||
))
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
DeutschebankThreeDSInitializeResponseOutcome::MethodRequired => Ok(Self {
|
||||
status: common_enums::AttemptStatus::Failure,
|
||||
response: Err(ErrorResponse {
|
||||
code: consts::NO_ERROR_CODE.to_owned(),
|
||||
message: "METHOD_REQUIRED Flow not supported for deutschebank 3ds payments".to_owned(),
|
||||
reason: Some("METHOD_REQUIRED Flow is not currently supported for deutschebank 3ds payments".to_owned()),
|
||||
status_code: item.http_code,
|
||||
attempt_status: None,
|
||||
connector_transaction_id: None,
|
||||
}),
|
||||
..item.data
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||
pub enum DeutschebankSEPAMandateStatus {
|
||||
@ -450,79 +693,117 @@ pub struct DeutschebankDirectDebitRequest {
|
||||
mandate: DeutschebankMandate,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum DeutschebankCompleteAuthorizeRequest {
|
||||
DeutschebankDirectDebitRequest(DeutschebankDirectDebitRequest),
|
||||
DeutschebankThreeDSCompleteAuthorizeRequest(DeutschebankThreeDSCompleteAuthorizeRequest),
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, PartialEq)]
|
||||
pub struct DeutschebankThreeDSCompleteAuthorizeRequest {
|
||||
cres: String,
|
||||
}
|
||||
|
||||
impl TryFrom<&DeutschebankRouterData<&PaymentsCompleteAuthorizeRouterData>>
|
||||
for DeutschebankDirectDebitRequest
|
||||
for DeutschebankCompleteAuthorizeRequest
|
||||
{
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(
|
||||
item: &DeutschebankRouterData<&PaymentsCompleteAuthorizeRouterData>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
let account_holder = item.router_data.get_billing_address()?.get_full_name()?;
|
||||
let redirect_response = item.router_data.request.redirect_response.clone().ok_or(
|
||||
errors::ConnectorError::MissingRequiredField {
|
||||
field_name: "redirect_response",
|
||||
},
|
||||
)?;
|
||||
let queries_params = redirect_response
|
||||
.params
|
||||
.map(|param| {
|
||||
let mut queries = HashMap::<String, String>::new();
|
||||
let values = param.peek().split('&').collect::<Vec<&str>>();
|
||||
for value in values {
|
||||
let pair = value.split('=').collect::<Vec<&str>>();
|
||||
queries.insert(
|
||||
pair.first()
|
||||
.ok_or(errors::ConnectorError::ResponseDeserializationFailed)?
|
||||
.to_string(),
|
||||
pair.get(1)
|
||||
.ok_or(errors::ConnectorError::ResponseDeserializationFailed)?
|
||||
.to_string(),
|
||||
);
|
||||
}
|
||||
Ok::<_, errors::ConnectorError>(queries)
|
||||
})
|
||||
.transpose()?
|
||||
.ok_or(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||
let reference = Secret::from(
|
||||
queries_params
|
||||
.get("reference")
|
||||
.ok_or(errors::ConnectorError::MissingRequiredField {
|
||||
field_name: "reference",
|
||||
})?
|
||||
.to_owned(),
|
||||
);
|
||||
let signed_on = queries_params
|
||||
.get("signed_on")
|
||||
.ok_or(errors::ConnectorError::MissingRequiredField {
|
||||
field_name: "signed_on",
|
||||
})?
|
||||
.to_owned();
|
||||
if matches!(item.router_data.payment_method, PaymentMethod::Card) {
|
||||
let redirect_response_payload = item
|
||||
.router_data
|
||||
.request
|
||||
.get_redirect_response_payload()?
|
||||
.expose();
|
||||
|
||||
match item.router_data.request.payment_method_data.clone() {
|
||||
Some(PaymentMethodData::BankDebit(BankDebitData::SepaBankDebit { iban, .. })) => {
|
||||
Ok(Self {
|
||||
amount_total: DeutschebankAmount {
|
||||
amount: item.amount,
|
||||
currency: item.router_data.request.currency,
|
||||
},
|
||||
means_of_payment: DeutschebankMeansOfPayment {
|
||||
bank_account: DeutschebankBankAccount {
|
||||
account_holder,
|
||||
iban: Secret::from(iban.peek().replace(" ", "")),
|
||||
let cres = redirect_response_payload
|
||||
.get("cres")
|
||||
.and_then(|v| v.as_str())
|
||||
.map(String::from)
|
||||
.ok_or(errors::ConnectorError::MissingRequiredField { field_name: "cres" })?;
|
||||
|
||||
Ok(Self::DeutschebankThreeDSCompleteAuthorizeRequest(
|
||||
DeutschebankThreeDSCompleteAuthorizeRequest { cres },
|
||||
))
|
||||
} else {
|
||||
match item.router_data.request.payment_method_data.clone() {
|
||||
Some(PaymentMethodData::BankDebit(BankDebitData::SepaBankDebit {
|
||||
iban, ..
|
||||
})) => {
|
||||
let account_holder = item.router_data.get_billing_address()?.get_full_name()?;
|
||||
let redirect_response =
|
||||
item.router_data.request.redirect_response.clone().ok_or(
|
||||
errors::ConnectorError::MissingRequiredField {
|
||||
field_name: "redirect_response",
|
||||
},
|
||||
)?;
|
||||
let queries_params = redirect_response
|
||||
.params
|
||||
.map(|param| {
|
||||
let mut queries = HashMap::<String, String>::new();
|
||||
let values = param.peek().split('&').collect::<Vec<&str>>();
|
||||
for value in values {
|
||||
let pair = value.split('=').collect::<Vec<&str>>();
|
||||
queries.insert(
|
||||
pair.first()
|
||||
.ok_or(
|
||||
errors::ConnectorError::ResponseDeserializationFailed,
|
||||
)?
|
||||
.to_string(),
|
||||
pair.get(1)
|
||||
.ok_or(
|
||||
errors::ConnectorError::ResponseDeserializationFailed,
|
||||
)?
|
||||
.to_string(),
|
||||
);
|
||||
}
|
||||
Ok::<_, errors::ConnectorError>(queries)
|
||||
})
|
||||
.transpose()?
|
||||
.ok_or(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||
let reference = Secret::from(
|
||||
queries_params
|
||||
.get("reference")
|
||||
.ok_or(errors::ConnectorError::MissingRequiredField {
|
||||
field_name: "reference",
|
||||
})?
|
||||
.to_owned(),
|
||||
);
|
||||
let signed_on = queries_params
|
||||
.get("signed_on")
|
||||
.ok_or(errors::ConnectorError::MissingRequiredField {
|
||||
field_name: "signed_on",
|
||||
})?
|
||||
.to_owned();
|
||||
Ok(Self::DeutschebankDirectDebitRequest(
|
||||
DeutschebankDirectDebitRequest {
|
||||
amount_total: DeutschebankAmount {
|
||||
amount: item.amount,
|
||||
currency: item.router_data.request.currency,
|
||||
},
|
||||
means_of_payment: DeutschebankMeansOfPayment {
|
||||
bank_account: DeutschebankBankAccount {
|
||||
account_holder,
|
||||
iban: Secret::from(iban.peek().replace(" ", "")),
|
||||
},
|
||||
},
|
||||
mandate: {
|
||||
DeutschebankMandate {
|
||||
reference,
|
||||
signed_on,
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
mandate: {
|
||||
DeutschebankMandate {
|
||||
reference,
|
||||
signed_on,
|
||||
}
|
||||
},
|
||||
})
|
||||
))
|
||||
}
|
||||
_ => Err(errors::ConnectorError::NotImplemented(
|
||||
utils::get_unimplemented_payment_method_error_message("deutschebank"),
|
||||
)
|
||||
.into()),
|
||||
}
|
||||
_ => Err(errors::ConnectorError::NotImplemented(
|
||||
utils::get_unimplemented_payment_method_error_message("deutschebank"),
|
||||
)
|
||||
.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -636,6 +917,8 @@ impl
|
||||
#[serde(rename_all = "UPPERCASE")]
|
||||
pub enum DeutschebankTransactionKind {
|
||||
Directdebit,
|
||||
#[serde(rename = "CREDITCARD_3DS20")]
|
||||
Creditcard3ds20,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, PartialEq)]
|
||||
@ -649,10 +932,24 @@ impl TryFrom<&DeutschebankRouterData<&PaymentsCaptureRouterData>> for Deutscheba
|
||||
fn try_from(
|
||||
item: &DeutschebankRouterData<&PaymentsCaptureRouterData>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
changed_amount: item.amount,
|
||||
kind: DeutschebankTransactionKind::Directdebit,
|
||||
})
|
||||
if matches!(item.router_data.payment_method, PaymentMethod::BankDebit) {
|
||||
Ok(Self {
|
||||
changed_amount: item.amount,
|
||||
kind: DeutschebankTransactionKind::Directdebit,
|
||||
})
|
||||
} else if item.router_data.is_three_ds()
|
||||
&& matches!(item.router_data.payment_method, PaymentMethod::Card)
|
||||
{
|
||||
Ok(Self {
|
||||
changed_amount: item.amount,
|
||||
kind: DeutschebankTransactionKind::Creditcard3ds20,
|
||||
})
|
||||
} else {
|
||||
Err(errors::ConnectorError::NotImplemented(
|
||||
utils::get_unimplemented_payment_method_error_message("deutschebank"),
|
||||
)
|
||||
.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -772,10 +1069,21 @@ pub struct DeutschebankReversalRequest {
|
||||
|
||||
impl TryFrom<&PaymentsCancelRouterData> for DeutschebankReversalRequest {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(_item: &PaymentsCancelRouterData) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
kind: DeutschebankTransactionKind::Directdebit,
|
||||
})
|
||||
fn try_from(item: &PaymentsCancelRouterData) -> Result<Self, Self::Error> {
|
||||
if matches!(item.payment_method, PaymentMethod::BankDebit) {
|
||||
Ok(Self {
|
||||
kind: DeutschebankTransactionKind::Directdebit,
|
||||
})
|
||||
} else if item.is_three_ds() && matches!(item.payment_method, PaymentMethod::Card) {
|
||||
Ok(Self {
|
||||
kind: DeutschebankTransactionKind::Creditcard3ds20,
|
||||
})
|
||||
} else {
|
||||
Err(errors::ConnectorError::NotImplemented(
|
||||
utils::get_unimplemented_payment_method_error_message("deutschebank"),
|
||||
)
|
||||
.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -815,10 +1123,24 @@ pub struct DeutschebankRefundRequest {
|
||||
impl<F> TryFrom<&DeutschebankRouterData<&RefundsRouterData<F>>> for DeutschebankRefundRequest {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(item: &DeutschebankRouterData<&RefundsRouterData<F>>) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
changed_amount: item.amount.to_owned(),
|
||||
kind: DeutschebankTransactionKind::Directdebit,
|
||||
})
|
||||
if matches!(item.router_data.payment_method, PaymentMethod::BankDebit) {
|
||||
Ok(Self {
|
||||
changed_amount: item.amount,
|
||||
kind: DeutschebankTransactionKind::Directdebit,
|
||||
})
|
||||
} else if item.router_data.is_three_ds()
|
||||
&& matches!(item.router_data.payment_method, PaymentMethod::Card)
|
||||
{
|
||||
Ok(Self {
|
||||
changed_amount: item.amount,
|
||||
kind: DeutschebankTransactionKind::Creditcard3ds20,
|
||||
})
|
||||
} else {
|
||||
Err(errors::ConnectorError::NotImplemented(
|
||||
utils::get_unimplemented_payment_method_error_message("deutschebank"),
|
||||
)
|
||||
.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -236,6 +236,10 @@ pub enum RedirectForm {
|
||||
access_token: String,
|
||||
step_up_url: String,
|
||||
},
|
||||
DeutschebankThreeDSChallengeFlow {
|
||||
acs_url: String,
|
||||
creq: String,
|
||||
},
|
||||
Payme,
|
||||
Braintree {
|
||||
client_token: String,
|
||||
@ -313,6 +317,9 @@ impl From<RedirectForm> for diesel_models::payment_attempt::RedirectForm {
|
||||
access_token,
|
||||
step_up_url,
|
||||
},
|
||||
RedirectForm::DeutschebankThreeDSChallengeFlow { acs_url, creq } => {
|
||||
Self::DeutschebankThreeDSChallengeFlow { acs_url, creq }
|
||||
}
|
||||
RedirectForm::Payme => Self::Payme,
|
||||
RedirectForm::Braintree {
|
||||
client_token,
|
||||
@ -392,6 +399,9 @@ impl From<diesel_models::payment_attempt::RedirectForm> for RedirectForm {
|
||||
access_token,
|
||||
step_up_url,
|
||||
},
|
||||
diesel_models::RedirectForm::DeutschebankThreeDSChallengeFlow { acs_url, creq } => {
|
||||
Self::DeutschebankThreeDSChallengeFlow { acs_url, creq }
|
||||
}
|
||||
diesel_models::payment_attempt::RedirectForm::Payme => Self::Payme,
|
||||
diesel_models::payment_attempt::RedirectForm::Braintree {
|
||||
client_token,
|
||||
|
||||
@ -942,6 +942,129 @@ impl Default for settings::RequiredFields {
|
||||
),
|
||||
}
|
||||
),
|
||||
(
|
||||
enums::Connector::Deutschebank,
|
||||
RequiredFieldFinal {
|
||||
mandate: HashMap::new(),
|
||||
non_mandate : HashMap::from(
|
||||
[
|
||||
(
|
||||
"payment_method_data.card.card_number".to_string(),
|
||||
RequiredFieldInfo {
|
||||
required_field: "payment_method_data.card.card_number".to_string(),
|
||||
display_name: "card_number".to_string(),
|
||||
field_type: enums::FieldType::UserCardNumber,
|
||||
value: None,
|
||||
}
|
||||
),
|
||||
(
|
||||
"payment_method_data.card.card_exp_month".to_string(),
|
||||
RequiredFieldInfo {
|
||||
required_field: "payment_method_data.card.card_exp_month".to_string(),
|
||||
display_name: "card_exp_month".to_string(),
|
||||
field_type: enums::FieldType::UserCardExpiryMonth,
|
||||
value: None,
|
||||
}
|
||||
),
|
||||
(
|
||||
"payment_method_data.card.card_exp_year".to_string(),
|
||||
RequiredFieldInfo {
|
||||
required_field: "payment_method_data.card.card_exp_year".to_string(),
|
||||
display_name: "card_exp_year".to_string(),
|
||||
field_type: enums::FieldType::UserCardExpiryYear,
|
||||
value: None,
|
||||
}
|
||||
),
|
||||
(
|
||||
"payment_method_data.card.card_cvc".to_string(),
|
||||
RequiredFieldInfo {
|
||||
required_field: "payment_method_data.card.card_cvc".to_string(),
|
||||
display_name: "card_cvc".to_string(),
|
||||
field_type: enums::FieldType::UserCardCvc,
|
||||
value: None,
|
||||
}
|
||||
),
|
||||
(
|
||||
"email".to_string(),
|
||||
RequiredFieldInfo {
|
||||
required_field: "email".to_string(),
|
||||
display_name: "email".to_string(),
|
||||
field_type: enums::FieldType::UserEmailAddress,
|
||||
value: None,
|
||||
}
|
||||
),
|
||||
(
|
||||
"billing.address.line1".to_string(),
|
||||
RequiredFieldInfo {
|
||||
required_field: "payment_method_data.billing.address.line1".to_string(),
|
||||
display_name: "line1".to_string(),
|
||||
field_type: enums::FieldType::UserAddressLine1,
|
||||
value: None,
|
||||
}
|
||||
),
|
||||
(
|
||||
"billing.address.city".to_string(),
|
||||
RequiredFieldInfo {
|
||||
required_field: "payment_method_data.billing.address.city".to_string(),
|
||||
display_name: "city".to_string(),
|
||||
field_type: enums::FieldType::UserAddressCity,
|
||||
value: None,
|
||||
}
|
||||
),
|
||||
(
|
||||
"billing.address.state".to_string(),
|
||||
RequiredFieldInfo {
|
||||
required_field: "payment_method_data.billing.address.state".to_string(),
|
||||
display_name: "state".to_string(),
|
||||
field_type: enums::FieldType::UserAddressState,
|
||||
value: None,
|
||||
}
|
||||
),
|
||||
(
|
||||
"billing.address.zip".to_string(),
|
||||
RequiredFieldInfo {
|
||||
required_field: "payment_method_data.billing.address.zip".to_string(),
|
||||
display_name: "zip".to_string(),
|
||||
field_type: enums::FieldType::UserAddressPincode,
|
||||
value: None,
|
||||
}
|
||||
),
|
||||
(
|
||||
"billing.address.country".to_string(),
|
||||
RequiredFieldInfo {
|
||||
required_field: "payment_method_data.billing.address.country".to_string(),
|
||||
display_name: "country".to_string(),
|
||||
field_type: enums::FieldType::UserAddressCountry{
|
||||
options: vec![
|
||||
"ALL".to_string(),
|
||||
]
|
||||
},
|
||||
value: None,
|
||||
}
|
||||
),
|
||||
(
|
||||
"billing.address.first_name".to_string(),
|
||||
RequiredFieldInfo {
|
||||
required_field: "payment_method_data.billing.address.first_name".to_string(),
|
||||
display_name: "first_name".to_string(),
|
||||
field_type: enums::FieldType::UserFullName,
|
||||
value: None,
|
||||
}
|
||||
),
|
||||
(
|
||||
"billing.address.last_name".to_string(),
|
||||
RequiredFieldInfo {
|
||||
required_field: "payment_method_data.billing.address.last_name".to_string(),
|
||||
display_name: "last_name".to_string(),
|
||||
field_type: enums::FieldType::UserFullName,
|
||||
value: None,
|
||||
}
|
||||
)
|
||||
]
|
||||
),
|
||||
common: HashMap::new(),
|
||||
}
|
||||
),
|
||||
(
|
||||
enums::Connector::Dlocal,
|
||||
RequiredFieldFinal {
|
||||
@ -4139,6 +4262,129 @@ impl Default for settings::RequiredFields {
|
||||
),
|
||||
}
|
||||
),
|
||||
(
|
||||
enums::Connector::Deutschebank,
|
||||
RequiredFieldFinal {
|
||||
mandate: HashMap::new(),
|
||||
non_mandate : HashMap::from(
|
||||
[
|
||||
(
|
||||
"payment_method_data.card.card_number".to_string(),
|
||||
RequiredFieldInfo {
|
||||
required_field: "payment_method_data.card.card_number".to_string(),
|
||||
display_name: "card_number".to_string(),
|
||||
field_type: enums::FieldType::UserCardNumber,
|
||||
value: None,
|
||||
}
|
||||
),
|
||||
(
|
||||
"payment_method_data.card.card_exp_month".to_string(),
|
||||
RequiredFieldInfo {
|
||||
required_field: "payment_method_data.card.card_exp_month".to_string(),
|
||||
display_name: "card_exp_month".to_string(),
|
||||
field_type: enums::FieldType::UserCardExpiryMonth,
|
||||
value: None,
|
||||
}
|
||||
),
|
||||
(
|
||||
"payment_method_data.card.card_exp_year".to_string(),
|
||||
RequiredFieldInfo {
|
||||
required_field: "payment_method_data.card.card_exp_year".to_string(),
|
||||
display_name: "card_exp_year".to_string(),
|
||||
field_type: enums::FieldType::UserCardExpiryYear,
|
||||
value: None,
|
||||
}
|
||||
),
|
||||
(
|
||||
"payment_method_data.card.card_cvc".to_string(),
|
||||
RequiredFieldInfo {
|
||||
required_field: "payment_method_data.card.card_cvc".to_string(),
|
||||
display_name: "card_cvc".to_string(),
|
||||
field_type: enums::FieldType::UserCardCvc,
|
||||
value: None,
|
||||
}
|
||||
),
|
||||
(
|
||||
"email".to_string(),
|
||||
RequiredFieldInfo {
|
||||
required_field: "email".to_string(),
|
||||
display_name: "email".to_string(),
|
||||
field_type: enums::FieldType::UserEmailAddress,
|
||||
value: None,
|
||||
}
|
||||
),
|
||||
(
|
||||
"billing.address.line1".to_string(),
|
||||
RequiredFieldInfo {
|
||||
required_field: "payment_method_data.billing.address.line1".to_string(),
|
||||
display_name: "line1".to_string(),
|
||||
field_type: enums::FieldType::UserAddressLine1,
|
||||
value: None,
|
||||
}
|
||||
),
|
||||
(
|
||||
"billing.address.city".to_string(),
|
||||
RequiredFieldInfo {
|
||||
required_field: "payment_method_data.billing.address.city".to_string(),
|
||||
display_name: "city".to_string(),
|
||||
field_type: enums::FieldType::UserAddressCity,
|
||||
value: None,
|
||||
}
|
||||
),
|
||||
(
|
||||
"billing.address.state".to_string(),
|
||||
RequiredFieldInfo {
|
||||
required_field: "payment_method_data.billing.address.state".to_string(),
|
||||
display_name: "state".to_string(),
|
||||
field_type: enums::FieldType::UserAddressState,
|
||||
value: None,
|
||||
}
|
||||
),
|
||||
(
|
||||
"billing.address.zip".to_string(),
|
||||
RequiredFieldInfo {
|
||||
required_field: "payment_method_data.billing.address.zip".to_string(),
|
||||
display_name: "zip".to_string(),
|
||||
field_type: enums::FieldType::UserAddressPincode,
|
||||
value: None,
|
||||
}
|
||||
),
|
||||
(
|
||||
"billing.address.country".to_string(),
|
||||
RequiredFieldInfo {
|
||||
required_field: "payment_method_data.billing.address.country".to_string(),
|
||||
display_name: "country".to_string(),
|
||||
field_type: enums::FieldType::UserAddressCountry{
|
||||
options: vec![
|
||||
"ALL".to_string(),
|
||||
]
|
||||
},
|
||||
value: None,
|
||||
}
|
||||
),
|
||||
(
|
||||
"billing.address.first_name".to_string(),
|
||||
RequiredFieldInfo {
|
||||
required_field: "payment_method_data.billing.address.first_name".to_string(),
|
||||
display_name: "first_name".to_string(),
|
||||
field_type: enums::FieldType::UserFullName,
|
||||
value: None,
|
||||
}
|
||||
),
|
||||
(
|
||||
"billing.address.last_name".to_string(),
|
||||
RequiredFieldInfo {
|
||||
required_field: "payment_method_data.billing.address.last_name".to_string(),
|
||||
display_name: "last_name".to_string(),
|
||||
field_type: enums::FieldType::UserFullName,
|
||||
value: None,
|
||||
}
|
||||
)
|
||||
]
|
||||
),
|
||||
common: HashMap::new(),
|
||||
}
|
||||
),
|
||||
(
|
||||
enums::Connector::Dlocal,
|
||||
RequiredFieldFinal {
|
||||
|
||||
@ -1535,6 +1535,46 @@ pub fn build_redirection_form(
|
||||
</script>")))
|
||||
}}
|
||||
}
|
||||
RedirectForm::DeutschebankThreeDSChallengeFlow { acs_url, creq } => {
|
||||
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(format!("<form id=\"PaReqForm\" method=\"POST\" action=\"{acs_url}\">
|
||||
<input type=\"hidden\" name=\"creq\" value=\"{creq}\">
|
||||
</form>")))
|
||||
(PreEscaped(format!("<script>
|
||||
{logging_template}
|
||||
window.onload = function() {{
|
||||
var paReqForm = document.querySelector('#PaReqForm'); if(paReqForm) paReqForm.submit();
|
||||
}}
|
||||
</script>")))
|
||||
}
|
||||
}
|
||||
}
|
||||
RedirectForm::Payme => {
|
||||
maud::html! {
|
||||
(maud::DOCTYPE)
|
||||
|
||||
214
cypress-tests/cypress/e2e/PaymentUtils/Deutschebank.js
Normal file
214
cypress-tests/cypress/e2e/PaymentUtils/Deutschebank.js
Normal file
@ -0,0 +1,214 @@
|
||||
const successful3DSCardDetails = {
|
||||
card_number: "4761739090000088",
|
||||
card_exp_month: "12",
|
||||
card_exp_year: "2034",
|
||||
card_holder_name: "John Doe",
|
||||
card_cvc: "123",
|
||||
};
|
||||
|
||||
export const connectorDetails = {
|
||||
card_pm: {
|
||||
PaymentIntent: {
|
||||
Request: {
|
||||
currency: "USD",
|
||||
customer_acceptance: null,
|
||||
},
|
||||
Response: {
|
||||
status: 200,
|
||||
body: {
|
||||
status: "requires_payment_method",
|
||||
},
|
||||
},
|
||||
},
|
||||
"3DSManualCapture": {
|
||||
Request: {
|
||||
payment_method: "card",
|
||||
payment_method_data: {
|
||||
card: successful3DSCardDetails,
|
||||
},
|
||||
currency: "USD",
|
||||
customer_acceptance: null,
|
||||
setup_future_usage: "on_session",
|
||||
},
|
||||
Response: {
|
||||
status: 200,
|
||||
body: {
|
||||
status: "requires_customer_action",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
"3DSAutoCapture": {
|
||||
Request: {
|
||||
payment_method: "card",
|
||||
payment_method_data: {
|
||||
card: successful3DSCardDetails,
|
||||
},
|
||||
currency: "USD",
|
||||
customer_acceptance: null,
|
||||
setup_future_usage: "on_session",
|
||||
},
|
||||
Response: {
|
||||
status: 200,
|
||||
body: {
|
||||
status: "requires_customer_action",
|
||||
},
|
||||
},
|
||||
},
|
||||
No3DSManualCapture: {
|
||||
Request: {
|
||||
currency: "USD",
|
||||
payment_method: "card",
|
||||
payment_method_data: {
|
||||
card: successful3DSCardDetails,
|
||||
},
|
||||
customer_acceptance: null,
|
||||
setup_future_usage: "on_session",
|
||||
},
|
||||
Response: {
|
||||
status: 400,
|
||||
body: {
|
||||
error: {
|
||||
type: "invalid_request",
|
||||
message: "Payment method type not supported",
|
||||
code: "IR_19",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
No3DSAutoCapture: {
|
||||
Request: {
|
||||
currency: "USD",
|
||||
payment_method: "card",
|
||||
payment_method_data: {
|
||||
card: successful3DSCardDetails,
|
||||
},
|
||||
customer_acceptance: null,
|
||||
setup_future_usage: "on_session",
|
||||
},
|
||||
Response: {
|
||||
status: 400,
|
||||
body: {
|
||||
error: {
|
||||
type: "invalid_request",
|
||||
message: "Payment method type not supported",
|
||||
code: "IR_19",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Capture: {
|
||||
Request: {
|
||||
payment_method: "card",
|
||||
payment_method_data: {
|
||||
card: successful3DSCardDetails,
|
||||
},
|
||||
customer_acceptance: null,
|
||||
},
|
||||
Response: {
|
||||
status: 200,
|
||||
body: {
|
||||
status: "succeeded",
|
||||
amount: 6500,
|
||||
amount_capturable: 0,
|
||||
amount_received: 6500,
|
||||
},
|
||||
},
|
||||
},
|
||||
PartialCapture: {
|
||||
Request: {},
|
||||
Response: {
|
||||
status: 200,
|
||||
body: {
|
||||
status: "partially_captured",
|
||||
amount: 6500,
|
||||
amount_capturable: 0,
|
||||
amount_received: 100,
|
||||
},
|
||||
},
|
||||
},
|
||||
Refund: {
|
||||
Configs: {
|
||||
TRIGGER_SKIP: true,
|
||||
},
|
||||
Request: {
|
||||
payment_method: "card",
|
||||
payment_method_data: {
|
||||
card: successful3DSCardDetails,
|
||||
},
|
||||
customer_acceptance: null,
|
||||
},
|
||||
Response: {
|
||||
status: 200,
|
||||
body: {
|
||||
status: "succeeded",
|
||||
},
|
||||
},
|
||||
},
|
||||
manualPaymentRefund: {
|
||||
Configs: {
|
||||
TRIGGER_SKIP: true,
|
||||
},
|
||||
Request: {
|
||||
payment_method: "card",
|
||||
payment_method_data: {
|
||||
card: successful3DSCardDetails,
|
||||
},
|
||||
customer_acceptance: null,
|
||||
},
|
||||
Response: {
|
||||
status: 200,
|
||||
body: {
|
||||
status: "succeeded",
|
||||
},
|
||||
},
|
||||
},
|
||||
manualPaymentPartialRefund: {
|
||||
Configs: {
|
||||
TRIGGER_SKIP: true,
|
||||
},
|
||||
Request: {
|
||||
payment_method: "card",
|
||||
payment_method_data: {
|
||||
card: successful3DSCardDetails,
|
||||
},
|
||||
customer_acceptance: null,
|
||||
},
|
||||
Response: {
|
||||
status: 200,
|
||||
body: {
|
||||
status: "succeeded",
|
||||
},
|
||||
},
|
||||
},
|
||||
PartialRefund: {
|
||||
Configs: {
|
||||
TRIGGER_SKIP: true,
|
||||
},
|
||||
Request: {
|
||||
payment_method: "card",
|
||||
payment_method_data: {
|
||||
card: successful3DSCardDetails,
|
||||
},
|
||||
customer_acceptance: null,
|
||||
},
|
||||
Response: {
|
||||
status: 200,
|
||||
body: {
|
||||
status: "succeeded",
|
||||
},
|
||||
},
|
||||
},
|
||||
SyncRefund: {
|
||||
Configs: {
|
||||
TRIGGER_SKIP: true,
|
||||
},
|
||||
Response: {
|
||||
status: 200,
|
||||
body: {
|
||||
status: "succeeded",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@ -26,6 +26,7 @@ import { connectorDetails as stripeConnectorDetails } from "./Stripe.js";
|
||||
import { connectorDetails as trustpayConnectorDetails } from "./Trustpay.js";
|
||||
import { connectorDetails as wellsfargoConnectorDetails } from "./WellsFargo.js";
|
||||
import { connectorDetails as worldpayConnectorDetails } from "./WorldPay.js";
|
||||
import { connectorDetails as deutschebankConnectorDetails } from "./Deutschebank.js";
|
||||
|
||||
const connectorDetails = {
|
||||
adyen: adyenConnectorDetails,
|
||||
@ -34,6 +35,7 @@ const connectorDetails = {
|
||||
checkout: checkoutConnectorDetails,
|
||||
commons: CommonConnectorDetails,
|
||||
cybersource: cybersourceConnectorDetails,
|
||||
deutschebank: deutschebankConnectorDetails,
|
||||
fiservemea: fiservemeaConnectorDetails,
|
||||
iatapay: iatapayConnectorDetails,
|
||||
itaubank: itaubankConnectorDetails,
|
||||
|
||||
Reference in New Issue
Block a user