diff --git a/crates/router/src/connector/bluesnap.rs b/crates/router/src/connector/bluesnap.rs index 6ffc2a6dc4e..60a9025ef00 100644 --- a/crates/router/src/connector/bluesnap.rs +++ b/crates/router/src/connector/bluesnap.rs @@ -23,7 +23,6 @@ use crate::{ errors::{self, CustomResult}, payments, }, - db::StorageInterface, headers, logger, services::{ self, @@ -33,7 +32,7 @@ use crate::{ types::{ self, api::{self, ConnectorCommon, ConnectorCommonExt}, - domain, ErrorResponse, Response, + ErrorResponse, Response, }, utils::{self, BytesExt}, }; @@ -975,19 +974,17 @@ impl api::IncomingWebhook for Bluesnap { &self, _request: &api::IncomingWebhookRequestDetails<'_>, ) -> CustomResult, errors::ConnectorError> { - Ok(Box::new(crypto::Md5)) + Ok(Box::new(crypto::HmacSha256)) } fn get_webhook_source_verification_signature( &self, request: &api::IncomingWebhookRequestDetails<'_>, ) -> CustomResult, errors::ConnectorError> { - let webhook_body: bluesnap::BluesnapWebhookBody = - serde_urlencoded::from_bytes(request.body) - .into_report() - .change_context(errors::ConnectorError::WebhookSignatureNotFound)?; - let signature = webhook_body.auth_key; - hex::decode(signature) + let security_header = + connector_utils::get_header_key_value("bls-signature", request.headers)?; + + hex::decode(security_header) .into_report() .change_context(errors::ConnectorError::WebhookSignatureNotFound) } @@ -997,51 +994,10 @@ impl api::IncomingWebhook for Bluesnap { _merchant_id: &str, _secret: &[u8], ) -> CustomResult, errors::ConnectorError> { - let webhook_body: bluesnap::BluesnapWebhookBody = - serde_urlencoded::from_bytes(request.body) - .into_report() - .change_context(errors::ConnectorError::WebhookSignatureNotFound)?; - let msg = webhook_body.reference_number + webhook_body.contract_id.as_str(); - Ok(msg.into_bytes()) - } + let timestamp = + connector_utils::get_header_key_value("bls-ipn-timestamp", request.headers)?; - async fn verify_webhook_source( - &self, - db: &dyn StorageInterface, - request: &api::IncomingWebhookRequestDetails<'_>, - merchant_account: &domain::MerchantAccount, - connector_label: &str, - key_store: &domain::MerchantKeyStore, - object_reference_id: api_models::webhooks::ObjectReferenceId, - ) -> CustomResult { - let algorithm = self - .get_webhook_source_verification_algorithm(request) - .change_context(errors::ConnectorError::WebhookSourceVerificationFailed)?; - - let signature = self - .get_webhook_source_verification_signature(request) - .change_context(errors::ConnectorError::WebhookSourceVerificationFailed)?; - let mut secret = self - .get_webhook_source_verification_merchant_secret( - db, - merchant_account, - connector_label, - key_store, - object_reference_id, - ) - .await - .change_context(errors::ConnectorError::WebhookSourceVerificationFailed)?; - let mut message = self - .get_webhook_source_verification_message( - request, - &merchant_account.merchant_id, - &secret, - ) - .change_context(errors::ConnectorError::WebhookSourceVerificationFailed)?; - message.append(&mut secret); - algorithm - .verify_signature(&secret, &signature, &message) - .change_context(errors::ConnectorError::WebhookSourceVerificationFailed) + Ok(format!("{}{}", timestamp, String::from_utf8_lossy(request.body)).into_bytes()) } fn get_webhook_object_reference_id( @@ -1053,8 +1009,8 @@ impl api::IncomingWebhook for Bluesnap { .into_report() .change_context(errors::ConnectorError::WebhookSignatureNotFound)?; Ok(api_models::webhooks::ObjectReferenceId::PaymentId( - api_models::payments::PaymentIdType::ConnectorTransactionId( - webhook_body.reference_number, + api_models::payments::PaymentIdType::PaymentAttemptId( + webhook_body.merchant_transaction_id, ), )) } @@ -1090,8 +1046,34 @@ impl api::IncomingWebhook for Bluesnap { serde_urlencoded::from_bytes(request.body) .into_report() .change_context(errors::ConnectorError::WebhookResourceObjectNotFound)?; + + let (card_transaction_type, processing_status) = match details.transaction_type { + bluesnap::BluesnapWebhookEvents::Decline + | bluesnap::BluesnapWebhookEvents::CcChargeFailed => Ok(( + bluesnap::BluesnapTxnType::Capture, + bluesnap::BluesnapProcessingStatus::Fail, + )), + bluesnap::BluesnapWebhookEvents::Charge => Ok(( + bluesnap::BluesnapTxnType::Capture, + bluesnap::BluesnapProcessingStatus::Success, + )), + bluesnap::BluesnapWebhookEvents::Unknown => { + Err(errors::ConnectorError::WebhookEventTypeNotFound) + } + }?; + + let psync_struct = bluesnap::BluesnapPaymentsResponse { + processing_info: bluesnap::ProcessingInfoResponse { + processing_status, + authorization_code: None, + network_transaction_id: None, + }, + transaction_id: details.reference_number, + card_transaction_type, + }; + let res_json = - utils::Encode::::encode_to_value(&details) + utils::Encode::::encode_to_value(&psync_struct) .change_context(errors::ConnectorError::WebhookResourceObjectNotFound)?; Ok(res_json) diff --git a/crates/router/src/connector/bluesnap/transformers.rs b/crates/router/src/connector/bluesnap/transformers.rs index 3c629d3abfb..e50e7a64704 100644 --- a/crates/router/src/connector/bluesnap/transformers.rs +++ b/crates/router/src/connector/bluesnap/transformers.rs @@ -32,6 +32,7 @@ pub struct BluesnapPaymentsRequest { three_d_secure: Option, transaction_fraud_info: Option, card_holder_info: Option, + merchant_transaction_id: Option, } #[derive(Debug, Serialize)] @@ -318,6 +319,7 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for BluesnapPaymentsRequest { fraud_session_id: item.payment_id.clone(), }), card_holder_info, + merchant_transaction_id: Some(item.connector_request_reference_id.clone()), }) } } @@ -485,6 +487,7 @@ impl TryFrom<&types::PaymentsCompleteAuthorizeRouterData> for BluesnapPaymentsRe item.get_billing_address()?, item.request.get_email()?, )?, + merchant_transaction_id: Some(item.connector_request_reference_id.clone()), }) } } @@ -679,9 +682,9 @@ impl From for enums::RefundStatus { #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct BluesnapPaymentsResponse { - processing_info: ProcessingInfoResponse, - transaction_id: String, - card_transaction_type: BluesnapTxnType, + pub processing_info: ProcessingInfoResponse, + pub transaction_id: String, + pub card_transaction_type: BluesnapTxnType, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -701,9 +704,9 @@ pub struct Refund { #[derive(Default, Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct ProcessingInfoResponse { - processing_status: BluesnapProcessingStatus, - authorization_code: Option, - network_transaction_id: Option>, + pub processing_status: BluesnapProcessingStatus, + pub authorization_code: Option, + pub network_transaction_id: Option>, } impl @@ -802,8 +805,7 @@ impl TryFrom> #[derive(Debug, Clone, Deserialize)] #[serde(rename_all = "camelCase")] pub struct BluesnapWebhookBody { - pub auth_key: String, - pub contract_id: String, + pub merchant_transaction_id: String, pub reference_number: String, } @@ -822,12 +824,11 @@ pub enum BluesnapWebhookEvents { #[serde(other)] Unknown, } -#[derive(Default, Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub struct BluesnapWebhookObjectResource { - pub auth_key: String, - pub contract_id: String, pub reference_number: String, + pub transaction_type: BluesnapWebhookEvents, } #[derive(Default, Debug, Clone, Serialize, Deserialize)] diff --git a/crates/router/tests/connectors/bluesnap.rs b/crates/router/tests/connectors/bluesnap.rs index ea3fe9a1fe2..b54261e77c7 100644 --- a/crates/router/tests/connectors/bluesnap.rs +++ b/crates/router/tests/connectors/bluesnap.rs @@ -411,7 +411,7 @@ async fn should_fail_payment_for_incorrect_cvc() { .unwrap(); assert_eq!( response.response.unwrap_err().message, - "Order creation failure due to problematic input.".to_string(), + "VALIDATION_GENERAL_FAILURE".to_string(), ); } @@ -437,7 +437,7 @@ async fn should_fail_payment_for_invalid_exp_month() { .unwrap(); assert_eq!( response.response.unwrap_err().message, - "Order creation failure due to problematic input.".to_string(), + "VALIDATION_GENERAL_FAILURE".to_string(), ); } @@ -463,7 +463,7 @@ async fn should_fail_payment_for_incorrect_expiry_year() { .unwrap(); assert_eq!( response.response.unwrap_err().message, - "Order creation failure due to problematic input.".to_string(), + "VALIDATION_GENERAL_FAILURE".to_string(), ); } @@ -485,7 +485,7 @@ async fn should_fail_void_payment_for_auto_capture() { .unwrap(); assert_eq!( void_response.response.unwrap_err().message, - "Transaction AUTH_REVERSAL failed. Transaction has already been captured." + "TRANSACTION_ALREADY_CAPTURED" ); } @@ -524,7 +524,7 @@ async fn should_fail_for_refund_amount_higher_than_payment_amount() { .unwrap(); assert_eq!( response.response.unwrap_err().message, - "Refund amount cannot be more than the refundable order amount.", + "REFUND_MAX_AMOUNT_FAILURE", ); }