fix(connector): [Bluesnap] Throw proper error message for redirection scenario (#1367)

Signed-off-by: chikke srujan <121822803+srujanchikke@users.noreply.github.com>
Co-authored-by: Prasunna Soppa <prasunna.soppa@juspay.in>
This commit is contained in:
chikke srujan
2023-06-09 19:38:28 +05:30
committed by GitHub
parent 86f679abc1
commit 4a8de7741d
11 changed files with 192 additions and 92 deletions

View File

@ -1094,9 +1094,15 @@ impl services::ConnectorRedirectResponse for Bluesnap {
match redirection_result.status.as_str() { match redirection_result.status.as_str() {
"Success" => Ok(payments::CallConnectorAction::Trigger), "Success" => Ok(payments::CallConnectorAction::Trigger),
_ => Ok(payments::CallConnectorAction::StatusUpdate( _ => Ok(payments::CallConnectorAction::StatusUpdate {
enums::AttemptStatus::AuthenticationFailed, status: enums::AttemptStatus::AuthenticationFailed,
)), error_code: redirection_result.code,
error_message: redirection_result
.info
.as_ref()
.and_then(|info| info.errors.as_ref().and_then(|error| error.first()))
.cloned(),
}),
} }
} }
} }

View File

@ -1,6 +1,7 @@
use api_models::enums as api_enums; use api_models::enums as api_enums;
use base64::Engine; use base64::Engine;
use common_utils::{ use common_utils::{
errors::CustomResult,
ext_traits::{ByteSliceExt, StringExt, ValueExt}, ext_traits::{ByteSliceExt, StringExt, ValueExt},
pii::Email, pii::Email,
}; };
@ -9,7 +10,7 @@ use masking::ExposeInterface;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ use crate::{
connector::utils::{self, RouterData}, connector::utils::{self, AddressDetailsData, PaymentsAuthorizeRequestData, RouterData},
consts, consts,
core::errors, core::errors,
pii::Secret, pii::Secret,
@ -27,6 +28,15 @@ pub struct BluesnapPaymentsRequest {
card_transaction_type: BluesnapTxnType, card_transaction_type: BluesnapTxnType,
three_d_secure: Option<BluesnapThreeDSecureInfo>, three_d_secure: Option<BluesnapThreeDSecureInfo>,
transaction_fraud_info: Option<TransactionFraudInfo>, transaction_fraud_info: Option<TransactionFraudInfo>,
card_holder_info: Option<BluesnapCardHolderInfo>,
}
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct BluesnapCardHolderInfo {
first_name: Secret<String>,
last_name: Secret<String>,
email: Email,
} }
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
@ -149,13 +159,16 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for BluesnapPaymentsRequest {
Some(enums::CaptureMethod::Manual) => BluesnapTxnType::AuthOnly, Some(enums::CaptureMethod::Manual) => BluesnapTxnType::AuthOnly,
_ => BluesnapTxnType::AuthCapture, _ => BluesnapTxnType::AuthCapture,
}; };
let payment_method = match item.request.payment_method_data.clone() { let (payment_method, card_holder_info) = match item.request.payment_method_data.clone() {
api::PaymentMethodData::Card(ccard) => Ok(PaymentMethodDetails::CreditCard(Card { api::PaymentMethodData::Card(ccard) => Ok((
card_number: ccard.card_number, PaymentMethodDetails::CreditCard(Card {
expiration_month: ccard.card_exp_month.clone(), card_number: ccard.card_number,
expiration_year: ccard.card_exp_year.clone(), expiration_month: ccard.card_exp_month.clone(),
security_code: ccard.card_cvc, expiration_year: ccard.card_exp_year.clone(),
})), security_code: ccard.card_cvc,
}),
get_card_holder_info(item)?,
)),
api::PaymentMethodData::Wallet(wallet_data) => match wallet_data { api::PaymentMethodData::Wallet(wallet_data) => match wallet_data {
api_models::payments::WalletData::GooglePay(payment_method_data) => { api_models::payments::WalletData::GooglePay(payment_method_data) => {
let gpay_object = Encode::<BluesnapGooglePayObject>::encode_to_string_of_json( let gpay_object = Encode::<BluesnapGooglePayObject>::encode_to_string_of_json(
@ -166,10 +179,13 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for BluesnapPaymentsRequest {
}, },
) )
.change_context(errors::ConnectorError::RequestEncodingFailed)?; .change_context(errors::ConnectorError::RequestEncodingFailed)?;
Ok(PaymentMethodDetails::Wallet(BluesnapWallet { Ok((
wallet_type: BluesnapWalletTypes::GooglePay, PaymentMethodDetails::Wallet(BluesnapWallet {
encoded_payment_token: consts::BASE64_ENGINE.encode(gpay_object), wallet_type: BluesnapWalletTypes::GooglePay,
})) encoded_payment_token: consts::BASE64_ENGINE.encode(gpay_object),
}),
None,
))
} }
api_models::payments::WalletData::ApplePay(payment_method_data) => { api_models::payments::WalletData::ApplePay(payment_method_data) => {
let apple_pay_payment_data = consts::BASE64_ENGINE let apple_pay_payment_data = consts::BASE64_ENGINE
@ -230,10 +246,13 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for BluesnapPaymentsRequest {
) )
.change_context(errors::ConnectorError::RequestEncodingFailed)?; .change_context(errors::ConnectorError::RequestEncodingFailed)?;
Ok(PaymentMethodDetails::Wallet(BluesnapWallet { Ok((
wallet_type: BluesnapWalletTypes::ApplePay, PaymentMethodDetails::Wallet(BluesnapWallet {
encoded_payment_token: consts::BASE64_ENGINE.encode(apple_pay_object), wallet_type: BluesnapWalletTypes::ApplePay,
})) encoded_payment_token: consts::BASE64_ENGINE.encode(apple_pay_object),
}),
None,
))
} }
_ => Err(errors::ConnectorError::NotImplemented( _ => Err(errors::ConnectorError::NotImplemented(
"Wallets".to_string(), "Wallets".to_string(),
@ -252,6 +271,7 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for BluesnapPaymentsRequest {
transaction_fraud_info: Some(TransactionFraudInfo { transaction_fraud_info: Some(TransactionFraudInfo {
fraud_session_id: item.payment_id.clone(), fraud_session_id: item.payment_id.clone(),
}), }),
card_holder_info,
}) })
} }
} }
@ -397,6 +417,7 @@ impl TryFrom<&types::PaymentsCompleteAuthorizeRouterData> for BluesnapPaymentsRe
transaction_fraud_info: Some(TransactionFraudInfo { transaction_fraud_info: Some(TransactionFraudInfo {
fraud_session_id: item.payment_id.clone(), fraud_session_id: item.payment_id.clone(),
}), }),
card_holder_info: None,
}) })
} }
} }
@ -411,6 +432,14 @@ pub struct BluesnapRedirectionResponse {
pub struct BluesnapThreeDsResult { pub struct BluesnapThreeDsResult {
three_d_secure: Option<BluesnapThreeDsReference>, three_d_secure: Option<BluesnapThreeDsReference>,
pub status: String, pub status: String,
pub code: Option<String>,
pub info: Option<RedirectErrorMessage>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RedirectErrorMessage {
pub errors: Option<Vec<String>>,
} }
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
@ -759,3 +788,14 @@ pub enum BluesnapErrors {
PaymentError(BluesnapErrorResponse), PaymentError(BluesnapErrorResponse),
AuthError(BluesnapAuthErrorResponse), AuthError(BluesnapAuthErrorResponse),
} }
fn get_card_holder_info(
item: &types::PaymentsAuthorizeRouterData,
) -> CustomResult<Option<BluesnapCardHolderInfo>, errors::ConnectorError> {
let address = item.get_billing_address()?;
Ok(Some(BluesnapCardHolderInfo {
first_name: address.get_first_name()?.clone(),
last_name: address.get_last_name()?.clone(),
email: item.request.get_email()?,
}))
}

View File

@ -1199,9 +1199,13 @@ impl services::ConnectorRedirectResponse for Checkout {
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?; .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
let connector_action = query let connector_action = query
.status .status
.map(|checkout_status| { .map(
payments::CallConnectorAction::StatusUpdate(checkout_status.into()) |checkout_status| payments::CallConnectorAction::StatusUpdate {
}) status: storage_models::enums::AttemptStatus::from(checkout_status),
error_code: None,
error_message: None,
},
)
.unwrap_or(payments::CallConnectorAction::Trigger); .unwrap_or(payments::CallConnectorAction::Trigger);
Ok(connector_action) Ok(connector_action)
} }

View File

@ -914,9 +914,11 @@ impl services::ConnectorRedirectResponse for Globalpay {
payments::CallConnectorAction::Trigger, payments::CallConnectorAction::Trigger,
|status| match status { |status| match status {
response::GlobalpayPaymentStatus::Captured => { response::GlobalpayPaymentStatus::Captured => {
payments::CallConnectorAction::StatusUpdate( payments::CallConnectorAction::StatusUpdate {
storage_models::enums::AttemptStatus::from(status), status: storage_models::enums::AttemptStatus::from(status),
) error_code: None,
error_message: None,
}
} }
_ => payments::CallConnectorAction::Trigger, _ => payments::CallConnectorAction::Trigger,
}, },

View File

@ -957,9 +957,11 @@ impl services::ConnectorRedirectResponse for Nuvei {
.switch()?; .switch()?;
match acs_response.trans_status { match acs_response.trans_status {
None | Some(nuvei::LiabilityShift::Failed) => { None | Some(nuvei::LiabilityShift::Failed) => {
Ok(payments::CallConnectorAction::StatusUpdate( Ok(payments::CallConnectorAction::StatusUpdate {
enums::AttemptStatus::AuthenticationFailed, status: enums::AttemptStatus::AuthenticationFailed,
)) error_code: None,
error_message: None,
})
} }
_ => Ok(payments::CallConnectorAction::Trigger), _ => Ok(payments::CallConnectorAction::Trigger),
} }

View File

@ -1831,9 +1831,11 @@ impl services::ConnectorRedirectResponse for Stripe {
transformers::StripePaymentStatus::Failed => { transformers::StripePaymentStatus::Failed => {
payments::CallConnectorAction::Trigger payments::CallConnectorAction::Trigger
} }
_ => payments::CallConnectorAction::StatusUpdate(enums::AttemptStatus::from( _ => payments::CallConnectorAction::StatusUpdate {
status, status: enums::AttemptStatus::from(status),
)), error_code: None,
error_message: None,
},
}, },
)) ))
} }

View File

@ -802,9 +802,11 @@ impl services::ConnectorRedirectResponse for Trustpay {
Ok(query.status.map_or( Ok(query.status.map_or(
payments::CallConnectorAction::Trigger, payments::CallConnectorAction::Trigger,
|status| match status.as_str() { |status| match status.as_str() {
"SuccessOk" => payments::CallConnectorAction::StatusUpdate( "SuccessOk" => payments::CallConnectorAction::StatusUpdate {
storage_models::enums::AttemptStatus::Charged, status: storage_models::enums::AttemptStatus::Charged,
), error_code: None,
error_message: None,
},
_ => payments::CallConnectorAction::Trigger, _ => payments::CallConnectorAction::Trigger,
}, },
)) ))

View File

@ -429,6 +429,7 @@ pub trait CardData {
delimiter: String, delimiter: String,
) -> Secret<String>; ) -> Secret<String>;
fn get_expiry_date_as_yyyymm(&self, delimiter: &str) -> Secret<String>; fn get_expiry_date_as_yyyymm(&self, delimiter: &str) -> Secret<String>;
fn get_expiry_year_4_digit(&self) -> Secret<String>;
} }
impl CardData for api::Card { impl CardData for api::Card {
@ -453,17 +454,21 @@ impl CardData for api::Card {
)) ))
} }
fn get_expiry_date_as_yyyymm(&self, delimiter: &str) -> Secret<String> { fn get_expiry_date_as_yyyymm(&self, delimiter: &str) -> Secret<String> {
let mut x = self.card_exp_year.peek().clone(); let year = self.get_expiry_year_4_digit();
if x.len() == 2 {
x = format!("20{}", x);
}
Secret::new(format!( Secret::new(format!(
"{}{}{}", "{}{}{}",
x, year.peek(),
delimiter, delimiter,
self.card_exp_month.peek().clone() self.card_exp_month.peek().clone()
)) ))
} }
fn get_expiry_year_4_digit(&self) -> Secret<String> {
let mut year = self.card_exp_year.peek().clone();
if year.len() == 2 {
year = format!("20{}", year);
}
Secret::new(year)
}
} }
#[track_caller] #[track_caller]

View File

@ -937,7 +937,11 @@ where
pub enum CallConnectorAction { pub enum CallConnectorAction {
Trigger, Trigger,
Avoid, Avoid,
StatusUpdate(storage_enums::AttemptStatus), StatusUpdate {
status: storage_enums::AttemptStatus,
error_code: Option<String>,
error_message: Option<String>,
},
HandleResponse(Vec<u8>), HandleResponse(Vec<u8>),
} }

View File

@ -21,6 +21,7 @@ use self::request::{ContentType, HeaderExt, RequestBuilderExt};
pub use self::request::{Method, Request, RequestBuilder}; pub use self::request::{Method, Request, RequestBuilder};
use crate::{ use crate::{
configs::settings::Connectors, configs::settings::Connectors,
consts,
core::{ core::{
errors::{self, CustomResult}, errors::{self, CustomResult},
payments, payments,
@ -190,8 +191,23 @@ where
connector_integration.handle_response(req, response) connector_integration.handle_response(req, response)
} }
payments::CallConnectorAction::Avoid => Ok(router_data), payments::CallConnectorAction::Avoid => Ok(router_data),
payments::CallConnectorAction::StatusUpdate(status) => { payments::CallConnectorAction::StatusUpdate {
status,
error_code,
error_message,
} => {
router_data.status = status; router_data.status = status;
let error_response = if error_code.is_some() | error_message.is_some() {
Some(ErrorResponse {
code: error_code.unwrap_or(consts::NO_ERROR_CODE.to_string()),
message: error_message.unwrap_or(consts::NO_ERROR_MESSAGE.to_string()),
status_code: 200, // This status code is ignored in redirection response it will override with 302 status code.
reason: None,
})
} else {
None
};
router_data.response = error_response.map(Err).unwrap_or(router_data.response);
Ok(router_data) Ok(router_data)
} }
payments::CallConnectorAction::Trigger => { payments::CallConnectorAction::Trigger => {

View File

@ -1,11 +1,13 @@
use std::str::FromStr; use std::str::FromStr;
use api_models::payments::{Address, AddressDetails};
use common_utils::pii::Email;
use masking::Secret; use masking::Secret;
use router::types::{self, api, storage::enums, ConnectorAuthType}; use router::types::{self, api, storage::enums, ConnectorAuthType, PaymentAddress};
use crate::{ use crate::{
connector_auth, connector_auth,
utils::{self, ConnectorActions}, utils::{self, ConnectorActions, PaymentInfo},
}; };
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
@ -34,6 +36,28 @@ impl utils::Connector for BluesnapTest {
"bluesnap".to_string() "bluesnap".to_string()
} }
} }
fn payment_method_details() -> Option<types::PaymentsAuthorizeData> {
Some(types::PaymentsAuthorizeData {
email: Some(Email::from_str("test@gmail.com").unwrap()),
..utils::PaymentAuthorizeType::default().0
})
}
fn get_payment_info() -> Option<PaymentInfo> {
Some(PaymentInfo {
address: Some(PaymentAddress {
billing: Some(Address {
address: Some(AddressDetails {
first_name: Some(Secret::new("joseph".to_string())),
last_name: Some(Secret::new("Doe".to_string())),
..Default::default()
}),
phone: None,
}),
..Default::default()
}),
..Default::default()
})
}
// Cards Positive Tests // Cards Positive Tests
// Creates a payment using the manual capture flow (Non 3DS). // Creates a payment using the manual capture flow (Non 3DS).
@ -42,7 +66,7 @@ impl utils::Connector for BluesnapTest {
#[actix_web::test] #[actix_web::test]
async fn should_only_authorize_payment() { async fn should_only_authorize_payment() {
let response = CONNECTOR let response = CONNECTOR
.authorize_payment(None, None) .authorize_payment(payment_method_details(), get_payment_info())
.await .await
.expect("Authorize payment response"); .expect("Authorize payment response");
assert_eq!(response.status, enums::AttemptStatus::Authorized); assert_eq!(response.status, enums::AttemptStatus::Authorized);
@ -54,7 +78,7 @@ async fn should_only_authorize_payment() {
#[actix_web::test] #[actix_web::test]
async fn should_capture_authorized_payment() { async fn should_capture_authorized_payment() {
let response = CONNECTOR let response = CONNECTOR
.authorize_and_capture_payment(None, None, None) .authorize_and_capture_payment(payment_method_details(), None, get_payment_info())
.await .await
.expect("Capture payment response"); .expect("Capture payment response");
assert_eq!(response.status, enums::AttemptStatus::Charged); assert_eq!(response.status, enums::AttemptStatus::Charged);
@ -67,12 +91,12 @@ async fn should_capture_authorized_payment() {
async fn should_partially_capture_authorized_payment() { async fn should_partially_capture_authorized_payment() {
let response = CONNECTOR let response = CONNECTOR
.authorize_and_capture_payment( .authorize_and_capture_payment(
None, payment_method_details(),
Some(types::PaymentsCaptureData { Some(types::PaymentsCaptureData {
amount_to_capture: 50, amount_to_capture: 50,
..utils::PaymentCaptureType::default().0 ..utils::PaymentCaptureType::default().0
}), }),
None, get_payment_info(),
) )
.await .await
.expect("Capture payment response"); .expect("Capture payment response");
@ -85,7 +109,7 @@ async fn should_partially_capture_authorized_payment() {
#[actix_web::test] #[actix_web::test]
async fn should_sync_authorized_payment() { async fn should_sync_authorized_payment() {
let authorize_response = CONNECTOR let authorize_response = CONNECTOR
.authorize_payment(None, None) .authorize_payment(payment_method_details(), get_payment_info())
.await .await
.expect("Authorize payment response"); .expect("Authorize payment response");
let txn_id = utils::get_connector_transaction_id(authorize_response.response); let txn_id = utils::get_connector_transaction_id(authorize_response.response);
@ -112,13 +136,13 @@ async fn should_sync_authorized_payment() {
async fn should_void_authorized_payment() { async fn should_void_authorized_payment() {
let response = CONNECTOR let response = CONNECTOR
.authorize_and_void_payment( .authorize_and_void_payment(
None, payment_method_details(),
Some(types::PaymentsCancelData { Some(types::PaymentsCancelData {
connector_transaction_id: String::from(""), connector_transaction_id: String::from(""),
cancellation_reason: Some("requested_by_customer".to_string()), cancellation_reason: Some("requested_by_customer".to_string()),
..Default::default() ..Default::default()
}), }),
None, get_payment_info(),
) )
.await .await
.expect("Void payment response"); .expect("Void payment response");
@ -131,7 +155,7 @@ async fn should_void_authorized_payment() {
#[actix_web::test] #[actix_web::test]
async fn should_refund_manually_captured_payment() { async fn should_refund_manually_captured_payment() {
let response = CONNECTOR let response = CONNECTOR
.capture_payment_and_refund(None, None, None, None) .capture_payment_and_refund(payment_method_details(), None, None, get_payment_info())
.await .await
.unwrap(); .unwrap();
let rsync_response = CONNECTOR let rsync_response = CONNECTOR
@ -156,13 +180,13 @@ async fn should_refund_manually_captured_payment() {
async fn should_partially_refund_manually_captured_payment() { async fn should_partially_refund_manually_captured_payment() {
let response = CONNECTOR let response = CONNECTOR
.capture_payment_and_refund( .capture_payment_and_refund(
None, payment_method_details(),
None, None,
Some(types::RefundsData { Some(types::RefundsData {
refund_amount: 50, refund_amount: 50,
..utils::PaymentRefundType::default().0 ..utils::PaymentRefundType::default().0
}), }),
None, get_payment_info(),
) )
.await .await
.unwrap(); .unwrap();
@ -187,7 +211,7 @@ async fn should_partially_refund_manually_captured_payment() {
#[actix_web::test] #[actix_web::test]
async fn should_sync_manually_captured_refund() { async fn should_sync_manually_captured_refund() {
let refund_response = CONNECTOR let refund_response = CONNECTOR
.capture_payment_and_refund(None, None, None, None) .capture_payment_and_refund(payment_method_details(), None, None, get_payment_info())
.await .await
.unwrap(); .unwrap();
let response = CONNECTOR let response = CONNECTOR
@ -210,7 +234,10 @@ async fn should_sync_manually_captured_refund() {
#[serial_test::serial] #[serial_test::serial]
#[actix_web::test] #[actix_web::test]
async fn should_make_payment() { async fn should_make_payment() {
let authorize_response = CONNECTOR.make_payment(None, None).await.unwrap(); let authorize_response = CONNECTOR
.make_payment(payment_method_details(), get_payment_info())
.await
.unwrap();
assert_eq!(authorize_response.status, enums::AttemptStatus::Charged); assert_eq!(authorize_response.status, enums::AttemptStatus::Charged);
} }
@ -219,7 +246,10 @@ async fn should_make_payment() {
#[serial_test::serial] #[serial_test::serial]
#[actix_web::test] #[actix_web::test]
async fn should_sync_auto_captured_payment() { async fn should_sync_auto_captured_payment() {
let authorize_response = CONNECTOR.make_payment(None, None).await.unwrap(); let authorize_response = CONNECTOR
.make_payment(payment_method_details(), get_payment_info())
.await
.unwrap();
assert_eq!(authorize_response.status, enums::AttemptStatus::Charged); assert_eq!(authorize_response.status, enums::AttemptStatus::Charged);
let txn_id = utils::get_connector_transaction_id(authorize_response.response); let txn_id = utils::get_connector_transaction_id(authorize_response.response);
assert_ne!(txn_id, None, "Empty connector transaction id"); assert_ne!(txn_id, None, "Empty connector transaction id");
@ -245,7 +275,7 @@ async fn should_sync_auto_captured_payment() {
#[actix_web::test] #[actix_web::test]
async fn should_refund_auto_captured_payment() { async fn should_refund_auto_captured_payment() {
let response = CONNECTOR let response = CONNECTOR
.make_payment_and_refund(None, None, None) .make_payment_and_refund(payment_method_details(), None, get_payment_info())
.await .await
.unwrap(); .unwrap();
let rsync_response = CONNECTOR let rsync_response = CONNECTOR
@ -270,12 +300,12 @@ async fn should_refund_auto_captured_payment() {
async fn should_partially_refund_succeeded_payment() { async fn should_partially_refund_succeeded_payment() {
let refund_response = CONNECTOR let refund_response = CONNECTOR
.make_payment_and_refund( .make_payment_and_refund(
None, payment_method_details(),
Some(types::RefundsData { Some(types::RefundsData {
refund_amount: 50, refund_amount: 50,
..utils::PaymentRefundType::default().0 ..utils::PaymentRefundType::default().0
}), }),
None, get_payment_info(),
) )
.await .await
.unwrap(); .unwrap();
@ -299,7 +329,10 @@ async fn should_partially_refund_succeeded_payment() {
#[serial_test::serial] #[serial_test::serial]
#[actix_web::test] #[actix_web::test]
async fn should_refund_succeeded_payment_multiple_times() { async fn should_refund_succeeded_payment_multiple_times() {
let authorize_response = CONNECTOR.make_payment(None, None).await.unwrap(); let authorize_response = CONNECTOR
.make_payment(payment_method_details(), get_payment_info())
.await
.unwrap();
assert_eq!(authorize_response.status, enums::AttemptStatus::Charged); assert_eq!(authorize_response.status, enums::AttemptStatus::Charged);
let transaction_id = utils::get_connector_transaction_id(authorize_response.response).unwrap(); let transaction_id = utils::get_connector_transaction_id(authorize_response.response).unwrap();
for _x in 0..2 { for _x in 0..2 {
@ -337,7 +370,7 @@ async fn should_refund_succeeded_payment_multiple_times() {
#[actix_web::test] #[actix_web::test]
async fn should_sync_refund() { async fn should_sync_refund() {
let refund_response = CONNECTOR let refund_response = CONNECTOR
.make_payment_and_refund(None, None, None) .make_payment_and_refund(payment_method_details(), None, get_payment_info())
.await .await
.unwrap(); .unwrap();
let response = CONNECTOR let response = CONNECTOR
@ -355,31 +388,6 @@ async fn should_sync_refund() {
); );
} }
// Cards Negative scenerios
// Creates a payment with incorrect card number.
#[serial_test::serial]
#[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::PaymentMethodData::Card(api::Card {
card_number: cards::CardNumber::from_str("1234567891011").unwrap(),
..utils::CCardType::default().0
}),
..utils::PaymentAuthorizeType::default().0
}),
None,
)
.await
.unwrap();
assert_eq!(
response.response.unwrap_err().message,
"Order creation failure due to problematic input.".to_string(),
);
}
// Creates a payment with incorrect CVC. // Creates a payment with incorrect CVC.
#[serial_test::serial] #[serial_test::serial]
@ -388,13 +396,15 @@ async fn should_fail_payment_for_incorrect_cvc() {
let response = CONNECTOR let response = CONNECTOR
.make_payment( .make_payment(
Some(types::PaymentsAuthorizeData { Some(types::PaymentsAuthorizeData {
email: Some(Email::from_str("test@gmail.com").unwrap()),
payment_method_data: types::api::PaymentMethodData::Card(api::Card { payment_method_data: types::api::PaymentMethodData::Card(api::Card {
card_holder_name: Secret::new("John Doe".to_string()),
card_cvc: Secret::new("12345".to_string()), card_cvc: Secret::new("12345".to_string()),
..utils::CCardType::default().0 ..utils::CCardType::default().0
}), }),
..utils::PaymentAuthorizeType::default().0 ..utils::PaymentAuthorizeType::default().0
}), }),
None, get_payment_info(),
) )
.await .await
.unwrap(); .unwrap();
@ -412,13 +422,15 @@ async fn should_fail_payment_for_invalid_exp_month() {
let response = CONNECTOR let response = CONNECTOR
.make_payment( .make_payment(
Some(types::PaymentsAuthorizeData { Some(types::PaymentsAuthorizeData {
email: Some(Email::from_str("test@gmail.com").unwrap()),
payment_method_data: types::api::PaymentMethodData::Card(api::Card { payment_method_data: types::api::PaymentMethodData::Card(api::Card {
card_holder_name: Secret::new("John Doe".to_string()),
card_exp_month: Secret::new("20".to_string()), card_exp_month: Secret::new("20".to_string()),
..utils::CCardType::default().0 ..utils::CCardType::default().0
}), }),
..utils::PaymentAuthorizeType::default().0 ..utils::PaymentAuthorizeType::default().0
}), }),
None, get_payment_info(),
) )
.await .await
.unwrap(); .unwrap();
@ -436,13 +448,15 @@ async fn should_fail_payment_for_incorrect_expiry_year() {
let response = CONNECTOR let response = CONNECTOR
.make_payment( .make_payment(
Some(types::PaymentsAuthorizeData { Some(types::PaymentsAuthorizeData {
email: Some(Email::from_str("test@gmail.com").unwrap()),
payment_method_data: types::api::PaymentMethodData::Card(api::Card { payment_method_data: types::api::PaymentMethodData::Card(api::Card {
card_holder_name: Secret::new("John Doe".to_string()),
card_exp_year: Secret::new("2000".to_string()), card_exp_year: Secret::new("2000".to_string()),
..utils::CCardType::default().0 ..utils::CCardType::default().0
}), }),
..utils::PaymentAuthorizeType::default().0 ..utils::PaymentAuthorizeType::default().0
}), }),
None, get_payment_info(),
) )
.await .await
.unwrap(); .unwrap();
@ -457,7 +471,10 @@ async fn should_fail_payment_for_incorrect_expiry_year() {
#[serial_test::serial] #[serial_test::serial]
#[actix_web::test] #[actix_web::test]
async fn should_fail_void_payment_for_auto_capture() { async fn should_fail_void_payment_for_auto_capture() {
let authorize_response = CONNECTOR.make_payment(None, None).await.unwrap(); let authorize_response = CONNECTOR
.make_payment(payment_method_details(), get_payment_info())
.await
.unwrap();
assert_eq!(authorize_response.status, enums::AttemptStatus::Charged); assert_eq!(authorize_response.status, enums::AttemptStatus::Charged);
let txn_id = utils::get_connector_transaction_id(authorize_response.response); let txn_id = utils::get_connector_transaction_id(authorize_response.response);
assert_ne!(txn_id, None, "Empty connector transaction id"); assert_ne!(txn_id, None, "Empty connector transaction id");
@ -495,12 +512,12 @@ async fn should_fail_capture_for_invalid_payment() {
async fn should_fail_for_refund_amount_higher_than_payment_amount() { async fn should_fail_for_refund_amount_higher_than_payment_amount() {
let response = CONNECTOR let response = CONNECTOR
.make_payment_and_refund( .make_payment_and_refund(
None, payment_method_details(),
Some(types::RefundsData { Some(types::RefundsData {
refund_amount: 150, refund_amount: 150,
..utils::PaymentRefundType::default().0 ..utils::PaymentRefundType::default().0
}), }),
None, get_payment_info(),
) )
.await .await
.unwrap(); .unwrap();