mirror of
				https://github.com/juspay/hyperswitch.git
				synced 2025-10-31 18:17:13 +08:00 
			
		
		
		
	test(stripe): add unit tests for stripe connector (#473)
This commit is contained in:
		| @ -12,6 +12,7 @@ pub(crate) struct ConnectorAuthentication { | ||||
|     pub payu: Option<BodyKey>, | ||||
|     pub rapyd: Option<BodyKey>, | ||||
|     pub shift4: Option<HeaderKey>, | ||||
|     pub stripe: Option<HeaderKey>, | ||||
|     pub worldpay: Option<HeaderKey>, | ||||
|     pub worldline: Option<SignatureKey>, | ||||
| } | ||||
|  | ||||
| @ -10,6 +10,7 @@ mod globalpay; | ||||
| mod payu; | ||||
| mod rapyd; | ||||
| mod shift4; | ||||
| mod stripe; | ||||
| mod utils; | ||||
| mod worldline; | ||||
| mod worldpay; | ||||
|  | ||||
							
								
								
									
										337
									
								
								crates/router/tests/connectors/stripe.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										337
									
								
								crates/router/tests/connectors/stripe.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,337 @@ | ||||
| use std::time::Duration; | ||||
|  | ||||
| use masking::Secret; | ||||
| use router::types::{self, api, storage::enums}; | ||||
|  | ||||
| use crate::{ | ||||
|     connector_auth, | ||||
|     utils::{self, ConnectorActions}, | ||||
| }; | ||||
|  | ||||
| struct Stripe; | ||||
| impl ConnectorActions for Stripe {} | ||||
| impl utils::Connector for Stripe { | ||||
|     fn get_data(&self) -> types::api::ConnectorData { | ||||
|         use router::connector::Stripe; | ||||
|         types::api::ConnectorData { | ||||
|             connector: Box::new(&Stripe), | ||||
|             connector_name: types::Connector::Stripe, | ||||
|             get_token: types::api::GetToken::Connector, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn get_auth_token(&self) -> types::ConnectorAuthType { | ||||
|         types::ConnectorAuthType::from( | ||||
|             connector_auth::ConnectorAuthentication::new() | ||||
|                 .stripe | ||||
|                 .expect("Missing connector authentication configuration"), | ||||
|         ) | ||||
|     } | ||||
|  | ||||
|     fn get_name(&self) -> String { | ||||
|         "stripe".to_string() | ||||
|     } | ||||
| } | ||||
|  | ||||
| fn get_payment_authorize_data() -> Option<types::PaymentsAuthorizeData> { | ||||
|     Some(types::PaymentsAuthorizeData { | ||||
|         payment_method_data: types::api::PaymentMethod::Card(api::CCard { | ||||
|             card_number: Secret::new("4242424242424242".to_string()), | ||||
|             ..utils::CCardType::default().0 | ||||
|         }), | ||||
|         ..utils::PaymentAuthorizeType::default().0 | ||||
|     }) | ||||
| } | ||||
|  | ||||
| #[actix_web::test] | ||||
| async fn should_only_authorize_payment() { | ||||
|     let response = Stripe {} | ||||
|         .authorize_payment(get_payment_authorize_data(), None) | ||||
|         .await | ||||
|         .unwrap(); | ||||
|     assert_eq!(response.status, enums::AttemptStatus::Authorized); | ||||
| } | ||||
|  | ||||
| #[actix_web::test] | ||||
| async fn should_authorize_and_capture_payment() { | ||||
|     let response = Stripe {} | ||||
|         .make_payment(get_payment_authorize_data(), None) | ||||
|         .await | ||||
|         .unwrap(); | ||||
|     assert_eq!(response.status, enums::AttemptStatus::Charged); | ||||
| } | ||||
|  | ||||
| #[actix_web::test] | ||||
| async fn should_capture_already_authorized_payment() { | ||||
|     let connector = Stripe {}; | ||||
|     let response = connector | ||||
|         .authorize_and_capture_payment(get_payment_authorize_data(), None, None) | ||||
|         .await; | ||||
|     assert_eq!(response.unwrap().status, enums::AttemptStatus::Charged); | ||||
| } | ||||
|  | ||||
| #[actix_web::test] | ||||
| async fn should_partially_capture_already_authorized_payment() { | ||||
|     let connector = Stripe {}; | ||||
|     let response = connector | ||||
|         .authorize_and_capture_payment( | ||||
|             get_payment_authorize_data(), | ||||
|             Some(types::PaymentsCaptureData { | ||||
|                 amount_to_capture: Some(50), | ||||
|                 ..utils::PaymentCaptureType::default().0 | ||||
|             }), | ||||
|             None, | ||||
|         ) | ||||
|         .await; | ||||
|     assert_eq!(response.unwrap().status, enums::AttemptStatus::Charged); | ||||
| } | ||||
|  | ||||
| #[actix_web::test] | ||||
| async fn should_sync_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 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,); | ||||
| } | ||||
|  | ||||
| #[actix_web::test] | ||||
| async fn should_void_already_authorized_payment() { | ||||
|     let connector = Stripe {}; | ||||
|     let response = connector | ||||
|         .authorize_and_void_payment( | ||||
|             get_payment_authorize_data(), | ||||
|             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::Voided); | ||||
| } | ||||
|  | ||||
| #[actix_web::test] | ||||
| async fn should_fail_payment_for_incorrect_card_number() { | ||||
|     let response = Stripe {} | ||||
|         .make_payment( | ||||
|             Some(types::PaymentsAuthorizeData { | ||||
|                 payment_method_data: types::api::PaymentMethod::Card(api::CCard { | ||||
|                     card_number: Secret::new("4024007134364842".to_string()), | ||||
|                     ..utils::CCardType::default().0 | ||||
|                 }), | ||||
|                 ..utils::PaymentAuthorizeType::default().0 | ||||
|             }), | ||||
|             None, | ||||
|         ) | ||||
|         .await | ||||
|         .unwrap(); | ||||
|     let x = response.response.unwrap_err(); | ||||
|     assert_eq!( | ||||
|         x.message, | ||||
|         "Your card was declined. Your request was in test mode, but used a non test (live) card. For a list of valid test cards, visit: https://stripe.com/docs/testing.", | ||||
|     ); | ||||
| } | ||||
|  | ||||
| #[actix_web::test] | ||||
| async fn should_fail_payment_for_no_card_number() { | ||||
|     let response = Stripe {} | ||||
|         .make_payment( | ||||
|             Some(types::PaymentsAuthorizeData { | ||||
|                 payment_method_data: types::api::PaymentMethod::Card(api::CCard { | ||||
|                     card_number: Secret::new("".to_string()), | ||||
|                     ..utils::CCardType::default().0 | ||||
|                 }), | ||||
|                 ..utils::PaymentAuthorizeType::default().0 | ||||
|             }), | ||||
|             None, | ||||
|         ) | ||||
|         .await | ||||
|         .unwrap(); | ||||
|     let x = response.response.unwrap_err(); | ||||
|     assert_eq!( | ||||
|         x.message, | ||||
|         "You passed an empty string for 'payment_method_data[card][number]'. We assume empty values are an attempt to unset a parameter; however 'payment_method_data[card][number]' cannot be unset. You should remove 'payment_method_data[card][number]' from your request or supply a non-empty value.", | ||||
|     ); | ||||
| } | ||||
|  | ||||
| #[actix_web::test] | ||||
| async fn should_fail_payment_for_invalid_exp_month() { | ||||
|     let response = Stripe {} | ||||
|         .make_payment( | ||||
|             Some(types::PaymentsAuthorizeData { | ||||
|                 payment_method_data: types::api::PaymentMethod::Card(api::CCard { | ||||
|                     card_exp_month: Secret::new("13".to_string()), | ||||
|                     ..utils::CCardType::default().0 | ||||
|                 }), | ||||
|                 ..utils::PaymentAuthorizeType::default().0 | ||||
|             }), | ||||
|             None, | ||||
|         ) | ||||
|         .await | ||||
|         .unwrap(); | ||||
|     let x = response.response.unwrap_err(); | ||||
|     assert_eq!(x.message, "Your card's expiration month is invalid.",); | ||||
| } | ||||
|  | ||||
| #[actix_web::test] | ||||
| async fn should_fail_payment_for_invalid_exp_year() { | ||||
|     let response = Stripe {} | ||||
|         .make_payment( | ||||
|             Some(types::PaymentsAuthorizeData { | ||||
|                 payment_method_data: types::api::PaymentMethod::Card(api::CCard { | ||||
|                     card_exp_year: Secret::new("2022".to_string()), | ||||
|                     ..utils::CCardType::default().0 | ||||
|                 }), | ||||
|                 ..utils::PaymentAuthorizeType::default().0 | ||||
|             }), | ||||
|             None, | ||||
|         ) | ||||
|         .await | ||||
|         .unwrap(); | ||||
|     let x = response.response.unwrap_err(); | ||||
|     assert_eq!(x.message, "Your card's expiration year is invalid.",); | ||||
| } | ||||
|  | ||||
| #[actix_web::test] | ||||
| async fn should_fail_payment_for_invalid_card_cvc() { | ||||
|     let response = Stripe {} | ||||
|         .make_payment( | ||||
|             Some(types::PaymentsAuthorizeData { | ||||
|                 payment_method_data: types::api::PaymentMethod::Card(api::CCard { | ||||
|                     card_cvc: Secret::new("12".to_string()), | ||||
|                     ..utils::CCardType::default().0 | ||||
|                 }), | ||||
|                 ..utils::PaymentAuthorizeType::default().0 | ||||
|             }), | ||||
|             None, | ||||
|         ) | ||||
|         .await | ||||
|         .unwrap(); | ||||
|     let x = response.response.unwrap_err(); | ||||
|     assert_eq!(x.message, "Your card's security code is invalid.",); | ||||
| } | ||||
|  | ||||
| #[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 | ||||
|         .unwrap(); | ||||
|     let err = response.response.unwrap_err(); | ||||
|     assert_eq!(err.message, "No such payment_intent: '12345'".to_string()); | ||||
|     assert_eq!(err.code, "resource_missing".to_string()); | ||||
| } | ||||
|  | ||||
| #[actix_web::test] | ||||
| async fn should_refund_succeeded_payment() { | ||||
|     let connector = Stripe {}; | ||||
|     let response = connector | ||||
|         .make_payment_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 {}; | ||||
|     let refund_response = connector | ||||
|         .make_payment_and_refund( | ||||
|             get_payment_authorize_data(), | ||||
|             Some(types::RefundsData { | ||||
|                 refund_amount: 50, | ||||
|                 ..utils::PaymentRefundType::default().0 | ||||
|             }), | ||||
|             None, | ||||
|         ) | ||||
|         .await | ||||
|         .unwrap(); | ||||
|     assert_eq!( | ||||
|         refund_response.response.unwrap().refund_status, | ||||
|         enums::RefundStatus::Success, | ||||
|     ); | ||||
| } | ||||
|  | ||||
| #[actix_web::test] | ||||
| async fn should_refund_succeeded_payment_multiple_times() { | ||||
|     let connector = Stripe {}; | ||||
|     connector | ||||
|         .make_payment_and_multiple_refund( | ||||
|             get_payment_authorize_data(), | ||||
|             Some(types::RefundsData { | ||||
|                 refund_amount: 50, | ||||
|                 ..utils::PaymentRefundType::default().0 | ||||
|             }), | ||||
|             None, | ||||
|         ) | ||||
|         .await; | ||||
| } | ||||
|  | ||||
| #[actix_web::test] | ||||
| async fn should_fail_refund_for_invalid_amount() { | ||||
|     let connector = Stripe {}; | ||||
|     let response = connector | ||||
|         .make_payment_and_refund( | ||||
|             get_payment_authorize_data(), | ||||
|             Some(types::RefundsData { | ||||
|                 refund_amount: 150, | ||||
|                 ..utils::PaymentRefundType::default().0 | ||||
|             }), | ||||
|             None, | ||||
|         ) | ||||
|         .await | ||||
|         .unwrap(); | ||||
|     assert_eq!( | ||||
|         response.response.unwrap_err().message, | ||||
|         "Refund amount ($1.50) is greater than charge amount ($1.00)", | ||||
|     ); | ||||
| } | ||||
|  | ||||
| #[actix_web::test] | ||||
| async fn should_sync_refund() { | ||||
|     let connector = Stripe {}; | ||||
|     let refund_response = connector | ||||
|         .make_payment_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, | ||||
|     ); | ||||
| } | ||||
| @ -1,4 +1,4 @@ | ||||
| use std::{fmt::Debug, marker::PhantomData, thread::sleep, time::Duration}; | ||||
| use std::{fmt::Debug, marker::PhantomData, time::Duration}; | ||||
|  | ||||
| use async_trait::async_trait; | ||||
| use error_stack::Report; | ||||
| @ -18,6 +18,10 @@ pub trait Connector { | ||||
|     fn get_connector_meta(&self) -> Option<serde_json::Value> { | ||||
|         None | ||||
|     } | ||||
|     /// interval in seconds to be followed when making the subsequent request whenever needed | ||||
|     fn get_request_interval(&self) -> u64 { | ||||
|         5 | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Default, Clone)] | ||||
| @ -36,14 +40,16 @@ 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(|| types::PaymentsAuthorizeData { | ||||
|             types::PaymentsAuthorizeData { | ||||
|                 confirm: false, | ||||
|                 capture_method: Some(storage_models::enums::CaptureMethod::Manual), | ||||
|                 ..PaymentAuthorizeType::default().0 | ||||
|             }), | ||||
|                 ..(payment_data.unwrap_or(PaymentAuthorizeType::default().0)) | ||||
|             }, | ||||
|             payment_info, | ||||
|         ); | ||||
|         call_connector(request, integration).await | ||||
|     } | ||||
|  | ||||
|     async fn make_payment( | ||||
|         &self, | ||||
|         payment_data: Option<types::PaymentsAuthorizeData>, | ||||
| @ -70,25 +76,23 @@ pub trait ConnectorActions: Connector { | ||||
|         call_connector(request, integration).await | ||||
|     } | ||||
|  | ||||
|     /// will retry the psync till the given status matches or retry max 3 times in a 10secs interval | ||||
|     /// will retry the psync till the given status matches or retry max 3 times | ||||
|     async fn psync_retry_till_status_matches( | ||||
|         &self, | ||||
|         status: enums::AttemptStatus, | ||||
|         payment_data: Option<types::PaymentsSyncData>, | ||||
|         payment_info: Option<PaymentInfo>, | ||||
|     ) -> Result<types::PaymentsSyncRouterData, Report<ConnectorError>> { | ||||
|         let max_try = 3; | ||||
|         let mut curr_try = 1; | ||||
|         while curr_try <= max_try { | ||||
|         let max_tries = 3; | ||||
|         for curr_try in 0..max_tries { | ||||
|             let sync_res = self | ||||
|                 .sync_payment(payment_data.clone(), payment_info.clone()) | ||||
|                 .await | ||||
|                 .unwrap(); | ||||
|             if (sync_res.status == status) || (curr_try == max_try) { | ||||
|             if (sync_res.status == status) || (curr_try == max_tries - 1) { | ||||
|                 return Ok(sync_res); | ||||
|             } | ||||
|             sleep(Duration::from_secs(10)); | ||||
|             curr_try += 1; | ||||
|             tokio::time::sleep(Duration::from_secs(self.get_request_interval())).await; | ||||
|         } | ||||
|         Err(errors::ConnectorError::ProcessingStepFailed(None).into()) | ||||
|     } | ||||
| @ -101,17 +105,34 @@ pub trait ConnectorActions: Connector { | ||||
|     ) -> Result<types::PaymentsCaptureRouterData, Report<ConnectorError>> { | ||||
|         let integration = self.get_data().connector.get_connector_integration(); | ||||
|         let request = self.generate_data( | ||||
|             payment_data.unwrap_or(types::PaymentsCaptureData { | ||||
|                 amount_to_capture: Some(100), | ||||
|                 currency: enums::Currency::USD, | ||||
|             types::PaymentsCaptureData { | ||||
|                 connector_transaction_id: transaction_id, | ||||
|                 amount: 100, | ||||
|             }), | ||||
|                 ..payment_data.unwrap_or(PaymentCaptureType::default().0) | ||||
|             }, | ||||
|             payment_info, | ||||
|         ); | ||||
|         call_connector(request, integration).await | ||||
|     } | ||||
|  | ||||
|     async fn authorize_and_capture_payment( | ||||
|         &self, | ||||
|         authorize_data: Option<types::PaymentsAuthorizeData>, | ||||
|         capture_data: Option<types::PaymentsCaptureData>, | ||||
|         payment_info: Option<PaymentInfo>, | ||||
|     ) -> Result<types::PaymentsCaptureRouterData, Report<ConnectorError>> { | ||||
|         let authorize_response = self | ||||
|             .authorize_payment(authorize_data, payment_info.clone()) | ||||
|             .await | ||||
|             .unwrap(); | ||||
|         assert_eq!(authorize_response.status, enums::AttemptStatus::Authorized); | ||||
|         let txn_id = get_connector_transaction_id(authorize_response); | ||||
|         let response = self | ||||
|             .capture_payment(txn_id.unwrap(), capture_data, payment_info) | ||||
|             .await | ||||
|             .unwrap(); | ||||
|         return Ok(response); | ||||
|     } | ||||
|  | ||||
|     async fn void_payment( | ||||
|         &self, | ||||
|         transaction_id: String, | ||||
| @ -120,15 +141,35 @@ pub trait ConnectorActions: Connector { | ||||
|     ) -> Result<types::PaymentsCancelRouterData, Report<ConnectorError>> { | ||||
|         let integration = self.get_data().connector.get_connector_integration(); | ||||
|         let request = self.generate_data( | ||||
|             payment_data.unwrap_or(types::PaymentsCancelData { | ||||
|             types::PaymentsCancelData { | ||||
|                 connector_transaction_id: transaction_id, | ||||
|                 cancellation_reason: Some("Test cancellation".to_string()), | ||||
|             }), | ||||
|                 ..payment_data.unwrap_or(PaymentCancelType::default().0) | ||||
|             }, | ||||
|             payment_info, | ||||
|         ); | ||||
|         call_connector(request, integration).await | ||||
|     } | ||||
|  | ||||
|     async fn authorize_and_void_payment( | ||||
|         &self, | ||||
|         authorize_data: Option<types::PaymentsAuthorizeData>, | ||||
|         void_data: Option<types::PaymentsCancelData>, | ||||
|         payment_info: Option<PaymentInfo>, | ||||
|     ) -> Result<types::PaymentsCancelRouterData, Report<ConnectorError>> { | ||||
|         let authorize_response = self | ||||
|             .authorize_payment(authorize_data, payment_info.clone()) | ||||
|             .await | ||||
|             .unwrap(); | ||||
|         assert_eq!(authorize_response.status, enums::AttemptStatus::Authorized); | ||||
|         let txn_id = get_connector_transaction_id(authorize_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) | ||||
|             .await | ||||
|             .unwrap(); | ||||
|         return Ok(response); | ||||
|     } | ||||
|  | ||||
|     async fn refund_payment( | ||||
|         &self, | ||||
|         transaction_id: String, | ||||
| @ -137,24 +178,70 @@ pub trait ConnectorActions: Connector { | ||||
|     ) -> Result<types::RefundExecuteRouterData, Report<ConnectorError>> { | ||||
|         let integration = self.get_data().connector.get_connector_integration(); | ||||
|         let request = self.generate_data( | ||||
|             payment_data.unwrap_or_else(|| types::RefundsData { | ||||
|                 amount: 100, | ||||
|                 currency: enums::Currency::USD, | ||||
|                 refund_id: uuid::Uuid::new_v4().to_string(), | ||||
|             types::RefundsData { | ||||
|                 connector_transaction_id: transaction_id, | ||||
|                 refund_amount: 100, | ||||
|                 connector_metadata: None, | ||||
|                 connector_refund_id: None, | ||||
|                 reason: Some("Customer returned product".to_string()), | ||||
|             }), | ||||
|                 ..payment_data.unwrap_or(PaymentRefundType::default().0) | ||||
|             }, | ||||
|             payment_info, | ||||
|         ); | ||||
|         call_connector(request, integration).await | ||||
|     } | ||||
|  | ||||
|     async fn make_payment_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 | ||||
|             .make_payment(authorize_data, payment_info.clone()) | ||||
|             .await | ||||
|             .unwrap(); | ||||
|  | ||||
|         //try refund for previous payment | ||||
|         let transaction_id = get_connector_transaction_id(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 make_payment_and_multiple_refund( | ||||
|         &self, | ||||
|         authorize_data: Option<types::PaymentsAuthorizeData>, | ||||
|         refund_data: Option<types::RefundsData>, | ||||
|         payment_info: Option<PaymentInfo>, | ||||
|     ) { | ||||
|         //make a successful payment | ||||
|         let response = self | ||||
|             .make_payment(authorize_data, payment_info.clone()) | ||||
|             .await | ||||
|             .unwrap(); | ||||
|  | ||||
|         //try refund for previous payment | ||||
|         let transaction_id = get_connector_transaction_id(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 | ||||
|                 .refund_payment( | ||||
|                     transaction_id.clone(), | ||||
|                     refund_data.clone(), | ||||
|                     payment_info.clone(), | ||||
|                 ) | ||||
|                 .await | ||||
|                 .unwrap(); | ||||
|             assert_eq!( | ||||
|                 refund_response.response.unwrap().refund_status, | ||||
|                 enums::RefundStatus::Success, | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     async fn sync_refund( | ||||
|         &self, | ||||
|         transaction_id: String, | ||||
|         refund_id: String, | ||||
|         payment_data: Option<types::RefundsData>, | ||||
|         payment_info: Option<PaymentInfo>, | ||||
|     ) -> Result<types::RefundSyncRouterData, Report<ConnectorError>> { | ||||
| @ -164,17 +251,45 @@ pub trait ConnectorActions: Connector { | ||||
|                 amount: 1000, | ||||
|                 currency: enums::Currency::USD, | ||||
|                 refund_id: uuid::Uuid::new_v4().to_string(), | ||||
|                 connector_transaction_id: transaction_id, | ||||
|                 connector_transaction_id: "".to_string(), | ||||
|                 refund_amount: 100, | ||||
|                 connector_metadata: None, | ||||
|                 reason: None, | ||||
|                 connector_refund_id: None, | ||||
|                 connector_refund_id: Some(refund_id), | ||||
|             }), | ||||
|             payment_info, | ||||
|         ); | ||||
|         call_connector(request, integration).await | ||||
|     } | ||||
|  | ||||
|     /// will retry the rsync till the given status matches or retry max 3 times | ||||
|     async fn rsync_retry_till_status_matches( | ||||
|         &self, | ||||
|         status: enums::RefundStatus, | ||||
|         refund_id: String, | ||||
|         payment_data: Option<types::RefundsData>, | ||||
|         payment_info: Option<PaymentInfo>, | ||||
|     ) -> Result<types::RefundSyncRouterData, Report<ConnectorError>> { | ||||
|         let max_tries = 3; | ||||
|         for curr_try in 0..max_tries { | ||||
|             let sync_res = self | ||||
|                 .sync_refund( | ||||
|                     refund_id.clone(), | ||||
|                     payment_data.clone(), | ||||
|                     payment_info.clone(), | ||||
|                 ) | ||||
|                 .await | ||||
|                 .unwrap(); | ||||
|             if (sync_res.clone().response.unwrap().refund_status == status) | ||||
|                 || (curr_try == max_tries - 1) | ||||
|             { | ||||
|                 return Ok(sync_res); | ||||
|             } | ||||
|             tokio::time::sleep(Duration::from_secs(self.get_request_interval())).await; | ||||
|         } | ||||
|         Err(errors::ConnectorError::ProcessingStepFailed(None).into()) | ||||
|     } | ||||
|  | ||||
|     fn generate_data<Flow, Req: From<Req>, Res>( | ||||
|         &self, | ||||
|         req: Req, | ||||
| @ -258,6 +373,8 @@ pub trait LocalMock { | ||||
| } | ||||
|  | ||||
| pub struct PaymentAuthorizeType(pub types::PaymentsAuthorizeData); | ||||
| pub struct PaymentCaptureType(pub types::PaymentsCaptureData); | ||||
| pub struct PaymentCancelType(pub types::PaymentsCancelData); | ||||
| pub struct PaymentSyncType(pub types::PaymentsSyncData); | ||||
| pub struct PaymentRefundType(pub types::RefundsData); | ||||
| pub struct CCardType(pub api::CCard); | ||||
| @ -296,6 +413,26 @@ impl Default for PaymentAuthorizeType { | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Default for PaymentCaptureType { | ||||
|     fn default() -> Self { | ||||
|         Self(types::PaymentsCaptureData { | ||||
|             amount_to_capture: Some(100), | ||||
|             currency: enums::Currency::USD, | ||||
|             connector_transaction_id: "".to_string(), | ||||
|             amount: 100, | ||||
|         }) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Default for PaymentCancelType { | ||||
|     fn default() -> Self { | ||||
|         Self(types::PaymentsCancelData { | ||||
|             cancellation_reason: Some("requested_by_customer".to_string()), | ||||
|             connector_transaction_id: "".to_string(), | ||||
|         }) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Default for BrowserInfoType { | ||||
|     fn default() -> Self { | ||||
|         let data = types::BrowserInformation { | ||||
| @ -330,13 +467,13 @@ impl Default for PaymentSyncType { | ||||
| impl Default for PaymentRefundType { | ||||
|     fn default() -> Self { | ||||
|         let data = types::RefundsData { | ||||
|             amount: 1000, | ||||
|             amount: 100, | ||||
|             currency: enums::Currency::USD, | ||||
|             refund_id: uuid::Uuid::new_v4().to_string(), | ||||
|             connector_transaction_id: String::new(), | ||||
|             refund_amount: 100, | ||||
|             connector_metadata: None, | ||||
|             reason: None, | ||||
|             reason: Some("Customer returned product".to_string()), | ||||
|             connector_refund_id: None, | ||||
|         }; | ||||
|         Self(data) | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 Jagan
					Jagan