Skip to content

Commit eee55bd

Browse files
feat(Connector):[Adyen]Implement ACH Direct Debits for Adyen (#1033)
Co-authored-by: Jagan Elavarasan <[email protected]>
1 parent 8c34114 commit eee55bd

File tree

3 files changed

+112
-10
lines changed

3 files changed

+112
-10
lines changed

crates/api_models/src/payments.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,9 @@ pub enum BankDebitData {
488488
/// Routing number for ach bank debit payment
489489
#[schema(value_type = String, example = "110000000")]
490490
routing_number: Secret<String>,
491+
492+
#[schema(value_type = String, example = "John Test")]
493+
bank_account_holder_name: Option<Secret<String>>,
491494
},
492495
SepaBankDebit {
493496
/// Billing details for bank debit

crates/router/src/connector/adyen/transformers.rs

Lines changed: 108 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
use api_models::{
2-
enums::DisputeStage, payments::MandateReferenceId, webhooks::IncomingWebhookEvent,
3-
};
1+
use api_models::{enums, payments, webhooks};
42
use cards::CardNumber;
53
use masking::PeekInterface;
64
use reqwest::Url;
@@ -280,6 +278,17 @@ pub enum AdyenPaymentMethod<'a> {
280278
Trustly(Box<BankRedirectionPMData>),
281279
Walley(Box<WalleyData>),
282280
WeChatPayWeb(Box<WeChatPayWebData>),
281+
AchDirectDebit(Box<AchDirectDebitData>),
282+
}
283+
284+
#[derive(Debug, Clone, Serialize)]
285+
#[serde(rename_all = "camelCase")]
286+
pub struct AchDirectDebitData {
287+
#[serde(rename = "type")]
288+
payment_type: PaymentType,
289+
bank_account_number: Secret<String>,
290+
bank_location_id: Secret<String>,
291+
owner_name: Secret<String>,
283292
}
284293

285294
#[derive(Debug, Clone, Serialize)]
@@ -652,6 +661,8 @@ pub enum PaymentType {
652661
Walley,
653662
#[serde(rename = "wechatpayWeb")]
654663
WeChatPayWeb,
664+
#[serde(rename = "ach")]
665+
AchDirectDebit,
655666
}
656667

657668
pub struct AdyenTestBankNames<'a>(&'a str);
@@ -755,6 +766,9 @@ impl<'a> TryFrom<&types::PaymentsAuthorizeRouterData> for AdyenPaymentRequest<'a
755766
api_models::payments::PaymentMethodData::BankRedirect(ref bank_redirect) => {
756767
AdyenPaymentRequest::try_from((item, bank_redirect))
757768
}
769+
api_models::payments::PaymentMethodData::BankDebit(ref bank_debit) => {
770+
AdyenPaymentRequest::try_from((item, bank_debit))
771+
}
758772
_ => Err(errors::ConnectorError::NotSupported {
759773
message: format!("{:?}", item.request.payment_method_type),
760774
connector: "Adyen",
@@ -905,6 +919,35 @@ fn get_country_code(item: &types::PaymentsAuthorizeRouterData) -> Option<api_enu
905919
.and_then(|billing| billing.address.as_ref().and_then(|address| address.country))
906920
}
907921

922+
impl<'a> TryFrom<&api_models::payments::BankDebitData> for AdyenPaymentMethod<'a> {
923+
type Error = Error;
924+
fn try_from(
925+
bank_debit_data: &api_models::payments::BankDebitData,
926+
) -> Result<Self, Self::Error> {
927+
match bank_debit_data {
928+
payments::BankDebitData::AchBankDebit {
929+
account_number,
930+
routing_number,
931+
billing_details: _,
932+
bank_account_holder_name,
933+
} => Ok(AdyenPaymentMethod::AchDirectDebit(Box::new(
934+
AchDirectDebitData {
935+
payment_type: PaymentType::AchDirectDebit,
936+
bank_account_number: account_number.clone(),
937+
bank_location_id: routing_number.clone(),
938+
owner_name: bank_account_holder_name.clone().ok_or(
939+
errors::ConnectorError::MissingRequiredField {
940+
field_name: "bank_account_holder_name",
941+
},
942+
)?,
943+
},
944+
))),
945+
946+
_ => Err(errors::ConnectorError::NotImplemented("Payment method".to_string()).into()),
947+
}
948+
}
949+
}
950+
908951
impl<'a> TryFrom<&api::Card> for AdyenPaymentMethod<'a> {
909952
type Error = Error;
910953
fn try_from(card: &api::Card) -> Result<Self, Self::Error> {
@@ -1107,12 +1150,18 @@ impl<'a> TryFrom<&api_models::payments::BankRedirectData> for AdyenPaymentMethod
11071150
}
11081151
}
11091152

1110-
impl<'a> TryFrom<(&types::PaymentsAuthorizeRouterData, MandateReferenceId)>
1111-
for AdyenPaymentRequest<'a>
1153+
impl<'a>
1154+
TryFrom<(
1155+
&types::PaymentsAuthorizeRouterData,
1156+
payments::MandateReferenceId,
1157+
)> for AdyenPaymentRequest<'a>
11121158
{
11131159
type Error = Error;
11141160
fn try_from(
1115-
value: (&types::PaymentsAuthorizeRouterData, MandateReferenceId),
1161+
value: (
1162+
&types::PaymentsAuthorizeRouterData,
1163+
payments::MandateReferenceId,
1164+
),
11161165
) -> Result<Self, Self::Error> {
11171166
let (item, mandate_ref_id) = value;
11181167
let amount = get_amount_data(item);
@@ -1124,7 +1173,7 @@ impl<'a> TryFrom<(&types::PaymentsAuthorizeRouterData, MandateReferenceId)>
11241173
let additional_data = get_additional_data(item);
11251174
let return_url = item.request.get_return_url()?;
11261175
let payment_method = match mandate_ref_id {
1127-
MandateReferenceId::ConnectorMandateId(connector_mandate_ids) => {
1176+
payments::MandateReferenceId::ConnectorMandateId(connector_mandate_ids) => {
11281177
let adyen_mandate = AdyenMandate {
11291178
payment_type: PaymentType::Scheme,
11301179
stored_payment_method_id: connector_mandate_ids.get_connector_mandate_id()?,
@@ -1133,7 +1182,7 @@ impl<'a> TryFrom<(&types::PaymentsAuthorizeRouterData, MandateReferenceId)>
11331182
adyen_mandate,
11341183
)))
11351184
}
1136-
MandateReferenceId::NetworkMandateId(network_mandate_id) => {
1185+
payments::MandateReferenceId::NetworkMandateId(network_mandate_id) => {
11371186
match item.request.payment_method_data {
11381187
api::PaymentMethodData::Card(ref card) => {
11391188
let card_issuer = card.get_card_issuer()?;
@@ -1220,6 +1269,55 @@ impl<'a> TryFrom<(&types::PaymentsAuthorizeRouterData, &api::Card)> for AdyenPay
12201269
}
12211270
}
12221271

1272+
impl<'a>
1273+
TryFrom<(
1274+
&types::PaymentsAuthorizeRouterData,
1275+
&api_models::payments::BankDebitData,
1276+
)> for AdyenPaymentRequest<'a>
1277+
{
1278+
type Error = Error;
1279+
1280+
fn try_from(
1281+
value: (
1282+
&types::PaymentsAuthorizeRouterData,
1283+
&api_models::payments::BankDebitData,
1284+
),
1285+
) -> Result<Self, Self::Error> {
1286+
let (item, bank_debit_data) = value;
1287+
let amount = get_amount_data(item);
1288+
let auth_type = AdyenAuthType::try_from(&item.connector_auth_type)?;
1289+
let shopper_interaction = AdyenShopperInteraction::from(item);
1290+
let recurring_processing_model = get_recurring_processing_model(item)?.0;
1291+
let browser_info = get_browser_info(item);
1292+
let additional_data = get_additional_data(item);
1293+
let return_url = item.request.get_return_url()?;
1294+
let payment_method = AdyenPaymentMethod::try_from(bank_debit_data)?;
1295+
let country_code = get_country_code(item);
1296+
let request = AdyenPaymentRequest {
1297+
amount,
1298+
merchant_account: auth_type.merchant_account,
1299+
payment_method,
1300+
reference: item.payment_id.to_string(),
1301+
return_url,
1302+
browser_info,
1303+
shopper_interaction,
1304+
recurring_processing_model,
1305+
additional_data,
1306+
shopper_name: None,
1307+
shopper_locale: None,
1308+
shopper_email: item.request.email.clone(),
1309+
telephone_number: None,
1310+
billing_address: None,
1311+
delivery_address: None,
1312+
country_code,
1313+
line_items: None,
1314+
shopper_reference: None,
1315+
store_payment_method: None,
1316+
};
1317+
Ok(request)
1318+
}
1319+
}
1320+
12231321
impl<'a>
12241322
TryFrom<(
12251323
&types::PaymentsAuthorizeRouterData,
@@ -1790,7 +1888,7 @@ pub fn is_chargeback_event(event_code: &WebhookEventCode) -> bool {
17901888
)
17911889
}
17921890

1793-
impl ForeignFrom<(WebhookEventCode, Option<DisputeStatus>)> for IncomingWebhookEvent {
1891+
impl ForeignFrom<(WebhookEventCode, Option<DisputeStatus>)> for webhooks::IncomingWebhookEvent {
17941892
fn foreign_from((code, status): (WebhookEventCode, Option<DisputeStatus>)) -> Self {
17951893
match (code, status) {
17961894
(WebhookEventCode::Authorisation, _) => Self::PaymentIntentSuccess,
@@ -1816,7 +1914,7 @@ impl ForeignFrom<(WebhookEventCode, Option<DisputeStatus>)> for IncomingWebhookE
18161914
}
18171915
}
18181916

1819-
impl From<WebhookEventCode> for DisputeStage {
1917+
impl From<WebhookEventCode> for enums::DisputeStage {
18201918
fn from(code: WebhookEventCode) -> Self {
18211919
match code {
18221920
WebhookEventCode::NotificationOfChargeback => Self::PreDispute,

crates/router/src/connector/stripe/transformers.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -668,6 +668,7 @@ fn get_bank_debit_data(
668668
billing_details,
669669
account_number,
670670
routing_number,
671+
..
671672
} => {
672673
let ach_data = BankDebitData::Ach {
673674
account_holder_type: "individual".to_string(),

0 commit comments

Comments
 (0)