mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-28 04:04:55 +08:00
feat(connector): [Trustpay] add authorize (cards 3ds, no3ds and bank redirects), refund, psync, rsync (#717)
Co-authored-by: Sangamesh <sangamesh.kulkarni@juspay.in> Co-authored-by: sai harsha <sai.harsha@sai.harsha-MacBookPro> Co-authored-by: Arun Raj M <jarnura47@gmail.com>
This commit is contained in:
@ -65,6 +65,7 @@ cards = [
|
|||||||
"stripe",
|
"stripe",
|
||||||
"worldline",
|
"worldline",
|
||||||
"worldpay",
|
"worldpay",
|
||||||
|
"trustpay",
|
||||||
]
|
]
|
||||||
|
|
||||||
[refund]
|
[refund]
|
||||||
@ -103,6 +104,8 @@ shift4.base_url = "https://api.shift4.com/"
|
|||||||
stripe.base_url = "https://api.stripe.com/"
|
stripe.base_url = "https://api.stripe.com/"
|
||||||
worldline.base_url = "https://eu.sandbox.api-ingenico.com/"
|
worldline.base_url = "https://eu.sandbox.api-ingenico.com/"
|
||||||
worldpay.base_url = "https://try.access.worldpay.com/"
|
worldpay.base_url = "https://try.access.worldpay.com/"
|
||||||
|
trustpay.base_url = "https://test-tpgw.trustpay.eu/"
|
||||||
|
trustpay.base_url_bank_redirects = "https://aapi.trustpay.eu/"
|
||||||
|
|
||||||
[scheduler]
|
[scheduler]
|
||||||
stream = "SCHEDULER_STREAM"
|
stream = "SCHEDULER_STREAM"
|
||||||
|
|||||||
@ -144,6 +144,8 @@ shift4.base_url = "https://api.shift4.com/"
|
|||||||
stripe.base_url = "https://api.stripe.com/"
|
stripe.base_url = "https://api.stripe.com/"
|
||||||
worldline.base_url = "https://eu.sandbox.api-ingenico.com/"
|
worldline.base_url = "https://eu.sandbox.api-ingenico.com/"
|
||||||
worldpay.base_url = "https://try.access.worldpay.com/"
|
worldpay.base_url = "https://try.access.worldpay.com/"
|
||||||
|
trustpay.base_url = "https://test-tpgw.trustpay.eu/"
|
||||||
|
trustpay.base_url_bank_redirects = "https://aapi.trustpay.eu/"
|
||||||
|
|
||||||
# This data is used to call respective connectors for wallets and cards
|
# This data is used to call respective connectors for wallets and cards
|
||||||
[connectors.supported]
|
[connectors.supported]
|
||||||
|
|||||||
@ -89,6 +89,8 @@ shift4.base_url = "https://api.shift4.com/"
|
|||||||
stripe.base_url = "https://api.stripe.com/"
|
stripe.base_url = "https://api.stripe.com/"
|
||||||
worldline.base_url = "https://eu.sandbox.api-ingenico.com/"
|
worldline.base_url = "https://eu.sandbox.api-ingenico.com/"
|
||||||
worldpay.base_url = "https://try.access.worldpay.com/"
|
worldpay.base_url = "https://try.access.worldpay.com/"
|
||||||
|
trustpay.base_url = "https://test-tpgw.trustpay.eu/"
|
||||||
|
trustpay.base_url_bank_redirects = "https://aapi.trustpay.eu/"
|
||||||
|
|
||||||
[connectors.supported]
|
[connectors.supported]
|
||||||
wallets = ["klarna", "braintree", "applepay"]
|
wallets = ["klarna", "braintree", "applepay"]
|
||||||
@ -112,6 +114,7 @@ cards = [
|
|||||||
"stripe",
|
"stripe",
|
||||||
"worldline",
|
"worldline",
|
||||||
"worldpay",
|
"worldpay",
|
||||||
|
"trustpay",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -539,6 +539,7 @@ pub enum MandateStatus {
|
|||||||
strum::Display,
|
strum::Display,
|
||||||
strum::EnumString,
|
strum::EnumString,
|
||||||
frunk::LabelledGeneric,
|
frunk::LabelledGeneric,
|
||||||
|
Hash,
|
||||||
)]
|
)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
#[strum(serialize_all = "snake_case")]
|
#[strum(serialize_all = "snake_case")]
|
||||||
@ -567,11 +568,18 @@ pub enum Connector {
|
|||||||
Stripe,
|
Stripe,
|
||||||
Worldline,
|
Worldline,
|
||||||
Worldpay,
|
Worldpay,
|
||||||
|
Trustpay,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Connector {
|
impl Connector {
|
||||||
pub fn supports_access_token(&self) -> bool {
|
pub fn supports_access_token(&self, payment_method: PaymentMethod) -> bool {
|
||||||
matches!(self, Self::Airwallex | Self::Globalpay | Self::Payu)
|
matches!(
|
||||||
|
(self, payment_method),
|
||||||
|
(Self::Airwallex, _)
|
||||||
|
| (Self::Globalpay, _)
|
||||||
|
| (Self::Payu, _)
|
||||||
|
| (Self::Trustpay, PaymentMethod::BankRedirect)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -611,6 +619,7 @@ pub enum RoutableConnectors {
|
|||||||
Worldline,
|
Worldline,
|
||||||
Worldpay,
|
Worldpay,
|
||||||
Multisafepay,
|
Multisafepay,
|
||||||
|
Trustpay,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wallets which support obtaining session object
|
/// Wallets which support obtaining session object
|
||||||
|
|||||||
@ -44,7 +44,7 @@ where
|
|||||||
/// Functionality, for specifically encoding `Self` into `String`
|
/// Functionality, for specifically encoding `Self` into `String`
|
||||||
/// after serialization by using `serde::Serialize`
|
/// after serialization by using `serde::Serialize`
|
||||||
///
|
///
|
||||||
fn encode(&'e self) -> CustomResult<String, errors::ParsingError>
|
fn url_encode(&'e self) -> CustomResult<String, errors::ParsingError>
|
||||||
where
|
where
|
||||||
Self: Serialize;
|
Self: Serialize;
|
||||||
|
|
||||||
@ -103,7 +103,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check without two functions can we combine this
|
// Check without two functions can we combine this
|
||||||
fn encode(&'e self) -> CustomResult<String, errors::ParsingError>
|
fn url_encode(&'e self) -> CustomResult<String, errors::ParsingError>
|
||||||
where
|
where
|
||||||
Self: Serialize,
|
Self: Serialize,
|
||||||
{
|
{
|
||||||
|
|||||||
@ -251,6 +251,7 @@ pub struct Connectors {
|
|||||||
pub stripe: ConnectorParams,
|
pub stripe: ConnectorParams,
|
||||||
pub worldline: ConnectorParams,
|
pub worldline: ConnectorParams,
|
||||||
pub worldpay: ConnectorParams,
|
pub worldpay: ConnectorParams,
|
||||||
|
pub trustpay: ConnectorParamsWithMoreUrls,
|
||||||
|
|
||||||
// Keep this field separate from the remaining fields
|
// Keep this field separate from the remaining fields
|
||||||
pub supported: SupportedConnectors,
|
pub supported: SupportedConnectors,
|
||||||
@ -262,6 +263,13 @@ pub struct ConnectorParams {
|
|||||||
pub base_url: String,
|
pub base_url: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Clone, Default)]
|
||||||
|
#[serde(default)]
|
||||||
|
pub struct ConnectorParamsWithMoreUrls {
|
||||||
|
pub base_url: String,
|
||||||
|
pub base_url_bank_redirects: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub struct SchedulerSettings {
|
pub struct SchedulerSettings {
|
||||||
|
|||||||
@ -18,6 +18,7 @@ pub mod payu;
|
|||||||
pub mod rapyd;
|
pub mod rapyd;
|
||||||
pub mod shift4;
|
pub mod shift4;
|
||||||
pub mod stripe;
|
pub mod stripe;
|
||||||
|
pub mod trustpay;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
pub mod worldline;
|
pub mod worldline;
|
||||||
pub mod worldpay;
|
pub mod worldpay;
|
||||||
@ -27,5 +28,6 @@ pub use self::{
|
|||||||
authorizedotnet::Authorizedotnet, bambora::Bambora, bluesnap::Bluesnap, braintree::Braintree,
|
authorizedotnet::Authorizedotnet, bambora::Bambora, bluesnap::Bluesnap, braintree::Braintree,
|
||||||
checkout::Checkout, cybersource::Cybersource, dlocal::Dlocal, fiserv::Fiserv,
|
checkout::Checkout, cybersource::Cybersource, dlocal::Dlocal, fiserv::Fiserv,
|
||||||
globalpay::Globalpay, klarna::Klarna, multisafepay::Multisafepay, nuvei::Nuvei, payu::Payu,
|
globalpay::Globalpay, klarna::Klarna, multisafepay::Multisafepay, nuvei::Nuvei, payu::Payu,
|
||||||
rapyd::Rapyd, shift4::Shift4, stripe::Stripe, worldline::Worldline, worldpay::Worldpay,
|
rapyd::Rapyd, shift4::Shift4, stripe::Stripe, trustpay::Trustpay, worldline::Worldline,
|
||||||
|
worldpay::Worldpay,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -346,7 +346,7 @@ impl
|
|||||||
req: &types::PaymentsAuthorizeRouterData,
|
req: &types::PaymentsAuthorizeRouterData,
|
||||||
) -> CustomResult<Option<String>, errors::ConnectorError> {
|
) -> CustomResult<Option<String>, errors::ConnectorError> {
|
||||||
let req = stripe::PaymentIntentRequest::try_from(req)?;
|
let req = stripe::PaymentIntentRequest::try_from(req)?;
|
||||||
let stripe_req = utils::Encode::<stripe::PaymentIntentRequest>::encode(&req)
|
let stripe_req = utils::Encode::<stripe::PaymentIntentRequest>::url_encode(&req)
|
||||||
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
|
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
|
||||||
Ok(Some(stripe_req))
|
Ok(Some(stripe_req))
|
||||||
}
|
}
|
||||||
|
|||||||
630
crates/router/src/connector/trustpay.rs
Normal file
630
crates/router/src/connector/trustpay.rs
Normal file
@ -0,0 +1,630 @@
|
|||||||
|
mod transformers;
|
||||||
|
|
||||||
|
use std::fmt::Debug;
|
||||||
|
|
||||||
|
use base64::Engine;
|
||||||
|
use error_stack::{IntoReport, ResultExt};
|
||||||
|
use transformers as trustpay;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
configs::settings,
|
||||||
|
consts,
|
||||||
|
core::{
|
||||||
|
errors::{self, CustomResult},
|
||||||
|
payments,
|
||||||
|
},
|
||||||
|
headers,
|
||||||
|
services::{self, ConnectorIntegration},
|
||||||
|
types::{
|
||||||
|
self,
|
||||||
|
api::{self, ConnectorCommon, ConnectorCommonExt},
|
||||||
|
ErrorResponse, Response,
|
||||||
|
},
|
||||||
|
utils::{self, BytesExt},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Trustpay;
|
||||||
|
|
||||||
|
impl<Flow, Request, Response> ConnectorCommonExt<Flow, Request, Response> for Trustpay
|
||||||
|
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> {
|
||||||
|
match req.payment_method {
|
||||||
|
storage_models::enums::PaymentMethod::BankRedirect => {
|
||||||
|
let token = req
|
||||||
|
.access_token
|
||||||
|
.clone()
|
||||||
|
.ok_or(errors::ConnectorError::FailedToObtainAuthType)?;
|
||||||
|
Ok(vec![
|
||||||
|
(
|
||||||
|
headers::CONTENT_TYPE.to_string(),
|
||||||
|
"application/json".to_owned(),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
headers::AUTHORIZATION.to_string(),
|
||||||
|
format!("Bearer {}", token.token),
|
||||||
|
),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let mut header = vec![(
|
||||||
|
headers::CONTENT_TYPE.to_string(),
|
||||||
|
self.get_content_type().to_string(),
|
||||||
|
)];
|
||||||
|
let mut api_key = self.get_auth_header(&req.connector_auth_type)?;
|
||||||
|
header.append(&mut api_key);
|
||||||
|
Ok(header)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ConnectorCommon for Trustpay {
|
||||||
|
fn id(&self) -> &'static str {
|
||||||
|
"trustpay"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn common_get_content_type(&self) -> &'static str {
|
||||||
|
"application/x-www-form-urlencoded"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn base_url<'a>(&self, connectors: &'a settings::Connectors) -> &'a str {
|
||||||
|
connectors.trustpay.base_url.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_auth_header(
|
||||||
|
&self,
|
||||||
|
auth_type: &types::ConnectorAuthType,
|
||||||
|
) -> CustomResult<Vec<(String, String)>, errors::ConnectorError> {
|
||||||
|
let auth = trustpay::TrustpayAuthType::try_from(auth_type)
|
||||||
|
.change_context(errors::ConnectorError::FailedToObtainAuthType)?;
|
||||||
|
Ok(vec![(headers::X_API_KEY.to_string(), auth.api_key)])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_error_response(
|
||||||
|
&self,
|
||||||
|
res: Response,
|
||||||
|
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
|
||||||
|
let response: trustpay::TrustpayErrorResponse = res
|
||||||
|
.response
|
||||||
|
.parse_struct("trustpay ErrorResponse")
|
||||||
|
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||||
|
let default_error = trustpay::Errors {
|
||||||
|
code: 0,
|
||||||
|
description: consts::NO_ERROR_CODE.to_string(),
|
||||||
|
};
|
||||||
|
Ok(ErrorResponse {
|
||||||
|
status_code: res.status_code,
|
||||||
|
code: response.status.to_string(),
|
||||||
|
message: format!("{:?}", response.errors.first().unwrap_or(&default_error)),
|
||||||
|
reason: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl api::Payment for Trustpay {}
|
||||||
|
|
||||||
|
impl api::PreVerify for Trustpay {}
|
||||||
|
impl ConnectorIntegration<api::Verify, types::VerifyRequestData, types::PaymentsResponseData>
|
||||||
|
for Trustpay
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
impl api::PaymentVoid for Trustpay {}
|
||||||
|
|
||||||
|
impl ConnectorIntegration<api::Void, types::PaymentsCancelData, types::PaymentsResponseData>
|
||||||
|
for Trustpay
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
impl api::ConnectorAccessToken for Trustpay {}
|
||||||
|
|
||||||
|
impl ConnectorIntegration<api::AccessTokenAuth, types::AccessTokenRequestData, types::AccessToken>
|
||||||
|
for Trustpay
|
||||||
|
{
|
||||||
|
fn get_url(
|
||||||
|
&self,
|
||||||
|
_req: &types::RefreshTokenRouterData,
|
||||||
|
connectors: &settings::Connectors,
|
||||||
|
) -> CustomResult<String, errors::ConnectorError> {
|
||||||
|
Ok(format!(
|
||||||
|
"{}{}",
|
||||||
|
connectors.trustpay.base_url_bank_redirects, "api/oauth2/token"
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_content_type(&self) -> &'static str {
|
||||||
|
self.common_get_content_type()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_headers(
|
||||||
|
&self,
|
||||||
|
req: &types::RefreshTokenRouterData,
|
||||||
|
_connectors: &settings::Connectors,
|
||||||
|
) -> CustomResult<Vec<(String, String)>, errors::ConnectorError> {
|
||||||
|
let auth = trustpay::TrustpayAuthType::try_from(&req.connector_auth_type)
|
||||||
|
.change_context(errors::ConnectorError::FailedToObtainAuthType)?;
|
||||||
|
let auth_value = format!(
|
||||||
|
"Basic {}",
|
||||||
|
consts::BASE64_ENGINE.encode(format!("{}:{}", auth.project_id, auth.secret_key))
|
||||||
|
);
|
||||||
|
Ok(vec![
|
||||||
|
(
|
||||||
|
headers::CONTENT_TYPE.to_string(),
|
||||||
|
types::RefreshTokenType::get_content_type(self).to_string(),
|
||||||
|
),
|
||||||
|
(headers::AUTHORIZATION.to_string(), auth_value),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_request_body(
|
||||||
|
&self,
|
||||||
|
req: &types::RefreshTokenRouterData,
|
||||||
|
) -> CustomResult<Option<String>, errors::ConnectorError> {
|
||||||
|
let trustpay_req =
|
||||||
|
utils::Encode::<trustpay::TrustpayAuthUpdateRequest>::convert_and_url_encode(req)
|
||||||
|
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
|
||||||
|
Ok(Some(trustpay_req))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_request(
|
||||||
|
&self,
|
||||||
|
req: &types::RefreshTokenRouterData,
|
||||||
|
connectors: &settings::Connectors,
|
||||||
|
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
|
||||||
|
let req = Some(
|
||||||
|
services::RequestBuilder::new()
|
||||||
|
.method(services::Method::Post)
|
||||||
|
.headers(types::RefreshTokenType::get_headers(self, req, connectors)?)
|
||||||
|
.url(&types::RefreshTokenType::get_url(self, req, connectors)?)
|
||||||
|
.body(types::RefreshTokenType::get_request_body(self, req)?)
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
Ok(req)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_response(
|
||||||
|
&self,
|
||||||
|
data: &types::RefreshTokenRouterData,
|
||||||
|
res: Response,
|
||||||
|
) -> CustomResult<types::RefreshTokenRouterData, errors::ConnectorError> {
|
||||||
|
let response: trustpay::TrustpayAuthUpdateResponse = res
|
||||||
|
.response
|
||||||
|
.parse_struct("trustpay TrustpayAuthUpdateResponse")
|
||||||
|
.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)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_error_response(
|
||||||
|
&self,
|
||||||
|
res: Response,
|
||||||
|
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
|
||||||
|
let response: trustpay::TrustpayAccessTokenErrorResponse = res
|
||||||
|
.response
|
||||||
|
.parse_struct("Trustpay AccessTokenErrorResponse")
|
||||||
|
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||||
|
Ok(ErrorResponse {
|
||||||
|
status_code: res.status_code,
|
||||||
|
code: response.result_info.result_code.to_string(),
|
||||||
|
message: response.result_info.additional_info.unwrap_or_default(),
|
||||||
|
reason: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl api::PaymentSync for Trustpay {}
|
||||||
|
impl ConnectorIntegration<api::PSync, types::PaymentsSyncData, types::PaymentsResponseData>
|
||||||
|
for Trustpay
|
||||||
|
{
|
||||||
|
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 id = req.request.connector_transaction_id.clone();
|
||||||
|
match req.payment_method {
|
||||||
|
storage_models::enums::PaymentMethod::BankRedirect => Ok(format!(
|
||||||
|
"{}{}/{}",
|
||||||
|
connectors.trustpay.base_url_bank_redirects,
|
||||||
|
"api/Payments/Payment",
|
||||||
|
id.get_connector_transaction_id()
|
||||||
|
.change_context(errors::ConnectorError::MissingConnectorTransactionID)?
|
||||||
|
)),
|
||||||
|
_ => Ok(format!(
|
||||||
|
"{}{}/{}",
|
||||||
|
self.base_url(connectors),
|
||||||
|
"api/v1/instance",
|
||||||
|
id.get_connector_transaction_id()
|
||||||
|
.change_context(errors::ConnectorError::MissingConnectorTransactionID)?
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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> {
|
||||||
|
let response: trustpay::TrustpayPaymentsResponse = res
|
||||||
|
.response
|
||||||
|
.parse_struct("trustpay PaymentsResponse")
|
||||||
|
.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 api::PaymentCapture for Trustpay {}
|
||||||
|
impl ConnectorIntegration<api::Capture, types::PaymentsCaptureData, types::PaymentsResponseData>
|
||||||
|
for Trustpay
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
impl api::PaymentSession for Trustpay {}
|
||||||
|
|
||||||
|
impl ConnectorIntegration<api::Session, types::PaymentsSessionData, types::PaymentsResponseData>
|
||||||
|
for Trustpay
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
impl api::PaymentAuthorize for Trustpay {}
|
||||||
|
|
||||||
|
impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::PaymentsResponseData>
|
||||||
|
for Trustpay
|
||||||
|
{
|
||||||
|
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> {
|
||||||
|
match req.payment_method {
|
||||||
|
storage_models::enums::PaymentMethod::BankRedirect => Ok(format!(
|
||||||
|
"{}{}",
|
||||||
|
connectors.trustpay.base_url_bank_redirects, "api/Payments/Payment"
|
||||||
|
)),
|
||||||
|
_ => Ok(format!(
|
||||||
|
"{}{}",
|
||||||
|
self.base_url(connectors),
|
||||||
|
"api/v1/purchase"
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_request_body(
|
||||||
|
&self,
|
||||||
|
req: &types::PaymentsAuthorizeRouterData,
|
||||||
|
) -> CustomResult<Option<String>, errors::ConnectorError> {
|
||||||
|
let trustpay_req = trustpay::TrustpayPaymentsRequest::try_from(req)?;
|
||||||
|
let trustpay_req_string = match req.payment_method {
|
||||||
|
storage_models::enums::PaymentMethod::BankRedirect => {
|
||||||
|
utils::Encode::<trustpay::PaymentRequestBankRedirect>::encode_to_string_of_json(
|
||||||
|
&trustpay_req,
|
||||||
|
)
|
||||||
|
.change_context(errors::ConnectorError::RequestEncodingFailed)?
|
||||||
|
}
|
||||||
|
_ => utils::Encode::<trustpay::PaymentRequestCards>::url_encode(&trustpay_req)
|
||||||
|
.change_context(errors::ConnectorError::RequestEncodingFailed)?,
|
||||||
|
};
|
||||||
|
Ok(Some(trustpay_req_string))
|
||||||
|
}
|
||||||
|
|
||||||
|
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> {
|
||||||
|
let response: trustpay::TrustpayPaymentsResponse = res
|
||||||
|
.response
|
||||||
|
.parse_struct("trustpay PaymentsResponse")
|
||||||
|
.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)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_error_response(
|
||||||
|
&self,
|
||||||
|
res: Response,
|
||||||
|
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
|
||||||
|
self.build_error_response(res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl api::Refund for Trustpay {}
|
||||||
|
impl api::RefundExecute for Trustpay {}
|
||||||
|
impl api::RefundSync for Trustpay {}
|
||||||
|
|
||||||
|
impl ConnectorIntegration<api::Execute, types::RefundsData, types::RefundsResponseData>
|
||||||
|
for Trustpay
|
||||||
|
{
|
||||||
|
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> {
|
||||||
|
match req.payment_method {
|
||||||
|
storage_models::enums::PaymentMethod::BankRedirect => Ok(format!(
|
||||||
|
"{}{}{}{}",
|
||||||
|
connectors.trustpay.base_url_bank_redirects,
|
||||||
|
"api/Payments/Payment/",
|
||||||
|
req.request.connector_transaction_id,
|
||||||
|
"/Refund"
|
||||||
|
)),
|
||||||
|
_ => Ok(format!("{}{}", self.base_url(connectors), "api/v1/Reverse")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_request_body(
|
||||||
|
&self,
|
||||||
|
req: &types::RefundsRouterData<api::Execute>,
|
||||||
|
) -> CustomResult<Option<String>, errors::ConnectorError> {
|
||||||
|
let trustpay_req = trustpay::TrustpayRefundRequest::try_from(req)?;
|
||||||
|
let trustpay_req_string = match req.payment_method {
|
||||||
|
storage_models::enums::PaymentMethod::BankRedirect => utils::Encode::<
|
||||||
|
trustpay::TrustpayRefundRequestBankRedirect,
|
||||||
|
>::encode_to_string_of_json(
|
||||||
|
&trustpay_req
|
||||||
|
)
|
||||||
|
.change_context(errors::ConnectorError::RequestEncodingFailed)?,
|
||||||
|
_ => utils::Encode::<trustpay::TrustpayRefundRequestCards>::url_encode(&trustpay_req)
|
||||||
|
.change_context(errors::ConnectorError::RequestEncodingFailed)?,
|
||||||
|
};
|
||||||
|
Ok(Some(trustpay_req_string))
|
||||||
|
}
|
||||||
|
|
||||||
|
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> {
|
||||||
|
let response: trustpay::RefundResponse = res
|
||||||
|
.response
|
||||||
|
.parse_struct("trustpay RefundResponse")
|
||||||
|
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
|
||||||
|
types::RouterData::try_from(types::ResponseRouterData {
|
||||||
|
response,
|
||||||
|
data: data.clone(),
|
||||||
|
http_code: res.status_code,
|
||||||
|
})
|
||||||
|
.change_context(errors::ConnectorError::ResponseHandlingFailed)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_error_response(
|
||||||
|
&self,
|
||||||
|
res: Response,
|
||||||
|
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
|
||||||
|
self.build_error_response(res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ConnectorIntegration<api::RSync, types::RefundsData, types::RefundsResponseData> for Trustpay {
|
||||||
|
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 id = req
|
||||||
|
.request
|
||||||
|
.connector_refund_id
|
||||||
|
.to_owned()
|
||||||
|
.ok_or_else(|| errors::ConnectorError::MissingConnectorRefundID)?;
|
||||||
|
match req.payment_method {
|
||||||
|
storage_models::enums::PaymentMethod::BankRedirect => Ok(format!(
|
||||||
|
"{}{}/{}",
|
||||||
|
connectors.trustpay.base_url_bank_redirects, "api/Payments/Payment", id
|
||||||
|
)),
|
||||||
|
_ => Ok(format!(
|
||||||
|
"{}{}/{}",
|
||||||
|
self.base_url(connectors),
|
||||||
|
"api/v1/instance",
|
||||||
|
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> {
|
||||||
|
let response: trustpay::RefundResponse = res
|
||||||
|
.response
|
||||||
|
.parse_struct("trustpay RefundResponse")
|
||||||
|
.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)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_error_response(
|
||||||
|
&self,
|
||||||
|
res: Response,
|
||||||
|
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
|
||||||
|
self.build_error_response(res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl api::IncomingWebhook for Trustpay {
|
||||||
|
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 Trustpay {
|
||||||
|
fn get_flow_type(
|
||||||
|
&self,
|
||||||
|
query_params: &str,
|
||||||
|
) -> CustomResult<payments::CallConnectorAction, errors::ConnectorError> {
|
||||||
|
let query =
|
||||||
|
serde_urlencoded::from_str::<transformers::TrustpayRedirectResponse>(query_params)
|
||||||
|
.into_report()
|
||||||
|
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||||
|
crate::logger::debug!(trustpay_redirect_response=?query);
|
||||||
|
Ok(query.status.map_or(
|
||||||
|
payments::CallConnectorAction::Trigger,
|
||||||
|
|status| match status.as_str() {
|
||||||
|
"SuccessOk" => payments::CallConnectorAction::StatusUpdate(
|
||||||
|
storage_models::enums::AttemptStatus::Charged,
|
||||||
|
),
|
||||||
|
_ => payments::CallConnectorAction::Trigger,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
1070
crates/router/src/connector/trustpay/transformers.rs
Normal file
1070
crates/router/src/connector/trustpay/transformers.rs
Normal file
File diff suppressed because it is too large
Load Diff
@ -222,6 +222,7 @@ pub enum CardIssuer {
|
|||||||
pub trait CardData {
|
pub trait CardData {
|
||||||
fn get_card_expiry_year_2_digit(&self) -> Secret<String>;
|
fn get_card_expiry_year_2_digit(&self) -> Secret<String>;
|
||||||
fn get_card_issuer(&self) -> Result<CardIssuer, Error>;
|
fn get_card_issuer(&self) -> Result<CardIssuer, Error>;
|
||||||
|
fn get_card_expiry_month_year_2_digit_with_delimiter(&self, delimiter: String) -> String;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CardData for api::Card {
|
impl CardData for api::Card {
|
||||||
@ -237,6 +238,15 @@ impl CardData for api::Card {
|
|||||||
.map(|card| card.split_whitespace().collect());
|
.map(|card| card.split_whitespace().collect());
|
||||||
get_card_issuer(card.peek().clone().as_str())
|
get_card_issuer(card.peek().clone().as_str())
|
||||||
}
|
}
|
||||||
|
fn get_card_expiry_month_year_2_digit_with_delimiter(&self, delimiter: String) -> String {
|
||||||
|
let year = self.get_card_expiry_year_2_digit();
|
||||||
|
format!(
|
||||||
|
"{}{}{}",
|
||||||
|
self.card_exp_month.peek().clone(),
|
||||||
|
delimiter,
|
||||||
|
year.peek()
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_card_issuer(card_number: &str) -> Result<CardIssuer, Error> {
|
fn get_card_issuer(card_number: &str) -> Result<CardIssuer, Error> {
|
||||||
|
|||||||
@ -86,7 +86,7 @@ pub fn mk_add_card_request(
|
|||||||
name_on_card: Some("John Doe".to_string().into()), // [#256]
|
name_on_card: Some("John Doe".to_string().into()), // [#256]
|
||||||
nickname: Some("router".to_string()), //
|
nickname: Some("router".to_string()), //
|
||||||
};
|
};
|
||||||
let body = utils::Encode::<AddCardRequest<'_>>::encode(&add_card_req)
|
let body = utils::Encode::<AddCardRequest<'_>>::url_encode(&add_card_req)
|
||||||
.change_context(errors::VaultError::RequestEncodingFailed)?;
|
.change_context(errors::VaultError::RequestEncodingFailed)?;
|
||||||
let mut url = locker.host.to_owned();
|
let mut url = locker.host.to_owned();
|
||||||
url.push_str("/card/addCard");
|
url.push_str("/card/addCard");
|
||||||
@ -139,7 +139,7 @@ pub fn mk_get_card_request<'a>(
|
|||||||
card_id,
|
card_id,
|
||||||
};
|
};
|
||||||
|
|
||||||
let body = utils::Encode::<GetCard<'_>>::encode(&get_card_req)
|
let body = utils::Encode::<GetCard<'_>>::url_encode(&get_card_req)
|
||||||
.change_context(errors::VaultError::RequestEncodingFailed)?;
|
.change_context(errors::VaultError::RequestEncodingFailed)?;
|
||||||
let mut url = locker.host.to_owned();
|
let mut url = locker.host.to_owned();
|
||||||
url.push_str("/card/getCard");
|
url.push_str("/card/getCard");
|
||||||
@ -158,7 +158,7 @@ pub fn mk_delete_card_request<'a>(
|
|||||||
merchant_id,
|
merchant_id,
|
||||||
card_id,
|
card_id,
|
||||||
};
|
};
|
||||||
let body = utils::Encode::<GetCard<'_>>::encode(&delete_card_req)
|
let body = utils::Encode::<GetCard<'_>>::url_encode(&delete_card_req)
|
||||||
.change_context(errors::VaultError::RequestEncodingFailed)?;
|
.change_context(errors::VaultError::RequestEncodingFailed)?;
|
||||||
let mut url = locker.host.to_owned();
|
let mut url = locker.host.to_owned();
|
||||||
url.push_str("/card/deleteCard");
|
url.push_str("/card/deleteCard");
|
||||||
|
|||||||
@ -10,7 +10,7 @@ use crate::{
|
|||||||
},
|
},
|
||||||
routes::AppState,
|
routes::AppState,
|
||||||
services,
|
services,
|
||||||
types::{self, api as api_types, storage},
|
types::{self, api as api_types, storage, transformers::ForeignInto},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// This function replaces the request and response type of routerdata with the
|
/// This function replaces the request and response type of routerdata with the
|
||||||
@ -82,7 +82,10 @@ pub async fn add_access_token<
|
|||||||
merchant_account: &storage::MerchantAccount,
|
merchant_account: &storage::MerchantAccount,
|
||||||
router_data: &types::RouterData<F, Req, Res>,
|
router_data: &types::RouterData<F, Req, Res>,
|
||||||
) -> RouterResult<types::AddAccessTokenResult> {
|
) -> RouterResult<types::AddAccessTokenResult> {
|
||||||
if connector.connector_name.supports_access_token() {
|
if connector
|
||||||
|
.connector_name
|
||||||
|
.supports_access_token(router_data.payment_method.foreign_into())
|
||||||
|
{
|
||||||
let merchant_id = &merchant_account.merchant_id;
|
let merchant_id = &merchant_account.merchant_id;
|
||||||
let store = &*state.store;
|
let store = &*state.store;
|
||||||
let old_access_token = store
|
let old_access_token = store
|
||||||
|
|||||||
@ -182,6 +182,7 @@ impl ConnectorData {
|
|||||||
"worldline" => Ok(Box::new(&connector::Worldline)),
|
"worldline" => Ok(Box::new(&connector::Worldline)),
|
||||||
"worldpay" => Ok(Box::new(&connector::Worldpay)),
|
"worldpay" => Ok(Box::new(&connector::Worldpay)),
|
||||||
"multisafepay" => Ok(Box::new(&connector::Multisafepay)),
|
"multisafepay" => Ok(Box::new(&connector::Multisafepay)),
|
||||||
|
"trustpay" => Ok(Box::new(&connector::Trustpay)),
|
||||||
_ => Err(report!(errors::ConnectorError::InvalidConnectorName)
|
_ => Err(report!(errors::ConnectorError::InvalidConnectorName)
|
||||||
.attach_printable(format!("invalid connector name: {connector_name}")))
|
.attach_printable(format!("invalid connector name: {connector_name}")))
|
||||||
.change_context(errors::ApiErrorResponse::InternalServerError),
|
.change_context(errors::ApiErrorResponse::InternalServerError),
|
||||||
|
|||||||
@ -22,6 +22,7 @@ pub(crate) struct ConnectorAuthentication {
|
|||||||
pub stripe: Option<HeaderKey>,
|
pub stripe: Option<HeaderKey>,
|
||||||
pub worldpay: Option<BodyKey>,
|
pub worldpay: Option<BodyKey>,
|
||||||
pub worldline: Option<SignatureKey>,
|
pub worldline: Option<SignatureKey>,
|
||||||
|
pub trustpay: Option<SignatureKey>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConnectorAuthentication {
|
impl ConnectorAuthentication {
|
||||||
|
|||||||
@ -18,6 +18,7 @@ mod payu;
|
|||||||
mod rapyd;
|
mod rapyd;
|
||||||
mod shift4;
|
mod shift4;
|
||||||
mod stripe;
|
mod stripe;
|
||||||
|
mod trustpay;
|
||||||
mod utils;
|
mod utils;
|
||||||
mod worldline;
|
mod worldline;
|
||||||
mod worldpay;
|
mod worldpay;
|
||||||
|
|||||||
245
crates/router/tests/connectors/trustpay.rs
Normal file
245
crates/router/tests/connectors/trustpay.rs
Normal file
@ -0,0 +1,245 @@
|
|||||||
|
use masking::Secret;
|
||||||
|
use router::types::{self, api, storage::enums, BrowserInformation};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
connector_auth,
|
||||||
|
utils::{self, ConnectorActions},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
struct TrustpayTest;
|
||||||
|
impl ConnectorActions for TrustpayTest {}
|
||||||
|
impl utils::Connector for TrustpayTest {
|
||||||
|
fn get_data(&self) -> types::api::ConnectorData {
|
||||||
|
use router::connector::Trustpay;
|
||||||
|
types::api::ConnectorData {
|
||||||
|
connector: Box::new(&Trustpay),
|
||||||
|
connector_name: types::Connector::Trustpay,
|
||||||
|
get_token: types::api::GetToken::Connector,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_auth_token(&self) -> types::ConnectorAuthType {
|
||||||
|
types::ConnectorAuthType::from(
|
||||||
|
connector_auth::ConnectorAuthentication::new()
|
||||||
|
.trustpay
|
||||||
|
.expect("Missing connector authentication configuration"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_name(&self) -> String {
|
||||||
|
"trustpay".to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_default_browser_info() -> BrowserInformation {
|
||||||
|
BrowserInformation {
|
||||||
|
color_depth: 24,
|
||||||
|
java_enabled: false,
|
||||||
|
java_script_enabled: true,
|
||||||
|
language: "en-US".to_string(),
|
||||||
|
screen_height: 1080,
|
||||||
|
screen_width: 1920,
|
||||||
|
time_zone: 3600,
|
||||||
|
accept_header: "*".to_string(),
|
||||||
|
user_agent: "none".to_string(),
|
||||||
|
ip_address: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_default_payment_authorize_data() -> Option<types::PaymentsAuthorizeData> {
|
||||||
|
Some(types::PaymentsAuthorizeData {
|
||||||
|
payment_method_data: types::api::PaymentMethodData::Card(api::Card {
|
||||||
|
card_number: Secret::new("4200000000000000".to_string()),
|
||||||
|
card_exp_year: Secret::new("25".to_string()),
|
||||||
|
card_cvc: Secret::new("123".to_string()),
|
||||||
|
..utils::CCardType::default().0
|
||||||
|
}),
|
||||||
|
browser_info: Some(get_default_browser_info()),
|
||||||
|
..utils::PaymentAuthorizeType::default().0
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_default_payment_info() -> Option<utils::PaymentInfo> {
|
||||||
|
Some(utils::PaymentInfo {
|
||||||
|
address: Some(types::PaymentAddress {
|
||||||
|
billing: Some(api::Address {
|
||||||
|
address: Some(api::AddressDetails {
|
||||||
|
first_name: Some(Secret::new("first".to_string())),
|
||||||
|
last_name: Some(Secret::new("last".to_string())),
|
||||||
|
line1: Some(Secret::new("line1".to_string())),
|
||||||
|
line2: Some(Secret::new("line2".to_string())),
|
||||||
|
city: Some("city".to_string()),
|
||||||
|
zip: Some(Secret::new("zip".to_string())),
|
||||||
|
country: Some("IN".to_string()),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
phone: None,
|
||||||
|
}),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
router_return_url: Some(String::from("http://localhost:8080")),
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static CONNECTOR: TrustpayTest = TrustpayTest {};
|
||||||
|
|
||||||
|
// Cards Positive Tests
|
||||||
|
// Creates a payment using the automatic capture flow (Non 3DS).
|
||||||
|
#[actix_web::test]
|
||||||
|
async fn should_make_payment() {
|
||||||
|
let authorize_response = CONNECTOR
|
||||||
|
.make_payment(
|
||||||
|
get_default_payment_authorize_data(),
|
||||||
|
get_default_payment_info(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(authorize_response.status, enums::AttemptStatus::Charged);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Synchronizes a payment using the automatic capture flow (Non 3DS).
|
||||||
|
#[actix_web::test]
|
||||||
|
async fn should_sync_auto_captured_payment() {
|
||||||
|
let authorize_response = CONNECTOR
|
||||||
|
.make_payment(
|
||||||
|
get_default_payment_authorize_data(),
|
||||||
|
get_default_payment_info(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(authorize_response.status, enums::AttemptStatus::Charged);
|
||||||
|
let txn_id = utils::get_connector_transaction_id(authorize_response.response);
|
||||||
|
assert_ne!(txn_id, None, "Empty connector transaction id");
|
||||||
|
let response = CONNECTOR
|
||||||
|
.psync_retry_till_status_matches(
|
||||||
|
enums::AttemptStatus::Charged,
|
||||||
|
Some(types::PaymentsSyncData {
|
||||||
|
connector_transaction_id: router::types::ResponseId::ConnectorTransactionId(
|
||||||
|
txn_id.unwrap(),
|
||||||
|
),
|
||||||
|
encoded_data: None,
|
||||||
|
capture_method: None,
|
||||||
|
}),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(response.status, enums::AttemptStatus::Charged,);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refunds a payment using the automatic capture flow (Non 3DS).
|
||||||
|
#[actix_web::test]
|
||||||
|
async fn should_refund_auto_captured_payment() {
|
||||||
|
let response = CONNECTOR
|
||||||
|
.make_payment_and_refund(
|
||||||
|
get_default_payment_authorize_data(),
|
||||||
|
None,
|
||||||
|
get_default_payment_info(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
response.response.unwrap().refund_status,
|
||||||
|
enums::RefundStatus::Success,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Synchronizes a refund using the automatic capture flow (Non 3DS).
|
||||||
|
#[actix_web::test]
|
||||||
|
async fn should_sync_refund() {
|
||||||
|
let refund_response = CONNECTOR
|
||||||
|
.make_payment_and_refund(
|
||||||
|
get_default_payment_authorize_data(),
|
||||||
|
None,
|
||||||
|
get_default_payment_info(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let response = CONNECTOR
|
||||||
|
.rsync_retry_till_status_matches(
|
||||||
|
enums::RefundStatus::Success,
|
||||||
|
refund_response.response.unwrap().connector_refund_id,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
response.response.unwrap().refund_status,
|
||||||
|
enums::RefundStatus::Success,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cards Negative scenerios
|
||||||
|
// Creates a payment with incorrect card number.
|
||||||
|
#[actix_web::test]
|
||||||
|
async fn should_fail_payment_for_incorrect_card_number() {
|
||||||
|
let payment_authorize_data = types::PaymentsAuthorizeData {
|
||||||
|
payment_method_data: types::api::PaymentMethodData::Card(api::Card {
|
||||||
|
card_number: Secret::new("1234567891011".to_string()),
|
||||||
|
card_exp_year: Secret::new("25".to_string()),
|
||||||
|
card_cvc: Secret::new("123".to_string()),
|
||||||
|
..utils::CCardType::default().0
|
||||||
|
}),
|
||||||
|
browser_info: Some(get_default_browser_info()),
|
||||||
|
..utils::PaymentAuthorizeType::default().0
|
||||||
|
};
|
||||||
|
let response = CONNECTOR
|
||||||
|
.make_payment(Some(payment_authorize_data), get_default_payment_info())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
response.response.unwrap_err().message,
|
||||||
|
"Errors { code: 61, description: \"invalid payment data (country or brand)\" }".to_string(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates a payment with empty card number.
|
||||||
|
#[actix_web::test]
|
||||||
|
async fn should_fail_payment_for_empty_card_number() {
|
||||||
|
let payment_authorize_data = types::PaymentsAuthorizeData {
|
||||||
|
payment_method_data: types::api::PaymentMethodData::Card(api::Card {
|
||||||
|
card_number: Secret::new("".to_string()),
|
||||||
|
card_exp_year: Secret::new("25".to_string()),
|
||||||
|
card_cvc: Secret::new("123".to_string()),
|
||||||
|
..utils::CCardType::default().0
|
||||||
|
}),
|
||||||
|
browser_info: Some(get_default_browser_info()),
|
||||||
|
..utils::PaymentAuthorizeType::default().0
|
||||||
|
};
|
||||||
|
let response = CONNECTOR
|
||||||
|
.make_payment(Some(payment_authorize_data), get_default_payment_info())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let x = response.response.unwrap_err();
|
||||||
|
assert_eq!(
|
||||||
|
x.message,
|
||||||
|
"Errors { code: 61, description: \"invalid payment data (country or brand)\" }",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates a payment with incorrect expiry year.
|
||||||
|
#[actix_web::test]
|
||||||
|
async fn should_fail_payment_for_incorrect_expiry_year() {
|
||||||
|
let payment_authorize_data = Some(types::PaymentsAuthorizeData {
|
||||||
|
payment_method_data: types::api::PaymentMethodData::Card(api::Card {
|
||||||
|
card_number: Secret::new("4200000000000000".to_string()),
|
||||||
|
card_exp_year: Secret::new("22".to_string()),
|
||||||
|
card_cvc: Secret::new("123".to_string()),
|
||||||
|
..utils::CCardType::default().0
|
||||||
|
}),
|
||||||
|
browser_info: Some(get_default_browser_info()),
|
||||||
|
..utils::PaymentAuthorizeType::default().0
|
||||||
|
});
|
||||||
|
let response = CONNECTOR
|
||||||
|
.make_payment(payment_authorize_data, get_default_payment_info())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
response.response.unwrap_err().message,
|
||||||
|
"Errors { code: 15, description: \"the provided expiration year is not valid\" }"
|
||||||
|
.to_string(),
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -75,6 +75,8 @@ shift4.base_url = "https://api.shift4.com/"
|
|||||||
stripe.base_url = "https://api.stripe.com/"
|
stripe.base_url = "https://api.stripe.com/"
|
||||||
worldline.base_url = "https://eu.sandbox.api-ingenico.com/"
|
worldline.base_url = "https://eu.sandbox.api-ingenico.com/"
|
||||||
worldpay.base_url = "https://try.access.worldpay.com/"
|
worldpay.base_url = "https://try.access.worldpay.com/"
|
||||||
|
trustpay.base_url = "https://test-tpgw.trustpay.eu/"
|
||||||
|
trustpay.base_url_bank_redirects = "https://aapi.trustpay.eu/"
|
||||||
|
|
||||||
[connectors.supported]
|
[connectors.supported]
|
||||||
wallets = ["klarna", "braintree", "applepay"]
|
wallets = ["klarna", "braintree", "applepay"]
|
||||||
@ -98,4 +100,5 @@ cards = [
|
|||||||
"stripe",
|
"stripe",
|
||||||
"worldline",
|
"worldline",
|
||||||
"worldpay",
|
"worldpay",
|
||||||
|
"trustpay",
|
||||||
]
|
]
|
||||||
|
|||||||
Reference in New Issue
Block a user