mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-11-01 19:42:27 +08:00
test(connector): add and update tests for stripe, cybersource and shift4 connectors (#485)
This commit is contained in:
@ -1,8 +1,6 @@
|
||||
mod transformers;
|
||||
|
||||
use std::fmt::Debug;
|
||||
|
||||
use bytes::Bytes;
|
||||
use error_stack::{ResultExt, IntoReport};
|
||||
|
||||
use crate::{
|
||||
@ -52,8 +50,8 @@ impl ConnectorCommon for {{project-name | downcase | pascal_case}} {
|
||||
connectors.{{project-name}}.base_url.as_ref()
|
||||
}
|
||||
|
||||
fn get_auth_header(&self,_auth_type:&types::ConnectorAuthType)-> CustomResult<Vec<(String,String)>,errors::ConnectorError> {
|
||||
let auth: {{project-name | downcase | pascal_case}}::{{project-name | downcase | pascal_case}}AuthType = auth_type
|
||||
fn get_auth_header(&self, auth_type:&types::ConnectorAuthType)-> CustomResult<Vec<(String,String)>,errors::ConnectorError> {
|
||||
let auth: {{project-name | downcase}}::{{project-name | downcase | pascal_case}}AuthType = auth_type
|
||||
.try_into()
|
||||
.change_context(errors::ConnectorError::FailedToObtainAuthType)?;
|
||||
Ok(vec![(headers::AUTHORIZATION.to_string(), auth.api_key)])
|
||||
@ -82,6 +80,13 @@ impl
|
||||
> for {{project-name | downcase | pascal_case}}
|
||||
{}
|
||||
|
||||
impl api::ConnectorAccessToken for {{project-name | downcase | pascal_case}} {}
|
||||
|
||||
impl ConnectorIntegration<api::AccessTokenAuth, types::AccessTokenRequestData, types::AccessToken>
|
||||
for {{project-name | downcase | pascal_case}}
|
||||
{
|
||||
}
|
||||
|
||||
impl api::PaymentSync for {{project-name | downcase | pascal_case}} {}
|
||||
impl
|
||||
ConnectorIntegration<api::PSync, types::PaymentsSyncData, types::PaymentsResponseData>
|
||||
@ -109,8 +114,8 @@ impl
|
||||
|
||||
fn build_request(
|
||||
&self,
|
||||
_req: &types::PaymentsSyncRouterData,
|
||||
_connectors: &settings::Connectors,
|
||||
req: &types::PaymentsSyncRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
|
||||
Ok(Some(
|
||||
services::RequestBuilder::new()
|
||||
@ -123,15 +128,15 @@ impl
|
||||
|
||||
fn get_error_response(
|
||||
&self,
|
||||
_res: types::Response,
|
||||
res: Response,
|
||||
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
|
||||
self.build_error_response(res)
|
||||
}
|
||||
|
||||
fn handle_response(
|
||||
&self,
|
||||
_data: &types::PaymentsSyncRouterData,
|
||||
_res: Response,
|
||||
data: &types::PaymentsSyncRouterData,
|
||||
res: Response,
|
||||
) -> CustomResult<types::PaymentsSyncRouterData, errors::ConnectorError> {
|
||||
logger::debug!(payment_sync_response=?res);
|
||||
let response: {{project-name | downcase}}:: {{project-name | downcase | pascal_case}}PaymentsResponse = res
|
||||
@ -185,8 +190,8 @@ impl
|
||||
|
||||
fn build_request(
|
||||
&self,
|
||||
_req: &types::PaymentsCaptureRouterData,
|
||||
_connectors: &settings::Connectors,
|
||||
req: &types::PaymentsCaptureRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
|
||||
Ok(Some(
|
||||
services::RequestBuilder::new()
|
||||
@ -201,8 +206,8 @@ impl
|
||||
|
||||
fn handle_response(
|
||||
&self,
|
||||
_data: &types::PaymentsCaptureRouterData,
|
||||
_res: Response,
|
||||
data: &types::PaymentsCaptureRouterData,
|
||||
res: Response,
|
||||
) -> CustomResult<types::PaymentsCaptureRouterData, errors::ConnectorError> {
|
||||
let response: {{project-name | downcase }}::{{project-name | downcase | pascal_case}}PaymentsResponse = res
|
||||
.response
|
||||
@ -220,7 +225,7 @@ impl
|
||||
|
||||
fn get_error_response(
|
||||
&self,
|
||||
_res: types::Response,
|
||||
res: Response,
|
||||
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
|
||||
self.build_error_response(res)
|
||||
}
|
||||
@ -299,7 +304,7 @@ impl
|
||||
.change_context(errors::ConnectorError::ResponseHandlingFailed)
|
||||
}
|
||||
|
||||
fn get_error_response(&self, res: types::Response) -> CustomResult<ErrorResponse,errors::ConnectorError> {
|
||||
fn get_error_response(&self, res: Response) -> CustomResult<ErrorResponse,errors::ConnectorError> {
|
||||
self.build_error_response(res)
|
||||
}
|
||||
}
|
||||
@ -357,7 +362,7 @@ impl
|
||||
.change_context(errors::ConnectorError::ResponseHandlingFailed)
|
||||
}
|
||||
|
||||
fn get_error_response(&self, res: types::Response) -> CustomResult<ErrorResponse,errors::ConnectorError> {
|
||||
fn get_error_response(&self, res: Response) -> CustomResult<ErrorResponse,errors::ConnectorError> {
|
||||
self.build_error_response(res)
|
||||
}
|
||||
}
|
||||
@ -407,7 +412,7 @@ impl
|
||||
.change_context(errors::ConnectorError::ResponseHandlingFailed)
|
||||
}
|
||||
|
||||
fn get_error_response(&self, res: types::Response) -> CustomResult<ErrorResponse,errors::ConnectorError> {
|
||||
fn get_error_response(&self, res: Response) -> CustomResult<ErrorResponse,errors::ConnectorError> {
|
||||
self.build_error_response(res)
|
||||
}
|
||||
}
|
||||
|
||||
@ -79,7 +79,7 @@ async fn should_sync_authorized_payment() {
|
||||
.authorize_payment(None, None)
|
||||
.await
|
||||
.expect("Authorize payment response");
|
||||
let txn_id = utils::get_connector_transaction_id(authorize_response);
|
||||
let txn_id = utils::get_connector_transaction_id(authorize_response.response);
|
||||
let response = CONNECTOR
|
||||
.psync_retry_till_status_matches(
|
||||
enums::AttemptStatus::Authorized,
|
||||
@ -182,7 +182,7 @@ async fn should_make_payment() {
|
||||
async fn should_sync_auto_captured_payment() {
|
||||
let authorize_response = CONNECTOR.make_payment(None, None).await.unwrap();
|
||||
assert_eq!(authorize_response.status, enums::AttemptStatus::Charged);
|
||||
let txn_id = utils::get_connector_transaction_id(authorize_response);
|
||||
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(
|
||||
@ -388,7 +388,7 @@ async fn should_fail_payment_for_incorrect_expiry_year() {
|
||||
async fn should_fail_void_payment_for_auto_capture() {
|
||||
let authorize_response = CONNECTOR.make_payment(None, None).await.unwrap();
|
||||
assert_eq!(authorize_response.status, enums::AttemptStatus::Charged);
|
||||
let txn_id = utils::get_connector_transaction_id(authorize_response);
|
||||
let txn_id = utils::get_connector_transaction_id(authorize_response.response);
|
||||
assert_ne!(txn_id, None, "Empty connector transaction id");
|
||||
let void_response = CONNECTOR
|
||||
.void_payment(txn_id.unwrap(), None, None)
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use crate::{core::errors,pii::PeekInterface,types::{self,api, storage::enums}};
|
||||
use crate::{core::errors,types::{self,api, storage::enums}};
|
||||
|
||||
//TODO: Fill the struct with respective fields
|
||||
#[derive(Default, Debug, Serialize, Eq, PartialEq)]
|
||||
@ -14,7 +14,9 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for {{project-name | downcase
|
||||
|
||||
//TODO: Fill the struct with respective fields
|
||||
// Auth Struct
|
||||
pub struct {{project-name | downcase | pascal_case}}AuthType {}
|
||||
pub struct {{project-name | downcase | pascal_case}}AuthType {
|
||||
pub(super) api_key: String
|
||||
}
|
||||
|
||||
impl TryFrom<&types::ConnectorAuthType> for {{project-name | downcase | pascal_case}}AuthType {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
@ -45,7 +47,10 @@ impl From<{{project-name | downcase | pascal_case}}PaymentStatus> for enums::Att
|
||||
|
||||
//TODO: Fill the struct with respective fields
|
||||
#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct {{project-name | downcase | pascal_case}}PaymentsResponse {}
|
||||
pub struct {{project-name | downcase | pascal_case}}PaymentsResponse {
|
||||
status: {{project-name | downcase | pascal_case}}PaymentStatus,
|
||||
id: String,
|
||||
}
|
||||
|
||||
impl<F,T> TryFrom<types::ResponseRouterData<F, {{project-name | downcase | pascal_case}}PaymentsResponse, T, types::PaymentsResponseData>> for types::RouterData<F, T, types::PaymentsResponseData> {
|
||||
type Error = error_stack::Report<errors::ParsingError>;
|
||||
@ -57,6 +62,7 @@ impl<F,T> TryFrom<types::ResponseRouterData<F, {{project-name | downcase | pasca
|
||||
redirection_data: None,
|
||||
redirect: false,
|
||||
mandate_reference: None,
|
||||
connector_metadata: None,
|
||||
}),
|
||||
..item.data
|
||||
})
|
||||
@ -87,8 +93,8 @@ pub enum RefundStatus {
|
||||
Processing,
|
||||
}
|
||||
|
||||
impl From<self::RefundStatus> for enums::RefundStatus {
|
||||
fn from(item: self::RefundStatus) -> Self {
|
||||
impl From<RefundStatus> for enums::RefundStatus {
|
||||
fn from(item: RefundStatus) -> Self {
|
||||
match item {
|
||||
RefundStatus::Succeeded => Self::Success,
|
||||
RefundStatus::Failed => Self::Failure,
|
||||
@ -108,7 +114,7 @@ impl TryFrom<types::RefundsResponseRouterData<api::Execute, RefundResponse>>
|
||||
{
|
||||
type Error = error_stack::Report<errors::ParsingError>;
|
||||
fn try_from(
|
||||
item: types::RefundsResponseRouterData<api::Execute, RefundResponse>,
|
||||
_item: types::RefundsResponseRouterData<api::Execute, RefundResponse>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ use error_stack::ResultExt;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
connector::utils::RefundsRequestData,
|
||||
core::errors,
|
||||
pii::PeekInterface,
|
||||
types::{self, api, storage::enums},
|
||||
@ -466,12 +467,7 @@ impl<F> TryFrom<&types::RefundsRouterData<F>> for AuthorizedotnetCreateSyncReque
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
|
||||
fn try_from(item: &types::RefundsRouterData<F>) -> Result<Self, Self::Error> {
|
||||
let transaction_id = item
|
||||
.request
|
||||
.connector_refund_id
|
||||
.clone()
|
||||
.get_required_value("connector_refund_id")
|
||||
.change_context(errors::ConnectorError::MissingConnectorRefundID)?;
|
||||
let transaction_id = item.request.get_connector_refund_id()?;
|
||||
let merchant_authentication = MerchantAuthentication::try_from(&item.connector_auth_type)?;
|
||||
|
||||
let payload = Self {
|
||||
|
||||
@ -7,6 +7,7 @@ use std::fmt::Debug;
|
||||
use error_stack::{IntoReport, ResultExt};
|
||||
|
||||
use self::transformers as checkout;
|
||||
use super::utils::RefundsRequestData;
|
||||
use crate::{
|
||||
configs::settings,
|
||||
consts,
|
||||
@ -19,7 +20,7 @@ use crate::{
|
||||
self,
|
||||
api::{self, ConnectorCommon},
|
||||
},
|
||||
utils::{self, BytesExt, OptionExt},
|
||||
utils::{self, BytesExt},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@ -674,12 +675,7 @@ impl services::ConnectorIntegration<api::RSync, types::RefundsData, types::Refun
|
||||
data: &types::RefundsRouterData<api::RSync>,
|
||||
res: types::Response,
|
||||
) -> CustomResult<types::RefundsRouterData<api::RSync>, errors::ConnectorError> {
|
||||
let refund_action_id = data
|
||||
.request
|
||||
.connector_refund_id
|
||||
.clone()
|
||||
.get_required_value("connector_refund_id")
|
||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||
let refund_action_id = data.request.get_connector_refund_id()?;
|
||||
|
||||
let response: Vec<checkout::ActionResponse> = res
|
||||
.response
|
||||
|
||||
@ -11,6 +11,7 @@ use url::Url;
|
||||
|
||||
use crate::{
|
||||
configs::settings,
|
||||
connector::utils::RefundsRequestData,
|
||||
consts,
|
||||
core::errors::{self, CustomResult},
|
||||
headers, logger,
|
||||
@ -19,7 +20,7 @@ use crate::{
|
||||
self,
|
||||
api::{self, ConnectorCommon, ConnectorCommonExt},
|
||||
},
|
||||
utils::{self, BytesExt, OptionExt},
|
||||
utils::{self, BytesExt},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@ -95,8 +96,19 @@ impl ConnectorCommon for Cybersource {
|
||||
Ok(types::ErrorResponse {
|
||||
status_code: res.status_code,
|
||||
code: consts::NO_ERROR_CODE.to_string(),
|
||||
message: response.details.to_string(),
|
||||
reason: None,
|
||||
message: response
|
||||
.message
|
||||
.map(|m| {
|
||||
format!(
|
||||
"{} {}",
|
||||
m,
|
||||
response.details.map(|d| d.to_string()).unwrap_or_default()
|
||||
)
|
||||
.trim()
|
||||
.to_string()
|
||||
})
|
||||
.unwrap_or_else(|| consts::NO_ERROR_MESSAGE.to_string()),
|
||||
reason: response.reason,
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -633,13 +645,7 @@ impl ConnectorIntegration<api::RSync, types::RefundsData, types::RefundsResponse
|
||||
req: &types::RefundSyncRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
let refund_id = req
|
||||
.response
|
||||
.clone()
|
||||
.ok()
|
||||
.get_required_value("response")
|
||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?
|
||||
.connector_refund_id;
|
||||
let refund_id = req.request.get_connector_refund_id()?;
|
||||
Ok(format!(
|
||||
"{}tss/v2/transactions/{}",
|
||||
self.base_url(connectors),
|
||||
|
||||
@ -260,7 +260,9 @@ impl From<CybersourcePaymentStatus> for enums::AttemptStatus {
|
||||
impl From<CybersourcePaymentStatus> for enums::RefundStatus {
|
||||
fn from(item: CybersourcePaymentStatus) -> Self {
|
||||
match item {
|
||||
CybersourcePaymentStatus::Succeeded => Self::Success,
|
||||
CybersourcePaymentStatus::Succeeded | CybersourcePaymentStatus::Transmitted => {
|
||||
Self::Success
|
||||
}
|
||||
CybersourcePaymentStatus::Failed => Self::Failure,
|
||||
_ => Self::Pending,
|
||||
}
|
||||
@ -390,9 +392,10 @@ impl<F, T>
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ErrorResponse {
|
||||
pub error_information: Option<ErrorInformation>,
|
||||
pub status: String,
|
||||
pub status: Option<String>,
|
||||
pub message: Option<String>,
|
||||
pub details: serde_json::Value,
|
||||
pub reason: Option<String>,
|
||||
pub details: Option<serde_json::Value>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize)]
|
||||
@ -413,7 +416,7 @@ impl<F> TryFrom<&types::RefundsRouterData<F>> for CybersourceRefundRequest {
|
||||
Ok(Self {
|
||||
order_information: OrderInformation {
|
||||
amount_details: Amount {
|
||||
total_amount: item.request.amount.to_string(),
|
||||
total_amount: item.request.refund_amount.to_string(),
|
||||
currency: item.request.currency.to_string(),
|
||||
},
|
||||
},
|
||||
|
||||
@ -6,6 +6,7 @@ use common_utils::ext_traits::ByteSliceExt;
|
||||
use error_stack::ResultExt;
|
||||
use transformers as shift4;
|
||||
|
||||
use super::utils::RefundsRequestData;
|
||||
use crate::{
|
||||
configs::settings,
|
||||
consts,
|
||||
@ -20,7 +21,7 @@ use crate::{
|
||||
api::{self, ConnectorCommon, ConnectorCommonExt},
|
||||
ErrorResponse,
|
||||
},
|
||||
utils::{self, BytesExt, OptionExt},
|
||||
utils::{self, BytesExt},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@ -444,12 +445,7 @@ impl ConnectorIntegration<api::RSync, types::RefundsData, types::RefundsResponse
|
||||
req: &types::RefundSyncRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
let refund_id = req
|
||||
.request
|
||||
.connector_refund_id
|
||||
.clone()
|
||||
.get_required_value("connector_refund_id")
|
||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||
let refund_id = req.request.get_connector_refund_id()?;
|
||||
Ok(format!(
|
||||
"{}refunds/{}",
|
||||
self.base_url(connectors),
|
||||
|
||||
@ -177,7 +177,7 @@ impl<F> TryFrom<&types::RefundsRouterData<F>> for Shift4RefundRequest {
|
||||
fn try_from(item: &types::RefundsRouterData<F>) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
charge_id: item.request.connector_transaction_id.clone(),
|
||||
amount: item.request.amount,
|
||||
amount: item.request.refund_amount,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@ use error_stack::{IntoReport, ResultExt};
|
||||
use router_env::{instrument, tracing};
|
||||
|
||||
use self::transformers as stripe;
|
||||
use super::utils::RefundsRequestData;
|
||||
use crate::{
|
||||
configs::settings,
|
||||
consts,
|
||||
@ -19,7 +20,7 @@ use crate::{
|
||||
self,
|
||||
api::{self, ConnectorCommon},
|
||||
},
|
||||
utils::{self, crypto, ByteSliceExt, BytesExt, OptionExt},
|
||||
utils::{self, crypto, ByteSliceExt, BytesExt},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@ -779,12 +780,7 @@ impl services::ConnectorIntegration<api::RSync, types::RefundsData, types::Refun
|
||||
req: &types::RefundsRouterData<api::RSync>,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
let id = req
|
||||
.request
|
||||
.connector_refund_id
|
||||
.clone()
|
||||
.get_required_value("connector_refund_id")
|
||||
.change_context(errors::ConnectorError::FailedToObtainIntegrationUrl)?;
|
||||
let id = req.request.get_connector_refund_id()?;
|
||||
Ok(format!("{}v1/refunds/{}", self.base_url(connectors), id))
|
||||
}
|
||||
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
use error_stack::ResultExt;
|
||||
use masking::Secret;
|
||||
|
||||
use crate::{
|
||||
core::errors,
|
||||
pii::PeekInterface,
|
||||
types::{self, api},
|
||||
utils::OptionExt,
|
||||
};
|
||||
|
||||
pub fn missing_field_err(
|
||||
@ -40,6 +42,19 @@ pub trait PaymentsRequestData {
|
||||
fn get_card(&self) -> Result<api::Card, Error>;
|
||||
}
|
||||
|
||||
pub trait RefundsRequestData {
|
||||
fn get_connector_refund_id(&self) -> Result<String, Error>;
|
||||
}
|
||||
|
||||
impl RefundsRequestData for types::RefundsData {
|
||||
fn get_connector_refund_id(&self) -> Result<String, Error> {
|
||||
self.connector_refund_id
|
||||
.clone()
|
||||
.get_required_value("connector_refund_id")
|
||||
.change_context(errors::ConnectorError::MissingConnectorTransactionID)
|
||||
}
|
||||
}
|
||||
|
||||
impl PaymentsRequestData for types::PaymentsAuthorizeRouterData {
|
||||
fn get_attempt_id(&self) -> Result<String, Error> {
|
||||
self.attempt_id
|
||||
|
||||
@ -9,6 +9,7 @@ use storage_models::enums;
|
||||
use time::{format_description, OffsetDateTime};
|
||||
use transformers as worldline;
|
||||
|
||||
use super::utils::RefundsRequestData;
|
||||
use crate::{
|
||||
configs::settings::Connectors,
|
||||
consts,
|
||||
@ -20,7 +21,7 @@ use crate::{
|
||||
api::{self, ConnectorCommon, ConnectorCommonExt},
|
||||
ErrorResponse,
|
||||
},
|
||||
utils::{self, BytesExt, OptionExt},
|
||||
utils::{self, BytesExt},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@ -600,12 +601,7 @@ impl ConnectorIntegration<api::RSync, types::RefundsData, types::RefundsResponse
|
||||
req: &types::RefundSyncRouterData,
|
||||
connectors: &Connectors,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
let refund_id = req
|
||||
.request
|
||||
.connector_refund_id
|
||||
.clone()
|
||||
.get_required_value("connector_refund_id")
|
||||
.change_context(errors::ConnectorError::FailedToObtainIntegrationUrl)?;
|
||||
let refund_id = req.request.get_connector_refund_id()?;
|
||||
let base_url = self.base_url(connectors);
|
||||
let auth: worldline::AuthType = worldline::AuthType::try_from(&req.connector_auth_type)?;
|
||||
let merchant_account_id = auth.merchant_account_id;
|
||||
|
||||
@ -1,12 +1,13 @@
|
||||
use futures::future::OptionFuture;
|
||||
use masking::Secret;
|
||||
use router::types::{self, api, storage::enums};
|
||||
use router::types::{
|
||||
self, api,
|
||||
storage::{self, enums},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
connector_auth,
|
||||
utils::{self, ConnectorActions, PaymentAuthorizeType},
|
||||
};
|
||||
|
||||
struct Cybersource;
|
||||
impl ConnectorActions for Cybersource {}
|
||||
impl utils::Connector for Cybersource {
|
||||
@ -18,7 +19,6 @@ impl utils::Connector for Cybersource {
|
||||
get_token: types::api::GetToken::Connector,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_auth_token(&self) -> types::ConnectorAuthType {
|
||||
types::ConnectorAuthType::from(
|
||||
connector_auth::ConnectorAuthentication::new()
|
||||
@ -26,7 +26,6 @@ impl utils::Connector for Cybersource {
|
||||
.expect("Missing connector authentication configuration"),
|
||||
)
|
||||
}
|
||||
|
||||
fn get_name(&self) -> String {
|
||||
"cybersource".to_string()
|
||||
}
|
||||
@ -56,9 +55,9 @@ fn get_default_payment_info() -> Option<utils::PaymentInfo> {
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
||||
fn get_default_payment_authorize_data() -> Option<types::PaymentsAuthorizeData> {
|
||||
Some(types::PaymentsAuthorizeData {
|
||||
currency: storage::enums::Currency::USD,
|
||||
email: Some(Secret::new("abc@gmail.com".to_string())),
|
||||
..PaymentAuthorizeType::default().0
|
||||
})
|
||||
@ -74,130 +73,221 @@ async fn should_only_authorize_payment() {
|
||||
.unwrap();
|
||||
assert_eq!(response.status, enums::AttemptStatus::Authorized);
|
||||
}
|
||||
|
||||
#[actix_web::test]
|
||||
async fn should_authorize_and_capture_payment() {
|
||||
let connector = Cybersource {};
|
||||
let response = connector
|
||||
async fn should_make_payment() {
|
||||
let response = Cybersource {}
|
||||
.make_payment(
|
||||
get_default_payment_authorize_data(),
|
||||
get_default_payment_info(),
|
||||
)
|
||||
.await;
|
||||
let sync_response = connector
|
||||
.sync_payment(
|
||||
Some(types::PaymentsSyncData {
|
||||
connector_transaction_id: router::types::ResponseId::ConnectorTransactionId(
|
||||
utils::get_connector_transaction_id(response.unwrap()).unwrap(),
|
||||
),
|
||||
encoded_data: None,
|
||||
capture_method: None,
|
||||
}),
|
||||
get_default_payment_info(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
//cybersource takes sometime to settle the transaction,so it will be in pending for long time
|
||||
assert_eq!(sync_response.status, enums::AttemptStatus::Pending);
|
||||
assert_eq!(response.status, enums::AttemptStatus::Pending);
|
||||
}
|
||||
|
||||
#[actix_web::test]
|
||||
async fn should_sync_capture_payment() {
|
||||
let sync_response = Cybersource {}
|
||||
.sync_payment(
|
||||
Some(types::PaymentsSyncData {
|
||||
connector_transaction_id: router::types::ResponseId::ConnectorTransactionId(
|
||||
"6736046645576085004953".to_string(),
|
||||
),
|
||||
encoded_data: None,
|
||||
capture_method: None,
|
||||
}),
|
||||
get_default_payment_info(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(sync_response.status, enums::AttemptStatus::Charged);
|
||||
}
|
||||
|
||||
#[actix_web::test]
|
||||
async fn should_capture_already_authorized_payment() {
|
||||
let connector = Cybersource {};
|
||||
let authorize_response = connector
|
||||
.authorize_payment(
|
||||
let response = connector
|
||||
.authorize_and_capture_payment(
|
||||
get_default_payment_authorize_data(),
|
||||
None,
|
||||
get_default_payment_info(),
|
||||
)
|
||||
.await;
|
||||
assert_eq!(response.unwrap().status, enums::AttemptStatus::Pending);
|
||||
}
|
||||
#[actix_web::test]
|
||||
async fn should_partially_capture_already_authorized_payment() {
|
||||
let connector = Cybersource {};
|
||||
let response = connector
|
||||
.authorize_and_capture_payment(
|
||||
get_default_payment_authorize_data(),
|
||||
Some(types::PaymentsCaptureData {
|
||||
amount_to_capture: Some(50),
|
||||
..utils::PaymentCaptureType::default().0
|
||||
}),
|
||||
get_default_payment_info(),
|
||||
)
|
||||
.await;
|
||||
assert_eq!(response.unwrap().status, enums::AttemptStatus::Pending);
|
||||
}
|
||||
|
||||
#[actix_web::test]
|
||||
async fn should_sync_payment() {
|
||||
let connector = Cybersource {};
|
||||
let response = connector
|
||||
.psync_retry_till_status_matches(
|
||||
enums::AttemptStatus::Charged,
|
||||
Some(types::PaymentsSyncData {
|
||||
connector_transaction_id: router::types::ResponseId::ConnectorTransactionId(
|
||||
"6699597903496176903954".to_string(),
|
||||
),
|
||||
encoded_data: None,
|
||||
capture_method: None,
|
||||
}),
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(authorize_response.status, enums::AttemptStatus::Authorized);
|
||||
let txn_id = utils::get_connector_transaction_id(authorize_response);
|
||||
let response: OptionFuture<_> = txn_id
|
||||
.map(|transaction_id| async move {
|
||||
connector
|
||||
.capture_payment(transaction_id, None, get_default_payment_info())
|
||||
.await
|
||||
.unwrap()
|
||||
.status
|
||||
})
|
||||
.into();
|
||||
//cybersource takes sometime to settle the transaction,so it will be in pending for long time
|
||||
assert_eq!(response.await, Some(enums::AttemptStatus::Pending));
|
||||
assert_eq!(response.status, enums::AttemptStatus::Charged,);
|
||||
}
|
||||
|
||||
#[actix_web::test]
|
||||
async fn should_void_already_authorized_payment() {
|
||||
let connector = Cybersource {};
|
||||
let authorize_response = connector
|
||||
.authorize_payment(
|
||||
let response = connector
|
||||
.authorize_and_void_payment(
|
||||
get_default_payment_authorize_data(),
|
||||
Some(types::PaymentsCancelData {
|
||||
connector_transaction_id: "".to_string(),
|
||||
cancellation_reason: Some("requested_by_customer".to_string()),
|
||||
}),
|
||||
get_default_payment_info(),
|
||||
)
|
||||
.await;
|
||||
assert_eq!(response.unwrap().status, enums::AttemptStatus::Voided);
|
||||
}
|
||||
#[actix_web::test]
|
||||
async fn should_fail_payment_for_incorrect_card_number() {
|
||||
let response = Cybersource {}
|
||||
.make_payment(
|
||||
Some(types::PaymentsAuthorizeData {
|
||||
payment_method_data: types::api::PaymentMethod::Card(api::Card {
|
||||
card_number: Secret::new("4024007134364111".to_string()),
|
||||
..utils::CCardType::default().0
|
||||
}),
|
||||
..get_default_payment_authorize_data().unwrap()
|
||||
}),
|
||||
get_default_payment_info(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(authorize_response.status, enums::AttemptStatus::Authorized);
|
||||
let txn_id = utils::get_connector_transaction_id(authorize_response);
|
||||
let response: OptionFuture<_> = txn_id
|
||||
.map(|transaction_id| async move {
|
||||
connector
|
||||
.void_payment(transaction_id, None, get_default_payment_info())
|
||||
.await
|
||||
.unwrap()
|
||||
.status
|
||||
})
|
||||
.into();
|
||||
assert_eq!(response.await, Some(enums::AttemptStatus::Voided));
|
||||
let x = response.response.unwrap_err();
|
||||
assert_eq!(x.message, "Decline - Invalid account number",);
|
||||
}
|
||||
|
||||
#[actix_web::test]
|
||||
async fn should_refund_succeeded_payment() {
|
||||
async fn should_fail_payment_for_no_card_number() {
|
||||
let response = Cybersource {}
|
||||
.make_payment(
|
||||
Some(types::PaymentsAuthorizeData {
|
||||
payment_method_data: types::api::PaymentMethod::Card(api::Card {
|
||||
card_number: Secret::new("".to_string()),
|
||||
..utils::CCardType::default().0
|
||||
}),
|
||||
..get_default_payment_authorize_data().unwrap()
|
||||
}),
|
||||
get_default_payment_info(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let x = response.response.unwrap_err();
|
||||
assert_eq!(x.message, "The order has been rejected by Decision Manager",);
|
||||
}
|
||||
#[actix_web::test]
|
||||
async fn should_fail_payment_for_invalid_exp_month() {
|
||||
let response = Cybersource {}
|
||||
.make_payment(
|
||||
Some(types::PaymentsAuthorizeData {
|
||||
payment_method_data: types::api::PaymentMethod::Card(api::Card {
|
||||
card_exp_month: Secret::new("13".to_string()),
|
||||
..utils::CCardType::default().0
|
||||
}),
|
||||
..get_default_payment_authorize_data().unwrap()
|
||||
}),
|
||||
get_default_payment_info(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let x = response.response.unwrap_err();
|
||||
assert_eq!(
|
||||
x.message,
|
||||
r#"Declined - One or more fields in the request contains invalid data [{"field":"paymentInformation.card.expirationMonth","reason":"INVALID_DATA"}]"#,
|
||||
);
|
||||
}
|
||||
#[actix_web::test]
|
||||
async fn should_fail_payment_for_invalid_exp_year() {
|
||||
let response = Cybersource {}
|
||||
.make_payment(
|
||||
Some(types::PaymentsAuthorizeData {
|
||||
payment_method_data: types::api::PaymentMethod::Card(api::Card {
|
||||
card_exp_year: Secret::new("2022".to_string()),
|
||||
..utils::CCardType::default().0
|
||||
}),
|
||||
..get_default_payment_authorize_data().unwrap()
|
||||
}),
|
||||
get_default_payment_info(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let x = response.response.unwrap_err();
|
||||
assert_eq!(x.message, "Decline - Expired card. You might also receive this if the expiration date you provided does not match the date the issuing bank has on file.",);
|
||||
}
|
||||
#[actix_web::test]
|
||||
async fn should_fail_payment_for_invalid_card_cvc() {
|
||||
let response = Cybersource {}
|
||||
.make_payment(
|
||||
Some(types::PaymentsAuthorizeData {
|
||||
payment_method_data: types::api::PaymentMethod::Card(api::Card {
|
||||
card_cvc: Secret::new("2131233213".to_string()),
|
||||
..utils::CCardType::default().0
|
||||
}),
|
||||
..get_default_payment_authorize_data().unwrap()
|
||||
}),
|
||||
get_default_payment_info(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let x = response.response.unwrap_err();
|
||||
assert_eq!(
|
||||
x.message,
|
||||
r#"Declined - One or more fields in the request contains invalid data [{"field":"paymentInformation.card.securityCode","reason":"INVALID_DATA"}]"#,
|
||||
);
|
||||
}
|
||||
// Voids a payment using automatic capture flow (Non 3DS).
|
||||
#[actix_web::test]
|
||||
async fn should_fail_void_payment_for_reversed_payment() {
|
||||
let connector = Cybersource {};
|
||||
//make a successful payment
|
||||
let response = connector
|
||||
// Authorize
|
||||
let authorize_response = connector
|
||||
.make_payment(
|
||||
get_default_payment_authorize_data(),
|
||||
get_default_payment_info(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
//try refund for previous payment
|
||||
let transaction_id = utils::get_connector_transaction_id(response).unwrap();
|
||||
let response = connector
|
||||
.refund_payment(transaction_id, None, get_default_payment_info())
|
||||
assert_eq!(authorize_response.status, enums::AttemptStatus::Pending);
|
||||
let txn_id = utils::get_connector_transaction_id(authorize_response.response);
|
||||
assert_ne!(txn_id, None, "Empty connector transaction id");
|
||||
// Void
|
||||
let void_response = connector
|
||||
.void_payment("6736046645576085004953".to_string(), None, None)
|
||||
.await
|
||||
.unwrap();
|
||||
let res = void_response.response.unwrap_err();
|
||||
assert_eq!(
|
||||
response.response.unwrap().refund_status,
|
||||
enums::RefundStatus::Pending, //cybersource takes sometime to refund the transaction,so it will be in pending state for long time
|
||||
res.message,
|
||||
"Decline - The authorization has already been reversed."
|
||||
);
|
||||
}
|
||||
|
||||
#[actix_web::test]
|
||||
async fn should_sync_refund() {
|
||||
async fn should_fail_capture_for_invalid_payment() {
|
||||
let connector = Cybersource {};
|
||||
let response = connector
|
||||
.sync_refund(
|
||||
"6738063831816571404953".to_string(),
|
||||
.capture_payment("12345".to_string(), None, get_default_payment_info())
|
||||
.await
|
||||
.unwrap();
|
||||
let err = response.response.unwrap_err();
|
||||
assert_eq!(
|
||||
err.message,
|
||||
r#"Declined - One or more fields in the request contains invalid data [{"field":"id","reason":"INVALID_DATA"}]"#
|
||||
);
|
||||
assert_eq!(err.code, "No error code".to_string());
|
||||
}
|
||||
#[actix_web::test]
|
||||
async fn should_refund_succeeded_payment() {
|
||||
let connector = Cybersource {};
|
||||
let response = connector
|
||||
.make_payment_and_refund(
|
||||
get_default_payment_authorize_data(),
|
||||
None,
|
||||
get_default_payment_info(),
|
||||
)
|
||||
@ -205,49 +295,98 @@ async fn should_sync_refund() {
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
response.response.unwrap().refund_status,
|
||||
enums::RefundStatus::Pending, //cybersource takes sometime to refund the transaction,so it will be in pending state for long time
|
||||
enums::RefundStatus::Pending,
|
||||
);
|
||||
}
|
||||
|
||||
#[actix_web::test]
|
||||
async fn should_fail_payment_for_incorrect_card_number() {
|
||||
let response = Cybersource {}
|
||||
.make_payment(
|
||||
Some(types::PaymentsAuthorizeData {
|
||||
payment_method_data: types::api::PaymentMethod::Card(api::Card {
|
||||
card_number: Secret::new("424242442424242".to_string()),
|
||||
..utils::CCardType::default().0
|
||||
}),
|
||||
..get_default_payment_authorize_data().unwrap()
|
||||
async fn should_refund_manually_captured_payment() {
|
||||
let connector = Cybersource {};
|
||||
let response = connector
|
||||
.auth_capture_and_refund(
|
||||
get_default_payment_authorize_data(),
|
||||
None,
|
||||
get_default_payment_info(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
response.response.unwrap().refund_status,
|
||||
enums::RefundStatus::Pending,
|
||||
);
|
||||
}
|
||||
#[actix_web::test]
|
||||
async fn should_partially_refund_succeeded_payment() {
|
||||
let connector = Cybersource {};
|
||||
let refund_response = connector
|
||||
.make_payment_and_refund(
|
||||
get_default_payment_authorize_data(),
|
||||
Some(types::RefundsData {
|
||||
refund_amount: 50,
|
||||
..utils::PaymentRefundType::default().0
|
||||
}),
|
||||
get_default_payment_info(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(response.status, enums::AttemptStatus::Failure);
|
||||
let x = response.response.unwrap_err();
|
||||
assert_eq!(x.message, "Decline - Invalid account number".to_string(),);
|
||||
assert_eq!(
|
||||
refund_response.response.unwrap().refund_status,
|
||||
enums::RefundStatus::Pending,
|
||||
);
|
||||
}
|
||||
|
||||
#[actix_web::test]
|
||||
async fn should_fail_payment_for_incorrect_exp_month() {
|
||||
let response = Cybersource {}
|
||||
.make_payment(
|
||||
Some(types::PaymentsAuthorizeData {
|
||||
payment_method_data: types::api::PaymentMethod::Card(api::Card {
|
||||
card_number: Secret::new("4242424242424242".to_string()),
|
||||
card_exp_month: Secret::new("101".to_string()),
|
||||
..utils::CCardType::default().0
|
||||
}),
|
||||
..get_default_payment_authorize_data().unwrap()
|
||||
async fn should_partially_refund_manually_captured_payment() {
|
||||
let connector = Cybersource {};
|
||||
let response = connector
|
||||
.auth_capture_and_refund(
|
||||
get_default_payment_authorize_data(),
|
||||
Some(types::RefundsData {
|
||||
refund_amount: 50,
|
||||
..utils::PaymentRefundType::default().0
|
||||
}),
|
||||
get_default_payment_info(),
|
||||
)
|
||||
.await;
|
||||
let x = response.unwrap().response.unwrap_err();
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
x.message,
|
||||
r#"[{"field":"paymentInformation.card.expirationMonth","reason":"INVALID_DATA"}]"#
|
||||
.to_string(),
|
||||
response.response.unwrap().refund_status,
|
||||
enums::RefundStatus::Pending,
|
||||
);
|
||||
}
|
||||
|
||||
#[actix_web::test]
|
||||
async fn should_fail_refund_for_invalid_amount() {
|
||||
let connector = Cybersource {};
|
||||
let response = connector
|
||||
.make_payment_and_refund(
|
||||
get_default_payment_authorize_data(),
|
||||
Some(types::RefundsData {
|
||||
refund_amount: 15000,
|
||||
..utils::PaymentRefundType::default().0
|
||||
}),
|
||||
get_default_payment_info(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
response.response.unwrap().refund_status,
|
||||
enums::RefundStatus::Pending,
|
||||
);
|
||||
}
|
||||
#[actix_web::test]
|
||||
async fn should_sync_refund() {
|
||||
let connector = Cybersource {};
|
||||
let response = connector
|
||||
.rsync_retry_till_status_matches(
|
||||
enums::RefundStatus::Success,
|
||||
"6699597076726585603955".to_string(),
|
||||
None,
|
||||
get_default_payment_info(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
response.response.unwrap().refund_status,
|
||||
enums::RefundStatus::Success,
|
||||
);
|
||||
}
|
||||
|
||||
@ -101,7 +101,7 @@ async fn should_capture_already_authorized_payment() {
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(authorize_response.status, enums::AttemptStatus::Authorized);
|
||||
let txn_id = utils::get_connector_transaction_id(authorize_response);
|
||||
let txn_id = utils::get_connector_transaction_id(authorize_response.response);
|
||||
let response: OptionFuture<_> = txn_id
|
||||
.map(|transaction_id| async move {
|
||||
connector
|
||||
@ -144,7 +144,7 @@ async fn should_refund_succeeded_payment() {
|
||||
let response = connector.make_payment(None, None).await.unwrap();
|
||||
|
||||
//try refund for previous payment
|
||||
if let Some(transaction_id) = utils::get_connector_transaction_id(response) {
|
||||
if let Some(transaction_id) = utils::get_connector_transaction_id(response.response) {
|
||||
let response = connector
|
||||
.refund_payment(transaction_id, None, None)
|
||||
.await
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
use std::{thread::sleep, time::Duration};
|
||||
|
||||
use futures::future::OptionFuture;
|
||||
use masking::Secret;
|
||||
use router::types::{
|
||||
self,
|
||||
@ -55,8 +54,7 @@ fn get_default_payment_info() -> Option<PaymentInfo> {
|
||||
}),
|
||||
..Default::default()
|
||||
}),
|
||||
auth_type: None,
|
||||
access_token: None,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
||||
@ -81,22 +79,17 @@ async fn should_authorize_and_capture_payment() {
|
||||
#[actix_web::test]
|
||||
async fn should_capture_already_authorized_payment() {
|
||||
let connector = Globalpay {};
|
||||
let authorize_response = connector
|
||||
.authorize_payment(None, get_default_payment_info())
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(authorize_response.status, enums::AttemptStatus::Authorized);
|
||||
let txn_id = utils::get_connector_transaction_id(authorize_response);
|
||||
let response: OptionFuture<_> = txn_id
|
||||
.map(|transaction_id| async move {
|
||||
connector
|
||||
.capture_payment(transaction_id, None, get_default_payment_info())
|
||||
.await
|
||||
.unwrap()
|
||||
.status
|
||||
})
|
||||
.into();
|
||||
assert_eq!(response.await, Some(enums::AttemptStatus::Charged));
|
||||
let response = connector
|
||||
.authorize_and_capture_payment(
|
||||
None,
|
||||
Some(types::PaymentsCaptureData {
|
||||
amount_to_capture: Some(50),
|
||||
..utils::PaymentCaptureType::default().0
|
||||
}),
|
||||
get_default_payment_info(),
|
||||
)
|
||||
.await;
|
||||
assert_eq!(response.unwrap().status, enums::AttemptStatus::Charged);
|
||||
}
|
||||
|
||||
#[actix_web::test]
|
||||
@ -106,10 +99,11 @@ async fn should_sync_payment() {
|
||||
.authorize_payment(None, get_default_payment_info())
|
||||
.await
|
||||
.unwrap();
|
||||
let txn_id = utils::get_connector_transaction_id(authorize_response);
|
||||
let txn_id = utils::get_connector_transaction_id(authorize_response.response);
|
||||
sleep(Duration::from_secs(5)); // to avoid 404 error as globalpay takes some time to process the new transaction
|
||||
let response = connector
|
||||
.sync_payment(
|
||||
.psync_retry_till_status_matches(
|
||||
enums::AttemptStatus::Authorized,
|
||||
Some(types::PaymentsSyncData {
|
||||
connector_transaction_id: router::types::ResponseId::ConnectorTransactionId(
|
||||
txn_id.unwrap(),
|
||||
@ -146,16 +140,8 @@ async fn should_fail_payment_for_incorrect_cvc() {
|
||||
#[actix_web::test]
|
||||
async fn should_refund_succeeded_payment() {
|
||||
let connector = Globalpay {};
|
||||
//make a successful payment
|
||||
let response = connector
|
||||
.make_payment(None, get_default_payment_info())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
//try refund for previous payment
|
||||
let transaction_id = utils::get_connector_transaction_id(response).unwrap();
|
||||
let response = connector
|
||||
.refund_payment(transaction_id, None, get_default_payment_info())
|
||||
.make_payment_and_refund(None, None, get_default_payment_info())
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
@ -167,39 +153,26 @@ async fn should_refund_succeeded_payment() {
|
||||
#[actix_web::test]
|
||||
async fn should_void_already_authorized_payment() {
|
||||
let connector = Globalpay {};
|
||||
let authorize_response = connector
|
||||
.authorize_payment(None, get_default_payment_info())
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(authorize_response.status, enums::AttemptStatus::Authorized);
|
||||
let txn_id = utils::get_connector_transaction_id(authorize_response);
|
||||
let response: OptionFuture<_> = txn_id
|
||||
.map(|transaction_id| async move {
|
||||
connector
|
||||
.void_payment(transaction_id, None, None)
|
||||
.await
|
||||
.unwrap()
|
||||
.status
|
||||
})
|
||||
.into();
|
||||
assert_eq!(response.await, Some(enums::AttemptStatus::Voided));
|
||||
let response = connector
|
||||
.authorize_and_void_payment(None, None, get_default_payment_info())
|
||||
.await;
|
||||
assert_eq!(response.unwrap().status, enums::AttemptStatus::Voided);
|
||||
}
|
||||
|
||||
#[actix_web::test]
|
||||
async fn should_sync_refund() {
|
||||
let connector = Globalpay {};
|
||||
let response = connector
|
||||
.make_payment(None, get_default_payment_info())
|
||||
let refund_response = connector
|
||||
.make_payment_and_refund(None, None, get_default_payment_info())
|
||||
.await
|
||||
.unwrap();
|
||||
let transaction_id = utils::get_connector_transaction_id(response).unwrap();
|
||||
connector
|
||||
.refund_payment(transaction_id.clone(), None, get_default_payment_info())
|
||||
.await
|
||||
.unwrap();
|
||||
sleep(Duration::from_secs(5)); // to avoid 404 error as globalpay takes some time to process the new transaction
|
||||
let response = connector
|
||||
.sync_refund(transaction_id, None, get_default_payment_info())
|
||||
.rsync_retry_till_status_matches(
|
||||
enums::RefundStatus::Success,
|
||||
refund_response.response.unwrap().connector_refund_id,
|
||||
None,
|
||||
get_default_payment_info(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
|
||||
@ -41,9 +41,8 @@ fn get_access_token() -> Option<AccessToken> {
|
||||
}
|
||||
fn get_default_payment_info() -> Option<utils::PaymentInfo> {
|
||||
Some(utils::PaymentInfo {
|
||||
address: None,
|
||||
auth_type: None,
|
||||
access_token: get_access_token(),
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
||||
@ -63,7 +62,7 @@ async fn should_authorize_card_payment() {
|
||||
.unwrap();
|
||||
// in Payu need Psync to get status therefore set to pending
|
||||
assert_eq!(authorize_response.status, enums::AttemptStatus::Pending);
|
||||
if let Some(transaction_id) = utils::get_connector_transaction_id(authorize_response) {
|
||||
if let Some(transaction_id) = utils::get_connector_transaction_id(authorize_response.response) {
|
||||
let sync_response = Payu {}
|
||||
.psync_retry_till_status_matches(
|
||||
enums::AttemptStatus::Authorized,
|
||||
@ -95,7 +94,7 @@ async fn should_authorize_gpay_payment() {
|
||||
..PaymentAuthorizeType::default().0
|
||||
}), get_default_payment_info()).await.unwrap();
|
||||
assert_eq!(authorize_response.status, enums::AttemptStatus::Pending);
|
||||
if let Some(transaction_id) = utils::get_connector_transaction_id(authorize_response) {
|
||||
if let Some(transaction_id) = utils::get_connector_transaction_id(authorize_response.response) {
|
||||
let sync_response = Payu {}
|
||||
.sync_payment(
|
||||
Some(types::PaymentsSyncData {
|
||||
@ -128,7 +127,7 @@ async fn should_capture_already_authorized_payment() {
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(authorize_response.status, enums::AttemptStatus::Pending);
|
||||
if let Some(transaction_id) = utils::get_connector_transaction_id(authorize_response) {
|
||||
if let Some(transaction_id) = utils::get_connector_transaction_id(authorize_response.response) {
|
||||
let sync_response = connector
|
||||
.psync_retry_till_status_matches(
|
||||
enums::AttemptStatus::Authorized,
|
||||
@ -184,7 +183,7 @@ async fn should_sync_payment() {
|
||||
.unwrap();
|
||||
assert_eq!(authorize_response.status, enums::AttemptStatus::Pending);
|
||||
|
||||
if let Some(transaction_id) = utils::get_connector_transaction_id(authorize_response) {
|
||||
if let Some(transaction_id) = utils::get_connector_transaction_id(authorize_response.response) {
|
||||
// Sync the Payment Data
|
||||
let response = connector
|
||||
.psync_retry_till_status_matches(
|
||||
@ -223,7 +222,7 @@ async fn should_void_already_authorized_payment() {
|
||||
assert_eq!(authorize_response.status, enums::AttemptStatus::Pending);
|
||||
|
||||
//try CANCEL for previous payment
|
||||
if let Some(transaction_id) = utils::get_connector_transaction_id(authorize_response) {
|
||||
if let Some(transaction_id) = utils::get_connector_transaction_id(authorize_response.response) {
|
||||
let void_response = connector
|
||||
.void_payment(transaction_id.clone(), None, get_default_payment_info())
|
||||
.await
|
||||
@ -264,7 +263,7 @@ async fn should_refund_succeeded_payment() {
|
||||
.unwrap();
|
||||
assert_eq!(authorize_response.status, enums::AttemptStatus::Pending);
|
||||
|
||||
if let Some(transaction_id) = utils::get_connector_transaction_id(authorize_response) {
|
||||
if let Some(transaction_id) = utils::get_connector_transaction_id(authorize_response.response) {
|
||||
let sync_response = connector
|
||||
.psync_retry_till_status_matches(
|
||||
enums::AttemptStatus::Authorized,
|
||||
|
||||
@ -81,7 +81,7 @@ async fn should_capture_already_authorized_payment() {
|
||||
let connector = Rapyd {};
|
||||
let authorize_response = connector.authorize_payment(None, None).await.unwrap();
|
||||
assert_eq!(authorize_response.status, enums::AttemptStatus::Authorized);
|
||||
let txn_id = utils::get_connector_transaction_id(authorize_response);
|
||||
let txn_id = utils::get_connector_transaction_id(authorize_response.response);
|
||||
let response: OptionFuture<_> = txn_id
|
||||
.map(|transaction_id| async move {
|
||||
connector
|
||||
@ -100,7 +100,7 @@ async fn voiding_already_authorized_payment_fails() {
|
||||
let connector = Rapyd {};
|
||||
let authorize_response = connector.authorize_payment(None, None).await.unwrap();
|
||||
assert_eq!(authorize_response.status, enums::AttemptStatus::Authorized);
|
||||
let txn_id = utils::get_connector_transaction_id(authorize_response);
|
||||
let txn_id = utils::get_connector_transaction_id(authorize_response.response);
|
||||
let response: OptionFuture<_> = txn_id
|
||||
.map(|transaction_id| async move {
|
||||
connector
|
||||
@ -120,7 +120,7 @@ async fn should_refund_succeeded_payment() {
|
||||
let response = connector.make_payment(None, None).await.unwrap();
|
||||
|
||||
//try refund for previous payment
|
||||
if let Some(transaction_id) = utils::get_connector_transaction_id(response) {
|
||||
if let Some(transaction_id) = utils::get_connector_transaction_id(response.response) {
|
||||
let response = connector
|
||||
.refund_payment(transaction_id, None, None)
|
||||
.await
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
use futures::future::OptionFuture;
|
||||
use masking::Secret;
|
||||
use router::types::{self, api, storage::enums};
|
||||
|
||||
@ -7,9 +6,10 @@ use crate::{
|
||||
utils::{self, ConnectorActions},
|
||||
};
|
||||
|
||||
struct Shift4;
|
||||
impl ConnectorActions for Shift4 {}
|
||||
impl utils::Connector for Shift4 {
|
||||
#[derive(Clone, Copy)]
|
||||
struct Shift4Test;
|
||||
impl ConnectorActions for Shift4Test {}
|
||||
impl utils::Connector for Shift4Test {
|
||||
fn get_data(&self) -> types::api::ConnectorData {
|
||||
use router::connector::Shift4;
|
||||
types::api::ConnectorData {
|
||||
@ -32,43 +32,146 @@ impl utils::Connector for Shift4 {
|
||||
}
|
||||
}
|
||||
|
||||
static CONNECTOR: Shift4Test = Shift4Test {};
|
||||
|
||||
// Cards Positive Tests
|
||||
// Creates a payment using the manual capture flow (Non 3DS).
|
||||
#[actix_web::test]
|
||||
async fn should_only_authorize_payment() {
|
||||
let response = Shift4 {}.authorize_payment(None, None).await.unwrap();
|
||||
let response = CONNECTOR.authorize_payment(None, None).await.unwrap();
|
||||
assert_eq!(response.status, enums::AttemptStatus::Authorized);
|
||||
}
|
||||
|
||||
// Creates a payment using the automatic capture flow (Non 3DS).
|
||||
#[actix_web::test]
|
||||
async fn should_authorize_and_capture_payment() {
|
||||
let response = Shift4 {}.make_payment(None, None).await.unwrap();
|
||||
assert_eq!(response.status, enums::AttemptStatus::Charged);
|
||||
async fn should_make_payment() {
|
||||
let authorize_response = CONNECTOR.make_payment(None, None).await.unwrap();
|
||||
assert_eq!(authorize_response.status, enums::AttemptStatus::Charged);
|
||||
}
|
||||
|
||||
// Captures a payment using the manual capture flow (Non 3DS).
|
||||
#[actix_web::test]
|
||||
async fn should_capture_already_authorized_payment() {
|
||||
let connector = Shift4 {};
|
||||
async fn should_capture_authorized_payment() {
|
||||
let connector = CONNECTOR;
|
||||
let response = connector
|
||||
.authorize_and_capture_payment(None, None, None)
|
||||
.await;
|
||||
assert_eq!(response.unwrap().status, enums::AttemptStatus::Charged);
|
||||
}
|
||||
|
||||
// Partially captures a payment using the manual capture flow (Non 3DS).
|
||||
#[actix_web::test]
|
||||
async fn should_partially_capture_authorized_payment() {
|
||||
let connector = CONNECTOR;
|
||||
let response = connector
|
||||
.authorize_and_capture_payment(
|
||||
None,
|
||||
Some(types::PaymentsCaptureData {
|
||||
amount_to_capture: Some(50),
|
||||
..utils::PaymentCaptureType::default().0
|
||||
}),
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(response.unwrap().status, enums::AttemptStatus::Charged);
|
||||
}
|
||||
|
||||
// Synchronizes a payment using the manual capture flow (Non 3DS).
|
||||
#[actix_web::test]
|
||||
async fn should_sync_authorized_payment() {
|
||||
let connector = CONNECTOR;
|
||||
let authorize_response = connector.authorize_payment(None, None).await.unwrap();
|
||||
assert_eq!(authorize_response.status, enums::AttemptStatus::Authorized);
|
||||
let txn_id = utils::get_connector_transaction_id(authorize_response);
|
||||
let response: OptionFuture<_> = txn_id
|
||||
.map(|transaction_id| async move {
|
||||
connector
|
||||
.capture_payment(transaction_id, None, None)
|
||||
.await
|
||||
.unwrap()
|
||||
.status
|
||||
})
|
||||
.into();
|
||||
assert_eq!(response.await, Some(enums::AttemptStatus::Charged));
|
||||
let txn_id = utils::get_connector_transaction_id(authorize_response.response);
|
||||
let response = connector
|
||||
.psync_retry_till_status_matches(
|
||||
enums::AttemptStatus::Authorized,
|
||||
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::Authorized,);
|
||||
}
|
||||
|
||||
// Synchronizes a payment using the automatic capture flow (Non 3DS).
|
||||
#[actix_web::test]
|
||||
async fn should_fail_payment_for_incorrect_cvc() {
|
||||
let response = Shift4 {}
|
||||
async fn should_sync_auto_captured_payment() {
|
||||
// Authorize
|
||||
let authorize_response = CONNECTOR.make_payment(None, None).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,);
|
||||
}
|
||||
|
||||
// Voids a payment using the manual capture flow (Non 3DS).
|
||||
#[actix_web::test]
|
||||
async fn should_void_authorized_payment() {
|
||||
let connector = CONNECTOR;
|
||||
let response = connector
|
||||
.authorize_and_void_payment(
|
||||
None,
|
||||
Some(types::PaymentsCancelData {
|
||||
connector_transaction_id: "".to_string(),
|
||||
cancellation_reason: Some("requested_by_customer".to_string()),
|
||||
}),
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(response.unwrap().status, enums::AttemptStatus::Pending); //shift4 doesn't allow voiding a payment
|
||||
}
|
||||
|
||||
// Cards Negative scenerios
|
||||
// Creates a payment with incorrect card number.
|
||||
#[actix_web::test]
|
||||
async fn should_fail_payment_for_incorrect_card_number() {
|
||||
let response = CONNECTOR
|
||||
.make_payment(
|
||||
Some(types::PaymentsAuthorizeData {
|
||||
payment_method_data: types::api::PaymentMethod::Card(api::Card {
|
||||
card_number: Secret::new("4024007134364842".to_string()),
|
||||
card_number: Secret::new("1234567891011".to_string()),
|
||||
..utils::CCardType::default().0
|
||||
}),
|
||||
..utils::PaymentAuthorizeType::default().0
|
||||
}),
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
response.response.unwrap_err().message,
|
||||
"Your request was in test mode, but used a non test card. For a list of valid test cards, visit our doc site.".to_string(),
|
||||
);
|
||||
}
|
||||
|
||||
// Creates a payment with empty card number.
|
||||
#[actix_web::test]
|
||||
async fn should_fail_payment_for_empty_card_number() {
|
||||
let response = CONNECTOR
|
||||
.make_payment(
|
||||
Some(types::PaymentsAuthorizeData {
|
||||
payment_method_data: types::api::PaymentMethod::Card(api::Card {
|
||||
card_number: Secret::new("".to_string()),
|
||||
..utils::CCardType::default().0
|
||||
}),
|
||||
..utils::PaymentAuthorizeType::default().0
|
||||
@ -80,25 +183,251 @@ async fn should_fail_payment_for_incorrect_cvc() {
|
||||
let x = response.response.unwrap_err();
|
||||
assert_eq!(
|
||||
x.message,
|
||||
"The card's security code failed verification.".to_string(),
|
||||
"The card number is not a valid credit card number.",
|
||||
);
|
||||
}
|
||||
|
||||
// Creates a payment with incorrect CVC.
|
||||
#[actix_web::test]
|
||||
async fn should_succeed_payment_for_incorrect_cvc() {
|
||||
let response = CONNECTOR
|
||||
.make_payment(
|
||||
Some(types::PaymentsAuthorizeData {
|
||||
payment_method_data: types::api::PaymentMethod::Card(api::Card {
|
||||
card_cvc: Secret::new("asdasd".to_string()), //shift4 accept invalid CVV as it doesn't accept CVV
|
||||
..utils::CCardType::default().0
|
||||
}),
|
||||
..utils::PaymentAuthorizeType::default().0
|
||||
}),
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(response.status, enums::AttemptStatus::Charged);
|
||||
}
|
||||
|
||||
// Creates a payment with incorrect expiry month.
|
||||
#[actix_web::test]
|
||||
async fn should_fail_payment_for_invalid_exp_month() {
|
||||
let response = CONNECTOR
|
||||
.make_payment(
|
||||
Some(types::PaymentsAuthorizeData {
|
||||
payment_method_data: types::api::PaymentMethod::Card(api::Card {
|
||||
card_exp_month: Secret::new("20".to_string()),
|
||||
..utils::CCardType::default().0
|
||||
}),
|
||||
..utils::PaymentAuthorizeType::default().0
|
||||
}),
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
response.response.unwrap_err().message,
|
||||
"The card's expiration month is invalid.".to_string(),
|
||||
);
|
||||
}
|
||||
|
||||
// Creates a payment with incorrect expiry year.
|
||||
#[actix_web::test]
|
||||
async fn should_fail_payment_for_incorrect_expiry_year() {
|
||||
let response = CONNECTOR
|
||||
.make_payment(
|
||||
Some(types::PaymentsAuthorizeData {
|
||||
payment_method_data: types::api::PaymentMethod::Card(api::Card {
|
||||
card_exp_year: Secret::new("2000".to_string()),
|
||||
..utils::CCardType::default().0
|
||||
}),
|
||||
..utils::PaymentAuthorizeType::default().0
|
||||
}),
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
response.response.unwrap_err().message,
|
||||
"The card has expired.".to_string(),
|
||||
);
|
||||
}
|
||||
|
||||
// Voids a payment using automatic capture flow (Non 3DS).
|
||||
#[actix_web::test]
|
||||
async fn should_fail_void_payment_for_auto_capture() {
|
||||
// Authorize
|
||||
let authorize_response = CONNECTOR.make_payment(None, None).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");
|
||||
|
||||
// Void
|
||||
let void_response = CONNECTOR
|
||||
.void_payment(txn_id.unwrap(), None, None)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(void_response.status, enums::AttemptStatus::Pending); //shift4 doesn't allow voiding a payment
|
||||
}
|
||||
|
||||
// Captures a payment using invalid connector payment id.
|
||||
#[actix_web::test]
|
||||
async fn should_fail_capture_for_invalid_payment() {
|
||||
// Capture
|
||||
let capture_response = CONNECTOR
|
||||
.capture_payment("123456789".to_string(), None, None)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
capture_response.response.unwrap_err().message,
|
||||
String::from("Charge '123456789' does not exist")
|
||||
);
|
||||
}
|
||||
|
||||
// Refunds a payment using the automatic capture flow (Non 3DS).
|
||||
#[actix_web::test]
|
||||
async fn should_refund_auto_captured_payment() {
|
||||
let connector = CONNECTOR;
|
||||
let response = connector
|
||||
.make_payment_and_refund(None, None, None)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
response.response.unwrap().refund_status,
|
||||
enums::RefundStatus::Success,
|
||||
);
|
||||
}
|
||||
|
||||
// Refunds a payment using the manual capture flow (Non 3DS).
|
||||
#[actix_web::test]
|
||||
async fn should_refund_manually_captured_payment() {
|
||||
let connector = CONNECTOR;
|
||||
let response = connector
|
||||
.auth_capture_and_refund(None, None, None)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
response.response.unwrap().refund_status,
|
||||
enums::RefundStatus::Success,
|
||||
);
|
||||
}
|
||||
|
||||
#[actix_web::test]
|
||||
async fn should_refund_succeeded_payment() {
|
||||
let connector = Shift4 {};
|
||||
//make a successful payment
|
||||
let response = connector.make_payment(None, None).await.unwrap();
|
||||
|
||||
//try refund for previous payment
|
||||
if let Some(transaction_id) = utils::get_connector_transaction_id(response) {
|
||||
let response = connector
|
||||
.refund_payment(transaction_id, None, None)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
response.response.unwrap().refund_status,
|
||||
enums::RefundStatus::Success,
|
||||
);
|
||||
}
|
||||
async fn should_partially_refund_succeeded_payment() {
|
||||
let connector = CONNECTOR;
|
||||
let refund_response = connector
|
||||
.make_payment_and_refund(
|
||||
None,
|
||||
Some(types::RefundsData {
|
||||
refund_amount: 50,
|
||||
..utils::PaymentRefundType::default().0
|
||||
}),
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
refund_response.response.unwrap().refund_status,
|
||||
enums::RefundStatus::Success,
|
||||
);
|
||||
}
|
||||
|
||||
// Partially refunds a payment using the manual capture flow (Non 3DS).
|
||||
#[actix_web::test]
|
||||
async fn should_partially_refund_manually_captured_payment() {
|
||||
let connector = CONNECTOR;
|
||||
let response = connector
|
||||
.auth_capture_and_refund(
|
||||
None,
|
||||
Some(types::RefundsData {
|
||||
refund_amount: 50,
|
||||
..utils::PaymentRefundType::default().0
|
||||
}),
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
response.response.unwrap().refund_status,
|
||||
enums::RefundStatus::Success,
|
||||
);
|
||||
}
|
||||
|
||||
#[actix_web::test]
|
||||
async fn should_refund_succeeded_payment_multiple_times() {
|
||||
let connector = CONNECTOR;
|
||||
connector
|
||||
.make_payment_and_multiple_refund(
|
||||
None,
|
||||
Some(types::RefundsData {
|
||||
refund_amount: 50,
|
||||
..utils::PaymentRefundType::default().0
|
||||
}),
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
// Refunds a payment with refund amount higher than payment amount.
|
||||
#[actix_web::test]
|
||||
async fn should_fail_for_refund_amount_higher_than_payment_amount() {
|
||||
let connector = CONNECTOR;
|
||||
let response = connector
|
||||
.make_payment_and_refund(
|
||||
None,
|
||||
Some(types::RefundsData {
|
||||
refund_amount: 150,
|
||||
..utils::PaymentRefundType::default().0
|
||||
}),
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
response.response.unwrap_err().message,
|
||||
"Invalid Refund data",
|
||||
);
|
||||
}
|
||||
|
||||
// Synchronizes a refund using the automatic capture flow (Non 3DS).
|
||||
#[actix_web::test]
|
||||
async fn should_sync_refund() {
|
||||
let connector = CONNECTOR;
|
||||
let refund_response = connector
|
||||
.make_payment_and_refund(None, None, None)
|
||||
.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,
|
||||
);
|
||||
}
|
||||
|
||||
// Synchronizes a refund using the manual capture flow (Non 3DS).
|
||||
#[actix_web::test]
|
||||
async fn should_sync_manually_captured_refund() {
|
||||
let connector = CONNECTOR;
|
||||
let refund_response = connector
|
||||
.auth_capture_and_refund(None, None, None)
|
||||
.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,
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use masking::Secret;
|
||||
use router::types::{self, api, storage::enums};
|
||||
|
||||
@ -53,7 +51,7 @@ async fn should_only_authorize_payment() {
|
||||
}
|
||||
|
||||
#[actix_web::test]
|
||||
async fn should_authorize_and_capture_payment() {
|
||||
async fn should_make_payment() {
|
||||
let response = Stripe {}
|
||||
.make_payment(get_payment_authorize_data(), None)
|
||||
.await
|
||||
@ -87,13 +85,13 @@ async fn should_partially_capture_already_authorized_payment() {
|
||||
}
|
||||
|
||||
#[actix_web::test]
|
||||
async fn should_sync_payment() {
|
||||
async fn should_sync_authorized_payment() {
|
||||
let connector = Stripe {};
|
||||
let authorize_response = connector
|
||||
.authorize_payment(get_payment_authorize_data(), None)
|
||||
.await
|
||||
.unwrap();
|
||||
let txn_id = utils::get_connector_transaction_id(authorize_response);
|
||||
let txn_id = utils::get_connector_transaction_id(authorize_response.response);
|
||||
let response = connector
|
||||
.psync_retry_till_status_matches(
|
||||
enums::AttemptStatus::Authorized,
|
||||
@ -111,6 +109,31 @@ async fn should_sync_payment() {
|
||||
assert_eq!(response.status, enums::AttemptStatus::Authorized,);
|
||||
}
|
||||
|
||||
#[actix_web::test]
|
||||
async fn should_sync_payment() {
|
||||
let connector = Stripe {};
|
||||
let authorize_response = connector
|
||||
.make_payment(get_payment_authorize_data(), None)
|
||||
.await
|
||||
.unwrap();
|
||||
let txn_id = utils::get_connector_transaction_id(authorize_response.response);
|
||||
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,);
|
||||
}
|
||||
|
||||
#[actix_web::test]
|
||||
async fn should_void_already_authorized_payment() {
|
||||
let connector = Stripe {};
|
||||
@ -118,7 +141,7 @@ async fn should_void_already_authorized_payment() {
|
||||
.authorize_and_void_payment(
|
||||
get_payment_authorize_data(),
|
||||
Some(types::PaymentsCancelData {
|
||||
connector_transaction_id: "".to_string(),
|
||||
connector_transaction_id: "".to_string(), // this connector_transaction_id will be ignored and the transaction_id from payment authorize data will be used for void
|
||||
cancellation_reason: Some("requested_by_customer".to_string()),
|
||||
}),
|
||||
None,
|
||||
@ -228,15 +251,33 @@ async fn should_fail_payment_for_invalid_card_cvc() {
|
||||
assert_eq!(x.message, "Your card's security code is invalid.",);
|
||||
}
|
||||
|
||||
// Voids a payment using automatic capture flow (Non 3DS).
|
||||
#[actix_web::test]
|
||||
async fn should_fail_void_payment_for_auto_capture() {
|
||||
let connector = Stripe {};
|
||||
// Authorize
|
||||
let authorize_response = connector
|
||||
.make_payment(get_payment_authorize_data(), None)
|
||||
.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");
|
||||
|
||||
// Void
|
||||
let void_response = connector
|
||||
.void_payment(txn_id.unwrap(), None, None)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
void_response.response.unwrap_err().message,
|
||||
"You cannot cancel this PaymentIntent because it has a status of succeeded. Only a PaymentIntent with one of the following statuses may be canceled: requires_payment_method, requires_capture, requires_confirmation, requires_action, processing."
|
||||
);
|
||||
}
|
||||
|
||||
#[actix_web::test]
|
||||
async fn should_fail_capture_for_invalid_payment() {
|
||||
let connector = Stripe {};
|
||||
let authorize_response = connector
|
||||
.authorize_payment(get_payment_authorize_data(), None)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(authorize_response.status, enums::AttemptStatus::Authorized);
|
||||
tokio::time::sleep(Duration::from_secs(5)).await; // to avoid 404 error as stripe takes some time to process the new transaction
|
||||
let response = connector
|
||||
.capture_payment("12345".to_string(), None, None)
|
||||
.await
|
||||
@ -259,6 +300,19 @@ async fn should_refund_succeeded_payment() {
|
||||
);
|
||||
}
|
||||
|
||||
#[actix_web::test]
|
||||
async fn should_refund_manually_captured_payment() {
|
||||
let connector = Stripe {};
|
||||
let response = connector
|
||||
.auth_capture_and_refund(get_payment_authorize_data(), None, None)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
response.response.unwrap().refund_status,
|
||||
enums::RefundStatus::Success,
|
||||
);
|
||||
}
|
||||
|
||||
#[actix_web::test]
|
||||
async fn should_partially_refund_succeeded_payment() {
|
||||
let connector = Stripe {};
|
||||
@ -279,6 +333,26 @@ async fn should_partially_refund_succeeded_payment() {
|
||||
);
|
||||
}
|
||||
|
||||
#[actix_web::test]
|
||||
async fn should_partially_refund_manually_captured_payment() {
|
||||
let connector = Stripe {};
|
||||
let response = connector
|
||||
.auth_capture_and_refund(
|
||||
get_payment_authorize_data(),
|
||||
Some(types::RefundsData {
|
||||
refund_amount: 50,
|
||||
..utils::PaymentRefundType::default().0
|
||||
}),
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
response.response.unwrap().refund_status,
|
||||
enums::RefundStatus::Success,
|
||||
);
|
||||
}
|
||||
|
||||
#[actix_web::test]
|
||||
async fn should_refund_succeeded_payment_multiple_times() {
|
||||
let connector = Stripe {};
|
||||
@ -335,3 +409,25 @@ async fn should_sync_refund() {
|
||||
enums::RefundStatus::Success,
|
||||
);
|
||||
}
|
||||
|
||||
#[actix_web::test]
|
||||
async fn should_sync_manually_captured_refund() {
|
||||
let connector = Stripe {};
|
||||
let refund_response = connector
|
||||
.auth_capture_and_refund(get_payment_authorize_data(), None, None)
|
||||
.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,
|
||||
);
|
||||
}
|
||||
|
||||
@ -29,6 +29,7 @@ pub struct PaymentInfo {
|
||||
pub address: Option<PaymentAddress>,
|
||||
pub auth_type: Option<enums::AuthenticationType>,
|
||||
pub access_token: Option<AccessToken>,
|
||||
pub router_return_url: Option<String>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
@ -41,7 +42,7 @@ pub trait ConnectorActions: Connector {
|
||||
let integration = self.get_data().connector.get_connector_integration();
|
||||
let request = self.generate_data(
|
||||
types::PaymentsAuthorizeData {
|
||||
confirm: false,
|
||||
confirm: true,
|
||||
capture_method: Some(storage_models::enums::CaptureMethod::Manual),
|
||||
..(payment_data.unwrap_or(PaymentAuthorizeType::default().0))
|
||||
},
|
||||
@ -57,7 +58,11 @@ pub trait ConnectorActions: Connector {
|
||||
) -> Result<types::PaymentsAuthorizeRouterData, Report<ConnectorError>> {
|
||||
let integration = self.get_data().connector.get_connector_integration();
|
||||
let request = self.generate_data(
|
||||
payment_data.unwrap_or_else(|| PaymentAuthorizeType::default().0),
|
||||
types::PaymentsAuthorizeData {
|
||||
confirm: true,
|
||||
capture_method: Some(storage_models::enums::CaptureMethod::Automatic),
|
||||
..(payment_data.unwrap_or(PaymentAuthorizeType::default().0))
|
||||
},
|
||||
payment_info,
|
||||
);
|
||||
call_connector(request, integration).await
|
||||
@ -125,7 +130,7 @@ pub trait ConnectorActions: Connector {
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(authorize_response.status, enums::AttemptStatus::Authorized);
|
||||
let txn_id = get_connector_transaction_id(authorize_response);
|
||||
let txn_id = get_connector_transaction_id(authorize_response.response);
|
||||
let response = self
|
||||
.capture_payment(txn_id.unwrap(), capture_data, payment_info)
|
||||
.await
|
||||
@ -161,7 +166,7 @@ pub trait ConnectorActions: Connector {
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(authorize_response.status, enums::AttemptStatus::Authorized);
|
||||
let txn_id = get_connector_transaction_id(authorize_response);
|
||||
let txn_id = get_connector_transaction_id(authorize_response.response);
|
||||
tokio::time::sleep(Duration::from_secs(self.get_request_interval())).await; // to avoid 404 error
|
||||
let response = self
|
||||
.void_payment(txn_id.unwrap(), void_data, payment_info)
|
||||
@ -222,7 +227,28 @@ pub trait ConnectorActions: Connector {
|
||||
.unwrap();
|
||||
|
||||
//try refund for previous payment
|
||||
let transaction_id = get_connector_transaction_id(response).unwrap();
|
||||
let transaction_id = get_connector_transaction_id(response.response).unwrap();
|
||||
tokio::time::sleep(Duration::from_secs(self.get_request_interval())).await; // to avoid 404 error
|
||||
Ok(self
|
||||
.refund_payment(transaction_id, refund_data, payment_info)
|
||||
.await
|
||||
.unwrap())
|
||||
}
|
||||
|
||||
async fn auth_capture_and_refund(
|
||||
&self,
|
||||
authorize_data: Option<types::PaymentsAuthorizeData>,
|
||||
refund_data: Option<types::RefundsData>,
|
||||
payment_info: Option<PaymentInfo>,
|
||||
) -> Result<types::RefundExecuteRouterData, Report<ConnectorError>> {
|
||||
//make a successful payment
|
||||
let response = self
|
||||
.authorize_and_capture_payment(authorize_data, None, payment_info.clone())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
//try refund for previous payment
|
||||
let transaction_id = get_connector_transaction_id(response.response).unwrap();
|
||||
tokio::time::sleep(Duration::from_secs(self.get_request_interval())).await; // to avoid 404 error
|
||||
Ok(self
|
||||
.refund_payment(transaction_id, refund_data, payment_info)
|
||||
@ -243,7 +269,7 @@ pub trait ConnectorActions: Connector {
|
||||
.unwrap();
|
||||
|
||||
//try refund for previous payment
|
||||
let transaction_id = get_connector_transaction_id(response).unwrap();
|
||||
let transaction_id = get_connector_transaction_id(response.response).unwrap();
|
||||
for _x in 0..2 {
|
||||
tokio::time::sleep(Duration::from_secs(self.get_request_interval())).await; // to avoid 404 error
|
||||
let refund_response = self
|
||||
@ -324,7 +350,7 @@ pub trait ConnectorActions: Connector {
|
||||
payment_id: uuid::Uuid::new_v4().to_string(),
|
||||
attempt_id: Some(uuid::Uuid::new_v4().to_string()),
|
||||
status: enums::AttemptStatus::default(),
|
||||
router_return_url: None,
|
||||
router_return_url: info.clone().and_then(|a| a.router_return_url),
|
||||
auth_type: info
|
||||
.clone()
|
||||
.map_or(enums::AuthenticationType::NoThreeDs, |a| {
|
||||
@ -516,9 +542,9 @@ impl Default for PaymentRefundType {
|
||||
}
|
||||
|
||||
pub fn get_connector_transaction_id(
|
||||
response: types::PaymentsAuthorizeRouterData,
|
||||
response: Result<types::PaymentsResponseData, types::ErrorResponse>,
|
||||
) -> Option<String> {
|
||||
match response.response {
|
||||
match response {
|
||||
Ok(types::PaymentsResponseData::TransactionResponse { resource_id, .. }) => {
|
||||
resource_id.get_connector_transaction_id().ok()
|
||||
}
|
||||
|
||||
@ -49,8 +49,7 @@ impl WorldlineTest {
|
||||
}),
|
||||
..Default::default()
|
||||
}),
|
||||
auth_type: None,
|
||||
access_token: None,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
||||
@ -196,7 +195,8 @@ async fn should_sync_manual_auth_payment() {
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(response.status, enums::AttemptStatus::Authorized);
|
||||
let connector_payment_id = utils::get_connector_transaction_id(response).unwrap_or_default();
|
||||
let connector_payment_id =
|
||||
utils::get_connector_transaction_id(response.response).unwrap_or_default();
|
||||
let sync_response = connector
|
||||
.sync_payment(
|
||||
Some(types::PaymentsSyncData {
|
||||
@ -228,7 +228,8 @@ async fn should_sync_auto_auth_payment() {
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(response.status, enums::AttemptStatus::Pending);
|
||||
let connector_payment_id = utils::get_connector_transaction_id(response).unwrap_or_default();
|
||||
let connector_payment_id =
|
||||
utils::get_connector_transaction_id(response.response).unwrap_or_default();
|
||||
let sync_response = connector
|
||||
.sync_payment(
|
||||
Some(types::PaymentsSyncData {
|
||||
@ -260,7 +261,8 @@ async fn should_capture_authorized_payment() {
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(response.status, enums::AttemptStatus::Authorized);
|
||||
let connector_payment_id = utils::get_connector_transaction_id(response).unwrap_or_default();
|
||||
let connector_payment_id =
|
||||
utils::get_connector_transaction_id(response.response).unwrap_or_default();
|
||||
let capture_response = WorldlineTest {}
|
||||
.capture_payment(connector_payment_id, None, None)
|
||||
.await
|
||||
@ -298,7 +300,8 @@ async fn should_cancel_unauthorized_payment() {
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(response.status, enums::AttemptStatus::Authorized);
|
||||
let connector_payment_id = utils::get_connector_transaction_id(response).unwrap_or_default();
|
||||
let connector_payment_id =
|
||||
utils::get_connector_transaction_id(response.response).unwrap_or_default();
|
||||
let cancel_response = connector
|
||||
.void_payment(connector_payment_id, None, None)
|
||||
.await
|
||||
@ -321,7 +324,8 @@ async fn should_cancel_uncaptured_payment() {
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(response.status, enums::AttemptStatus::Pending);
|
||||
let connector_payment_id = utils::get_connector_transaction_id(response).unwrap_or_default();
|
||||
let connector_payment_id =
|
||||
utils::get_connector_transaction_id(response.response).unwrap_or_default();
|
||||
let cancel_response = connector
|
||||
.void_payment(connector_payment_id, None, None)
|
||||
.await
|
||||
@ -356,7 +360,8 @@ async fn should_fail_refund_with_invalid_payment_status() {
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(response.status, enums::AttemptStatus::Authorized);
|
||||
let connector_payment_id = utils::get_connector_transaction_id(response).unwrap_or_default();
|
||||
let connector_payment_id =
|
||||
utils::get_connector_transaction_id(response.response).unwrap_or_default();
|
||||
let refund_response = connector
|
||||
.refund_payment(connector_payment_id, None, None)
|
||||
.await
|
||||
|
||||
@ -51,7 +51,7 @@ async fn should_authorize_card_payment() {
|
||||
let response = conn.authorize_payment(None, None).await.unwrap();
|
||||
assert_eq!(response.status, enums::AttemptStatus::Authorized);
|
||||
assert_eq!(
|
||||
utils::get_connector_transaction_id(response),
|
||||
utils::get_connector_transaction_id(response.response),
|
||||
Some("123456".to_string())
|
||||
);
|
||||
}
|
||||
@ -76,7 +76,7 @@ async fn should_authorize_gpay_payment() {
|
||||
.unwrap();
|
||||
assert_eq!(response.status, enums::AttemptStatus::Authorized);
|
||||
assert_eq!(
|
||||
utils::get_connector_transaction_id(response),
|
||||
utils::get_connector_transaction_id(response.response),
|
||||
Some("123456".to_string())
|
||||
);
|
||||
}
|
||||
@ -101,7 +101,7 @@ async fn should_authorize_applepay_payment() {
|
||||
.unwrap();
|
||||
assert_eq!(response.status, enums::AttemptStatus::Authorized);
|
||||
assert_eq!(
|
||||
utils::get_connector_transaction_id(response),
|
||||
utils::get_connector_transaction_id(response.response),
|
||||
Some("123456".to_string())
|
||||
);
|
||||
}
|
||||
@ -113,7 +113,7 @@ async fn should_capture_already_authorized_payment() {
|
||||
let _mock = connector.start_server(get_mock_config()).await;
|
||||
let authorize_response = connector.authorize_payment(None, None).await.unwrap();
|
||||
assert_eq!(authorize_response.status, enums::AttemptStatus::Authorized);
|
||||
let txn_id = utils::get_connector_transaction_id(authorize_response);
|
||||
let txn_id = utils::get_connector_transaction_id(authorize_response.response);
|
||||
let response: OptionFuture<_> = txn_id
|
||||
.map(|transaction_id| async move {
|
||||
connector
|
||||
@ -154,7 +154,7 @@ async fn should_void_already_authorized_payment() {
|
||||
let _mock = connector.start_server(get_mock_config()).await;
|
||||
let authorize_response = connector.authorize_payment(None, None).await.unwrap();
|
||||
assert_eq!(authorize_response.status, enums::AttemptStatus::Authorized);
|
||||
let txn_id = utils::get_connector_transaction_id(authorize_response);
|
||||
let txn_id = utils::get_connector_transaction_id(authorize_response.response);
|
||||
let response: OptionFuture<_> = txn_id
|
||||
.map(|transaction_id| async move {
|
||||
connector
|
||||
@ -195,7 +195,7 @@ async fn should_refund_succeeded_payment() {
|
||||
let response = connector.make_payment(None, None).await.unwrap();
|
||||
|
||||
//try refund for previous payment
|
||||
let transaction_id = utils::get_connector_transaction_id(response).unwrap();
|
||||
let transaction_id = utils::get_connector_transaction_id(response.response).unwrap();
|
||||
let response = connector
|
||||
.refund_payment(transaction_id, None, None)
|
||||
.await
|
||||
|
||||
@ -11,7 +11,7 @@ fi
|
||||
cd $SCRIPT/..
|
||||
# remove template files if already created for this connector
|
||||
rm -rf $conn/$pg $conn/$pg.rs
|
||||
git checkout $conn.rs $src/types/api.rs $src/configs/settings.rs config/Development.toml config/docker_compose.toml config/config.example.toml loadtest/config/Development.toml crates/router/src/configs/defaults.toml crates/api_models/src/enums.rs
|
||||
git checkout $conn.rs $src/types/api.rs $src/configs/settings.rs config/Development.toml config/docker_compose.toml config/config.example.toml loadtest/config/Development.toml crates/api_models/src/enums.rs
|
||||
# add enum for this connector in required places
|
||||
sed -i'' -e "s/pub use self::{/pub mod ${pg};\n\npub use self::{/" $conn.rs
|
||||
sed -i'' -e "s/};/${pg}::${pgc},\n};/" $conn.rs
|
||||
@ -25,10 +25,9 @@ sed -i'' -e "s/\[connectors.supported\]/[connectors.${pg}]\nbase_url = ""\n\n[co
|
||||
sed -r -i'' -e "s/cards = \[(.*)\]/cards = [\1, \"${pg}\"]/" config/config.example.toml
|
||||
sed -i'' -e "s/\[connectors.supported\]/[connectors.${pg}]\nbase_url = ""\n\n[connectors.supported]/" loadtest/config/Development.toml
|
||||
sed -r -i'' -e "s/cards = \[(.*)\]/cards = [\1, \"${pg}\"]/" loadtest/config/Development.toml
|
||||
sed -i'' -e "s/\[connectors.supported\]/[connectors.${pg}]\nbase_url = ""\n\n[connectors.supported]/" crates/router/src/configs/defaults.toml
|
||||
sed -i'' -e "s/Dummy,/Dummy,\n\t${pgc},/" crates/api_models/src/enums.rs
|
||||
# remove temporary files created in above step
|
||||
rm $conn.rs-e $src/types/api.rs-e $src/configs/settings.rs-e config/Development.toml-e config/docker_compose.toml-e config/config.example.toml-e loadtest/config/Development.toml-e crates/router/src/configs/defaults.toml-e crates/api_models/src/enums.rs-e
|
||||
rm $conn.rs-e $src/types/api.rs-e $src/configs/settings.rs-e config/Development.toml-e config/docker_compose.toml-e config/config.example.toml-e loadtest/config/Development.toml-e crates/api_models/src/enums.rs-e
|
||||
cd $conn/
|
||||
# generate template files for the connector
|
||||
cargo install cargo-generate
|
||||
|
||||
Reference in New Issue
Block a user