mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-28 20:23:43 +08:00
feat(core): Hyperswitch <|> UCS Mandate flow integration (#8738)
Co-authored-by: Aishwariyaa Anand <aishwariyaa.anand@Aishwariyaa-Anand-C3PGW02T6Y.local> Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
20049d52fa
commit
f94f39ef0c
@ -20,6 +20,7 @@ use crate::{
|
||||
unified_connector_service::{
|
||||
build_unified_connector_service_auth_metadata,
|
||||
handle_unified_connector_service_response_for_payment_authorize,
|
||||
handle_unified_connector_service_response_for_payment_repeat,
|
||||
},
|
||||
},
|
||||
logger,
|
||||
@ -515,51 +516,23 @@ impl Feature<api::Authorize, types::PaymentsAuthorizeData> for types::PaymentsAu
|
||||
merchant_connector_account: domain::MerchantConnectorAccountTypeDetails,
|
||||
merchant_context: &domain::MerchantContext,
|
||||
) -> RouterResult<()> {
|
||||
let client = state
|
||||
.grpc_client
|
||||
.unified_connector_service_client
|
||||
.clone()
|
||||
.ok_or(ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Failed to fetch Unified Connector Service client")?;
|
||||
|
||||
let payment_authorize_request =
|
||||
payments_grpc::PaymentServiceAuthorizeRequest::foreign_try_from(self)
|
||||
.change_context(ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Failed to construct Payment Authorize Request")?;
|
||||
|
||||
let connector_auth_metadata = build_unified_connector_service_auth_metadata(
|
||||
merchant_connector_account,
|
||||
merchant_context,
|
||||
)
|
||||
.change_context(ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Failed to construct request metadata")?;
|
||||
|
||||
let response = client
|
||||
.payment_authorize(
|
||||
payment_authorize_request,
|
||||
connector_auth_metadata,
|
||||
state.get_grpc_headers(),
|
||||
if self.request.mandate_id.is_some() {
|
||||
call_unified_connector_service_repeat_payment(
|
||||
self,
|
||||
state,
|
||||
merchant_connector_account,
|
||||
merchant_context,
|
||||
)
|
||||
.await
|
||||
.change_context(ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Failed to authorize payment")?;
|
||||
|
||||
let payment_authorize_response = response.into_inner();
|
||||
|
||||
let (status, router_data_response) =
|
||||
handle_unified_connector_service_response_for_payment_authorize(
|
||||
payment_authorize_response.clone(),
|
||||
} else {
|
||||
call_unified_connector_service_authorize(
|
||||
self,
|
||||
state,
|
||||
merchant_connector_account,
|
||||
merchant_context,
|
||||
)
|
||||
.change_context(ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Failed to deserialize UCS response")?;
|
||||
|
||||
self.status = status;
|
||||
self.response = router_data_response;
|
||||
self.raw_connector_response = payment_authorize_response
|
||||
.raw_connector_response
|
||||
.map(Secret::new);
|
||||
|
||||
Ok(())
|
||||
.await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -846,3 +819,115 @@ async fn process_capture_flow(
|
||||
router_data.response = Ok(updated_response);
|
||||
Ok(router_data)
|
||||
}
|
||||
|
||||
async fn call_unified_connector_service_authorize(
|
||||
router_data: &mut types::RouterData<
|
||||
api::Authorize,
|
||||
types::PaymentsAuthorizeData,
|
||||
types::PaymentsResponseData,
|
||||
>,
|
||||
state: &SessionState,
|
||||
#[cfg(feature = "v1")] merchant_connector_account: helpers::MerchantConnectorAccountType,
|
||||
#[cfg(feature = "v2")] merchant_connector_account: domain::MerchantConnectorAccountTypeDetails,
|
||||
merchant_context: &domain::MerchantContext,
|
||||
) -> RouterResult<()> {
|
||||
let client = state
|
||||
.grpc_client
|
||||
.unified_connector_service_client
|
||||
.clone()
|
||||
.ok_or(ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Failed to fetch Unified Connector Service client")?;
|
||||
|
||||
let payment_authorize_request =
|
||||
payments_grpc::PaymentServiceAuthorizeRequest::foreign_try_from(router_data)
|
||||
.change_context(ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Failed to construct Payment Authorize Request")?;
|
||||
|
||||
let connector_auth_metadata =
|
||||
build_unified_connector_service_auth_metadata(merchant_connector_account, merchant_context)
|
||||
.change_context(ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Failed to construct request metadata")?;
|
||||
|
||||
let response = client
|
||||
.payment_authorize(
|
||||
payment_authorize_request,
|
||||
connector_auth_metadata,
|
||||
state.get_grpc_headers(),
|
||||
)
|
||||
.await
|
||||
.change_context(ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Failed to authorize payment")?;
|
||||
|
||||
let payment_authorize_response = response.into_inner();
|
||||
|
||||
let (status, router_data_response) =
|
||||
handle_unified_connector_service_response_for_payment_authorize(
|
||||
payment_authorize_response.clone(),
|
||||
)
|
||||
.change_context(ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Failed to deserialize UCS response")?;
|
||||
|
||||
router_data.status = status;
|
||||
router_data.response = router_data_response;
|
||||
router_data.raw_connector_response = payment_authorize_response
|
||||
.raw_connector_response
|
||||
.map(Secret::new);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn call_unified_connector_service_repeat_payment(
|
||||
router_data: &mut types::RouterData<
|
||||
api::Authorize,
|
||||
types::PaymentsAuthorizeData,
|
||||
types::PaymentsResponseData,
|
||||
>,
|
||||
state: &SessionState,
|
||||
#[cfg(feature = "v1")] merchant_connector_account: helpers::MerchantConnectorAccountType,
|
||||
#[cfg(feature = "v2")] merchant_connector_account: domain::MerchantConnectorAccountTypeDetails,
|
||||
merchant_context: &domain::MerchantContext,
|
||||
) -> RouterResult<()> {
|
||||
let client = state
|
||||
.grpc_client
|
||||
.unified_connector_service_client
|
||||
.clone()
|
||||
.ok_or(ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Failed to fetch Unified Connector Service client")?;
|
||||
|
||||
let payment_repeat_request =
|
||||
payments_grpc::PaymentServiceRepeatEverythingRequest::foreign_try_from(router_data)
|
||||
.change_context(ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Failed to construct Payment Authorize Request")?;
|
||||
|
||||
let connector_auth_metadata =
|
||||
build_unified_connector_service_auth_metadata(merchant_connector_account, merchant_context)
|
||||
.change_context(ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Failed to construct request metadata")?;
|
||||
|
||||
let response = client
|
||||
.payment_repeat(
|
||||
payment_repeat_request,
|
||||
connector_auth_metadata,
|
||||
state.get_grpc_headers(),
|
||||
)
|
||||
.await
|
||||
.change_context(ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Failed to authorize payment")?;
|
||||
|
||||
let payment_repeat_response = response.into_inner();
|
||||
|
||||
let (status, router_data_response) =
|
||||
handle_unified_connector_service_response_for_payment_repeat(
|
||||
payment_repeat_response.clone(),
|
||||
)
|
||||
.change_context(ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Failed to deserialize UCS response")?;
|
||||
|
||||
router_data.status = status;
|
||||
router_data.response = router_data_response;
|
||||
router_data.raw_connector_response = payment_repeat_response
|
||||
.raw_connector_response
|
||||
.map(Secret::new);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -1,19 +1,25 @@
|
||||
use async_trait::async_trait;
|
||||
use common_types::payments as common_payments_types;
|
||||
use error_stack::ResultExt;
|
||||
use router_env::logger;
|
||||
use unified_connector_service_client::payments as payments_grpc;
|
||||
|
||||
use super::{ConstructFlowSpecificData, Feature};
|
||||
use crate::{
|
||||
core::{
|
||||
errors::{ConnectorErrorExt, RouterResult},
|
||||
errors::{ApiErrorResponse, ConnectorErrorExt, RouterResult},
|
||||
mandate,
|
||||
payments::{
|
||||
self, access_token, customers, helpers, tokenization, transformers, PaymentData,
|
||||
},
|
||||
unified_connector_service::{
|
||||
build_unified_connector_service_auth_metadata,
|
||||
handle_unified_connector_service_response_for_payment_register,
|
||||
},
|
||||
},
|
||||
routes::SessionState,
|
||||
services,
|
||||
types::{self, api, domain},
|
||||
types::{self, api, domain, transformers::ForeignTryFrom},
|
||||
};
|
||||
|
||||
#[cfg(feature = "v1")]
|
||||
@ -200,6 +206,62 @@ impl Feature<api::SetupMandate, types::SetupMandateRequestData> for types::Setup
|
||||
_ => Ok((None, true)),
|
||||
}
|
||||
}
|
||||
|
||||
async fn call_unified_connector_service<'a>(
|
||||
&mut self,
|
||||
state: &SessionState,
|
||||
#[cfg(feature = "v1")] merchant_connector_account: helpers::MerchantConnectorAccountType,
|
||||
#[cfg(feature = "v2")]
|
||||
merchant_connector_account: domain::MerchantConnectorAccountTypeDetails,
|
||||
merchant_context: &domain::MerchantContext,
|
||||
) -> RouterResult<()> {
|
||||
let client = state
|
||||
.grpc_client
|
||||
.unified_connector_service_client
|
||||
.clone()
|
||||
.ok_or(ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Failed to fetch Unified Connector Service client")?;
|
||||
|
||||
let payment_register_request =
|
||||
payments_grpc::PaymentServiceRegisterRequest::foreign_try_from(self)
|
||||
.change_context(ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Failed to construct Payment Setup Mandate Request")?;
|
||||
|
||||
let connector_auth_metadata = build_unified_connector_service_auth_metadata(
|
||||
merchant_connector_account,
|
||||
merchant_context,
|
||||
)
|
||||
.change_context(ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Failed to construct request metadata")?;
|
||||
|
||||
let response = client
|
||||
.payment_setup_mandate(
|
||||
payment_register_request,
|
||||
connector_auth_metadata,
|
||||
state.get_grpc_headers(),
|
||||
)
|
||||
.await
|
||||
.change_context(ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Failed to Setup Mandate payment")?;
|
||||
|
||||
let payment_register_response = response.into_inner();
|
||||
|
||||
let (status, router_data_response) =
|
||||
handle_unified_connector_service_response_for_payment_register(
|
||||
payment_register_response.clone(),
|
||||
)
|
||||
.change_context(ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Failed to deserialize UCS response")?;
|
||||
|
||||
self.status = status;
|
||||
self.response = router_data_response;
|
||||
// UCS does not return raw connector response for setup mandate right now
|
||||
// self.raw_connector_response = payment_register_response
|
||||
// .raw_connector_response
|
||||
// .map(Secret::new);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl mandate::MandateBehaviour for types::SetupMandateRequestData {
|
||||
|
||||
@ -10,7 +10,7 @@ use hyperswitch_domain_models::merchant_connector_account::MerchantConnectorAcco
|
||||
use hyperswitch_domain_models::{
|
||||
merchant_context::MerchantContext,
|
||||
router_data::{ConnectorAuthType, ErrorResponse, RouterData},
|
||||
router_response_types::{PaymentsResponseData, RedirectForm},
|
||||
router_response_types::PaymentsResponseData,
|
||||
};
|
||||
use masking::{ExposeInterface, PeekInterface, Secret};
|
||||
use unified_connector_service_client::payments::{
|
||||
@ -238,85 +238,8 @@ pub fn handle_unified_connector_service_response_for_payment_authorize(
|
||||
> {
|
||||
let status = AttemptStatus::foreign_try_from(response.status())?;
|
||||
|
||||
let connector_response_reference_id =
|
||||
response.response_ref_id.as_ref().and_then(|identifier| {
|
||||
identifier
|
||||
.id_type
|
||||
.clone()
|
||||
.and_then(|id_type| match id_type {
|
||||
payments_grpc::identifier::IdType::Id(id) => Some(id),
|
||||
payments_grpc::identifier::IdType::EncodedData(encoded_data) => {
|
||||
Some(encoded_data)
|
||||
}
|
||||
payments_grpc::identifier::IdType::NoResponseIdMarker(_) => None,
|
||||
})
|
||||
});
|
||||
|
||||
let transaction_id = response.transaction_id.as_ref().and_then(|id| {
|
||||
id.id_type.clone().and_then(|id_type| match id_type {
|
||||
payments_grpc::identifier::IdType::Id(id) => Some(id),
|
||||
payments_grpc::identifier::IdType::EncodedData(encoded_data) => Some(encoded_data),
|
||||
payments_grpc::identifier::IdType::NoResponseIdMarker(_) => None,
|
||||
})
|
||||
});
|
||||
|
||||
let router_data_response = match status {
|
||||
AttemptStatus::Charged |
|
||||
AttemptStatus::Authorized |
|
||||
AttemptStatus::AuthenticationPending |
|
||||
AttemptStatus::DeviceDataCollectionPending |
|
||||
AttemptStatus::Started |
|
||||
AttemptStatus::AuthenticationSuccessful |
|
||||
AttemptStatus::Authorizing |
|
||||
AttemptStatus::ConfirmationAwaited |
|
||||
AttemptStatus::Pending => Ok(PaymentsResponseData::TransactionResponse {
|
||||
resource_id: match transaction_id.as_ref() {
|
||||
Some(transaction_id) => hyperswitch_domain_models::router_request_types::ResponseId::ConnectorTransactionId(transaction_id.clone()),
|
||||
None => hyperswitch_domain_models::router_request_types::ResponseId::NoResponseId,
|
||||
},
|
||||
redirection_data: Box::new(
|
||||
response
|
||||
.redirection_data
|
||||
.clone()
|
||||
.map(RedirectForm::foreign_try_from)
|
||||
.transpose()?
|
||||
),
|
||||
mandate_reference: Box::new(None),
|
||||
connector_metadata: None,
|
||||
network_txn_id: response.network_txn_id.clone(),
|
||||
connector_response_reference_id,
|
||||
incremental_authorization_allowed: response.incremental_authorization_allowed,
|
||||
charges: None,
|
||||
}),
|
||||
AttemptStatus::AuthenticationFailed
|
||||
| AttemptStatus::AuthorizationFailed
|
||||
| AttemptStatus::Unresolved
|
||||
| AttemptStatus::Failure => Err(ErrorResponse {
|
||||
code: response.error_code().to_owned(),
|
||||
message: response.error_message().to_owned(),
|
||||
reason: Some(response.error_message().to_owned()),
|
||||
status_code: 500,
|
||||
attempt_status: Some(status),
|
||||
connector_transaction_id: connector_response_reference_id,
|
||||
network_decline_code: None,
|
||||
network_advice_code: None,
|
||||
network_error_message: None,
|
||||
}),
|
||||
AttemptStatus::RouterDeclined |
|
||||
AttemptStatus::CodInitiated |
|
||||
AttemptStatus::Voided |
|
||||
AttemptStatus::VoidInitiated |
|
||||
AttemptStatus::CaptureInitiated |
|
||||
AttemptStatus::VoidFailed |
|
||||
AttemptStatus::AutoRefunded |
|
||||
AttemptStatus::PartialCharged |
|
||||
AttemptStatus::PartialChargedAndChargeable |
|
||||
AttemptStatus::PaymentMethodAwaited |
|
||||
AttemptStatus::CaptureFailed |
|
||||
AttemptStatus::IntegrityFailure => return Err(UnifiedConnectorServiceError::NotImplemented(format!(
|
||||
"AttemptStatus {status:?} is not implemented for Unified Connector Service"
|
||||
)).into()),
|
||||
};
|
||||
let router_data_response =
|
||||
Result::<PaymentsResponseData, ErrorResponse>::foreign_try_from(response)?;
|
||||
|
||||
Ok((status, router_data_response))
|
||||
}
|
||||
@ -329,75 +252,36 @@ pub fn handle_unified_connector_service_response_for_payment_get(
|
||||
> {
|
||||
let status = AttemptStatus::foreign_try_from(response.status())?;
|
||||
|
||||
let connector_response_reference_id =
|
||||
response.response_ref_id.as_ref().and_then(|identifier| {
|
||||
identifier
|
||||
.id_type
|
||||
.clone()
|
||||
.and_then(|id_type| match id_type {
|
||||
payments_grpc::identifier::IdType::Id(id) => Some(id),
|
||||
payments_grpc::identifier::IdType::EncodedData(encoded_data) => {
|
||||
Some(encoded_data)
|
||||
}
|
||||
payments_grpc::identifier::IdType::NoResponseIdMarker(_) => None,
|
||||
})
|
||||
});
|
||||
|
||||
let router_data_response = match status {
|
||||
AttemptStatus::Charged |
|
||||
AttemptStatus::Authorized |
|
||||
AttemptStatus::AuthenticationPending |
|
||||
AttemptStatus::DeviceDataCollectionPending |
|
||||
AttemptStatus::Started |
|
||||
AttemptStatus::AuthenticationSuccessful |
|
||||
AttemptStatus::Authorizing |
|
||||
AttemptStatus::ConfirmationAwaited |
|
||||
AttemptStatus::Pending => Ok(
|
||||
PaymentsResponseData::TransactionResponse {
|
||||
resource_id: match connector_response_reference_id.as_ref() {
|
||||
Some(connector_response_reference_id) => hyperswitch_domain_models::router_request_types::ResponseId::ConnectorTransactionId(connector_response_reference_id.clone()),
|
||||
None => hyperswitch_domain_models::router_request_types::ResponseId::NoResponseId,
|
||||
},
|
||||
redirection_data: Box::new(
|
||||
None
|
||||
),
|
||||
mandate_reference: Box::new(None),
|
||||
connector_metadata: None,
|
||||
network_txn_id: response.network_txn_id.clone(),
|
||||
connector_response_reference_id,
|
||||
incremental_authorization_allowed: None,
|
||||
charges: None,
|
||||
}
|
||||
),
|
||||
AttemptStatus::AuthenticationFailed
|
||||
| AttemptStatus::AuthorizationFailed
|
||||
| AttemptStatus::Failure => Err(ErrorResponse {
|
||||
code: response.error_code().to_owned(),
|
||||
message: response.error_message().to_owned(),
|
||||
reason: Some(response.error_message().to_owned()),
|
||||
status_code: 500,
|
||||
attempt_status: Some(status),
|
||||
connector_transaction_id: connector_response_reference_id,
|
||||
network_decline_code: None,
|
||||
network_advice_code: None,
|
||||
network_error_message: None,
|
||||
}),
|
||||
AttemptStatus::RouterDeclined |
|
||||
AttemptStatus::CodInitiated |
|
||||
AttemptStatus::Voided |
|
||||
AttemptStatus::VoidInitiated |
|
||||
AttemptStatus::CaptureInitiated |
|
||||
AttemptStatus::VoidFailed |
|
||||
AttemptStatus::AutoRefunded |
|
||||
AttemptStatus::PartialCharged |
|
||||
AttemptStatus::PartialChargedAndChargeable |
|
||||
AttemptStatus::Unresolved |
|
||||
AttemptStatus::PaymentMethodAwaited |
|
||||
AttemptStatus::CaptureFailed |
|
||||
AttemptStatus::IntegrityFailure => return Err(UnifiedConnectorServiceError::NotImplemented(format!(
|
||||
"AttemptStatus {status:?} is not implemented for Unified Connector Service"
|
||||
)).into()),
|
||||
};
|
||||
let router_data_response =
|
||||
Result::<PaymentsResponseData, ErrorResponse>::foreign_try_from(response)?;
|
||||
|
||||
Ok((status, router_data_response))
|
||||
}
|
||||
|
||||
pub fn handle_unified_connector_service_response_for_payment_register(
|
||||
response: payments_grpc::PaymentServiceRegisterResponse,
|
||||
) -> CustomResult<
|
||||
(AttemptStatus, Result<PaymentsResponseData, ErrorResponse>),
|
||||
UnifiedConnectorServiceError,
|
||||
> {
|
||||
let status = AttemptStatus::foreign_try_from(response.status())?;
|
||||
|
||||
let router_data_response =
|
||||
Result::<PaymentsResponseData, ErrorResponse>::foreign_try_from(response)?;
|
||||
|
||||
Ok((status, router_data_response))
|
||||
}
|
||||
|
||||
pub fn handle_unified_connector_service_response_for_payment_repeat(
|
||||
response: payments_grpc::PaymentServiceRepeatEverythingResponse,
|
||||
) -> CustomResult<
|
||||
(AttemptStatus, Result<PaymentsResponseData, ErrorResponse>),
|
||||
UnifiedConnectorServiceError,
|
||||
> {
|
||||
let status = AttemptStatus::foreign_try_from(response.status())?;
|
||||
|
||||
let router_data_response =
|
||||
Result::<PaymentsResponseData, ErrorResponse>::foreign_try_from(response)?;
|
||||
|
||||
Ok((status, router_data_response))
|
||||
}
|
||||
|
||||
@ -6,9 +6,11 @@ use diesel_models::enums as storage_enums;
|
||||
use error_stack::ResultExt;
|
||||
use external_services::grpc_client::unified_connector_service::UnifiedConnectorServiceError;
|
||||
use hyperswitch_domain_models::{
|
||||
router_data::RouterData,
|
||||
router_flow_types::payments::{Authorize, PSync},
|
||||
router_request_types::{AuthenticationData, PaymentsAuthorizeData, PaymentsSyncData},
|
||||
router_data::{ErrorResponse, RouterData},
|
||||
router_flow_types::payments::{Authorize, PSync, SetupMandate},
|
||||
router_request_types::{
|
||||
AuthenticationData, PaymentsAuthorizeData, PaymentsSyncData, SetupMandateRequestData,
|
||||
},
|
||||
router_response_types::{PaymentsResponseData, RedirectForm},
|
||||
};
|
||||
use masking::{ExposeInterface, PeekInterface};
|
||||
@ -167,6 +169,443 @@ impl ForeignTryFrom<&RouterData<Authorize, PaymentsAuthorizeData, PaymentsRespon
|
||||
}
|
||||
}
|
||||
|
||||
impl ForeignTryFrom<&RouterData<SetupMandate, SetupMandateRequestData, PaymentsResponseData>>
|
||||
for payments_grpc::PaymentServiceRegisterRequest
|
||||
{
|
||||
type Error = error_stack::Report<UnifiedConnectorServiceError>;
|
||||
|
||||
fn foreign_try_from(
|
||||
router_data: &RouterData<SetupMandate, SetupMandateRequestData, PaymentsResponseData>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
let currency = payments_grpc::Currency::foreign_try_from(router_data.request.currency)?;
|
||||
let payment_method = router_data
|
||||
.request
|
||||
.payment_method_type
|
||||
.map(|payment_method_type| {
|
||||
build_unified_connector_service_payment_method(
|
||||
router_data.request.payment_method_data.clone(),
|
||||
payment_method_type,
|
||||
)
|
||||
})
|
||||
.transpose()?;
|
||||
let address = payments_grpc::PaymentAddress::foreign_try_from(router_data.address.clone())?;
|
||||
let auth_type = payments_grpc::AuthenticationType::foreign_try_from(router_data.auth_type)?;
|
||||
let browser_info = router_data
|
||||
.request
|
||||
.browser_info
|
||||
.clone()
|
||||
.map(payments_grpc::BrowserInformation::foreign_try_from)
|
||||
.transpose()?;
|
||||
let setup_future_usage = router_data
|
||||
.request
|
||||
.setup_future_usage
|
||||
.map(payments_grpc::FutureUsage::foreign_try_from)
|
||||
.transpose()?;
|
||||
let customer_acceptance = router_data
|
||||
.request
|
||||
.customer_acceptance
|
||||
.clone()
|
||||
.map(payments_grpc::CustomerAcceptance::foreign_try_from)
|
||||
.transpose()?;
|
||||
|
||||
Ok(Self {
|
||||
request_ref_id: Some(Identifier {
|
||||
id_type: Some(payments_grpc::identifier::IdType::Id(
|
||||
router_data.connector_request_reference_id.clone(),
|
||||
)),
|
||||
}),
|
||||
currency: currency.into(),
|
||||
payment_method,
|
||||
minor_amount: router_data.request.amount,
|
||||
email: router_data
|
||||
.request
|
||||
.email
|
||||
.clone()
|
||||
.map(|e| e.expose().expose()),
|
||||
customer_name: router_data
|
||||
.request
|
||||
.customer_name
|
||||
.clone()
|
||||
.map(|customer_name| customer_name.peek().to_owned()),
|
||||
connector_customer_id: router_data
|
||||
.request
|
||||
.customer_id
|
||||
.as_ref()
|
||||
.map(|id| id.get_string_repr().to_string()),
|
||||
address: Some(address),
|
||||
auth_type: auth_type.into(),
|
||||
enrolled_for_3ds: false,
|
||||
authentication_data: None,
|
||||
metadata: router_data
|
||||
.request
|
||||
.metadata
|
||||
.as_ref()
|
||||
.map(|secret| secret.peek())
|
||||
.and_then(|val| val.as_object()) //secret
|
||||
.map(|map| {
|
||||
map.iter()
|
||||
.filter_map(|(k, v)| v.as_str().map(|s| (k.clone(), s.to_string())))
|
||||
.collect::<HashMap<String, String>>()
|
||||
})
|
||||
.unwrap_or_default(),
|
||||
return_url: router_data.request.router_return_url.clone(),
|
||||
webhook_url: router_data.request.webhook_url.clone(),
|
||||
complete_authorize_url: router_data.request.complete_authorize_url.clone(),
|
||||
access_token: None,
|
||||
session_token: None,
|
||||
order_tax_amount: None,
|
||||
order_category: None,
|
||||
merchant_order_reference_id: None,
|
||||
shipping_cost: router_data
|
||||
.request
|
||||
.shipping_cost
|
||||
.map(|cost| cost.get_amount_as_i64()),
|
||||
setup_future_usage: setup_future_usage.map(|s| s.into()),
|
||||
off_session: router_data.request.off_session,
|
||||
request_incremental_authorization: router_data
|
||||
.request
|
||||
.request_incremental_authorization,
|
||||
request_extended_authorization: None,
|
||||
customer_acceptance,
|
||||
browser_info,
|
||||
payment_experience: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ForeignTryFrom<&RouterData<Authorize, PaymentsAuthorizeData, PaymentsResponseData>>
|
||||
for payments_grpc::PaymentServiceRepeatEverythingRequest
|
||||
{
|
||||
type Error = error_stack::Report<UnifiedConnectorServiceError>;
|
||||
|
||||
fn foreign_try_from(
|
||||
router_data: &RouterData<Authorize, PaymentsAuthorizeData, PaymentsResponseData>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
let currency = payments_grpc::Currency::foreign_try_from(router_data.request.currency)?;
|
||||
|
||||
let mandate_reference = match &router_data.request.mandate_id {
|
||||
Some(mandate) => match &mandate.mandate_reference_id {
|
||||
Some(api_models::payments::MandateReferenceId::ConnectorMandateId(
|
||||
connector_mandate_id,
|
||||
)) => Some(payments_grpc::MandateReference {
|
||||
mandate_id: connector_mandate_id.get_connector_mandate_id(),
|
||||
}),
|
||||
_ => {
|
||||
return Err(UnifiedConnectorServiceError::MissingRequiredField {
|
||||
field_name: "connector_mandate_id",
|
||||
}
|
||||
.into())
|
||||
}
|
||||
},
|
||||
None => {
|
||||
return Err(UnifiedConnectorServiceError::MissingRequiredField {
|
||||
field_name: "connector_mandate_id",
|
||||
}
|
||||
.into())
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
request_ref_id: Some(Identifier {
|
||||
id_type: Some(payments_grpc::identifier::IdType::Id(
|
||||
router_data.connector_request_reference_id.clone(),
|
||||
)),
|
||||
}),
|
||||
mandate_reference,
|
||||
amount: router_data.request.amount,
|
||||
currency: currency.into(),
|
||||
minor_amount: router_data.request.amount,
|
||||
merchant_order_reference_id: router_data.request.merchant_order_reference_id.clone(),
|
||||
metadata: router_data
|
||||
.request
|
||||
.metadata
|
||||
.as_ref()
|
||||
.and_then(|val| val.as_object())
|
||||
.map(|map| {
|
||||
map.iter()
|
||||
.filter_map(|(k, v)| v.as_str().map(|s| (k.clone(), s.to_string())))
|
||||
.collect::<HashMap<String, String>>()
|
||||
})
|
||||
.unwrap_or_default(),
|
||||
webhook_url: router_data.request.webhook_url.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ForeignTryFrom<payments_grpc::PaymentServiceAuthorizeResponse>
|
||||
for Result<PaymentsResponseData, ErrorResponse>
|
||||
{
|
||||
type Error = error_stack::Report<UnifiedConnectorServiceError>;
|
||||
|
||||
fn foreign_try_from(
|
||||
response: payments_grpc::PaymentServiceAuthorizeResponse,
|
||||
) -> Result<Self, Self::Error> {
|
||||
let status = AttemptStatus::foreign_try_from(response.status())?;
|
||||
|
||||
let connector_response_reference_id =
|
||||
response.response_ref_id.as_ref().and_then(|identifier| {
|
||||
identifier
|
||||
.id_type
|
||||
.clone()
|
||||
.and_then(|id_type| match id_type {
|
||||
payments_grpc::identifier::IdType::Id(id) => Some(id),
|
||||
payments_grpc::identifier::IdType::EncodedData(encoded_data) => {
|
||||
Some(encoded_data)
|
||||
}
|
||||
payments_grpc::identifier::IdType::NoResponseIdMarker(_) => None,
|
||||
})
|
||||
});
|
||||
|
||||
let transaction_id = response.transaction_id.as_ref().and_then(|id| {
|
||||
id.id_type.clone().and_then(|id_type| match id_type {
|
||||
payments_grpc::identifier::IdType::Id(id) => Some(id),
|
||||
payments_grpc::identifier::IdType::EncodedData(encoded_data) => Some(encoded_data),
|
||||
payments_grpc::identifier::IdType::NoResponseIdMarker(_) => None,
|
||||
})
|
||||
});
|
||||
|
||||
let response = if response.error_code.is_some() {
|
||||
Err(ErrorResponse {
|
||||
code: response.error_code().to_owned(),
|
||||
message: response.error_message().to_owned(),
|
||||
reason: Some(response.error_message().to_owned()),
|
||||
status_code: 500, //TODO: To be handled once UCS sends proper status codes
|
||||
attempt_status: Some(status),
|
||||
connector_transaction_id: connector_response_reference_id,
|
||||
network_decline_code: None,
|
||||
network_advice_code: None,
|
||||
network_error_message: None,
|
||||
})
|
||||
} else {
|
||||
Ok(PaymentsResponseData::TransactionResponse {
|
||||
resource_id: match transaction_id.as_ref() {
|
||||
Some(transaction_id) => hyperswitch_domain_models::router_request_types::ResponseId::ConnectorTransactionId(transaction_id.clone()),
|
||||
None => hyperswitch_domain_models::router_request_types::ResponseId::NoResponseId,
|
||||
},
|
||||
redirection_data: Box::new(
|
||||
response
|
||||
.redirection_data
|
||||
.clone()
|
||||
.map(RedirectForm::foreign_try_from)
|
||||
.transpose()?
|
||||
),
|
||||
mandate_reference: Box::new(None),
|
||||
connector_metadata: None,
|
||||
network_txn_id: response.network_txn_id.clone(),
|
||||
connector_response_reference_id,
|
||||
incremental_authorization_allowed: response.incremental_authorization_allowed,
|
||||
charges: None,
|
||||
})
|
||||
};
|
||||
|
||||
Ok(response)
|
||||
}
|
||||
}
|
||||
|
||||
impl ForeignTryFrom<payments_grpc::PaymentServiceGetResponse>
|
||||
for Result<PaymentsResponseData, ErrorResponse>
|
||||
{
|
||||
type Error = error_stack::Report<UnifiedConnectorServiceError>;
|
||||
|
||||
fn foreign_try_from(
|
||||
response: payments_grpc::PaymentServiceGetResponse,
|
||||
) -> Result<Self, Self::Error> {
|
||||
let status = AttemptStatus::foreign_try_from(response.status())?;
|
||||
|
||||
let connector_response_reference_id =
|
||||
response.response_ref_id.as_ref().and_then(|identifier| {
|
||||
identifier
|
||||
.id_type
|
||||
.clone()
|
||||
.and_then(|id_type| match id_type {
|
||||
payments_grpc::identifier::IdType::Id(id) => Some(id),
|
||||
payments_grpc::identifier::IdType::EncodedData(encoded_data) => {
|
||||
Some(encoded_data)
|
||||
}
|
||||
payments_grpc::identifier::IdType::NoResponseIdMarker(_) => None,
|
||||
})
|
||||
});
|
||||
|
||||
let response = if response.error_code.is_some() {
|
||||
Err(ErrorResponse {
|
||||
code: response.error_code().to_owned(),
|
||||
message: response.error_message().to_owned(),
|
||||
reason: Some(response.error_message().to_owned()),
|
||||
status_code: 500, //TODO: To be handled once UCS sends proper status codes
|
||||
attempt_status: Some(status),
|
||||
connector_transaction_id: connector_response_reference_id,
|
||||
network_decline_code: None,
|
||||
network_advice_code: None,
|
||||
network_error_message: None,
|
||||
})
|
||||
} else {
|
||||
Ok(PaymentsResponseData::TransactionResponse {
|
||||
resource_id: match connector_response_reference_id.as_ref() {
|
||||
Some(connector_response_reference_id) => hyperswitch_domain_models::router_request_types::ResponseId::ConnectorTransactionId(connector_response_reference_id.clone()),
|
||||
None => hyperswitch_domain_models::router_request_types::ResponseId::NoResponseId,
|
||||
},
|
||||
redirection_data: Box::new(
|
||||
None
|
||||
),
|
||||
mandate_reference: Box::new(None),
|
||||
connector_metadata: None,
|
||||
network_txn_id: response.network_txn_id.clone(),
|
||||
connector_response_reference_id,
|
||||
incremental_authorization_allowed: None,
|
||||
charges: None,
|
||||
}
|
||||
)
|
||||
};
|
||||
|
||||
Ok(response)
|
||||
}
|
||||
}
|
||||
|
||||
impl ForeignTryFrom<payments_grpc::PaymentServiceRegisterResponse>
|
||||
for Result<PaymentsResponseData, ErrorResponse>
|
||||
{
|
||||
type Error = error_stack::Report<UnifiedConnectorServiceError>;
|
||||
|
||||
fn foreign_try_from(
|
||||
response: payments_grpc::PaymentServiceRegisterResponse,
|
||||
) -> Result<Self, Self::Error> {
|
||||
let status = AttemptStatus::foreign_try_from(response.status())?;
|
||||
|
||||
let connector_response_reference_id =
|
||||
response.response_ref_id.as_ref().and_then(|identifier| {
|
||||
identifier
|
||||
.id_type
|
||||
.clone()
|
||||
.and_then(|id_type| match id_type {
|
||||
payments_grpc::identifier::IdType::Id(id) => Some(id),
|
||||
payments_grpc::identifier::IdType::EncodedData(encoded_data) => {
|
||||
Some(encoded_data)
|
||||
}
|
||||
payments_grpc::identifier::IdType::NoResponseIdMarker(_) => None,
|
||||
})
|
||||
});
|
||||
|
||||
let response = if response.error_code.is_some() {
|
||||
Err(ErrorResponse {
|
||||
code: response.error_code().to_owned(),
|
||||
message: response.error_message().to_owned(),
|
||||
reason: Some(response.error_message().to_owned()),
|
||||
status_code: 500, //TODO: To be handled once UCS sends proper status codes
|
||||
attempt_status: Some(status),
|
||||
connector_transaction_id: connector_response_reference_id,
|
||||
network_decline_code: None,
|
||||
network_advice_code: None,
|
||||
network_error_message: None,
|
||||
})
|
||||
} else {
|
||||
Ok(PaymentsResponseData::TransactionResponse {
|
||||
resource_id: response.registration_id.as_ref().and_then(|identifier| {
|
||||
identifier
|
||||
.id_type
|
||||
.clone()
|
||||
.and_then(|id_type| match id_type {
|
||||
payments_grpc::identifier::IdType::Id(id) => Some(
|
||||
hyperswitch_domain_models::router_request_types::ResponseId::ConnectorTransactionId(id),
|
||||
),
|
||||
payments_grpc::identifier::IdType::EncodedData(encoded_data) => Some(
|
||||
hyperswitch_domain_models::router_request_types::ResponseId::ConnectorTransactionId(encoded_data),
|
||||
),
|
||||
payments_grpc::identifier::IdType::NoResponseIdMarker(_) => None,
|
||||
})
|
||||
}).unwrap_or(hyperswitch_domain_models::router_request_types::ResponseId::NoResponseId),
|
||||
redirection_data: Box::new(
|
||||
response
|
||||
.redirection_data
|
||||
.clone()
|
||||
.map(RedirectForm::foreign_try_from)
|
||||
.transpose()?
|
||||
),
|
||||
mandate_reference: Box::new(
|
||||
response.mandate_reference.map(|grpc_mandate| {
|
||||
hyperswitch_domain_models::router_response_types::MandateReference {
|
||||
connector_mandate_id: grpc_mandate.mandate_id,
|
||||
payment_method_id: None,
|
||||
mandate_metadata: None,
|
||||
connector_mandate_request_reference_id: None,
|
||||
}
|
||||
})
|
||||
),
|
||||
connector_metadata: None,
|
||||
network_txn_id: response.network_txn_id,
|
||||
connector_response_reference_id,
|
||||
incremental_authorization_allowed: response.incremental_authorization_allowed,
|
||||
charges: None,
|
||||
})
|
||||
};
|
||||
|
||||
Ok(response)
|
||||
}
|
||||
}
|
||||
|
||||
impl ForeignTryFrom<payments_grpc::PaymentServiceRepeatEverythingResponse>
|
||||
for Result<PaymentsResponseData, ErrorResponse>
|
||||
{
|
||||
type Error = error_stack::Report<UnifiedConnectorServiceError>;
|
||||
|
||||
fn foreign_try_from(
|
||||
response: payments_grpc::PaymentServiceRepeatEverythingResponse,
|
||||
) -> Result<Self, Self::Error> {
|
||||
let status = AttemptStatus::foreign_try_from(response.status())?;
|
||||
|
||||
let connector_response_reference_id =
|
||||
response.response_ref_id.as_ref().and_then(|identifier| {
|
||||
identifier
|
||||
.id_type
|
||||
.clone()
|
||||
.and_then(|id_type| match id_type {
|
||||
payments_grpc::identifier::IdType::Id(id) => Some(id),
|
||||
payments_grpc::identifier::IdType::EncodedData(encoded_data) => {
|
||||
Some(encoded_data)
|
||||
}
|
||||
payments_grpc::identifier::IdType::NoResponseIdMarker(_) => None,
|
||||
})
|
||||
});
|
||||
|
||||
let transaction_id = response.transaction_id.as_ref().and_then(|id| {
|
||||
id.id_type.clone().and_then(|id_type| match id_type {
|
||||
payments_grpc::identifier::IdType::Id(id) => Some(id),
|
||||
payments_grpc::identifier::IdType::EncodedData(encoded_data) => Some(encoded_data),
|
||||
payments_grpc::identifier::IdType::NoResponseIdMarker(_) => None,
|
||||
})
|
||||
});
|
||||
|
||||
let response = if response.error_code.is_some() {
|
||||
Err(ErrorResponse {
|
||||
code: response.error_code().to_owned(),
|
||||
message: response.error_message().to_owned(),
|
||||
reason: Some(response.error_message().to_owned()),
|
||||
status_code: 500, //TODO: To be handled once UCS sends proper status codes
|
||||
attempt_status: Some(status),
|
||||
connector_transaction_id: transaction_id,
|
||||
network_decline_code: None,
|
||||
network_advice_code: None,
|
||||
network_error_message: None,
|
||||
})
|
||||
} else {
|
||||
Ok(PaymentsResponseData::TransactionResponse {
|
||||
resource_id: match transaction_id.as_ref() {
|
||||
Some(transaction_id) => hyperswitch_domain_models::router_request_types::ResponseId::ConnectorTransactionId(transaction_id.clone()),
|
||||
None => hyperswitch_domain_models::router_request_types::ResponseId::NoResponseId,
|
||||
},
|
||||
redirection_data: Box::new(None),
|
||||
mandate_reference: Box::new(None),
|
||||
connector_metadata: None,
|
||||
network_txn_id: response.network_txn_id.clone(),
|
||||
connector_response_reference_id,
|
||||
incremental_authorization_allowed: None,
|
||||
charges: None,
|
||||
})
|
||||
};
|
||||
|
||||
Ok(response)
|
||||
}
|
||||
}
|
||||
|
||||
impl ForeignTryFrom<common_enums::Currency> for payments_grpc::Currency {
|
||||
type Error = error_stack::Report<UnifiedConnectorServiceError>;
|
||||
|
||||
@ -480,3 +919,48 @@ impl ForeignTryFrom<payments_grpc::HttpMethod> for Method {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ForeignTryFrom<storage_enums::FutureUsage> for payments_grpc::FutureUsage {
|
||||
type Error = error_stack::Report<UnifiedConnectorServiceError>;
|
||||
|
||||
fn foreign_try_from(future_usage: storage_enums::FutureUsage) -> Result<Self, Self::Error> {
|
||||
match future_usage {
|
||||
storage_enums::FutureUsage::OnSession => Ok(Self::OnSession),
|
||||
storage_enums::FutureUsage::OffSession => Ok(Self::OffSession),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ForeignTryFrom<common_types::payments::CustomerAcceptance>
|
||||
for payments_grpc::CustomerAcceptance
|
||||
{
|
||||
type Error = error_stack::Report<UnifiedConnectorServiceError>;
|
||||
|
||||
fn foreign_try_from(
|
||||
customer_acceptance: common_types::payments::CustomerAcceptance,
|
||||
) -> Result<Self, Self::Error> {
|
||||
let acceptance_type = match customer_acceptance.acceptance_type {
|
||||
common_types::payments::AcceptanceType::Online => payments_grpc::AcceptanceType::Online,
|
||||
common_types::payments::AcceptanceType::Offline => {
|
||||
payments_grpc::AcceptanceType::Offline
|
||||
}
|
||||
};
|
||||
|
||||
let online_mandate_details =
|
||||
customer_acceptance
|
||||
.online
|
||||
.map(|online| payments_grpc::OnlineMandate {
|
||||
ip_address: online.ip_address.map(|ip| ip.peek().to_string()),
|
||||
user_agent: online.user_agent,
|
||||
});
|
||||
|
||||
Ok(Self {
|
||||
acceptance_type: acceptance_type.into(),
|
||||
accepted_at: customer_acceptance
|
||||
.accepted_at
|
||||
.map(|dt| dt.assume_utc().unix_timestamp())
|
||||
.unwrap_or_default(),
|
||||
online_mandate_details,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user