Skip to content

Commit cea89f9

Browse files
feat(payments): add support for connector testing (Adyen) (#7874)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> (cherry picked from commit f1bb4a0)
1 parent 2c9b8ab commit cea89f9

File tree

12 files changed

+256
-51
lines changed

12 files changed

+256
-51
lines changed

api-reference-v2/openapi_spec.json

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3775,6 +3775,17 @@
37753775
},
37763776
"additionalProperties": false
37773777
},
3778+
"AdyenConnectorMetadata": {
3779+
"type": "object",
3780+
"required": [
3781+
"testing"
3782+
],
3783+
"properties": {
3784+
"testing": {
3785+
"$ref": "#/components/schemas/AdyenTestingData"
3786+
}
3787+
}
3788+
},
37783789
"AdyenSplitData": {
37793790
"type": "object",
37803791
"description": "Fee information for Split Payments to be charged on the payment being collected for Adyen",
@@ -3848,6 +3859,18 @@
38483859
"Vat"
38493860
]
38503861
},
3862+
"AdyenTestingData": {
3863+
"type": "object",
3864+
"required": [
3865+
"holder_name"
3866+
],
3867+
"properties": {
3868+
"holder_name": {
3869+
"type": "string",
3870+
"description": "Holder name to be sent to Adyen for a card payment(CIT) or a generic payment(MIT). This value overrides the values for card.card_holder_name and applies during both CIT and MIT payment transactions."
3871+
}
3872+
}
3873+
},
38513874
"AirwallexData": {
38523875
"type": "object",
38533876
"properties": {
@@ -7960,6 +7983,14 @@
79607983
}
79617984
],
79627985
"nullable": true
7986+
},
7987+
"adyen": {
7988+
"allOf": [
7989+
{
7990+
"$ref": "#/components/schemas/AdyenConnectorMetadata"
7991+
}
7992+
],
7993+
"nullable": true
79637994
}
79647995
}
79657996
},

api-reference/openapi_spec.json

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6069,6 +6069,17 @@
60696069
},
60706070
"additionalProperties": false
60716071
},
6072+
"AdyenConnectorMetadata": {
6073+
"type": "object",
6074+
"required": [
6075+
"testing"
6076+
],
6077+
"properties": {
6078+
"testing": {
6079+
"$ref": "#/components/schemas/AdyenTestingData"
6080+
}
6081+
}
6082+
},
60726083
"AdyenSplitData": {
60736084
"type": "object",
60746085
"description": "Fee information for Split Payments to be charged on the payment being collected for Adyen",
@@ -6142,6 +6153,18 @@
61426153
"Vat"
61436154
]
61446155
},
6156+
"AdyenTestingData": {
6157+
"type": "object",
6158+
"required": [
6159+
"holder_name"
6160+
],
6161+
"properties": {
6162+
"holder_name": {
6163+
"type": "string",
6164+
"description": "Holder name to be sent to Adyen for a card payment(CIT) or a generic payment(MIT). This value overrides the values for card.card_holder_name and applies during both CIT and MIT payment transactions."
6165+
}
6166+
}
6167+
},
61456168
"AirwallexData": {
61466169
"type": "object",
61476170
"properties": {
@@ -10010,6 +10033,14 @@
1001010033
}
1001110034
],
1001210035
"nullable": true
10036+
},
10037+
"adyen": {
10038+
"allOf": [
10039+
{
10040+
"$ref": "#/components/schemas/AdyenConnectorMetadata"
10041+
}
10042+
],
10043+
"nullable": true
1001310044
}
1001410045
}
1001510046
},

crates/api_models/src/payments.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6720,6 +6720,7 @@ pub struct ConnectorMetadata {
67206720
pub airwallex: Option<AirwallexData>,
67216721
pub noon: Option<NoonData>,
67226722
pub braintree: Option<BraintreeData>,
6723+
pub adyen: Option<AdyenConnectorMetadata>,
67236724
}
67246725

67256726
impl ConnectorMetadata {
@@ -6770,6 +6771,18 @@ pub struct BraintreeData {
67706771
pub merchant_config_currency: Option<api_enums::Currency>,
67716772
}
67726773

6774+
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)]
6775+
pub struct AdyenConnectorMetadata {
6776+
pub testing: AdyenTestingData,
6777+
}
6778+
6779+
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)]
6780+
pub struct AdyenTestingData {
6781+
/// Holder name to be sent to Adyen for a card payment(CIT) or a generic payment(MIT). This value overrides the values for card.card_holder_name and applies during both CIT and MIT payment transactions.
6782+
#[schema(value_type = String)]
6783+
pub holder_name: Option<Secret<String>>,
6784+
}
6785+
67736786
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)]
67746787
pub struct ApplepayConnectorMetadataRequest {
67756788
pub session_token_data: Option<SessionTokenInfo>,

crates/hyperswitch_connectors/src/connectors/adyen/transformers.rs

Lines changed: 95 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use common_enums::enums as storage_enums;
1010
use common_utils::ext_traits::OptionExt;
1111
use common_utils::{
1212
errors::{CustomResult, ParsingError},
13-
ext_traits::Encode,
13+
ext_traits::{Encode, ValueExt},
1414
pii::Email,
1515
request::Method,
1616
types::MinorUnit,
@@ -1222,6 +1222,7 @@ pub struct AdyenMandate {
12221222
#[serde(rename = "type")]
12231223
payment_type: PaymentType,
12241224
stored_payment_method_id: Secret<String>,
1225+
holder_name: Option<Secret<String>>,
12251226
}
12261227

12271228
#[serde_with::skip_serializing_none]
@@ -2008,13 +2009,21 @@ impl TryFrom<(&BankDebitData, &PaymentsAuthorizeRouterData)> for AdyenPaymentMet
20082009
account_number,
20092010
sort_code,
20102011
..
2011-
} => Ok(AdyenPaymentMethod::BacsDirectDebit(Box::new(
2012-
BacsDirectDebitData {
2013-
bank_account_number: account_number.clone(),
2014-
bank_location_id: sort_code.clone(),
2015-
holder_name: item.get_billing_full_name()?,
2016-
},
2017-
))),
2012+
} => {
2013+
let testing_data = item
2014+
.request
2015+
.get_connector_testing_data()
2016+
.map(AdyenTestingData::try_from)
2017+
.transpose()?;
2018+
let test_holder_name = testing_data.and_then(|test_data| test_data.holder_name);
2019+
Ok(AdyenPaymentMethod::BacsDirectDebit(Box::new(
2020+
BacsDirectDebitData {
2021+
bank_account_number: account_number.clone(),
2022+
bank_location_id: sort_code.clone(),
2023+
holder_name: test_holder_name.unwrap_or(item.get_billing_full_name()?),
2024+
},
2025+
)))
2026+
}
20182027

20192028
BankDebitData::BecsBankDebit { .. } => Err(errors::ConnectorError::NotImplemented(
20202029
utils::get_unimplemented_payment_method_error_message("Adyen"),
@@ -2396,30 +2405,38 @@ impl TryFrom<(&BankRedirectData, &PaymentsAuthorizeRouterData)> for AdyenPayment
23962405
card_exp_month,
23972406
card_exp_year,
23982407
..
2399-
} => Ok(AdyenPaymentMethod::BancontactCard(Box::new(AdyenCard {
2400-
brand: Some(CardBrand::Bcmc),
2401-
number: card_number
2402-
.as_ref()
2403-
.ok_or(errors::ConnectorError::MissingRequiredField {
2404-
field_name: "bancontact_card.card_number",
2405-
})?
2406-
.clone(),
2407-
expiry_month: card_exp_month
2408-
.as_ref()
2409-
.ok_or(errors::ConnectorError::MissingRequiredField {
2410-
field_name: "bancontact_card.card_exp_month",
2411-
})?
2412-
.clone(),
2413-
expiry_year: card_exp_year
2414-
.as_ref()
2415-
.ok_or(errors::ConnectorError::MissingRequiredField {
2416-
field_name: "bancontact_card.card_exp_year",
2417-
})?
2418-
.clone(),
2419-
holder_name: Some(item.get_billing_full_name()?),
2420-
cvc: None,
2421-
network_payment_reference: None,
2422-
}))),
2408+
} => {
2409+
let testing_data = item
2410+
.request
2411+
.get_connector_testing_data()
2412+
.map(AdyenTestingData::try_from)
2413+
.transpose()?;
2414+
let test_holder_name = testing_data.and_then(|test_data| test_data.holder_name);
2415+
Ok(AdyenPaymentMethod::BancontactCard(Box::new(AdyenCard {
2416+
brand: Some(CardBrand::Bcmc),
2417+
number: card_number
2418+
.as_ref()
2419+
.ok_or(errors::ConnectorError::MissingRequiredField {
2420+
field_name: "bancontact_card.card_number",
2421+
})?
2422+
.clone(),
2423+
expiry_month: card_exp_month
2424+
.as_ref()
2425+
.ok_or(errors::ConnectorError::MissingRequiredField {
2426+
field_name: "bancontact_card.card_exp_month",
2427+
})?
2428+
.clone(),
2429+
expiry_year: card_exp_year
2430+
.as_ref()
2431+
.ok_or(errors::ConnectorError::MissingRequiredField {
2432+
field_name: "bancontact_card.card_exp_year",
2433+
})?
2434+
.clone(),
2435+
holder_name: test_holder_name.or(Some(item.get_billing_full_name()?)),
2436+
cvc: None,
2437+
network_payment_reference: None,
2438+
})))
2439+
}
24232440
BankRedirectData::Bizum { .. } => Ok(AdyenPaymentMethod::Bizum),
24242441
BankRedirectData::Blik { blik_code } => {
24252442
Ok(AdyenPaymentMethod::Blik(Box::new(BlikRedirectionData {
@@ -2684,6 +2701,13 @@ impl
26842701
let additional_data = get_additional_data(item.router_data);
26852702
let return_url = item.router_data.request.get_router_return_url()?;
26862703
let payment_method_type = item.router_data.request.payment_method_type;
2704+
let testing_data = item
2705+
.router_data
2706+
.request
2707+
.get_connector_testing_data()
2708+
.map(AdyenTestingData::try_from)
2709+
.transpose()?;
2710+
let test_holder_name = testing_data.and_then(|test_data| test_data.holder_name);
26872711
let payment_method = match mandate_ref_id {
26882712
payments::MandateReferenceId::ConnectorMandateId(connector_mandate_ids) => {
26892713
let adyen_mandate = AdyenMandate {
@@ -2696,6 +2720,7 @@ impl
26962720
.get_connector_mandate_id()
26972721
.ok_or_else(missing_field_err("mandate_id"))?,
26982722
),
2723+
holder_name: test_holder_name,
26992724
};
27002725
Ok::<PaymentMethod<'_>, Self::Error>(PaymentMethod::AdyenMandatePaymentMethod(
27012726
Box::new(adyen_mandate),
@@ -2727,7 +2752,7 @@ impl
27272752
.get_expiry_year_4_digit()
27282753
.clone(),
27292754
cvc: None,
2730-
holder_name: card_holder_name,
2755+
holder_name: test_holder_name.or(card_holder_name),
27312756
brand: Some(brand),
27322757
network_payment_reference: Some(Secret::new(network_mandate_id)),
27332758
};
@@ -2768,7 +2793,7 @@ impl
27682793
number: token_data.get_network_token(),
27692794
expiry_month: token_data.get_network_token_expiry_month(),
27702795
expiry_year: token_data.get_expiry_year_4_digit(),
2771-
holder_name: card_holder_name,
2796+
holder_name: test_holder_name.or(card_holder_name),
27722797
brand: Some(brand), // FIXME: Remove hardcoding
27732798
network_payment_reference: Some(Secret::new(
27742799
network_mandate_id.network_transaction_id,
@@ -2879,7 +2904,15 @@ impl TryFrom<(&AdyenRouterData<&PaymentsAuthorizeRouterData>, &Card)> for AdyenP
28792904
let country_code = get_country_code(item.router_data.get_optional_billing());
28802905
let additional_data = get_additional_data(item.router_data);
28812906
let return_url = item.router_data.request.get_router_return_url()?;
2882-
let card_holder_name = item.router_data.get_optional_billing_full_name();
2907+
let testing_data = item
2908+
.router_data
2909+
.request
2910+
.get_connector_testing_data()
2911+
.map(AdyenTestingData::try_from)
2912+
.transpose()?;
2913+
let test_holder_name = testing_data.and_then(|test_data| test_data.holder_name);
2914+
let card_holder_name =
2915+
test_holder_name.or(item.router_data.get_optional_billing_full_name());
28832916
let payment_method = PaymentMethod::AdyenPaymentMethod(Box::new(
28842917
AdyenPaymentMethod::try_from((card_data, card_holder_name))?,
28852918
));
@@ -5505,6 +5538,24 @@ pub struct DefenseDocuments {
55055538
defense_document_type_code: String,
55065539
}
55075540

5541+
#[derive(Debug, Deserialize)]
5542+
pub struct AdyenTestingData {
5543+
holder_name: Option<Secret<String>>,
5544+
}
5545+
5546+
impl TryFrom<common_utils::pii::SecretSerdeValue> for AdyenTestingData {
5547+
type Error = error_stack::Report<errors::ConnectorError>;
5548+
fn try_from(testing_data: common_utils::pii::SecretSerdeValue) -> Result<Self, Self::Error> {
5549+
let testing_data = testing_data
5550+
.expose()
5551+
.parse_value::<Self>("AdyenTestingData")
5552+
.change_context(errors::ConnectorError::InvalidDataFormat {
5553+
field_name: "connector_metadata.adyen.testing",
5554+
})?;
5555+
Ok(testing_data)
5556+
}
5557+
}
5558+
55085559
impl TryFrom<&SubmitEvidenceRouterData> for Evidence {
55095560
type Error = error_stack::Report<errors::ConnectorError>;
55105561
fn try_from(item: &SubmitEvidenceRouterData) -> Result<Self, Self::Error> {
@@ -5782,7 +5833,15 @@ impl
57825833
let country_code = get_country_code(item.router_data.get_optional_billing());
57835834
let additional_data = get_additional_data(item.router_data);
57845835
let return_url = item.router_data.request.get_router_return_url()?;
5785-
let card_holder_name = item.router_data.get_optional_billing_full_name();
5836+
let testing_data = item
5837+
.router_data
5838+
.request
5839+
.get_connector_testing_data()
5840+
.map(AdyenTestingData::try_from)
5841+
.transpose()?;
5842+
let test_holder_name = testing_data.and_then(|test_data| test_data.holder_name);
5843+
let card_holder_name =
5844+
test_holder_name.or(item.router_data.get_optional_billing_full_name());
57865845
let payment_method = PaymentMethod::AdyenPaymentMethod(Box::new(
57875846
AdyenPaymentMethod::try_from((token_data, card_holder_name))?,
57885847
));

crates/hyperswitch_connectors/src/utils.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1685,6 +1685,7 @@ pub trait PaymentsAuthorizeRequestData {
16851685
fn get_card_network_from_additional_payment_method_data(
16861686
&self,
16871687
) -> Result<enums::CardNetwork, Error>;
1688+
fn get_connector_testing_data(&self) -> Option<pii::SecretSerdeValue>;
16881689
}
16891690

16901691
impl PaymentsAuthorizeRequestData for PaymentsAuthorizeData {
@@ -1905,6 +1906,9 @@ impl PaymentsAuthorizeRequestData for PaymentsAuthorizeData {
19051906
.into()),
19061907
}
19071908
}
1909+
fn get_connector_testing_data(&self) -> Option<pii::SecretSerdeValue> {
1910+
self.connector_testing_data.clone()
1911+
}
19081912
}
19091913

19101914
pub trait PaymentsCaptureRequestData {
@@ -5998,6 +6002,7 @@ pub(crate) fn convert_setup_mandate_router_data_to_authorize_router_data(
59986002
shipping_cost: data.request.shipping_cost,
59996003
merchant_account_id: None,
60006004
merchant_config_currency: None,
6005+
connector_testing_data: data.request.connector_testing_data.clone(),
60016006
}
60026007
}
60036008

crates/hyperswitch_domain_models/src/router_request_types.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ pub struct PaymentsAuthorizeData {
7676
pub additional_payment_method_data: Option<AdditionalPaymentData>,
7777
pub merchant_account_id: Option<Secret<String>>,
7878
pub merchant_config_currency: Option<storage_enums::Currency>,
79+
pub connector_testing_data: Option<pii::SecretSerdeValue>,
7980
}
8081
#[derive(Debug, Clone)]
8182
pub struct PaymentsPostSessionTokensData {
@@ -935,4 +936,5 @@ pub struct SetupMandateRequestData {
935936
// MinorUnit for amount framework
936937
pub minor_amount: Option<MinorUnit>,
937938
pub shipping_cost: Option<MinorUnit>,
939+
pub connector_testing_data: Option<pii::SecretSerdeValue>,
938940
}

crates/openapi/src/openapi.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -734,6 +734,8 @@ Never share your secret api keys. Keep them guarded and secure.
734734
api_models::payments::PaymentsUpdateMetadataRequest,
735735
api_models::payments::PaymentsUpdateMetadataResponse,
736736
api_models::payments::CtpServiceDetails,
737+
api_models::payments::AdyenConnectorMetadata,
738+
api_models::payments::AdyenTestingData,
737739
api_models::feature_matrix::FeatureMatrixListResponse,
738740
api_models::feature_matrix::FeatureMatrixRequest,
739741
api_models::feature_matrix::ConnectorFeatureMatrixResponse,

crates/openapi/src/openapi_v2.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -701,6 +701,8 @@ Never share your secret api keys. Keep them guarded and secure.
701701
api_models::payments::DisplayAmountOnSdk,
702702
api_models::payments::ErrorDetails,
703703
api_models::payments::CtpServiceDetails,
704+
api_models::payments::AdyenConnectorMetadata,
705+
api_models::payments::AdyenTestingData,
704706
api_models::feature_matrix::FeatureMatrixListResponse,
705707
api_models::feature_matrix::FeatureMatrixRequest,
706708
api_models::feature_matrix::ConnectorFeatureMatrixResponse,

0 commit comments

Comments
 (0)