diff --git a/crates/api_models/src/payment_methods.rs b/crates/api_models/src/payment_methods.rs index 64d792f2c1..e2b9decc79 100644 --- a/crates/api_models/src/payment_methods.rs +++ b/crates/api_models/src/payment_methods.rs @@ -205,6 +205,8 @@ pub struct ResponsePaymentMethodTypes { /// The Bank debit payment method information, if applicable for a payment method type. pub bank_debits: Option, + /// The Bank transfer payment method information, if applicable for a payment method type. + pub bank_transfers: Option, } #[derive(Debug, Clone, serde::Deserialize, serde::Serialize, ToSchema)] @@ -217,6 +219,13 @@ pub struct ResponsePaymentMethodsEnabled { pub payment_method_types: Vec, } +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema, PartialEq, Eq)] +pub struct BankTransferTypes { + /// The list of eligible connectors for a given payment experience + #[schema(example = json!(["stripe", "adyen"]))] + pub eligible_connectors: Vec, +} + #[derive(Clone, Debug)] pub struct ResponsePaymentMethodIntermediate { pub payment_method_type: api_enums::PaymentMethodType, diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index 3d5cd6414d..d71c3ae4c7 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -724,13 +724,14 @@ pub enum BankRedirectData { } #[derive(Debug, Clone, Eq, PartialEq, serde::Deserialize, serde::Serialize)] -pub struct AchBankTransferData { - pub billing_details: AchBillingDetails, +pub struct AchBillingDetails { + pub email: Email, } #[derive(Debug, Clone, Eq, PartialEq, serde::Deserialize, serde::Serialize)] -pub struct AchBillingDetails { +pub struct SepaAndBacsBillingDetails { pub email: Email, + pub name: Secret, } #[derive(Debug, Clone, Eq, PartialEq, serde::Deserialize, serde::Serialize, ToSchema)] @@ -757,7 +758,16 @@ pub struct BankRedirectBilling { #[derive(Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize, ToSchema)] #[serde(rename_all = "snake_case")] pub enum BankTransferData { - AchBankTransfer(AchBankTransferData), + AchBankTransfer { + billing_details: AchBillingDetails, + }, + SepaBankTransfer { + billing_details: SepaAndBacsBillingDetails, + country: api_enums::CountryAlpha2, + }, + BacsBankTransfer { + billing_details: SepaAndBacsBillingDetails, + }, } #[derive(serde::Deserialize, serde::Serialize, Debug, Clone, ToSchema, Eq, PartialEq)] @@ -878,6 +888,7 @@ pub struct CardResponse { } #[derive(Debug, Clone, Eq, PartialEq, serde::Serialize)] +#[serde(rename_all = "snake_case")] pub enum PaymentMethodDataResponse { #[serde(rename = "card")] Card(CardResponse), @@ -1071,10 +1082,34 @@ pub struct NextAction { #[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)] pub struct NextStepsRequirements { - pub ach_credit_transfer: AchTransfer, + #[serde(flatten)] + pub bank_transfer_instructions: BankTransferInstructions, pub receiver: ReceiverDetails, } +#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum BankTransferInstructions { + AchCreditTransfer(Box), + SepaBankInstructions(Box), + BacsBankInstructions(Box), +} + +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct SepaBankTransferInstructions { + pub account_holder_name: Secret, + pub bic: Secret, + pub country: String, + pub iban: Secret, +} + +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct BacsBankTransferInstructions { + pub account_holder_name: Secret, + pub account_number: Secret, + pub sort_code: Secret, +} + #[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)] pub struct AchTransfer { pub account_number: Secret, @@ -1085,8 +1120,9 @@ pub struct AchTransfer { #[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)] pub struct ReceiverDetails { - pub amount_received: i64, - pub amount_charged: i64, + amount_received: i64, + amount_charged: Option, + amount_remaining: Option, } #[derive(Setter, Clone, Default, Debug, Eq, PartialEq, serde::Serialize, ToSchema)] diff --git a/crates/api_models/src/webhooks.rs b/crates/api_models/src/webhooks.rs index c966e12f33..15df7da9ee 100644 --- a/crates/api_models/src/webhooks.rs +++ b/crates/api_models/src/webhooks.rs @@ -10,6 +10,7 @@ pub enum IncomingWebhookEvent { PaymentIntentFailure, PaymentIntentSuccess, PaymentIntentProcessing, + PaymentIntentPartiallyFunded, PaymentActionRequired, EventNotSupported, SourceChargeable, @@ -45,6 +46,7 @@ impl From for WebhookFlow { IncomingWebhookEvent::PaymentIntentSuccess => Self::Payment, IncomingWebhookEvent::PaymentIntentProcessing => Self::Payment, IncomingWebhookEvent::PaymentActionRequired => Self::Payment, + IncomingWebhookEvent::PaymentIntentPartiallyFunded => Self::Payment, IncomingWebhookEvent::EventNotSupported => Self::ReturnResponse, IncomingWebhookEvent::RefundSuccess => Self::Refund, IncomingWebhookEvent::RefundFailure => Self::Refund, diff --git a/crates/router/src/connector/stripe.rs b/crates/router/src/connector/stripe.rs index 06a68c049a..7190397ca7 100644 --- a/crates/router/src/connector/stripe.rs +++ b/crates/router/src/connector/stripe.rs @@ -686,9 +686,14 @@ impl match &req.request.payment_method_data { api_models::payments::PaymentMethodData::BankTransfer(bank_transfer_data) => { match bank_transfer_data.deref() { - api_models::payments::BankTransferData::AchBankTransfer(_) => { + api_models::payments::BankTransferData::AchBankTransfer { .. } => { Ok(format!("{}{}", self.base_url(connectors), "v1/charges")) } + _ => Ok(format!( + "{}{}", + self.base_url(connectors), + "v1/payment_intents" + )), } } _ => Ok(format!( @@ -1682,9 +1687,6 @@ impl api::IncomingWebhook for Stripe { stripe::WebhookEventType::SourceChargeable => { api::IncomingWebhookEvent::SourceChargeable } - stripe::WebhookEventType::SourceTransactionCreated => { - api::IncomingWebhookEvent::SourceTransactionCreated - } stripe::WebhookEventType::ChargeSucceeded => api::IncomingWebhookEvent::ChargeSucceeded, stripe::WebhookEventType::DisputeCreated => api::IncomingWebhookEvent::DisputeOpened, stripe::WebhookEventType::DisputeClosed => api::IncomingWebhookEvent::DisputeCancelled, @@ -1695,6 +1697,12 @@ impl api::IncomingWebhook for Stripe { .status .ok_or(errors::ConnectorError::WebhookEventTypeNotFound)?, )?, + stripe::WebhookEventType::PaymentIntentPartiallyFunded => { + api::IncomingWebhookEvent::PaymentIntentPartiallyFunded + } + stripe::WebhookEventType::PaymentIntentRequiresAction => { + api::IncomingWebhookEvent::PaymentActionRequired + } _ => Err(errors::ConnectorError::WebhookEventTypeNotFound).into_report()?, }) } diff --git a/crates/router/src/connector/stripe/transformers.rs b/crates/router/src/connector/stripe/transformers.rs index 8609a50053..228427c29a 100644 --- a/crates/router/src/connector/stripe/transformers.rs +++ b/crates/router/src/connector/stripe/transformers.rs @@ -15,7 +15,7 @@ use url::Url; use uuid::Uuid; use crate::{ - collect_missing_value_keys, consts, + collect_missing_value_keys, connector, consts, core::errors, services, types::{self, api, storage::enums, transformers::ForeignFrom}, @@ -286,6 +286,61 @@ pub struct StripeBankRedirectData { pub bank_specific_data: Option, } +#[derive(Debug, Eq, PartialEq, Serialize)] +pub struct AchBankTransferData { + #[serde(rename = "owner[email]")] + pub email: Email, +} + +#[derive(Debug, Eq, PartialEq, Serialize)] +pub struct BacsBankTransferData { + #[serde(rename = "payment_method_data[type]")] + pub payment_method_data_type: StripePaymentMethodType, + #[serde(rename = "payment_method_options[customer_balance][bank_transfer][type]")] + pub bank_transfer_type: BankTransferType, + #[serde(rename = "payment_method_options[customer_balance][funding_type]")] + pub balance_funding_type: BankTransferType, + #[serde(rename = "payment_method_types[0]")] + pub payment_method_type: StripePaymentMethodType, +} + +#[derive(Debug, Eq, PartialEq, Serialize)] +pub struct SepaBankTransferData { + #[serde(rename = "payment_method_data[type]")] + pub payment_method_data_type: StripePaymentMethodType, + #[serde(rename = "payment_method_options[customer_balance][bank_transfer][type]")] + pub bank_transfer_type: BankTransferType, + #[serde(rename = "payment_method_options[customer_balance][funding_type]")] + pub balance_funding_type: BankTransferType, + #[serde(rename = "payment_method_types[0]")] + pub payment_method_type: StripePaymentMethodType, + #[serde( + rename = "payment_method_options[customer_balance][bank_transfer][eu_bank_transfer][country]" + )] + pub country: api_models::enums::CountryAlpha2, +} + +#[derive(Debug, Eq, PartialEq, Serialize)] +pub struct StripeAchSourceRequest { + #[serde(rename = "type")] + pub transfer_type: StripePaymentMethodType, + #[serde(flatten)] + pub payment_method_data: AchBankTransferData, + pub currency: String, +} + +// Remove untagged when Deserialize is added +#[derive(Debug, Eq, PartialEq, Serialize)] +#[serde(untagged)] +pub enum StripePaymentMethodData { + Card(StripeCardData), + PayLater(StripePayLaterData), + Wallet(StripeWallet), + BankRedirect(StripeBankRedirectData), + BankDebit(StripeBankDebitData), + BankTransfer(StripeBankTransferData), +} + #[derive(Debug, Eq, PartialEq, Serialize)] #[serde(tag = "payment_method_data[type]")] pub enum BankDebitData { @@ -332,24 +387,12 @@ pub struct BankTransferData { pub email: Email, } -#[derive(Debug, Eq, PartialEq, Serialize)] -pub struct StripeAchSourceRequest { - #[serde(rename = "type")] - pub transfer_type: StripePaymentMethodType, - #[serde(rename = "owner[email]")] - pub email: Email, - pub currency: String, -} - #[derive(Debug, Eq, PartialEq, Serialize)] #[serde(untagged)] -pub enum StripePaymentMethodData { - Card(StripeCardData), - PayLater(StripePayLaterData), - Wallet(StripeWallet), - BankRedirect(StripeBankRedirectData), - BankDebit(StripeBankDebitData), - AchBankTransfer(BankTransferData), +pub enum StripeBankTransferData { + AchBankTransfer(Box), + SepaBankTransfer(Box), + BacsBankTransfers(Box), } #[derive(Debug, Eq, PartialEq, Serialize)] @@ -445,6 +488,16 @@ pub enum StripePaymentMethodType { Alipay, #[serde(rename = "p24")] Przelewy24, + CustomerBalance, +} + +#[derive(Debug, Eq, PartialEq, Serialize, Clone)] +#[serde(rename_all = "snake_case")] +pub enum BankTransferType { + GbBankTransfer, + EuBankTransfer, + #[serde(rename = "bank_transfer")] + BankTransfers, } #[derive(Debug, Eq, PartialEq, Serialize, Clone)] @@ -1011,13 +1064,63 @@ fn create_stripe_payment_method( } payments::PaymentMethodData::BankTransfer(bank_transfer_data) => { match bank_transfer_data.deref() { - payments::BankTransferData::AchBankTransfer(ach_bank_transfer_data) => Ok(( - StripePaymentMethodData::AchBankTransfer(BankTransferData { - email: ach_bank_transfer_data.billing_details.email.to_owned(), - }), + payments::BankTransferData::AchBankTransfer { billing_details } => Ok(( + StripePaymentMethodData::BankTransfer(StripeBankTransferData::AchBankTransfer( + Box::new(AchBankTransferData { + email: billing_details.email.to_owned(), + }), + )), StripePaymentMethodType::AchCreditTransfer, StripeBillingAddress::default(), )), + payments::BankTransferData::SepaBankTransfer { + billing_details, + country, + } => { + let billing_details = StripeBillingAddress { + email: Some(billing_details.email.clone()), + name: Some(billing_details.name.clone()), + ..Default::default() + }; + Ok(( + StripePaymentMethodData::BankTransfer( + StripeBankTransferData::SepaBankTransfer(Box::new( + SepaBankTransferData { + payment_method_data_type: + StripePaymentMethodType::CustomerBalance, + bank_transfer_type: BankTransferType::EuBankTransfer, + balance_funding_type: BankTransferType::BankTransfers, + payment_method_type: StripePaymentMethodType::CustomerBalance, + country: country.to_owned(), + }, + )), + ), + StripePaymentMethodType::CustomerBalance, + billing_details, + )) + } + payments::BankTransferData::BacsBankTransfer { billing_details } => { + let billing_details = StripeBillingAddress { + email: Some(billing_details.email.clone()), + name: Some(billing_details.name.clone()), + ..Default::default() + }; + Ok(( + StripePaymentMethodData::BankTransfer( + StripeBankTransferData::BacsBankTransfers(Box::new( + BacsBankTransferData { + payment_method_data_type: + StripePaymentMethodType::CustomerBalance, + bank_transfer_type: BankTransferType::GbBankTransfer, + balance_funding_type: BankTransferType::BankTransfers, + payment_method_type: StripePaymentMethodType::CustomerBalance, + }, + )), + ), + StripePaymentMethodType::CustomerBalance, + billing_details, + )) + } } } _ => Err(errors::ConnectorError::NotImplemented( @@ -1336,6 +1439,20 @@ pub struct AchReceiverDetails { pub amount_charged: i64, } +#[serde_with::skip_serializing_none] +#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)] +pub struct SepaAndBacsBankTransferInstructions { + pub bacs_bank_instructions: Option, + pub sepa_bank_instructions: Option, + pub receiver: SepaAndBacsReceiver, +} + +#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)] +pub struct SepaAndBacsReceiver { + pub amount_received: i64, + pub amount_remaining: i64, +} + #[derive(Debug, Default, Eq, PartialEq, Deserialize)] pub struct PaymentSyncResponse { #[serde(flatten)] @@ -1455,7 +1572,8 @@ impl ForeignFrom<(Option, String)> for types::Mandat | StripePaymentMethodOptions::Alipay {} | StripePaymentMethodOptions::Sepa {} | StripePaymentMethodOptions::Bancontact {} - | StripePaymentMethodOptions::Przelewy24 {} => None, + | StripePaymentMethodOptions::Przelewy24 {} + | StripePaymentMethodOptions::CustomerBalance {} => None, }), payment_method_id: Some(payment_method_id), } @@ -1470,9 +1588,12 @@ impl fn try_from( item: types::ResponseRouterData, ) -> Result { - let redirection_data = item.response.next_action.map(|next_action_response| { - services::RedirectForm::from((next_action_response.get_url(), services::Method::Get)) - }); + let redirect_data = item.response.next_action.clone(); + let redirection_data = redirect_data + .and_then(|redirection_data| redirection_data.get_url()) + .map(|redirection_url| { + services::RedirectForm::from((redirection_url, services::Method::Get)) + }); let mandate_reference = item.response.payment_method.map(|pm| { types::MandateReference::foreign_from((item.response.payment_method_options, pm)) @@ -1501,6 +1622,34 @@ impl } } +pub fn get_connector_metadata( + next_action: Option<&StripeNextActionResponse>, + amount: i64, +) -> CustomResult, errors::ConnectorError> { + let next_action_response = next_action + .and_then(|next_action_response| match next_action_response { + StripeNextActionResponse::DisplayBankTransferInstructions(response) => { + Some(SepaAndBacsBankTransferInstructions { + sepa_bank_instructions: response.financial_addresses[0].iban.to_owned(), + bacs_bank_instructions: response.financial_addresses[0] + .sort_code + .to_owned(), + receiver: SepaAndBacsReceiver { + amount_received: amount - response.amount_remaining, + amount_remaining: response.amount_remaining, + }, + }) + } + _ => None, + }).map(|response| { + common_utils::ext_traits::Encode::::encode_to_value( + &response, + ) + .change_context(errors::ConnectorError::ResponseHandlingFailed) + }).transpose()?; + Ok(next_action_response) +} + impl TryFrom> for types::RouterData @@ -1514,15 +1663,11 @@ impl types::PaymentsResponseData, >, ) -> Result { - let redirection_data = item - .response - .next_action - .as_ref() - .map(|next_action_response| { - services::RedirectForm::from(( - next_action_response.get_url(), - services::Method::Get, - )) + let redirect_data = item.response.next_action.clone(); + let redirection_data = redirect_data + .and_then(|redirection_data| redirection_data.get_url()) + .map(|redirection_url| { + services::RedirectForm::from((redirection_url, services::Method::Get)) }); let mandate_reference = item.response.payment_method.clone().map(|pm| { @@ -1542,12 +1687,15 @@ impl status_code: item.http_code, }); + let connector_metadata = + get_connector_metadata(item.response.next_action.as_ref(), item.response.amount)?; + let response = error_res.map_or( Ok(types::PaymentsResponseData::TransactionResponse { resource_id: types::ResponseId::ConnectorTransactionId(item.response.id.clone()), redirection_data, mandate_reference, - connector_metadata: None, + connector_metadata, network_txn_id: None, }), Err, @@ -1570,9 +1718,12 @@ impl fn try_from( item: types::ResponseRouterData, ) -> Result { - let redirection_data = item.response.next_action.map(|next_action_response| { - services::RedirectForm::from((next_action_response.get_url(), services::Method::Get)) - }); + let redirect_data = item.response.next_action.clone(); + let redirection_data = redirect_data + .and_then(|redirection_data| redirection_data.get_url()) + .map(|redirection_url| { + services::RedirectForm::from((redirection_url, services::Method::Get)) + }); let mandate_reference = item.response.payment_method.map(|pm| { types::MandateReference::foreign_from((item.response.payment_method_options, pm)) @@ -1616,18 +1767,20 @@ pub enum StripeNextActionResponse { AlipayHandleRedirect(StripeRedirectToUrlResponse), VerifyWithMicrodeposits(StripeVerifyWithMicroDepositsResponse), WechatPayDisplayQrCode(StripeRedirectToQr), + DisplayBankTransferInstructions(StripeBankTransferDetails), } impl StripeNextActionResponse { - fn get_url(&self) -> Url { + fn get_url(&self) -> Option { match self { Self::RedirectToUrl(redirect_to_url) | Self::AlipayHandleRedirect(redirect_to_url) => { - redirect_to_url.url.to_owned() + Some(redirect_to_url.url.to_owned()) } - Self::WechatPayDisplayQrCode(redirect_to_url) => redirect_to_url.data.to_owned(), + Self::WechatPayDisplayQrCode(redirect_to_url) => Some(redirect_to_url.data.to_owned()), Self::VerifyWithMicrodeposits(verify_with_microdeposits) => { - verify_with_microdeposits.hosted_verification_url.to_owned() + Some(verify_with_microdeposits.hosted_verification_url.to_owned()) } + Self::DisplayBankTransferInstructions(_) => None, } } } @@ -1668,6 +1821,41 @@ pub struct StripeVerifyWithMicroDepositsResponse { hosted_verification_url: Url, } +#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] +pub struct StripeBankTransferDetails { + pub amount_remaining: i64, + pub currency: String, + pub financial_addresses: Vec, + pub hosted_instructions_url: Option, + pub reference: Option, + #[serde(rename = "type")] + pub bank_transfer_type: Option, +} + +#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] +pub struct StripeFinanicalInformation { + pub iban: Option, + pub sort_code: Option, + pub supported_networks: Vec, + #[serde(rename = "type")] + pub financial_info_type: String, +} + +#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] +pub struct SepaFinancialDetails { + pub account_holder_name: String, + pub bic: String, + pub country: String, + pub iban: String, +} + +#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] +pub struct BacsFinancialDetails { + pub account_holder_name: String, + pub account_number: String, + pub sort_code: String, +} + // REFUND : // Type definition for Stripe RefundRequest @@ -1872,6 +2060,7 @@ pub enum StripePaymentMethodOptions { Alipay {}, #[serde(rename = "p24")] Przelewy24 {}, + CustomerBalance {}, } #[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] @@ -1916,14 +2105,9 @@ impl TryFrom<&types::PaymentsPreProcessingRouterData> for StripeAchSourceRequest fn try_from(item: &types::PaymentsPreProcessingRouterData) -> Result { Ok(Self { transfer_type: StripePaymentMethodType::AchCreditTransfer, - email: item - .request - .email - .clone() - .get_required_value("email") - .change_context(errors::ConnectorError::MissingRequiredField { - field_name: "email", - })?, + payment_method_data: AchBankTransferData { + email: connector::utils::PaymentsPreProcessingData::get_email(&item.request)?, + }, currency: item .request .currency @@ -2006,7 +2190,7 @@ impl TryFrom::encode_to_value( &connector_source_response.source, ) - .change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?; + .change_context(errors::ConnectorError::ResponseHandlingFailed)?; Ok(Self { status: enums::AttemptStatus::from(item.response.status), response: Ok(types::PaymentsResponseData::TransactionResponse { @@ -2185,6 +2369,8 @@ pub enum WebhookEventType { SourceChargeable, #[serde(rename = "source.transaction.created")] SourceTransactionCreated, + #[serde(rename = "payment_intent.partially_funded")] + PaymentIntentPartiallyFunded, } #[derive(Debug, Serialize, strum::Display, Deserialize, PartialEq)] @@ -2303,11 +2489,32 @@ impl } api::PaymentMethodData::BankTransfer(bank_transfer_data) => { match bank_transfer_data.deref() { - payments::BankTransferData::AchBankTransfer(ach_bank_transfer_data) => { - Ok(Self::AchBankTransfer(BankTransferData { - email: ach_bank_transfer_data.billing_details.email.to_owned(), - })) + payments::BankTransferData::AchBankTransfer { billing_details } => { + Ok(Self::BankTransfer(StripeBankTransferData::AchBankTransfer( + Box::new(AchBankTransferData { + email: billing_details.email.to_owned(), + }), + ))) } + payments::BankTransferData::SepaBankTransfer { country, .. } => Ok( + Self::BankTransfer(StripeBankTransferData::SepaBankTransfer(Box::new( + SepaBankTransferData { + payment_method_data_type: StripePaymentMethodType::CustomerBalance, + bank_transfer_type: BankTransferType::EuBankTransfer, + balance_funding_type: BankTransferType::BankTransfers, + payment_method_type: StripePaymentMethodType::CustomerBalance, + country: country.to_owned(), + }, + ))), + ), + payments::BankTransferData::BacsBankTransfer { .. } => Ok(Self::BankTransfer( + StripeBankTransferData::BacsBankTransfers(Box::new(BacsBankTransferData { + payment_method_data_type: StripePaymentMethodType::CustomerBalance, + bank_transfer_type: BankTransferType::GbBankTransfer, + balance_funding_type: BankTransferType::BankTransfers, + payment_method_type: StripePaymentMethodType::CustomerBalance, + })), + )), } } api::PaymentMethodData::MandatePayment | api::PaymentMethodData::Crypto(_) => { @@ -2326,35 +2533,58 @@ impl pub struct StripeGpayToken { pub id: String, } + pub fn get_bank_transfer_request_data( req: &types::PaymentsAuthorizeRouterData, bank_transfer_data: &api_models::payments::BankTransferData, ) -> CustomResult, errors::ConnectorError> { match bank_transfer_data { - api_models::payments::BankTransferData::AchBankTransfer(_) => { + api_models::payments::BankTransferData::AchBankTransfer { .. } => { let req = ChargesRequest::try_from(req)?; let request = utils::Encode::::url_encode(&req) .change_context(errors::ConnectorError::RequestEncodingFailed)?; Ok(Some(request)) } + _ => { + let req = PaymentIntentRequest::try_from(req)?; + let request = utils::Encode::::url_encode(&req) + .change_context(errors::ConnectorError::RequestEncodingFailed)?; + Ok(Some(request)) + } } } + pub fn get_bank_transfer_authorize_response( data: &types::PaymentsAuthorizeRouterData, res: types::Response, - _bank_transfer_data: &api_models::payments::BankTransferData, + bank_transfer_data: &api_models::payments::BankTransferData, ) -> CustomResult { - let response: ChargesResponse = res - .response - .parse_struct("ChargesResponse") - .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + match bank_transfer_data { + api_models::payments::BankTransferData::AchBankTransfer { .. } => { + let response: ChargesResponse = res + .response + .parse_struct("ChargesResponse") + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; - types::RouterData::try_from(types::ResponseRouterData { - response, - data: data.clone(), - http_code: res.status_code, - }) - .change_context(errors::ConnectorError::ResponseHandlingFailed) + types::RouterData::try_from(types::ResponseRouterData { + response, + data: data.clone(), + http_code: res.status_code, + }) + } + _ => { + let response: PaymentIntentResponse = res + .response + .parse_struct("PaymentIntentResponse") + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + + types::RouterData::try_from(types::ResponseRouterData { + response, + data: data.clone(), + http_code: res.status_code, + }) + } + } } pub fn construct_file_upload_request( diff --git a/crates/router/src/connector/utils.rs b/crates/router/src/connector/utils.rs index f68e54ef9f..fa0488b65e 100644 --- a/crates/router/src/connector/utils.rs +++ b/crates/router/src/connector/utils.rs @@ -159,6 +159,16 @@ impl RouterData for types::RouterData Result; +} + +impl PaymentsPreProcessingData for types::PaymentsPreProcessingData { + fn get_email(&self) -> Result { + self.email.clone().ok_or_else(missing_field_err("email")) + } +} + pub trait PaymentsAuthorizeRequestData { fn is_auto_capture(&self) -> Result; fn get_email(&self) -> Result; diff --git a/crates/router/src/core/payment_methods/cards.rs b/crates/router/src/core/payment_methods/cards.rs index a8682829b0..a268992813 100644 --- a/crates/router/src/core/payment_methods/cards.rs +++ b/crates/router/src/core/payment_methods/cards.rs @@ -881,6 +881,9 @@ pub async fn list_payment_methods( let mut bank_debits_consolidated_hm = HashMap::>::new(); + let mut bank_transfer_consolidated_hm = + HashMap::>::new(); + for element in response.clone() { let payment_method = element.payment_method; let payment_method_type = element.payment_method_type; @@ -982,6 +985,17 @@ pub async fn list_payment_methods( bank_debits_consolidated_hm.insert(element.payment_method_type, vec![connector]); } } + + if element.payment_method == api_enums::PaymentMethod::BankTransfer { + let connector = element.connector.clone(); + if let Some(vector_of_connectors) = + bank_transfer_consolidated_hm.get_mut(&element.payment_method_type) + { + vector_of_connectors.push(connector); + } else { + bank_transfer_consolidated_hm.insert(element.payment_method_type, vec![connector]); + } + } } let mut payment_method_responses: Vec = vec![]; @@ -1002,6 +1016,7 @@ pub async fn list_payment_methods( card_networks: None, bank_names: None, bank_debits: None, + bank_transfers: None, }) } @@ -1028,6 +1043,7 @@ pub async fn list_payment_methods( payment_experience: None, bank_names: None, bank_debits: None, + bank_transfers: None, }) } @@ -1050,6 +1066,7 @@ pub async fn list_payment_methods( payment_experience: None, card_networks: None, bank_debits: None, + bank_transfers: None, } }) } @@ -1073,8 +1090,9 @@ pub async fn list_payment_methods( payment_experience: None, card_networks: None, bank_debits: Some(api_models::payment_methods::BankDebitTypes { - eligible_connectors: connectors, + eligible_connectors: connectors.clone(), }), + bank_transfers: None, } }) } @@ -1086,6 +1104,32 @@ pub async fn list_payment_methods( }); } + let mut bank_transfer_payment_method_types = vec![]; + + for key in bank_transfer_consolidated_hm.iter() { + let payment_method_type = *key.0; + let connectors = key.1.clone(); + bank_transfer_payment_method_types.push({ + ResponsePaymentMethodTypes { + payment_method_type, + bank_names: None, + payment_experience: None, + card_networks: None, + bank_debits: None, + bank_transfers: Some(api_models::payment_methods::BankTransferTypes { + eligible_connectors: connectors, + }), + } + }) + } + + if !bank_transfer_payment_method_types.is_empty() { + payment_method_responses.push(ResponsePaymentMethodsEnabled { + payment_method: api_enums::PaymentMethod::BankTransfer, + payment_method_types: bank_transfer_payment_method_types, + }); + } + response .is_empty() .then(|| Err(report!(errors::ApiErrorResponse::PaymentMethodNotFound))) diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 8b9e1c40e8..fb068f0b87 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -691,7 +691,7 @@ where //TODO: For ACH transfers, if preprocessing_step is not required for connectors encountered in future, add the check let router_data_and_should_continue_payment = match payment_data.payment_method_data.clone() { Some(api_models::payments::PaymentMethodData::BankTransfer(data)) => match data.deref() { - api_models::payments::BankTransferData::AchBankTransfer(_) => { + api_models::payments::BankTransferData::AchBankTransfer { .. } => { if payment_data.payment_attempt.preprocessing_step_id.is_none() { ( router_data.preprocessing_steps(state, connector).await?, @@ -701,6 +701,7 @@ where (router_data, should_continue_payment) } } + _ => (router_data, should_continue_payment), }, _ => (router_data, should_continue_payment), }; diff --git a/crates/router/src/types/transformers.rs b/crates/router/src/types/transformers.rs index 777af030c5..b02d5d5003 100644 --- a/crates/router/src/types/transformers.rs +++ b/crates/router/src/types/transformers.rs @@ -218,7 +218,8 @@ impl ForeignTryFrom for storage_enums::EventType { api_enums::IntentStatus::Succeeded => Ok(Self::PaymentSucceeded), api_enums::IntentStatus::Failed => Ok(Self::PaymentFailed), api_enums::IntentStatus::Processing => Ok(Self::PaymentProcessing), - api_enums::IntentStatus::RequiresMerchantAction => Ok(Self::ActionRequired), + api_enums::IntentStatus::RequiresMerchantAction + | api_enums::IntentStatus::RequiresCustomerAction => Ok(Self::ActionRequired), _ => Err(errors::ValidationError::IncorrectValueProvided { field_name: "intent_status", }),