mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-27 11:24:45 +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:
@ -488,6 +488,9 @@ pub enum BankDebitData {
|
||||
/// Routing number for ach bank debit payment
|
||||
#[schema(value_type = String, example = "110000000")]
|
||||
routing_number: Secret<String>,
|
||||
|
||||
#[schema(value_type = String, example = "John Test")]
|
||||
bank_account_holder_name: Option<Secret<String>>,
|
||||
},
|
||||
SepaBankDebit {
|
||||
/// Billing details for bank debit
|
||||
|
||||
@ -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