mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 00:49:42 +08:00
feat(connector): add support for capture, void, psync, refund and rsync for cybersource (#381)
This commit is contained in:
@ -14,8 +14,12 @@ use crate::{
|
||||
configs::settings,
|
||||
consts,
|
||||
core::errors::{self, CustomResult},
|
||||
headers, logger, services,
|
||||
types::{self, api},
|
||||
headers, logger,
|
||||
services::{self, ConnectorIntegration},
|
||||
types::{
|
||||
self,
|
||||
api::{self, ConnectorCommon, ConnectorCommonExt},
|
||||
},
|
||||
utils::{self, BytesExt},
|
||||
};
|
||||
|
||||
@ -33,23 +37,25 @@ impl Cybersource {
|
||||
auth: cybersource::CybersourceAuthType,
|
||||
host: String,
|
||||
resource: &str,
|
||||
payload: &str,
|
||||
payload: &String,
|
||||
date: OffsetDateTime,
|
||||
http_method: services::Method,
|
||||
) -> 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 is_post_method = matches!(http_method, services::Method::Post);
|
||||
let digest_str = if is_post_method { "digest " } else { "" };
|
||||
let headers = format!("host date (request-target) {digest_str}v-c-merchant-id");
|
||||
let request_target = if is_post_method {
|
||||
format!("(request-target): post {resource}\ndigest: SHA-256={payload}\n")
|
||||
} else {
|
||||
format!("(request-target): get {resource}\n")
|
||||
};
|
||||
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())
|
||||
"host: {host}\ndate: {date}\n{request_target}v-c-merchant-id: {merchant_account}"
|
||||
);
|
||||
let key_value = consts::BASE64_ENGINE
|
||||
.decode(api_secret)
|
||||
@ -59,25 +65,97 @@ impl Cybersource {
|
||||
let signature_value =
|
||||
consts::BASE64_ENGINE.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}""#
|
||||
r#"keyid="{api_key}", algorithm="HmacSHA256", headers="{headers}", signature="{signature_value}""#
|
||||
);
|
||||
|
||||
Ok(signature_header)
|
||||
}
|
||||
}
|
||||
|
||||
impl api::ConnectorCommon for Cybersource {
|
||||
impl ConnectorCommon for Cybersource {
|
||||
fn id(&self) -> &'static str {
|
||||
"cybersource"
|
||||
}
|
||||
|
||||
fn common_get_content_type(&self) -> &'static str {
|
||||
"application/json"
|
||||
"application/json;charset=utf-8"
|
||||
}
|
||||
|
||||
fn base_url<'a>(&self, connectors: &'a settings::Connectors) -> &'a str {
|
||||
connectors.cybersource.base_url.as_ref()
|
||||
}
|
||||
|
||||
fn build_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.details.to_string(),
|
||||
reason: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<Flow, Request, Response> ConnectorCommonExt<Flow, Request, Response> for Cybersource
|
||||
where
|
||||
Self: ConnectorIntegration<Flow, Request, Response>,
|
||||
{
|
||||
fn build_headers(
|
||||
&self,
|
||||
req: &types::RouterData<Flow, Request, Response>,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<Vec<(String, String)>, errors::ConnectorError> {
|
||||
let date = OffsetDateTime::now_utc();
|
||||
let cybersource_req = self.get_request_body(req)?;
|
||||
let auth = cybersource::CybersourceAuthType::try_from(&req.connector_auth_type)?;
|
||||
let merchant_account = auth.merchant_account.clone();
|
||||
let base_url = connectors.cybersource.base_url.as_str();
|
||||
let cybersource_host = Url::parse(base_url)
|
||||
.into_report()
|
||||
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
|
||||
let host = cybersource_host
|
||||
.host_str()
|
||||
.ok_or(errors::ConnectorError::RequestEncodingFailed)?;
|
||||
let path: String = self
|
||||
.get_url(req, connectors)?
|
||||
.chars()
|
||||
.skip(base_url.len() - 1)
|
||||
.collect();
|
||||
let sha256 =
|
||||
self.generate_digest(cybersource_req.map_or("{}".to_string(), |s| s).as_bytes());
|
||||
let http_method = self.get_http_method();
|
||||
let signature = self.generate_signature(
|
||||
auth,
|
||||
host.to_string(),
|
||||
path.as_str(),
|
||||
&sha256,
|
||||
date,
|
||||
http_method,
|
||||
)?;
|
||||
|
||||
let mut headers = vec![
|
||||
(
|
||||
headers::CONTENT_TYPE.to_string(),
|
||||
self.get_content_type().to_string(),
|
||||
),
|
||||
(
|
||||
headers::ACCEPT.to_string(),
|
||||
"application/hal+json;charset=utf-8".to_string(),
|
||||
),
|
||||
("v-c-merchant-id".to_string(), merchant_account),
|
||||
("Date".to_string(), date.to_string()),
|
||||
("Host".to_string(), host.to_string()),
|
||||
("Signature".to_string(), signature),
|
||||
];
|
||||
if matches!(http_method, services::Method::Post | services::Method::Put) {
|
||||
headers.push(("Digest".to_string(), format!("SHA-256={}", sha256)));
|
||||
}
|
||||
Ok(headers)
|
||||
}
|
||||
}
|
||||
|
||||
impl api::Payment for Cybersource {}
|
||||
@ -87,68 +165,197 @@ 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 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>
|
||||
impl ConnectorIntegration<api::Session, types::PaymentsSessionData, types::PaymentsResponseData>
|
||||
for Cybersource
|
||||
{
|
||||
}
|
||||
|
||||
impl
|
||||
services::ConnectorIntegration<
|
||||
api::Authorize,
|
||||
types::PaymentsAuthorizeData,
|
||||
types::PaymentsResponseData,
|
||||
> for Cybersource
|
||||
impl ConnectorIntegration<api::Capture, types::PaymentsCaptureData, types::PaymentsResponseData>
|
||||
for Cybersource
|
||||
{
|
||||
fn get_headers(
|
||||
&self,
|
||||
_req: &types::PaymentsAuthorizeRouterData,
|
||||
_connectors: &settings::Connectors,
|
||||
req: &types::PaymentsCaptureRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> 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)
|
||||
self.build_headers(req, connectors)
|
||||
}
|
||||
|
||||
fn get_content_type(&self) -> &'static str {
|
||||
"application/json;charset=utf-8"
|
||||
self.common_get_content_type()
|
||||
}
|
||||
|
||||
fn get_url(
|
||||
&self,
|
||||
req: &types::PaymentsCaptureRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
let connector_payment_id = req.request.connector_transaction_id.clone();
|
||||
Ok(format!(
|
||||
"{}pts/v2/payments/{}/captures",
|
||||
self.base_url(connectors),
|
||||
connector_payment_id
|
||||
))
|
||||
}
|
||||
|
||||
fn get_request_body(
|
||||
&self,
|
||||
req: &types::PaymentsCaptureRouterData,
|
||||
) -> CustomResult<Option<String>, errors::ConnectorError> {
|
||||
let req_obj = cybersource::CybersourcePaymentsRequest::try_from(req)?;
|
||||
let req =
|
||||
utils::Encode::<cybersource::CybersourcePaymentsRequest>::encode_to_string_of_json(
|
||||
&req_obj,
|
||||
)
|
||||
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
|
||||
Ok(Some(req))
|
||||
}
|
||||
fn build_request(
|
||||
&self,
|
||||
req: &types::PaymentsCaptureRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
|
||||
Ok(Some(
|
||||
services::RequestBuilder::new()
|
||||
.method(services::Method::Post)
|
||||
.url(&types::PaymentsCaptureType::get_url(self, req, connectors)?)
|
||||
.headers(types::PaymentsCaptureType::get_headers(
|
||||
self, req, connectors,
|
||||
)?)
|
||||
.body(types::PaymentsCaptureType::get_request_body(self, req)?)
|
||||
.build(),
|
||||
))
|
||||
}
|
||||
fn handle_response(
|
||||
&self,
|
||||
data: &types::PaymentsCaptureRouterData,
|
||||
res: types::Response,
|
||||
) -> CustomResult<
|
||||
types::RouterData<api::Capture, types::PaymentsCaptureData, types::PaymentsResponseData>,
|
||||
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> {
|
||||
self.build_error_response(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl ConnectorIntegration<api::PSync, types::PaymentsSyncData, types::PaymentsResponseData>
|
||||
for Cybersource
|
||||
{
|
||||
fn get_headers(
|
||||
&self,
|
||||
req: &types::PaymentsSyncRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<Vec<(String, String)>, errors::ConnectorError> {
|
||||
self.build_headers(req, connectors)
|
||||
}
|
||||
|
||||
fn get_http_method(&self) -> services::Method {
|
||||
services::Method::Get
|
||||
}
|
||||
|
||||
fn get_url(
|
||||
&self,
|
||||
req: &types::PaymentsSyncRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
let connector_payment_id = req
|
||||
.request
|
||||
.connector_transaction_id
|
||||
.get_connector_transaction_id()
|
||||
.change_context(errors::ConnectorError::MissingConnectorTransactionID)?;
|
||||
Ok(format!(
|
||||
"{}tss/v2/transactions/{}",
|
||||
self.base_url(connectors),
|
||||
connector_payment_id
|
||||
))
|
||||
}
|
||||
|
||||
fn get_content_type(&self) -> &'static str {
|
||||
self.common_get_content_type()
|
||||
}
|
||||
|
||||
fn get_request_body(
|
||||
&self,
|
||||
_req: &types::PaymentsSyncRouterData,
|
||||
) -> CustomResult<Option<String>, errors::ConnectorError> {
|
||||
Ok(Some("{}".to_string()))
|
||||
}
|
||||
fn build_request(
|
||||
&self,
|
||||
req: &types::PaymentsSyncRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
|
||||
Ok(Some(
|
||||
services::RequestBuilder::new()
|
||||
.method(services::Method::Get)
|
||||
.url(&types::PaymentsSyncType::get_url(self, req, connectors)?)
|
||||
.headers(types::PaymentsSyncType::get_headers(self, req, connectors)?)
|
||||
.build(),
|
||||
))
|
||||
}
|
||||
fn handle_response(
|
||||
&self,
|
||||
data: &types::PaymentsSyncRouterData,
|
||||
res: types::Response,
|
||||
) -> CustomResult<types::PaymentsSyncRouterData, errors::ConnectorError> {
|
||||
let response: cybersource::CybersourceTransactionResponse = res
|
||||
.response
|
||||
.parse_struct("Cybersource PaymentSyncResponse")
|
||||
.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> {
|
||||
self.build_error_response(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::PaymentsResponseData>
|
||||
for Cybersource
|
||||
{
|
||||
fn get_headers(
|
||||
&self,
|
||||
req: &types::PaymentsAuthorizeRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<Vec<(String, String)>, errors::ConnectorError> {
|
||||
self.build_headers(req, connectors)
|
||||
}
|
||||
|
||||
fn get_content_type(&self) -> &'static str {
|
||||
self.common_get_content_type()
|
||||
}
|
||||
|
||||
fn get_url(
|
||||
@ -162,61 +369,36 @@ impl
|
||||
))
|
||||
}
|
||||
|
||||
fn get_request_body(
|
||||
&self,
|
||||
req: &types::PaymentsAuthorizeRouterData,
|
||||
) -> CustomResult<Option<String>, errors::ConnectorError> {
|
||||
let req_obj = cybersource::CybersourcePaymentsRequest::try_from(req)?;
|
||||
let cybersource_req =
|
||||
utils::Encode::<cybersource::CybersourcePaymentsRequest>::encode_to_string_of_json(
|
||||
&req_obj,
|
||||
)
|
||||
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
|
||||
Ok(Some(cybersource_req))
|
||||
}
|
||||
|
||||
fn build_request(
|
||||
&self,
|
||||
req: &types::PaymentsAuthorizeRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
|
||||
let date = OffsetDateTime::now_utc();
|
||||
let request = services::RequestBuilder::new()
|
||||
.method(services::Method::Post)
|
||||
.url(&types::PaymentsAuthorizeType::get_url(
|
||||
self, req, connectors,
|
||||
)?)
|
||||
.headers(types::PaymentsAuthorizeType::get_headers(
|
||||
self, req, connectors,
|
||||
)?)
|
||||
.body(self.get_request_body(req)?)
|
||||
.build();
|
||||
|
||||
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, connectors,
|
||||
)?)
|
||||
.body(Some(cybersource_req))
|
||||
.build();
|
||||
|
||||
Ok(Some(request))
|
||||
}
|
||||
None => Err(errors::ConnectorError::RequestEncodingFailed.into()),
|
||||
}
|
||||
Ok(Some(request))
|
||||
}
|
||||
|
||||
fn handle_response(
|
||||
@ -242,26 +424,85 @@ impl
|
||||
&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,
|
||||
})
|
||||
self.build_error_response(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl
|
||||
services::ConnectorIntegration<
|
||||
api::Void,
|
||||
types::PaymentsCancelData,
|
||||
types::PaymentsResponseData,
|
||||
> for Cybersource
|
||||
impl ConnectorIntegration<api::Void, types::PaymentsCancelData, types::PaymentsResponseData>
|
||||
for Cybersource
|
||||
{
|
||||
fn get_headers(
|
||||
&self,
|
||||
req: &types::PaymentsCancelRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<Vec<(String, String)>, errors::ConnectorError> {
|
||||
self.build_headers(req, connectors)
|
||||
}
|
||||
|
||||
fn get_url(
|
||||
&self,
|
||||
req: &types::PaymentsCancelRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
let connector_payment_id = req.request.connector_transaction_id.clone();
|
||||
Ok(format!(
|
||||
"{}pts/v2/payments/{}/voids",
|
||||
self.base_url(connectors),
|
||||
connector_payment_id
|
||||
))
|
||||
}
|
||||
|
||||
fn get_content_type(&self) -> &'static str {
|
||||
self.common_get_content_type()
|
||||
}
|
||||
|
||||
fn get_request_body(
|
||||
&self,
|
||||
_req: &types::PaymentsCancelRouterData,
|
||||
) -> CustomResult<Option<String>, errors::ConnectorError> {
|
||||
Ok(Some("{}".to_string()))
|
||||
}
|
||||
|
||||
fn build_request(
|
||||
&self,
|
||||
req: &types::PaymentsCancelRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
|
||||
Ok(Some(
|
||||
services::RequestBuilder::new()
|
||||
.method(services::Method::Post)
|
||||
.url(&types::PaymentsVoidType::get_url(self, req, connectors)?)
|
||||
.headers(types::PaymentsVoidType::get_headers(self, req, connectors)?)
|
||||
.body(self.get_request_body(req)?)
|
||||
.build(),
|
||||
))
|
||||
}
|
||||
|
||||
fn handle_response(
|
||||
&self,
|
||||
data: &types::PaymentsCancelRouterData,
|
||||
res: types::Response,
|
||||
) -> CustomResult<types::PaymentsCancelRouterData, 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> {
|
||||
self.build_error_response(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl api::Refund for Cybersource {}
|
||||
@ -269,15 +510,153 @@ impl api::RefundExecute for Cybersource {}
|
||||
impl api::RefundSync for Cybersource {}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl services::ConnectorIntegration<api::Execute, types::RefundsData, types::RefundsResponseData>
|
||||
impl ConnectorIntegration<api::Execute, types::RefundsData, types::RefundsResponseData>
|
||||
for Cybersource
|
||||
{
|
||||
fn get_headers(
|
||||
&self,
|
||||
req: &types::RefundExecuteRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<Vec<(String, 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::RefundExecuteRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
let connector_payment_id = req.request.connector_transaction_id.clone();
|
||||
Ok(format!(
|
||||
"{}pts/v2/payments/{}/refunds",
|
||||
self.base_url(connectors),
|
||||
connector_payment_id
|
||||
))
|
||||
}
|
||||
|
||||
fn get_request_body(
|
||||
&self,
|
||||
req: &types::RefundExecuteRouterData,
|
||||
) -> CustomResult<Option<String>, errors::ConnectorError> {
|
||||
let req_obj = cybersource::CybersourceRefundRequest::try_from(req)?;
|
||||
let req = utils::Encode::<cybersource::CybersourceRefundRequest>::encode_to_string_of_json(
|
||||
&req_obj,
|
||||
)
|
||||
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
|
||||
Ok(Some(req))
|
||||
}
|
||||
fn build_request(
|
||||
&self,
|
||||
req: &types::RefundExecuteRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
|
||||
Ok(Some(
|
||||
services::RequestBuilder::new()
|
||||
.method(services::Method::Post)
|
||||
.url(&types::RefundExecuteType::get_url(self, req, connectors)?)
|
||||
.headers(types::RefundExecuteType::get_headers(
|
||||
self, req, connectors,
|
||||
)?)
|
||||
.body(self.get_request_body(req)?)
|
||||
.build(),
|
||||
))
|
||||
}
|
||||
|
||||
fn handle_response(
|
||||
&self,
|
||||
data: &types::RefundExecuteRouterData,
|
||||
res: types::Response,
|
||||
) -> CustomResult<types::RefundExecuteRouterData, errors::ConnectorError> {
|
||||
let response: cybersource::CybersourcePaymentsResponse = res
|
||||
.response
|
||||
.parse_struct("Cybersource PaymentResponse")
|
||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||
logger::debug!(cybersource_refund_response=?response);
|
||||
types::RouterData::try_from(types::ResponseRouterData {
|
||||
response,
|
||||
data: data.clone(),
|
||||
http_code: res.status_code,
|
||||
})
|
||||
.change_context(errors::ConnectorError::ResponseHandlingFailed)
|
||||
}
|
||||
fn get_error_response(
|
||||
&self,
|
||||
res: Bytes,
|
||||
) -> CustomResult<types::ErrorResponse, errors::ConnectorError> {
|
||||
self.build_error_response(res)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl services::ConnectorIntegration<api::RSync, types::RefundsData, types::RefundsResponseData>
|
||||
impl ConnectorIntegration<api::RSync, types::RefundsData, types::RefundsResponseData>
|
||||
for Cybersource
|
||||
{
|
||||
fn get_headers(
|
||||
&self,
|
||||
req: &types::RefundSyncRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<Vec<(String, String)>, errors::ConnectorError> {
|
||||
self.build_headers(req, connectors)
|
||||
}
|
||||
fn get_content_type(&self) -> &'static str {
|
||||
self.common_get_content_type()
|
||||
}
|
||||
fn get_http_method(&self) -> services::Method {
|
||||
services::Method::Get
|
||||
}
|
||||
fn get_url(
|
||||
&self,
|
||||
req: &types::RefundSyncRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
Ok(format!(
|
||||
"{}tss/v2/transactions/{}",
|
||||
self.base_url(connectors),
|
||||
req.request.connector_transaction_id
|
||||
))
|
||||
}
|
||||
fn build_request(
|
||||
&self,
|
||||
req: &types::RefundSyncRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
|
||||
Ok(Some(
|
||||
services::RequestBuilder::new()
|
||||
.method(services::Method::Get)
|
||||
.url(&types::RefundSyncType::get_url(self, req, connectors)?)
|
||||
.headers(types::RefundSyncType::get_headers(self, req, connectors)?)
|
||||
.body(types::RefundSyncType::get_request_body(self, req)?)
|
||||
.build(),
|
||||
))
|
||||
}
|
||||
fn handle_response(
|
||||
&self,
|
||||
data: &types::RefundSyncRouterData,
|
||||
res: types::Response,
|
||||
) -> CustomResult<types::RefundSyncRouterData, errors::ConnectorError> {
|
||||
let response: cybersource::CybersourceTransactionResponse = res
|
||||
.response
|
||||
.parse_struct("Cybersource PaymentSyncResponse")
|
||||
.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> {
|
||||
self.build_error_response(res)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
|
||||
@ -4,6 +4,8 @@ use masking::Secret;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
connector::utils::{self, AddressDetailsData, PaymentsRequestData, PhoneDetailsData},
|
||||
consts,
|
||||
core::errors,
|
||||
pii::PeekInterface,
|
||||
types::{self, api, storage::enums},
|
||||
@ -18,8 +20,17 @@ pub struct CybersourcePaymentsRequest {
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Serialize, Eq, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ProcessingInformation {
|
||||
capture: bool,
|
||||
capture_options: Option<CaptureOptions>,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Serialize, Eq, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CaptureOptions {
|
||||
capture_sequence_number: u32,
|
||||
total_capture_count: u32,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Serialize, Eq, PartialEq)]
|
||||
@ -72,35 +83,25 @@ pub struct BillTo {
|
||||
|
||||
// for cybersource each item in Billing is mandatory
|
||||
fn build_bill_to(
|
||||
address_details: payments::Address,
|
||||
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
|
||||
}
|
||||
) -> Result<BillTo, error_stack::Report<errors::ConnectorError>> {
|
||||
let address = address_details
|
||||
.address
|
||||
.as_ref()
|
||||
.ok_or_else(utils::missing_field_err("billing.address"))?;
|
||||
Ok(BillTo {
|
||||
first_name: address.get_first_name()?.to_owned(),
|
||||
last_name: address.get_last_name()?.to_owned(),
|
||||
address1: address.get_line1()?.to_owned(),
|
||||
locality: address.get_city()?.to_owned(),
|
||||
administrative_area: address.get_line2()?.to_owned(),
|
||||
postal_code: address.get_zip()?.to_owned(),
|
||||
country: address.get_country()?.to_owned(),
|
||||
email,
|
||||
phone_number,
|
||||
})
|
||||
}
|
||||
|
||||
impl TryFrom<&types::PaymentsAuthorizeRouterData> for CybersourcePaymentsRequest {
|
||||
@ -108,30 +109,17 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for CybersourcePaymentsRequest
|
||||
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 phone = item.get_billing_phone()?;
|
||||
let phone_number = phone.get_number()?;
|
||||
let country_code = phone.get_country_code()?;
|
||||
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)?;
|
||||
.ok_or_else(utils::missing_field_err("email"))?;
|
||||
let bill_to = build_bill_to(item.get_billing()?, email, number_with_code)?;
|
||||
|
||||
let order_information = OrderInformationWithBill {
|
||||
amount_details: Amount {
|
||||
@ -155,6 +143,7 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for CybersourcePaymentsRequest
|
||||
item.request.capture_method,
|
||||
Some(enums::CaptureMethod::Automatic) | None
|
||||
),
|
||||
capture_options: None,
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
@ -168,6 +157,49 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for CybersourcePaymentsRequest
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&types::PaymentsCaptureRouterData> for CybersourcePaymentsRequest {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(value: &types::PaymentsCaptureRouterData) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
processing_information: ProcessingInformation {
|
||||
capture_options: Some(CaptureOptions {
|
||||
capture_sequence_number: 1,
|
||||
total_capture_count: 1,
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
order_information: OrderInformationWithBill {
|
||||
amount_details: Amount {
|
||||
total_amount: value
|
||||
.request
|
||||
.amount_to_capture
|
||||
.map(|amount| amount.to_string())
|
||||
.ok_or_else(utils::missing_field_err("amount_to_capture"))?,
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&types::RefundExecuteRouterData> for CybersourcePaymentsRequest {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(value: &types::RefundExecuteRouterData) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
order_information: OrderInformationWithBill {
|
||||
amount_details: Amount {
|
||||
total_amount: value.request.refund_amount.to_string(),
|
||||
currency: value.request.currency.to_string(),
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CybersourceAuthType {
|
||||
pub(super) api_key: String,
|
||||
pub(super) merchant_account: String,
|
||||
@ -199,6 +231,11 @@ pub enum CybersourcePaymentStatus {
|
||||
Authorized,
|
||||
Succeeded,
|
||||
Failed,
|
||||
Voided,
|
||||
Reversed,
|
||||
Pending,
|
||||
Declined,
|
||||
Transmitted,
|
||||
#[default]
|
||||
Processing,
|
||||
}
|
||||
@ -207,28 +244,110 @@ 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::Succeeded | CybersourcePaymentStatus::Transmitted => {
|
||||
Self::Charged
|
||||
}
|
||||
CybersourcePaymentStatus::Voided | CybersourcePaymentStatus::Reversed => Self::Voided,
|
||||
CybersourcePaymentStatus::Failed | CybersourcePaymentStatus::Declined => Self::Failure,
|
||||
CybersourcePaymentStatus::Processing => Self::Authorizing,
|
||||
CybersourcePaymentStatus::Pending => Self::Pending,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CybersourcePaymentStatus> for enums::RefundStatus {
|
||||
fn from(item: CybersourcePaymentStatus) -> Self {
|
||||
match item {
|
||||
CybersourcePaymentStatus::Succeeded => Self::Success,
|
||||
CybersourcePaymentStatus::Failed => Self::Failure,
|
||||
_ => Self::Pending,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, Deserialize, Eq, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CybersourcePaymentsResponse {
|
||||
id: String,
|
||||
status: CybersourcePaymentStatus,
|
||||
error_information: Option<CybersourceErrorInformation>,
|
||||
}
|
||||
|
||||
impl TryFrom<types::PaymentsResponseRouterData<CybersourcePaymentsResponse>>
|
||||
for types::PaymentsAuthorizeRouterData
|
||||
#[derive(Default, Debug, Clone, Deserialize, Eq, PartialEq)]
|
||||
pub struct CybersourceErrorInformation {
|
||||
reason: String,
|
||||
message: String,
|
||||
}
|
||||
|
||||
impl<F, T>
|
||||
TryFrom<
|
||||
types::ResponseRouterData<F, CybersourcePaymentsResponse, T, types::PaymentsResponseData>,
|
||||
> for types::RouterData<F, T, types::PaymentsResponseData>
|
||||
{
|
||||
type Error = error_stack::Report<errors::ParsingError>;
|
||||
fn try_from(
|
||||
item: types::PaymentsResponseRouterData<CybersourcePaymentsResponse>,
|
||||
item: types::ResponseRouterData<
|
||||
F,
|
||||
CybersourcePaymentsResponse,
|
||||
T,
|
||||
types::PaymentsResponseData,
|
||||
>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
status: item.response.status.into(),
|
||||
response: match item.response.error_information {
|
||||
Some(error) => Err(types::ErrorResponse {
|
||||
code: consts::NO_ERROR_CODE.to_string(),
|
||||
message: error.message,
|
||||
reason: Some(error.reason),
|
||||
}),
|
||||
_ => Ok(types::PaymentsResponseData::TransactionResponse {
|
||||
resource_id: types::ResponseId::ConnectorTransactionId(item.response.id),
|
||||
redirection_data: None,
|
||||
redirect: false,
|
||||
mandate_reference: None,
|
||||
connector_metadata: None,
|
||||
}),
|
||||
},
|
||||
..item.data
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CybersourceTransactionResponse {
|
||||
id: String,
|
||||
application_information: ApplicationInformation,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ApplicationInformation {
|
||||
status: CybersourcePaymentStatus,
|
||||
}
|
||||
|
||||
impl<F, T>
|
||||
TryFrom<
|
||||
types::ResponseRouterData<
|
||||
F,
|
||||
CybersourceTransactionResponse,
|
||||
T,
|
||||
types::PaymentsResponseData,
|
||||
>,
|
||||
> for types::RouterData<F, T, types::PaymentsResponseData>
|
||||
{
|
||||
type Error = error_stack::Report<errors::ParsingError>;
|
||||
fn try_from(
|
||||
item: types::ResponseRouterData<
|
||||
F,
|
||||
CybersourceTransactionResponse,
|
||||
T,
|
||||
types::PaymentsResponseData,
|
||||
>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
status: item.response.application_information.status.into(),
|
||||
response: Ok(types::PaymentsResponseData::TransactionResponse {
|
||||
resource_id: types::ResponseId::ConnectorTransactionId(item.response.id),
|
||||
redirection_data: None,
|
||||
@ -247,6 +366,7 @@ pub struct ErrorResponse {
|
||||
pub error_information: Option<ErrorInformation>,
|
||||
pub status: String,
|
||||
pub message: Option<String>,
|
||||
pub details: serde_json::Value,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize)]
|
||||
@ -256,12 +376,13 @@ pub struct ErrorInformation {
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CybersourceRefundRequest {
|
||||
order_information: OrderInformation,
|
||||
}
|
||||
|
||||
impl<F> TryFrom<&types::RefundsRouterData<F>> for CybersourceRefundRequest {
|
||||
type Error = error_stack::Report<errors::ParsingError>;
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(item: &types::RefundsRouterData<F>) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
order_information: OrderInformation {
|
||||
@ -274,42 +395,37 @@ impl<F> TryFrom<&types::RefundsRouterData<F>> for CybersourceRefundRequest {
|
||||
}
|
||||
}
|
||||
|
||||
#[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>
|
||||
impl TryFrom<types::RefundsResponseRouterData<api::Execute, CybersourcePaymentsResponse>>
|
||||
for types::RefundsRouterData<api::Execute>
|
||||
{
|
||||
type Error = error_stack::Report<errors::ParsingError>;
|
||||
fn try_from(
|
||||
item: types::RefundsResponseRouterData<api::RSync, CybersourceRefundResponse>,
|
||||
item: types::RefundsResponseRouterData<api::Execute, CybersourcePaymentsResponse>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
let refund_status = enums::RefundStatus::from(item.response.status);
|
||||
Ok(Self {
|
||||
response: Ok(types::RefundsResponseData {
|
||||
connector_refund_id: item.response.id,
|
||||
refund_status: enums::RefundStatus::from(item.response.status),
|
||||
refund_status,
|
||||
}),
|
||||
..item.data
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<types::RefundsResponseRouterData<api::RSync, CybersourceTransactionResponse>>
|
||||
for types::RefundsRouterData<api::RSync>
|
||||
{
|
||||
type Error = error_stack::Report<errors::ParsingError>;
|
||||
fn try_from(
|
||||
item: types::RefundsResponseRouterData<api::RSync, CybersourceTransactionResponse>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
response: Ok(types::RefundsResponseData {
|
||||
connector_refund_id: item.response.id,
|
||||
refund_status: enums::RefundStatus::from(
|
||||
item.response.application_information.status,
|
||||
),
|
||||
}),
|
||||
..item.data
|
||||
})
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
use error_stack::ResultExt;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::{
|
||||
@ -9,7 +8,6 @@ use crate::{
|
||||
connector::utils::{self, CardData, PaymentsRequestData},
|
||||
core::errors,
|
||||
types::{self, api, storage::enums},
|
||||
utils::OptionExt,
|
||||
};
|
||||
|
||||
impl TryFrom<&types::PaymentsAuthorizeRouterData> for GlobalpayPaymentsRequest {
|
||||
@ -18,8 +16,7 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for GlobalpayPaymentsRequest {
|
||||
let metadata = item
|
||||
.connector_meta_data
|
||||
.to_owned()
|
||||
.get_required_value("connector_meta_data")
|
||||
.change_context(errors::ConnectorError::NoConnectorMetaData)?;
|
||||
.ok_or_else(utils::missing_field_err("connector_meta"))?;
|
||||
let account_name = metadata
|
||||
.as_object()
|
||||
.and_then(|o| o.get("account_name"))
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
use masking::Secret;
|
||||
|
||||
use crate::{
|
||||
core::errors,
|
||||
pii::PeekInterface,
|
||||
@ -18,10 +20,50 @@ pub fn missing_field_err(
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
pub trait PaymentsRequestData {
|
||||
fn get_attempt_id(&self) -> Result<String, Error>;
|
||||
fn get_billing(&self) -> Result<&api::Address, Error>;
|
||||
fn get_billing_country(&self) -> Result<String, Error>;
|
||||
fn get_billing_phone(&self) -> Result<&api::PhoneDetails, Error>;
|
||||
fn get_card(&self) -> Result<api::CCard, Error>;
|
||||
}
|
||||
|
||||
impl PaymentsRequestData for types::PaymentsAuthorizeRouterData {
|
||||
fn get_attempt_id(&self) -> Result<String, Error> {
|
||||
self.attempt_id
|
||||
.clone()
|
||||
.ok_or_else(missing_field_err("attempt_id"))
|
||||
}
|
||||
|
||||
fn get_billing_country(&self) -> Result<String, Error> {
|
||||
self.address
|
||||
.billing
|
||||
.as_ref()
|
||||
.and_then(|a| a.address.as_ref())
|
||||
.and_then(|ad| ad.country.clone())
|
||||
.ok_or_else(missing_field_err("billing.address.country"))
|
||||
}
|
||||
|
||||
fn get_card(&self) -> Result<api::CCard, Error> {
|
||||
match self.request.payment_method_data.clone() {
|
||||
api::PaymentMethod::Card(card) => Ok(card),
|
||||
_ => Err(missing_field_err("card")()),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_billing_phone(&self) -> Result<&api::PhoneDetails, Error> {
|
||||
self.address
|
||||
.billing
|
||||
.as_ref()
|
||||
.and_then(|a| a.phone.as_ref())
|
||||
.ok_or_else(missing_field_err("billing.phone"))
|
||||
}
|
||||
fn get_billing(&self) -> Result<&api::Address, Error> {
|
||||
self.address
|
||||
.billing
|
||||
.as_ref()
|
||||
.ok_or_else(missing_field_err("billing"))
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CardData {
|
||||
fn get_card_number(&self) -> String;
|
||||
fn get_card_expiry_month(&self) -> String;
|
||||
@ -29,6 +71,7 @@ pub trait CardData {
|
||||
fn get_card_expiry_year_2_digit(&self) -> String;
|
||||
fn get_card_cvc(&self) -> String;
|
||||
}
|
||||
|
||||
impl CardData for api::CCard {
|
||||
fn get_card_number(&self) -> String {
|
||||
self.card_number.peek().clone()
|
||||
@ -47,26 +90,74 @@ impl CardData for api::CCard {
|
||||
self.card_cvc.peek().clone()
|
||||
}
|
||||
}
|
||||
impl PaymentsRequestData for types::PaymentsAuthorizeRouterData {
|
||||
fn get_attempt_id(&self) -> Result<String, Error> {
|
||||
self.attempt_id
|
||||
.clone()
|
||||
.ok_or_else(missing_field_err("attempt_id"))
|
||||
}
|
||||
pub trait PhoneDetailsData {
|
||||
fn get_number(&self) -> Result<Secret<String>, Error>;
|
||||
fn get_country_code(&self) -> Result<String, Error>;
|
||||
}
|
||||
|
||||
fn get_billing_country(&self) -> Result<String, Error> {
|
||||
self.address
|
||||
.billing
|
||||
impl PhoneDetailsData for api::PhoneDetails {
|
||||
fn get_country_code(&self) -> Result<String, Error> {
|
||||
self.country_code
|
||||
.clone()
|
||||
.and_then(|a| a.address)
|
||||
.and_then(|ad| ad.country)
|
||||
.ok_or_else(missing_field_err("billing.country"))
|
||||
.ok_or_else(missing_field_err("billing.phone.country_code"))
|
||||
}
|
||||
|
||||
fn get_card(&self) -> Result<api::CCard, Error> {
|
||||
match self.request.payment_method_data.clone() {
|
||||
api::PaymentMethod::Card(card) => Ok(card),
|
||||
_ => Err(missing_field_err("card")()),
|
||||
}
|
||||
fn get_number(&self) -> Result<Secret<String>, Error> {
|
||||
self.number
|
||||
.clone()
|
||||
.ok_or_else(missing_field_err("billing.phone.number"))
|
||||
}
|
||||
}
|
||||
|
||||
pub trait AddressDetailsData {
|
||||
fn get_first_name(&self) -> Result<&Secret<String>, Error>;
|
||||
fn get_last_name(&self) -> Result<&Secret<String>, Error>;
|
||||
fn get_line1(&self) -> Result<&Secret<String>, Error>;
|
||||
fn get_city(&self) -> Result<&String, Error>;
|
||||
fn get_line2(&self) -> Result<&Secret<String>, Error>;
|
||||
fn get_zip(&self) -> Result<&Secret<String>, Error>;
|
||||
fn get_country(&self) -> Result<&String, Error>;
|
||||
}
|
||||
|
||||
impl AddressDetailsData for api::AddressDetails {
|
||||
fn get_first_name(&self) -> Result<&Secret<String>, Error> {
|
||||
self.first_name
|
||||
.as_ref()
|
||||
.ok_or_else(missing_field_err("address.first_name"))
|
||||
}
|
||||
|
||||
fn get_last_name(&self) -> Result<&Secret<String>, Error> {
|
||||
self.last_name
|
||||
.as_ref()
|
||||
.ok_or_else(missing_field_err("address.last_name"))
|
||||
}
|
||||
|
||||
fn get_line1(&self) -> Result<&Secret<String>, Error> {
|
||||
self.line1
|
||||
.as_ref()
|
||||
.ok_or_else(missing_field_err("address.line1"))
|
||||
}
|
||||
|
||||
fn get_city(&self) -> Result<&String, Error> {
|
||||
self.city
|
||||
.as_ref()
|
||||
.ok_or_else(missing_field_err("address.city"))
|
||||
}
|
||||
|
||||
fn get_line2(&self) -> Result<&Secret<String>, Error> {
|
||||
self.line2
|
||||
.as_ref()
|
||||
.ok_or_else(missing_field_err("address.line2"))
|
||||
}
|
||||
|
||||
fn get_zip(&self) -> Result<&Secret<String>, Error> {
|
||||
self.zip
|
||||
.as_ref()
|
||||
.ok_or_else(missing_field_err("address.zip"))
|
||||
}
|
||||
|
||||
fn get_country(&self) -> Result<&String, Error> {
|
||||
self.country
|
||||
.as_ref()
|
||||
.ok_or_else(missing_field_err("address.country"))
|
||||
}
|
||||
}
|
||||
|
||||
@ -58,6 +58,11 @@ pub trait ConnectorIntegration<T, Req, Resp>: ConnectorIntegrationAny<T, Req, Re
|
||||
mime::APPLICATION_JSON.essence_str()
|
||||
}
|
||||
|
||||
/// primarily used when creating signature based on request method of payment flow
|
||||
fn get_http_method(&self) -> Method {
|
||||
Method::Post
|
||||
}
|
||||
|
||||
fn get_url(
|
||||
&self,
|
||||
_req: &types::RouterData<T, Req, Resp>,
|
||||
|
||||
Reference in New Issue
Block a user