feat(core): Added support for unified_connector_service CardNumber and Secret<String> Type (#9044)

Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
Debarshi Gupta
2025-08-27 21:07:24 +05:30
committed by GitHub
parent 4b7f2093db
commit cf64d2a9dc
5 changed files with 186 additions and 91 deletions

View File

@ -89,7 +89,8 @@ reqwest = { version = "0.11.27", features = ["json", "rustls-tls", "gzip", "mult
ring = "0.17.14"
rust_decimal = { version = "1.37.1", features = ["serde-with-float", "serde-with-str"] }
rust-i18n = { git = "https://github.com/kashif-m/rust-i18n", rev = "f2d8096aaaff7a87a847c35a5394c269f75e077a" }
unified-connector-service-client = { git = "https://github.com/juspay/connector-service", rev = "aae51574fed25b0a7849e3832cff8904bc310cf5", package = "rust-grpc-client" }
unified-connector-service-client = { git = "https://github.com/juspay/connector-service", rev = "a56de0a4ee393af5c401f02b6b4344cb263f4cac", package = "rust-grpc-client" }
unified-connector-service-cards = { git = "https://github.com/juspay/connector-service", rev = "a56de0a4ee393af5c401f02b6b4344cb263f4cac", package = "ucs_cards" }
rustc-hash = "1.1.0"
rustls = "0.22"
rustls-pemfile = "2"

View File

@ -18,6 +18,7 @@ use hyperswitch_domain_models::{
};
use masking::{ExposeInterface, PeekInterface, Secret};
use router_env::logger;
use unified_connector_service_cards::CardNumber;
use unified_connector_service_client::payments::{
self as payments_grpc, payment_method::PaymentMethod, CardDetails, CardPaymentMethodType,
PaymentServiceAuthorizeResponse, RewardPaymentMethodType,
@ -277,11 +278,17 @@ pub fn build_unified_connector_service_payment_method(
.transpose()?;
let card_details = CardDetails {
card_number: card.card_number.get_card_no(),
card_exp_month,
card_exp_year: card.get_expiry_year_4_digit().peek().to_string(),
card_cvc: card.card_cvc.peek().to_string(),
card_holder_name: card.card_holder_name.map(|name| name.expose()),
card_number: Some(
CardNumber::from_str(&card.card_number.get_card_no()).change_context(
UnifiedConnectorServiceError::RequestEncodingFailedWithReason(
"Failed to parse card number".to_string(),
),
)?,
),
card_exp_month: Some(card_exp_month.into()),
card_exp_year: Some(card.get_expiry_year_4_digit().expose().into()),
card_cvc: Some(card.card_cvc.expose().into()),
card_holder_name: card.card_holder_name.map(|name| name.expose().into()),
card_issuer: card.card_issuer.clone(),
card_network: card_network.map(|card_network| card_network.into()),
card_type: card.card_type.clone(),
@ -316,12 +323,13 @@ pub fn build_unified_connector_service_payment_method(
hyperswitch_domain_models::payment_method_data::UpiData::UpiCollect(
upi_collect_data,
) => {
let vpa_id = upi_collect_data.vpa_id.map(|vpa| vpa.expose());
let upi_details = payments_grpc::UpiCollect { vpa_id };
let upi_details = payments_grpc::UpiCollect {
vpa_id: upi_collect_data.vpa_id.map(|vpa| vpa.expose().into()),
};
PaymentMethod::UpiCollect(upi_details)
}
hyperswitch_domain_models::payment_method_data::UpiData::UpiIntent(_) => {
let upi_details = payments_grpc::UpiIntent {};
let upi_details = payments_grpc::UpiIntent { app_name: None };
PaymentMethod::UpiIntent(upi_details)
}
};
@ -369,13 +377,13 @@ pub fn build_unified_connector_service_payment_method_for_external_proxy(
.map(payments_grpc::CardNetwork::foreign_try_from)
.transpose()?;
let card_details = CardDetails {
card_number: external_vault_card.card_number.peek().to_string(),
card_exp_month: external_vault_card.card_exp_month.peek().to_string(),
card_exp_year: external_vault_card.card_exp_year.peek().to_string(),
card_cvc: external_vault_card.card_cvc.peek().to_string(),
card_holder_name: external_vault_card
.card_holder_name
.map(|name| name.expose()),
card_number: Some(CardNumber::from_str(external_vault_card.card_number.peek()).change_context(
UnifiedConnectorServiceError::RequestEncodingFailedWithReason("Failed to parse card number".to_string())
)?),
card_exp_month: Some(external_vault_card.card_exp_month.expose().into()),
card_exp_year: Some(external_vault_card.card_exp_year.expose().into()),
card_cvc: Some(external_vault_card.card_cvc.expose().into()),
card_holder_name: external_vault_card.card_holder_name.map(|name| name.expose().into()),
card_issuer: external_vault_card.card_issuer.clone(),
card_network: card_network.map(|card_network| card_network.into()),
card_type: external_vault_card.card_type.clone(),

View File

@ -141,7 +141,7 @@ impl ForeignTryFrom<&RouterData<Authorize, PaymentsAuthorizeData, PaymentsRespon
.request
.email
.clone()
.map(|e| e.expose().expose()),
.map(|e| e.expose().expose().into()),
browser_info,
access_token: None,
session_token: None,
@ -193,6 +193,7 @@ impl ForeignTryFrom<&RouterData<Authorize, PaymentsAuthorizeData, PaymentsRespon
.collect::<HashMap<String, String>>()
})
.unwrap_or_default(),
test_mode: None,
})
}
}
@ -264,7 +265,7 @@ impl
.request
.email
.clone()
.map(|e| e.expose().expose()),
.map(|e| e.expose().expose().into()),
browser_info,
access_token: None,
session_token: None,
@ -316,6 +317,7 @@ impl
.collect::<HashMap<String, String>>()
})
.unwrap_or_default(),
test_mode: None,
})
}
}
@ -372,7 +374,7 @@ impl ForeignTryFrom<&RouterData<SetupMandate, SetupMandateRequestData, PaymentsR
.request
.email
.clone()
.map(|e| e.expose().expose()),
.map(|e| e.expose().expose().into()),
customer_name: router_data
.request
.customer_name
@ -495,8 +497,10 @@ impl ForeignTryFrom<&RouterData<Authorize, PaymentsAuthorizeData, PaymentsRespon
.request
.email
.clone()
.map(|e| e.expose().expose()),
.map(|e| e.expose().expose().into()),
browser_info,
test_mode: None,
payment_method_type: None,
})
}
}
@ -861,11 +865,6 @@ impl ForeignTryFrom<hyperswitch_domain_models::payment_address::PaymentAddress>
let shipping = payment_address.get_shipping().map(|address| {
let details = address.address.as_ref();
let get_str =
|opt: &Option<masking::Secret<String>>| opt.as_ref().map(|s| s.peek().to_owned());
let get_plain = |opt: &Option<String>| opt.clone();
let country = details.and_then(|details| {
details
.country
@ -875,20 +874,23 @@ impl ForeignTryFrom<hyperswitch_domain_models::payment_address::PaymentAddress>
});
payments_grpc::Address {
first_name: get_str(&details.and_then(|d| d.first_name.clone())),
last_name: get_str(&details.and_then(|d| d.last_name.clone())),
line1: get_str(&details.and_then(|d| d.line1.clone())),
line2: get_str(&details.and_then(|d| d.line2.clone())),
line3: get_str(&details.and_then(|d| d.line3.clone())),
city: get_plain(&details.and_then(|d| d.city.clone())),
state: get_str(&details.and_then(|d| d.state.clone())),
zip_code: get_str(&details.and_then(|d| d.zip.clone())),
first_name: details.and_then(|d| d.first_name.as_ref().map(|s| s.clone().expose())),
last_name: details.and_then(|d| d.last_name.as_ref().map(|s| s.clone().expose())),
line1: details.and_then(|d| d.line1.as_ref().map(|s| s.clone().expose().into())),
line2: details.and_then(|d| d.line2.as_ref().map(|s| s.clone().expose().into())),
line3: details.and_then(|d| d.line3.as_ref().map(|s| s.clone().expose().into())),
city: details.and_then(|d| d.city.as_ref().map(|s| s.clone().into())),
state: details.and_then(|d| d.state.as_ref().map(|s| s.clone().expose().into())),
zip_code: details.and_then(|d| d.zip.as_ref().map(|s| s.clone().expose().into())),
country_alpha2_code: country,
email: address.email.as_ref().map(|e| e.peek().to_string()),
email: address
.email
.as_ref()
.map(|e| e.clone().expose().expose().into()),
phone_number: address
.phone
.as_ref()
.and_then(|phone| phone.number.as_ref().map(|n| n.peek().to_string())),
.and_then(|phone| phone.number.as_ref().map(|n| n.clone().expose().into())),
phone_country_code: address.phone.as_ref().and_then(|p| p.country_code.clone()),
}
});
@ -896,11 +898,6 @@ impl ForeignTryFrom<hyperswitch_domain_models::payment_address::PaymentAddress>
let billing = payment_address.get_payment_billing().map(|address| {
let details = address.address.as_ref();
let get_str =
|opt: &Option<masking::Secret<String>>| opt.as_ref().map(|s| s.peek().to_owned());
let get_plain = |opt: &Option<String>| opt.clone();
let country = details.and_then(|details| {
details
.country
@ -910,20 +907,21 @@ impl ForeignTryFrom<hyperswitch_domain_models::payment_address::PaymentAddress>
});
payments_grpc::Address {
first_name: get_str(&details.and_then(|d| d.first_name.clone())),
last_name: get_str(&details.and_then(|d| d.last_name.clone())),
line1: get_str(&details.and_then(|d| d.line1.clone())),
line2: get_str(&details.and_then(|d| d.line2.clone())),
line3: get_str(&details.and_then(|d| d.line3.clone())),
city: get_plain(&details.and_then(|d| d.city.clone())),
state: get_str(&details.and_then(|d| d.state.clone())),
zip_code: get_str(&details.and_then(|d| d.zip.clone())),
first_name: details
.and_then(|d| d.first_name.as_ref().map(|s| s.peek().to_string())),
last_name: details.and_then(|d| d.last_name.as_ref().map(|s| s.peek().to_string())),
line1: details.and_then(|d| d.line1.as_ref().map(|s| s.peek().to_string().into())),
line2: details.and_then(|d| d.line2.as_ref().map(|s| s.peek().to_string().into())),
line3: details.and_then(|d| d.line3.as_ref().map(|s| s.peek().to_string().into())),
city: details.and_then(|d| d.city.as_ref().map(|s| s.clone().into())),
state: details.and_then(|d| d.state.as_ref().map(|s| s.peek().to_string().into())),
zip_code: details.and_then(|d| d.zip.as_ref().map(|s| s.peek().to_string().into())),
country_alpha2_code: country,
email: address.email.as_ref().map(|e| e.peek().to_string()),
email: address.email.as_ref().map(|e| e.peek().to_string().into()),
phone_number: address
.phone
.as_ref()
.and_then(|phone| phone.number.as_ref().map(|n| n.peek().to_string())),
.and_then(|phone| phone.number.as_ref().map(|n| n.peek().to_string().into())),
phone_country_code: address.phone.as_ref().and_then(|p| p.country_code.clone()),
}
});
@ -932,12 +930,6 @@ impl ForeignTryFrom<hyperswitch_domain_models::payment_address::PaymentAddress>
payment_address.get_payment_method_billing().map(|address| {
let details = address.address.as_ref();
let get_str = |opt: &Option<masking::Secret<String>>| {
opt.as_ref().map(|s| s.peek().to_owned())
};
let get_plain = |opt: &Option<String>| opt.clone();
let country = details.and_then(|details| {
details
.country
@ -947,20 +939,30 @@ impl ForeignTryFrom<hyperswitch_domain_models::payment_address::PaymentAddress>
});
payments_grpc::Address {
first_name: get_str(&details.and_then(|d| d.first_name.clone())),
last_name: get_str(&details.and_then(|d| d.last_name.clone())),
line1: get_str(&details.and_then(|d| d.line1.clone())),
line2: get_str(&details.and_then(|d| d.line2.clone())),
line3: get_str(&details.and_then(|d| d.line3.clone())),
city: get_plain(&details.and_then(|d| d.city.clone())),
state: get_str(&details.and_then(|d| d.state.clone())),
zip_code: get_str(&details.and_then(|d| d.zip.clone())),
first_name: details
.and_then(|d| d.first_name.as_ref().map(|s| s.peek().to_string())),
last_name: details
.and_then(|d| d.last_name.as_ref().map(|s| s.peek().to_string())),
line1: details
.and_then(|d| d.line1.as_ref().map(|s| s.peek().to_string().into())),
line2: details
.and_then(|d| d.line2.as_ref().map(|s| s.peek().to_string().into())),
line3: details
.and_then(|d| d.line3.as_ref().map(|s| s.peek().to_string().into())),
city: details.and_then(|d| d.city.as_ref().map(|s| s.clone().into())),
state: details
.and_then(|d| d.state.as_ref().map(|s| s.peek().to_string().into())),
zip_code: details
.and_then(|d| d.zip.as_ref().map(|s| s.peek().to_string().into())),
country_alpha2_code: country,
email: address.email.as_ref().map(|e| e.peek().to_string()),
email: address
.email
.as_ref()
.map(|e| e.clone().expose().expose().into()),
phone_number: address
.phone
.as_ref()
.and_then(|phone| phone.number.as_ref().map(|n| n.peek().to_string())),
.and_then(|phone| phone.number.as_ref().map(|n| n.clone().expose().into())),
phone_country_code: address.phone.as_ref().and_then(|p| p.country_code.clone()),
}
});