diff --git a/crates/hyperswitch_connectors/src/connectors/fiuu/transformers.rs b/crates/hyperswitch_connectors/src/connectors/fiuu/transformers.rs index 3639a7db4c8d..c0fcd77d8f00 100644 --- a/crates/hyperswitch_connectors/src/connectors/fiuu/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/fiuu/transformers.rs @@ -205,8 +205,11 @@ impl TryFrom<&FiuuRouterData<&PaymentsAuthorizeRouterData>> for FiuuMandateReque let order_id = item.router_data.connector_request_reference_id.clone(); let currency = item.router_data.request.currency; let amount = item.amount.clone(); - let billing_name = item.router_data.get_billing_full_name()?; - let email = item.router_data.request.get_email()?; + let billing_name = item + .router_data + .request + .get_card_holder_name_from_additional_payment_method_data()?; + let email = item.router_data.get_billing_email()?; let token = Secret::new(item.router_data.request.get_connector_mandate_id()?); let verify_key = auth.verify_key; let recurring_request = FiuuRecurringRequest { @@ -313,8 +316,6 @@ pub struct FiuuCardData { cc_year: Secret, #[serde(rename = "mpstokenstatus")] mps_token_status: Option, - #[serde(rename = "CustName")] - customer_name: Option>, #[serde(rename = "CustEmail")] customer_email: Option, } @@ -548,15 +549,11 @@ impl TryFrom<(&Card, &PaymentsAuthorizeRouterData)> for FiuuPaymentMethodData { fn try_from( (req_card, item): (&Card, &PaymentsAuthorizeRouterData), ) -> Result { - let (mps_token_status, customer_name, customer_email) = + let (mps_token_status, customer_email) = if item.request.is_customer_initiated_mandate_payment() { - ( - Some(1), - Some(item.request.get_customer_name()?), - Some(item.request.get_email()?), - ) + (Some(1), Some(item.get_billing_email()?)) } else { - (None, None, None) + (None, None) }; let non_3ds = match item.is_three_ds() { false => 1, @@ -570,7 +567,6 @@ impl TryFrom<(&Card, &PaymentsAuthorizeRouterData)> for FiuuPaymentMethodData { cc_month: req_card.card_exp_month.clone(), cc_year: req_card.card_exp_year.clone(), mps_token_status, - customer_name, customer_email, }))) } diff --git a/crates/hyperswitch_connectors/src/utils.rs b/crates/hyperswitch_connectors/src/utils.rs index bbabcefc02d4..81c9aa6084db 100644 --- a/crates/hyperswitch_connectors/src/utils.rs +++ b/crates/hyperswitch_connectors/src/utils.rs @@ -1155,6 +1155,9 @@ pub trait PaymentsAuthorizeRequestData { fn get_metadata_as_object(&self) -> Option; fn get_authentication_data(&self) -> Result; fn get_customer_name(&self) -> Result, Error>; + fn get_card_holder_name_from_additional_payment_method_data( + &self, + ) -> Result, Error>; } impl PaymentsAuthorizeRequestData for PaymentsAuthorizeData { @@ -1316,6 +1319,23 @@ impl PaymentsAuthorizeRequestData for PaymentsAuthorizeData { .clone() .ok_or_else(missing_field_err("customer_name")) } + + fn get_card_holder_name_from_additional_payment_method_data( + &self, + ) -> Result, Error> { + match &self.additional_payment_method_data { + Some(payments::AdditionalPaymentData::Card(card_data)) => Ok(card_data + .card_holder_name + .clone() + .ok_or_else(|| errors::ConnectorError::MissingRequiredField { + field_name: "card_holder_name", + })?), + _ => Err(errors::ConnectorError::MissingRequiredFields { + field_names: vec!["card_holder_name"], + } + .into()), + } + } } pub trait PaymentsCaptureRequestData { diff --git a/crates/router/src/configs/defaults/payment_connector_required_fields.rs b/crates/router/src/configs/defaults/payment_connector_required_fields.rs index 1c02619a8e97..fa538cc3192f 100644 --- a/crates/router/src/configs/defaults/payment_connector_required_fields.rs +++ b/crates/router/src/configs/defaults/payment_connector_required_fields.rs @@ -292,24 +292,6 @@ impl Default for settings::RequiredFields { common: HashMap::new(), } ), - ( - enums::Connector::Fiuu, - RequiredFieldFinal { - mandate: HashMap::from([ - ( - "billing.email".to_string(), - RequiredFieldInfo { - required_field: "payment_method_data.billing.email".to_string(), - display_name: "email".to_string(), - field_type: enums::FieldType::UserEmailAddress, - value: None, - } - ) - ]), - non_mandate: HashMap::new(), - common: HashMap::new(), - } - ), ( enums::Connector::Authorizedotnet, RequiredFieldFinal { @@ -1423,8 +1405,37 @@ impl Default for settings::RequiredFields { ( enums::Connector::Fiuu, RequiredFieldFinal { - mandate: HashMap::new(), - non_mandate: HashMap::from( + mandate: HashMap::from([ + ( + "billing.email".to_string(), + RequiredFieldInfo { + required_field: "payment_method_data.billing.email".to_string(), + display_name: "email".to_string(), + field_type: enums::FieldType::UserEmailAddress, + value: None, + } + ), + ( + "billing.address.first_name".to_string(), + RequiredFieldInfo { + required_field: "payment_method_data.billing.address.first_name".to_string(), + display_name: "card_holder_name".to_string(), + field_type: enums::FieldType::UserFullName, + value: None, + } + ), + ( + "billing.address.last_name".to_string(), + RequiredFieldInfo { + required_field: "payment_method_data.billing.address.last_name".to_string(), + display_name: "card_holder_name".to_string(), + field_type: enums::FieldType::UserFullName, + value: None, + } + ), + ]), + non_mandate: HashMap::new(), + common: HashMap::from( [ ( "payment_method_data.card.card_number".to_string(), @@ -1464,7 +1475,6 @@ impl Default for settings::RequiredFields { ) ] ), - common: HashMap::new(), } ), ( @@ -3402,24 +3412,6 @@ impl Default for settings::RequiredFields { common: HashMap::new(), } ), - ( - enums::Connector::Fiuu, - RequiredFieldFinal { - mandate: HashMap::from([ - ( - "billing.email".to_string(), - RequiredFieldInfo { - required_field: "payment_method_data.billing.email".to_string(), - display_name: "email".to_string(), - field_type: enums::FieldType::UserEmailAddress, - value: None, - } - ) - ]), - non_mandate: HashMap::new(), - common: HashMap::new(), - } - ), ( enums::Connector::Authorizedotnet, RequiredFieldFinal { @@ -4533,8 +4525,37 @@ impl Default for settings::RequiredFields { ( enums::Connector::Fiuu, RequiredFieldFinal { - mandate: HashMap::new(), - non_mandate: HashMap::from( + mandate: HashMap::from([ + ( + "billing.email".to_string(), + RequiredFieldInfo { + required_field: "payment_method_data.billing.email".to_string(), + display_name: "email".to_string(), + field_type: enums::FieldType::UserEmailAddress, + value: None, + } + ), + ( + "billing.address.first_name".to_string(), + RequiredFieldInfo { + required_field: "payment_method_data.billing.address.first_name".to_string(), + display_name: "card_holder_name".to_string(), + field_type: enums::FieldType::UserFullName, + value: None, + } + ), + ( + "billing.address.last_name".to_string(), + RequiredFieldInfo { + required_field: "payment_method_data.billing.address.last_name".to_string(), + display_name: "card_holder_name".to_string(), + field_type: enums::FieldType::UserFullName, + value: None, + } + ), + ]), + non_mandate: HashMap::new(), + common: HashMap::from( [ ( "payment_method_data.card.card_number".to_string(), @@ -4574,7 +4595,6 @@ impl Default for settings::RequiredFields { ) ] ), - common: HashMap::new(), } ), ( diff --git a/crates/router/src/core/payment_methods/cards.rs b/crates/router/src/core/payment_methods/cards.rs index bcf0f9348627..cbd535903ae2 100644 --- a/crates/router/src/core/payment_methods/cards.rs +++ b/crates/router/src/core/payment_methods/cards.rs @@ -3103,15 +3103,18 @@ pub async fn list_payment_methods( .await .transpose()?; let setup_future_usage = payment_intent.as_ref().and_then(|pi| pi.setup_future_usage); + let is_cit_transaction = payment_attempt + .as_ref() + .map(|pa| pa.mandate_details.is_some()) + .unwrap_or(false) + || setup_future_usage + .map(|future_usage| future_usage == common_enums::FutureUsage::OffSession) + .unwrap_or(false); let payment_type = payment_attempt.as_ref().map(|pa| { let amount = api::Amount::from(pa.net_amount.get_order_amount()); let mandate_type = if pa.mandate_id.is_some() { Some(api::MandateTransactionType::RecurringMandateTransaction) - } else if pa.mandate_details.is_some() - || setup_future_usage - .map(|future_usage| future_usage == common_enums::enums::FutureUsage::OffSession) - .unwrap_or(false) - { + } else if is_cit_transaction { Some(api::MandateTransactionType::NewMandateTransaction) } else { None @@ -3619,16 +3622,13 @@ pub async fn list_payment_methods( .get(&connector_variant) .map(|required_fields_final| { let mut required_fields_hs = required_fields_final.common.clone(); - if let Some(pa) = payment_attempt.as_ref() { - if let Some(_mandate) = &pa.mandate_details { + if is_cit_transaction { required_fields_hs .extend(required_fields_final.mandate.clone()); } else { required_fields_hs .extend(required_fields_final.non_mandate.clone()); } - } - required_fields_hs = should_collect_shipping_or_billing_details_from_wallet_connector( &payment_method, element.payment_experience.as_ref(), diff --git a/cypress-tests/cypress/e2e/PaymentUtils/Fiuu.js b/cypress-tests/cypress/e2e/PaymentUtils/Fiuu.js index 24a445dd3c25..4a910c7897c1 100644 --- a/cypress-tests/cypress/e2e/PaymentUtils/Fiuu.js +++ b/cypress-tests/cypress/e2e/PaymentUtils/Fiuu.js @@ -9,9 +9,9 @@ const successfulNo3DSCardDetails = { const successfulThreeDSTestCardDetails = { card_number: "5105105105105100", card_exp_month: "12", - card_exp_year: "2030", + card_exp_year: "2031", card_holder_name: "joseph Doe", - card_cvc: "123", + card_cvc: "444", }; const singleUseMandateData = { @@ -75,7 +75,7 @@ export const connectorDetails = { Response: { status: 200, body: { - status: "requires_capture", + status: "requires_customer_action", }, }, }, @@ -287,6 +287,20 @@ export const connectorDetails = { payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, + billing: { + address: { + line1: "1467", + line2: "Harrison Street", + line3: "Harrison Street", + city: "San Fransico", + state: "California", + zip: "94122", + country: "NL", + first_name: "joseph", + last_name: "Doe", + }, + email: "johndoe@gmail.com" + }, }, currency: "USD", mandate_data: multiUseMandateData, @@ -351,7 +365,21 @@ export const connectorDetails = { payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, - }, + billing: { + address: { + line1: "1467", + line2: "Harrison Street", + line3: "Harrison Street", + city: "San Fransico", + state: "California", + zip: "94122", + country: "NL", + first_name: "joseph", + last_name: "Doe", + }, + email: "johndoe@gmail.com" + }, + }, currency: "USD", mandate_data: null, customer_acceptance: { @@ -375,6 +403,20 @@ export const connectorDetails = { payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, + billing: { + address: { + line1: "1467", + line2: "Harrison Street", + line3: "Harrison Street", + city: "San Fransico", + state: "California", + zip: "94122", + country: "NL", + first_name: "joseph", + last_name: "Doe", + }, + email: "johndoe@gmail.com" + }, }, currency: "USD", setup_future_usage: "on_session", @@ -399,6 +441,20 @@ export const connectorDetails = { payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, + billing: { + address: { + line1: "1467", + line2: "Harrison Street", + line3: "Harrison Street", + city: "San Fransico", + state: "California", + zip: "94122", + country: "NL", + first_name: "joseph", + last_name: "Doe", + }, + email: "johndoe@gmail.com" + }, }, currency: "USD", setup_future_usage: "on_session", @@ -423,6 +479,20 @@ export const connectorDetails = { payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, + billing: { + address: { + line1: "1467", + line2: "Harrison Street", + line3: "Harrison Street", + city: "San Fransico", + state: "California", + zip: "94122", + country: "NL", + first_name: "joseph", + last_name: "Doe", + }, + email: "johndoe@gmail.com" + }, }, setup_future_usage: "off_session", customer_acceptance: { @@ -446,6 +516,20 @@ export const connectorDetails = { payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, + billing: { + address: { + line1: "1467", + line2: "Harrison Street", + line3: "Harrison Street", + city: "San Fransico", + state: "California", + zip: "94122", + country: "NL", + first_name: "joseph", + last_name: "Doe", + }, + email: "johndoe@gmail.com" + }, }, setup_future_usage: "off_session", customer_acceptance: { @@ -491,6 +575,20 @@ export const connectorDetails = { payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, + billing: { + address: { + line1: "1467", + line2: "Harrison Street", + line3: "Harrison Street", + city: "San Fransico", + state: "California", + zip: "94122", + country: "NL", + first_name: "joseph", + last_name: "Doe", + }, + email: "johndoe@gmail.com" + }, }, currency: "USD", mandate_data: null, @@ -515,6 +613,20 @@ export const connectorDetails = { payment_method: "card", payment_method_data: { card: successfulThreeDSTestCardDetails, + billing: { + address: { + line1: "1467", + line2: "Harrison Street", + line3: "Harrison Street", + city: "San Fransico", + state: "California", + zip: "94122", + country: "NL", + first_name: "joseph", + last_name: "Doe", + }, + email: "johndoe@gmail.com" + }, }, currency: "USD", mandate_data: null, @@ -540,6 +652,20 @@ export const connectorDetails = { payment_method: "card", payment_method_data: { card: successfulThreeDSTestCardDetails, + billing: { + address: { + line1: "1467", + line2: "Harrison Street", + line3: "Harrison Street", + city: "San Fransico", + state: "California", + zip: "94122", + country: "NL", + first_name: "joseph", + last_name: "Doe", + }, + email: "johndoe@gmail.com" + }, }, mandate_data: null, authentication_type: "three_ds", diff --git a/cypress-tests/cypress/support/commands.js b/cypress-tests/cypress/support/commands.js index 9b938e673d77..5b2ae9bb8ad7 100644 --- a/cypress-tests/cypress/support/commands.js +++ b/cypress-tests/cypress/support/commands.js @@ -1769,7 +1769,11 @@ Cypress.Commands.add( const nextActionUrl = response.body.next_action.redirect_to_url; cy.log(nextActionUrl); } else if (response.body.authentication_type === "no_three_ds") { - expect(response.body.status).to.equal("succeeded"); + if (response.body.connector === "fiuu") { + expect(response.body.status).to.equal("failed"); + } else { + expect(response.body.status).to.equal("succeeded"); + } } else { throw new Error( `Invalid authentication type ${response.body.authentication_type}` @@ -1783,7 +1787,11 @@ Cypress.Commands.add( const nextActionUrl = response.body.next_action.redirect_to_url; cy.log(nextActionUrl); } else if (response.body.authentication_type === "no_three_ds") { - expect(response.body.status).to.equal("requires_capture"); + if (response.body.connector === "fiuu") { + expect(response.body.status).to.equal("failed"); + } else { + expect(response.body.status).to.equal("requires_capture"); + } } else { throw new Error( `Invalid authentication type ${response.body.authentication_type}` @@ -1861,7 +1869,11 @@ Cypress.Commands.add( const nextActionUrl = response.body.next_action.redirect_to_url; cy.log(nextActionUrl); } else if (response.body.authentication_type === "no_three_ds") { - expect(response.body.status).to.equal("requires_capture"); + if (response.body.connector === "fiuu") { + expect(response.body.status).to.equal("failed"); + } else { + expect(response.body.status).to.equal("requires_capture"); + } } else { throw new Error( `Invalid authentication type ${response.body.authentication_type}` diff --git a/cypress-tests/cypress/support/redirectionHandler.js b/cypress-tests/cypress/support/redirectionHandler.js index aa6db7d50747..b3eb89868f05 100644 --- a/cypress-tests/cypress/support/redirectionHandler.js +++ b/cypress-tests/cypress/support/redirectionHandler.js @@ -352,10 +352,22 @@ function threeDsRedirection(redirection_url, expected_url, connectorId) { .then(() => { cy.get('input[name="challengeDataEntry"]').click().type("1234"); cy.get('input[value="SUBMIT"]').click(); - }) + }); }); - } - else { + } else if (connectorId === "fiuu") { + cy.get('form[id="cc_form"]', { timeout: WAIT_TIME_IATAPAY }) + .should("exist") + .then((form) => { + cy.get('button.pay-btn[name="pay"]').click(); + cy.get("div.otp") + .invoke("text") + .then((otpText) => { + const otp = otpText.match(/\d+/)[0]; // Extract the numeric OTP + cy.get("input#otp-input").should("not.be.disabled").type(otp); + cy.get('button.pay-btn').click(); + }); + }); + } else { // If connectorId is neither of adyen, trustpay, nmi, stripe, bankofamerica or cybersource, wait for 10 seconds cy.wait(WAIT_TIME); }