use std::{fmt::Debug, marker::PhantomData, str::FromStr, sync::Arc, time::Duration}; use async_trait::async_trait; use common_utils::{id_type::GenerateId, pii::Email}; use error_stack::Report; use hyperswitch_domain_models::router_data_v2::flow_common_types::PaymentFlowData; use masking::Secret; use router::{ configs::settings::Settings, core::{errors::ConnectorError, payments}, db::StorageImpl, routes, services::{ self, connector_integration_interface::{BoxedConnectorIntegrationInterface, ConnectorEnum}, }, types::{self, storage::enums, AccessToken, MinorUnit, PaymentAddress, RouterData}, }; use test_utils::connector_auth::ConnectorAuthType; use tokio::sync::oneshot; use wiremock::{Mock, MockServer}; pub trait Connector { fn get_data(&self) -> types::api::ConnectorData; fn get_auth_token(&self) -> types::ConnectorAuthType; fn get_name(&self) -> String; fn get_connector_meta(&self) -> Option { None } /// interval in seconds to be followed when making the subsequent request whenever needed fn get_request_interval(&self) -> u64 { 5 } #[cfg(feature = "payouts")] fn get_payout_data(&self) -> Option { None } } pub fn construct_connector_data_old( connector: types::api::BoxedConnector, connector_name: types::Connector, get_token: types::api::GetToken, merchant_connector_id: Option, ) -> types::api::ConnectorData { types::api::ConnectorData { connector: ConnectorEnum::Old(connector), connector_name, get_token, merchant_connector_id, } } #[derive(Debug, Default, Clone)] pub struct PaymentInfo { pub address: Option, pub auth_type: Option, pub access_token: Option, pub connector_meta_data: Option, pub connector_customer: Option, pub payment_method_token: Option, #[cfg(feature = "payouts")] pub payout_method_data: Option, #[cfg(feature = "payouts")] pub currency: Option, } impl PaymentInfo { pub fn with_default_billing_name() -> Self { Self { address: Some(PaymentAddress::new( None, None, Some(hyperswitch_domain_models::address::Address { address: Some(hyperswitch_domain_models::address::AddressDetails { first_name: Some(Secret::new("John".to_string())), last_name: Some(Secret::new("Doe".to_string())), ..Default::default() }), phone: None, email: None, }), None, )), ..Default::default() } } } #[async_trait] pub trait ConnectorActions: Connector { /// For initiating payments when `CaptureMethod` is set to `Manual` /// This doesn't complete the transaction, `PaymentsCapture` needs to be done manually async fn authorize_payment( &self, payment_data: Option, payment_info: Option, ) -> Result> { let integration = self.get_data().connector.get_connector_integration(); let request = self.generate_data( types::PaymentsAuthorizeData { confirm: true, capture_method: Some(diesel_models::enums::CaptureMethod::Manual), ..(payment_data.unwrap_or(PaymentAuthorizeType::default().0)) }, payment_info, ); Box::pin(call_connector(request, integration)).await } async fn create_connector_customer( &self, payment_data: Option, payment_info: Option, ) -> Result> { let integration: BoxedConnectorIntegrationInterface<_, PaymentFlowData, _, _> = self.get_data().connector.get_connector_integration(); let request = self.generate_data( types::ConnectorCustomerData { ..(payment_data.unwrap_or(CustomerType::default().0)) }, payment_info, ); Box::pin(call_connector(request, integration)).await } async fn create_connector_pm_token( &self, payment_data: Option, payment_info: Option, ) -> Result> { let integration = self.get_data().connector.get_connector_integration(); let request = self.generate_data( types::PaymentMethodTokenizationData { ..(payment_data.unwrap_or(TokenType::default().0)) }, payment_info, ); Box::pin(call_connector(request, integration)).await } /// For initiating payments when `CaptureMethod` is set to `Automatic` /// This does complete the transaction without user intervention to Capture the payment async fn make_payment( &self, payment_data: Option, payment_info: Option, ) -> Result> { let integration = self.get_data().connector.get_connector_integration(); let request = self.generate_data( types::PaymentsAuthorizeData { confirm: true, capture_method: Some(diesel_models::enums::CaptureMethod::Automatic), ..(payment_data.unwrap_or(PaymentAuthorizeType::default().0)) }, payment_info, ); Box::pin(call_connector(request, integration)).await } async fn sync_payment( &self, payment_data: Option, payment_info: Option, ) -> Result> { let integration = self.get_data().connector.get_connector_integration(); let request = self.generate_data( payment_data.unwrap_or_else(|| PaymentSyncType::default().0), payment_info, ); Box::pin(call_connector(request, integration)).await } /// 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, payment_info: Option, ) -> Result> { 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_tries - 1) { return Ok(sync_res); } tokio::time::sleep(Duration::from_secs(self.get_request_interval())).await; } Err(ConnectorError::ProcessingStepFailed(None).into()) } async fn capture_payment( &self, transaction_id: String, payment_data: Option, payment_info: Option, ) -> Result> { let integration = self.get_data().connector.get_connector_integration(); let request = self.generate_data( types::PaymentsCaptureData { connector_transaction_id: transaction_id, ..payment_data.unwrap_or(PaymentCaptureType::default().0) }, payment_info, ); Box::pin(call_connector(request, integration)).await } async fn authorize_and_capture_payment( &self, authorize_data: Option, capture_data: Option, payment_info: Option, ) -> Result> { 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.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, payment_data: Option, payment_info: Option, ) -> Result> { let integration = self.get_data().connector.get_connector_integration(); let request = self.generate_data( types::PaymentsCancelData { connector_transaction_id: transaction_id, ..payment_data.unwrap_or(PaymentCancelType::default().0) }, payment_info, ); Box::pin(call_connector(request, integration)).await } async fn authorize_and_void_payment( &self, authorize_data: Option, void_data: Option, payment_info: Option, ) -> Result> { 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.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, refund_data: Option, payment_info: Option, ) -> Result> { let integration = self.get_data().connector.get_connector_integration(); let request = self.generate_data( types::RefundsData { connector_transaction_id: transaction_id, ..refund_data.unwrap_or(PaymentRefundType::default().0) }, payment_info, ); Box::pin(call_connector(request, integration)).await } async fn capture_payment_and_refund( &self, authorize_data: Option, capture_data: Option, refund_data: Option, payment_info: Option, ) -> Result> { //make a successful payment let response = self .authorize_and_capture_payment(authorize_data, capture_data, payment_info.clone()) .await .unwrap(); let txn_id = self.get_connector_transaction_id_from_capture_data(response); //try refund for previous payment tokio::time::sleep(Duration::from_secs(self.get_request_interval())).await; // to avoid 404 error Ok(self .refund_payment(txn_id.unwrap(), refund_data, payment_info) .await .unwrap()) } async fn make_payment_and_refund( &self, authorize_data: Option, refund_data: Option, payment_info: Option, ) -> Result> { //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.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, refund_data: Option, payment_info: Option, ) -> Result> { //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) .await .unwrap()) } async fn make_payment_and_multiple_refund( &self, authorize_data: Option, refund_data: Option, payment_info: Option, ) { //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.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, refund_id: String, payment_data: Option, payment_info: Option, ) -> Result> { let integration = self.get_data().connector.get_connector_integration(); let request = self.generate_data( payment_data.unwrap_or_else(|| types::RefundsData { payment_amount: 1000, minor_payment_amount: MinorUnit::new(1000), currency: enums::Currency::USD, refund_id: uuid::Uuid::new_v4().to_string(), connector_transaction_id: "".to_string(), webhook_url: None, refund_amount: 100, minor_refund_amount: MinorUnit::new(100), connector_metadata: None, refund_connector_metadata: None, reason: None, connector_refund_id: Some(refund_id), browser_info: None, split_refunds: None, integrity_object: None, refund_status: enums::RefundStatus::Pending, merchant_account_id: None, merchant_config_currency: None, capture_method: None, additional_payment_method_data: None, }), payment_info, ); Box::pin(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, payment_info: Option, ) -> Result> { 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(ConnectorError::ProcessingStepFailed(None).into()) } #[cfg(feature = "payouts")] fn get_payout_request( &self, connector_payout_id: Option, payout_type: enums::PayoutType, payment_info: Option, ) -> RouterData { self.generate_data( types::PayoutsData { payout_id: common_utils::id_type::PayoutId::generate(), amount: 1, minor_amount: MinorUnit::new(1), connector_payout_id, destination_currency: payment_info.to_owned().map_or(enums::Currency::EUR, |pi| { pi.currency.map_or(enums::Currency::EUR, |c| c) }), source_currency: payment_info.to_owned().map_or(enums::Currency::EUR, |pi| { pi.currency.map_or(enums::Currency::EUR, |c| c) }), entity_type: enums::PayoutEntityType::Individual, payout_type: Some(payout_type), customer_details: Some(payments::CustomerDetails { customer_id: Some(common_utils::generate_customer_id_of_default_length()), name: Some(Secret::new("John Doe".to_string())), email: Email::from_str("john.doe@example").ok(), phone: Some(Secret::new("620874518".to_string())), phone_country_code: Some("+31".to_string()), tax_registration_id: Some("1232343243".to_string().into()), }), vendor_details: None, priority: None, connector_transfer_method_id: None, webhook_url: None, browser_info: None, }, payment_info, ) } fn generate_data, Res>( &self, req: Req, info: Option, ) -> RouterData { let merchant_id = common_utils::id_type::MerchantId::try_from(std::borrow::Cow::from(self.get_name())) .unwrap(); RouterData { flow: PhantomData, merchant_id, customer_id: Some(common_utils::generate_customer_id_of_default_length()), connector: self.get_name(), tenant_id: common_utils::id_type::TenantId::try_from_string("public".to_string()) .unwrap(), payment_id: uuid::Uuid::new_v4().to_string(), attempt_id: uuid::Uuid::new_v4().to_string(), status: enums::AttemptStatus::default(), auth_type: info .clone() .map_or(enums::AuthenticationType::NoThreeDs, |a| { a.auth_type .map_or(enums::AuthenticationType::NoThreeDs, |a| a) }), payment_method: enums::PaymentMethod::Card, connector_auth_type: self.get_auth_token(), description: Some("This is a test".to_string()), payment_method_status: None, request: req, response: Err(types::ErrorResponse::default()), address: info .clone() .and_then(|a| a.address) .or_else(|| Some(PaymentAddress::default())) .unwrap(), connector_meta_data: info .clone() .and_then(|a| a.connector_meta_data.map(Secret::new)), connector_wallets_details: None, amount_captured: None, minor_amount_captured: None, access_token: info.clone().and_then(|a| a.access_token), session_token: None, reference_id: None, payment_method_token: info.clone().and_then(|a| { a.payment_method_token .map(|token| types::PaymentMethodToken::Token(Secret::new(token))) }), connector_customer: info.clone().and_then(|a| a.connector_customer), recurring_mandate_payment_data: None, preprocessing_id: None, connector_request_reference_id: uuid::Uuid::new_v4().to_string(), #[cfg(feature = "payouts")] payout_method_data: info.and_then(|p| p.payout_method_data), #[cfg(feature = "payouts")] quote_id: None, test_mode: None, payment_method_balance: None, connector_api_version: None, connector_http_status_code: None, apple_pay_flow: None, external_latency: None, frm_metadata: None, refund_id: None, dispute_id: None, connector_response: None, integrity_check: Ok(()), additional_merchant_data: None, header_payload: None, connector_mandate_request_reference_id: None, psd2_sca_exemption_type: None, authentication_id: None, raw_connector_response: None, is_payment_id_from_merchant: None, l2_l3_data: None, minor_amount_capturable: None, } } fn get_connector_transaction_id_from_capture_data( &self, response: types::PaymentsCaptureRouterData, ) -> Option { match response.response { Ok(types::PaymentsResponseData::TransactionResponse { resource_id, .. }) => { resource_id.get_connector_transaction_id().ok() } Ok(types::PaymentsResponseData::SessionResponse { .. }) => None, Ok(types::PaymentsResponseData::SessionTokenResponse { .. }) => None, Ok(types::PaymentsResponseData::TokenizationResponse { .. }) => None, Ok(types::PaymentsResponseData::TransactionUnresolvedResponse { .. }) => None, Ok(types::PaymentsResponseData::ConnectorCustomerResponse(..)) => None, Ok(types::PaymentsResponseData::PreProcessingResponse { .. }) => None, Ok(types::PaymentsResponseData::ThreeDSEnrollmentResponse { .. }) => None, Ok(types::PaymentsResponseData::MultipleCaptureResponse { .. }) => None, Ok(types::PaymentsResponseData::IncrementalAuthorizationResponse { .. }) => None, Ok(types::PaymentsResponseData::PostProcessingResponse { .. }) => None, Ok(types::PaymentsResponseData::PaymentResourceUpdateResponse { .. }) => None, Ok(types::PaymentsResponseData::PaymentsCreateOrderResponse { .. }) => None, Err(_) => None, } } #[cfg(feature = "payouts")] async fn verify_payout_eligibility( &self, payout_type: enums::PayoutType, payment_info: Option, ) -> Result> { let connector_integration: services::BoxedPayoutConnectorIntegrationInterface< types::api::PoEligibility, types::PayoutsData, types::PayoutsResponseData, > = self .get_payout_data() .ok_or(ConnectorError::FailedToObtainPreferredConnector)? .connector .get_connector_integration(); let request = self.get_payout_request(None, payout_type, payment_info); let tx: oneshot::Sender<()> = oneshot::channel().0; let app_state = Box::pin(routes::AppState::with_storage( Settings::new().unwrap(), StorageImpl::PostgresqlTest, tx, Box::new(services::MockApiClient), )) .await; let state = Arc::new(app_state) .get_session_state( &common_utils::id_type::TenantId::try_from_string("public".to_string()).unwrap(), None, || {}, ) .unwrap(); let res = services::api::execute_connector_processing_step( &state, connector_integration, &request, payments::CallConnectorAction::Trigger, None, None, ) .await?; Ok(res.response.unwrap()) } #[cfg(feature = "payouts")] async fn fulfill_payout( &self, connector_payout_id: Option, payout_type: enums::PayoutType, payment_info: Option, ) -> Result> { let connector_integration: services::BoxedPayoutConnectorIntegrationInterface< types::api::PoFulfill, types::PayoutsData, types::PayoutsResponseData, > = self .get_payout_data() .ok_or(ConnectorError::FailedToObtainPreferredConnector)? .connector .get_connector_integration(); let request = self.get_payout_request(connector_payout_id, payout_type, payment_info); let tx: oneshot::Sender<()> = oneshot::channel().0; let app_state = Box::pin(routes::AppState::with_storage( Settings::new().unwrap(), StorageImpl::PostgresqlTest, tx, Box::new(services::MockApiClient), )) .await; let state = Arc::new(app_state) .get_session_state( &common_utils::id_type::TenantId::try_from_string("public".to_string()).unwrap(), None, || {}, ) .unwrap(); let res = services::api::execute_connector_processing_step( &state, connector_integration, &request, payments::CallConnectorAction::Trigger, None, None, ) .await?; Ok(res.response.unwrap()) } #[cfg(feature = "payouts")] async fn create_payout( &self, connector_customer: Option, payout_type: enums::PayoutType, payment_info: Option, ) -> Result> { let connector_integration: services::BoxedPayoutConnectorIntegrationInterface< types::api::PoCreate, types::PayoutsData, types::PayoutsResponseData, > = self .get_payout_data() .ok_or(ConnectorError::FailedToObtainPreferredConnector)? .connector .get_connector_integration(); let mut request = self.get_payout_request(None, payout_type, payment_info); request.connector_customer = connector_customer; let tx: oneshot::Sender<()> = oneshot::channel().0; let app_state = Box::pin(routes::AppState::with_storage( Settings::new().unwrap(), StorageImpl::PostgresqlTest, tx, Box::new(services::MockApiClient), )) .await; let state = Arc::new(app_state) .get_session_state( &common_utils::id_type::TenantId::try_from_string("public".to_string()).unwrap(), None, || {}, ) .unwrap(); let res = services::api::execute_connector_processing_step( &state, connector_integration, &request, payments::CallConnectorAction::Trigger, None, None, ) .await?; Ok(res.response.unwrap()) } #[cfg(feature = "payouts")] async fn cancel_payout( &self, connector_payout_id: String, payout_type: enums::PayoutType, payment_info: Option, ) -> Result> { let connector_integration: services::BoxedPayoutConnectorIntegrationInterface< types::api::PoCancel, types::PayoutsData, types::PayoutsResponseData, > = self .get_payout_data() .ok_or(ConnectorError::FailedToObtainPreferredConnector)? .connector .get_connector_integration(); let request = self.get_payout_request(Some(connector_payout_id), payout_type, payment_info); let tx: oneshot::Sender<()> = oneshot::channel().0; let app_state = Box::pin(routes::AppState::with_storage( Settings::new().unwrap(), StorageImpl::PostgresqlTest, tx, Box::new(services::MockApiClient), )) .await; let state = Arc::new(app_state) .get_session_state( &common_utils::id_type::TenantId::try_from_string("public".to_string()).unwrap(), None, || {}, ) .unwrap(); let res = services::api::execute_connector_processing_step( &state, connector_integration, &request, payments::CallConnectorAction::Trigger, None, None, ) .await?; Ok(res.response.unwrap()) } #[cfg(feature = "payouts")] async fn create_and_fulfill_payout( &self, connector_customer: Option, payout_type: enums::PayoutType, payment_info: Option, ) -> Result> { let create_res = self .create_payout(connector_customer, payout_type, payment_info.to_owned()) .await?; assert_eq!( create_res.status.unwrap(), enums::PayoutStatus::RequiresFulfillment ); let fulfill_res = self .fulfill_payout( create_res.connector_payout_id, payout_type, payment_info.to_owned(), ) .await?; Ok(fulfill_res) } #[cfg(feature = "payouts")] async fn create_and_cancel_payout( &self, connector_customer: Option, payout_type: enums::PayoutType, payment_info: Option, ) -> Result> { let create_res = self .create_payout(connector_customer, payout_type, payment_info.to_owned()) .await?; assert_eq!( create_res.status.unwrap(), enums::PayoutStatus::RequiresFulfillment ); let cancel_res = self .cancel_payout( create_res .connector_payout_id .ok_or(ConnectorError::MissingRequiredField { field_name: "connector_payout_id", })?, payout_type, payment_info.to_owned(), ) .await?; Ok(cancel_res) } #[cfg(feature = "payouts")] async fn create_payout_recipient( &self, payout_type: enums::PayoutType, payment_info: Option, ) -> Result> { let connector_integration: services::BoxedPayoutConnectorIntegrationInterface< types::api::PoRecipient, types::PayoutsData, types::PayoutsResponseData, > = self .get_payout_data() .ok_or(ConnectorError::FailedToObtainPreferredConnector)? .connector .get_connector_integration(); let request = self.get_payout_request(None, payout_type, payment_info); let tx = oneshot::channel().0; let app_state = Box::pin(routes::AppState::with_storage( Settings::new().unwrap(), StorageImpl::PostgresqlTest, tx, Box::new(services::MockApiClient), )) .await; let state = Arc::new(app_state) .get_session_state( &common_utils::id_type::TenantId::try_from_string("public".to_string()).unwrap(), None, || {}, ) .unwrap(); let res = services::api::execute_connector_processing_step( &state, connector_integration, &request, payments::CallConnectorAction::Trigger, None, None, ) .await?; Ok(res.response.unwrap()) } } async fn call_connector< T: Debug + Clone + 'static, ResourceCommonData: Debug + Clone + services::connector_integration_interface::RouterDataConversion + 'static, Req: Debug + Clone + 'static, Resp: Debug + Clone + 'static, >( request: RouterData, integration: BoxedConnectorIntegrationInterface, ) -> Result, Report> { let conf = Settings::new().unwrap(); let tx: oneshot::Sender<()> = oneshot::channel().0; let app_state = Box::pin(routes::AppState::with_storage( conf, StorageImpl::PostgresqlTest, tx, Box::new(services::MockApiClient), )) .await; let state = Arc::new(app_state) .get_session_state( &common_utils::id_type::TenantId::try_from_string("public".to_string()).unwrap(), None, || {}, ) .unwrap(); services::api::execute_connector_processing_step( &state, integration, &request, payments::CallConnectorAction::Trigger, None, None, ) .await } pub struct MockConfig { pub address: Option, pub mocks: Vec, } #[async_trait] pub trait LocalMock { async fn start_server(&self, config: MockConfig) -> MockServer { let address = config .address .unwrap_or_else(|| "127.0.0.1:9090".to_string()); let listener = std::net::TcpListener::bind(address).unwrap(); let expected_server_address = listener .local_addr() .expect("Failed to get server address."); let mock_server = MockServer::builder().listener(listener).start().await; assert_eq!(&expected_server_address, mock_server.address()); for mock in config.mocks { mock_server.register(mock).await; } mock_server } } 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 types::domain::Card); pub struct BrowserInfoType(pub types::BrowserInformation); pub struct CustomerType(pub types::ConnectorCustomerData); pub struct TokenType(pub types::PaymentMethodTokenizationData); impl Default for CCardType { fn default() -> Self { Self(types::domain::Card { card_number: cards::CardNumber::from_str("4200000000000000").unwrap(), card_exp_month: Secret::new("10".to_string()), card_exp_year: Secret::new("2025".to_string()), card_cvc: Secret::new("999".to_string()), card_issuer: None, card_network: None, card_type: None, card_issuing_country: None, bank_code: None, nick_name: Some(Secret::new("nick_name".into())), card_holder_name: Some(Secret::new("card holder name".into())), co_badged_card_data: None, }) } } impl Default for PaymentAuthorizeType { fn default() -> Self { let data = types::PaymentsAuthorizeData { payment_method_data: types::domain::PaymentMethodData::Card(CCardType::default().0), amount: 100, minor_amount: MinorUnit::new(100), order_tax_amount: Some(MinorUnit::zero()), currency: enums::Currency::USD, confirm: true, statement_descriptor_suffix: None, statement_descriptor: None, capture_method: None, setup_future_usage: None, mandate_id: None, off_session: None, setup_mandate_details: None, browser_info: Some(BrowserInfoType::default().0), order_details: None, order_category: None, email: None, customer_name: None, session_token: None, enrolled_for_3ds: false, related_transaction_id: None, payment_experience: None, payment_method_type: None, router_return_url: None, complete_authorize_url: None, webhook_url: None, customer_id: None, surcharge_details: None, request_incremental_authorization: false, request_extended_authorization: None, metadata: None, authentication_data: None, customer_acceptance: None, split_payments: None, integrity_object: None, merchant_order_reference_id: None, additional_payment_method_data: None, shipping_cost: None, merchant_account_id: None, merchant_config_currency: None, connector_testing_data: None, order_id: None, locale: None, payment_channel: None, enable_partial_authorization: None, enable_overcapture: None, is_stored_credential: None, }; Self(data) } } impl Default for PaymentCaptureType { fn default() -> Self { Self(types::PaymentsCaptureData { amount_to_capture: 100, currency: enums::Currency::USD, connector_transaction_id: "".to_string(), payment_amount: 100, ..Default::default() }) } } impl Default for PaymentCancelType { fn default() -> Self { Self(types::PaymentsCancelData { cancellation_reason: Some("requested_by_customer".to_string()), connector_transaction_id: "".to_string(), ..Default::default() }) } } impl Default for BrowserInfoType { fn default() -> Self { let data = types::BrowserInformation { user_agent: Some("".to_string()), accept_header: Some("".to_string()), language: Some("nl-NL".to_string()), color_depth: Some(24), screen_height: Some(723), screen_width: Some(1536), time_zone: Some(0), java_enabled: Some(true), java_script_enabled: Some(true), ip_address: Some("127.0.0.1".parse().unwrap()), device_model: Some("Apple IPHONE 7".to_string()), os_type: Some("IOS or ANDROID".to_string()), os_version: Some("IOS 14.5".to_string()), accept_language: Some("en".to_string()), referer: None, }; Self(data) } } impl Default for PaymentSyncType { fn default() -> Self { let data = types::PaymentsSyncData { mandate_id: None, connector_transaction_id: types::ResponseId::ConnectorTransactionId( "12345".to_string(), ), encoded_data: None, capture_method: None, sync_type: types::SyncRequestType::SinglePaymentSync, connector_meta: None, payment_method_type: None, currency: enums::Currency::USD, payment_experience: None, amount: MinorUnit::new(100), integrity_object: None, ..Default::default() }; Self(data) } } impl Default for PaymentRefundType { fn default() -> Self { let data = types::RefundsData { payment_amount: 100, minor_payment_amount: MinorUnit::new(100), currency: enums::Currency::USD, refund_id: uuid::Uuid::new_v4().to_string(), connector_transaction_id: String::new(), refund_amount: 100, minor_refund_amount: MinorUnit::new(100), webhook_url: None, connector_metadata: None, refund_connector_metadata: None, reason: Some("Customer returned product".to_string()), connector_refund_id: None, browser_info: None, split_refunds: None, integrity_object: None, refund_status: enums::RefundStatus::Pending, merchant_account_id: None, merchant_config_currency: None, capture_method: None, additional_payment_method_data: None, }; Self(data) } } impl Default for CustomerType { fn default() -> Self { let data = types::ConnectorCustomerData { payment_method_data: Some(types::domain::PaymentMethodData::Card( CCardType::default().0, )), description: None, email: Email::from_str("test@juspay.in").ok(), phone: None, name: None, preprocessing_id: None, split_payments: None, customer_acceptance: None, setup_future_usage: None, customer_id: None, billing_address: None, }; Self(data) } } impl Default for TokenType { fn default() -> Self { let data = types::PaymentMethodTokenizationData { payment_method_data: types::domain::PaymentMethodData::Card(CCardType::default().0), browser_info: None, amount: Some(100), currency: enums::Currency::USD, split_payments: None, mandate_id: None, setup_future_usage: None, customer_acceptance: None, setup_mandate_details: None, }; Self(data) } } pub fn get_connector_transaction_id( response: Result, ) -> Option { match response { Ok(types::PaymentsResponseData::TransactionResponse { resource_id, .. }) => { resource_id.get_connector_transaction_id().ok() } Ok(types::PaymentsResponseData::SessionResponse { .. }) => None, Ok(types::PaymentsResponseData::SessionTokenResponse { .. }) => None, Ok(types::PaymentsResponseData::TokenizationResponse { .. }) => None, Ok(types::PaymentsResponseData::TransactionUnresolvedResponse { .. }) => None, Ok(types::PaymentsResponseData::PreProcessingResponse { .. }) => None, Ok(types::PaymentsResponseData::ConnectorCustomerResponse(..)) => None, Ok(types::PaymentsResponseData::ThreeDSEnrollmentResponse { .. }) => None, Ok(types::PaymentsResponseData::MultipleCaptureResponse { .. }) => None, Ok(types::PaymentsResponseData::IncrementalAuthorizationResponse { .. }) => None, Ok(types::PaymentsResponseData::PostProcessingResponse { .. }) => None, Ok(types::PaymentsResponseData::PaymentResourceUpdateResponse { .. }) => None, Ok(types::PaymentsResponseData::PaymentsCreateOrderResponse { .. }) => None, Err(_) => None, } } pub fn get_connector_metadata( response: Result, ) -> Option { match response { Ok(types::PaymentsResponseData::TransactionResponse { resource_id: _, redirection_data: _, mandate_reference: _, connector_metadata, network_txn_id: _, connector_response_reference_id: _, incremental_authorization_allowed: _, charges: _, }) => connector_metadata, _ => None, } } pub fn to_connector_auth_type(auth_type: ConnectorAuthType) -> types::ConnectorAuthType { match auth_type { ConnectorAuthType::HeaderKey { api_key } => types::ConnectorAuthType::HeaderKey { api_key }, ConnectorAuthType::BodyKey { api_key, key1 } => { types::ConnectorAuthType::BodyKey { api_key, key1 } } ConnectorAuthType::SignatureKey { api_key, key1, api_secret, } => types::ConnectorAuthType::SignatureKey { api_key, key1, api_secret, }, ConnectorAuthType::MultiAuthKey { api_key, key1, api_secret, key2, } => types::ConnectorAuthType::MultiAuthKey { api_key, key1, api_secret, key2, }, _ => types::ConnectorAuthType::NoKey, } }