mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-11-04 05:59:48 +08:00
refactor(connector): [Bluesnap] Enahnce 3ds Flow (#2115)
This commit is contained in:
@ -398,7 +398,7 @@ ach = { currency = "USD" }
|
|||||||
cashapp = {country = "US", currency = "USD"}
|
cashapp = {country = "US", currency = "USD"}
|
||||||
|
|
||||||
[connector_customer]
|
[connector_customer]
|
||||||
connector_list = "stax"
|
connector_list = "stax,stripe"
|
||||||
payout_connector_list = "wise"
|
payout_connector_list = "wise"
|
||||||
|
|
||||||
[bank_config.online_banking_fpx]
|
[bank_config.online_banking_fpx]
|
||||||
|
|||||||
@ -378,7 +378,7 @@ trustpay = {payment_method = "card,bank_redirect,wallet"}
|
|||||||
stripe = {payment_method = "card,bank_redirect,pay_later,wallet,bank_debit"}
|
stripe = {payment_method = "card,bank_redirect,pay_later,wallet,bank_debit"}
|
||||||
|
|
||||||
[connector_customer]
|
[connector_customer]
|
||||||
connector_list = "bluesnap,stax,stripe"
|
connector_list = "stax,stripe"
|
||||||
payout_connector_list = "wise"
|
payout_connector_list = "wise"
|
||||||
|
|
||||||
[dummy_connector]
|
[dummy_connector]
|
||||||
|
|||||||
@ -292,7 +292,7 @@ card.credit = {connector_list = "stripe,adyen,authorizedotnet,globalpay,worldpay
|
|||||||
card.debit = {connector_list = "stripe,adyen,authorizedotnet,globalpay,worldpay,multisafepay,nmi,nexinets,noon"}
|
card.debit = {connector_list = "stripe,adyen,authorizedotnet,globalpay,worldpay,multisafepay,nmi,nexinets,noon"}
|
||||||
|
|
||||||
[connector_customer]
|
[connector_customer]
|
||||||
connector_list = "stax"
|
connector_list = "stax,stripe"
|
||||||
payout_connector_list = "wise"
|
payout_connector_list = "wise"
|
||||||
|
|
||||||
[multiple_api_version_supported_connectors]
|
[multiple_api_version_supported_connectors]
|
||||||
|
|||||||
@ -157,6 +157,15 @@ impl ConnectorValidation for Bluesnap {
|
|||||||
&self,
|
&self,
|
||||||
data: &types::PaymentsSyncRouterData,
|
data: &types::PaymentsSyncRouterData,
|
||||||
) -> CustomResult<(), errors::ConnectorError> {
|
) -> CustomResult<(), errors::ConnectorError> {
|
||||||
|
// If 3DS payment was triggered, connector will have context about payment in CompleteAuthorizeFlow and thus can't make force_sync
|
||||||
|
if data.is_three_ds() && data.status == enums::AttemptStatus::AuthenticationPending {
|
||||||
|
return Err(
|
||||||
|
errors::ConnectorError::MissingConnectorRelatedTransactionID {
|
||||||
|
id: "connector_transaction_id".to_string(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.into_report();
|
||||||
|
}
|
||||||
// if connector_transaction_id is present, psync can be made
|
// if connector_transaction_id is present, psync can be made
|
||||||
if data
|
if data
|
||||||
.request
|
.request
|
||||||
@ -194,100 +203,6 @@ impl ConnectorIntegration<api::Verify, types::VerifyRequestData, types::Payments
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
impl api::ConnectorCustomer for Bluesnap {}
|
|
||||||
|
|
||||||
impl
|
|
||||||
ConnectorIntegration<
|
|
||||||
api::CreateConnectorCustomer,
|
|
||||||
types::ConnectorCustomerData,
|
|
||||||
types::PaymentsResponseData,
|
|
||||||
> for Bluesnap
|
|
||||||
{
|
|
||||||
fn get_headers(
|
|
||||||
&self,
|
|
||||||
req: &types::ConnectorCustomerRouterData,
|
|
||||||
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::ConnectorCustomerRouterData,
|
|
||||||
connectors: &settings::Connectors,
|
|
||||||
) -> CustomResult<String, errors::ConnectorError> {
|
|
||||||
Ok(format!(
|
|
||||||
"{}services/2/vaulted-shoppers",
|
|
||||||
self.base_url(connectors),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_request_body(
|
|
||||||
&self,
|
|
||||||
req: &types::ConnectorCustomerRouterData,
|
|
||||||
) -> CustomResult<Option<types::RequestBody>, errors::ConnectorError> {
|
|
||||||
let connector_request = bluesnap::BluesnapCustomerRequest::try_from(req)?;
|
|
||||||
let bluesnap_req = types::RequestBody::log_and_get_request_body(
|
|
||||||
&connector_request,
|
|
||||||
utils::Encode::<bluesnap::BluesnapCustomerRequest>::encode_to_string_of_json,
|
|
||||||
)
|
|
||||||
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
|
|
||||||
Ok(Some(bluesnap_req))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn build_request(
|
|
||||||
&self,
|
|
||||||
req: &types::ConnectorCustomerRouterData,
|
|
||||||
connectors: &settings::Connectors,
|
|
||||||
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
|
|
||||||
Ok(Some(
|
|
||||||
services::RequestBuilder::new()
|
|
||||||
.method(services::Method::Post)
|
|
||||||
.url(&types::ConnectorCustomerType::get_url(
|
|
||||||
self, req, connectors,
|
|
||||||
)?)
|
|
||||||
.attach_default_headers()
|
|
||||||
.headers(types::ConnectorCustomerType::get_headers(
|
|
||||||
self, req, connectors,
|
|
||||||
)?)
|
|
||||||
.body(types::ConnectorCustomerType::get_request_body(self, req)?)
|
|
||||||
.build(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_response(
|
|
||||||
&self,
|
|
||||||
data: &types::ConnectorCustomerRouterData,
|
|
||||||
res: Response,
|
|
||||||
) -> CustomResult<types::ConnectorCustomerRouterData, errors::ConnectorError>
|
|
||||||
where
|
|
||||||
types::PaymentsResponseData: Clone,
|
|
||||||
{
|
|
||||||
let response: bluesnap::BluesnapCustomerResponse = res
|
|
||||||
.response
|
|
||||||
.parse_struct("BluesnapCustomerResponse")
|
|
||||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
|
||||||
router_env::logger::info!(connector_response=?response);
|
|
||||||
|
|
||||||
types::RouterData::try_from(types::ResponseRouterData {
|
|
||||||
response,
|
|
||||||
data: data.clone(),
|
|
||||||
http_code: res.status_code,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_error_response(
|
|
||||||
&self,
|
|
||||||
res: Response,
|
|
||||||
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
|
|
||||||
self.build_error_response(res)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl api::PaymentVoid for Bluesnap {}
|
impl api::PaymentVoid for Bluesnap {}
|
||||||
|
|
||||||
impl ConnectorIntegration<api::Void, types::PaymentsCancelData, types::PaymentsResponseData>
|
impl ConnectorIntegration<api::Void, types::PaymentsCancelData, types::PaymentsResponseData>
|
||||||
@ -650,18 +565,18 @@ impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::P
|
|||||||
req: &types::PaymentsAuthorizeRouterData,
|
req: &types::PaymentsAuthorizeRouterData,
|
||||||
connectors: &settings::Connectors,
|
connectors: &settings::Connectors,
|
||||||
) -> CustomResult<String, errors::ConnectorError> {
|
) -> CustomResult<String, errors::ConnectorError> {
|
||||||
match req.is_three_ds() && !req.request.is_wallet() {
|
if req.is_three_ds() && req.request.is_card() {
|
||||||
true => Ok(format!(
|
Ok(format!(
|
||||||
"{}{}{}",
|
"{}{}",
|
||||||
self.base_url(connectors),
|
self.base_url(connectors),
|
||||||
"services/2/payment-fields-tokens?shopperId=",
|
"services/2/payment-fields-tokens/prefill",
|
||||||
req.get_connector_customer_id()?
|
))
|
||||||
)),
|
} else {
|
||||||
_ => Ok(format!(
|
Ok(format!(
|
||||||
"{}{}",
|
"{}{}",
|
||||||
self.base_url(connectors),
|
self.base_url(connectors),
|
||||||
"services/2/transactions"
|
"services/2/transactions"
|
||||||
)),
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -669,6 +584,17 @@ impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::P
|
|||||||
&self,
|
&self,
|
||||||
req: &types::PaymentsAuthorizeRouterData,
|
req: &types::PaymentsAuthorizeRouterData,
|
||||||
) -> CustomResult<Option<types::RequestBody>, errors::ConnectorError> {
|
) -> CustomResult<Option<types::RequestBody>, errors::ConnectorError> {
|
||||||
|
match req.is_three_ds() && req.request.is_card() {
|
||||||
|
true => {
|
||||||
|
let connector_req = bluesnap::BluesnapPaymentsTokenRequest::try_from(req)?;
|
||||||
|
let bluesnap_req = types::RequestBody::log_and_get_request_body(
|
||||||
|
&connector_req,
|
||||||
|
utils::Encode::<bluesnap::BluesnapPaymentsRequest>::encode_to_string_of_json,
|
||||||
|
)
|
||||||
|
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
|
||||||
|
Ok(Some(bluesnap_req))
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
let connector_req = bluesnap::BluesnapPaymentsRequest::try_from(req)?;
|
let connector_req = bluesnap::BluesnapPaymentsRequest::try_from(req)?;
|
||||||
let bluesnap_req = types::RequestBody::log_and_get_request_body(
|
let bluesnap_req = types::RequestBody::log_and_get_request_body(
|
||||||
&connector_req,
|
&connector_req,
|
||||||
@ -677,6 +603,8 @@ impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::P
|
|||||||
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
|
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
|
||||||
Ok(Some(bluesnap_req))
|
Ok(Some(bluesnap_req))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn build_request(
|
fn build_request(
|
||||||
&self,
|
&self,
|
||||||
@ -704,9 +632,10 @@ impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::P
|
|||||||
data: &types::PaymentsAuthorizeRouterData,
|
data: &types::PaymentsAuthorizeRouterData,
|
||||||
res: Response,
|
res: Response,
|
||||||
) -> CustomResult<types::PaymentsAuthorizeRouterData, errors::ConnectorError> {
|
) -> CustomResult<types::PaymentsAuthorizeRouterData, errors::ConnectorError> {
|
||||||
match (data.is_three_ds() && !data.request.is_wallet(), res.headers) {
|
match (data.is_three_ds() && data.request.is_card(), res.headers) {
|
||||||
(true, Some(headers)) => {
|
(true, Some(headers)) => {
|
||||||
let location = connector_utils::get_http_header("Location", &headers)?;
|
let location = connector_utils::get_http_header("Location", &headers)
|
||||||
|
.change_context(errors::ConnectorError::ResponseHandlingFailed)?; // If location headers are not present connector will return 4XX so this error will never be propagated
|
||||||
let payment_fields_token = location
|
let payment_fields_token = location
|
||||||
.split('/')
|
.split('/')
|
||||||
.last()
|
.last()
|
||||||
@ -783,7 +712,7 @@ impl
|
|||||||
&self,
|
&self,
|
||||||
req: &types::PaymentsCompleteAuthorizeRouterData,
|
req: &types::PaymentsCompleteAuthorizeRouterData,
|
||||||
) -> CustomResult<Option<types::RequestBody>, errors::ConnectorError> {
|
) -> CustomResult<Option<types::RequestBody>, errors::ConnectorError> {
|
||||||
let connector_req = bluesnap::BluesnapPaymentsRequest::try_from(req)?;
|
let connector_req = bluesnap::BluesnapCompletePaymentsRequest::try_from(req)?;
|
||||||
let bluesnap_req = types::RequestBody::log_and_get_request_body(
|
let bluesnap_req = types::RequestBody::log_and_get_request_body(
|
||||||
&connector_req,
|
&connector_req,
|
||||||
utils::Encode::<bluesnap::BluesnapPaymentsRequest>::encode_to_string_of_json,
|
utils::Encode::<bluesnap::BluesnapPaymentsRequest>::encode_to_string_of_json,
|
||||||
|
|||||||
@ -6,7 +6,7 @@ use common_utils::{
|
|||||||
pii::Email,
|
pii::Email,
|
||||||
};
|
};
|
||||||
use error_stack::{IntoReport, ResultExt};
|
use error_stack::{IntoReport, ResultExt};
|
||||||
use masking::ExposeInterface;
|
use masking::{ExposeInterface, PeekInterface};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -161,6 +161,42 @@ pub struct BluesnapConnectorMetaData {
|
|||||||
pub merchant_id: String,
|
pub merchant_id: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct BluesnapPaymentsTokenRequest {
|
||||||
|
cc_number: cards::CardNumber,
|
||||||
|
exp_date: Secret<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&types::PaymentsAuthorizeRouterData> for BluesnapPaymentsTokenRequest {
|
||||||
|
type Error = error_stack::Report<errors::ConnectorError>;
|
||||||
|
fn try_from(item: &types::PaymentsAuthorizeRouterData) -> Result<Self, Self::Error> {
|
||||||
|
match item.request.payment_method_data {
|
||||||
|
api::PaymentMethodData::Card(ref ccard) => Ok(Self {
|
||||||
|
cc_number: ccard.card_number.clone(),
|
||||||
|
exp_date: ccard.get_expiry_date_as_mmyyyy("/"),
|
||||||
|
}),
|
||||||
|
api::PaymentMethodData::Wallet(_)
|
||||||
|
| 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::CardRedirect(_)
|
||||||
|
| payments::PaymentMethodData::Voucher(_)
|
||||||
|
| payments::PaymentMethodData::GiftCard(_) => {
|
||||||
|
Err(errors::ConnectorError::NotImplemented(
|
||||||
|
"Selected payment method via Token flow through bluesnap".to_string(),
|
||||||
|
))
|
||||||
|
.into_report()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TryFrom<&types::PaymentsAuthorizeRouterData> for BluesnapPaymentsRequest {
|
impl TryFrom<&types::PaymentsAuthorizeRouterData> for BluesnapPaymentsRequest {
|
||||||
type Error = error_stack::Report<errors::ConnectorError>;
|
type Error = error_stack::Report<errors::ConnectorError>;
|
||||||
fn try_from(item: &types::PaymentsAuthorizeRouterData) -> Result<Self, Self::Error> {
|
fn try_from(item: &types::PaymentsAuthorizeRouterData) -> Result<Self, Self::Error> {
|
||||||
@ -444,7 +480,20 @@ impl TryFrom<types::PaymentsSessionResponseRouterData<BluesnapWalletTokenRespons
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<&types::PaymentsCompleteAuthorizeRouterData> for BluesnapPaymentsRequest {
|
#[derive(Debug, Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct BluesnapCompletePaymentsRequest {
|
||||||
|
amount: String,
|
||||||
|
currency: enums::Currency,
|
||||||
|
card_transaction_type: BluesnapTxnType,
|
||||||
|
pf_token: String,
|
||||||
|
three_d_secure: Option<BluesnapThreeDSecureInfo>,
|
||||||
|
transaction_fraud_info: Option<TransactionFraudInfo>,
|
||||||
|
card_holder_info: Option<BluesnapCardHolderInfo>,
|
||||||
|
merchant_transaction_id: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&types::PaymentsCompleteAuthorizeRouterData> for BluesnapCompletePaymentsRequest {
|
||||||
type Error = error_stack::Report<errors::ConnectorError>;
|
type Error = error_stack::Report<errors::ConnectorError>;
|
||||||
fn try_from(item: &types::PaymentsCompleteAuthorizeRouterData) -> Result<Self, Self::Error> {
|
fn try_from(item: &types::PaymentsCompleteAuthorizeRouterData) -> Result<Self, Self::Error> {
|
||||||
let redirection_response: BluesnapRedirectionResponse = item
|
let redirection_response: BluesnapRedirectionResponse = item
|
||||||
@ -458,6 +507,22 @@ impl TryFrom<&types::PaymentsCompleteAuthorizeRouterData> for BluesnapPaymentsRe
|
|||||||
.parse_value("BluesnapRedirectionResponse")
|
.parse_value("BluesnapRedirectionResponse")
|
||||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||||
|
|
||||||
|
let pf_token = item
|
||||||
|
.request
|
||||||
|
.redirect_response
|
||||||
|
.clone()
|
||||||
|
.and_then(|res| res.params.to_owned())
|
||||||
|
.ok_or(errors::ConnectorError::MissingConnectorRedirectionPayload {
|
||||||
|
field_name: "request.redirect_response.params",
|
||||||
|
})?
|
||||||
|
.peek()
|
||||||
|
.split_once('=')
|
||||||
|
.ok_or(errors::ConnectorError::MissingConnectorRedirectionPayload {
|
||||||
|
field_name: "request.redirect_response.params.paymentToken",
|
||||||
|
})?
|
||||||
|
.1
|
||||||
|
.to_string();
|
||||||
|
|
||||||
let redirection_result: BluesnapThreeDsResult = redirection_response
|
let redirection_result: BluesnapThreeDsResult = redirection_response
|
||||||
.authentication_response
|
.authentication_response
|
||||||
.parse_struct("BluesnapThreeDsResult")
|
.parse_struct("BluesnapThreeDsResult")
|
||||||
@ -467,23 +532,8 @@ impl TryFrom<&types::PaymentsCompleteAuthorizeRouterData> for BluesnapPaymentsRe
|
|||||||
Some(enums::CaptureMethod::Manual) => BluesnapTxnType::AuthOnly,
|
Some(enums::CaptureMethod::Manual) => BluesnapTxnType::AuthOnly,
|
||||||
_ => BluesnapTxnType::AuthCapture,
|
_ => BluesnapTxnType::AuthCapture,
|
||||||
};
|
};
|
||||||
let payment_method = if let Some(api::PaymentMethodData::Card(ccard)) =
|
|
||||||
item.request.payment_method_data.clone()
|
|
||||||
{
|
|
||||||
PaymentMethodDetails::CreditCard(Card {
|
|
||||||
card_number: ccard.card_number.clone(),
|
|
||||||
expiration_month: ccard.card_exp_month.clone(),
|
|
||||||
expiration_year: ccard.get_expiry_year_4_digit(),
|
|
||||||
security_code: ccard.card_cvc,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
Err(errors::ConnectorError::MissingConnectorRedirectionPayload {
|
|
||||||
field_name: "request.payment_method_data",
|
|
||||||
})?
|
|
||||||
};
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
amount: utils::to_currency_base_unit(item.request.amount, item.request.currency)?,
|
amount: utils::to_currency_base_unit(item.request.amount, item.request.currency)?,
|
||||||
payment_method,
|
|
||||||
currency: item.request.currency,
|
currency: item.request.currency,
|
||||||
card_transaction_type: auth_mode,
|
card_transaction_type: auth_mode,
|
||||||
three_d_secure: Some(BluesnapThreeDSecureInfo {
|
three_d_secure: Some(BluesnapThreeDSecureInfo {
|
||||||
@ -502,6 +552,7 @@ impl TryFrom<&types::PaymentsCompleteAuthorizeRouterData> for BluesnapPaymentsRe
|
|||||||
item.request.get_email()?,
|
item.request.get_email()?,
|
||||||
)?,
|
)?,
|
||||||
merchant_transaction_id: Some(item.connector_request_reference_id.clone()),
|
merchant_transaction_id: Some(item.connector_request_reference_id.clone()),
|
||||||
|
pf_token,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -594,21 +645,6 @@ impl TryFrom<&types::ConnectorAuthType> for BluesnapAuthType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct BluesnapCustomerRequest {
|
|
||||||
email: Option<Email>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<&types::ConnectorCustomerRouterData> for BluesnapCustomerRequest {
|
|
||||||
type Error = error_stack::Report<errors::ConnectorError>;
|
|
||||||
fn try_from(item: &types::ConnectorCustomerRouterData) -> Result<Self, Self::Error> {
|
|
||||||
Ok(Self {
|
|
||||||
email: item.request.email.to_owned(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct BluesnapCustomerResponse {
|
pub struct BluesnapCustomerResponse {
|
||||||
|
|||||||
@ -262,6 +262,7 @@ pub trait PaymentsAuthorizeRequestData {
|
|||||||
fn get_webhook_url(&self) -> Result<String, Error>;
|
fn get_webhook_url(&self) -> Result<String, Error>;
|
||||||
fn get_router_return_url(&self) -> Result<String, Error>;
|
fn get_router_return_url(&self) -> Result<String, Error>;
|
||||||
fn is_wallet(&self) -> bool;
|
fn is_wallet(&self) -> bool;
|
||||||
|
fn is_card(&self) -> bool;
|
||||||
fn get_payment_method_type(&self) -> Result<diesel_models::enums::PaymentMethodType, Error>;
|
fn get_payment_method_type(&self) -> Result<diesel_models::enums::PaymentMethodType, Error>;
|
||||||
fn get_connector_mandate_id(&self) -> Result<String, Error>;
|
fn get_connector_mandate_id(&self) -> Result<String, Error>;
|
||||||
fn get_complete_authorize_url(&self) -> Result<String, Error>;
|
fn get_complete_authorize_url(&self) -> Result<String, Error>;
|
||||||
@ -338,6 +339,9 @@ impl PaymentsAuthorizeRequestData for types::PaymentsAuthorizeData {
|
|||||||
fn is_wallet(&self) -> bool {
|
fn is_wallet(&self) -> bool {
|
||||||
matches!(self.payment_method_data, api::PaymentMethodData::Wallet(_))
|
matches!(self.payment_method_data, api::PaymentMethodData::Wallet(_))
|
||||||
}
|
}
|
||||||
|
fn is_card(&self) -> bool {
|
||||||
|
matches!(self.payment_method_data, api::PaymentMethodData::Card(_))
|
||||||
|
}
|
||||||
|
|
||||||
fn get_payment_method_type(&self) -> Result<diesel_models::enums::PaymentMethodType, Error> {
|
fn get_payment_method_type(&self) -> Result<diesel_models::enums::PaymentMethodType, Error> {
|
||||||
self.payment_method_type
|
self.payment_method_type
|
||||||
@ -591,6 +595,7 @@ pub trait CardData {
|
|||||||
delimiter: String,
|
delimiter: String,
|
||||||
) -> Secret<String>;
|
) -> Secret<String>;
|
||||||
fn get_expiry_date_as_yyyymm(&self, delimiter: &str) -> Secret<String>;
|
fn get_expiry_date_as_yyyymm(&self, delimiter: &str) -> Secret<String>;
|
||||||
|
fn get_expiry_date_as_mmyyyy(&self, delimiter: &str) -> Secret<String>;
|
||||||
fn get_expiry_year_4_digit(&self) -> Secret<String>;
|
fn get_expiry_year_4_digit(&self) -> Secret<String>;
|
||||||
fn get_expiry_date_as_yymm(&self) -> Secret<String>;
|
fn get_expiry_date_as_yymm(&self) -> Secret<String>;
|
||||||
}
|
}
|
||||||
@ -625,6 +630,15 @@ impl CardData for api::Card {
|
|||||||
self.card_exp_month.peek().clone()
|
self.card_exp_month.peek().clone()
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
fn get_expiry_date_as_mmyyyy(&self, delimiter: &str) -> Secret<String> {
|
||||||
|
let year = self.get_expiry_year_4_digit();
|
||||||
|
Secret::new(format!(
|
||||||
|
"{}{}{}",
|
||||||
|
self.card_exp_month.peek().clone(),
|
||||||
|
delimiter,
|
||||||
|
year.peek()
|
||||||
|
))
|
||||||
|
}
|
||||||
fn get_expiry_year_4_digit(&self) -> Secret<String> {
|
fn get_expiry_year_4_digit(&self) -> Secret<String> {
|
||||||
let mut year = self.card_exp_year.peek().clone();
|
let mut year = self.card_exp_year.peek().clone();
|
||||||
if year.len() == 2 {
|
if year.len() == 2 {
|
||||||
|
|||||||
@ -212,6 +212,7 @@ default_imp_for_create_customer!(
|
|||||||
connector::Authorizedotnet,
|
connector::Authorizedotnet,
|
||||||
connector::Bambora,
|
connector::Bambora,
|
||||||
connector::Bitpay,
|
connector::Bitpay,
|
||||||
|
connector::Bluesnap,
|
||||||
connector::Boku,
|
connector::Boku,
|
||||||
connector::Braintree,
|
connector::Braintree,
|
||||||
connector::Cashtocode,
|
connector::Cashtocode,
|
||||||
|
|||||||
@ -1058,29 +1058,24 @@ pub fn build_redirection_form(
|
|||||||
RedirectForm::BlueSnap {
|
RedirectForm::BlueSnap {
|
||||||
payment_fields_token,
|
payment_fields_token,
|
||||||
} => {
|
} => {
|
||||||
let card_details = if let Some(api::PaymentMethodData::Card(ccard)) =
|
let card_details =
|
||||||
payment_method_data
|
if let Some(api::PaymentMethodData::Card(ccard)) = payment_method_data {
|
||||||
{
|
|
||||||
format!(
|
format!(
|
||||||
"var newCard={{ccNumber: \"{}\",cvv: \"{}\",expDate: \"{}/{}\",amount: {},currency: \"{}\"}};",
|
"var saveCardDirectly={{cvv: \"{}\",amount: {},currency: \"{}\"}};",
|
||||||
ccard.card_number.peek(),
|
|
||||||
ccard.card_cvc.peek(),
|
ccard.card_cvc.peek(),
|
||||||
ccard.card_exp_month.peek(),
|
|
||||||
ccard.card_exp_year.peek(),
|
|
||||||
amount,
|
amount,
|
||||||
currency
|
currency
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
"".to_string()
|
"".to_string()
|
||||||
};
|
};
|
||||||
|
let bluesnap_sdk_url = config.connectors.bluesnap.secondary_base_url;
|
||||||
let bluesnap_url = config.connectors.bluesnap.secondary_base_url;
|
|
||||||
maud::html! {
|
maud::html! {
|
||||||
(maud::DOCTYPE)
|
(maud::DOCTYPE)
|
||||||
html {
|
html {
|
||||||
head {
|
head {
|
||||||
meta name="viewport" content="width=device-width, initial-scale=1";
|
meta name="viewport" content="width=device-width, initial-scale=1";
|
||||||
(PreEscaped(format!("<script src=\"{bluesnap_url}web-sdk/5/bluesnap.js\"></script>")))
|
(PreEscaped(format!("<script src=\"{bluesnap_sdk_url}web-sdk/5/bluesnap.js\"></script>")))
|
||||||
}
|
}
|
||||||
body style="background-color: #ffffff; padding: 20px; font-family: Arial, Helvetica, Sans-Serif;" {
|
body style="background-color: #ffffff; padding: 20px; font-family: Arial, Helvetica, Sans-Serif;" {
|
||||||
|
|
||||||
@ -1110,7 +1105,7 @@ pub fn build_redirection_form(
|
|||||||
function(sdkResponse) {{
|
function(sdkResponse) {{
|
||||||
console.log(sdkResponse);
|
console.log(sdkResponse);
|
||||||
var f = document.createElement('form');
|
var f = document.createElement('form');
|
||||||
f.action=window.location.pathname.replace(/payments\\/redirect\\/(\\w+)\\/(\\w+)\\/\\w+/, \"payments/$1/$2/redirect/complete/bluesnap\");
|
f.action=window.location.pathname.replace(/payments\\/redirect\\/(\\w+)\\/(\\w+)\\/\\w+/, \"payments/$1/$2/redirect/complete/bluesnap?paymentToken={payment_fields_token}\");
|
||||||
f.method='POST';
|
f.method='POST';
|
||||||
var i=document.createElement('input');
|
var i=document.createElement('input');
|
||||||
i.type='hidden';
|
i.type='hidden';
|
||||||
@ -1121,7 +1116,7 @@ pub fn build_redirection_form(
|
|||||||
f.submit();
|
f.submit();
|
||||||
}});
|
}});
|
||||||
{card_details}
|
{card_details}
|
||||||
bluesnap.threeDsPaymentsSubmitData(newCard);
|
bluesnap.threeDsPaymentsSubmitData(saveCardDirectly);
|
||||||
</script>
|
</script>
|
||||||
")))
|
")))
|
||||||
}}
|
}}
|
||||||
|
|||||||
Reference in New Issue
Block a user