mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-11-01 19:42:27 +08:00
feat(connector): add authorize, capture, void, refund, psync, rsync support for Dlocal connector (#650)
Co-authored-by: Venkatesh <inventvenkat@gmail.com>
This commit is contained in:
@ -142,6 +142,7 @@ pub struct Connectors {
|
||||
pub braintree: ConnectorParams,
|
||||
pub checkout: ConnectorParams,
|
||||
pub cybersource: ConnectorParams,
|
||||
pub dlocal: ConnectorParams,
|
||||
pub fiserv: ConnectorParams,
|
||||
pub globalpay: ConnectorParams,
|
||||
pub klarna: ConnectorParams,
|
||||
|
||||
@ -16,9 +16,11 @@ pub mod utils;
|
||||
pub mod worldline;
|
||||
pub mod worldpay;
|
||||
|
||||
pub mod dlocal;
|
||||
|
||||
pub use self::{
|
||||
aci::Aci, adyen::Adyen, applepay::Applepay, authorizedotnet::Authorizedotnet,
|
||||
braintree::Braintree, checkout::Checkout, cybersource::Cybersource, fiserv::Fiserv,
|
||||
globalpay::Globalpay, klarna::Klarna, payu::Payu, rapyd::Rapyd, shift4::Shift4, stripe::Stripe,
|
||||
worldline::Worldline, worldpay::Worldpay,
|
||||
braintree::Braintree, checkout::Checkout, cybersource::Cybersource, dlocal::Dlocal,
|
||||
fiserv::Fiserv, globalpay::Globalpay, klarna::Klarna, payu::Payu, rapyd::Rapyd, shift4::Shift4,
|
||||
stripe::Stripe, worldline::Worldline, worldpay::Worldpay,
|
||||
};
|
||||
|
||||
597
crates/router/src/connector/dlocal.rs
Normal file
597
crates/router/src/connector/dlocal.rs
Normal file
@ -0,0 +1,597 @@
|
||||
mod transformers;
|
||||
|
||||
use std::fmt::Debug;
|
||||
|
||||
use common_utils::{
|
||||
crypto::{self, SignMessage},
|
||||
date_time,
|
||||
};
|
||||
use error_stack::{IntoReport, ResultExt};
|
||||
use hex::encode;
|
||||
use transformers as dlocal;
|
||||
|
||||
use crate::{
|
||||
configs::settings,
|
||||
core::{
|
||||
errors::{self, CustomResult},
|
||||
payments,
|
||||
},
|
||||
headers, logger,
|
||||
services::{self, ConnectorIntegration},
|
||||
types::{
|
||||
self,
|
||||
api::{self, ConnectorCommon, ConnectorCommonExt},
|
||||
ErrorResponse, Response,
|
||||
},
|
||||
utils::{self, BytesExt},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Dlocal;
|
||||
|
||||
impl api::Payment for Dlocal {}
|
||||
impl api::PaymentSession for Dlocal {}
|
||||
impl api::ConnectorAccessToken for Dlocal {}
|
||||
impl api::PreVerify for Dlocal {}
|
||||
impl api::PaymentAuthorize for Dlocal {}
|
||||
impl api::PaymentSync for Dlocal {}
|
||||
impl api::PaymentCapture for Dlocal {}
|
||||
impl api::PaymentVoid for Dlocal {}
|
||||
impl api::Refund for Dlocal {}
|
||||
impl api::RefundExecute for Dlocal {}
|
||||
impl api::RefundSync for Dlocal {}
|
||||
|
||||
impl<Flow, Request, Response> ConnectorCommonExt<Flow, Request, Response> for Dlocal
|
||||
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 dlocal_req = match self.get_request_body(req)? {
|
||||
Some(val) => val,
|
||||
None => "".to_string(),
|
||||
};
|
||||
|
||||
let date = date_time::date_as_yyyymmddthhmmssmmmz()
|
||||
.into_report()
|
||||
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
|
||||
|
||||
let auth = dlocal::DlocalAuthType::try_from(&req.connector_auth_type)?;
|
||||
let sign_req: String = format!("{}{}{}", auth.x_login, date, dlocal_req);
|
||||
let authz = crypto::HmacSha256::sign_message(
|
||||
&crypto::HmacSha256,
|
||||
auth.secret.as_bytes(),
|
||||
sign_req.as_bytes(),
|
||||
)
|
||||
.change_context(errors::ConnectorError::RequestEncodingFailed)
|
||||
.attach_printable("Failed to sign the message")?;
|
||||
let auth_string: String = format!("V2-HMAC-SHA256, Signature: {}", encode(authz));
|
||||
let headers = vec![
|
||||
(headers::AUTHORIZATION.to_string(), auth_string),
|
||||
(headers::X_LOGIN.to_string(), auth.x_login),
|
||||
(headers::X_TRANS_KEY.to_string(), auth.x_trans_key),
|
||||
(headers::X_VERSION.to_string(), "2.1".to_string()),
|
||||
(headers::X_DATE.to_string(), date),
|
||||
(
|
||||
headers::CONTENT_TYPE.to_string(),
|
||||
Self.get_content_type().to_string(),
|
||||
),
|
||||
];
|
||||
Ok(headers)
|
||||
}
|
||||
}
|
||||
|
||||
impl ConnectorCommon for Dlocal {
|
||||
fn id(&self) -> &'static str {
|
||||
"dlocal"
|
||||
}
|
||||
|
||||
fn common_get_content_type(&self) -> &'static str {
|
||||
"application/json"
|
||||
}
|
||||
|
||||
fn base_url<'a>(&self, connectors: &'a settings::Connectors) -> &'a str {
|
||||
connectors.dlocal.base_url.as_ref()
|
||||
}
|
||||
|
||||
fn build_error_response(
|
||||
&self,
|
||||
res: Response,
|
||||
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
|
||||
let response: dlocal::DlocalErrorResponse = res
|
||||
.response
|
||||
.parse_struct("Dlocal ErrorResponse")
|
||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||
|
||||
Ok(ErrorResponse {
|
||||
status_code: res.status_code,
|
||||
code: response.code.to_string(),
|
||||
message: response.message,
|
||||
reason: response.param,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ConnectorIntegration<api::Session, types::PaymentsSessionData, types::PaymentsResponseData>
|
||||
for Dlocal
|
||||
{
|
||||
//TODO: implement sessions flow
|
||||
}
|
||||
|
||||
impl ConnectorIntegration<api::AccessTokenAuth, types::AccessTokenRequestData, types::AccessToken>
|
||||
for Dlocal
|
||||
{
|
||||
}
|
||||
|
||||
impl ConnectorIntegration<api::Verify, types::VerifyRequestData, types::PaymentsResponseData>
|
||||
for Dlocal
|
||||
{
|
||||
}
|
||||
|
||||
impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::PaymentsResponseData>
|
||||
for Dlocal
|
||||
{
|
||||
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(
|
||||
&self,
|
||||
_req: &types::PaymentsAuthorizeRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
Ok(format!("{}secure_payments", self.base_url(connectors)))
|
||||
}
|
||||
|
||||
fn get_request_body(
|
||||
&self,
|
||||
req: &types::PaymentsAuthorizeRouterData,
|
||||
) -> CustomResult<Option<String>, errors::ConnectorError> {
|
||||
let dlocal_req = utils::Encode::<dlocal::DlocalPaymentsRequest>::convert_and_encode(req)
|
||||
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
|
||||
Ok(Some(dlocal_req))
|
||||
}
|
||||
|
||||
fn build_request(
|
||||
&self,
|
||||
req: &types::PaymentsAuthorizeRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
|
||||
Ok(Some(
|
||||
services::RequestBuilder::new()
|
||||
.method(services::Method::Post)
|
||||
.url(&types::PaymentsAuthorizeType::get_url(
|
||||
self, req, connectors,
|
||||
)?)
|
||||
.headers(types::PaymentsAuthorizeType::get_headers(
|
||||
self, req, connectors,
|
||||
)?)
|
||||
.body(types::PaymentsAuthorizeType::get_request_body(self, req)?)
|
||||
.build(),
|
||||
))
|
||||
}
|
||||
|
||||
fn handle_response(
|
||||
&self,
|
||||
data: &types::PaymentsAuthorizeRouterData,
|
||||
res: Response,
|
||||
) -> CustomResult<types::PaymentsAuthorizeRouterData, errors::ConnectorError> {
|
||||
logger::debug!(dlocal_payments_authorize_response=?res);
|
||||
let response: dlocal::DlocalPaymentsResponse = res
|
||||
.response
|
||||
.parse_struct("Dlocal PaymentsAuthorizeResponse")
|
||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||
types::ResponseRouterData {
|
||||
response,
|
||||
data: data.clone(),
|
||||
http_code: res.status_code,
|
||||
}
|
||||
.try_into()
|
||||
.change_context(errors::ConnectorError::ResponseHandlingFailed)
|
||||
}
|
||||
|
||||
fn get_error_response(
|
||||
&self,
|
||||
res: Response,
|
||||
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
|
||||
self.build_error_response(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl ConnectorIntegration<api::PSync, types::PaymentsSyncData, types::PaymentsResponseData>
|
||||
for Dlocal
|
||||
{
|
||||
fn get_headers(
|
||||
&self,
|
||||
req: &types::PaymentsSyncRouterData,
|
||||
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::PaymentsSyncRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
let sync_data = dlocal::DlocalPaymentsSyncRequest::try_from(req)?;
|
||||
Ok(format!(
|
||||
"{}payments/{}/status",
|
||||
self.base_url(connectors),
|
||||
sync_data.authz_id,
|
||||
))
|
||||
}
|
||||
|
||||
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 get_error_response(
|
||||
&self,
|
||||
res: Response,
|
||||
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
|
||||
self.build_error_response(res)
|
||||
}
|
||||
|
||||
fn handle_response(
|
||||
&self,
|
||||
data: &types::PaymentsSyncRouterData,
|
||||
res: Response,
|
||||
) -> CustomResult<types::PaymentsSyncRouterData, errors::ConnectorError> {
|
||||
logger::debug!(dlocal_payment_sync_response=?res);
|
||||
let response: dlocal::DlocalPaymentsResponse = res
|
||||
.response
|
||||
.parse_struct("Dlocal PaymentsSyncResponse")
|
||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||
types::RouterData::try_from(types::ResponseRouterData {
|
||||
response,
|
||||
data: data.clone(),
|
||||
http_code: res.status_code,
|
||||
})
|
||||
.change_context(errors::ConnectorError::ResponseHandlingFailed)
|
||||
}
|
||||
}
|
||||
|
||||
impl ConnectorIntegration<api::Capture, types::PaymentsCaptureData, types::PaymentsResponseData>
|
||||
for Dlocal
|
||||
{
|
||||
fn get_headers(
|
||||
&self,
|
||||
req: &types::PaymentsCaptureRouterData,
|
||||
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::PaymentsCaptureRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
Ok(format!("{}payments", self.base_url(connectors)))
|
||||
}
|
||||
|
||||
fn get_request_body(
|
||||
&self,
|
||||
req: &types::PaymentsCaptureRouterData,
|
||||
) -> CustomResult<Option<String>, errors::ConnectorError> {
|
||||
let dlocal_req =
|
||||
utils::Encode::<dlocal::DlocalPaymentsCaptureRequest>::convert_and_encode(req)
|
||||
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
|
||||
Ok(Some(dlocal_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: Response,
|
||||
) -> CustomResult<types::PaymentsCaptureRouterData, errors::ConnectorError> {
|
||||
logger::debug!(dlocal_payments_capture_response=?res);
|
||||
let response: dlocal::DlocalPaymentsResponse = res
|
||||
.response
|
||||
.parse_struct("Dlocal PaymentsCaptureResponse")
|
||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||
types::ResponseRouterData {
|
||||
response,
|
||||
data: data.clone(),
|
||||
http_code: res.status_code,
|
||||
}
|
||||
.try_into()
|
||||
.change_context(errors::ConnectorError::ResponseHandlingFailed)
|
||||
}
|
||||
|
||||
fn get_error_response(
|
||||
&self,
|
||||
res: Response,
|
||||
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
|
||||
self.build_error_response(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl ConnectorIntegration<api::Void, types::PaymentsCancelData, types::PaymentsResponseData>
|
||||
for Dlocal
|
||||
{
|
||||
fn get_headers(
|
||||
&self,
|
||||
req: &types::PaymentsCancelRouterData,
|
||||
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::PaymentsCancelRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
let cancel_data = dlocal::DlocalPaymentsCancelRequest::try_from(req)?;
|
||||
Ok(format!(
|
||||
"{}payments/{}/cancel",
|
||||
self.base_url(connectors),
|
||||
cancel_data.cancel_id
|
||||
))
|
||||
}
|
||||
|
||||
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)?)
|
||||
.build(),
|
||||
))
|
||||
}
|
||||
|
||||
fn handle_response(
|
||||
&self,
|
||||
data: &types::PaymentsCancelRouterData,
|
||||
res: Response,
|
||||
) -> CustomResult<types::PaymentsCancelRouterData, errors::ConnectorError> {
|
||||
logger::debug!(dlocal_payments_cancel_response=?res);
|
||||
let response: dlocal::DlocalPaymentsResponse = res
|
||||
.response
|
||||
.parse_struct("Dlocal PaymentsCancelResponse")
|
||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||
types::ResponseRouterData {
|
||||
response,
|
||||
data: data.clone(),
|
||||
http_code: res.status_code,
|
||||
}
|
||||
.try_into()
|
||||
.change_context(errors::ConnectorError::ResponseHandlingFailed)
|
||||
}
|
||||
|
||||
fn get_error_response(
|
||||
&self,
|
||||
res: Response,
|
||||
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
|
||||
self.build_error_response(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl ConnectorIntegration<api::Execute, types::RefundsData, types::RefundsResponseData> for Dlocal {
|
||||
fn get_headers(
|
||||
&self,
|
||||
req: &types::RefundsRouterData<api::Execute>,
|
||||
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::RefundsRouterData<api::Execute>,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
Ok(format!("{}refunds", self.base_url(connectors)))
|
||||
}
|
||||
|
||||
fn get_request_body(
|
||||
&self,
|
||||
req: &types::RefundsRouterData<api::Execute>,
|
||||
) -> CustomResult<Option<String>, errors::ConnectorError> {
|
||||
let dlocal_req = utils::Encode::<dlocal::RefundRequest>::convert_and_encode(req)
|
||||
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
|
||||
Ok(Some(dlocal_req))
|
||||
}
|
||||
|
||||
fn build_request(
|
||||
&self,
|
||||
req: &types::RefundsRouterData<api::Execute>,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
|
||||
let request = services::RequestBuilder::new()
|
||||
.method(services::Method::Post)
|
||||
.url(&types::RefundExecuteType::get_url(self, req, connectors)?)
|
||||
.headers(types::RefundExecuteType::get_headers(
|
||||
self, req, connectors,
|
||||
)?)
|
||||
.body(types::RefundExecuteType::get_request_body(self, req)?)
|
||||
.build();
|
||||
Ok(Some(request))
|
||||
}
|
||||
|
||||
fn handle_response(
|
||||
&self,
|
||||
data: &types::RefundsRouterData<api::Execute>,
|
||||
res: Response,
|
||||
) -> CustomResult<types::RefundsRouterData<api::Execute>, errors::ConnectorError> {
|
||||
logger::debug!(dlocal_refund_response=?res);
|
||||
let response: dlocal::RefundResponse =
|
||||
res.response
|
||||
.parse_struct("Dlocal RefundResponse")
|
||||
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
|
||||
types::ResponseRouterData {
|
||||
response,
|
||||
data: data.clone(),
|
||||
http_code: res.status_code,
|
||||
}
|
||||
.try_into()
|
||||
.change_context(errors::ConnectorError::ResponseHandlingFailed)
|
||||
}
|
||||
|
||||
fn get_error_response(
|
||||
&self,
|
||||
res: Response,
|
||||
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
|
||||
self.build_error_response(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl ConnectorIntegration<api::RSync, types::RefundsData, types::RefundsResponseData> for Dlocal {
|
||||
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_url(
|
||||
&self,
|
||||
req: &types::RefundSyncRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
let sync_data = dlocal::DlocalRefundsSyncRequest::try_from(req)?;
|
||||
Ok(format!(
|
||||
"{}refunds/{}/status",
|
||||
self.base_url(connectors),
|
||||
sync_data.refund_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)?)
|
||||
.build(),
|
||||
))
|
||||
}
|
||||
|
||||
fn handle_response(
|
||||
&self,
|
||||
data: &types::RefundSyncRouterData,
|
||||
res: Response,
|
||||
) -> CustomResult<types::RefundSyncRouterData, errors::ConnectorError> {
|
||||
logger::debug!(dlocal_refund_sync_response=?res);
|
||||
let response: dlocal::RefundResponse = res
|
||||
.response
|
||||
.parse_struct("Dlocal RefundSyncResponse")
|
||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||
types::ResponseRouterData {
|
||||
response,
|
||||
data: data.clone(),
|
||||
http_code: res.status_code,
|
||||
}
|
||||
.try_into()
|
||||
.change_context(errors::ConnectorError::ResponseHandlingFailed)
|
||||
}
|
||||
|
||||
fn get_error_response(
|
||||
&self,
|
||||
res: Response,
|
||||
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
|
||||
self.build_error_response(res)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl api::IncomingWebhook for Dlocal {
|
||||
fn get_webhook_object_reference_id(
|
||||
&self,
|
||||
_request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::WebhooksNotImplemented).into_report()
|
||||
}
|
||||
|
||||
fn get_webhook_event_type(
|
||||
&self,
|
||||
_request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<api::IncomingWebhookEvent, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::WebhooksNotImplemented).into_report()
|
||||
}
|
||||
|
||||
fn get_webhook_resource_object(
|
||||
&self,
|
||||
_request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<serde_json::Value, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::WebhooksNotImplemented).into_report()
|
||||
}
|
||||
}
|
||||
|
||||
impl services::ConnectorRedirectResponse for Dlocal {
|
||||
fn get_flow_type(
|
||||
&self,
|
||||
_query_params: &str,
|
||||
) -> CustomResult<payments::CallConnectorAction, errors::ConnectorError> {
|
||||
Ok(payments::CallConnectorAction::Trigger)
|
||||
}
|
||||
}
|
||||
513
crates/router/src/connector/dlocal/transformers.rs
Normal file
513
crates/router/src/connector/dlocal/transformers.rs
Normal file
@ -0,0 +1,513 @@
|
||||
use api_models::payments::AddressDetails;
|
||||
use common_utils::pii::{self, Email};
|
||||
use error_stack::ResultExt;
|
||||
use masking::{PeekInterface, Secret};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use url::Url;
|
||||
|
||||
use crate::{
|
||||
connector::utils::{AddressDetailsData, PaymentsRequestData},
|
||||
core::errors,
|
||||
services,
|
||||
types::{self, api, storage::enums},
|
||||
};
|
||||
|
||||
#[derive(Debug, Default, Eq, PartialEq, Serialize)]
|
||||
pub struct Payer {
|
||||
pub name: Option<Secret<String>>,
|
||||
pub email: Option<Secret<String, Email>>,
|
||||
pub document: Secret<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Eq, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Card {
|
||||
pub holder_name: Secret<String>,
|
||||
pub number: Secret<String, pii::CardNumber>,
|
||||
pub cvv: Secret<String>,
|
||||
pub expiration_month: Secret<String>,
|
||||
pub expiration_year: Secret<String>,
|
||||
pub capture: String,
|
||||
pub installments_id: Option<String>,
|
||||
pub installments: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Eq, PartialEq, Serialize)]
|
||||
pub struct ThreeDSecureReqData {
|
||||
pub force: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Default, Deserialize, Clone, Eq, PartialEq)]
|
||||
#[serde(rename_all = "UPPERCASE")]
|
||||
pub enum PaymentMethodId {
|
||||
#[default]
|
||||
Card,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Default, Deserialize, Clone, Eq, PartialEq)]
|
||||
#[serde(rename_all = "UPPERCASE")]
|
||||
pub enum PaymentMethodFlow {
|
||||
#[default]
|
||||
Direct,
|
||||
ReDirect,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Serialize, Eq, PartialEq)]
|
||||
pub struct DlocalPaymentsRequest {
|
||||
pub amount: i64,
|
||||
pub currency: enums::Currency,
|
||||
pub country: String,
|
||||
pub payment_method_id: PaymentMethodId,
|
||||
pub payment_method_flow: PaymentMethodFlow,
|
||||
pub payer: Payer,
|
||||
pub card: Option<Card>,
|
||||
pub order_id: String,
|
||||
pub three_dsecure: Option<ThreeDSecureReqData>,
|
||||
pub callback_url: Option<String>,
|
||||
}
|
||||
|
||||
impl TryFrom<&types::PaymentsAuthorizeRouterData> for DlocalPaymentsRequest {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(item: &types::PaymentsAuthorizeRouterData) -> Result<Self, Self::Error> {
|
||||
let email = item.request.email.clone();
|
||||
let address = item.get_billing_address()?;
|
||||
let country = address.get_country()?;
|
||||
let name = get_payer_name(address);
|
||||
match item.request.payment_method_data {
|
||||
api::PaymentMethod::Card(ref ccard) => {
|
||||
let should_capture = matches!(
|
||||
item.request.capture_method,
|
||||
Some(enums::CaptureMethod::Automatic)
|
||||
);
|
||||
let payment_request = Self {
|
||||
amount: item.request.amount,
|
||||
currency: item.request.currency,
|
||||
payment_method_id: PaymentMethodId::Card,
|
||||
payment_method_flow: PaymentMethodFlow::Direct,
|
||||
country: country.to_string(),
|
||||
payer: Payer {
|
||||
name,
|
||||
email,
|
||||
// [#589]: Allow securely collecting PII from customer in payments request
|
||||
document: get_doc_from_currency(country.to_string()),
|
||||
},
|
||||
card: Some(Card {
|
||||
holder_name: ccard.card_holder_name.clone(),
|
||||
number: ccard.card_number.clone(),
|
||||
cvv: ccard.card_cvc.clone(),
|
||||
expiration_month: ccard.card_exp_month.clone(),
|
||||
expiration_year: ccard.card_exp_year.clone(),
|
||||
capture: should_capture.to_string(),
|
||||
installments_id: item
|
||||
.request
|
||||
.mandate_id
|
||||
.as_ref()
|
||||
.map(|ids| ids.mandate_id.clone()),
|
||||
// [#595[FEATURE] Pass Mandate history information in payment flows/request]
|
||||
installments: item.request.mandate_id.clone().map(|_| "1".to_string()),
|
||||
}),
|
||||
order_id: item.payment_id.clone(),
|
||||
three_dsecure: match item.auth_type {
|
||||
storage_models::enums::AuthenticationType::ThreeDs => {
|
||||
Some(ThreeDSecureReqData { force: true })
|
||||
}
|
||||
storage_models::enums::AuthenticationType::NoThreeDs => None,
|
||||
},
|
||||
callback_url: item.return_url.clone(),
|
||||
};
|
||||
Ok(payment_request)
|
||||
}
|
||||
_ => Err(errors::ConnectorError::NotImplemented("Payment Method".to_string()).into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_payer_name(address: &AddressDetails) -> Option<Secret<String>> {
|
||||
let first_name = address
|
||||
.first_name
|
||||
.clone()
|
||||
.map_or("".to_string(), |first_name| first_name.peek().to_string());
|
||||
let last_name = address
|
||||
.last_name
|
||||
.clone()
|
||||
.map_or("".to_string(), |last_name| last_name.peek().to_string());
|
||||
let name: String = format!("{} {}", first_name, last_name).trim().to_string();
|
||||
if !name.is_empty() {
|
||||
Some(Secret::new(name))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DlocalPaymentsSyncRequest {
|
||||
pub authz_id: String,
|
||||
}
|
||||
|
||||
impl TryFrom<&types::PaymentsSyncRouterData> for DlocalPaymentsSyncRequest {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(item: &types::PaymentsSyncRouterData) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
authz_id: (item
|
||||
.request
|
||||
.connector_transaction_id
|
||||
.get_connector_transaction_id()
|
||||
.change_context(errors::ConnectorError::MissingConnectorTransactionID)?),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DlocalPaymentsCancelRequest {
|
||||
pub cancel_id: String,
|
||||
}
|
||||
|
||||
impl TryFrom<&types::PaymentsCancelRouterData> for DlocalPaymentsCancelRequest {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(item: &types::PaymentsCancelRouterData) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
cancel_id: item.request.connector_transaction_id.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Serialize, Eq, PartialEq)]
|
||||
pub struct DlocalPaymentsCaptureRequest {
|
||||
pub authorization_id: String,
|
||||
pub amount: i64,
|
||||
pub currency: String,
|
||||
pub order_id: String,
|
||||
}
|
||||
|
||||
impl TryFrom<&types::PaymentsCaptureRouterData> for DlocalPaymentsCaptureRequest {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(item: &types::PaymentsCaptureRouterData) -> Result<Self, Self::Error> {
|
||||
let amount_to_capture = match item.request.amount_to_capture {
|
||||
Some(val) => val,
|
||||
None => item.request.amount,
|
||||
};
|
||||
Ok(Self {
|
||||
authorization_id: item.request.connector_transaction_id.clone(),
|
||||
amount: amount_to_capture,
|
||||
currency: item.request.currency.to_string(),
|
||||
order_id: item.payment_id.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
// Auth Struct
|
||||
pub struct DlocalAuthType {
|
||||
pub(super) x_login: String,
|
||||
pub(super) x_trans_key: String,
|
||||
pub(super) secret: String,
|
||||
}
|
||||
|
||||
impl TryFrom<&types::ConnectorAuthType> for DlocalAuthType {
|
||||
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 {
|
||||
x_login: api_key.to_string(),
|
||||
x_trans_key: key1.to_string(),
|
||||
secret: api_secret.to_string(),
|
||||
})
|
||||
} else {
|
||||
Err(errors::ConnectorError::FailedToObtainAuthType.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Clone, Eq, Default, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "UPPERCASE")]
|
||||
pub enum DlocalPaymentStatus {
|
||||
Authorized,
|
||||
Paid,
|
||||
Verified,
|
||||
Cancelled,
|
||||
#[default]
|
||||
Pending,
|
||||
Rejected,
|
||||
}
|
||||
|
||||
impl From<DlocalPaymentStatus> for enums::AttemptStatus {
|
||||
fn from(item: DlocalPaymentStatus) -> Self {
|
||||
match item {
|
||||
DlocalPaymentStatus::Authorized => Self::Authorized,
|
||||
DlocalPaymentStatus::Verified => Self::Authorized,
|
||||
DlocalPaymentStatus::Paid => Self::Charged,
|
||||
DlocalPaymentStatus::Pending => Self::AuthenticationPending,
|
||||
DlocalPaymentStatus::Cancelled => Self::Voided,
|
||||
DlocalPaymentStatus::Rejected => Self::AuthenticationFailed,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Eq, Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct ThreeDSecureResData {
|
||||
pub redirect_url: Option<Url>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Eq, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct DlocalPaymentsResponse {
|
||||
status: DlocalPaymentStatus,
|
||||
id: String,
|
||||
three_dsecure: Option<ThreeDSecureResData>,
|
||||
}
|
||||
|
||||
impl<F, T>
|
||||
TryFrom<types::ResponseRouterData<F, DlocalPaymentsResponse, T, types::PaymentsResponseData>>
|
||||
for types::RouterData<F, T, types::PaymentsResponseData>
|
||||
{
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(
|
||||
item: types::ResponseRouterData<F, DlocalPaymentsResponse, T, types::PaymentsResponseData>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
let redirection_data = item
|
||||
.response
|
||||
.three_dsecure
|
||||
.and_then(|three_secure_data| three_secure_data.redirect_url)
|
||||
.map(|redirect_url| {
|
||||
services::RedirectForm::from((redirect_url, services::Method::Get))
|
||||
});
|
||||
|
||||
let response = types::PaymentsResponseData::TransactionResponse {
|
||||
resource_id: types::ResponseId::ConnectorTransactionId(item.response.id),
|
||||
redirection_data,
|
||||
mandate_reference: None,
|
||||
connector_metadata: None,
|
||||
};
|
||||
Ok(Self {
|
||||
status: enums::AttemptStatus::from(item.response.status),
|
||||
response: Ok(response),
|
||||
..item.data
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct DlocalPaymentsSyncResponse {
|
||||
status: DlocalPaymentStatus,
|
||||
id: String,
|
||||
}
|
||||
|
||||
impl<F, T>
|
||||
TryFrom<
|
||||
types::ResponseRouterData<F, DlocalPaymentsSyncResponse, T, types::PaymentsResponseData>,
|
||||
> for types::RouterData<F, T, types::PaymentsResponseData>
|
||||
{
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(
|
||||
item: types::ResponseRouterData<
|
||||
F,
|
||||
DlocalPaymentsSyncResponse,
|
||||
T,
|
||||
types::PaymentsResponseData,
|
||||
>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
status: enums::AttemptStatus::from(item.response.status),
|
||||
response: Ok(types::PaymentsResponseData::TransactionResponse {
|
||||
resource_id: types::ResponseId::ConnectorTransactionId(item.response.id),
|
||||
redirection_data: None,
|
||||
mandate_reference: None,
|
||||
connector_metadata: None,
|
||||
}),
|
||||
..item.data
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct DlocalPaymentsCaptureResponse {
|
||||
status: DlocalPaymentStatus,
|
||||
id: String,
|
||||
}
|
||||
|
||||
impl<F, T>
|
||||
TryFrom<
|
||||
types::ResponseRouterData<F, DlocalPaymentsCaptureResponse, T, types::PaymentsResponseData>,
|
||||
> for types::RouterData<F, T, types::PaymentsResponseData>
|
||||
{
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(
|
||||
item: types::ResponseRouterData<
|
||||
F,
|
||||
DlocalPaymentsCaptureResponse,
|
||||
T,
|
||||
types::PaymentsResponseData,
|
||||
>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
status: enums::AttemptStatus::from(item.response.status),
|
||||
response: Ok(types::PaymentsResponseData::TransactionResponse {
|
||||
resource_id: types::ResponseId::ConnectorTransactionId(item.response.id),
|
||||
redirection_data: None,
|
||||
mandate_reference: None,
|
||||
connector_metadata: None,
|
||||
}),
|
||||
..item.data
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DlocalPaymentsCancelResponse {
|
||||
status: DlocalPaymentStatus,
|
||||
id: String,
|
||||
}
|
||||
|
||||
impl<F, T>
|
||||
TryFrom<
|
||||
types::ResponseRouterData<F, DlocalPaymentsCancelResponse, T, types::PaymentsResponseData>,
|
||||
> for types::RouterData<F, T, types::PaymentsResponseData>
|
||||
{
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(
|
||||
item: types::ResponseRouterData<
|
||||
F,
|
||||
DlocalPaymentsCancelResponse,
|
||||
T,
|
||||
types::PaymentsResponseData,
|
||||
>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
status: enums::AttemptStatus::from(item.response.status),
|
||||
response: Ok(types::PaymentsResponseData::TransactionResponse {
|
||||
resource_id: types::ResponseId::ConnectorTransactionId(item.response.id),
|
||||
redirection_data: None,
|
||||
mandate_reference: None,
|
||||
connector_metadata: None,
|
||||
}),
|
||||
..item.data
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// REFUND :
|
||||
#[derive(Default, Debug, Serialize)]
|
||||
pub struct RefundRequest {
|
||||
pub amount: String,
|
||||
pub payment_id: String,
|
||||
pub currency: enums::Currency,
|
||||
pub id: String,
|
||||
}
|
||||
|
||||
impl<F> TryFrom<&types::RefundsRouterData<F>> for RefundRequest {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(item: &types::RefundsRouterData<F>) -> Result<Self, Self::Error> {
|
||||
let amount_to_refund = item.request.refund_amount.to_string();
|
||||
Ok(Self {
|
||||
amount: amount_to_refund,
|
||||
payment_id: item.request.connector_transaction_id.clone(),
|
||||
currency: item.request.currency,
|
||||
id: item.request.refund_id.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug, Serialize, Default, Deserialize, Clone)]
|
||||
#[serde(rename_all = "UPPERCASE")]
|
||||
pub enum RefundStatus {
|
||||
Success,
|
||||
#[default]
|
||||
Pending,
|
||||
Rejected,
|
||||
Cancelled,
|
||||
}
|
||||
|
||||
impl From<RefundStatus> for enums::RefundStatus {
|
||||
fn from(item: RefundStatus) -> Self {
|
||||
match item {
|
||||
RefundStatus::Success => Self::Success,
|
||||
RefundStatus::Pending => Self::Pending,
|
||||
RefundStatus::Rejected => Self::ManualReview,
|
||||
RefundStatus::Cancelled => Self::Failure,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct RefundResponse {
|
||||
pub id: String,
|
||||
pub status: RefundStatus,
|
||||
}
|
||||
|
||||
impl TryFrom<types::RefundsResponseRouterData<api::Execute, RefundResponse>>
|
||||
for types::RefundsRouterData<api::Execute>
|
||||
{
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(
|
||||
item: types::RefundsResponseRouterData<api::Execute, RefundResponse>,
|
||||
) -> 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,
|
||||
}),
|
||||
..item.data
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct DlocalRefundsSyncRequest {
|
||||
pub refund_id: String,
|
||||
}
|
||||
|
||||
impl TryFrom<&types::RefundSyncRouterData> for DlocalRefundsSyncRequest {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(item: &types::RefundSyncRouterData) -> Result<Self, Self::Error> {
|
||||
let refund_id = match item.request.connector_refund_id.clone() {
|
||||
Some(val) => val,
|
||||
None => item.request.refund_id.clone(),
|
||||
};
|
||||
Ok(Self {
|
||||
refund_id: (refund_id),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<types::RefundsResponseRouterData<api::RSync, RefundResponse>>
|
||||
for types::RefundsRouterData<api::RSync>
|
||||
{
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(
|
||||
item: types::RefundsResponseRouterData<api::RSync, RefundResponse>,
|
||||
) -> 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,
|
||||
}),
|
||||
..item.data
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct DlocalErrorResponse {
|
||||
pub code: i32,
|
||||
pub message: String,
|
||||
pub param: Option<String>,
|
||||
}
|
||||
|
||||
fn get_doc_from_currency(country: String) -> Secret<String> {
|
||||
let doc = match country.as_str() {
|
||||
"BR" => "91483309223",
|
||||
"ZA" => "2001014800086",
|
||||
"BD" | "GT" | "HN" | "PK" | "SN" | "TH" => "1234567890001",
|
||||
"CR" | "SV" | "VN" => "123456789",
|
||||
"DO" | "NG" => "12345678901",
|
||||
"EG" => "12345678901112",
|
||||
"GH" | "ID" | "RW" | "UG" => "1234567890111123",
|
||||
"IN" => "NHSTP6374G",
|
||||
"CI" => "CA124356789",
|
||||
"JP" | "MY" | "PH" => "123456789012",
|
||||
"NI" => "1234567890111A",
|
||||
"TZ" => "12345678912345678900",
|
||||
_ => "12345678",
|
||||
};
|
||||
Secret::new(doc.to_string())
|
||||
}
|
||||
@ -38,6 +38,7 @@ pub trait PaymentsRequestData {
|
||||
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_billing_address(&self) -> Result<&api::AddressDetails, Error>;
|
||||
fn get_card(&self) -> Result<api::Card, Error>;
|
||||
fn get_return_url(&self) -> Result<String, Error>;
|
||||
}
|
||||
@ -79,6 +80,13 @@ impl PaymentsRequestData for types::PaymentsAuthorizeRouterData {
|
||||
.and_then(|a| a.phone.as_ref())
|
||||
.ok_or_else(missing_field_err("billing.phone"))
|
||||
}
|
||||
fn get_billing_address(&self) -> Result<&api::AddressDetails, Error> {
|
||||
self.address
|
||||
.billing
|
||||
.as_ref()
|
||||
.and_then(|a| a.address.as_ref())
|
||||
.ok_or_else(missing_field_err("billing.address"))
|
||||
}
|
||||
fn get_billing(&self) -> Result<&api::Address, Error> {
|
||||
self.address
|
||||
.billing
|
||||
|
||||
@ -50,6 +50,10 @@ pub mod headers {
|
||||
pub const X_API_VERSION: &str = "X-ApiVersion";
|
||||
pub const DATE: &str = "Date";
|
||||
pub const X_MERCHANT_ID: &str = "X-Merchant-Id";
|
||||
pub const X_LOGIN: &str = "X-Login";
|
||||
pub const X_TRANS_KEY: &str = "X-Trans-Key";
|
||||
pub const X_VERSION: &str = "X-Version";
|
||||
pub const X_DATE: &str = "X-Date";
|
||||
}
|
||||
|
||||
pub mod pii {
|
||||
|
||||
@ -167,6 +167,7 @@ impl ConnectorData {
|
||||
"braintree" => Ok(Box::new(&connector::Braintree)),
|
||||
"checkout" => Ok(Box::new(&connector::Checkout)),
|
||||
"cybersource" => Ok(Box::new(&connector::Cybersource)),
|
||||
"dlocal" => Ok(Box::new(&connector::Dlocal)),
|
||||
"fiserv" => Ok(Box::new(&connector::Fiserv)),
|
||||
"globalpay" => Ok(Box::new(&connector::Globalpay)),
|
||||
"klarna" => Ok(Box::new(&connector::Klarna)),
|
||||
|
||||
Reference in New Issue
Block a user