mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-11-01 11:06:50 +08:00
feat(connector): [Cybersource] Add payout flows for Card (#4511)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
@ -348,6 +348,7 @@ pub enum PayoutConnectors {
|
||||
Wise,
|
||||
Paypal,
|
||||
Ebanx,
|
||||
Cybersource,
|
||||
}
|
||||
|
||||
#[cfg(feature = "payouts")]
|
||||
@ -359,6 +360,7 @@ impl From<PayoutConnectors> for RoutableConnectors {
|
||||
PayoutConnectors::Wise => Self::Wise,
|
||||
PayoutConnectors::Paypal => Self::Paypal,
|
||||
PayoutConnectors::Ebanx => Self::Ebanx,
|
||||
PayoutConnectors::Cybersource => Self::Cybersource,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -372,6 +374,7 @@ impl From<PayoutConnectors> for Connector {
|
||||
PayoutConnectors::Wise => Self::Wise,
|
||||
PayoutConnectors::Paypal => Self::Paypal,
|
||||
PayoutConnectors::Ebanx => Self::Ebanx,
|
||||
PayoutConnectors::Cybersource => Self::Cybersource,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -386,6 +389,7 @@ impl TryFrom<Connector> for PayoutConnectors {
|
||||
Connector::Wise => Ok(Self::Wise),
|
||||
Connector::Paypal => Ok(Self::Paypal),
|
||||
Connector::Ebanx => Ok(Self::Ebanx),
|
||||
Connector::Cybersource => Ok(Self::Cybersource),
|
||||
_ => Err(format!("Invalid payout connector {}", value)),
|
||||
}
|
||||
}
|
||||
|
||||
@ -133,6 +133,8 @@ pub struct ConnectorConfig {
|
||||
pub coinbase: Option<ConnectorTomlConfig>,
|
||||
pub cryptopay: Option<ConnectorTomlConfig>,
|
||||
pub cybersource: Option<ConnectorTomlConfig>,
|
||||
#[cfg(feature = "payouts")]
|
||||
pub cybersource_payout: Option<ConnectorTomlConfig>,
|
||||
pub iatapay: Option<ConnectorTomlConfig>,
|
||||
pub opennode: Option<ConnectorTomlConfig>,
|
||||
pub bambora: Option<ConnectorTomlConfig>,
|
||||
@ -223,6 +225,7 @@ impl ConnectorConfig {
|
||||
PayoutConnectors::Wise => Ok(connector_data.wise_payout),
|
||||
PayoutConnectors::Paypal => Ok(connector_data.paypal_payout),
|
||||
PayoutConnectors::Ebanx => Ok(connector_data.ebanx_payout),
|
||||
PayoutConnectors::Cybersource => Ok(connector_data.cybersource_payout),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -304,6 +304,10 @@ impl api::PaymentsPreProcessing for Cybersource {}
|
||||
impl api::PaymentsCompleteAuthorize for Cybersource {}
|
||||
impl api::ConnectorMandateRevoke for Cybersource {}
|
||||
|
||||
impl api::Payouts for Cybersource {}
|
||||
#[cfg(feature = "payouts")]
|
||||
impl api::PayoutFulfill for Cybersource {}
|
||||
|
||||
impl
|
||||
ConnectorIntegration<
|
||||
api::PaymentMethodToken,
|
||||
@ -965,6 +969,124 @@ impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::P
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "payouts")]
|
||||
impl ConnectorIntegration<api::PoFulfill, types::PayoutsData, types::PayoutsResponseData>
|
||||
for Cybersource
|
||||
{
|
||||
fn get_url(
|
||||
&self,
|
||||
_req: &types::PayoutsRouterData<api::PoFulfill>,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
Ok(format!("{}pts/v2/payouts", self.base_url(connectors)))
|
||||
}
|
||||
|
||||
fn get_headers(
|
||||
&self,
|
||||
req: &types::PayoutsRouterData<api::PoFulfill>,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<Vec<(String, request::Maskable<String>)>, errors::ConnectorError> {
|
||||
self.build_headers(req, connectors)
|
||||
}
|
||||
|
||||
fn get_request_body(
|
||||
&self,
|
||||
req: &types::PayoutsRouterData<api::PoFulfill>,
|
||||
_connectors: &settings::Connectors,
|
||||
) -> CustomResult<RequestContent, errors::ConnectorError> {
|
||||
let connector_router_data = cybersource::CybersourceRouterData::try_from((
|
||||
&self.get_currency_unit(),
|
||||
req.request.destination_currency,
|
||||
req.request.amount,
|
||||
req,
|
||||
))?;
|
||||
let connector_req =
|
||||
cybersource::CybersourcePayoutFulfillRequest::try_from(&connector_router_data)?;
|
||||
Ok(RequestContent::Json(Box::new(connector_req)))
|
||||
}
|
||||
|
||||
fn build_request(
|
||||
&self,
|
||||
req: &types::PayoutsRouterData<api::PoFulfill>,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
|
||||
let request = services::RequestBuilder::new()
|
||||
.method(services::Method::Post)
|
||||
.url(&types::PayoutFulfillType::get_url(self, req, connectors)?)
|
||||
.attach_default_headers()
|
||||
.headers(types::PayoutFulfillType::get_headers(
|
||||
self, req, connectors,
|
||||
)?)
|
||||
.set_body(types::PayoutFulfillType::get_request_body(
|
||||
self, req, connectors,
|
||||
)?)
|
||||
.build();
|
||||
|
||||
Ok(Some(request))
|
||||
}
|
||||
|
||||
fn handle_response(
|
||||
&self,
|
||||
data: &types::PayoutsRouterData<api::PoFulfill>,
|
||||
event_builder: Option<&mut ConnectorEvent>,
|
||||
res: types::Response,
|
||||
) -> CustomResult<types::PayoutsRouterData<api::PoFulfill>, errors::ConnectorError> {
|
||||
let response: cybersource::CybersourceFulfillResponse = res
|
||||
.response
|
||||
.parse_struct("CybersourceFulfillResponse")
|
||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||
|
||||
event_builder.map(|i| i.set_response_body(&response));
|
||||
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: types::Response,
|
||||
event_builder: Option<&mut ConnectorEvent>,
|
||||
) -> CustomResult<types::ErrorResponse, errors::ConnectorError> {
|
||||
self.build_error_response(res, event_builder)
|
||||
}
|
||||
|
||||
fn get_5xx_error_response(
|
||||
&self,
|
||||
res: types::Response,
|
||||
event_builder: Option<&mut ConnectorEvent>,
|
||||
) -> CustomResult<types::ErrorResponse, errors::ConnectorError> {
|
||||
let response: cybersource::CybersourceServerErrorResponse = res
|
||||
.response
|
||||
.parse_struct("CybersourceServerErrorResponse")
|
||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||
|
||||
event_builder.map(|event| event.set_response_body(&response));
|
||||
router_env::logger::info!(error_response=?response);
|
||||
|
||||
let attempt_status = match response.reason {
|
||||
Some(reason) => match reason {
|
||||
transformers::Reason::SystemError => Some(enums::AttemptStatus::Failure),
|
||||
transformers::Reason::ServerTimeout | transformers::Reason::ServiceTimeout => None,
|
||||
},
|
||||
None => None,
|
||||
};
|
||||
Ok(types::ErrorResponse {
|
||||
status_code: res.status_code,
|
||||
reason: response.status.clone(),
|
||||
code: response.status.unwrap_or(consts::NO_ERROR_CODE.to_string()),
|
||||
message: response
|
||||
.message
|
||||
.unwrap_or(consts::NO_ERROR_MESSAGE.to_string()),
|
||||
attempt_status,
|
||||
connector_transaction_id: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl
|
||||
ConnectorIntegration<
|
||||
api::CompleteAuthorize,
|
||||
|
||||
@ -1,4 +1,9 @@
|
||||
use api_models::payments;
|
||||
#[cfg(feature = "payouts")]
|
||||
use api_models::{
|
||||
payments::{AddressDetails, PhoneDetails},
|
||||
payouts::PayoutMethodData,
|
||||
};
|
||||
use base64::Engine;
|
||||
use common_enums::FutureUsage;
|
||||
use common_utils::{ext_traits::ValueExt, pii};
|
||||
@ -111,7 +116,7 @@ impl TryFrom<&types::SetupMandateRouterData> for CybersourceZeroMandateRequest {
|
||||
number: ccard.card_number,
|
||||
expiration_month: ccard.card_exp_month,
|
||||
expiration_year: ccard.card_exp_year,
|
||||
security_code: ccard.card_cvc,
|
||||
security_code: Some(ccard.card_cvc),
|
||||
card_type,
|
||||
},
|
||||
}),
|
||||
@ -404,7 +409,7 @@ pub struct Card {
|
||||
number: cards::CardNumber,
|
||||
expiration_month: Secret<String>,
|
||||
expiration_year: Secret<String>,
|
||||
security_code: Secret<String>,
|
||||
security_code: Option<Secret<String>>,
|
||||
#[serde(rename = "type")]
|
||||
card_type: Option<String>,
|
||||
}
|
||||
@ -849,7 +854,7 @@ impl
|
||||
number: ccard.card_number,
|
||||
expiration_month: ccard.card_exp_month,
|
||||
expiration_year: ccard.card_exp_year,
|
||||
security_code: ccard.card_cvc,
|
||||
security_code: Some(ccard.card_cvc),
|
||||
card_type: card_type.clone(),
|
||||
},
|
||||
});
|
||||
@ -900,7 +905,7 @@ impl
|
||||
number: ccard.card_number,
|
||||
expiration_month: ccard.card_exp_month,
|
||||
expiration_year: ccard.card_exp_year,
|
||||
security_code: ccard.card_cvc,
|
||||
security_code: Some(ccard.card_cvc),
|
||||
card_type,
|
||||
},
|
||||
});
|
||||
@ -1278,7 +1283,7 @@ impl TryFrom<&CybersourceRouterData<&types::PaymentsAuthorizeRouterData>>
|
||||
number: ccard.card_number,
|
||||
expiration_month: ccard.card_exp_month,
|
||||
expiration_year: ccard.card_exp_year,
|
||||
security_code: ccard.card_cvc,
|
||||
security_code: Some(ccard.card_cvc),
|
||||
card_type,
|
||||
},
|
||||
});
|
||||
@ -1982,7 +1987,7 @@ impl TryFrom<&CybersourceRouterData<&types::PaymentsPreProcessingRouterData>>
|
||||
number: ccard.card_number,
|
||||
expiration_month: ccard.card_exp_month,
|
||||
expiration_year: ccard.card_exp_year,
|
||||
security_code: ccard.card_cvc,
|
||||
security_code: Some(ccard.card_cvc),
|
||||
card_type,
|
||||
},
|
||||
}))
|
||||
@ -2775,6 +2780,223 @@ impl TryFrom<types::RefundsResponseRouterData<api::RSync, CybersourceRsyncRespon
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "payouts")]
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CybersourcePayoutFulfillRequest {
|
||||
client_reference_information: ClientReferenceInformation,
|
||||
order_information: OrderInformation,
|
||||
recipient_information: CybersourceRecipientInfo,
|
||||
sender_information: CybersourceSenderInfo,
|
||||
processing_information: CybersourceProcessingInfo,
|
||||
payment_information: PaymentInformation,
|
||||
}
|
||||
|
||||
#[cfg(feature = "payouts")]
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CybersourceRecipientInfo {
|
||||
first_name: Secret<String>,
|
||||
last_name: Secret<String>,
|
||||
address1: Secret<String>,
|
||||
locality: String,
|
||||
administrative_area: Secret<String>,
|
||||
postal_code: Secret<String>,
|
||||
country: api_enums::CountryAlpha2,
|
||||
phone_number: Option<Secret<String>>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "payouts")]
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CybersourceSenderInfo {
|
||||
reference_number: String,
|
||||
account: CybersourceAccountInfo,
|
||||
}
|
||||
|
||||
#[cfg(feature = "payouts")]
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CybersourceAccountInfo {
|
||||
funds_source: CybersourcePayoutFundSourceType,
|
||||
}
|
||||
|
||||
#[cfg(feature = "payouts")]
|
||||
#[derive(Debug, Serialize)]
|
||||
pub enum CybersourcePayoutFundSourceType {
|
||||
#[serde(rename = "05")]
|
||||
Disbursement,
|
||||
}
|
||||
|
||||
#[cfg(feature = "payouts")]
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CybersourceProcessingInfo {
|
||||
business_application_id: CybersourcePayoutBusinessType,
|
||||
}
|
||||
|
||||
#[cfg(feature = "payouts")]
|
||||
#[derive(Debug, Serialize)]
|
||||
pub enum CybersourcePayoutBusinessType {
|
||||
#[serde(rename = "PP")]
|
||||
PersonToPerson,
|
||||
#[serde(rename = "AA")]
|
||||
AccountToAccount,
|
||||
}
|
||||
|
||||
#[cfg(feature = "payouts")]
|
||||
impl TryFrom<&CybersourceRouterData<&types::PayoutsRouterData<api::PoFulfill>>>
|
||||
for CybersourcePayoutFulfillRequest
|
||||
{
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(
|
||||
item: &CybersourceRouterData<&types::PayoutsRouterData<api::PoFulfill>>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
match item.router_data.request.payout_type {
|
||||
enums::PayoutType::Card => {
|
||||
let client_reference_information = ClientReferenceInformation {
|
||||
code: Some(item.router_data.request.payout_id.clone()),
|
||||
};
|
||||
|
||||
let order_information = OrderInformation {
|
||||
amount_details: Amount {
|
||||
total_amount: item.amount.to_owned(),
|
||||
currency: item.router_data.request.destination_currency,
|
||||
},
|
||||
};
|
||||
|
||||
let billing_address = item.router_data.get_billing_address()?;
|
||||
let phone_address = item.router_data.get_billing_phone()?;
|
||||
let recipient_information =
|
||||
CybersourceRecipientInfo::try_from((billing_address, phone_address))?;
|
||||
|
||||
let sender_information = CybersourceSenderInfo {
|
||||
reference_number: item.router_data.request.payout_id.clone(),
|
||||
account: CybersourceAccountInfo {
|
||||
funds_source: CybersourcePayoutFundSourceType::Disbursement,
|
||||
},
|
||||
};
|
||||
|
||||
let processing_information = CybersourceProcessingInfo {
|
||||
business_application_id: CybersourcePayoutBusinessType::PersonToPerson, // this means sender and receiver are different
|
||||
};
|
||||
|
||||
let payout_method_data = item.router_data.get_payout_method_data()?;
|
||||
let payment_information = PaymentInformation::try_from(payout_method_data)?;
|
||||
|
||||
Ok(Self {
|
||||
client_reference_information,
|
||||
order_information,
|
||||
recipient_information,
|
||||
sender_information,
|
||||
processing_information,
|
||||
payment_information,
|
||||
})
|
||||
}
|
||||
enums::PayoutType::Bank | enums::PayoutType::Wallet => {
|
||||
Err(errors::ConnectorError::NotSupported {
|
||||
message: "PayoutType is not supported".to_string(),
|
||||
connector: "Cybersource",
|
||||
})?
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "payouts")]
|
||||
impl TryFrom<(&AddressDetails, &PhoneDetails)> for CybersourceRecipientInfo {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(item: (&AddressDetails, &PhoneDetails)) -> Result<Self, Self::Error> {
|
||||
let (billing_address, phone_address) = item;
|
||||
Ok(Self {
|
||||
first_name: billing_address.get_first_name()?.to_owned(),
|
||||
last_name: billing_address.get_last_name()?.to_owned(),
|
||||
address1: billing_address.get_line1()?.to_owned(),
|
||||
locality: billing_address.get_city()?.to_owned(),
|
||||
administrative_area: billing_address.get_state()?.to_owned(),
|
||||
postal_code: billing_address.get_zip()?.to_owned(),
|
||||
country: billing_address.get_country()?.to_owned(),
|
||||
phone_number: phone_address.number.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "payouts")]
|
||||
impl TryFrom<PayoutMethodData> for PaymentInformation {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(item: PayoutMethodData) -> Result<Self, Self::Error> {
|
||||
match item {
|
||||
PayoutMethodData::Card(card_details) => {
|
||||
let card_issuer = card_details.get_card_issuer().ok();
|
||||
let card_type = card_issuer.map(String::from);
|
||||
let card = Card {
|
||||
number: card_details.card_number,
|
||||
expiration_month: card_details.expiry_month,
|
||||
expiration_year: card_details.expiry_year,
|
||||
security_code: None,
|
||||
card_type,
|
||||
};
|
||||
Ok(Self::Cards(CardPaymentInformation { card }))
|
||||
}
|
||||
PayoutMethodData::Bank(_) | PayoutMethodData::Wallet(_) => {
|
||||
Err(errors::ConnectorError::NotSupported {
|
||||
message: "PayoutMethod is not supported".to_string(),
|
||||
connector: "Cybersource",
|
||||
})?
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "payouts")]
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CybersourceFulfillResponse {
|
||||
id: String,
|
||||
status: CybersourcePayoutStatus,
|
||||
}
|
||||
|
||||
#[cfg(feature = "payouts")]
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||
pub enum CybersourcePayoutStatus {
|
||||
Accepted,
|
||||
Declined,
|
||||
InvalidRequest,
|
||||
}
|
||||
|
||||
#[cfg(feature = "payouts")]
|
||||
impl ForeignFrom<CybersourcePayoutStatus> for enums::PayoutStatus {
|
||||
fn foreign_from(status: CybersourcePayoutStatus) -> Self {
|
||||
match status {
|
||||
CybersourcePayoutStatus::Accepted => Self::Success,
|
||||
CybersourcePayoutStatus::Declined | CybersourcePayoutStatus::InvalidRequest => {
|
||||
Self::Failed
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "payouts")]
|
||||
impl<F> TryFrom<types::PayoutsResponseRouterData<F, CybersourceFulfillResponse>>
|
||||
for types::PayoutsRouterData<F>
|
||||
{
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(
|
||||
item: types::PayoutsResponseRouterData<F, CybersourceFulfillResponse>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
response: Ok(types::PayoutsResponseData {
|
||||
status: Some(enums::PayoutStatus::foreign_from(item.response.status)),
|
||||
connector_payout_id: item.response.id,
|
||||
payout_eligible: None,
|
||||
should_add_next_step_to_process_tracker: false,
|
||||
}),
|
||||
..item.data
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CybersourceStandardErrorResponse {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[cfg(feature = "payouts")]
|
||||
use api_models::payouts::PayoutVendorAccountDetails;
|
||||
use api_models::payouts::{self, PayoutVendorAccountDetails};
|
||||
use api_models::{
|
||||
enums::{CanadaStatesAbbreviation, UsStatesAbbreviation},
|
||||
payments::{self, OrderDetailsWithAmount},
|
||||
@ -1078,6 +1078,80 @@ pub trait CardData {
|
||||
fn get_expiry_year_as_i32(&self) -> Result<Secret<i32>, Error>;
|
||||
}
|
||||
|
||||
#[cfg(feature = "payouts")]
|
||||
impl CardData for payouts::Card {
|
||||
fn get_card_expiry_year_2_digit(&self) -> Result<Secret<String>, errors::ConnectorError> {
|
||||
let binding = self.expiry_year.clone();
|
||||
let year = binding.peek();
|
||||
Ok(Secret::new(
|
||||
year.get(year.len() - 2..)
|
||||
.ok_or(errors::ConnectorError::RequestEncodingFailed)?
|
||||
.to_string(),
|
||||
))
|
||||
}
|
||||
fn get_card_issuer(&self) -> Result<CardIssuer, Error> {
|
||||
get_card_issuer(self.card_number.peek())
|
||||
}
|
||||
fn get_card_expiry_month_year_2_digit_with_delimiter(
|
||||
&self,
|
||||
delimiter: String,
|
||||
) -> Result<Secret<String>, errors::ConnectorError> {
|
||||
let year = self.get_card_expiry_year_2_digit()?;
|
||||
Ok(Secret::new(format!(
|
||||
"{}{}{}",
|
||||
self.expiry_month.peek(),
|
||||
delimiter,
|
||||
year.peek()
|
||||
)))
|
||||
}
|
||||
fn get_expiry_date_as_yyyymm(&self, delimiter: &str) -> Secret<String> {
|
||||
let year = self.get_expiry_year_4_digit();
|
||||
Secret::new(format!(
|
||||
"{}{}{}",
|
||||
year.peek(),
|
||||
delimiter,
|
||||
self.expiry_month.peek()
|
||||
))
|
||||
}
|
||||
fn get_expiry_date_as_mmyyyy(&self, delimiter: &str) -> Secret<String> {
|
||||
let year = self.get_expiry_year_4_digit();
|
||||
Secret::new(format!(
|
||||
"{}{}{}",
|
||||
self.expiry_month.peek(),
|
||||
delimiter,
|
||||
year.peek()
|
||||
))
|
||||
}
|
||||
fn get_expiry_year_4_digit(&self) -> Secret<String> {
|
||||
let mut year = self.expiry_year.peek().clone();
|
||||
if year.len() == 2 {
|
||||
year = format!("20{}", year);
|
||||
}
|
||||
Secret::new(year)
|
||||
}
|
||||
fn get_expiry_date_as_yymm(&self) -> Result<Secret<String>, errors::ConnectorError> {
|
||||
let year = self.get_card_expiry_year_2_digit()?.expose();
|
||||
let month = self.expiry_month.clone().expose();
|
||||
Ok(Secret::new(format!("{year}{month}")))
|
||||
}
|
||||
fn get_expiry_month_as_i8(&self) -> Result<Secret<i8>, Error> {
|
||||
self.expiry_month
|
||||
.peek()
|
||||
.clone()
|
||||
.parse::<i8>()
|
||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)
|
||||
.map(Secret::new)
|
||||
}
|
||||
fn get_expiry_year_as_i32(&self) -> Result<Secret<i32>, Error> {
|
||||
self.expiry_year
|
||||
.peek()
|
||||
.clone()
|
||||
.parse::<i32>()
|
||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)
|
||||
.map(Secret::new)
|
||||
}
|
||||
}
|
||||
|
||||
impl CardData for domain::Card {
|
||||
fn get_card_expiry_year_2_digit(&self) -> Result<Secret<String>, errors::ConnectorError> {
|
||||
let binding = self.card_exp_year.clone();
|
||||
|
||||
@ -975,7 +975,6 @@ default_imp_for_payouts!(
|
||||
connector::Cashtocode,
|
||||
connector::Checkout,
|
||||
connector::Cryptopay,
|
||||
connector::Cybersource,
|
||||
connector::Coinbase,
|
||||
connector::Dlocal,
|
||||
connector::Fiserv,
|
||||
@ -1232,7 +1231,6 @@ default_imp_for_payouts_fulfill!(
|
||||
connector::Cashtocode,
|
||||
connector::Checkout,
|
||||
connector::Cryptopay,
|
||||
connector::Cybersource,
|
||||
connector::Coinbase,
|
||||
connector::Dlocal,
|
||||
connector::Fiserv,
|
||||
|
||||
@ -15721,7 +15721,8 @@
|
||||
"stripe",
|
||||
"wise",
|
||||
"paypal",
|
||||
"ebanx"
|
||||
"ebanx",
|
||||
"cybersource"
|
||||
]
|
||||
},
|
||||
"PayoutCreateRequest": {
|
||||
|
||||
Reference in New Issue
Block a user