diff --git a/crates/router/src/connector/prophetpay.rs b/crates/router/src/connector/prophetpay.rs index e5ebe6331b..efe87bcefd 100644 --- a/crates/router/src/connector/prophetpay.rs +++ b/crates/router/src/connector/prophetpay.rs @@ -107,16 +107,15 @@ impl ConnectorCommon for Prophetpay { &self, res: Response, ) -> CustomResult { - let response: prophetpay::ProphetpayErrorResponse = res + let response: serde_json::Value = res .response - .parse_struct("ProphetpayErrorResponse") + .parse_struct("ProphetPayErrorResponse") .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; - Ok(ErrorResponse { status_code: res.status_code, - code: response.status.to_string(), - message: response.title, - reason: Some(response.errors.to_string()), + code: consts::NO_ERROR_CODE.to_string(), + message: consts::NO_ERROR_MESSAGE.to_string(), + reason: Some(response.to_string()), attempt_status: None, }) } @@ -324,7 +323,7 @@ impl where types::PaymentsResponseData: Clone, { - let response: prophetpay::ProphetpayResponse = res + let response: prophetpay::ProphetpayCompleteAuthResponse = res .response .parse_struct("prophetpay ProphetpayResponse") .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; @@ -407,9 +406,9 @@ impl ConnectorIntegration CustomResult { - let response: prophetpay::ProphetpayResponse = res + let response: prophetpay::ProphetpaySyncResponse = res .response - .parse_struct("prophetpay ProphetpayResponse") + .parse_struct("prophetpay PaymentsSyncResponse") .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; types::RouterData::try_from(types::ResponseRouterData { response, @@ -431,9 +430,12 @@ impl ConnectorIntegration for Prophetpay { + /* fn get_headers( &self, req: &types::PaymentsCancelRouterData, @@ -471,33 +473,25 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { - Ok(Some( - services::RequestBuilder::new() - .method(services::Method::Get) - .url(&types::PaymentsVoidType::get_url(self, req, connectors)?) - .attach_default_headers() - .headers(types::PaymentsVoidType::get_headers(self, req, connectors)?) - .body(types::PaymentsVoidType::get_request_body( - self, req, connectors, - )?) - .build(), - )) + Err(errors::ConnectorError::NotImplemented("Void flow not implemented".to_string()).into()) } + /* fn handle_response( &self, data: &types::PaymentsCancelRouterData, res: Response, ) -> CustomResult { - let response: prophetpay::ProphetpayResponse = res + let response: prophetpay::ProphetpayVoidResponse = res .response - .parse_struct("prophetpay ProphetpayResponse") + .parse_struct("prophetpay PaymentsCancelResponse") .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; types::RouterData::try_from(types::ResponseRouterData { response, @@ -512,6 +506,7 @@ impl ConnectorIntegration CustomResult { self.build_error_response(res) } + */ } impl ConnectorIntegration @@ -652,7 +647,7 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { Ok(Some( services::RequestBuilder::new() - .method(services::Method::Get) + .method(services::Method::Post) .url(&types::RefundSyncType::get_url(self, req, connectors)?) .attach_default_headers() .headers(types::RefundSyncType::get_headers(self, req, connectors)?) @@ -668,7 +663,7 @@ impl ConnectorIntegration CustomResult { - let response: prophetpay::ProphetpayRefundResponse = res + let response: prophetpay::ProphetpayRefundSyncResponse = res .response .parse_struct("prophetpay ProphetpayRefundResponse") .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; diff --git a/crates/router/src/connector/prophetpay/transformers.rs b/crates/router/src/connector/prophetpay/transformers.rs index 74071d5b85..b8cf3e3a1f 100644 --- a/crates/router/src/connector/prophetpay/transformers.rs +++ b/crates/router/src/connector/prophetpay/transformers.rs @@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize}; use url::Url; use crate::{ - connector::utils, + connector::utils::{self, to_connector_meta}, core::errors, services, types::{self, api, storage::enums}, @@ -159,7 +159,11 @@ impl TryFrom<&ProphetpayRouterData<&types::PaymentsAuthorizeRouterData>> ), } } else { - Err(errors::ConnectorError::NotImplemented("Payment methods".to_string()).into()) + Err(errors::ConnectorError::CurrencyNotSupported { + message: item.router_data.request.currency.to_string(), + connector: "Prophetpay", + } + .into()) } } } @@ -266,10 +270,7 @@ impl TryFrom<&ProphetpayRouterData<&types::PaymentsCompleteAuthorizeRouterData>> Ok(Self { amount: item.amount.to_owned(), ref_info: item.router_data.connector_request_reference_id.to_owned(), - inquiry_reference: format!( - "inquiry_{}", - item.router_data.connector_request_reference_id - ), + inquiry_reference: item.router_data.connector_request_reference_id.clone(), profile: auth_data.profile_id, action_type: ProphetpayActionType::get_action_type(&ProphetpayActionType::Charge), card_token, @@ -346,8 +347,8 @@ impl TryFrom<&types::PaymentsSyncRouterData> for ProphetpaySyncRequest { .change_context(errors::ConnectorError::MissingConnectorTransactionID)?; Ok(Self { transaction_id, - ref_info: item.attempt_id.to_owned(), - inquiry_reference: format!("inquiry_{}", item.attempt_id), + ref_info: item.connector_request_reference_id.to_owned(), + inquiry_reference: item.connector_request_reference_id.clone(), profile: auth_data.profile_id, action_type: ProphetpayActionType::get_action_type(&ProphetpayActionType::Inquiry), }) @@ -355,66 +356,170 @@ impl TryFrom<&types::PaymentsSyncRouterData> for ProphetpaySyncRequest { } #[derive(Debug, Clone, Deserialize)] -pub enum ProphetpayPaymentStatus { - Success, - #[serde(rename = "Transaction Approved")] - Charged, - Failure, - #[serde(rename = "Transaction Voided")] - Voided, - #[serde(rename = "Requires a card on file.")] - CardTokenNotFound, - #[serde(rename = "RefInfo and InquiryReference are duplicated")] - DuplicateValue, - #[serde(rename = "Profile is missing")] - MissingProfile, - #[serde(rename = "RefInfo is empty.")] - EmptyRef, +#[serde(rename_all = "camelCase")] +pub struct ProphetpayCompleteAuthResponse { + pub success: bool, + pub response_text: String, + #[serde(rename = "transactionID")] + pub transaction_id: String, + pub response_code: String, } -impl From for enums::AttemptStatus { - fn from(item: ProphetpayPaymentStatus) -> Self { - match item { - ProphetpayPaymentStatus::Success | ProphetpayPaymentStatus::Charged => Self::Charged, - ProphetpayPaymentStatus::Failure - | ProphetpayPaymentStatus::CardTokenNotFound - | ProphetpayPaymentStatus::DuplicateValue - | ProphetpayPaymentStatus::MissingProfile - | ProphetpayPaymentStatus::EmptyRef => Self::Failure, - ProphetpayPaymentStatus::Voided => Self::Voided, +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ProphetpayCardTokenData { + card_token: Secret, +} + +impl + TryFrom< + types::ResponseRouterData< + F, + ProphetpayCompleteAuthResponse, + types::CompleteAuthorizeData, + types::PaymentsResponseData, + >, + > for types::RouterData +{ + type Error = error_stack::Report; + fn try_from( + item: types::ResponseRouterData< + F, + ProphetpayCompleteAuthResponse, + types::CompleteAuthorizeData, + types::PaymentsResponseData, + >, + ) -> Result { + if item.response.success { + let card_token = get_card_token(item.data.request.redirect_response.clone())?; + let card_token_data = ProphetpayCardTokenData { + card_token: Secret::from(card_token), + }; + let connector_metadata = serde_json::to_value(card_token_data).ok(); + Ok(Self { + status: enums::AttemptStatus::Charged, + response: Ok(types::PaymentsResponseData::TransactionResponse { + resource_id: types::ResponseId::ConnectorTransactionId( + item.response.transaction_id, + ), + redirection_data: None, + mandate_reference: None, + connector_metadata, + network_txn_id: None, + connector_response_reference_id: None, + }), + ..item.data + }) + } else { + Ok(Self { + status: enums::AttemptStatus::Failure, + response: Err(types::ErrorResponse { + code: item.response.response_code, + message: item.response.response_text.clone(), + reason: Some(item.response.response_text), + status_code: item.http_code, + attempt_status: None, + }), + ..item.data + }) } } } #[derive(Debug, Clone, Deserialize)] #[serde(rename_all = "camelCase")] -pub struct ProphetpayResponse { - pub response_text: ProphetpayPaymentStatus, +pub struct ProphetpaySyncResponse { + success: bool, + pub response_text: String, #[serde(rename = "transactionID")] pub transaction_id: String, + pub response_code: String, } -impl TryFrom> +impl + TryFrom> for types::RouterData { type Error = error_stack::Report; fn try_from( - item: types::ResponseRouterData, + item: types::ResponseRouterData, ) -> Result { - Ok(Self { - status: enums::AttemptStatus::from(item.response.response_text), - response: Ok(types::PaymentsResponseData::TransactionResponse { - resource_id: types::ResponseId::ConnectorTransactionId( - item.response.transaction_id, - ), - redirection_data: None, - mandate_reference: None, - connector_metadata: None, - network_txn_id: None, - connector_response_reference_id: None, - }), - ..item.data - }) + if item.response.success { + Ok(Self { + status: enums::AttemptStatus::Charged, + response: Ok(types::PaymentsResponseData::TransactionResponse { + resource_id: types::ResponseId::ConnectorTransactionId( + item.response.transaction_id, + ), + redirection_data: None, + mandate_reference: None, + connector_metadata: None, + network_txn_id: None, + connector_response_reference_id: None, + }), + ..item.data + }) + } else { + Ok(Self { + status: enums::AttemptStatus::Failure, + response: Err(types::ErrorResponse { + code: item.response.response_code, + message: item.response.response_text.clone(), + reason: Some(item.response.response_text), + status_code: item.http_code, + attempt_status: None, + }), + ..item.data + }) + } + } +} + +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ProphetpayVoidResponse { + pub success: bool, + pub response_text: String, + #[serde(rename = "transactionID")] + pub transaction_id: String, + pub response_code: String, +} + +impl + TryFrom> + for types::RouterData +{ + type Error = error_stack::Report; + fn try_from( + item: types::ResponseRouterData, + ) -> Result { + if item.response.success { + Ok(Self { + status: enums::AttemptStatus::Voided, + response: Ok(types::PaymentsResponseData::TransactionResponse { + resource_id: types::ResponseId::ConnectorTransactionId( + item.response.transaction_id, + ), + redirection_data: None, + mandate_reference: None, + connector_metadata: None, + network_txn_id: None, + connector_response_reference_id: None, + }), + ..item.data + }) + } else { + Ok(Self { + status: enums::AttemptStatus::VoidFailed, + response: Err(types::ErrorResponse { + code: item.response.response_code, + message: item.response.response_text.clone(), + reason: Some(item.response.response_text), + status_code: item.http_code, + attempt_status: None, + }), + ..item.data + }) + } } } @@ -435,8 +540,8 @@ impl TryFrom<&types::PaymentsCancelRouterData> for ProphetpayVoidRequest { let transaction_id = item.request.connector_transaction_id.to_owned(); Ok(Self { transaction_id, - ref_info: item.attempt_id.to_owned(), - inquiry_reference: format!("inquiry_{}", item.attempt_id), + ref_info: item.connector_request_reference_id.to_owned(), + inquiry_reference: item.connector_request_reference_id.clone(), profile: auth_data.profile_id, action_type: ProphetpayActionType::get_action_type(&ProphetpayActionType::Inquiry), }) @@ -447,6 +552,7 @@ impl TryFrom<&types::PaymentsCancelRouterData> for ProphetpayVoidRequest { #[serde(rename_all = "camelCase")] pub struct ProphetpayRefundRequest { pub amount: f64, + pub card_token: Secret, pub transaction_id: String, pub profile: Secret, pub ref_info: String, @@ -459,47 +565,26 @@ impl TryFrom<&ProphetpayRouterData<&types::RefundsRouterData>> for Prophet fn try_from( item: &ProphetpayRouterData<&types::RefundsRouterData>, ) -> Result { - let auth_data = ProphetpayAuthType::try_from(&item.router_data.connector_auth_type)?; - let transaction_id = item.router_data.request.connector_transaction_id.to_owned(); - Ok(Self { - transaction_id, - amount: item.amount.to_owned(), - profile: auth_data.profile_id, - ref_info: item.router_data.request.refund_id.to_owned(), - inquiry_reference: format!("inquiry_{}", item.router_data.request.refund_id), - action_type: ProphetpayActionType::get_action_type(&ProphetpayActionType::Refund), - }) - } -} + if item.router_data.request.payment_amount == item.router_data.request.refund_amount { + let auth_data = ProphetpayAuthType::try_from(&item.router_data.connector_auth_type)?; + let transaction_id = item.router_data.request.connector_transaction_id.to_owned(); + let card_token_data: ProphetpayCardTokenData = + to_connector_meta(item.router_data.request.connector_metadata.clone())?; -#[allow(dead_code)] -#[derive(Debug, Deserialize, Clone)] -pub enum RefundStatus { - Success, - Failure, - #[serde(rename = "Transaction Voided")] - Voided, - #[serde(rename = "Requires a card on file.")] - CardTokenNotFound, - #[serde(rename = "RefInfo and InquiryReference are duplicated")] - DuplicateValue, - #[serde(rename = "Profile is missing")] - MissingProfile, - #[serde(rename = "RefInfo is empty.")] - EmptyRef, -} - -impl From for enums::RefundStatus { - fn from(item: RefundStatus) -> Self { - match item { - RefundStatus::Success - // in retrieving refund, if it is successful, it is shown as voided - | RefundStatus::Voided => Self::Success, - RefundStatus::Failure - | RefundStatus::CardTokenNotFound - | RefundStatus::DuplicateValue - | RefundStatus::MissingProfile - | RefundStatus::EmptyRef => Self::Failure, + Ok(Self { + transaction_id, + amount: item.amount.to_owned(), + card_token: card_token_data.card_token, + profile: auth_data.profile_id, + ref_info: item.router_data.connector_request_reference_id.to_owned(), + inquiry_reference: item.router_data.connector_request_reference_id.clone(), + action_type: ProphetpayActionType::get_action_type(&ProphetpayActionType::Refund), + }) + } else { + Err(errors::ConnectorError::NotImplemented( + "Partial Refund is Not Supported".to_string(), + ) + .into()) } } } @@ -507,7 +592,10 @@ impl From for enums::RefundStatus { #[derive(Debug, Clone, Deserialize)] #[serde(rename_all = "camelCase")] pub struct ProphetpayRefundResponse { - pub response_text: RefundStatus, + pub success: bool, + pub response_text: String, + pub tran_seq_number: String, + pub response_code: String, } impl TryFrom> @@ -517,20 +605,75 @@ impl TryFrom, ) -> Result { - Ok(Self { - response: Ok(types::RefundsResponseData { - // no refund id is generated, rather transaction id is used for referring to status in refund also - connector_refund_id: item.data.request.connector_transaction_id.clone(), - refund_status: enums::RefundStatus::from(item.response.response_text), - }), - ..item.data - }) + if item.response.success { + Ok(Self { + response: Ok(types::RefundsResponseData { + // no refund id is generated, tranSeqNumber is kept for future usage + connector_refund_id: item.response.tran_seq_number, + refund_status: enums::RefundStatus::Success, + }), + ..item.data + }) + } else { + Ok(Self { + status: enums::AttemptStatus::Failure, + response: Err(types::ErrorResponse { + code: item.response.response_code, + message: item.response.response_text.clone(), + reason: Some(item.response.response_text), + status_code: item.http_code, + attempt_status: None, + }), + ..item.data + }) + } } } +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ProphetpayRefundSyncResponse { + pub success: bool, + pub response_text: String, + pub response_code: String, +} + +impl TryFrom> + for types::RefundsRouterData +{ + type Error = error_stack::Report; + fn try_from( + item: types::RefundsResponseRouterData, + ) -> Result { + if item.response.success { + Ok(Self { + response: Ok(types::RefundsResponseData { + // no refund id is generated, rather transaction id is used for referring to status in refund also + connector_refund_id: item.data.request.connector_transaction_id.clone(), + refund_status: enums::RefundStatus::Success, + }), + ..item.data + }) + } else { + Ok(Self { + status: enums::AttemptStatus::Failure, + response: Err(types::ErrorResponse { + code: item.response.response_code, + message: item.response.response_text.clone(), + reason: Some(item.response.response_text), + status_code: item.http_code, + attempt_status: None, + }), + ..item.data + }) + } + } +} #[derive(Debug, Clone, Serialize)] #[serde(rename_all = "camelCase")] pub struct ProphetpayRefundSyncRequest { + transaction_id: String, + inquiry_reference: String, ref_info: String, profile: Secret, action_type: i8, @@ -541,36 +684,11 @@ impl TryFrom<&types::RefundSyncRouterData> for ProphetpayRefundSyncRequest { fn try_from(item: &types::RefundSyncRouterData) -> Result { let auth_data = ProphetpayAuthType::try_from(&item.connector_auth_type)?; Ok(Self { - ref_info: item.attempt_id.to_owned(), + transaction_id: item.request.connector_transaction_id.clone(), + ref_info: item.connector_request_reference_id.to_owned(), + inquiry_reference: item.connector_request_reference_id.clone(), profile: auth_data.profile_id, action_type: ProphetpayActionType::get_action_type(&ProphetpayActionType::Inquiry), }) } } - -impl TryFrom> - for types::RefundsRouterData -{ - type Error = error_stack::Report; - fn try_from( - item: types::RefundsResponseRouterData, - ) -> Result { - Ok(Self { - response: Ok(types::RefundsResponseData { - connector_refund_id: item.data.request.connector_transaction_id.clone(), - refund_status: enums::RefundStatus::from(item.response.response_text), - }), - ..item.data - }) - } -} - -// Error Response body is yet to be confirmed with the connector -#[derive(Default, Debug, Serialize, Deserialize, PartialEq)] -#[serde(rename_all = "camelCase")] -pub struct ProphetpayErrorResponse { - pub status: u16, - pub title: String, - pub trace_id: String, - pub errors: serde_json::Value, -}