From b00deb965dc511ac843a80589aa34f55c3ced297 Mon Sep 17 00:00:00 2001 From: sweta-sharma <77436883+Sweta-Kumari-Sharma@users.noreply.github.com> Date: Fri, 28 Mar 2025 17:08:55 +0530 Subject: [PATCH] refactor(request_body): Added FRM data in payment request (#7615) --- .../src/connectors/adyen/transformers.rs | 380 ++++++++++++++++-- 1 file changed, 342 insertions(+), 38 deletions(-) diff --git a/crates/hyperswitch_connectors/src/connectors/adyen/transformers.rs b/crates/hyperswitch_connectors/src/connectors/adyen/transformers.rs index 810e473045..f977ba6a66 100644 --- a/crates/hyperswitch_connectors/src/connectors/adyen/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/adyen/transformers.rs @@ -142,6 +142,8 @@ pub struct AdditionalData { funds_availability: Option, refusal_reason_raw: Option, refusal_code_raw: Option, + #[serde(flatten)] + riskdata: Option, } #[serde_with::skip_serializing_none] @@ -176,6 +178,82 @@ pub struct LineItem { quantity: Option, } +#[serde_with::skip_serializing_none] +#[derive(Clone, Default, Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct RiskData { + #[serde(rename = "riskdata.basket.item1.itemID")] + item_i_d: Option, + #[serde(rename = "riskdata.basket.item1.productTitle")] + product_title: Option, + #[serde(rename = "riskdata.basket.item1.amountPerItem")] + amount_per_item: Option, + #[serde(rename = "riskdata.basket.item1.currency")] + currency: Option, + #[serde(rename = "riskdata.basket.item1.upc")] + upc: Option, + #[serde(rename = "riskdata.basket.item1.brand")] + brand: Option, + #[serde(rename = "riskdata.basket.item1.manufacturer")] + manufacturer: Option, + #[serde(rename = "riskdata.basket.item1.category")] + category: Option, + #[serde(rename = "riskdata.basket.item1.quantity")] + quantity: Option, + #[serde(rename = "riskdata.basket.item1.color")] + color: Option, + #[serde(rename = "riskdata.basket.item1.size")] + size: Option, + #[serde(rename = "riskdata.deviceCountry")] + device_country: Option, + #[serde(rename = "riskdata.houseNumberorName")] + house_numberor_name: Option, + #[serde(rename = "riskdata.accountCreationDate")] + account_creation_date: Option, + #[serde(rename = "riskdata.affiliateChannel")] + affiliate_channel: Option, + #[serde(rename = "riskdata.avgOrderValue")] + avg_order_value: Option, + #[serde(rename = "riskdata.deliveryMethod")] + delivery_method: Option, + #[serde(rename = "riskdata.emailName")] + email_name: Option, + #[serde(rename = "riskdata.emailDomain")] + email_domain: Option, + #[serde(rename = "riskdata.lastOrderDate")] + last_order_date: Option, + #[serde(rename = "riskdata.merchantReference")] + merchant_reference: Option, + #[serde(rename = "riskdata.paymentMethod")] + payment_method: Option, + #[serde(rename = "riskdata.promotionName")] + promotion_name: Option, + #[serde(rename = "riskdata.secondaryPhoneNumber")] + secondary_phone_number: Option, + #[serde(rename = "riskdata.timefromLogintoOrder")] + timefrom_loginto_order: Option, + #[serde(rename = "riskdata.totalSessionTime")] + total_session_time: Option, + #[serde(rename = "riskdata.totalAuthorizedAmountInLast30Days")] + total_authorized_amount_in_last30_days: Option, + #[serde(rename = "riskdata.totalOrderQuantity")] + total_order_quantity: Option, + #[serde(rename = "riskdata.totalLifetimeValue")] + total_lifetime_value: Option, + #[serde(rename = "riskdata.visitsMonth")] + visits_month: Option, + #[serde(rename = "riskdata.visitsWeek")] + visits_week: Option, + #[serde(rename = "riskdata.visitsYear")] + visits_year: Option, + #[serde(rename = "riskdata.shipToName")] + ship_to_name: Option, + #[serde(rename = "riskdata.first8charactersofAddressLine1Zip")] + first8charactersof_address_line1_zip: Option, + #[serde(rename = "riskdata.affiliateOrder")] + affiliate_order: Option, +} + #[serde_with::skip_serializing_none] #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] @@ -205,10 +283,10 @@ pub struct AdyenPaymentRequest<'a> { country_code: Option, line_items: Option>, channel: Option, - metadata: Option, merchant_order_reference: Option, splits: Option>, store: Option, + device_fingerprint: Option>, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -1745,12 +1823,18 @@ fn get_additional_data(item: &PaymentsAuthorizeRouterData) -> Option (None, None), }; + let riskdata = item.request.metadata.clone().and_then(get_risk_data); + let execute_three_d = if matches!(item.auth_type, storage_enums::AuthenticationType::ThreeDs) { Some("true".to_string()) } else { None }; - if authorisation_type.is_none() && manual_capture.is_none() && execute_three_d.is_none() { + if authorisation_type.is_none() + && manual_capture.is_none() + && execute_three_d.is_none() + && riskdata.is_none() + { //without this if-condition when the above 3 values are None, additionalData will be serialized to JSON like this -> additionalData: {} //returning None, ensures that additionalData key will not be present in the serialized JSON None @@ -1763,6 +1847,7 @@ fn get_additional_data(item: &PaymentsAuthorizeRouterData) -> Option for AdyenPaymentMethod<'_> { } } +fn get_str(key: &str, riskdata: &serde_json::Value) -> Option { + riskdata + .get(key) + .and_then(|v| v.as_str()) + .map(|s| s.to_string()) +} + +fn get_bool(key: &str, riskdata: &serde_json::Value) -> Option { + riskdata.get(key).and_then(|v| v.as_bool()) +} + +pub fn get_risk_data(metadata: serde_json::Value) -> Option { + let item_i_d = get_str("riskdata.basket.item1.itemID", &metadata); + let product_title = get_str("riskdata.basket.item1.productTitle", &metadata); + let amount_per_item = get_str("riskdata.basket.item1.amountPerItem", &metadata); + let currency = get_str("riskdata.basket.item1.currency", &metadata); + let upc = get_str("riskdata.basket.item1.upc", &metadata); + let brand = get_str("riskdata.basket.item1.brand", &metadata); + let manufacturer = get_str("riskdata.basket.item1.manufacturer", &metadata); + let category = get_str("riskdata.basket.item1.category", &metadata); + let quantity = get_str("riskdata.basket.item1.quantity", &metadata); + let color = get_str("riskdata.basket.item1.color", &metadata); + let size = get_str("riskdata.basket.item1.size", &metadata); + + let device_country = get_str("riskdata.deviceCountry", &metadata); + let house_numberor_name = get_str("riskdata.houseNumberorName", &metadata); + let account_creation_date = get_str("riskdata.accountCreationDate", &metadata); + let affiliate_channel = get_str("riskdata.affiliateChannel", &metadata); + let avg_order_value = get_str("riskdata.avgOrderValue", &metadata); + let delivery_method = get_str("riskdata.deliveryMethod", &metadata); + let email_name = get_str("riskdata.emailName", &metadata); + let email_domain = get_str("riskdata.emailDomain", &metadata); + let last_order_date = get_str("riskdata.lastOrderDate", &metadata); + let merchant_reference = get_str("riskdata.merchantReference", &metadata); + let payment_method = get_str("riskdata.paymentMethod", &metadata); + let promotion_name = get_str("riskdata.promotionName", &metadata); + let secondary_phone_number = get_str("riskdata.secondaryPhoneNumber", &metadata); + let timefrom_loginto_order = get_str("riskdata.timefromLogintoOrder", &metadata); + let total_session_time = get_str("riskdata.totalSessionTime", &metadata); + let total_authorized_amount_in_last30_days = + get_str("riskdata.totalAuthorizedAmountInLast30Days", &metadata); + let total_order_quantity = get_str("riskdata.totalOrderQuantity", &metadata); + let total_lifetime_value = get_str("riskdata.totalLifetimeValue", &metadata); + let visits_month = get_str("riskdata.visitsMonth", &metadata); + let visits_week = get_str("riskdata.visitsWeek", &metadata); + let visits_year = get_str("riskdata.visitsYear", &metadata); + let ship_to_name = get_str("riskdata.shipToName", &metadata); + let first8charactersof_address_line1_zip = + get_str("riskdata.first8charactersofAddressLine1Zip", &metadata); + let affiliate_order = get_bool("riskdata.affiliateOrder", &metadata); + + Some(RiskData { + item_i_d, + product_title, + amount_per_item, + currency, + upc, + brand, + manufacturer, + category, + quantity, + color, + size, + device_country, + house_numberor_name, + account_creation_date, + affiliate_channel, + avg_order_value, + delivery_method, + email_name, + email_domain, + last_order_date, + merchant_reference, + payment_method, + promotion_name, + secondary_phone_number, + timefrom_loginto_order, + total_session_time, + total_authorized_amount_in_last30_days, + total_order_quantity, + total_lifetime_value, + visits_month, + visits_week, + visits_year, + ship_to_name, + first8charactersof_address_line1_zip, + affiliate_order, + }) +} + +fn get_device_fingerprint(metadata: serde_json::Value) -> Option> { + metadata + .get("device_fingerprint") + .and_then(|value| value.as_str()) + .map(|fingerprint| Secret::new(fingerprint.to_string())) +} + impl TryFrom<( &AdyenRouterData<&PaymentsAuthorizeRouterData>, @@ -2630,6 +2812,19 @@ impl _ => (None, None), }; + let device_fingerprint = item + .router_data + .request + .metadata + .clone() + .and_then(get_device_fingerprint); + + let billing_address = + get_address_info(item.router_data.get_optional_billing()).and_then(Result::ok); + let delivery_address = + get_address_info(item.router_data.get_optional_shipping()).and_then(Result::ok); + let telephone_number = item.router_data.get_optional_billing_phone_number(); + Ok(AdyenPaymentRequest { amount, merchant_account: auth_type.merchant_account, @@ -2641,13 +2836,13 @@ impl browser_info, additional_data, mpi_data: None, - telephone_number: None, + telephone_number, shopper_name: None, shopper_email: None, shopper_locale: None, social_security_number: None, - billing_address: None, - delivery_address: None, + billing_address, + delivery_address, country_code: None, line_items: None, shopper_reference, @@ -2655,10 +2850,10 @@ impl channel: None, shopper_statement: item.router_data.request.statement_descriptor.clone(), shopper_ip: item.router_data.request.get_ip_address_as_optional(), - metadata: item.router_data.request.metadata.clone().map(Into::into), merchant_order_reference: item.router_data.request.merchant_order_reference_id.clone(), store, splits, + device_fingerprint, }) } } @@ -2697,6 +2892,17 @@ impl TryFrom<(&AdyenRouterData<&PaymentsAuthorizeRouterData>, &Card)> for AdyenP _ => (None, None), }; + let device_fingerprint = item + .router_data + .request + .metadata + .clone() + .and_then(get_device_fingerprint); + + let delivery_address = + get_address_info(item.router_data.get_optional_shipping()).and_then(Result::ok); + let telephone_number = item.router_data.get_optional_billing_phone_number(); + Ok(AdyenPaymentRequest { amount, merchant_account: auth_type.merchant_account, @@ -2708,13 +2914,13 @@ impl TryFrom<(&AdyenRouterData<&PaymentsAuthorizeRouterData>, &Card)> for AdyenP browser_info, additional_data, mpi_data: None, - telephone_number: None, + telephone_number, shopper_name, shopper_email, shopper_locale: None, social_security_number: None, billing_address, - delivery_address: None, + delivery_address, country_code, line_items: None, shopper_reference, @@ -2722,10 +2928,10 @@ impl TryFrom<(&AdyenRouterData<&PaymentsAuthorizeRouterData>, &Card)> for AdyenP channel: None, shopper_statement: item.router_data.request.statement_descriptor.clone(), shopper_ip: item.router_data.request.get_ip_address_as_optional(), - metadata: item.router_data.request.metadata.clone().map(Into::into), merchant_order_reference: item.router_data.request.merchant_order_reference_id.clone(), store, splits, + device_fingerprint, }) } } @@ -2764,6 +2970,20 @@ impl )) => get_adyen_split_request(adyen_split_payment, item.router_data.request.currency), _ => (None, None), }; + + let device_fingerprint = item + .router_data + .request + .metadata + .clone() + .and_then(get_device_fingerprint); + + let billing_address = + get_address_info(item.router_data.get_optional_billing()).and_then(Result::ok); + let delivery_address = + get_address_info(item.router_data.get_optional_shipping()).and_then(Result::ok); + let telephone_number = item.router_data.get_optional_billing_phone_number(); + let request = AdyenPaymentRequest { amount, merchant_account: auth_type.merchant_account, @@ -2779,9 +2999,9 @@ impl shopper_locale: None, shopper_email: item.router_data.get_optional_billing_email(), social_security_number: None, - telephone_number: None, - billing_address: None, - delivery_address: None, + telephone_number, + billing_address, + delivery_address, country_code, line_items: None, shopper_reference, @@ -2789,10 +3009,10 @@ impl channel: None, shopper_statement: item.router_data.request.statement_descriptor.clone(), shopper_ip: item.router_data.request.get_ip_address_as_optional(), - metadata: item.router_data.request.metadata.clone().map(Into::into), merchant_order_reference: item.router_data.request.merchant_order_reference_id.clone(), store, splits, + device_fingerprint, }; Ok(request) } @@ -2828,6 +3048,17 @@ impl TryFrom<(&AdyenRouterData<&PaymentsAuthorizeRouterData>, &VoucherData)> _ => (None, None), }; + let device_fingerprint = item + .router_data + .request + .metadata + .clone() + .and_then(get_device_fingerprint); + + let delivery_address = + get_address_info(item.router_data.get_optional_shipping()).and_then(Result::ok); + let telephone_number = item.router_data.get_optional_billing_phone_number(); + let request = AdyenPaymentRequest { amount, merchant_account: auth_type.merchant_account, @@ -2843,9 +3074,9 @@ impl TryFrom<(&AdyenRouterData<&PaymentsAuthorizeRouterData>, &VoucherData)> shopper_email: item.router_data.get_optional_billing_email(), social_security_number, mpi_data: None, - telephone_number: None, + telephone_number, billing_address, - delivery_address: None, + delivery_address, country_code: None, line_items: None, shopper_reference: None, @@ -2853,10 +3084,10 @@ impl TryFrom<(&AdyenRouterData<&PaymentsAuthorizeRouterData>, &VoucherData)> channel: None, shopper_statement: item.router_data.request.statement_descriptor.clone(), shopper_ip: item.router_data.request.get_ip_address_as_optional(), - metadata: item.router_data.request.metadata.clone().map(Into::into), merchant_order_reference: item.router_data.request.merchant_order_reference_id.clone(), store, splits, + device_fingerprint, }; Ok(request) } @@ -2891,6 +3122,19 @@ impl )) => get_adyen_split_request(adyen_split_payment, item.router_data.request.currency), _ => (None, None), }; + + let device_fingerprint = item + .router_data + .request + .metadata + .clone() + .and_then(get_device_fingerprint); + let billing_address = + get_address_info(item.router_data.get_optional_billing()).and_then(Result::ok); + let delivery_address = + get_address_info(item.router_data.get_optional_shipping()).and_then(Result::ok); + let telephone_number = item.router_data.get_optional_billing_phone_number(); + let request = AdyenPaymentRequest { amount, merchant_account: auth_type.merchant_account, @@ -2906,9 +3150,9 @@ impl shopper_locale: None, shopper_email: item.router_data.get_optional_billing_email(), social_security_number: None, - telephone_number: None, - billing_address: None, - delivery_address: None, + telephone_number, + billing_address, + delivery_address, country_code: None, line_items: None, shopper_reference: None, @@ -2916,10 +3160,10 @@ impl channel: None, shopper_statement: item.router_data.request.statement_descriptor.clone(), shopper_ip: item.router_data.request.get_ip_address_as_optional(), - metadata: item.router_data.request.metadata.clone().map(Into::into), merchant_order_reference: item.router_data.request.merchant_order_reference_id.clone(), store, splits, + device_fingerprint, }; Ok(request) } @@ -2954,6 +3198,19 @@ impl _ => (None, None), }; + let device_fingerprint = item + .router_data + .request + .metadata + .clone() + .and_then(get_device_fingerprint); + + let billing_address = + get_address_info(item.router_data.get_optional_billing()).and_then(Result::ok); + let delivery_address = + get_address_info(item.router_data.get_optional_shipping()).and_then(Result::ok); + let telephone_number = item.router_data.get_optional_billing_phone_number(); + let request = AdyenPaymentRequest { amount, merchant_account: auth_type.merchant_account, @@ -2968,9 +3225,9 @@ impl shopper_name: None, shopper_locale: None, shopper_email: item.router_data.get_optional_billing_email(), - telephone_number: None, - billing_address: None, - delivery_address: None, + telephone_number, + billing_address, + delivery_address, country_code: None, line_items: None, shopper_reference: None, @@ -2979,10 +3236,10 @@ impl social_security_number: None, shopper_statement: item.router_data.request.statement_descriptor.clone(), shopper_ip: item.router_data.request.get_ip_address_as_optional(), - metadata: item.router_data.request.metadata.clone().map(Into::into), merchant_order_reference: item.router_data.request.merchant_order_reference_id.clone(), store, splits, + device_fingerprint, }; Ok(request) } @@ -3023,6 +3280,16 @@ impl )) => get_adyen_split_request(adyen_split_payment, item.router_data.request.currency), _ => (None, None), }; + let device_fingerprint = item + .router_data + .request + .metadata + .clone() + .and_then(get_device_fingerprint); + + let delivery_address = + get_address_info(item.router_data.get_optional_shipping()).and_then(Result::ok); + let telephone_number = item.router_data.get_optional_billing_phone_number(); Ok(AdyenPaymentRequest { amount, @@ -3035,13 +3302,13 @@ impl browser_info, additional_data, mpi_data: None, - telephone_number: None, + telephone_number, shopper_name: None, shopper_email: item.router_data.get_optional_billing_email(), shopper_locale, social_security_number: None, billing_address, - delivery_address: None, + delivery_address, country_code: country, line_items, shopper_reference, @@ -3049,10 +3316,10 @@ impl channel: None, shopper_statement: item.router_data.request.statement_descriptor.clone(), shopper_ip: item.router_data.request.get_ip_address_as_optional(), - metadata: item.router_data.request.metadata.clone().map(Into::into), merchant_order_reference: item.router_data.request.merchant_order_reference_id.clone(), store, splits, + device_fingerprint, }) } } @@ -3134,6 +3401,18 @@ impl TryFrom<(&AdyenRouterData<&PaymentsAuthorizeRouterData>, &WalletData)> )) => get_adyen_split_request(adyen_split_payment, item.router_data.request.currency), _ => (None, None), }; + + let device_fingerprint = item + .router_data + .request + .metadata + .clone() + .and_then(get_device_fingerprint); + + let delivery_address = + get_address_info(item.router_data.get_optional_shipping()).and_then(Result::ok); + let telephone_number = item.router_data.get_optional_billing_phone_number(); + Ok(AdyenPaymentRequest { amount, merchant_account: auth_type.merchant_account, @@ -3145,13 +3424,13 @@ impl TryFrom<(&AdyenRouterData<&PaymentsAuthorizeRouterData>, &WalletData)> browser_info, additional_data, mpi_data, - telephone_number: None, + telephone_number, shopper_name: None, shopper_email, shopper_locale: None, social_security_number: None, billing_address, - delivery_address: None, + delivery_address, country_code: None, line_items: None, shopper_reference, @@ -3159,10 +3438,10 @@ impl TryFrom<(&AdyenRouterData<&PaymentsAuthorizeRouterData>, &WalletData)> channel, shopper_statement: item.router_data.request.statement_descriptor.clone(), shopper_ip: item.router_data.request.get_ip_address_as_optional(), - metadata: item.router_data.request.metadata.clone().map(Into::into), merchant_order_reference: item.router_data.request.merchant_order_reference_id.clone(), store, splits, + device_fingerprint, }) } } @@ -3219,6 +3498,12 @@ impl )) => get_adyen_split_request(adyen_split_payment, item.router_data.request.currency), _ => (None, None), }; + let device_fingerprint = item + .router_data + .request + .metadata + .clone() + .and_then(get_device_fingerprint); Ok(AdyenPaymentRequest { amount, @@ -3245,10 +3530,10 @@ impl channel: None, shopper_statement: item.router_data.request.statement_descriptor.clone(), shopper_ip: item.router_data.request.get_ip_address_as_optional(), - metadata: item.router_data.request.metadata.clone().map(Into::into), merchant_order_reference: item.router_data.request.merchant_order_reference_id.clone(), store, splits, + device_fingerprint, }) } } @@ -3291,7 +3576,16 @@ impl )) => get_adyen_split_request(adyen_split_payment, item.router_data.request.currency), _ => (None, None), }; - + let device_fingerprint = item + .router_data + .request + .metadata + .clone() + .and_then(get_device_fingerprint); + let billing_address = + get_address_info(item.router_data.get_optional_billing()).and_then(Result::ok); + let delivery_address = + get_address_info(item.router_data.get_optional_shipping()).and_then(Result::ok); Ok(AdyenPaymentRequest { amount, merchant_account: auth_type.merchant_account, @@ -3307,8 +3601,8 @@ impl shopper_name, shopper_email, shopper_locale: None, - billing_address: None, - delivery_address: None, + billing_address, + delivery_address, country_code: None, line_items: None, shopper_reference: None, @@ -3317,10 +3611,10 @@ impl social_security_number: None, shopper_statement: item.router_data.request.statement_descriptor.clone(), shopper_ip: item.router_data.request.get_ip_address_as_optional(), - metadata: item.router_data.request.metadata.clone().map(Into::into), merchant_order_reference: item.router_data.request.merchant_order_reference_id.clone(), store, splits, + device_fingerprint, }) } } @@ -5493,6 +5787,16 @@ impl )) => get_adyen_split_request(adyen_split_payment, item.router_data.request.currency), _ => (None, None), }; + let device_fingerprint = item + .router_data + .request + .metadata + .clone() + .and_then(get_device_fingerprint); + + let delivery_address = + get_address_info(item.router_data.get_optional_shipping()).and_then(Result::ok); + let telephone_number = item.router_data.get_optional_billing_phone_number(); Ok(AdyenPaymentRequest { amount, @@ -5504,13 +5808,13 @@ impl recurring_processing_model, browser_info, additional_data, - telephone_number: None, + telephone_number, shopper_name, shopper_email, shopper_locale: None, social_security_number: None, billing_address, - delivery_address: None, + delivery_address, country_code, line_items: None, shopper_reference, @@ -5518,11 +5822,11 @@ impl channel: None, shopper_statement: item.router_data.request.statement_descriptor.clone(), shopper_ip: item.router_data.request.get_ip_address_as_optional(), - metadata: item.router_data.request.metadata.clone().map(Into::into), merchant_order_reference: item.router_data.request.merchant_order_reference_id.clone(), mpi_data: Some(mpi_data), store, splits, + device_fingerprint, }) } }