Skip to content

Commit a72f040

Browse files
feat(connector): [Cybersource] Add payout flows for Card (#4511)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
1 parent df2c2ca commit a72f040

File tree

7 files changed

+434
-10
lines changed

7 files changed

+434
-10
lines changed

crates/api_models/src/enums.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,7 @@ pub enum PayoutConnectors {
348348
Wise,
349349
Paypal,
350350
Ebanx,
351+
Cybersource,
351352
}
352353

353354
#[cfg(feature = "payouts")]
@@ -359,6 +360,7 @@ impl From<PayoutConnectors> for RoutableConnectors {
359360
PayoutConnectors::Wise => Self::Wise,
360361
PayoutConnectors::Paypal => Self::Paypal,
361362
PayoutConnectors::Ebanx => Self::Ebanx,
363+
PayoutConnectors::Cybersource => Self::Cybersource,
362364
}
363365
}
364366
}
@@ -372,6 +374,7 @@ impl From<PayoutConnectors> for Connector {
372374
PayoutConnectors::Wise => Self::Wise,
373375
PayoutConnectors::Paypal => Self::Paypal,
374376
PayoutConnectors::Ebanx => Self::Ebanx,
377+
PayoutConnectors::Cybersource => Self::Cybersource,
375378
}
376379
}
377380
}
@@ -386,6 +389,7 @@ impl TryFrom<Connector> for PayoutConnectors {
386389
Connector::Wise => Ok(Self::Wise),
387390
Connector::Paypal => Ok(Self::Paypal),
388391
Connector::Ebanx => Ok(Self::Ebanx),
392+
Connector::Cybersource => Ok(Self::Cybersource),
389393
_ => Err(format!("Invalid payout connector {}", value)),
390394
}
391395
}

crates/connector_configs/src/connector.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,8 @@ pub struct ConnectorConfig {
133133
pub coinbase: Option<ConnectorTomlConfig>,
134134
pub cryptopay: Option<ConnectorTomlConfig>,
135135
pub cybersource: Option<ConnectorTomlConfig>,
136+
#[cfg(feature = "payouts")]
137+
pub cybersource_payout: Option<ConnectorTomlConfig>,
136138
pub iatapay: Option<ConnectorTomlConfig>,
137139
pub opennode: Option<ConnectorTomlConfig>,
138140
pub bambora: Option<ConnectorTomlConfig>,
@@ -223,6 +225,7 @@ impl ConnectorConfig {
223225
PayoutConnectors::Wise => Ok(connector_data.wise_payout),
224226
PayoutConnectors::Paypal => Ok(connector_data.paypal_payout),
225227
PayoutConnectors::Ebanx => Ok(connector_data.ebanx_payout),
228+
PayoutConnectors::Cybersource => Ok(connector_data.cybersource_payout),
226229
}
227230
}
228231

crates/router/src/connector/cybersource.rs

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,10 @@ impl api::PaymentsPreProcessing for Cybersource {}
304304
impl api::PaymentsCompleteAuthorize for Cybersource {}
305305
impl api::ConnectorMandateRevoke for Cybersource {}
306306

307+
impl api::Payouts for Cybersource {}
308+
#[cfg(feature = "payouts")]
309+
impl api::PayoutFulfill for Cybersource {}
310+
307311
impl
308312
ConnectorIntegration<
309313
api::PaymentMethodToken,
@@ -965,6 +969,124 @@ impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::P
965969
}
966970
}
967971

972+
#[cfg(feature = "payouts")]
973+
impl ConnectorIntegration<api::PoFulfill, types::PayoutsData, types::PayoutsResponseData>
974+
for Cybersource
975+
{
976+
fn get_url(
977+
&self,
978+
_req: &types::PayoutsRouterData<api::PoFulfill>,
979+
connectors: &settings::Connectors,
980+
) -> CustomResult<String, errors::ConnectorError> {
981+
Ok(format!("{}pts/v2/payouts", self.base_url(connectors)))
982+
}
983+
984+
fn get_headers(
985+
&self,
986+
req: &types::PayoutsRouterData<api::PoFulfill>,
987+
connectors: &settings::Connectors,
988+
) -> CustomResult<Vec<(String, request::Maskable<String>)>, errors::ConnectorError> {
989+
self.build_headers(req, connectors)
990+
}
991+
992+
fn get_request_body(
993+
&self,
994+
req: &types::PayoutsRouterData<api::PoFulfill>,
995+
_connectors: &settings::Connectors,
996+
) -> CustomResult<RequestContent, errors::ConnectorError> {
997+
let connector_router_data = cybersource::CybersourceRouterData::try_from((
998+
&self.get_currency_unit(),
999+
req.request.destination_currency,
1000+
req.request.amount,
1001+
req,
1002+
))?;
1003+
let connector_req =
1004+
cybersource::CybersourcePayoutFulfillRequest::try_from(&connector_router_data)?;
1005+
Ok(RequestContent::Json(Box::new(connector_req)))
1006+
}
1007+
1008+
fn build_request(
1009+
&self,
1010+
req: &types::PayoutsRouterData<api::PoFulfill>,
1011+
connectors: &settings::Connectors,
1012+
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
1013+
let request = services::RequestBuilder::new()
1014+
.method(services::Method::Post)
1015+
.url(&types::PayoutFulfillType::get_url(self, req, connectors)?)
1016+
.attach_default_headers()
1017+
.headers(types::PayoutFulfillType::get_headers(
1018+
self, req, connectors,
1019+
)?)
1020+
.set_body(types::PayoutFulfillType::get_request_body(
1021+
self, req, connectors,
1022+
)?)
1023+
.build();
1024+
1025+
Ok(Some(request))
1026+
}
1027+
1028+
fn handle_response(
1029+
&self,
1030+
data: &types::PayoutsRouterData<api::PoFulfill>,
1031+
event_builder: Option<&mut ConnectorEvent>,
1032+
res: types::Response,
1033+
) -> CustomResult<types::PayoutsRouterData<api::PoFulfill>, errors::ConnectorError> {
1034+
let response: cybersource::CybersourceFulfillResponse = res
1035+
.response
1036+
.parse_struct("CybersourceFulfillResponse")
1037+
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
1038+
1039+
event_builder.map(|i| i.set_response_body(&response));
1040+
router_env::logger::info!(connector_response=?response);
1041+
1042+
types::RouterData::try_from(types::ResponseRouterData {
1043+
response,
1044+
data: data.clone(),
1045+
http_code: res.status_code,
1046+
})
1047+
}
1048+
1049+
fn get_error_response(
1050+
&self,
1051+
res: types::Response,
1052+
event_builder: Option<&mut ConnectorEvent>,
1053+
) -> CustomResult<types::ErrorResponse, errors::ConnectorError> {
1054+
self.build_error_response(res, event_builder)
1055+
}
1056+
1057+
fn get_5xx_error_response(
1058+
&self,
1059+
res: types::Response,
1060+
event_builder: Option<&mut ConnectorEvent>,
1061+
) -> CustomResult<types::ErrorResponse, errors::ConnectorError> {
1062+
let response: cybersource::CybersourceServerErrorResponse = res
1063+
.response
1064+
.parse_struct("CybersourceServerErrorResponse")
1065+
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
1066+
1067+
event_builder.map(|event| event.set_response_body(&response));
1068+
router_env::logger::info!(error_response=?response);
1069+
1070+
let attempt_status = match response.reason {
1071+
Some(reason) => match reason {
1072+
transformers::Reason::SystemError => Some(enums::AttemptStatus::Failure),
1073+
transformers::Reason::ServerTimeout | transformers::Reason::ServiceTimeout => None,
1074+
},
1075+
None => None,
1076+
};
1077+
Ok(types::ErrorResponse {
1078+
status_code: res.status_code,
1079+
reason: response.status.clone(),
1080+
code: response.status.unwrap_or(consts::NO_ERROR_CODE.to_string()),
1081+
message: response
1082+
.message
1083+
.unwrap_or(consts::NO_ERROR_MESSAGE.to_string()),
1084+
attempt_status,
1085+
connector_transaction_id: None,
1086+
})
1087+
}
1088+
}
1089+
9681090
impl
9691091
ConnectorIntegration<
9701092
api::CompleteAuthorize,

0 commit comments

Comments
 (0)