mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 00:49:42 +08:00
feat(core): Implement UCS based upi for paytm and phonepe (#8732)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Co-authored-by: Gnanasundari24 <118818938+Gnanasundari24@users.noreply.github.com>
This commit is contained in:
@ -32,18 +32,18 @@ pub use hyperswitch_connectors::connectors::{
|
||||
novalnet::Novalnet, nuvei, nuvei::Nuvei, opayo, opayo::Opayo, opennode, opennode::Opennode,
|
||||
paybox, paybox::Paybox, payeezy, payeezy::Payeezy, payload, payload::Payload, payme,
|
||||
payme::Payme, payone, payone::Payone, paypal, paypal::Paypal, paystack, paystack::Paystack,
|
||||
payu, payu::Payu, placetopay, placetopay::Placetopay, plaid, plaid::Plaid, powertranz,
|
||||
powertranz::Powertranz, prophetpay, prophetpay::Prophetpay, rapyd, rapyd::Rapyd, razorpay,
|
||||
razorpay::Razorpay, recurly, recurly::Recurly, redsys, redsys::Redsys, riskified,
|
||||
riskified::Riskified, santander, santander::Santander, shift4, shift4::Shift4, signifyd,
|
||||
signifyd::Signifyd, silverflow, silverflow::Silverflow, square, square::Square, stax,
|
||||
stax::Stax, stripe, stripe::Stripe, stripebilling, stripebilling::Stripebilling, taxjar,
|
||||
taxjar::Taxjar, threedsecureio, threedsecureio::Threedsecureio, thunes, thunes::Thunes,
|
||||
tokenio, tokenio::Tokenio, trustpay, trustpay::Trustpay, trustpayments,
|
||||
trustpayments::Trustpayments, tsys, tsys::Tsys, unified_authentication_service,
|
||||
unified_authentication_service::UnifiedAuthenticationService, vgs, vgs::Vgs, volt, volt::Volt,
|
||||
wellsfargo, wellsfargo::Wellsfargo, wellsfargopayout, wellsfargopayout::Wellsfargopayout, wise,
|
||||
wise::Wise, worldline, worldline::Worldline, worldpay, worldpay::Worldpay, worldpayvantiv,
|
||||
worldpayvantiv::Worldpayvantiv, worldpayxml, worldpayxml::Worldpayxml, xendit, xendit::Xendit,
|
||||
zen, zen::Zen, zsl, zsl::Zsl,
|
||||
paytm, paytm::Paytm, payu, payu::Payu, phonepe, phonepe::Phonepe, placetopay,
|
||||
placetopay::Placetopay, plaid, plaid::Plaid, powertranz, powertranz::Powertranz, prophetpay,
|
||||
prophetpay::Prophetpay, rapyd, rapyd::Rapyd, razorpay, razorpay::Razorpay, recurly,
|
||||
recurly::Recurly, redsys, redsys::Redsys, riskified, riskified::Riskified, santander,
|
||||
santander::Santander, shift4, shift4::Shift4, signifyd, signifyd::Signifyd, silverflow,
|
||||
silverflow::Silverflow, square, square::Square, stax, stax::Stax, stripe, stripe::Stripe,
|
||||
stripebilling, stripebilling::Stripebilling, taxjar, taxjar::Taxjar, threedsecureio,
|
||||
threedsecureio::Threedsecureio, thunes, thunes::Thunes, tokenio, tokenio::Tokenio, trustpay,
|
||||
trustpay::Trustpay, trustpayments, trustpayments::Trustpayments, tsys, tsys::Tsys,
|
||||
unified_authentication_service, unified_authentication_service::UnifiedAuthenticationService,
|
||||
vgs, vgs::Vgs, volt, volt::Volt, wellsfargo, wellsfargo::Wellsfargo, wellsfargopayout,
|
||||
wellsfargopayout::Wellsfargopayout, wise, wise::Wise, worldline, worldline::Worldline,
|
||||
worldpay, worldpay::Worldpay, worldpayvantiv, worldpayvantiv::Worldpayvantiv, worldpayxml,
|
||||
worldpayxml::Worldpayxml, xendit, xendit::Xendit, zen, zen::Zen, zsl, zsl::Zsl,
|
||||
};
|
||||
|
||||
@ -522,6 +522,14 @@ impl ConnectorAuthTypeAndMetadataValidation<'_> {
|
||||
threedsecureio::transformers::ThreedsecureioAuthType::try_from(self.auth_type)?;
|
||||
Ok(())
|
||||
}
|
||||
api_enums::Connector::Phonepe => {
|
||||
phonepe::transformers::PhonepeAuthType::try_from(self.auth_type)?;
|
||||
Ok(())
|
||||
}
|
||||
api_enums::Connector::Paytm => {
|
||||
paytm::transformers::PaytmAuthType::try_from(self.auth_type)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -57,6 +57,16 @@ pub async fn should_call_unified_connector_service<F: Clone, T>(
|
||||
let payment_method = router_data.payment_method.to_string();
|
||||
let flow_name = get_flow_name::<F>()?;
|
||||
|
||||
let is_ucs_only_connector = state
|
||||
.conf
|
||||
.grpc_client
|
||||
.unified_connector_service
|
||||
.as_ref()
|
||||
.is_some_and(|config| config.ucs_only_connectors.contains(&connector_name));
|
||||
|
||||
if is_ucs_only_connector {
|
||||
return Ok(true);
|
||||
}
|
||||
let config_key = format!(
|
||||
"{}_{}_{}_{}_{}",
|
||||
consts::UCS_ROLLOUT_PERCENT_CONFIG_PREFIX,
|
||||
@ -135,11 +145,9 @@ pub fn build_unified_connector_service_payment_method(
|
||||
let upi_details = payments_grpc::UpiCollect { vpa_id };
|
||||
PaymentMethod::UpiCollect(upi_details)
|
||||
}
|
||||
_ => {
|
||||
return Err(UnifiedConnectorServiceError::NotImplemented(format!(
|
||||
"Unimplemented payment method subtype: {payment_method_type:?}"
|
||||
))
|
||||
.into());
|
||||
hyperswitch_domain_models::payment_method_data::UpiData::UpiIntent(_) => {
|
||||
let upi_details = payments_grpc::UpiIntent {};
|
||||
PaymentMethod::UpiIntent(upi_details)
|
||||
}
|
||||
};
|
||||
|
||||
@ -238,6 +246,112 @@ pub fn handle_unified_connector_service_response_for_payment_authorize(
|
||||
> {
|
||||
let status = AttemptStatus::foreign_try_from(response.status())?;
|
||||
|
||||
// <<<<<<< HEAD
|
||||
// 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 (connector_metadata, redirection_data) = match response.redirection_data.clone() {
|
||||
// Some(redirection_data) => match redirection_data.form_type {
|
||||
// Some(ref form_type) => match form_type {
|
||||
// payments_grpc::redirect_form::FormType::Uri(uri) => {
|
||||
// let image_data = QrImage::new_from_data(uri.uri.clone())
|
||||
// .change_context(UnifiedConnectorServiceError::ParsingFailed)?;
|
||||
// let image_data_url = Url::parse(image_data.data.clone().as_str())
|
||||
// .change_context(UnifiedConnectorServiceError::ParsingFailed)?;
|
||||
// let qr_code_info = QrCodeInformation::QrDataUrl {
|
||||
// image_data_url,
|
||||
// display_to_timestamp: None,
|
||||
// };
|
||||
// (
|
||||
// Some(qr_code_info.encode_to_value())
|
||||
// .transpose()
|
||||
// .change_context(UnifiedConnectorServiceError::ParsingFailed)?,
|
||||
// None,
|
||||
// )
|
||||
// }
|
||||
// _ => (
|
||||
// None,
|
||||
// Some(RedirectForm::foreign_try_from(redirection_data)).transpose()?,
|
||||
// ),
|
||||
// },
|
||||
// None => (None, None),
|
||||
// },
|
||||
// None => (None, 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(
|
||||
// redirection_data
|
||||
// ),
|
||||
// mandate_reference: Box::new(None),
|
||||
// connector_metadata,
|
||||
// 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)?;
|
||||
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use api_models::payments::QrCodeInformation;
|
||||
use common_enums::{AttemptStatus, AuthenticationType};
|
||||
use common_utils::request::Method;
|
||||
use common_utils::{ext_traits::Encode, request::Method};
|
||||
use diesel_models::enums as storage_enums;
|
||||
use error_stack::ResultExt;
|
||||
use external_services::grpc_client::unified_connector_service::UnifiedConnectorServiceError;
|
||||
use hyperswitch_connectors::utils::QrImage;
|
||||
use hyperswitch_domain_models::{
|
||||
router_data::{ErrorResponse, RouterData},
|
||||
router_flow_types::payments::{Authorize, PSync, SetupMandate},
|
||||
@ -14,7 +16,9 @@ use hyperswitch_domain_models::{
|
||||
router_response_types::{PaymentsResponseData, RedirectForm},
|
||||
};
|
||||
use masking::{ExposeInterface, PeekInterface};
|
||||
use router_env::tracing;
|
||||
use unified_connector_service_client::payments::{self as payments_grpc, Identifier};
|
||||
use url::Url;
|
||||
|
||||
use crate::{
|
||||
core::unified_connector_service::build_unified_connector_service_payment_method,
|
||||
@ -364,6 +368,35 @@ impl ForeignTryFrom<payments_grpc::PaymentServiceAuthorizeResponse>
|
||||
})
|
||||
});
|
||||
|
||||
let (connector_metadata, redirection_data) = match response.redirection_data.clone() {
|
||||
Some(redirection_data) => match redirection_data.form_type {
|
||||
Some(ref form_type) => match form_type {
|
||||
payments_grpc::redirect_form::FormType::Uri(uri) => {
|
||||
let image_data = QrImage::new_from_data(uri.uri.clone())
|
||||
.change_context(UnifiedConnectorServiceError::ParsingFailed)?;
|
||||
let image_data_url = Url::parse(image_data.data.clone().as_str())
|
||||
.change_context(UnifiedConnectorServiceError::ParsingFailed)?;
|
||||
let qr_code_info = QrCodeInformation::QrDataUrl {
|
||||
image_data_url,
|
||||
display_to_timestamp: None,
|
||||
};
|
||||
(
|
||||
Some(qr_code_info.encode_to_value())
|
||||
.transpose()
|
||||
.change_context(UnifiedConnectorServiceError::ParsingFailed)?,
|
||||
None,
|
||||
)
|
||||
}
|
||||
_ => (
|
||||
None,
|
||||
Some(RedirectForm::foreign_try_from(redirection_data)).transpose()?,
|
||||
),
|
||||
},
|
||||
None => (None, None),
|
||||
},
|
||||
None => (None, None),
|
||||
};
|
||||
|
||||
let response = if response.error_code.is_some() {
|
||||
Err(ErrorResponse {
|
||||
code: response.error_code().to_owned(),
|
||||
@ -383,14 +416,10 @@ impl ForeignTryFrom<payments_grpc::PaymentServiceAuthorizeResponse>
|
||||
None => hyperswitch_domain_models::router_request_types::ResponseId::NoResponseId,
|
||||
},
|
||||
redirection_data: Box::new(
|
||||
response
|
||||
.redirection_data
|
||||
.clone()
|
||||
.map(RedirectForm::foreign_try_from)
|
||||
.transpose()?
|
||||
redirection_data
|
||||
),
|
||||
mandate_reference: Box::new(None),
|
||||
connector_metadata: None,
|
||||
connector_metadata,
|
||||
network_txn_id: response.network_txn_id.clone(),
|
||||
connector_response_reference_id,
|
||||
incremental_authorization_allowed: response.incremental_authorization_allowed,
|
||||
@ -907,6 +936,7 @@ impl ForeignTryFrom<payments_grpc::HttpMethod> for Method {
|
||||
type Error = error_stack::Report<UnifiedConnectorServiceError>;
|
||||
|
||||
fn foreign_try_from(value: payments_grpc::HttpMethod) -> Result<Self, Self::Error> {
|
||||
tracing::debug!("Converting gRPC HttpMethod: {:?}", value);
|
||||
match value {
|
||||
payments_grpc::HttpMethod::Get => Ok(Self::Get),
|
||||
payments_grpc::HttpMethod::Post => Ok(Self::Post),
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use error_stack::{report, ResultExt};
|
||||
use hyperswitch_connectors::connectors::{Paytm, Phonepe};
|
||||
|
||||
use crate::{
|
||||
configs::settings::Connectors,
|
||||
@ -442,6 +443,8 @@ impl ConnectorData {
|
||||
.attach_printable(format!("invalid connector name: {connector_name}")))
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
}
|
||||
enums::Connector::Phonepe => Ok(ConnectorEnum::Old(Box::new(Phonepe::new()))),
|
||||
enums::Connector::Paytm => Ok(ConnectorEnum::Old(Box::new(Paytm::new()))),
|
||||
},
|
||||
Err(_) => Err(report!(errors::ConnectorError::InvalidConnectorName)
|
||||
.attach_printable(format!("invalid connector name: {connector_name}")))
|
||||
|
||||
@ -187,6 +187,8 @@ impl ForeignTryFrom<api_enums::Connector> for common_enums::RoutableConnectors {
|
||||
message: "Taxjar is not a routable connector".to_string(),
|
||||
})?
|
||||
}
|
||||
api_enums::Connector::Phonepe => Self::Phonepe,
|
||||
api_enums::Connector::Paytm => Self::Paytm,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user