feat(connector): Cybersource Authorize (#154)

Co-authored-by: Arun Raj M <jarnura47@gmail.com>
This commit is contained in:
Sahebjot singh
2022-12-24 15:09:49 +05:30
committed by GitHub
parent e7579a4819
commit 6123823523
26 changed files with 658 additions and 7 deletions

View File

@ -38,7 +38,7 @@ locker_decryption_key2 = ""
[connectors.supported]
wallets = ["klarna","braintree","applepay"]
cards = ["stripe","adyen","authorizedotnet","checkout","braintree","aci","shift4"]
cards = ["stripe","adyen","authorizedotnet","checkout","braintree","aci","shift4","cybersource"]
[eph_key]
validity = 1
@ -67,6 +67,9 @@ base_url = "https://api-na.playground.klarna.com/"
[connectors.applepay]
base_url = "https://apple-pay-gateway.apple.com/"
[connectors.cybersource]
base_url = "https://apitest.cybersource.com/"
[connectors.shift4]
base_url = "https://api.shift4.com/"

View File

@ -119,14 +119,16 @@ base_url = "https://api-na.playground.klarna.com/"
[connectors.applepay]
base_url = "https://apple-pay-gateway.apple.com/"
[connectors.cybersource]
base_url = "https://apitest.cybersource.com/"
[connectors.shift4]
base_url = "https://api.shift4.com/"
# This data is used to call respective connectors for wallets and cards
[connectors.supported]
wallets = ["klarna","braintree","applepay"]
cards = ["stripe","adyen","authorizedotnet","checkout","braintree"]
cards = ["stripe","adyen","authorizedotnet","checkout","braintree", "cybersource"]
# Scheduler settings provides a point to modify the behaviour of scheduler flow.
# It defines the the streams/queues name and configuration as well as event selection variables

View File

@ -74,9 +74,12 @@ base_url = "https://api-na.playground.klarna.com/"
[connectors.applepay]
base_url = "https://apple-pay-gateway.apple.com/"
[connectors.cybersource]
base_url = "https://apitest.cybersource.com/"
[connectors.shift4]
base_url = "https://api.shift4.com/"
[connectors.supported]
wallets = ["klarna","braintree","applepay"]
cards = ["stripe","adyen","authorizedotnet","checkout","braintree","shift4"]
cards = ["stripe","adyen","authorizedotnet","checkout","braintree","shift4","cybersource"]

View File

@ -491,6 +491,7 @@ pub enum Connector {
Authorizedotnet,
Braintree,
Checkout,
Cybersource,
#[default]
Dummy,
Klarna,

View File

@ -57,5 +57,5 @@ num_partitions = 64
max_read_count = 100
[connectors.supported]
wallets = ["klarna", "braintree"]
cards = ["stripe", "adyen", "authorizedotnet", "checkout", "braintree"]
wallets = ["klarna","braintree"]
cards = ["stripe","adyen","authorizedotnet","checkout","braintree", "cybersource"]

View File

@ -113,6 +113,7 @@ pub struct Connectors {
pub braintree: ConnectorParams,
pub checkout: ConnectorParams,
pub klarna: ConnectorParams,
pub cybersource: ConnectorParams,
pub shift4: ConnectorParams,
pub stripe: ConnectorParams,
pub supported: SupportedConnectors,

View File

@ -4,6 +4,7 @@ pub mod applepay;
pub mod authorizedotnet;
pub mod braintree;
pub mod checkout;
pub mod cybersource;
pub mod klarna;
pub mod stripe;
@ -11,5 +12,6 @@ pub mod shift4;
pub use self::{
aci::Aci, adyen::Adyen, applepay::Applepay, authorizedotnet::Authorizedotnet,
braintree::Braintree, checkout::Checkout, klarna::Klarna, shift4::Shift4, stripe::Stripe,
braintree::Braintree, checkout::Checkout, cybersource::Cybersource, klarna::Klarna,
shift4::Shift4, stripe::Stripe,
};

View File

@ -0,0 +1,303 @@
mod transformers;
use std::fmt::Debug;
use base64;
use bytes::Bytes;
use error_stack::{IntoReport, ResultExt};
use ring::{digest, hmac};
use time::OffsetDateTime;
use transformers as cybersource;
use url::Url;
use crate::{
configs::settings,
consts,
core::errors::{self, CustomResult},
headers, logger, services,
types::{self, api},
utils::{self, BytesExt},
};
#[derive(Debug, Clone)]
pub struct Cybersource;
impl Cybersource {
pub fn generate_digest(&self, payload: &[u8]) -> String {
let payload_digest = digest::digest(&digest::SHA256, payload);
base64::encode(payload_digest)
}
pub fn generate_signature(
&self,
auth: cybersource::CybersourceAuthType,
host: String,
resource: &str,
payload: &str,
date: OffsetDateTime,
) -> CustomResult<String, errors::ConnectorError> {
let cybersource::CybersourceAuthType {
api_key,
merchant_account,
api_secret,
} = auth;
let headers_for_post_method = "host date (request-target) digest v-c-merchant-id";
let signature_string = format!(
"host: {host}\n\
date: {date}\n\
(request-target): post {resource}\n\
digest: SHA-256={}\n\
v-c-merchant-id: {merchant_account}",
self.generate_digest(payload.as_bytes())
);
let key_value = base64::decode(api_secret)
.into_report()
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
let key = hmac::Key::new(hmac::HMAC_SHA256, &key_value);
let signature_value =
base64::encode(hmac::sign(&key, signature_string.as_bytes()).as_ref());
let signature_header = format!(
r#"keyid="{api_key}", algorithm="HmacSHA256", headers="{headers_for_post_method}", signature="{signature_value}""#
);
Ok(signature_header)
}
}
impl api::ConnectorCommon for Cybersource {
fn id(&self) -> &'static str {
"cybersource"
}
fn common_get_content_type(&self) -> &'static str {
"application/json"
}
fn base_url<'a>(&self, connectors: &'a settings::Connectors) -> &'a str {
connectors.cybersource.base_url.as_ref()
}
}
impl api::Payment for Cybersource {}
impl api::PaymentAuthorize for Cybersource {}
impl api::PaymentSync for Cybersource {}
impl api::PaymentVoid for Cybersource {}
impl api::PaymentCapture for Cybersource {}
impl api::PreVerify for Cybersource {}
impl
services::ConnectorIntegration<
api::Verify,
types::VerifyRequestData,
types::PaymentsResponseData,
> for Cybersource
{
}
impl api::PaymentSession for Cybersource {}
impl
services::ConnectorIntegration<
api::Session,
types::PaymentsSessionData,
types::PaymentsResponseData,
> for Cybersource
{
}
impl
services::ConnectorIntegration<
api::Capture,
types::PaymentsCaptureData,
types::PaymentsResponseData,
> for Cybersource
{
}
impl
services::ConnectorIntegration<api::PSync, types::PaymentsSyncData, types::PaymentsResponseData>
for Cybersource
{
}
impl
services::ConnectorIntegration<
api::Authorize,
types::PaymentsAuthorizeData,
types::PaymentsResponseData,
> for Cybersource
{
fn get_headers(
&self,
_req: &types::PaymentsAuthorizeRouterData,
) -> CustomResult<Vec<(String, String)>, errors::ConnectorError> {
let headers = vec![
(
headers::CONTENT_TYPE.to_string(),
types::PaymentsAuthorizeType::get_content_type(self).to_string(),
),
(
headers::ACCEPT.to_string(),
"application/hal+json;charset=utf-8".to_string(),
),
];
Ok(headers)
}
fn get_content_type(&self) -> &'static str {
"application/json;charset=utf-8"
}
fn get_url(
&self,
_req: &types::PaymentsAuthorizeRouterData,
connectors: &settings::Connectors,
) -> CustomResult<String, errors::ConnectorError> {
Ok(format!(
"{}pts/v2/payments/",
api::ConnectorCommon::base_url(self, connectors)
))
}
fn build_request(
&self,
req: &types::PaymentsAuthorizeRouterData,
connectors: &settings::Connectors,
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
let date = OffsetDateTime::now_utc();
let cybersource_req =
utils::Encode::<cybersource::CybersourcePaymentsRequest>::convert_and_encode(req)
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
let auth: cybersource::CybersourceAuthType =
cybersource::CybersourceAuthType::try_from(&req.connector_auth_type)?;
let merchant_account = auth.merchant_account.clone();
let cybersource_host = Url::parse(connectors.cybersource.base_url.as_str())
.into_report()
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
match cybersource_host.host_str() {
Some(host) => {
let signature = self.generate_signature(
auth,
host.to_string(),
"/pts/v2/payments/",
&cybersource_req,
date,
)?;
let headers = vec![
(
"Digest".to_string(),
format!(
"SHA-256={}",
self.generate_digest(cybersource_req.as_bytes())
),
),
("v-c-merchant-id".to_string(), merchant_account),
("Date".to_string(), date.to_string()),
("Host".to_string(), host.to_string()),
("Signature".to_string(), signature),
];
let request = services::RequestBuilder::new()
.method(services::Method::Post)
.url(&types::PaymentsAuthorizeType::get_url(
self, req, connectors,
)?)
.headers(headers)
.headers(types::PaymentsAuthorizeType::get_headers(self, req)?)
.body(Some(cybersource_req))
.build();
Ok(Some(request))
}
None => Err(errors::ConnectorError::RequestEncodingFailed.into()),
}
}
fn handle_response(
&self,
data: &types::PaymentsAuthorizeRouterData,
res: types::Response,
) -> CustomResult<types::PaymentsAuthorizeRouterData, errors::ConnectorError> {
let response: cybersource::CybersourcePaymentsResponse = res
.response
.parse_struct("Cybersource PaymentResponse")
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
logger::debug!(cybersourcepayments_create_response=?response);
types::ResponseRouterData {
response,
data: data.clone(),
http_code: res.status_code,
}
.try_into()
.change_context(errors::ConnectorError::ResponseHandlingFailed)
}
fn get_error_response(
&self,
res: Bytes,
) -> CustomResult<types::ErrorResponse, errors::ConnectorError> {
let response: cybersource::ErrorResponse = res
.parse_struct("Cybersource ErrorResponse")
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
Ok(types::ErrorResponse {
code: consts::NO_ERROR_CODE.to_string(),
message: response
.message
.unwrap_or_else(|| consts::NO_ERROR_MESSAGE.to_string()),
reason: None,
})
}
}
impl
services::ConnectorIntegration<
api::Void,
types::PaymentsCancelData,
types::PaymentsResponseData,
> for Cybersource
{
}
impl api::Refund for Cybersource {}
impl api::RefundExecute for Cybersource {}
impl api::RefundSync for Cybersource {}
#[allow(dead_code)]
impl services::ConnectorIntegration<api::Execute, types::RefundsData, types::RefundsResponseData>
for Cybersource
{
}
#[allow(dead_code)]
impl services::ConnectorIntegration<api::RSync, types::RefundsData, types::RefundsResponseData>
for Cybersource
{
}
#[async_trait::async_trait]
impl api::IncomingWebhook for Cybersource {
fn get_webhook_object_reference_id(
&self,
_body: &[u8],
) -> CustomResult<String, errors::ConnectorError> {
Err(errors::ConnectorError::NotImplemented("cybersource".to_string()).into())
}
fn get_webhook_event_type(
&self,
_body: &[u8],
) -> CustomResult<api::IncomingWebhookEvent, errors::ConnectorError> {
Err(errors::ConnectorError::NotImplemented("cybersource".to_string()).into())
}
fn get_webhook_resource_object(
&self,
_body: &[u8],
) -> CustomResult<serde_json::Value, errors::ConnectorError> {
Err(errors::ConnectorError::NotImplemented("cybersource".to_string()).into())
}
}
impl services::ConnectorRedirectResponse for Cybersource {}

View File

@ -0,0 +1,316 @@
use api_models::payments;
use common_utils::pii;
use masking::Secret;
use serde::{Deserialize, Serialize};
use crate::{
core::errors,
pii::PeekInterface,
types::{self, api, storage::enums},
};
#[derive(Default, Debug, Serialize, Eq, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct CybersourcePaymentsRequest {
processing_information: ProcessingInformation,
payment_information: PaymentInformation,
order_information: OrderInformationWithBill,
}
#[derive(Default, Debug, Serialize, Eq, PartialEq)]
pub struct ProcessingInformation {
capture: bool,
}
#[derive(Default, Debug, Serialize, Eq, PartialEq)]
pub struct PaymentInformation {
card: Card,
}
#[derive(Default, Debug, Serialize, Eq, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct Card {
number: String,
expiration_month: String,
expiration_year: String,
security_code: String,
}
#[derive(Default, Debug, Serialize, Eq, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct OrderInformationWithBill {
amount_details: Amount,
bill_to: BillTo,
}
#[derive(Default, Debug, Serialize, Eq, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct OrderInformation {
amount_details: Amount,
}
#[derive(Default, Debug, Serialize, Eq, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct Amount {
total_amount: String,
currency: String,
}
#[derive(Default, Debug, Serialize, Eq, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct BillTo {
first_name: Secret<String>,
last_name: Secret<String>,
address1: Secret<String>,
locality: String,
administrative_area: Secret<String>,
postal_code: Secret<String>,
country: String,
email: Secret<String, pii::Email>,
phone_number: Secret<String>,
}
// for cybersource each item in Billing is mandatory
fn build_bill_to(
address_details: payments::Address,
email: Secret<String, pii::Email>,
phone_number: Secret<String>,
) -> Option<BillTo> {
if let Some(api_models::payments::AddressDetails {
first_name: Some(f_name),
last_name: Some(last_name),
line1: Some(address1),
city: Some(city),
line2: Some(administrative_area),
zip: Some(postal_code),
country: Some(country),
..
}) = address_details.address
{
Some(BillTo {
first_name: f_name,
last_name,
address1,
locality: city,
administrative_area,
postal_code,
country,
email,
phone_number,
})
} else {
None
}
}
impl TryFrom<&types::PaymentsAuthorizeRouterData> for CybersourcePaymentsRequest {
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(item: &types::PaymentsAuthorizeRouterData) -> Result<Self, Self::Error> {
match item.request.payment_method_data {
api::PaymentMethod::Card(ref ccard) => {
let address = item
.address
.billing
.clone()
.ok_or(errors::ConnectorError::RequestEncodingFailed)?;
let phone = address
.clone()
.phone
.ok_or(errors::ConnectorError::RequestEncodingFailed)?;
let phone_number = phone
.number
.ok_or(errors::ConnectorError::RequestEncodingFailed)?;
let country_code = phone
.country_code
.ok_or(errors::ConnectorError::RequestEncodingFailed)?;
let number_with_code =
Secret::new(format!("{}{}", country_code, phone_number.peek()));
let email = item
.request
.email
.clone()
.ok_or(errors::ConnectorError::RequestEncodingFailed)?;
let bill_to = build_bill_to(address, email, number_with_code)
.ok_or(errors::ConnectorError::RequestEncodingFailed)?;
let order_information = OrderInformationWithBill {
amount_details: Amount {
total_amount: item.request.amount.to_string(),
currency: item.request.currency.to_string().to_uppercase(),
},
bill_to,
};
let payment_information = PaymentInformation {
card: Card {
number: ccard.card_number.peek().clone(),
expiration_month: ccard.card_exp_month.peek().clone(),
expiration_year: ccard.card_exp_year.peek().clone(),
security_code: ccard.card_cvc.peek().clone(),
},
};
let processing_information = ProcessingInformation {
capture: matches!(
item.request.capture_method,
Some(enums::CaptureMethod::Automatic) | None
),
};
Ok(Self {
processing_information,
payment_information,
order_information,
})
}
_ => Err(errors::ConnectorError::NotImplemented("Payment methods".to_string()).into()),
}
}
}
pub struct CybersourceAuthType {
pub(super) api_key: String,
pub(super) merchant_account: String,
pub(super) api_secret: String,
}
impl TryFrom<&types::ConnectorAuthType> for CybersourceAuthType {
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(auth_type: &types::ConnectorAuthType) -> Result<Self, Self::Error> {
if let types::ConnectorAuthType::SignatureKey {
api_key,
key1,
api_secret,
} = auth_type
{
Ok(Self {
api_key: api_key.to_string(),
merchant_account: key1.to_string(),
api_secret: api_secret.to_string(),
})
} else {
Err(errors::ConnectorError::FailedToObtainAuthType)?
}
}
}
#[derive(Debug, Default, Clone, Deserialize, Eq, PartialEq)]
#[serde(rename_all = "UPPERCASE")]
pub enum CybersourcePaymentStatus {
Authorized,
Succeeded,
Failed,
#[default]
Processing,
}
impl From<CybersourcePaymentStatus> for enums::AttemptStatus {
fn from(item: CybersourcePaymentStatus) -> Self {
match item {
CybersourcePaymentStatus::Authorized => Self::Authorized,
CybersourcePaymentStatus::Succeeded => Self::Charged,
CybersourcePaymentStatus::Failed => Self::Failure,
CybersourcePaymentStatus::Processing => Self::Authorizing,
}
}
}
#[derive(Default, Debug, Clone, Deserialize, Eq, PartialEq)]
pub struct CybersourcePaymentsResponse {
id: String,
status: CybersourcePaymentStatus,
}
impl TryFrom<types::PaymentsResponseRouterData<CybersourcePaymentsResponse>>
for types::PaymentsAuthorizeRouterData
{
type Error = error_stack::Report<errors::ParsingError>;
fn try_from(
item: types::PaymentsResponseRouterData<CybersourcePaymentsResponse>,
) -> Result<Self, Self::Error> {
Ok(Self {
status: item.response.status.into(),
response: Ok(types::PaymentsResponseData::TransactionResponse {
resource_id: types::ResponseId::ConnectorTransactionId(item.response.id),
redirection_data: None,
redirect: false,
mandate_reference: None,
}),
..item.data
})
}
}
#[derive(Debug, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ErrorResponse {
pub error_information: Option<ErrorInformation>,
pub status: String,
pub message: Option<String>,
}
#[derive(Debug, Default, Deserialize)]
pub struct ErrorInformation {
pub message: String,
pub reason: String,
}
#[derive(Default, Debug, Serialize)]
pub struct CybersourceRefundRequest {
order_information: OrderInformation,
}
impl<F> TryFrom<&types::RefundsRouterData<F>> for CybersourceRefundRequest {
type Error = error_stack::Report<errors::ParsingError>;
fn try_from(item: &types::RefundsRouterData<F>) -> Result<Self, Self::Error> {
Ok(Self {
order_information: OrderInformation {
amount_details: Amount {
total_amount: item.request.amount.to_string(),
currency: item.request.currency.to_string(),
},
},
})
}
}
#[allow(dead_code)]
#[derive(Debug, Default, Deserialize, Clone)]
pub enum RefundStatus {
Succeeded,
Failed,
#[default]
Processing,
}
impl From<RefundStatus> for enums::RefundStatus {
fn from(item: RefundStatus) -> Self {
match item {
self::RefundStatus::Succeeded => Self::Success,
self::RefundStatus::Failed => Self::Failure,
self::RefundStatus::Processing => Self::Pending,
}
}
}
#[derive(Default, Debug, Clone, Deserialize)]
pub struct CybersourceRefundResponse {
pub id: String,
pub status: RefundStatus,
}
impl TryFrom<types::RefundsResponseRouterData<api::RSync, CybersourceRefundResponse>>
for types::RefundsRouterData<api::RSync>
{
type Error = error_stack::Report<errors::ParsingError>;
fn try_from(
item: types::RefundsResponseRouterData<api::RSync, CybersourceRefundResponse>,
) -> Result<Self, Self::Error> {
Ok(Self {
response: Ok(types::RefundsResponseData {
connector_refund_id: item.response.id,
refund_status: enums::RefundStatus::from(item.response.status),
}),
..item.data
})
}
}

View File

@ -469,6 +469,7 @@ where
pub refunds: Vec<storage::Refund>,
pub sessions_token: Vec<api::SessionToken>,
pub card_cvc: Option<pii::Secret<String>>,
pub email: Option<masking::Secret<String, pii::Email>>,
}
#[derive(Debug)]

View File

@ -113,6 +113,7 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsCancelRequest>
payment_attempt,
currency,
amount,
email: None,
mandate_id: None,
setup_mandate: None,
token: None,

View File

@ -125,6 +125,7 @@ impl<F: Send + Clone> GetTracker<F, payments::PaymentData<F>, api::PaymentsCaptu
currency,
force_sync: None,
amount,
email: None,
mandate_id: None,
setup_mandate: None,
token: None,

View File

@ -142,6 +142,7 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsRequest> for Pa
currency,
connector_response,
amount,
email: request.email.clone(),
mandate_id: None,
setup_mandate,
token,

View File

@ -214,6 +214,7 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsRequest> for Pa
payment_attempt,
currency,
amount,
email: request.email.clone(),
mandate_id,
setup_mandate,
token,

View File

@ -136,6 +136,7 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::VerifyRequest> for Paym
/// currency and amount are irrelevant in this scenario
currency: storage_enums::Currency::default(),
amount: api::Amount::Zero,
email: None,
mandate_id: None,
setup_mandate: request.mandate_data.clone(),
token: request.payment_token.clone(),

View File

@ -13,6 +13,7 @@ use crate::{
payments::{self, helpers, operations, PaymentData},
},
db::StorageInterface,
pii,
pii::Secret,
routes::AppState,
types::{
@ -131,6 +132,7 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsSessionRequest>
payment_attempt,
currency,
amount,
email: None::<Secret<String, pii::Email>>,
mandate_id: None,
token: None,
setup_mandate: None,

View File

@ -12,6 +12,7 @@ use crate::{
payments::{helpers, operations, CustomerDetails, PaymentAddress, PaymentData},
},
db::StorageInterface,
pii,
pii::Secret,
routes::AppState,
types::{
@ -128,6 +129,7 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsStartRequest> f
payment_intent,
currency,
amount,
email: None::<Secret<String, pii::Email>>,
mandate_id: None,
connector_response,
setup_mandate: None,

View File

@ -275,6 +275,7 @@ async fn get_tracker_for_sync<
connector_response,
currency,
amount,
email: None,
mandate_id: None,
setup_mandate: None,
token: None,

View File

@ -164,6 +164,7 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsRequest> for Pa
payment_attempt,
currency,
amount,
email: request.email.clone(),
mandate_id,
token,
setup_mandate,

View File

@ -433,6 +433,7 @@ impl<F: Clone> TryFrom<PaymentData<F>> for types::PaymentsAuthorizeData {
amount: payment_data.amount.into(),
currency: payment_data.currency,
browser_info,
email: payment_data.email,
order_details,
})
}

View File

@ -13,6 +13,7 @@ pub mod transformers;
use std::marker::PhantomData;
pub use api_models::enums::Connector;
use common_utils::pii::Email;
use error_stack::{IntoReport, ResultExt};
use self::{api::payments, storage::enums as storage_enums};
@ -90,6 +91,7 @@ pub struct RouterData<Flow, Request, Response> {
pub struct PaymentsAuthorizeData {
pub payment_method_data: payments::PaymentMethod,
pub amount: i64,
pub email: Option<masking::Secret<String, Email>>,
pub currency: storage_enums::Currency,
pub confirm: bool,
pub statement_descriptor_suffix: Option<String>,

View File

@ -146,6 +146,7 @@ impl ConnectorData {
"braintree" => Ok(Box::new(&connector::Braintree)),
"klarna" => Ok(Box::new(&connector::Klarna)),
"applepay" => Ok(Box::new(&connector::Applepay)),
"cybersource" => Ok(Box::new(&connector::Cybersource)),
"shift4" => Ok(Box::new(&connector::Shift4)),
_ => Err(report!(errors::UnexpectedError)
.attach_printable(format!("invalid connector name: {connector_name}")))

View File

@ -48,6 +48,7 @@ fn construct_payment_router_data() -> types::PaymentsAuthorizeRouterData {
capture_method: None,
browser_info: None,
order_details: None,
email: None,
},
response: Err(types::ErrorResponse::default()),
payment_method_id: None,

View File

@ -48,6 +48,7 @@ fn construct_payment_router_data() -> types::PaymentsAuthorizeRouterData {
capture_method: None,
browser_info: None,
order_details: None,
email: None,
},
payment_method_id: None,
response: Err(types::ErrorResponse::default()),

View File

@ -45,6 +45,7 @@ fn construct_payment_router_data() -> types::PaymentsAuthorizeRouterData {
capture_method: None,
browser_info: None,
order_details: None,
email: None,
},
response: Err(types::ErrorResponse::default()),
payment_method_id: None,

View File

@ -134,6 +134,7 @@ impl Default for PaymentAuthorizeType {
setup_mandate_details: None,
browser_info: None,
order_details: None,
email: None,
};
Self(data)
}