refactor(connector): [AdyenPlatform] update required fields (#8990)

Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
Kashif
2025-08-19 16:12:23 +05:30
committed by GitHub
parent ba15c0f194
commit 72eb25f074
2 changed files with 150 additions and 160 deletions

View File

@ -2,9 +2,9 @@ use api_models::{payouts, webhooks};
use common_enums::enums; use common_enums::enums;
use common_utils::pii; use common_utils::pii;
use error_stack::{report, ResultExt}; use error_stack::{report, ResultExt};
use hyperswitch_domain_models::types; use hyperswitch_domain_models::types::{self, PayoutsRouterData};
use hyperswitch_interfaces::errors::ConnectorError; use hyperswitch_interfaces::errors::ConnectorError;
use masking::Secret; use masking::{ExposeInterface, Secret};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use super::{AdyenPlatformRouterData, Error}; use super::{AdyenPlatformRouterData, Error};
@ -68,10 +68,11 @@ pub struct AdyenBankAccountDetails {
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct AdyenAccountHolder { pub struct AdyenAccountHolder {
address: AdyenAddress, address: Option<AdyenAddress>,
first_name: Option<Secret<String>>, first_name: Option<Secret<String>>,
last_name: Option<Secret<String>>, last_name: Option<Secret<String>>,
full_name: Option<Secret<String>>, full_name: Option<Secret<String>>,
email: Option<pii::Email>,
#[serde(rename = "reference")] #[serde(rename = "reference")]
customer_id: Option<String>, customer_id: Option<String>,
#[serde(rename = "type")] #[serde(rename = "type")]
@ -251,40 +252,94 @@ impl TryFrom<&hyperswitch_domain_models::address::AddressDetails> for AdyenAddre
} }
} }
impl<F> TryFrom<(&types::PayoutsRouterData<F>, enums::PayoutType)> for AdyenAccountHolder { impl<F> TryFrom<(&PayoutsRouterData<F>, &payouts::CardPayout)> for AdyenAccountHolder {
type Error = Error; type Error = Error;
fn try_from( fn try_from(
(router_data, payout_type): (&types::PayoutsRouterData<F>, enums::PayoutType), (router_data, card): (&PayoutsRouterData<F>, &payouts::CardPayout),
) -> Result<Self, Self::Error> { ) -> Result<Self, Self::Error> {
let billing_address = router_data.get_billing_address()?; let billing_address = router_data.get_optional_billing();
let (first_name, last_name, full_name) = match payout_type {
enums::PayoutType::Card => ( // Address is required for both card and bank payouts
Some(router_data.get_billing_first_name()?), let address = billing_address
Some(router_data.get_billing_last_name()?), .and_then(|billing| billing.address.as_ref().map(|addr| addr.try_into()))
None, .transpose()?
), .ok_or(ConnectorError::MissingRequiredField {
enums::PayoutType::Bank => (None, None, Some(router_data.get_billing_full_name()?)), field_name: "address",
_ => Err(ConnectorError::NotSupported { })?;
message: "Payout method not supported".to_string(),
connector: "Adyen", let (first_name, last_name) = if let Some(card_holder_name) = &card.card_holder_name {
})?, let exposed_name = card_holder_name.clone().expose();
let name_parts: Vec<&str> = exposed_name.split_whitespace().collect();
let first_name = name_parts
.first()
.map(|s| Secret::new(s.to_string()))
.ok_or(ConnectorError::MissingRequiredField {
field_name: "card_holder_name.first_name",
})?;
let last_name = if name_parts.len() > 1 {
let remaining_names: Vec<&str> = name_parts.iter().skip(1).copied().collect();
Some(Secret::new(remaining_names.join(" ")))
} else {
return Err(ConnectorError::MissingRequiredField {
field_name: "card_holder_name.last_name",
}
.into());
};
(Some(first_name), last_name)
} else {
return Err(ConnectorError::MissingRequiredField {
field_name: "card_holder_name",
}
.into());
}; };
Ok(Self { Ok(Self {
address: billing_address.try_into()?, address: Some(address),
first_name, first_name,
last_name, last_name,
full_name, full_name: None,
email: router_data.get_optional_billing_email(),
customer_id: Some(router_data.get_customer_id()?.get_string_repr().to_owned()), customer_id: Some(router_data.get_customer_id()?.get_string_repr().to_owned()),
entity_type: Some(EntityType::from(router_data.request.entity_type)), entity_type: Some(EntityType::from(router_data.request.entity_type)),
}) })
} }
} }
impl<F> TryFrom<&AdyenPlatformRouterData<&types::PayoutsRouterData<F>>> for AdyenTransferRequest { impl<F> TryFrom<(&PayoutsRouterData<F>, &payouts::Bank)> for AdyenAccountHolder {
type Error = Error;
fn try_from(
(router_data, _bank): (&PayoutsRouterData<F>, &payouts::Bank),
) -> Result<Self, Self::Error> {
let billing_address = router_data.get_optional_billing();
// Address is required for both card and bank payouts
let address = billing_address
.and_then(|billing| billing.address.as_ref().map(|addr| addr.try_into()))
.transpose()?
.ok_or(ConnectorError::MissingRequiredField {
field_name: "address",
})?;
let full_name = router_data.get_billing_full_name()?;
Ok(Self {
address: Some(address),
first_name: None,
last_name: None,
full_name: Some(full_name),
email: router_data.get_optional_billing_email(),
customer_id: Some(router_data.get_customer_id()?.get_string_repr().to_owned()),
entity_type: Some(EntityType::from(router_data.request.entity_type)),
})
}
}
impl<F> TryFrom<&AdyenPlatformRouterData<&PayoutsRouterData<F>>> for AdyenTransferRequest {
type Error = Error; type Error = Error;
fn try_from( fn try_from(
item: &AdyenPlatformRouterData<&types::PayoutsRouterData<F>>, item: &AdyenPlatformRouterData<&PayoutsRouterData<F>>,
) -> Result<Self, Self::Error> { ) -> Result<Self, Self::Error> {
let request = &item.router_data.request; let request = &item.router_data.request;
let (counterparty, priority) = match item.router_data.get_payout_method_data()? { let (counterparty, priority) = match item.router_data.get_payout_method_data()? {
@ -292,8 +347,7 @@ impl<F> TryFrom<&AdyenPlatformRouterData<&types::PayoutsRouterData<F>>> for Adye
utils::get_unimplemented_payment_method_error_message("Adyenplatform"), utils::get_unimplemented_payment_method_error_message("Adyenplatform"),
))?, ))?,
payouts::PayoutMethodData::Card(c) => { payouts::PayoutMethodData::Card(c) => {
let card_holder: AdyenAccountHolder = let card_holder: AdyenAccountHolder = (item.router_data, &c).try_into()?;
(item.router_data, enums::PayoutType::Card).try_into()?;
let card_identification = AdyenCardIdentification { let card_identification = AdyenCardIdentification {
card_number: c.card_number, card_number: c.card_number,
expiry_month: c.expiry_month, expiry_month: c.expiry_month,
@ -309,8 +363,7 @@ impl<F> TryFrom<&AdyenPlatformRouterData<&types::PayoutsRouterData<F>>> for Adye
(counterparty, None) (counterparty, None)
} }
payouts::PayoutMethodData::Bank(bd) => { payouts::PayoutMethodData::Bank(bd) => {
let account_holder: AdyenAccountHolder = let account_holder: AdyenAccountHolder = (item.router_data, &bd).try_into()?;
(item.router_data, enums::PayoutType::Bank).try_into()?;
let bank_details = match bd { let bank_details = match bd {
payouts::Bank::Sepa(b) => AdyenBankAccountIdentification { payouts::Bank::Sepa(b) => AdyenBankAccountIdentification {
bank_type: "iban".to_string(), bank_type: "iban".to_string(),
@ -367,9 +420,7 @@ impl<F> TryFrom<&AdyenPlatformRouterData<&types::PayoutsRouterData<F>>> for Adye
} }
} }
impl<F> TryFrom<PayoutsResponseRouterData<F, AdyenTransferResponse>> impl<F> TryFrom<PayoutsResponseRouterData<F, AdyenTransferResponse>> for PayoutsRouterData<F> {
for types::PayoutsRouterData<F>
{
type Error = Error; type Error = Error;
fn try_from( fn try_from(
item: PayoutsResponseRouterData<F, AdyenTransferResponse>, item: PayoutsResponseRouterData<F, AdyenTransferResponse>,

View File

@ -66,12 +66,81 @@ impl Default for PayoutRequiredFields {
} }
} }
fn get_billing_details_for_payment_method(
connector: PayoutConnectors,
payment_method_type: PaymentMethodType,
) -> HashMap<String, RequiredFieldInfo> {
match connector {
PayoutConnectors::Adyenplatform => {
let mut fields = HashMap::from([
(
"billing.address.line1".to_string(),
RequiredFieldInfo {
required_field: "billing.address.line1".to_string(),
display_name: "billing_address_line1".to_string(),
field_type: FieldType::Text,
value: None,
},
),
(
"billing.address.line2".to_string(),
RequiredFieldInfo {
required_field: "billing.address.line2".to_string(),
display_name: "billing_address_line2".to_string(),
field_type: FieldType::Text,
value: None,
},
),
(
"billing.address.city".to_string(),
RequiredFieldInfo {
required_field: "billing.address.city".to_string(),
display_name: "billing_address_city".to_string(),
field_type: FieldType::Text,
value: None,
},
),
(
"billing.address.country".to_string(),
RequiredFieldInfo {
required_field: "billing.address.country".to_string(),
display_name: "billing_address_country".to_string(),
field_type: FieldType::UserAddressCountry {
options: get_countries_for_connector(connector)
.iter()
.map(|country| country.to_string())
.collect::<Vec<String>>(),
},
value: None,
},
),
]);
// Add first_name for bank payouts only
if payment_method_type == PaymentMethodType::Sepa {
fields.insert(
"billing.address.first_name".to_string(),
RequiredFieldInfo {
required_field: "billing.address.first_name".to_string(),
display_name: "billing_address_first_name".to_string(),
field_type: FieldType::Text,
value: None,
},
);
}
fields
}
_ => get_billing_details(connector),
}
}
#[cfg(feature = "v1")] #[cfg(feature = "v1")]
fn get_connector_payment_method_type_fields( fn get_connector_payment_method_type_fields(
connector: PayoutConnectors, connector: PayoutConnectors,
payment_method_type: PaymentMethodType, payment_method_type: PaymentMethodType,
) -> (PaymentMethodType, ConnectorFields) { ) -> (PaymentMethodType, ConnectorFields) {
let mut common_fields = get_billing_details(connector); let mut common_fields = get_billing_details_for_payment_method(connector, payment_method_type);
match payment_method_type { match payment_method_type {
// Card // Card
PaymentMethodType::Debit => { PaymentMethodType::Debit => {
@ -409,67 +478,6 @@ fn get_billing_details(connector: PayoutConnectors) -> HashMap<String, RequiredF
}, },
), ),
]), ]),
PayoutConnectors::Adyenplatform => HashMap::from([
(
"billing.address.line1".to_string(),
RequiredFieldInfo {
required_field: "billing.address.line1".to_string(),
display_name: "billing_address_line1".to_string(),
field_type: FieldType::Text,
value: None,
},
),
(
"billing.address.line2".to_string(),
RequiredFieldInfo {
required_field: "billing.address.line2".to_string(),
display_name: "billing_address_line2".to_string(),
field_type: FieldType::Text,
value: None,
},
),
(
"billing.address.city".to_string(),
RequiredFieldInfo {
required_field: "billing.address.city".to_string(),
display_name: "billing_address_city".to_string(),
field_type: FieldType::Text,
value: None,
},
),
(
"billing.address.country".to_string(),
RequiredFieldInfo {
required_field: "billing.address.country".to_string(),
display_name: "billing_address_country".to_string(),
field_type: FieldType::UserAddressCountry {
options: get_countries_for_connector(connector)
.iter()
.map(|country| country.to_string())
.collect::<Vec<String>>(),
},
value: None,
},
),
(
"billing.address.first_name".to_string(),
RequiredFieldInfo {
required_field: "billing.address.first_name".to_string(),
display_name: "billing_address_first_name".to_string(),
field_type: FieldType::Text,
value: None,
},
),
(
"billing.address.last_name".to_string(),
RequiredFieldInfo {
required_field: "billing.address.last_name".to_string(),
display_name: "billing_address_last_name".to_string(),
field_type: FieldType::Text,
value: None,
},
),
]),
PayoutConnectors::Wise => HashMap::from([ PayoutConnectors::Wise => HashMap::from([
( (
"billing.address.line1".to_string(), "billing.address.line1".to_string(),
@ -531,75 +539,6 @@ fn get_billing_details(connector: PayoutConnectors) -> HashMap<String, RequiredF
}, },
), ),
]), ]),
_ => HashMap::from([ _ => HashMap::from([]),
(
"billing.address.line1".to_string(),
RequiredFieldInfo {
required_field: "billing.address.line1".to_string(),
display_name: "billing_address_line1".to_string(),
field_type: FieldType::Text,
value: None,
},
),
(
"billing.address.line2".to_string(),
RequiredFieldInfo {
required_field: "billing.address.line2".to_string(),
display_name: "billing_address_line2".to_string(),
field_type: FieldType::Text,
value: None,
},
),
(
"billing.address.city".to_string(),
RequiredFieldInfo {
required_field: "billing.address.city".to_string(),
display_name: "billing_address_city".to_string(),
field_type: FieldType::Text,
value: None,
},
),
(
"billing.address.zip".to_string(),
RequiredFieldInfo {
required_field: "billing.address.zip".to_string(),
display_name: "billing_address_zip".to_string(),
field_type: FieldType::Text,
value: None,
},
),
(
"billing.address.country".to_string(),
RequiredFieldInfo {
required_field: "billing.address.country".to_string(),
display_name: "billing_address_country".to_string(),
field_type: FieldType::UserAddressCountry {
options: get_countries_for_connector(connector)
.iter()
.map(|country| country.to_string())
.collect::<Vec<String>>(),
},
value: None,
},
),
(
"billing.address.first_name".to_string(),
RequiredFieldInfo {
required_field: "billing.address.first_name".to_string(),
display_name: "billing_address_first_name".to_string(),
field_type: FieldType::Text,
value: None,
},
),
(
"billing.address.last_name".to_string(),
RequiredFieldInfo {
required_field: "billing.address.last_name".to_string(),
display_name: "billing_address_last_name".to_string(),
field_type: FieldType::Text,
value: None,
},
),
]),
} }
} }