mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-30 01:27:31 +08:00
feat(connector): [Moneris] Add payments flow (#7249)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
@ -6993,6 +6993,7 @@
|
||||
"klarna",
|
||||
"mifinity",
|
||||
"mollie",
|
||||
"moneris",
|
||||
"multisafepay",
|
||||
"netcetera",
|
||||
"nexinets",
|
||||
@ -19361,6 +19362,7 @@
|
||||
"klarna",
|
||||
"mifinity",
|
||||
"mollie",
|
||||
"moneris",
|
||||
"multisafepay",
|
||||
"nexinets",
|
||||
"nexixpay",
|
||||
|
||||
@ -9559,6 +9559,7 @@
|
||||
"klarna",
|
||||
"mifinity",
|
||||
"mollie",
|
||||
"moneris",
|
||||
"multisafepay",
|
||||
"netcetera",
|
||||
"nexinets",
|
||||
@ -24453,6 +24454,7 @@
|
||||
"klarna",
|
||||
"mifinity",
|
||||
"mollie",
|
||||
"moneris",
|
||||
"multisafepay",
|
||||
"nexinets",
|
||||
"nexixpay",
|
||||
|
||||
@ -78,7 +78,7 @@ klarna.base_url = "https://api{{klarna_region}}.klarna.com/"
|
||||
mifinity.base_url = "https://secure.mifinity.com/"
|
||||
mollie.base_url = "https://api.mollie.com/v2/"
|
||||
mollie.secondary_base_url = "https://api.cc.mollie.com/v1/"
|
||||
moneris.base_url = "https://api.sb.moneris.io"
|
||||
moneris.base_url = "https://api.moneris.io"
|
||||
multisafepay.base_url = "https://testapi.multisafepay.com/"
|
||||
nexinets.base_url = "https://api.payengine.de/v1"
|
||||
nexixpay.base_url = "https://xpay.nexigroup.com/api/phoenix-0.0/psp/api/v1"
|
||||
|
||||
@ -93,7 +93,7 @@ pub enum RoutableConnectors {
|
||||
Klarna,
|
||||
Mifinity,
|
||||
Mollie,
|
||||
// Moneris,
|
||||
Moneris,
|
||||
Multisafepay,
|
||||
Nexinets,
|
||||
Nexixpay,
|
||||
@ -233,7 +233,7 @@ pub enum Connector {
|
||||
Klarna,
|
||||
Mifinity,
|
||||
Mollie,
|
||||
// Moneris,
|
||||
Moneris,
|
||||
Multisafepay,
|
||||
Netcetera,
|
||||
Nexinets,
|
||||
@ -318,6 +318,7 @@ impl Connector {
|
||||
| (Self::Deutschebank, _)
|
||||
| (Self::Globalpay, _)
|
||||
| (Self::Jpmorgan, _)
|
||||
| (Self::Moneris, _)
|
||||
| (Self::Paypal, _)
|
||||
| (Self::Payu, _)
|
||||
| (Self::Trustpay, PaymentMethod::BankRedirect)
|
||||
@ -384,7 +385,7 @@ impl Connector {
|
||||
| Self::Klarna
|
||||
| Self::Mifinity
|
||||
| Self::Mollie
|
||||
// | Self::Moneris
|
||||
| Self::Moneris
|
||||
| Self::Multisafepay
|
||||
| Self::Nexinets
|
||||
| Self::Nexixpay
|
||||
@ -513,6 +514,7 @@ impl From<RoutableConnectors> for Connector {
|
||||
RoutableConnectors::Klarna => Self::Klarna,
|
||||
RoutableConnectors::Mifinity => Self::Mifinity,
|
||||
RoutableConnectors::Mollie => Self::Mollie,
|
||||
RoutableConnectors::Moneris => Self::Moneris,
|
||||
RoutableConnectors::Multisafepay => Self::Multisafepay,
|
||||
RoutableConnectors::Nexinets => Self::Nexinets,
|
||||
RoutableConnectors::Nexixpay => Self::Nexixpay,
|
||||
|
||||
@ -203,6 +203,7 @@ pub struct ConnectorConfig {
|
||||
pub klarna: Option<ConnectorTomlConfig>,
|
||||
pub mifinity: Option<ConnectorTomlConfig>,
|
||||
pub mollie: Option<ConnectorTomlConfig>,
|
||||
pub moneris: Option<ConnectorTomlConfig>,
|
||||
pub multisafepay: Option<ConnectorTomlConfig>,
|
||||
pub nexinets: Option<ConnectorTomlConfig>,
|
||||
pub nexixpay: Option<ConnectorTomlConfig>,
|
||||
@ -367,6 +368,7 @@ impl ConnectorConfig {
|
||||
Connector::Klarna => Ok(connector_data.klarna),
|
||||
Connector::Mifinity => Ok(connector_data.mifinity),
|
||||
Connector::Mollie => Ok(connector_data.mollie),
|
||||
Connector::Moneris => Ok(connector_data.moneris),
|
||||
Connector::Multisafepay => Ok(connector_data.multisafepay),
|
||||
Connector::Nexinets => Ok(connector_data.nexinets),
|
||||
Connector::Nexixpay => Ok(connector_data.nexixpay),
|
||||
|
||||
@ -1878,6 +1878,48 @@ key1="Profile Token"
|
||||
[mollie.connector_webhook_details]
|
||||
merchant_secret="Source verification key"
|
||||
|
||||
[moneris]
|
||||
[[moneris.credit]]
|
||||
payment_method_type = "Mastercard"
|
||||
[[moneris.credit]]
|
||||
payment_method_type = "Visa"
|
||||
[[moneris.credit]]
|
||||
payment_method_type = "Interac"
|
||||
[[moneris.credit]]
|
||||
payment_method_type = "AmericanExpress"
|
||||
[[moneris.credit]]
|
||||
payment_method_type = "JCB"
|
||||
[[moneris.credit]]
|
||||
payment_method_type = "DinersClub"
|
||||
[[moneris.credit]]
|
||||
payment_method_type = "Discover"
|
||||
[[moneris.credit]]
|
||||
payment_method_type = "CartesBancaires"
|
||||
[[moneris.credit]]
|
||||
payment_method_type = "UnionPay"
|
||||
[[moneris.debit]]
|
||||
payment_method_type = "Mastercard"
|
||||
[[moneris.debit]]
|
||||
payment_method_type = "Visa"
|
||||
[[moneris.debit]]
|
||||
payment_method_type = "Interac"
|
||||
[[moneris.debit]]
|
||||
payment_method_type = "AmericanExpress"
|
||||
[[moneris.debit]]
|
||||
payment_method_type = "JCB"
|
||||
[[moneris.debit]]
|
||||
payment_method_type = "DinersClub"
|
||||
[[moneris.debit]]
|
||||
payment_method_type = "Discover"
|
||||
[[moneris.debit]]
|
||||
payment_method_type = "CartesBancaires"
|
||||
[[moneris.debit]]
|
||||
payment_method_type = "UnionPay"
|
||||
[moneris.connector_auth.SignatureKey]
|
||||
api_key="Client Secret"
|
||||
key1="Client Id"
|
||||
api_secret="Merchant Id"
|
||||
|
||||
[multisafepay]
|
||||
[[multisafepay.credit]]
|
||||
payment_method_type = "Mastercard"
|
||||
|
||||
@ -1588,6 +1588,48 @@ key1="Profile Token"
|
||||
[mollie.connector_webhook_details]
|
||||
merchant_secret="Source verification key"
|
||||
|
||||
[moneris]
|
||||
[[moneris.credit]]
|
||||
payment_method_type = "Mastercard"
|
||||
[[moneris.credit]]
|
||||
payment_method_type = "Visa"
|
||||
[[moneris.credit]]
|
||||
payment_method_type = "Interac"
|
||||
[[moneris.credit]]
|
||||
payment_method_type = "AmericanExpress"
|
||||
[[moneris.credit]]
|
||||
payment_method_type = "JCB"
|
||||
[[moneris.credit]]
|
||||
payment_method_type = "DinersClub"
|
||||
[[moneris.credit]]
|
||||
payment_method_type = "Discover"
|
||||
[[moneris.credit]]
|
||||
payment_method_type = "CartesBancaires"
|
||||
[[moneris.credit]]
|
||||
payment_method_type = "UnionPay"
|
||||
[[moneris.debit]]
|
||||
payment_method_type = "Mastercard"
|
||||
[[moneris.debit]]
|
||||
payment_method_type = "Visa"
|
||||
[[moneris.debit]]
|
||||
payment_method_type = "Interac"
|
||||
[[moneris.debit]]
|
||||
payment_method_type = "AmericanExpress"
|
||||
[[moneris.debit]]
|
||||
payment_method_type = "JCB"
|
||||
[[moneris.debit]]
|
||||
payment_method_type = "DinersClub"
|
||||
[[moneris.debit]]
|
||||
payment_method_type = "Discover"
|
||||
[[moneris.debit]]
|
||||
payment_method_type = "CartesBancaires"
|
||||
[[moneris.debit]]
|
||||
payment_method_type = "UnionPay"
|
||||
[moneris.connector_auth.SignatureKey]
|
||||
api_key="Client Secret"
|
||||
key1="Client Id"
|
||||
api_secret="Merchant Id"
|
||||
|
||||
[multisafepay]
|
||||
[[multisafepay.credit]]
|
||||
payment_method_type = "Mastercard"
|
||||
|
||||
@ -1826,6 +1826,48 @@ key1="Profile Token"
|
||||
[mollie.connector_webhook_details]
|
||||
merchant_secret="Source verification key"
|
||||
|
||||
[moneris]
|
||||
[[moneris.credit]]
|
||||
payment_method_type = "Mastercard"
|
||||
[[moneris.credit]]
|
||||
payment_method_type = "Visa"
|
||||
[[moneris.credit]]
|
||||
payment_method_type = "Interac"
|
||||
[[moneris.credit]]
|
||||
payment_method_type = "AmericanExpress"
|
||||
[[moneris.credit]]
|
||||
payment_method_type = "JCB"
|
||||
[[moneris.credit]]
|
||||
payment_method_type = "DinersClub"
|
||||
[[moneris.credit]]
|
||||
payment_method_type = "Discover"
|
||||
[[moneris.credit]]
|
||||
payment_method_type = "CartesBancaires"
|
||||
[[moneris.credit]]
|
||||
payment_method_type = "UnionPay"
|
||||
[[moneris.debit]]
|
||||
payment_method_type = "Mastercard"
|
||||
[[moneris.debit]]
|
||||
payment_method_type = "Visa"
|
||||
[[moneris.debit]]
|
||||
payment_method_type = "Interac"
|
||||
[[moneris.debit]]
|
||||
payment_method_type = "AmericanExpress"
|
||||
[[moneris.debit]]
|
||||
payment_method_type = "JCB"
|
||||
[[moneris.debit]]
|
||||
payment_method_type = "DinersClub"
|
||||
[[moneris.debit]]
|
||||
payment_method_type = "Discover"
|
||||
[[moneris.debit]]
|
||||
payment_method_type = "CartesBancaires"
|
||||
[[moneris.debit]]
|
||||
payment_method_type = "UnionPay"
|
||||
[moneris.connector_auth.SignatureKey]
|
||||
api_key="Client Secret"
|
||||
key1="Client Id"
|
||||
api_secret="Merchant Id"
|
||||
|
||||
[multisafepay]
|
||||
[[multisafepay.credit]]
|
||||
payment_method_type = "Mastercard"
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
pub mod transformers;
|
||||
|
||||
use common_enums::enums;
|
||||
use common_utils::{
|
||||
errors::CustomResult,
|
||||
ext_traits::BytesExt,
|
||||
request::{Method, Request, RequestBuilder, RequestContent},
|
||||
types::{AmountConvertor, StringMinorUnit, StringMinorUnitForConnector},
|
||||
types::{AmountConvertor, MinorUnit, MinorUnitForConnector},
|
||||
};
|
||||
use error_stack::{report, ResultExt};
|
||||
use hyperswitch_domain_models::{
|
||||
@ -21,8 +22,8 @@ use hyperswitch_domain_models::{
|
||||
},
|
||||
router_response_types::{PaymentsResponseData, RefundsResponseData},
|
||||
types::{
|
||||
PaymentsAuthorizeRouterData, PaymentsCaptureRouterData, PaymentsSyncRouterData,
|
||||
RefundSyncRouterData, RefundsRouterData,
|
||||
PaymentsAuthorizeRouterData, PaymentsCancelRouterData, PaymentsCaptureRouterData,
|
||||
PaymentsSyncRouterData, RefreshTokenRouterData, RefundSyncRouterData, RefundsRouterData,
|
||||
},
|
||||
};
|
||||
use hyperswitch_interfaces::{
|
||||
@ -33,23 +34,27 @@ use hyperswitch_interfaces::{
|
||||
configs::Connectors,
|
||||
errors,
|
||||
events::connector_api_logs::ConnectorEvent,
|
||||
types::{self, Response},
|
||||
types::{self, RefreshTokenType, Response},
|
||||
webhooks,
|
||||
};
|
||||
use masking::{ExposeInterface, Mask};
|
||||
use masking::{ExposeInterface, Mask, PeekInterface};
|
||||
use transformers as moneris;
|
||||
|
||||
use crate::{constants::headers, types::ResponseRouterData, utils};
|
||||
use crate::{
|
||||
constants::headers,
|
||||
types::ResponseRouterData,
|
||||
utils::{self, RefundsRequestData},
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Moneris {
|
||||
amount_converter: &'static (dyn AmountConvertor<Output = StringMinorUnit> + Sync),
|
||||
amount_converter: &'static (dyn AmountConvertor<Output = MinorUnit> + Sync),
|
||||
}
|
||||
|
||||
impl Moneris {
|
||||
pub fn new() -> &'static Self {
|
||||
&Self {
|
||||
amount_converter: &StringMinorUnitForConnector,
|
||||
amount_converter: &MinorUnitForConnector,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -82,12 +87,31 @@ where
|
||||
req: &RouterData<Flow, Request, Response>,
|
||||
_connectors: &Connectors,
|
||||
) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
|
||||
let mut header = vec![(
|
||||
let auth = moneris::MonerisAuthType::try_from(&req.connector_auth_type)
|
||||
.change_context(errors::ConnectorError::FailedToObtainAuthType)?;
|
||||
let mut header = vec![
|
||||
(
|
||||
headers::CONTENT_TYPE.to_string(),
|
||||
self.get_content_type().to_string().into(),
|
||||
)];
|
||||
let mut api_key = self.get_auth_header(&req.connector_auth_type)?;
|
||||
header.append(&mut api_key);
|
||||
),
|
||||
(
|
||||
moneris::auth_headers::API_VERSION.to_string(),
|
||||
"2024-09-17".to_string().into(),
|
||||
),
|
||||
(
|
||||
moneris::auth_headers::X_MERCHANT_ID.to_string(),
|
||||
auth.merchant_id.expose().into_masked(),
|
||||
),
|
||||
];
|
||||
let access_token = req
|
||||
.access_token
|
||||
.clone()
|
||||
.ok_or(errors::ConnectorError::FailedToObtainAuthType)?;
|
||||
let auth_header = (
|
||||
headers::AUTHORIZATION.to_string(),
|
||||
format!("Bearer {}", access_token.token.peek()).into_masked(),
|
||||
);
|
||||
header.push(auth_header);
|
||||
Ok(header)
|
||||
}
|
||||
}
|
||||
@ -117,7 +141,7 @@ impl ConnectorCommon for Moneris {
|
||||
.change_context(errors::ConnectorError::FailedToObtainAuthType)?;
|
||||
Ok(vec![(
|
||||
headers::AUTHORIZATION.to_string(),
|
||||
auth.api_key.expose().into_masked(),
|
||||
auth.client_id.expose().into_masked(),
|
||||
)])
|
||||
}
|
||||
|
||||
@ -134,11 +158,20 @@ impl ConnectorCommon for Moneris {
|
||||
event_builder.map(|i| i.set_response_body(&response));
|
||||
router_env::logger::info!(connector_response=?response);
|
||||
|
||||
let reason = match &response.errors {
|
||||
Some(error_list) => error_list
|
||||
.iter()
|
||||
.map(|error| error.parameter_name.clone())
|
||||
.collect::<Vec<String>>()
|
||||
.join(" & "),
|
||||
None => response.title.clone(),
|
||||
};
|
||||
|
||||
Ok(ErrorResponse {
|
||||
status_code: res.status_code,
|
||||
code: response.code,
|
||||
message: response.message,
|
||||
reason: response.reason,
|
||||
code: response.category,
|
||||
message: response.title,
|
||||
reason: Some(reason),
|
||||
attempt_status: None,
|
||||
connector_transaction_id: None,
|
||||
})
|
||||
@ -146,16 +179,133 @@ impl ConnectorCommon for Moneris {
|
||||
}
|
||||
|
||||
impl ConnectorValidation for Moneris {
|
||||
//TODO: implement functions when support enabled
|
||||
fn validate_connector_against_payment_request(
|
||||
&self,
|
||||
capture_method: Option<enums::CaptureMethod>,
|
||||
_payment_method: enums::PaymentMethod,
|
||||
_pmt: Option<enums::PaymentMethodType>,
|
||||
) -> CustomResult<(), errors::ConnectorError> {
|
||||
let capture_method = capture_method.unwrap_or_default();
|
||||
match capture_method {
|
||||
enums::CaptureMethod::Automatic | enums::CaptureMethod::Manual => Ok(()),
|
||||
enums::CaptureMethod::ManualMultiple
|
||||
| enums::CaptureMethod::Scheduled
|
||||
| enums::CaptureMethod::SequentialAutomatic => Err(
|
||||
utils::construct_not_implemented_error_report(capture_method, self.id()),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ConnectorIntegration<Session, PaymentsSessionData, PaymentsResponseData> for Moneris {
|
||||
//TODO: implement sessions flow
|
||||
}
|
||||
|
||||
impl ConnectorIntegration<AccessTokenAuth, AccessTokenRequestData, AccessToken> for Moneris {}
|
||||
impl ConnectorIntegration<AccessTokenAuth, AccessTokenRequestData, AccessToken> for Moneris {
|
||||
fn get_url(
|
||||
&self,
|
||||
_req: &RefreshTokenRouterData,
|
||||
connectors: &Connectors,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
Ok(format!("{}/oauth2/token", self.base_url(connectors)))
|
||||
}
|
||||
fn get_content_type(&self) -> &'static str {
|
||||
"application/x-www-form-urlencoded"
|
||||
}
|
||||
fn get_headers(
|
||||
&self,
|
||||
_req: &RefreshTokenRouterData,
|
||||
_connectors: &Connectors,
|
||||
) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
|
||||
Ok(vec![(
|
||||
headers::CONTENT_TYPE.to_string(),
|
||||
RefreshTokenType::get_content_type(self).to_string().into(),
|
||||
)])
|
||||
}
|
||||
|
||||
impl ConnectorIntegration<SetupMandate, SetupMandateRequestData, PaymentsResponseData> for Moneris {}
|
||||
fn get_request_body(
|
||||
&self,
|
||||
req: &RefreshTokenRouterData,
|
||||
_connectors: &Connectors,
|
||||
) -> CustomResult<RequestContent, errors::ConnectorError> {
|
||||
let connector_req = moneris::MonerisAuthRequest::try_from(req)?;
|
||||
Ok(RequestContent::FormUrlEncoded(Box::new(connector_req)))
|
||||
}
|
||||
|
||||
fn build_request(
|
||||
&self,
|
||||
req: &RefreshTokenRouterData,
|
||||
connectors: &Connectors,
|
||||
) -> CustomResult<Option<Request>, errors::ConnectorError> {
|
||||
let req = Some(
|
||||
RequestBuilder::new()
|
||||
.method(Method::Post)
|
||||
.attach_default_headers()
|
||||
.headers(RefreshTokenType::get_headers(self, req, connectors)?)
|
||||
.url(&RefreshTokenType::get_url(self, req, connectors)?)
|
||||
.set_body(RefreshTokenType::get_request_body(self, req, connectors)?)
|
||||
.build(),
|
||||
);
|
||||
Ok(req)
|
||||
}
|
||||
|
||||
fn handle_response(
|
||||
&self,
|
||||
data: &RefreshTokenRouterData,
|
||||
event_builder: Option<&mut ConnectorEvent>,
|
||||
res: Response,
|
||||
) -> CustomResult<RefreshTokenRouterData, errors::ConnectorError> {
|
||||
let response: moneris::MonerisAuthResponse = res
|
||||
.response
|
||||
.parse_struct("Moneris MonerisAuthResponse")
|
||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||
|
||||
event_builder.map(|i| i.set_response_body(&response));
|
||||
router_env::logger::info!(connector_response=?response);
|
||||
|
||||
RouterData::try_from(ResponseRouterData {
|
||||
response,
|
||||
data: data.clone(),
|
||||
http_code: res.status_code,
|
||||
})
|
||||
}
|
||||
|
||||
fn get_error_response(
|
||||
&self,
|
||||
res: Response,
|
||||
event_builder: Option<&mut ConnectorEvent>,
|
||||
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
|
||||
// auth error have different structure than common error
|
||||
let response: moneris::MonerisAuthErrorResponse = res
|
||||
.response
|
||||
.parse_struct("MonerisAuthErrorResponse")
|
||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||
event_builder.map(|i| i.set_error_response_body(&response));
|
||||
router_env::logger::info!(connector_response=?response);
|
||||
Ok(ErrorResponse {
|
||||
status_code: res.status_code,
|
||||
code: response.error.to_string(),
|
||||
message: response.error.clone(),
|
||||
reason: response.error_description,
|
||||
attempt_status: None,
|
||||
connector_transaction_id: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ConnectorIntegration<SetupMandate, SetupMandateRequestData, PaymentsResponseData> for Moneris {
|
||||
// Not Implemented (R)
|
||||
fn build_request(
|
||||
&self,
|
||||
_req: &RouterData<SetupMandate, SetupMandateRequestData, PaymentsResponseData>,
|
||||
_connectors: &Connectors,
|
||||
) -> CustomResult<Option<Request>, errors::ConnectorError> {
|
||||
Err(
|
||||
errors::ConnectorError::NotImplemented("Setup Mandate flow for Moneris".to_string())
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl ConnectorIntegration<Authorize, PaymentsAuthorizeData, PaymentsResponseData> for Moneris {
|
||||
fn get_headers(
|
||||
@ -173,9 +323,9 @@ impl ConnectorIntegration<Authorize, PaymentsAuthorizeData, PaymentsResponseData
|
||||
fn get_url(
|
||||
&self,
|
||||
_req: &PaymentsAuthorizeRouterData,
|
||||
_connectors: &Connectors,
|
||||
connectors: &Connectors,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into())
|
||||
Ok(format!("{}/payments", self.base_url(connectors)))
|
||||
}
|
||||
|
||||
fn get_request_body(
|
||||
@ -259,10 +409,18 @@ impl ConnectorIntegration<PSync, PaymentsSyncData, PaymentsResponseData> for Mon
|
||||
|
||||
fn get_url(
|
||||
&self,
|
||||
_req: &PaymentsSyncRouterData,
|
||||
_connectors: &Connectors,
|
||||
req: &PaymentsSyncRouterData,
|
||||
connectors: &Connectors,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into())
|
||||
let connector_payment_id = req
|
||||
.request
|
||||
.connector_transaction_id
|
||||
.get_connector_transaction_id()
|
||||
.change_context(errors::ConnectorError::MissingConnectorTransactionID)?;
|
||||
Ok(format!(
|
||||
"{}/payments/{connector_payment_id}",
|
||||
self.base_url(connectors)
|
||||
))
|
||||
}
|
||||
|
||||
fn build_request(
|
||||
@ -323,18 +481,31 @@ impl ConnectorIntegration<Capture, PaymentsCaptureData, PaymentsResponseData> fo
|
||||
|
||||
fn get_url(
|
||||
&self,
|
||||
_req: &PaymentsCaptureRouterData,
|
||||
_connectors: &Connectors,
|
||||
req: &PaymentsCaptureRouterData,
|
||||
connectors: &Connectors,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into())
|
||||
let connector_payment_id = req.request.connector_transaction_id.clone();
|
||||
Ok(format!(
|
||||
"{}/payments/{connector_payment_id}/complete",
|
||||
self.base_url(connectors)
|
||||
))
|
||||
}
|
||||
|
||||
fn get_request_body(
|
||||
&self,
|
||||
_req: &PaymentsCaptureRouterData,
|
||||
req: &PaymentsCaptureRouterData,
|
||||
_connectors: &Connectors,
|
||||
) -> CustomResult<RequestContent, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::NotImplemented("get_request_body method".to_string()).into())
|
||||
let amount = utils::convert_amount(
|
||||
self.amount_converter,
|
||||
req.request.minor_amount_to_capture,
|
||||
req.request.currency,
|
||||
)?;
|
||||
|
||||
let connector_router_data = moneris::MonerisRouterData::from((amount, req));
|
||||
let connector_req =
|
||||
moneris::MonerisPaymentsCaptureRequest::try_from(&connector_router_data)?;
|
||||
Ok(RequestContent::Json(Box::new(connector_req)))
|
||||
}
|
||||
|
||||
fn build_request(
|
||||
@ -385,7 +556,77 @@ impl ConnectorIntegration<Capture, PaymentsCaptureData, PaymentsResponseData> fo
|
||||
}
|
||||
}
|
||||
|
||||
impl ConnectorIntegration<Void, PaymentsCancelData, PaymentsResponseData> for Moneris {}
|
||||
impl ConnectorIntegration<Void, PaymentsCancelData, PaymentsResponseData> for Moneris {
|
||||
fn get_headers(
|
||||
&self,
|
||||
req: &PaymentsCancelRouterData,
|
||||
connectors: &Connectors,
|
||||
) -> CustomResult<Vec<(String, masking::Maskable<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: &PaymentsCancelRouterData,
|
||||
connectors: &Connectors,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
let connector_payment_id = req.request.connector_transaction_id.clone();
|
||||
Ok(format!(
|
||||
"{}/payments/{connector_payment_id}/cancel",
|
||||
self.base_url(connectors)
|
||||
))
|
||||
}
|
||||
|
||||
fn get_request_body(
|
||||
&self,
|
||||
req: &PaymentsCancelRouterData,
|
||||
_connectors: &Connectors,
|
||||
) -> CustomResult<RequestContent, errors::ConnectorError> {
|
||||
let connector_req = moneris::MonerisCancelRequest::try_from(req)?;
|
||||
Ok(RequestContent::Json(Box::new(connector_req)))
|
||||
}
|
||||
|
||||
fn build_request(
|
||||
&self,
|
||||
req: &PaymentsCancelRouterData,
|
||||
connectors: &Connectors,
|
||||
) -> CustomResult<Option<Request>, errors::ConnectorError> {
|
||||
Ok(Some(
|
||||
RequestBuilder::new()
|
||||
.method(Method::Post)
|
||||
.url(&types::PaymentsVoidType::get_url(self, req, connectors)?)
|
||||
.attach_default_headers()
|
||||
.headers(types::PaymentsVoidType::get_headers(self, req, connectors)?)
|
||||
.set_body(types::PaymentsVoidType::get_request_body(
|
||||
self, req, connectors,
|
||||
)?)
|
||||
.build(),
|
||||
))
|
||||
}
|
||||
|
||||
fn handle_response(
|
||||
&self,
|
||||
data: &PaymentsCancelRouterData,
|
||||
event_builder: Option<&mut ConnectorEvent>,
|
||||
res: Response,
|
||||
) -> CustomResult<PaymentsCancelRouterData, errors::ConnectorError> {
|
||||
let response: moneris::MonerisPaymentsResponse = res
|
||||
.response
|
||||
.parse_struct("Moneris PaymentsCancelResponse")
|
||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||
event_builder.map(|i| i.set_response_body(&response));
|
||||
router_env::logger::info!(connector_response=?response);
|
||||
RouterData::try_from(ResponseRouterData {
|
||||
response,
|
||||
data: data.clone(),
|
||||
http_code: res.status_code,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ConnectorIntegration<Execute, RefundsData, RefundsResponseData> for Moneris {
|
||||
fn get_headers(
|
||||
@ -403,9 +644,9 @@ impl ConnectorIntegration<Execute, RefundsData, RefundsResponseData> for Moneris
|
||||
fn get_url(
|
||||
&self,
|
||||
_req: &RefundsRouterData<Execute>,
|
||||
_connectors: &Connectors,
|
||||
connectors: &Connectors,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into())
|
||||
Ok(format!("{}/refunds", self.base_url(connectors)))
|
||||
}
|
||||
|
||||
fn get_request_body(
|
||||
@ -486,10 +727,11 @@ impl ConnectorIntegration<RSync, RefundsData, RefundsResponseData> for Moneris {
|
||||
|
||||
fn get_url(
|
||||
&self,
|
||||
_req: &RefundSyncRouterData,
|
||||
_connectors: &Connectors,
|
||||
req: &RefundSyncRouterData,
|
||||
connectors: &Connectors,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into())
|
||||
let refund_id = req.request.get_connector_refund_id()?;
|
||||
Ok(format!("{}/refunds/{refund_id}", self.base_url(connectors)))
|
||||
}
|
||||
|
||||
fn build_request(
|
||||
|
||||
@ -1,31 +1,38 @@
|
||||
use common_enums::enums;
|
||||
use common_utils::types::StringMinorUnit;
|
||||
use common_utils::types::MinorUnit;
|
||||
use error_stack::ResultExt;
|
||||
use hyperswitch_domain_models::{
|
||||
payment_method_data::PaymentMethodData,
|
||||
router_data::{ConnectorAuthType, RouterData},
|
||||
router_data::{AccessToken, ConnectorAuthType, RouterData},
|
||||
router_flow_types::refunds::{Execute, RSync},
|
||||
router_request_types::ResponseId,
|
||||
router_response_types::{PaymentsResponseData, RefundsResponseData},
|
||||
types::{PaymentsAuthorizeRouterData, RefundsRouterData},
|
||||
types::{
|
||||
PaymentsAuthorizeRouterData, PaymentsCancelRouterData, PaymentsCaptureRouterData,
|
||||
RefundsRouterData,
|
||||
},
|
||||
};
|
||||
use hyperswitch_interfaces::errors;
|
||||
use masking::Secret;
|
||||
use masking::{PeekInterface, Secret};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
types::{RefundsResponseRouterData, ResponseRouterData},
|
||||
utils::PaymentsAuthorizeRequestData,
|
||||
types::{RefreshTokenRouterData, RefundsResponseRouterData, ResponseRouterData},
|
||||
utils::{
|
||||
BrowserInformationData, CardData as _, PaymentsAuthorizeRequestData,
|
||||
RouterData as OtherRouterData,
|
||||
},
|
||||
};
|
||||
|
||||
//TODO: Fill the struct with respective fields
|
||||
const CLIENT_CREDENTIALS: &str = "client_credentials";
|
||||
|
||||
pub struct MonerisRouterData<T> {
|
||||
pub amount: StringMinorUnit, // The type of amount that a connector accepts, for example, String, i64, f64, etc.
|
||||
pub amount: MinorUnit, // The type of amount that a connector accepts, for example, String, i64, f64, etc.
|
||||
pub router_data: T,
|
||||
}
|
||||
|
||||
impl<T> From<(StringMinorUnit, T)> for MonerisRouterData<T> {
|
||||
fn from((amount, item): (StringMinorUnit, T)) -> Self {
|
||||
//Todo : use utils to convert the amount to the type of amount that a connector accepts
|
||||
impl<T> From<(MinorUnit, T)> for MonerisRouterData<T> {
|
||||
fn from((amount, item): (MinorUnit, T)) -> Self {
|
||||
Self {
|
||||
amount,
|
||||
router_data: item,
|
||||
@ -33,20 +40,47 @@ impl<T> From<(StringMinorUnit, T)> for MonerisRouterData<T> {
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: Fill the struct with respective fields
|
||||
#[derive(Default, Debug, Serialize, PartialEq)]
|
||||
pub mod auth_headers {
|
||||
pub const X_MERCHANT_ID: &str = "X-Merchant-Id";
|
||||
pub const API_VERSION: &str = "Api-Version";
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct MonerisPaymentsRequest {
|
||||
amount: StringMinorUnit,
|
||||
idempotency_key: String,
|
||||
amount: Amount,
|
||||
payment_method: PaymentMethod,
|
||||
automatic_capture: bool,
|
||||
ipv4: Secret<String, common_utils::pii::IpAddress>,
|
||||
}
|
||||
#[derive(Default, Debug, Serialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Amount {
|
||||
currency: enums::Currency,
|
||||
amount: MinorUnit,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PaymentMethod {
|
||||
payment_method_source: PaymentMethodSource,
|
||||
card: MonerisCard,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, PartialEq)]
|
||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||
pub enum PaymentMethodSource {
|
||||
Card,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Serialize, Eq, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct MonerisCard {
|
||||
number: cards::CardNumber,
|
||||
expiry_month: Secret<String>,
|
||||
expiry_year: Secret<String>,
|
||||
cvc: Secret<String>,
|
||||
complete: bool,
|
||||
card_number: cards::CardNumber,
|
||||
expiry_month: Secret<i64>,
|
||||
expiry_year: Secret<i64>,
|
||||
card_security_code: Secret<String>,
|
||||
}
|
||||
|
||||
impl TryFrom<&MonerisRouterData<&PaymentsAuthorizeRouterData>> for MonerisPaymentsRequest {
|
||||
@ -54,18 +88,51 @@ impl TryFrom<&MonerisRouterData<&PaymentsAuthorizeRouterData>> for MonerisPaymen
|
||||
fn try_from(
|
||||
item: &MonerisRouterData<&PaymentsAuthorizeRouterData>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
match item.router_data.request.payment_method_data.clone() {
|
||||
PaymentMethodData::Card(req_card) => {
|
||||
let card = MonerisCard {
|
||||
number: req_card.card_number,
|
||||
expiry_month: req_card.card_exp_month,
|
||||
expiry_year: req_card.card_exp_year,
|
||||
cvc: req_card.card_cvc,
|
||||
complete: item.router_data.request.is_auto_capture()?,
|
||||
if item.router_data.is_three_ds() {
|
||||
Err(errors::ConnectorError::NotSupported {
|
||||
message: "Card 3DS".to_string(),
|
||||
connector: "Moneris",
|
||||
})?
|
||||
};
|
||||
match item.router_data.request.payment_method_data.clone() {
|
||||
PaymentMethodData::Card(ref req_card) => {
|
||||
let idempotency_key = uuid::Uuid::new_v4().to_string();
|
||||
let amount = Amount {
|
||||
currency: item.router_data.request.currency,
|
||||
amount: item.amount,
|
||||
};
|
||||
let payment_method = PaymentMethod {
|
||||
payment_method_source: PaymentMethodSource::Card,
|
||||
card: MonerisCard {
|
||||
card_number: req_card.card_number.clone(),
|
||||
expiry_month: Secret::new(
|
||||
req_card
|
||||
.card_exp_month
|
||||
.peek()
|
||||
.parse::<i64>()
|
||||
.change_context(errors::ConnectorError::ParsingFailed)?,
|
||||
),
|
||||
expiry_year: Secret::new(
|
||||
req_card
|
||||
.get_expiry_year_4_digit()
|
||||
.peek()
|
||||
.parse::<i64>()
|
||||
.change_context(errors::ConnectorError::ParsingFailed)?,
|
||||
),
|
||||
card_security_code: req_card.card_cvc.clone(),
|
||||
},
|
||||
};
|
||||
let automatic_capture = item.router_data.request.is_auto_capture()?;
|
||||
|
||||
let browser_info = item.router_data.request.get_browser_info()?;
|
||||
let ipv4 = browser_info.get_ip_address()?;
|
||||
|
||||
Ok(Self {
|
||||
amount: item.amount.clone(),
|
||||
card,
|
||||
idempotency_key,
|
||||
amount,
|
||||
payment_method,
|
||||
automatic_capture,
|
||||
ipv4,
|
||||
})
|
||||
}
|
||||
_ => Err(errors::ConnectorError::NotImplemented("Payment method".to_string()).into()),
|
||||
@ -73,49 +140,106 @@ impl TryFrom<&MonerisRouterData<&PaymentsAuthorizeRouterData>> for MonerisPaymen
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: Fill the struct with respective fields
|
||||
// Auth Struct
|
||||
pub struct MonerisAuthType {
|
||||
pub(super) api_key: Secret<String>,
|
||||
pub(super) client_id: Secret<String>,
|
||||
pub(super) client_secret: Secret<String>,
|
||||
pub(super) merchant_id: Secret<String>,
|
||||
}
|
||||
|
||||
impl TryFrom<&ConnectorAuthType> for MonerisAuthType {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(auth_type: &ConnectorAuthType) -> Result<Self, Self::Error> {
|
||||
match auth_type {
|
||||
ConnectorAuthType::HeaderKey { api_key } => Ok(Self {
|
||||
api_key: api_key.to_owned(),
|
||||
ConnectorAuthType::SignatureKey {
|
||||
api_key,
|
||||
key1,
|
||||
api_secret,
|
||||
} => Ok(Self {
|
||||
client_id: key1.to_owned(),
|
||||
client_secret: api_key.to_owned(),
|
||||
merchant_id: api_secret.to_owned(),
|
||||
}),
|
||||
_ => Err(errors::ConnectorError::FailedToObtainAuthType.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
// PaymentsResponse
|
||||
//TODO: Append the remaining status flags
|
||||
|
||||
#[derive(Debug, Clone, Serialize, PartialEq)]
|
||||
pub struct MonerisAuthRequest {
|
||||
client_id: Secret<String>,
|
||||
client_secret: Secret<String>,
|
||||
grant_type: String,
|
||||
}
|
||||
|
||||
impl TryFrom<&RefreshTokenRouterData> for MonerisAuthRequest {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(item: &RefreshTokenRouterData) -> Result<Self, Self::Error> {
|
||||
let auth = MonerisAuthType::try_from(&item.connector_auth_type)?;
|
||||
Ok(Self {
|
||||
client_id: auth.client_id.clone(),
|
||||
client_secret: auth.client_secret.clone(),
|
||||
grant_type: CLIENT_CREDENTIALS.to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub struct MonerisAuthResponse {
|
||||
access_token: Secret<String>,
|
||||
token_type: String,
|
||||
expires_in: String,
|
||||
}
|
||||
|
||||
impl<F, T> TryFrom<ResponseRouterData<F, MonerisAuthResponse, T, AccessToken>>
|
||||
for RouterData<F, T, AccessToken>
|
||||
{
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(
|
||||
item: ResponseRouterData<F, MonerisAuthResponse, T, AccessToken>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
response: Ok(AccessToken {
|
||||
token: item.response.access_token,
|
||||
expires: item
|
||||
.response
|
||||
.expires_in
|
||||
.parse::<i64>()
|
||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?,
|
||||
}),
|
||||
..item.data
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||
pub enum MonerisPaymentStatus {
|
||||
Succeeded,
|
||||
Failed,
|
||||
#[default]
|
||||
Processing,
|
||||
Canceled,
|
||||
Declined,
|
||||
DeclinedRetry,
|
||||
Authorized,
|
||||
}
|
||||
|
||||
impl From<MonerisPaymentStatus> for common_enums::AttemptStatus {
|
||||
fn from(item: MonerisPaymentStatus) -> Self {
|
||||
match item {
|
||||
MonerisPaymentStatus::Succeeded => Self::Charged,
|
||||
MonerisPaymentStatus::Failed => Self::Failure,
|
||||
MonerisPaymentStatus::Processing => Self::Authorizing,
|
||||
MonerisPaymentStatus::Authorized => Self::Authorized,
|
||||
MonerisPaymentStatus::Canceled => Self::Voided,
|
||||
MonerisPaymentStatus::Declined | MonerisPaymentStatus::DeclinedRetry => Self::Failure,
|
||||
MonerisPaymentStatus::Processing => Self::Pending,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: Fill the struct with respective fields
|
||||
#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct MonerisPaymentsResponse {
|
||||
status: MonerisPaymentStatus,
|
||||
id: String,
|
||||
payment_status: MonerisPaymentStatus,
|
||||
payment_id: String,
|
||||
}
|
||||
|
||||
impl<F, T> TryFrom<ResponseRouterData<F, MonerisPaymentsResponse, T, PaymentsResponseData>>
|
||||
@ -126,14 +250,14 @@ impl<F, T> TryFrom<ResponseRouterData<F, MonerisPaymentsResponse, T, PaymentsRes
|
||||
item: ResponseRouterData<F, MonerisPaymentsResponse, T, PaymentsResponseData>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
status: common_enums::AttemptStatus::from(item.response.status),
|
||||
status: common_enums::AttemptStatus::from(item.response.payment_status),
|
||||
response: Ok(PaymentsResponseData::TransactionResponse {
|
||||
resource_id: ResponseId::ConnectorTransactionId(item.response.id),
|
||||
resource_id: ResponseId::ConnectorTransactionId(item.response.payment_id.clone()),
|
||||
redirection_data: Box::new(None),
|
||||
mandate_reference: Box::new(None),
|
||||
connector_metadata: None,
|
||||
network_txn_id: None,
|
||||
connector_response_reference_id: None,
|
||||
connector_response_reference_id: Some(item.response.payment_id),
|
||||
incremental_authorization_allowed: None,
|
||||
charges: None,
|
||||
}),
|
||||
@ -142,50 +266,101 @@ impl<F, T> TryFrom<ResponseRouterData<F, MonerisPaymentsResponse, T, PaymentsRes
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: Fill the struct with respective fields
|
||||
// REFUND :
|
||||
// Type definition for RefundRequest
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct MonerisPaymentsCaptureRequest {
|
||||
amount: Amount,
|
||||
idempotency_key: String,
|
||||
}
|
||||
|
||||
impl TryFrom<&MonerisRouterData<&PaymentsCaptureRouterData>> for MonerisPaymentsCaptureRequest {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(item: &MonerisRouterData<&PaymentsCaptureRouterData>) -> Result<Self, Self::Error> {
|
||||
let amount = Amount {
|
||||
currency: item.router_data.request.currency,
|
||||
amount: item.amount,
|
||||
};
|
||||
let idempotency_key = uuid::Uuid::new_v4().to_string();
|
||||
Ok(Self {
|
||||
amount,
|
||||
idempotency_key,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct MonerisCancelRequest {
|
||||
idempotency_key: String,
|
||||
reason: Option<String>,
|
||||
}
|
||||
|
||||
impl TryFrom<&PaymentsCancelRouterData> for MonerisCancelRequest {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(item: &PaymentsCancelRouterData) -> Result<Self, Self::Error> {
|
||||
let idempotency_key = uuid::Uuid::new_v4().to_string();
|
||||
let reason = item.request.cancellation_reason.clone();
|
||||
Ok(Self {
|
||||
idempotency_key,
|
||||
reason,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct MonerisRefundRequest {
|
||||
pub amount: StringMinorUnit,
|
||||
pub refund_amount: Amount,
|
||||
pub idempotency_key: String,
|
||||
pub reason: Option<String>,
|
||||
pub payment_id: String,
|
||||
}
|
||||
|
||||
impl<F> TryFrom<&MonerisRouterData<&RefundsRouterData<F>>> for MonerisRefundRequest {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(item: &MonerisRouterData<&RefundsRouterData<F>>) -> Result<Self, Self::Error> {
|
||||
let refund_amount = Amount {
|
||||
currency: item.router_data.request.currency,
|
||||
amount: item.amount,
|
||||
};
|
||||
let idempotency_key = uuid::Uuid::new_v4().to_string();
|
||||
let reason = item.router_data.request.reason.clone();
|
||||
let payment_id = item.router_data.request.connector_transaction_id.clone();
|
||||
Ok(Self {
|
||||
amount: item.amount.to_owned(),
|
||||
refund_amount,
|
||||
idempotency_key,
|
||||
reason,
|
||||
payment_id,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Type definition for Refund Response
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug, Serialize, Default, Deserialize, Clone)]
|
||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||
pub enum RefundStatus {
|
||||
Succeeded,
|
||||
Failed,
|
||||
#[default]
|
||||
Processing,
|
||||
Declined,
|
||||
DeclinedRetry,
|
||||
}
|
||||
|
||||
impl From<RefundStatus> for enums::RefundStatus {
|
||||
fn from(item: RefundStatus) -> Self {
|
||||
match item {
|
||||
RefundStatus::Succeeded => Self::Success,
|
||||
RefundStatus::Failed => Self::Failure,
|
||||
RefundStatus::Declined | RefundStatus::DeclinedRetry => Self::Failure,
|
||||
RefundStatus::Processing => Self::Pending,
|
||||
//TODO: Review mapping
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: Fill the struct with respective fields
|
||||
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RefundResponse {
|
||||
id: String,
|
||||
status: RefundStatus,
|
||||
refund_id: String,
|
||||
refund_status: RefundStatus,
|
||||
}
|
||||
|
||||
impl TryFrom<RefundsResponseRouterData<Execute, RefundResponse>> for RefundsRouterData<Execute> {
|
||||
@ -195,8 +370,8 @@ impl TryFrom<RefundsResponseRouterData<Execute, RefundResponse>> for RefundsRout
|
||||
) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
response: Ok(RefundsResponseData {
|
||||
connector_refund_id: item.response.id.to_string(),
|
||||
refund_status: enums::RefundStatus::from(item.response.status),
|
||||
connector_refund_id: item.response.refund_id.to_string(),
|
||||
refund_status: enums::RefundStatus::from(item.response.refund_status),
|
||||
}),
|
||||
..item.data
|
||||
})
|
||||
@ -210,19 +385,32 @@ impl TryFrom<RefundsResponseRouterData<RSync, RefundResponse>> for RefundsRouter
|
||||
) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
response: Ok(RefundsResponseData {
|
||||
connector_refund_id: item.response.id.to_string(),
|
||||
refund_status: enums::RefundStatus::from(item.response.status),
|
||||
connector_refund_id: item.response.refund_id.to_string(),
|
||||
refund_status: enums::RefundStatus::from(item.response.refund_status),
|
||||
}),
|
||||
..item.data
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: Fill the struct with respective fields
|
||||
#[derive(Default, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct MonerisErrorResponse {
|
||||
pub status_code: u16,
|
||||
pub code: String,
|
||||
pub message: String,
|
||||
pub reason: Option<String>,
|
||||
pub status: u16,
|
||||
pub category: String,
|
||||
pub title: String,
|
||||
pub errors: Option<Vec<MonerisError>>,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct MonerisError {
|
||||
pub reason_code: String,
|
||||
pub parameter_name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct MonerisAuthErrorResponse {
|
||||
pub error: String,
|
||||
pub error_description: Option<String>,
|
||||
}
|
||||
|
||||
@ -1997,6 +1997,53 @@ impl Default for settings::RequiredFields {
|
||||
common: HashMap::new(),
|
||||
}
|
||||
),
|
||||
(
|
||||
enums::Connector::Moneris,
|
||||
RequiredFieldFinal {
|
||||
mandate: HashMap::new(),
|
||||
non_mandate: HashMap::from(
|
||||
[
|
||||
(
|
||||
"payment_method_data.card.card_number".to_string(),
|
||||
RequiredFieldInfo {
|
||||
required_field: "payment_method_data.card.card_number".to_string(),
|
||||
display_name: "card_number".to_string(),
|
||||
field_type: enums::FieldType::UserCardNumber,
|
||||
value: None,
|
||||
}
|
||||
),
|
||||
(
|
||||
"payment_method_data.card.card_exp_month".to_string(),
|
||||
RequiredFieldInfo {
|
||||
required_field: "payment_method_data.card.card_exp_month".to_string(),
|
||||
display_name: "card_exp_month".to_string(),
|
||||
field_type: enums::FieldType::UserCardExpiryMonth,
|
||||
value: None,
|
||||
}
|
||||
),
|
||||
(
|
||||
"payment_method_data.card.card_exp_year".to_string(),
|
||||
RequiredFieldInfo {
|
||||
required_field: "payment_method_data.card.card_exp_year".to_string(),
|
||||
display_name: "card_exp_year".to_string(),
|
||||
field_type: enums::FieldType::UserCardExpiryYear,
|
||||
value: None,
|
||||
}
|
||||
),
|
||||
(
|
||||
"payment_method_data.card.card_cvc".to_string(),
|
||||
RequiredFieldInfo {
|
||||
required_field: "payment_method_data.card.card_cvc".to_string(),
|
||||
display_name: "card_cvc".to_string(),
|
||||
field_type: enums::FieldType::UserCardCvc,
|
||||
value: None,
|
||||
}
|
||||
),
|
||||
]
|
||||
),
|
||||
common: HashMap::new(),
|
||||
}
|
||||
),
|
||||
(
|
||||
enums::Connector::Multisafepay,
|
||||
RequiredFieldFinal {
|
||||
@ -5457,6 +5504,53 @@ impl Default for settings::RequiredFields {
|
||||
common: HashMap::new(),
|
||||
}
|
||||
),
|
||||
(
|
||||
enums::Connector::Moneris,
|
||||
RequiredFieldFinal {
|
||||
mandate: HashMap::new(),
|
||||
non_mandate: HashMap::from(
|
||||
[
|
||||
(
|
||||
"payment_method_data.card.card_number".to_string(),
|
||||
RequiredFieldInfo {
|
||||
required_field: "payment_method_data.card.card_number".to_string(),
|
||||
display_name: "card_number".to_string(),
|
||||
field_type: enums::FieldType::UserCardNumber,
|
||||
value: None,
|
||||
}
|
||||
),
|
||||
(
|
||||
"payment_method_data.card.card_exp_month".to_string(),
|
||||
RequiredFieldInfo {
|
||||
required_field: "payment_method_data.card.card_exp_month".to_string(),
|
||||
display_name: "card_exp_month".to_string(),
|
||||
field_type: enums::FieldType::UserCardExpiryMonth,
|
||||
value: None,
|
||||
}
|
||||
),
|
||||
(
|
||||
"payment_method_data.card.card_exp_year".to_string(),
|
||||
RequiredFieldInfo {
|
||||
required_field: "payment_method_data.card.card_exp_year".to_string(),
|
||||
display_name: "card_exp_year".to_string(),
|
||||
field_type: enums::FieldType::UserCardExpiryYear,
|
||||
value: None,
|
||||
}
|
||||
),
|
||||
(
|
||||
"payment_method_data.card.card_cvc".to_string(),
|
||||
RequiredFieldInfo {
|
||||
required_field: "payment_method_data.card.card_cvc".to_string(),
|
||||
display_name: "card_cvc".to_string(),
|
||||
field_type: enums::FieldType::UserCardCvc,
|
||||
value: None,
|
||||
}
|
||||
),
|
||||
]
|
||||
),
|
||||
common: HashMap::new(),
|
||||
}
|
||||
),
|
||||
(
|
||||
enums::Connector::Multisafepay,
|
||||
RequiredFieldFinal {
|
||||
|
||||
@ -1433,6 +1433,10 @@ impl ConnectorAuthTypeAndMetadataValidation<'_> {
|
||||
mollie::transformers::MollieAuthType::try_from(self.auth_type)?;
|
||||
Ok(())
|
||||
}
|
||||
api_enums::Connector::Moneris => {
|
||||
moneris::transformers::MonerisAuthType::try_from(self.auth_type)?;
|
||||
Ok(())
|
||||
}
|
||||
api_enums::Connector::Multisafepay => {
|
||||
multisafepay::transformers::MultisafepayAuthType::try_from(self.auth_type)?;
|
||||
Ok(())
|
||||
|
||||
@ -464,6 +464,9 @@ impl ConnectorData {
|
||||
// enums::Connector::Moneris => Ok(ConnectorEnum::Old(Box::new(connector::Moneris))),
|
||||
Ok(ConnectorEnum::Old(Box::new(connector::Mollie::new())))
|
||||
}
|
||||
enums::Connector::Moneris => {
|
||||
Ok(ConnectorEnum::Old(Box::new(connector::Moneris::new())))
|
||||
}
|
||||
enums::Connector::Nexixpay => {
|
||||
Ok(ConnectorEnum::Old(Box::new(connector::Nexixpay::new())))
|
||||
}
|
||||
|
||||
@ -263,7 +263,7 @@ impl ForeignTryFrom<api_enums::Connector> for common_enums::RoutableConnectors {
|
||||
api_enums::Connector::Klarna => Self::Klarna,
|
||||
api_enums::Connector::Mifinity => Self::Mifinity,
|
||||
api_enums::Connector::Mollie => Self::Mollie,
|
||||
// api_enums::Connector::Moneris => Self::Moneris,
|
||||
api_enums::Connector::Moneris => Self::Moneris,
|
||||
api_enums::Connector::Multisafepay => Self::Multisafepay,
|
||||
api_enums::Connector::Netcetera => {
|
||||
Err(common_utils::errors::ValidationError::InvalidValue {
|
||||
|
||||
@ -13,7 +13,7 @@ impl utils::Connector for MonerisTest {
|
||||
use router::connector::Moneris;
|
||||
utils::construct_connector_data_old(
|
||||
Box::new(Moneris::new()),
|
||||
types::Connector::Plaid,
|
||||
types::Connector::Moneris,
|
||||
api::GetToken::Connector,
|
||||
None,
|
||||
)
|
||||
|
||||
@ -57,7 +57,7 @@ pub struct ConnectorAuthentication {
|
||||
pub jpmorgan: Option<BodyKey>,
|
||||
pub mifinity: Option<HeaderKey>,
|
||||
pub mollie: Option<BodyKey>,
|
||||
pub moneris: Option<HeaderKey>,
|
||||
pub moneris: Option<SignatureKey>,
|
||||
pub multisafepay: Option<HeaderKey>,
|
||||
pub netcetera: Option<HeaderKey>,
|
||||
pub nexinets: Option<BodyKey>,
|
||||
|
||||
316
cypress-tests/cypress/e2e/configs/Payment/Moneris.js
Normal file
316
cypress-tests/cypress/e2e/configs/Payment/Moneris.js
Normal file
@ -0,0 +1,316 @@
|
||||
import { getCustomExchange } from "./Modifiers";
|
||||
|
||||
const successfulNo3DSCardDetails = {
|
||||
card_number: "4242424242424242",
|
||||
card_exp_month: "01",
|
||||
card_exp_year: "27",
|
||||
card_holder_name: "John",
|
||||
card_cvc: "123",
|
||||
};
|
||||
|
||||
const successfulThreeDSTestCardDetails = {
|
||||
card_number: "4242424242424242",
|
||||
card_exp_month: "01",
|
||||
card_exp_year: "27",
|
||||
card_holder_name: "Joseph",
|
||||
card_cvc: "123",
|
||||
};
|
||||
|
||||
export const connectorDetails = {
|
||||
card_pm: {
|
||||
PaymentIntent: {
|
||||
Request: {
|
||||
currency: "USD",
|
||||
amount: 5000,
|
||||
customer_acceptance: null,
|
||||
setup_future_usage: "on_session",
|
||||
},
|
||||
Response: {
|
||||
status: 200,
|
||||
body: {
|
||||
status: "requires_payment_method",
|
||||
},
|
||||
},
|
||||
},
|
||||
PaymentIntentWithShippingCost: {
|
||||
Request: {
|
||||
currency: "USD",
|
||||
amount: 5000,
|
||||
shipping_cost: 50,
|
||||
},
|
||||
Response: {
|
||||
status: 200,
|
||||
body: {
|
||||
status: "requires_payment_method",
|
||||
amount: 5000,
|
||||
shipping_cost: 50,
|
||||
},
|
||||
},
|
||||
},
|
||||
PaymentConfirmWithShippingCost: {
|
||||
Request: {
|
||||
payment_method: "card",
|
||||
payment_method_data: {
|
||||
card: successfulNo3DSCardDetails,
|
||||
},
|
||||
customer_acceptance: null,
|
||||
setup_future_usage: "on_session",
|
||||
},
|
||||
Response: {
|
||||
status: 200,
|
||||
body: {
|
||||
status: "succeeded",
|
||||
shipping_cost: 50,
|
||||
amount_received: 5050,
|
||||
amount: 5000,
|
||||
net_amount: 5050,
|
||||
},
|
||||
},
|
||||
},
|
||||
"3DSManualCapture": getCustomExchange({
|
||||
Request: {
|
||||
amount: 5000,
|
||||
payment_method: "card",
|
||||
payment_method_data: {
|
||||
card: successfulThreeDSTestCardDetails,
|
||||
},
|
||||
currency: "USD",
|
||||
customer_acceptance: null,
|
||||
setup_future_usage: "on_session",
|
||||
},
|
||||
}),
|
||||
"3DSAutoCapture": getCustomExchange({
|
||||
Request: {
|
||||
payment_method: "card",
|
||||
amount: 5000,
|
||||
payment_method_data: {
|
||||
card: successfulThreeDSTestCardDetails,
|
||||
},
|
||||
currency: "USD",
|
||||
customer_acceptance: null,
|
||||
setup_future_usage: "on_session",
|
||||
},
|
||||
}),
|
||||
No3DSManualCapture: {
|
||||
Request: {
|
||||
payment_method: "card",
|
||||
amount: 5000,
|
||||
payment_method_data: {
|
||||
card: successfulNo3DSCardDetails,
|
||||
},
|
||||
currency: "USD",
|
||||
customer_acceptance: null,
|
||||
setup_future_usage: "on_session",
|
||||
},
|
||||
Response: {
|
||||
status: 200,
|
||||
body: {
|
||||
status: "requires_capture",
|
||||
},
|
||||
},
|
||||
},
|
||||
No3DSAutoCapture: {
|
||||
Request: {
|
||||
payment_method: "card",
|
||||
amount: 5000,
|
||||
payment_method_data: {
|
||||
card: successfulNo3DSCardDetails,
|
||||
},
|
||||
currency: "USD",
|
||||
customer_acceptance: null,
|
||||
setup_future_usage: "on_session",
|
||||
},
|
||||
Response: {
|
||||
status: 200,
|
||||
body: {
|
||||
status: "succeeded",
|
||||
},
|
||||
},
|
||||
},
|
||||
Capture: {
|
||||
Request: {
|
||||
amount_to_capture: 5000,
|
||||
},
|
||||
Response: {
|
||||
status: 200,
|
||||
body: {
|
||||
status: "succeeded",
|
||||
amount: 5000,
|
||||
amount_capturable: 0,
|
||||
amount_received: 5000,
|
||||
},
|
||||
},
|
||||
},
|
||||
PartialCapture: {
|
||||
Request: {
|
||||
amount_to_capture: 2000,
|
||||
},
|
||||
Response: {
|
||||
status: 200,
|
||||
body: {
|
||||
status: "partially_captured",
|
||||
amount: 5000,
|
||||
amount_capturable: 0,
|
||||
amount_received: 2000,
|
||||
},
|
||||
},
|
||||
},
|
||||
Void: {
|
||||
Request: {},
|
||||
Response: {
|
||||
status: 200,
|
||||
body: {
|
||||
status: "cancelled",
|
||||
},
|
||||
},
|
||||
},
|
||||
Refund: {
|
||||
Request: {
|
||||
amount: 5000,
|
||||
},
|
||||
Response: {
|
||||
status: 200,
|
||||
body: {
|
||||
status: "succeeded",
|
||||
},
|
||||
},
|
||||
},
|
||||
PartialRefund: {
|
||||
Request: {
|
||||
amount: 2000,
|
||||
},
|
||||
Response: {
|
||||
status: 200,
|
||||
body: {
|
||||
status: "succeeded",
|
||||
},
|
||||
},
|
||||
},
|
||||
manualPaymentRefund: {
|
||||
Request: {
|
||||
amount: 5000,
|
||||
},
|
||||
Response: {
|
||||
status: 200,
|
||||
body: {
|
||||
status: "succeeded",
|
||||
},
|
||||
},
|
||||
},
|
||||
manualPaymentPartialRefund: {
|
||||
Request: {
|
||||
amount: 2000,
|
||||
},
|
||||
Response: {
|
||||
status: 200,
|
||||
body: {
|
||||
status: "succeeded",
|
||||
},
|
||||
},
|
||||
},
|
||||
SyncRefund: {
|
||||
Response: {
|
||||
status: 200,
|
||||
body: {
|
||||
status: "succeeded",
|
||||
},
|
||||
},
|
||||
},
|
||||
ZeroAuthMandate: {
|
||||
Response: {
|
||||
status: 501,
|
||||
body: {
|
||||
error: {
|
||||
type: "invalid_request",
|
||||
message: "Setup Mandate flow for Moneris is not implemented",
|
||||
code: "IR_00",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ZeroAuthPaymentIntent: {
|
||||
Request: {
|
||||
amount: 0,
|
||||
setup_future_usage: "off_session",
|
||||
currency: "USD",
|
||||
},
|
||||
Response: {
|
||||
status: 200,
|
||||
body: {
|
||||
status: "requires_payment_method",
|
||||
setup_future_usage: "off_session",
|
||||
},
|
||||
},
|
||||
},
|
||||
ZeroAuthConfirmPayment: {
|
||||
Request: {
|
||||
payment_type: "setup_mandate",
|
||||
payment_method: "card",
|
||||
payment_method_type: "credit",
|
||||
payment_method_data: {
|
||||
card: successfulNo3DSCardDetails,
|
||||
},
|
||||
},
|
||||
Response: {
|
||||
status: 501,
|
||||
body: {
|
||||
error: {
|
||||
type: "invalid_request",
|
||||
message: "Setup Mandate flow for Moneris is not implemented",
|
||||
code: "IR_00",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
SaveCardUseNo3DSAutoCapture: {
|
||||
Request: {
|
||||
payment_method: "card",
|
||||
amount: 5000,
|
||||
payment_method_data: {
|
||||
card: successfulNo3DSCardDetails,
|
||||
},
|
||||
currency: "USD",
|
||||
setup_future_usage: "on_session",
|
||||
customer_acceptance: {
|
||||
acceptance_type: "offline",
|
||||
accepted_at: "1963-05-03T04:07:52.723Z",
|
||||
online: {
|
||||
ip_address: "127.0.0.1",
|
||||
user_agent: "amet irure esse",
|
||||
},
|
||||
},
|
||||
},
|
||||
Response: {
|
||||
status: 200,
|
||||
body: {
|
||||
status: "succeeded",
|
||||
},
|
||||
},
|
||||
},
|
||||
SaveCardUseNo3DSManualCapture: {
|
||||
Request: {
|
||||
payment_method: "card",
|
||||
amount: 5000,
|
||||
payment_method_data: {
|
||||
card: successfulNo3DSCardDetails,
|
||||
},
|
||||
currency: "USD",
|
||||
setup_future_usage: "on_session",
|
||||
customer_acceptance: {
|
||||
acceptance_type: "offline",
|
||||
accepted_at: "1963-05-03T04:07:52.723Z",
|
||||
online: {
|
||||
ip_address: "127.0.0.1",
|
||||
user_agent: "amet irure esse",
|
||||
},
|
||||
},
|
||||
},
|
||||
Response: {
|
||||
status: 200,
|
||||
body: {
|
||||
status: "requires_capture",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@ -14,6 +14,7 @@ import { connectorDetails as fiuuConnectorDetails } from "./Fiuu.js";
|
||||
import { connectorDetails as iatapayConnectorDetails } from "./Iatapay.js";
|
||||
import { connectorDetails as itaubankConnectorDetails } from "./ItauBank.js";
|
||||
import { connectorDetails as jpmorganConnectorDetails } from "./Jpmorgan.js";
|
||||
import { connectorDetails as monerisConnectorDetails } from "./Moneris.js";
|
||||
import { connectorDetails as nexixpayConnectorDetails } from "./Nexixpay.js";
|
||||
import { connectorDetails as nmiConnectorDetails } from "./Nmi.js";
|
||||
import { connectorDetails as noonConnectorDetails } from "./Noon.js";
|
||||
@ -39,6 +40,7 @@ const connectorDetails = {
|
||||
iatapay: iatapayConnectorDetails,
|
||||
itaubank: itaubankConnectorDetails,
|
||||
jpmorgan: jpmorganConnectorDetails,
|
||||
moneris: monerisConnectorDetails,
|
||||
nexixpay: nexixpayConnectorDetails,
|
||||
nmi: nmiConnectorDetails,
|
||||
novalnet: novalnetConnectorDetails,
|
||||
|
||||
Reference in New Issue
Block a user