mirror of
				https://github.com/juspay/hyperswitch.git
				synced 2025-10-31 18:17:13 +08:00 
			
		
		
		
	feat(Connector):[Adyen]Implement ACH Direct Debits for Adyen (#1033)
Co-authored-by: Jagan Elavarasan <jaganelavarasan@gmail.com>
This commit is contained in:
		| @ -1,6 +1,4 @@ | ||||
| use api_models::{ | ||||
|     enums::DisputeStage, payments::MandateReferenceId, webhooks::IncomingWebhookEvent, | ||||
| }; | ||||
| use api_models::{enums, payments, webhooks}; | ||||
| use cards::CardNumber; | ||||
| use masking::PeekInterface; | ||||
| use reqwest::Url; | ||||
| @ -280,6 +278,17 @@ pub enum AdyenPaymentMethod<'a> { | ||||
|     Trustly(Box<BankRedirectionPMData>), | ||||
|     Walley(Box<WalleyData>), | ||||
|     WeChatPayWeb(Box<WeChatPayWebData>), | ||||
|     AchDirectDebit(Box<AchDirectDebitData>), | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Clone, Serialize)] | ||||
| #[serde(rename_all = "camelCase")] | ||||
| pub struct AchDirectDebitData { | ||||
|     #[serde(rename = "type")] | ||||
|     payment_type: PaymentType, | ||||
|     bank_account_number: Secret<String>, | ||||
|     bank_location_id: Secret<String>, | ||||
|     owner_name: Secret<String>, | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Clone, Serialize)] | ||||
| @ -652,6 +661,8 @@ pub enum PaymentType { | ||||
|     Walley, | ||||
|     #[serde(rename = "wechatpayWeb")] | ||||
|     WeChatPayWeb, | ||||
|     #[serde(rename = "ach")] | ||||
|     AchDirectDebit, | ||||
| } | ||||
|  | ||||
| pub struct AdyenTestBankNames<'a>(&'a str); | ||||
| @ -755,6 +766,9 @@ impl<'a> TryFrom<&types::PaymentsAuthorizeRouterData> for AdyenPaymentRequest<'a | ||||
|                 api_models::payments::PaymentMethodData::BankRedirect(ref bank_redirect) => { | ||||
|                     AdyenPaymentRequest::try_from((item, bank_redirect)) | ||||
|                 } | ||||
|                 api_models::payments::PaymentMethodData::BankDebit(ref bank_debit) => { | ||||
|                     AdyenPaymentRequest::try_from((item, bank_debit)) | ||||
|                 } | ||||
|                 _ => Err(errors::ConnectorError::NotSupported { | ||||
|                     message: format!("{:?}", item.request.payment_method_type), | ||||
|                     connector: "Adyen", | ||||
| @ -905,6 +919,35 @@ fn get_country_code(item: &types::PaymentsAuthorizeRouterData) -> Option<api_enu | ||||
|         .and_then(|billing| billing.address.as_ref().and_then(|address| address.country)) | ||||
| } | ||||
|  | ||||
| impl<'a> TryFrom<&api_models::payments::BankDebitData> for AdyenPaymentMethod<'a> { | ||||
|     type Error = Error; | ||||
|     fn try_from( | ||||
|         bank_debit_data: &api_models::payments::BankDebitData, | ||||
|     ) -> Result<Self, Self::Error> { | ||||
|         match bank_debit_data { | ||||
|             payments::BankDebitData::AchBankDebit { | ||||
|                 account_number, | ||||
|                 routing_number, | ||||
|                 billing_details: _, | ||||
|                 bank_account_holder_name, | ||||
|             } => Ok(AdyenPaymentMethod::AchDirectDebit(Box::new( | ||||
|                 AchDirectDebitData { | ||||
|                     payment_type: PaymentType::AchDirectDebit, | ||||
|                     bank_account_number: account_number.clone(), | ||||
|                     bank_location_id: routing_number.clone(), | ||||
|                     owner_name: bank_account_holder_name.clone().ok_or( | ||||
|                         errors::ConnectorError::MissingRequiredField { | ||||
|                             field_name: "bank_account_holder_name", | ||||
|                         }, | ||||
|                     )?, | ||||
|                 }, | ||||
|             ))), | ||||
|  | ||||
|             _ => Err(errors::ConnectorError::NotImplemented("Payment method".to_string()).into()), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a> TryFrom<&api::Card> for AdyenPaymentMethod<'a> { | ||||
|     type Error = Error; | ||||
|     fn try_from(card: &api::Card) -> Result<Self, Self::Error> { | ||||
| @ -1107,12 +1150,18 @@ impl<'a> TryFrom<&api_models::payments::BankRedirectData> for AdyenPaymentMethod | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a> TryFrom<(&types::PaymentsAuthorizeRouterData, MandateReferenceId)> | ||||
|     for AdyenPaymentRequest<'a> | ||||
| impl<'a> | ||||
|     TryFrom<( | ||||
|         &types::PaymentsAuthorizeRouterData, | ||||
|         payments::MandateReferenceId, | ||||
|     )> for AdyenPaymentRequest<'a> | ||||
| { | ||||
|     type Error = Error; | ||||
|     fn try_from( | ||||
|         value: (&types::PaymentsAuthorizeRouterData, MandateReferenceId), | ||||
|         value: ( | ||||
|             &types::PaymentsAuthorizeRouterData, | ||||
|             payments::MandateReferenceId, | ||||
|         ), | ||||
|     ) -> Result<Self, Self::Error> { | ||||
|         let (item, mandate_ref_id) = value; | ||||
|         let amount = get_amount_data(item); | ||||
| @ -1124,7 +1173,7 @@ impl<'a> TryFrom<(&types::PaymentsAuthorizeRouterData, MandateReferenceId)> | ||||
|         let additional_data = get_additional_data(item); | ||||
|         let return_url = item.request.get_return_url()?; | ||||
|         let payment_method = match mandate_ref_id { | ||||
|             MandateReferenceId::ConnectorMandateId(connector_mandate_ids) => { | ||||
|             payments::MandateReferenceId::ConnectorMandateId(connector_mandate_ids) => { | ||||
|                 let adyen_mandate = AdyenMandate { | ||||
|                     payment_type: PaymentType::Scheme, | ||||
|                     stored_payment_method_id: connector_mandate_ids.get_connector_mandate_id()?, | ||||
| @ -1133,7 +1182,7 @@ impl<'a> TryFrom<(&types::PaymentsAuthorizeRouterData, MandateReferenceId)> | ||||
|                     adyen_mandate, | ||||
|                 ))) | ||||
|             } | ||||
|             MandateReferenceId::NetworkMandateId(network_mandate_id) => { | ||||
|             payments::MandateReferenceId::NetworkMandateId(network_mandate_id) => { | ||||
|                 match item.request.payment_method_data { | ||||
|                     api::PaymentMethodData::Card(ref card) => { | ||||
|                         let card_issuer = card.get_card_issuer()?; | ||||
| @ -1220,6 +1269,55 @@ impl<'a> TryFrom<(&types::PaymentsAuthorizeRouterData, &api::Card)> for AdyenPay | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a> | ||||
|     TryFrom<( | ||||
|         &types::PaymentsAuthorizeRouterData, | ||||
|         &api_models::payments::BankDebitData, | ||||
|     )> for AdyenPaymentRequest<'a> | ||||
| { | ||||
|     type Error = Error; | ||||
|  | ||||
|     fn try_from( | ||||
|         value: ( | ||||
|             &types::PaymentsAuthorizeRouterData, | ||||
|             &api_models::payments::BankDebitData, | ||||
|         ), | ||||
|     ) -> Result<Self, Self::Error> { | ||||
|         let (item, bank_debit_data) = value; | ||||
|         let amount = get_amount_data(item); | ||||
|         let auth_type = AdyenAuthType::try_from(&item.connector_auth_type)?; | ||||
|         let shopper_interaction = AdyenShopperInteraction::from(item); | ||||
|         let recurring_processing_model = get_recurring_processing_model(item)?.0; | ||||
|         let browser_info = get_browser_info(item); | ||||
|         let additional_data = get_additional_data(item); | ||||
|         let return_url = item.request.get_return_url()?; | ||||
|         let payment_method = AdyenPaymentMethod::try_from(bank_debit_data)?; | ||||
|         let country_code = get_country_code(item); | ||||
|         let request = AdyenPaymentRequest { | ||||
|             amount, | ||||
|             merchant_account: auth_type.merchant_account, | ||||
|             payment_method, | ||||
|             reference: item.payment_id.to_string(), | ||||
|             return_url, | ||||
|             browser_info, | ||||
|             shopper_interaction, | ||||
|             recurring_processing_model, | ||||
|             additional_data, | ||||
|             shopper_name: None, | ||||
|             shopper_locale: None, | ||||
|             shopper_email: item.request.email.clone(), | ||||
|             telephone_number: None, | ||||
|             billing_address: None, | ||||
|             delivery_address: None, | ||||
|             country_code, | ||||
|             line_items: None, | ||||
|             shopper_reference: None, | ||||
|             store_payment_method: None, | ||||
|         }; | ||||
|         Ok(request) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a> | ||||
|     TryFrom<( | ||||
|         &types::PaymentsAuthorizeRouterData, | ||||
| @ -1790,7 +1888,7 @@ pub fn is_chargeback_event(event_code: &WebhookEventCode) -> bool { | ||||
|     ) | ||||
| } | ||||
|  | ||||
| impl ForeignFrom<(WebhookEventCode, Option<DisputeStatus>)> for IncomingWebhookEvent { | ||||
| impl ForeignFrom<(WebhookEventCode, Option<DisputeStatus>)> for webhooks::IncomingWebhookEvent { | ||||
|     fn foreign_from((code, status): (WebhookEventCode, Option<DisputeStatus>)) -> Self { | ||||
|         match (code, status) { | ||||
|             (WebhookEventCode::Authorisation, _) => Self::PaymentIntentSuccess, | ||||
| @ -1816,7 +1914,7 @@ impl ForeignFrom<(WebhookEventCode, Option<DisputeStatus>)> for IncomingWebhookE | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<WebhookEventCode> for DisputeStage { | ||||
| impl From<WebhookEventCode> for enums::DisputeStage { | ||||
|     fn from(code: WebhookEventCode) -> Self { | ||||
|         match code { | ||||
|             WebhookEventCode::NotificationOfChargeback => Self::PreDispute, | ||||
|  | ||||
| @ -668,6 +668,7 @@ fn get_bank_debit_data( | ||||
|             billing_details, | ||||
|             account_number, | ||||
|             routing_number, | ||||
|             .. | ||||
|         } => { | ||||
|             let ach_data = BankDebitData::Ach { | ||||
|                 account_holder_type: "individual".to_string(), | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 Swangi Kumari
					Swangi Kumari