mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-28 04:04:55 +08:00
feat(connector): [Tsys] Add cards for Payments and Refunds flow (#1716)
Co-authored-by: Prasunna Soppa <prasunna.soppa@juspay.in>
This commit is contained in:
@ -108,7 +108,7 @@ pub enum Connector {
|
||||
Shift4,
|
||||
Stripe,
|
||||
Trustpay,
|
||||
// Tsys,
|
||||
Tsys,
|
||||
Worldline,
|
||||
Worldpay,
|
||||
Zen,
|
||||
@ -216,7 +216,7 @@ pub enum RoutableConnectors {
|
||||
Shift4,
|
||||
Stripe,
|
||||
Trustpay,
|
||||
// Tsys,
|
||||
Tsys,
|
||||
Worldline,
|
||||
Worldpay,
|
||||
Zen,
|
||||
|
||||
@ -3,7 +3,6 @@ mod transformers;
|
||||
use std::fmt::Debug;
|
||||
|
||||
use error_stack::{IntoReport, ResultExt};
|
||||
use masking::ExposeInterface;
|
||||
use transformers as tsys;
|
||||
|
||||
use crate::{
|
||||
@ -12,7 +11,7 @@ use crate::{
|
||||
headers,
|
||||
services::{
|
||||
self,
|
||||
request::{self, Mask},
|
||||
request::{self},
|
||||
ConnectorIntegration,
|
||||
},
|
||||
types::{
|
||||
@ -54,17 +53,13 @@ where
|
||||
{
|
||||
fn build_headers(
|
||||
&self,
|
||||
req: &types::RouterData<Flow, Request, Response>,
|
||||
_req: &types::RouterData<Flow, Request, Response>,
|
||||
_connectors: &settings::Connectors,
|
||||
) -> CustomResult<Vec<(String, request::Maskable<String>)>, errors::ConnectorError> {
|
||||
let mut header = vec![(
|
||||
let header = vec![(
|
||||
headers::CONTENT_TYPE.to_string(),
|
||||
types::PaymentsAuthorizeType::get_content_type(self)
|
||||
.to_string()
|
||||
.into(),
|
||||
Self::get_content_type(self).to_string().into(),
|
||||
)];
|
||||
let mut api_key = self.get_auth_header(&req.connector_auth_type)?;
|
||||
header.append(&mut api_key);
|
||||
Ok(header)
|
||||
}
|
||||
}
|
||||
@ -81,35 +76,6 @@ impl ConnectorCommon for Tsys {
|
||||
fn base_url<'a>(&self, connectors: &'a settings::Connectors) -> &'a str {
|
||||
connectors.tsys.base_url.as_ref()
|
||||
}
|
||||
|
||||
fn get_auth_header(
|
||||
&self,
|
||||
auth_type: &types::ConnectorAuthType,
|
||||
) -> CustomResult<Vec<(String, request::Maskable<String>)>, errors::ConnectorError> {
|
||||
let auth = tsys::TsysAuthType::try_from(auth_type)
|
||||
.change_context(errors::ConnectorError::FailedToObtainAuthType)?;
|
||||
Ok(vec![(
|
||||
headers::AUTHORIZATION.to_string(),
|
||||
auth.api_key.expose().into_masked(),
|
||||
)])
|
||||
}
|
||||
|
||||
fn build_error_response(
|
||||
&self,
|
||||
res: Response,
|
||||
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
|
||||
let response: tsys::TsysErrorResponse = res
|
||||
.response
|
||||
.parse_struct("TsysErrorResponse")
|
||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||
|
||||
Ok(ErrorResponse {
|
||||
status_code: res.status_code,
|
||||
code: response.code,
|
||||
message: response.message,
|
||||
reason: response.reason,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ConnectorIntegration<api::Session, types::PaymentsSessionData, types::PaymentsResponseData>
|
||||
@ -145,9 +111,12 @@ impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::P
|
||||
fn get_url(
|
||||
&self,
|
||||
_req: &types::PaymentsAuthorizeRouterData,
|
||||
_connectors: &settings::Connectors,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into())
|
||||
Ok(format!(
|
||||
"{}servlets/transnox_api_server",
|
||||
self.base_url(connectors)
|
||||
))
|
||||
}
|
||||
|
||||
fn get_request_body(
|
||||
@ -225,9 +194,25 @@ impl ConnectorIntegration<api::PSync, types::PaymentsSyncData, types::PaymentsRe
|
||||
fn get_url(
|
||||
&self,
|
||||
_req: &types::PaymentsSyncRouterData,
|
||||
_connectors: &settings::Connectors,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into())
|
||||
Ok(format!(
|
||||
"{}servlets/Transnox_API_server",
|
||||
self.base_url(connectors)
|
||||
))
|
||||
}
|
||||
|
||||
fn get_request_body(
|
||||
&self,
|
||||
req: &types::PaymentsSyncRouterData,
|
||||
) -> CustomResult<Option<types::RequestBody>, errors::ConnectorError> {
|
||||
let req_obj = tsys::TsysSyncRequest::try_from(req)?;
|
||||
let tsys_req = types::RequestBody::log_and_get_request_body(
|
||||
&req_obj,
|
||||
utils::Encode::<tsys::TsysSyncRequest>::encode_to_string_of_json,
|
||||
)
|
||||
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
|
||||
Ok(Some(tsys_req))
|
||||
}
|
||||
|
||||
fn build_request(
|
||||
@ -237,10 +222,11 @@ impl ConnectorIntegration<api::PSync, types::PaymentsSyncData, types::PaymentsRe
|
||||
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
|
||||
Ok(Some(
|
||||
services::RequestBuilder::new()
|
||||
.method(services::Method::Get)
|
||||
.method(services::Method::Post)
|
||||
.url(&types::PaymentsSyncType::get_url(self, req, connectors)?)
|
||||
.attach_default_headers()
|
||||
.headers(types::PaymentsSyncType::get_headers(self, req, connectors)?)
|
||||
.body(types::PaymentsSyncType::get_request_body(self, req)?)
|
||||
.build(),
|
||||
))
|
||||
}
|
||||
@ -250,7 +236,7 @@ impl ConnectorIntegration<api::PSync, types::PaymentsSyncData, types::PaymentsRe
|
||||
data: &types::PaymentsSyncRouterData,
|
||||
res: Response,
|
||||
) -> CustomResult<types::PaymentsSyncRouterData, errors::ConnectorError> {
|
||||
let response: tsys::TsysPaymentsResponse = res
|
||||
let response: tsys::TsysSyncResponse = res
|
||||
.response
|
||||
.parse_struct("tsys PaymentsSyncResponse")
|
||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||
@ -287,16 +273,25 @@ impl ConnectorIntegration<api::Capture, types::PaymentsCaptureData, types::Payme
|
||||
fn get_url(
|
||||
&self,
|
||||
_req: &types::PaymentsCaptureRouterData,
|
||||
_connectors: &settings::Connectors,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into())
|
||||
Ok(format!(
|
||||
"{}servlets/Transnox_API_server",
|
||||
self.base_url(connectors)
|
||||
))
|
||||
}
|
||||
|
||||
fn get_request_body(
|
||||
&self,
|
||||
_req: &types::PaymentsCaptureRouterData,
|
||||
req: &types::PaymentsCaptureRouterData,
|
||||
) -> CustomResult<Option<types::RequestBody>, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::NotImplemented("get_request_body method".to_string()).into())
|
||||
let connector_req = tsys::TsysPaymentsCaptureRequest::try_from(req)?;
|
||||
let tsys_req = types::RequestBody::log_and_get_request_body(
|
||||
&connector_req,
|
||||
utils::Encode::<tsys::TsysPaymentsCaptureRequest>::encode_to_string_of_json,
|
||||
)
|
||||
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
|
||||
Ok(Some(tsys_req))
|
||||
}
|
||||
|
||||
fn build_request(
|
||||
@ -344,6 +339,77 @@ impl ConnectorIntegration<api::Capture, types::PaymentsCaptureData, types::Payme
|
||||
impl ConnectorIntegration<api::Void, types::PaymentsCancelData, types::PaymentsResponseData>
|
||||
for Tsys
|
||||
{
|
||||
fn get_headers(
|
||||
&self,
|
||||
req: &types::PaymentsCancelRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<Vec<(String, request::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: &types::PaymentsCancelRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
Ok(format!(
|
||||
"{}servlets/Transnox_API_server",
|
||||
self.base_url(connectors),
|
||||
))
|
||||
}
|
||||
fn get_request_body(
|
||||
&self,
|
||||
req: &types::PaymentsCancelRouterData,
|
||||
) -> CustomResult<Option<types::RequestBody>, errors::ConnectorError> {
|
||||
let req_obj = tsys::TsysPaymentsCancelRequest::try_from(req)?;
|
||||
let tsys_req = types::RequestBody::log_and_get_request_body(
|
||||
&req_obj,
|
||||
utils::Encode::<tsys::TsysPaymentsCancelRequest>::encode_to_string_of_json,
|
||||
)
|
||||
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
|
||||
Ok(Some(tsys_req))
|
||||
}
|
||||
|
||||
fn build_request(
|
||||
&self,
|
||||
req: &types::PaymentsCancelRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
|
||||
let request = services::RequestBuilder::new()
|
||||
.method(services::Method::Post)
|
||||
.url(&types::PaymentsVoidType::get_url(self, req, connectors)?)
|
||||
.headers(types::PaymentsVoidType::get_headers(self, req, connectors)?)
|
||||
.body(types::PaymentsVoidType::get_request_body(self, req)?)
|
||||
.build();
|
||||
|
||||
Ok(Some(request))
|
||||
}
|
||||
|
||||
fn handle_response(
|
||||
&self,
|
||||
data: &types::PaymentsCancelRouterData,
|
||||
res: Response,
|
||||
) -> CustomResult<types::PaymentsCancelRouterData, errors::ConnectorError> {
|
||||
let response: tsys::TsysPaymentsResponse = res
|
||||
.response
|
||||
.parse_struct("PaymentCancelResponse")
|
||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||
types::RouterData::try_from(types::ResponseRouterData {
|
||||
response,
|
||||
data: data.clone(),
|
||||
http_code: res.status_code,
|
||||
})
|
||||
}
|
||||
fn get_error_response(
|
||||
&self,
|
||||
res: Response,
|
||||
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
|
||||
self.build_error_response(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl ConnectorIntegration<api::Execute, types::RefundsData, types::RefundsResponseData> for Tsys {
|
||||
@ -362,9 +428,12 @@ impl ConnectorIntegration<api::Execute, types::RefundsData, types::RefundsRespon
|
||||
fn get_url(
|
||||
&self,
|
||||
_req: &types::RefundsRouterData<api::Execute>,
|
||||
_connectors: &settings::Connectors,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into())
|
||||
Ok(format!(
|
||||
"{}servlets/Transnox_API_server",
|
||||
self.base_url(connectors)
|
||||
))
|
||||
}
|
||||
|
||||
fn get_request_body(
|
||||
@ -437,9 +506,25 @@ impl ConnectorIntegration<api::RSync, types::RefundsData, types::RefundsResponse
|
||||
fn get_url(
|
||||
&self,
|
||||
_req: &types::RefundSyncRouterData,
|
||||
_connectors: &settings::Connectors,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into())
|
||||
Ok(format!(
|
||||
"{}servlets/Transnox_API_server",
|
||||
self.base_url(connectors),
|
||||
))
|
||||
}
|
||||
|
||||
fn get_request_body(
|
||||
&self,
|
||||
req: &types::RefundSyncRouterData,
|
||||
) -> CustomResult<Option<types::RequestBody>, errors::ConnectorError> {
|
||||
let req_obj = tsys::TsysSyncRequest::try_from(req)?;
|
||||
let tsys_req = types::RequestBody::log_and_get_request_body(
|
||||
&req_obj,
|
||||
utils::Encode::<tsys::TsysSyncRequest>::encode_to_string_of_json,
|
||||
)
|
||||
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
|
||||
Ok(Some(tsys_req))
|
||||
}
|
||||
|
||||
fn build_request(
|
||||
@ -449,7 +534,7 @@ impl ConnectorIntegration<api::RSync, types::RefundsData, types::RefundsResponse
|
||||
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
|
||||
Ok(Some(
|
||||
services::RequestBuilder::new()
|
||||
.method(services::Method::Get)
|
||||
.method(services::Method::Post)
|
||||
.url(&types::RefundSyncType::get_url(self, req, connectors)?)
|
||||
.attach_default_headers()
|
||||
.headers(types::RefundSyncType::get_headers(self, req, connectors)?)
|
||||
@ -463,8 +548,8 @@ impl ConnectorIntegration<api::RSync, types::RefundsData, types::RefundsResponse
|
||||
data: &types::RefundSyncRouterData,
|
||||
res: Response,
|
||||
) -> CustomResult<types::RefundSyncRouterData, errors::ConnectorError> {
|
||||
let response: tsys::RefundResponse =
|
||||
res.response
|
||||
let response: tsys::TsysSyncResponse = res
|
||||
.response
|
||||
.parse_struct("tsys RefundSyncResponse")
|
||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||
types::RouterData::try_from(types::ResponseRouterData {
|
||||
|
||||
@ -1,45 +1,68 @@
|
||||
use error_stack::ResultExt;
|
||||
use masking::Secret;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
connector::utils::PaymentsAuthorizeRequestData,
|
||||
connector::utils::{CardData, PaymentsAuthorizeRequestData, RefundsRequestData},
|
||||
core::errors,
|
||||
types::{self, api, storage::enums},
|
||||
types::{
|
||||
self, api,
|
||||
storage::{self, enums},
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Default, Debug, Serialize, Eq, PartialEq)]
|
||||
pub struct TsysPaymentsRequest {
|
||||
amount: i64,
|
||||
card: TsysCard,
|
||||
#[derive(Debug, Serialize)]
|
||||
pub enum TsysPaymentsRequest {
|
||||
Auth(TsysPaymentAuthSaleRequest),
|
||||
Sale(TsysPaymentAuthSaleRequest),
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Serialize, Eq, PartialEq)]
|
||||
pub struct TsysCard {
|
||||
name: Secret<String>,
|
||||
number: cards::CardNumber,
|
||||
expiry_month: Secret<String>,
|
||||
expiry_year: Secret<String>,
|
||||
cvc: Secret<String>,
|
||||
complete: bool,
|
||||
#[derive(Default, Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct TsysPaymentAuthSaleRequest {
|
||||
#[serde(rename = "deviceID")]
|
||||
device_id: Secret<String>,
|
||||
transaction_key: Secret<String>,
|
||||
card_data_source: String,
|
||||
transaction_amount: String,
|
||||
currency_code: storage::enums::Currency,
|
||||
card_number: cards::CardNumber,
|
||||
expiration_date: Secret<String>,
|
||||
cvv2: Secret<String>,
|
||||
terminal_capability: String,
|
||||
terminal_operating_environment: String,
|
||||
cardholder_authentication_method: String,
|
||||
#[serde(rename = "developerID")]
|
||||
developer_id: Secret<String>,
|
||||
}
|
||||
|
||||
impl TryFrom<&types::PaymentsAuthorizeRouterData> for TsysPaymentsRequest {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(item: &types::PaymentsAuthorizeRouterData) -> Result<Self, Self::Error> {
|
||||
match item.request.payment_method_data.clone() {
|
||||
api::PaymentMethodData::Card(req_card) => {
|
||||
let card = TsysCard {
|
||||
name: req_card.card_holder_name,
|
||||
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.request.is_auto_capture()?,
|
||||
api::PaymentMethodData::Card(ccard) => {
|
||||
let connector_auth: TsysAuthType =
|
||||
TsysAuthType::try_from(&item.connector_auth_type)?;
|
||||
let auth_data: TsysPaymentAuthSaleRequest = TsysPaymentAuthSaleRequest {
|
||||
device_id: connector_auth.device_id,
|
||||
transaction_key: connector_auth.transaction_key,
|
||||
card_data_source: "INTERNET".to_string(),
|
||||
transaction_amount: item.request.amount.to_string(),
|
||||
currency_code: item.request.currency,
|
||||
card_number: ccard.card_number.clone(),
|
||||
expiration_date: ccard
|
||||
.get_card_expiry_month_year_2_digit_with_delimiter("/".to_owned()),
|
||||
cvv2: ccard.card_cvc,
|
||||
terminal_capability: "ICC_CHIP_READ_ONLY".to_string(),
|
||||
terminal_operating_environment: "ON_MERCHANT_PREMISES_ATTENDED".to_string(),
|
||||
cardholder_authentication_method: "NOT_AUTHENTICATED".to_string(),
|
||||
developer_id: connector_auth.developer_id,
|
||||
};
|
||||
Ok(Self {
|
||||
amount: item.request.amount,
|
||||
card,
|
||||
})
|
||||
if item.request.is_auto_capture()? {
|
||||
Ok(Self::Sale(auth_data))
|
||||
} else {
|
||||
Ok(Self::Auth(auth_data))
|
||||
}
|
||||
}
|
||||
_ => Err(errors::ConnectorError::NotImplemented("Payment methods".to_string()).into()),
|
||||
}
|
||||
@ -48,44 +71,152 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for TsysPaymentsRequest {
|
||||
|
||||
// Auth Struct
|
||||
pub struct TsysAuthType {
|
||||
pub(super) api_key: Secret<String>,
|
||||
pub(super) device_id: Secret<String>,
|
||||
pub(super) transaction_key: Secret<String>,
|
||||
pub(super) developer_id: Secret<String>,
|
||||
}
|
||||
|
||||
impl TryFrom<&types::ConnectorAuthType> for TsysAuthType {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(auth_type: &types::ConnectorAuthType) -> Result<Self, Self::Error> {
|
||||
match auth_type {
|
||||
types::ConnectorAuthType::HeaderKey { api_key } => Ok(Self {
|
||||
api_key: Secret::new(api_key.to_string()),
|
||||
types::ConnectorAuthType::SignatureKey {
|
||||
api_key,
|
||||
key1,
|
||||
api_secret,
|
||||
} => Ok(Self {
|
||||
device_id: Secret::new(api_key.to_string()),
|
||||
transaction_key: Secret::new(key1.to_string()),
|
||||
developer_id: Secret::new(api_secret.to_string()),
|
||||
}),
|
||||
_ => Err(errors::ConnectorError::FailedToObtainAuthType.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PaymentsResponse
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[serde(rename_all = "UPPERCASE")]
|
||||
pub enum TsysPaymentStatus {
|
||||
Succeeded,
|
||||
Failed,
|
||||
#[default]
|
||||
Processing,
|
||||
Pass,
|
||||
Fail,
|
||||
}
|
||||
|
||||
impl From<TsysPaymentStatus> for enums::AttemptStatus {
|
||||
fn from(item: TsysPaymentStatus) -> Self {
|
||||
match item {
|
||||
TsysPaymentStatus::Succeeded => Self::Charged,
|
||||
TsysPaymentStatus::Failed => Self::Failure,
|
||||
TsysPaymentStatus::Processing => Self::Authorizing,
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[serde(rename_all = "UPPERCASE")]
|
||||
pub enum TsysTransactionStatus {
|
||||
Approved,
|
||||
Declined,
|
||||
Void,
|
||||
}
|
||||
|
||||
impl From<TsysTransactionDetails> for enums::AttemptStatus {
|
||||
fn from(item: TsysTransactionDetails) -> Self {
|
||||
match item.transaction_status {
|
||||
TsysTransactionStatus::Approved => {
|
||||
if item.transaction_type.contains("Auth-Only") {
|
||||
Self::Authorized
|
||||
} else {
|
||||
Self::Charged
|
||||
}
|
||||
}
|
||||
TsysTransactionStatus::Void => Self::Voided,
|
||||
TsysTransactionStatus::Declined => Self::Failure,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct TsysPaymentsResponse {
|
||||
status: TsysPaymentStatus,
|
||||
id: String,
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct TsysErrorResponse {
|
||||
pub status: TsysPaymentStatus,
|
||||
pub response_code: String,
|
||||
pub response_message: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct TsysTransactionDetails {
|
||||
#[serde(rename = "transactionID")]
|
||||
transaction_id: String,
|
||||
transaction_type: String,
|
||||
transaction_status: TsysTransactionStatus,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct TsysPaymentsSyncResponse {
|
||||
pub status: TsysPaymentStatus,
|
||||
pub response_code: String,
|
||||
pub response_message: String,
|
||||
pub transaction_details: TsysTransactionDetails,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct TsysResponse {
|
||||
pub status: TsysPaymentStatus,
|
||||
pub response_code: String,
|
||||
pub response_message: String,
|
||||
#[serde(rename = "transactionID")]
|
||||
pub transaction_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum TsysResponseTypes {
|
||||
SuccessResponse(TsysResponse),
|
||||
ErrorResponse(TsysErrorResponse),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
pub enum TsysPaymentsResponse {
|
||||
AuthResponse(TsysResponseTypes),
|
||||
SaleResponse(TsysResponseTypes),
|
||||
CaptureResponse(TsysResponseTypes),
|
||||
VoidResponse(TsysResponseTypes),
|
||||
}
|
||||
|
||||
fn get_error_response(
|
||||
connector_error_response: TsysErrorResponse,
|
||||
status_code: u16,
|
||||
) -> types::ErrorResponse {
|
||||
types::ErrorResponse {
|
||||
code: connector_error_response.response_code,
|
||||
message: connector_error_response.response_message.clone(),
|
||||
reason: Some(connector_error_response.response_message),
|
||||
status_code,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_payments_response(connector_response: TsysResponse) -> types::PaymentsResponseData {
|
||||
types::PaymentsResponseData::TransactionResponse {
|
||||
resource_id: types::ResponseId::ConnectorTransactionId(connector_response.transaction_id),
|
||||
redirection_data: None,
|
||||
mandate_reference: None,
|
||||
connector_metadata: None,
|
||||
network_txn_id: None,
|
||||
connector_response_reference_id: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_payments_sync_response(
|
||||
connector_response: &TsysPaymentsSyncResponse,
|
||||
) -> types::PaymentsResponseData {
|
||||
types::PaymentsResponseData::TransactionResponse {
|
||||
resource_id: types::ResponseId::ConnectorTransactionId(
|
||||
connector_response
|
||||
.transaction_details
|
||||
.transaction_id
|
||||
.clone(),
|
||||
),
|
||||
redirection_data: None,
|
||||
mandate_reference: None,
|
||||
connector_metadata: None,
|
||||
network_txn_id: None,
|
||||
connector_response_reference_id: None,
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, T>
|
||||
@ -96,62 +227,253 @@ impl<F, T>
|
||||
fn try_from(
|
||||
item: types::ResponseRouterData<F, TsysPaymentsResponse, T, types::PaymentsResponseData>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
let (response, status) = match item.response {
|
||||
TsysPaymentsResponse::AuthResponse(resp) => match resp {
|
||||
TsysResponseTypes::SuccessResponse(auth_response) => (
|
||||
Ok(get_payments_response(auth_response)),
|
||||
enums::AttemptStatus::Authorized,
|
||||
),
|
||||
TsysResponseTypes::ErrorResponse(connector_error_response) => (
|
||||
Err(get_error_response(connector_error_response, item.http_code)),
|
||||
enums::AttemptStatus::AuthorizationFailed,
|
||||
),
|
||||
},
|
||||
TsysPaymentsResponse::SaleResponse(resp) => match resp {
|
||||
TsysResponseTypes::SuccessResponse(sale_response) => (
|
||||
Ok(get_payments_response(sale_response)),
|
||||
enums::AttemptStatus::Charged,
|
||||
),
|
||||
TsysResponseTypes::ErrorResponse(connector_error_response) => (
|
||||
Err(get_error_response(connector_error_response, item.http_code)),
|
||||
enums::AttemptStatus::Failure,
|
||||
),
|
||||
},
|
||||
TsysPaymentsResponse::CaptureResponse(resp) => match resp {
|
||||
TsysResponseTypes::SuccessResponse(capture_response) => (
|
||||
Ok(get_payments_response(capture_response)),
|
||||
enums::AttemptStatus::Charged,
|
||||
),
|
||||
TsysResponseTypes::ErrorResponse(connector_error_response) => (
|
||||
Err(get_error_response(connector_error_response, item.http_code)),
|
||||
enums::AttemptStatus::CaptureFailed,
|
||||
),
|
||||
},
|
||||
TsysPaymentsResponse::VoidResponse(resp) => match resp {
|
||||
TsysResponseTypes::SuccessResponse(void_response) => (
|
||||
Ok(get_payments_response(void_response)),
|
||||
enums::AttemptStatus::Voided,
|
||||
),
|
||||
TsysResponseTypes::ErrorResponse(connector_error_response) => (
|
||||
Err(get_error_response(connector_error_response, item.http_code)),
|
||||
enums::AttemptStatus::VoidFailed,
|
||||
),
|
||||
},
|
||||
};
|
||||
Ok(Self {
|
||||
status: enums::AttemptStatus::from(item.response.status),
|
||||
response: Ok(types::PaymentsResponseData::TransactionResponse {
|
||||
resource_id: types::ResponseId::ConnectorTransactionId(item.response.id),
|
||||
redirection_data: None,
|
||||
mandate_reference: None,
|
||||
connector_metadata: None,
|
||||
network_txn_id: None,
|
||||
connector_response_reference_id: None,
|
||||
}),
|
||||
status,
|
||||
response,
|
||||
..item.data
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct TsysSearchTransactionRequest {
|
||||
#[serde(rename = "deviceID")]
|
||||
device_id: Secret<String>,
|
||||
transaction_key: Secret<String>,
|
||||
#[serde(rename = "transactionID")]
|
||||
transaction_id: String,
|
||||
#[serde(rename = "developerID")]
|
||||
developer_id: Secret<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "PascalCase")]
|
||||
pub struct TsysSyncRequest {
|
||||
search_transaction: TsysSearchTransactionRequest,
|
||||
}
|
||||
|
||||
impl TryFrom<&types::PaymentsSyncRouterData> for TsysSyncRequest {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(item: &types::PaymentsSyncRouterData) -> Result<Self, Self::Error> {
|
||||
let connector_auth: TsysAuthType = TsysAuthType::try_from(&item.connector_auth_type)?;
|
||||
let search_transaction = TsysSearchTransactionRequest {
|
||||
device_id: connector_auth.device_id,
|
||||
transaction_key: connector_auth.transaction_key,
|
||||
transaction_id: item
|
||||
.request
|
||||
.connector_transaction_id
|
||||
.get_connector_transaction_id()
|
||||
.change_context(errors::ConnectorError::MissingConnectorTransactionID)?,
|
||||
developer_id: connector_auth.developer_id,
|
||||
};
|
||||
Ok(Self { search_transaction })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum SearchResponseTypes {
|
||||
SuccessResponse(TsysPaymentsSyncResponse),
|
||||
ErrorResponse(TsysErrorResponse),
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "PascalCase")]
|
||||
pub struct TsysSyncResponse {
|
||||
search_transaction_response: SearchResponseTypes,
|
||||
}
|
||||
|
||||
impl<F, T> TryFrom<types::ResponseRouterData<F, TsysSyncResponse, T, types::PaymentsResponseData>>
|
||||
for types::RouterData<F, T, types::PaymentsResponseData>
|
||||
{
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(
|
||||
item: types::ResponseRouterData<F, TsysSyncResponse, T, types::PaymentsResponseData>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
let tsys_search_response = item.response.search_transaction_response;
|
||||
let (response, status) = match tsys_search_response {
|
||||
SearchResponseTypes::SuccessResponse(search_response) => (
|
||||
Ok(get_payments_sync_response(&search_response)),
|
||||
enums::AttemptStatus::from(search_response.transaction_details),
|
||||
),
|
||||
SearchResponseTypes::ErrorResponse(connector_error_response) => (
|
||||
Err(get_error_response(connector_error_response, item.http_code)),
|
||||
item.data.status,
|
||||
),
|
||||
};
|
||||
Ok(Self {
|
||||
status,
|
||||
response,
|
||||
..item.data
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct TsysCancelRequest {
|
||||
#[serde(rename = "deviceID")]
|
||||
device_id: Secret<String>,
|
||||
transaction_key: Secret<String>,
|
||||
#[serde(rename = "transactionID")]
|
||||
transaction_id: String,
|
||||
#[serde(rename = "developerID")]
|
||||
developer_id: Secret<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "PascalCase")]
|
||||
pub struct TsysPaymentsCancelRequest {
|
||||
void: TsysCancelRequest,
|
||||
}
|
||||
impl TryFrom<&types::PaymentsCancelRouterData> for TsysPaymentsCancelRequest {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(item: &types::PaymentsCancelRouterData) -> Result<Self, Self::Error> {
|
||||
let connector_auth: TsysAuthType = TsysAuthType::try_from(&item.connector_auth_type)?;
|
||||
let void = TsysCancelRequest {
|
||||
device_id: connector_auth.device_id,
|
||||
transaction_key: connector_auth.transaction_key,
|
||||
transaction_id: item.request.connector_transaction_id.clone(),
|
||||
developer_id: connector_auth.developer_id,
|
||||
};
|
||||
Ok(Self { void })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct TsysCaptureRequest {
|
||||
#[serde(rename = "deviceID")]
|
||||
device_id: Secret<String>,
|
||||
transaction_key: Secret<String>,
|
||||
transaction_amount: String,
|
||||
#[serde(rename = "transactionID")]
|
||||
transaction_id: String,
|
||||
#[serde(rename = "developerID")]
|
||||
developer_id: Secret<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "PascalCase")]
|
||||
|
||||
pub struct TsysPaymentsCaptureRequest {
|
||||
capture: TsysCaptureRequest,
|
||||
}
|
||||
|
||||
impl TryFrom<&types::PaymentsCaptureRouterData> for TsysPaymentsCaptureRequest {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(item: &types::PaymentsCaptureRouterData) -> Result<Self, Self::Error> {
|
||||
let connector_auth: TsysAuthType = TsysAuthType::try_from(&item.connector_auth_type)?;
|
||||
let capture = TsysCaptureRequest {
|
||||
device_id: connector_auth.device_id,
|
||||
transaction_key: connector_auth.transaction_key,
|
||||
transaction_id: item.request.connector_transaction_id.clone(),
|
||||
developer_id: connector_auth.developer_id,
|
||||
transaction_amount: item.request.amount_to_capture.to_string(),
|
||||
};
|
||||
Ok(Self { capture })
|
||||
}
|
||||
}
|
||||
// REFUND :
|
||||
// Type definition for RefundRequest
|
||||
#[derive(Default, Debug, Serialize)]
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct TsysReturnRequest {
|
||||
#[serde(rename = "deviceID")]
|
||||
device_id: Secret<String>,
|
||||
transaction_key: Secret<String>,
|
||||
transaction_amount: String,
|
||||
#[serde(rename = "transactionID")]
|
||||
transaction_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "PascalCase")]
|
||||
pub struct TsysRefundRequest {
|
||||
pub amount: i64,
|
||||
#[serde(rename = "Return")]
|
||||
return_request: TsysReturnRequest,
|
||||
}
|
||||
|
||||
impl<F> TryFrom<&types::RefundsRouterData<F>> for TsysRefundRequest {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(item: &types::RefundsRouterData<F>) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
amount: item.request.refund_amount,
|
||||
})
|
||||
let connector_auth: TsysAuthType = TsysAuthType::try_from(&item.connector_auth_type)?;
|
||||
let return_request = TsysReturnRequest {
|
||||
device_id: connector_auth.device_id,
|
||||
transaction_key: connector_auth.transaction_key,
|
||||
transaction_amount: item.request.refund_amount.to_string(),
|
||||
transaction_id: item.request.connector_transaction_id.clone(),
|
||||
};
|
||||
Ok(Self { return_request })
|
||||
}
|
||||
}
|
||||
|
||||
// Type definition for Refund Response
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug, Serialize, Default, Deserialize, Clone)]
|
||||
pub enum RefundStatus {
|
||||
Succeeded,
|
||||
Failed,
|
||||
#[default]
|
||||
Processing,
|
||||
}
|
||||
|
||||
impl From<RefundStatus> for enums::RefundStatus {
|
||||
fn from(item: RefundStatus) -> Self {
|
||||
impl From<TsysPaymentStatus> for enums::RefundStatus {
|
||||
fn from(item: TsysPaymentStatus) -> Self {
|
||||
match item {
|
||||
RefundStatus::Succeeded => Self::Success,
|
||||
RefundStatus::Failed => Self::Failure,
|
||||
RefundStatus::Processing => Self::Pending,
|
||||
//TODO: Review mapping
|
||||
TsysPaymentStatus::Pass => Self::Success,
|
||||
TsysPaymentStatus::Fail => Self::Failure,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
|
||||
impl From<TsysTransactionDetails> for enums::RefundStatus {
|
||||
fn from(item: TsysTransactionDetails) -> Self {
|
||||
match item.transaction_status {
|
||||
TsysTransactionStatus::Approved => Self::Pending,
|
||||
TsysTransactionStatus::Void => Self::Success,
|
||||
TsysTransactionStatus::Declined => Self::Failure,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "PascalCase")]
|
||||
pub struct RefundResponse {
|
||||
id: String,
|
||||
status: RefundStatus,
|
||||
return_response: TsysResponseTypes,
|
||||
}
|
||||
|
||||
impl TryFrom<types::RefundsResponseRouterData<api::Execute, RefundResponse>>
|
||||
@ -161,37 +483,59 @@ impl TryFrom<types::RefundsResponseRouterData<api::Execute, RefundResponse>>
|
||||
fn try_from(
|
||||
item: types::RefundsResponseRouterData<api::Execute, RefundResponse>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
response: Ok(types::RefundsResponseData {
|
||||
connector_refund_id: item.response.id.to_string(),
|
||||
refund_status: enums::RefundStatus::from(item.response.status),
|
||||
let tsys_return_response = item.response.return_response;
|
||||
let response = match tsys_return_response {
|
||||
TsysResponseTypes::SuccessResponse(return_response) => Ok(types::RefundsResponseData {
|
||||
connector_refund_id: return_response.transaction_id,
|
||||
refund_status: enums::RefundStatus::from(return_response.status),
|
||||
}),
|
||||
TsysResponseTypes::ErrorResponse(connector_error_response) => {
|
||||
Err(get_error_response(connector_error_response, item.http_code))
|
||||
}
|
||||
};
|
||||
Ok(Self {
|
||||
response,
|
||||
..item.data
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<types::RefundsResponseRouterData<api::RSync, RefundResponse>>
|
||||
impl TryFrom<&types::RefundSyncRouterData> for TsysSyncRequest {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(item: &types::RefundSyncRouterData) -> Result<Self, Self::Error> {
|
||||
let connector_auth: TsysAuthType = TsysAuthType::try_from(&item.connector_auth_type)?;
|
||||
let search_transaction = TsysSearchTransactionRequest {
|
||||
device_id: connector_auth.device_id,
|
||||
transaction_key: connector_auth.transaction_key,
|
||||
transaction_id: item.request.get_connector_refund_id()?,
|
||||
developer_id: connector_auth.developer_id,
|
||||
};
|
||||
Ok(Self { search_transaction })
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<types::RefundsResponseRouterData<api::RSync, TsysSyncResponse>>
|
||||
for types::RefundsRouterData<api::RSync>
|
||||
{
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(
|
||||
item: types::RefundsResponseRouterData<api::RSync, RefundResponse>,
|
||||
item: types::RefundsResponseRouterData<api::RSync, TsysSyncResponse>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
let tsys_search_response = item.response.search_transaction_response;
|
||||
let response = match tsys_search_response {
|
||||
SearchResponseTypes::SuccessResponse(search_response) => {
|
||||
Ok(types::RefundsResponseData {
|
||||
connector_refund_id: search_response.transaction_details.transaction_id.clone(),
|
||||
refund_status: enums::RefundStatus::from(search_response.transaction_details),
|
||||
})
|
||||
}
|
||||
SearchResponseTypes::ErrorResponse(connector_error_response) => {
|
||||
Err(get_error_response(connector_error_response, item.http_code))
|
||||
}
|
||||
};
|
||||
Ok(Self {
|
||||
response: Ok(types::RefundsResponseData {
|
||||
connector_refund_id: item.response.id.to_string(),
|
||||
refund_status: enums::RefundStatus::from(item.response.status),
|
||||
}),
|
||||
response,
|
||||
..item.data
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct TsysErrorResponse {
|
||||
pub status_code: u16,
|
||||
pub code: String,
|
||||
pub message: String,
|
||||
pub reason: Option<String>,
|
||||
}
|
||||
|
||||
@ -253,6 +253,7 @@ impl ConnectorData {
|
||||
enums::Connector::Nexinets => Ok(Box::new(&connector::Nexinets)),
|
||||
enums::Connector::Paypal => Ok(Box::new(&connector::Paypal)),
|
||||
enums::Connector::Trustpay => Ok(Box::new(&connector::Trustpay)),
|
||||
enums::Connector::Tsys => Ok(Box::new(&connector::Tsys)),
|
||||
enums::Connector::Zen => Ok(Box::new(&connector::Zen)),
|
||||
},
|
||||
Err(_) => Err(report!(errors::ConnectorError::InvalidConnectorName)
|
||||
|
||||
@ -143,6 +143,12 @@ key1 = "key1"
|
||||
api_key="Classic PMT API Key"
|
||||
key1 = "Evoucher PMT API Key"
|
||||
|
||||
|
||||
[tsys]
|
||||
api_key="device id"
|
||||
key1 = "transaction key"
|
||||
api_secret = "developer id"
|
||||
|
||||
[globepay]
|
||||
api_key = "Partner code"
|
||||
key1 = "Credential code"
|
||||
@ -150,6 +156,3 @@ key1 = "Credential code"
|
||||
[powertranz]
|
||||
api_key="PowerTranz-PowerTranzPassword"
|
||||
key1 = "PowerTranz-PowerTranzId"
|
||||
|
||||
[tsys]
|
||||
api_key="API Key"
|
||||
@ -1,8 +1,13 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use cards::CardNumber;
|
||||
use masking::Secret;
|
||||
use router::types::{self, api, storage::enums};
|
||||
use test_utils::connector_auth;
|
||||
|
||||
use crate::utils::{self, ConnectorActions};
|
||||
use crate::{
|
||||
connector_auth,
|
||||
utils::{self, ConnectorActions},
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct TsysTest;
|
||||
@ -12,7 +17,7 @@ impl utils::Connector for TsysTest {
|
||||
use router::connector::Tsys;
|
||||
types::api::ConnectorData {
|
||||
connector: Box::new(&Tsys),
|
||||
connector_name: types::Connector::DummyConnector1,
|
||||
connector_name: types::Connector::Tsys,
|
||||
get_token: types::api::GetToken::Connector,
|
||||
}
|
||||
}
|
||||
@ -20,7 +25,7 @@ impl utils::Connector for TsysTest {
|
||||
fn get_auth_token(&self) -> types::ConnectorAuthType {
|
||||
types::ConnectorAuthType::from(
|
||||
connector_auth::ConnectorAuthentication::new()
|
||||
.dummyconnector
|
||||
.tsys
|
||||
.expect("Missing connector authentication configuration"),
|
||||
)
|
||||
}
|
||||
@ -37,7 +42,13 @@ fn get_default_payment_info() -> Option<utils::PaymentInfo> {
|
||||
}
|
||||
|
||||
fn payment_method_details() -> Option<types::PaymentsAuthorizeData> {
|
||||
None
|
||||
Some(types::PaymentsAuthorizeData {
|
||||
payment_method_data: types::api::PaymentMethodData::Card(api::Card {
|
||||
card_number: CardNumber::from_str("4111111111111111").unwrap(),
|
||||
..utils::CCardType::default().0
|
||||
}),
|
||||
..utils::PaymentAuthorizeType::default().0
|
||||
})
|
||||
}
|
||||
|
||||
// Cards Positive Tests
|
||||
@ -301,7 +312,7 @@ async fn should_fail_payment_for_incorrect_cvc() {
|
||||
.make_payment(
|
||||
Some(types::PaymentsAuthorizeData {
|
||||
payment_method_data: types::api::PaymentMethodData::Card(api::Card {
|
||||
card_cvc: Secret::new("12345".to_string()),
|
||||
card_cvc: Secret::new("".to_string()),
|
||||
..utils::CCardType::default().0
|
||||
}),
|
||||
..utils::PaymentAuthorizeType::default().0
|
||||
@ -312,7 +323,7 @@ async fn should_fail_payment_for_incorrect_cvc() {
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
response.response.unwrap_err().message,
|
||||
"Your card's security code is invalid.".to_string(),
|
||||
"The value of element cvv2 is not valid.".to_string(),
|
||||
);
|
||||
}
|
||||
|
||||
@ -334,7 +345,7 @@ async fn should_fail_payment_for_invalid_exp_month() {
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
response.response.unwrap_err().message,
|
||||
"Your card's expiration month is invalid.".to_string(),
|
||||
"The value of element 'expirationDate' is not valid., ".to_string(),
|
||||
);
|
||||
}
|
||||
|
||||
@ -345,7 +356,7 @@ async fn should_fail_payment_for_incorrect_expiry_year() {
|
||||
.make_payment(
|
||||
Some(types::PaymentsAuthorizeData {
|
||||
payment_method_data: types::api::PaymentMethodData::Card(api::Card {
|
||||
card_exp_year: Secret::new("2000".to_string()),
|
||||
card_exp_year: Secret::new("abcd".to_string()),
|
||||
..utils::CCardType::default().0
|
||||
}),
|
||||
..utils::PaymentAuthorizeType::default().0
|
||||
@ -356,12 +367,13 @@ async fn should_fail_payment_for_incorrect_expiry_year() {
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
response.response.unwrap_err().message,
|
||||
"Your card's expiration year is invalid.".to_string(),
|
||||
"The value of element 'expirationDate' is not valid., ".to_string(),
|
||||
);
|
||||
}
|
||||
|
||||
// Voids a payment using automatic capture flow (Non 3DS).
|
||||
#[actix_web::test]
|
||||
#[ignore = "Connector Refunds the payment on Void call for Auto Captured Payment"]
|
||||
async fn should_fail_void_payment_for_auto_capture() {
|
||||
let authorize_response = CONNECTOR
|
||||
.make_payment(payment_method_details(), get_default_payment_info())
|
||||
@ -389,7 +401,7 @@ async fn should_fail_capture_for_invalid_payment() {
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
capture_response.response.unwrap_err().message,
|
||||
String::from("No such payment_intent: '123456789'")
|
||||
String::from("Record(s) Not Found.")
|
||||
);
|
||||
}
|
||||
|
||||
@ -409,7 +421,7 @@ async fn should_fail_for_refund_amount_higher_than_payment_amount() {
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
response.response.unwrap_err().message,
|
||||
"Refund amount (₹1.50) is greater than charge amount (₹1.00)",
|
||||
"Return Not Allowed.",
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -46,6 +46,7 @@ pub struct ConnectorAuthentication {
|
||||
pub stripe_au: Option<HeaderKey>,
|
||||
pub stripe_uk: Option<HeaderKey>,
|
||||
pub trustpay: Option<SignatureKey>,
|
||||
pub tsys: Option<SignatureKey>,
|
||||
pub worldpay: Option<BodyKey>,
|
||||
pub worldline: Option<SignatureKey>,
|
||||
pub zen: Option<HeaderKey>,
|
||||
|
||||
@ -2998,6 +2998,7 @@
|
||||
"shift4",
|
||||
"stripe",
|
||||
"trustpay",
|
||||
"tsys",
|
||||
"worldline",
|
||||
"worldpay",
|
||||
"zen"
|
||||
|
||||
Reference in New Issue
Block a user