Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(pm_auth): Support different pm types in PM auth #3114

Merged
merged 14 commits into from
Mar 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions crates/api_models/src/payment_methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,17 +222,24 @@ pub struct PaymentMethodDataBankCreds {
pub connector_details: Vec<BankAccountConnectorDetails>,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct BankAccountTokenData {
pub payment_method_type: api_enums::PaymentMethodType,
pub payment_method: api_enums::PaymentMethod,
pub connector_details: BankAccountConnectorDetails,
}

#[derive(Debug, Clone, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct BankAccountConnectorDetails {
pub connector: String,
pub account_id: String,
pub account_id: masking::Secret<String>,
pub mca_id: String,
pub access_token: BankAccountAccessCreds,
}

#[derive(Debug, Clone, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
pub enum BankAccountAccessCreds {
AccessToken(String),
AccessToken(masking::Secret<String>),
}
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, ToSchema)]
pub struct CardDetailFromLocker {
Expand Down
92 changes: 78 additions & 14 deletions crates/pm_auth/src/connector/plaid/transformers.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::collections::HashMap;

use common_enums::PaymentMethodType;
use masking::Secret;
use common_enums::{PaymentMethod, PaymentMethodType};
use masking::{PeekInterface, Secret};
use serde::{Deserialize, Serialize};

use crate::{core::errors, types};
Expand Down Expand Up @@ -200,13 +200,19 @@ pub struct PlaidBankAccountCredentialsBacs {
impl TryFrom<&types::BankDetailsRouterData> for PlaidBankAccountCredentialsRequest {
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(item: &types::BankDetailsRouterData) -> Result<Self, Self::Error> {
let options = item.request.optional_ids.as_ref().map(|bank_account_ids| {
let ids = bank_account_ids
.ids
.iter()
.map(|id| id.peek().to_string())
.collect::<Vec<_>>();

BankAccountCredentialsOptions { account_ids: ids }
});

Ok(Self {
access_token: item.request.access_token.clone(),
options: item.request.optional_ids.as_ref().map(|bank_account_ids| {
BankAccountCredentialsOptions {
account_ids: bank_account_ids.ids.clone(),
}
}),
access_token: item.request.access_token.peek().to_string(),
options,
})
}
}
Expand All @@ -232,26 +238,84 @@ impl<F, T>
) -> Result<Self, Self::Error> {
let (account_numbers, accounts_info) = (item.response.numbers, item.response.accounts);
let mut bank_account_vec = Vec::new();
let mut id_to_suptype = HashMap::new();
let mut id_to_subtype = HashMap::new();

accounts_info.into_iter().for_each(|acc| {
id_to_suptype.insert(acc.account_id, (acc.subtype, acc.name));
id_to_subtype.insert(acc.account_id, (acc.subtype, acc.name));
});

account_numbers.ach.into_iter().for_each(|ach| {
let (acc_type, acc_name) =
if let Some((_type, name)) = id_to_suptype.get(&ach.account_id) {
if let Some((_type, name)) = id_to_subtype.get(&ach.account_id) {
(_type.to_owned(), Some(name.clone()))
} else {
(None, None)
};

let account_details =
types::PaymentMethodTypeDetails::Ach(types::BankAccountDetailsAch {
account_number: Secret::new(ach.account),
routing_number: Secret::new(ach.routing),
});

let bank_details_new = types::BankAccountDetails {
account_name: acc_name,
account_number: ach.account,
routing_number: ach.routing,
account_details,
payment_method_type: PaymentMethodType::Ach,
account_id: ach.account_id,
payment_method: PaymentMethod::BankDebit,
account_id: ach.account_id.into(),
account_type: acc_type,
};

bank_account_vec.push(bank_details_new);
});

account_numbers.bacs.into_iter().for_each(|bacs| {
let (acc_type, acc_name) =
if let Some((_type, name)) = id_to_subtype.get(&bacs.account_id) {
(_type.to_owned(), Some(name.clone()))
} else {
(None, None)
};

let account_details =
types::PaymentMethodTypeDetails::Bacs(types::BankAccountDetailsBacs {
account_number: Secret::new(bacs.account),
sort_code: Secret::new(bacs.sort_code),
});

let bank_details_new = types::BankAccountDetails {
account_name: acc_name,
account_details,
payment_method_type: PaymentMethodType::Bacs,
payment_method: PaymentMethod::BankDebit,
account_id: bacs.account_id.into(),
account_type: acc_type,
};

bank_account_vec.push(bank_details_new);
});

account_numbers.international.into_iter().for_each(|sepa| {
let (acc_type, acc_name) =
if let Some((_type, name)) = id_to_subtype.get(&sepa.account_id) {
(_type.to_owned(), Some(name.clone()))
} else {
(None, None)
};

let account_details =
types::PaymentMethodTypeDetails::Sepa(types::BankAccountDetailsSepa {
iban: Secret::new(sepa.iban),
bic: Secret::new(sepa.bic),
});

let bank_details_new = types::BankAccountDetails {
account_name: acc_name,
account_details,
payment_method_type: PaymentMethodType::Sepa,
payment_method: PaymentMethod::BankDebit,
account_id: sepa.account_id.into(),
account_type: acc_type,
};

Expand Down
36 changes: 30 additions & 6 deletions crates/pm_auth/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ pub mod api;
use std::marker::PhantomData;

use api::auth_service::{BankAccountCredentials, ExchangeToken, LinkToken};
use common_enums::PaymentMethodType;
use common_enums::{PaymentMethod, PaymentMethodType};
use masking::Secret;
#[derive(Debug, Clone)]
pub struct PaymentAuthRouterData<F, Request, Response> {
Expand Down Expand Up @@ -55,13 +55,13 @@ pub type ExchangeTokenRouterData =

#[derive(Debug, Clone)]
pub struct BankAccountCredentialsRequest {
pub access_token: String,
pub access_token: Secret<String>,
pub optional_ids: Option<BankAccountOptionalIDs>,
}

#[derive(Debug, Clone)]
pub struct BankAccountOptionalIDs {
pub ids: Vec<String>,
pub ids: Vec<Secret<String>>,
}

#[derive(Debug, Clone)]
Expand All @@ -72,13 +72,37 @@ pub struct BankAccountCredentialsResponse {
#[derive(Debug, Clone)]
pub struct BankAccountDetails {
pub account_name: Option<String>,
pub account_number: String,
pub routing_number: String,
pub account_details: PaymentMethodTypeDetails,
pub payment_method_type: PaymentMethodType,
pub account_id: String,
pub payment_method: PaymentMethod,
pub account_id: Secret<String>,
pub account_type: Option<String>,
}

#[derive(Debug, Clone)]
pub enum PaymentMethodTypeDetails {
Ach(BankAccountDetailsAch),
Bacs(BankAccountDetailsBacs),
Sepa(BankAccountDetailsSepa),
}
#[derive(Debug, Clone)]
pub struct BankAccountDetailsAch {
pub account_number: Secret<String>,
pub routing_number: Secret<String>,
}

#[derive(Debug, Clone)]
pub struct BankAccountDetailsBacs {
pub account_number: Secret<String>,
pub sort_code: Secret<String>,
}

#[derive(Debug, Clone)]
pub struct BankAccountDetailsSepa {
pub iban: Secret<String>,
pub bic: Secret<String>,
}

pub type BankDetailsRouterData = PaymentAuthRouterData<
BankAccountCredentials,
BankAccountCredentialsRequest,
Expand Down
30 changes: 27 additions & 3 deletions crates/router/src/configs/defaults.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6453,7 +6453,15 @@ impl Default for super::settings::RequiredFields {
RequiredFieldFinal {
mandate: HashMap::new(),
non_mandate: HashMap::new(),
common: HashMap::new(),
common: HashMap::from([ (
"billing.address.first_name".to_string(),
RequiredFieldInfo {
required_field: "billing.address.first_name".to_string(),
display_name: "billing_first_name".to_string(),
field_type: enums::FieldType::UserBillingName,
value: None,
}
)]),
})]
)}
),
Expand All @@ -6466,7 +6474,15 @@ impl Default for super::settings::RequiredFields {
RequiredFieldFinal {
mandate: HashMap::new(),
non_mandate: HashMap::new(),
common: HashMap::new(),
common: HashMap::from([ (
"billing.address.first_name".to_string(),
RequiredFieldInfo {
required_field: "billing.address.first_name".to_string(),
display_name: "billing_first_name".to_string(),
field_type: enums::FieldType::UserBillingName,
value: None,
}
)]),
}
),
]),
Expand All @@ -6481,7 +6497,15 @@ impl Default for super::settings::RequiredFields {
RequiredFieldFinal {
mandate: HashMap::new(),
non_mandate: HashMap::new(),
common: HashMap::new(),
common: HashMap::from([ (
"billing.address.first_name".to_string(),
RequiredFieldInfo {
required_field: "billing.address.first_name".to_string(),
display_name: "billing_first_name".to_string(),
field_type: enums::FieldType::UserBillingName,
value: None,
}
)]),
}
),
]),
Expand Down
29 changes: 18 additions & 11 deletions crates/router/src/core/payment_methods/cards.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use api_models::{
admin::{self, PaymentMethodsEnabled},
enums::{self as api_enums},
payment_methods::{
BankAccountConnectorDetails, CardDetailsPaymentMethod, CardNetworkTypes,
BankAccountTokenData, CardDetailsPaymentMethod, CardNetworkTypes,
CustomerDefaultPaymentMethodResponse, MaskedBankDetails, PaymentExperienceTypes,
PaymentMethodsData, RequestPaymentMethodTypes, RequiredFieldInfo,
ResponsePaymentMethodIntermediate, ResponsePaymentMethodTypes,
Expand Down Expand Up @@ -2824,14 +2824,14 @@ pub async fn list_customer_payment_method(

enums::PaymentMethod::BankDebit => {
// Retrieve the pm_auth connector details so that it can be tokenized
let bank_account_connector_details = get_bank_account_connector_details(&pm, key)
let bank_account_token_data = get_bank_account_connector_details(&pm, key)
.await
.unwrap_or_else(|err| {
logger::error!(error=?err);
None
});
if let Some(connector_details) = bank_account_connector_details {
let token_data = PaymentTokenData::AuthBankDebit(connector_details);
if let Some(data) = bank_account_token_data {
let token_data = PaymentTokenData::AuthBankDebit(data);
(None, None, token_data)
} else {
continue;
Expand Down Expand Up @@ -3133,7 +3133,7 @@ async fn get_masked_bank_details(
async fn get_bank_account_connector_details(
pm: &payment_method::PaymentMethod,
key: &[u8],
) -> errors::RouterResult<Option<BankAccountConnectorDetails>> {
) -> errors::RouterResult<Option<BankAccountTokenData>> {
let payment_method_data =
decrypt::<serde_json::Value, masking::WithType>(pm.payment_method_data.clone(), key)
.await
Expand Down Expand Up @@ -3162,12 +3162,19 @@ async fn get_bank_account_connector_details(
.connector_details
.first()
.ok_or(errors::ApiErrorResponse::InternalServerError)?;
Ok(Some(BankAccountConnectorDetails {
connector: connector_details.connector.clone(),
account_id: connector_details.account_id.clone(),
mca_id: connector_details.mca_id.clone(),
access_token: connector_details.access_token.clone(),
}))

let pm_type = pm
.payment_method_type
.get_required_value("payment_method_type")
.attach_printable("PaymentMethodType not found")?;

let token_data = BankAccountTokenData {
payment_method_type: pm_type,
payment_method: pm.payment_method,
connector_details: connector_details.clone(),
};

Ok(Some(token_data))
}
},
None => Ok(None),
Expand Down
Loading
Loading