feat(payment_methods): Added value Field in required Field for Pre-filling (#1827)

Co-authored-by: Sarthak Soni <sarthak.soni@juspay.in>
Co-authored-by: AkshayaFoiger <akshaya.shankar@juspay.in>
Co-authored-by: Chethan Rao <70657455+Chethan-rao@users.noreply.github.com>
This commit is contained in:
Sarthak Soni
2023-08-01 18:28:26 +05:30
committed by GitHub
parent 801946f29f
commit e047a11ded
6 changed files with 527 additions and 709 deletions

View File

@ -1,4 +1,4 @@
use std::collections::HashSet;
use std::collections::HashMap;
use cards::CardNumber;
use common_utils::{crypto::OptionalEncryptableName, pii};
@ -220,7 +220,7 @@ pub struct ResponsePaymentMethodTypes {
pub bank_transfers: Option<BankTransferTypes>,
/// Required fields for the payment_method_type.
pub required_fields: Option<HashSet<RequiredFieldInfo>>,
pub required_fields: Option<HashMap<String, RequiredFieldInfo>>,
}
/// Required fields info used while listing the payment_method_data
@ -235,6 +235,8 @@ pub struct RequiredFieldInfo {
/// Possible field type of required field
#[schema(value_type = FieldType)]
pub field_type: api_enums::FieldType,
pub value: Option<String>,
}
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize, ToSchema)]

File diff suppressed because it is too large Load Diff

View File

@ -276,7 +276,14 @@ pub struct PaymentMethodType(pub HashMap<enums::PaymentMethodType, ConnectorFiel
#[derive(Debug, Deserialize, Clone)]
pub struct ConnectorFields {
pub fields: HashMap<enums::Connector, Vec<RequiredFieldInfo>>,
pub fields: HashMap<enums::Connector, RequiredFieldFinal>,
}
#[derive(Debug, Deserialize, Clone)]
pub struct RequiredFieldFinal {
pub mandate: HashMap<String, RequiredFieldInfo>,
pub non_mandate: HashMap<String, RequiredFieldInfo>,
pub common: HashMap<String, RequiredFieldInfo>,
}
fn string_set_deser<'a, D>(

View File

@ -48,7 +48,7 @@ use crate::{
api::{self, PaymentMethodCreateExt},
domain::{self, types::decrypt},
storage::{self, enums},
transformers::ForeignInto,
transformers::{ForeignFrom, ForeignInto},
},
utils::{self, ConnectorResponseExt, OptionExt},
};
@ -727,6 +727,13 @@ pub fn get_banks(
}
}
fn get_val(str: String, val: &serde_json::Value) -> Option<String> {
str.split('.')
.fold(Some(val), |acc, x| acc.and_then(|v| v.get(x)))
.and_then(|v| v.as_str())
.map(|s| s.to_string())
}
pub async fn list_payment_methods(
state: &routes::AppState,
merchant_account: domain::MerchantAccount,
@ -743,7 +750,7 @@ pub async fn list_payment_methods(
)
.await?;
let address = payment_intent
let shipping_address = payment_intent
.as_ref()
.async_map(|pi| async {
helpers::get_address_by_id(db, pi.shipping_address_id.clone(), &key_store).await
@ -752,6 +759,34 @@ pub async fn list_payment_methods(
.transpose()?
.flatten();
let billing_address = payment_intent
.as_ref()
.async_map(|pi| async {
helpers::get_address_by_id(db, pi.billing_address_id.clone(), &key_store).await
})
.await
.transpose()?
.flatten();
let customer = payment_intent
.as_ref()
.async_and_then(|pi| async {
pi.customer_id
.as_ref()
.async_and_then(|cust| async {
db.find_customer_by_customer_id_merchant_id(
cust.as_str(),
&pi.merchant_id,
&key_store,
)
.await
.to_not_found_response(errors::ApiErrorResponse::CustomerNotFound)
.ok()
})
.await
})
.await;
let payment_attempt = payment_intent
.as_ref()
.async_map(|pi| async {
@ -795,7 +830,7 @@ pub async fn list_payment_methods(
&mut response,
payment_intent.as_ref(),
payment_attempt.as_ref(),
address.as_ref(),
shipping_address.as_ref(),
mca.connector_name,
pm_config_mapping,
&state.conf.mandates.supported_payment_methods,
@ -803,6 +838,13 @@ pub async fn list_payment_methods(
.await?;
}
let req = api_models::payments::PaymentsRequest::foreign_from((
payment_attempt.as_ref(),
shipping_address.as_ref(),
billing_address.as_ref(),
customer.as_ref(),
));
let req_val = serde_json::to_value(req).ok();
logger::debug!(filtered_payment_methods=?response);
let mut payment_experiences_consolidated_hm: HashMap<
@ -826,7 +868,7 @@ pub async fn list_payment_methods(
let mut required_fields_hm = HashMap::<
api_enums::PaymentMethod,
HashMap<api_enums::PaymentMethodType, HashSet<RequiredFieldInfo>>,
HashMap<api_enums::PaymentMethodType, HashMap<String, RequiredFieldInfo>>,
>::new();
for element in response.clone() {
@ -853,15 +895,34 @@ pub async fn list_payment_methods(
required_fields_hm_for_each_connector
.fields
.get(&connector_variant)
.map(|required_fields_vec| {
// If payment_method_type already exist in required_fields_hm, extend the required_fields hs to existing hs.
let required_fields_hs =
HashSet::from_iter(required_fields_vec.iter().cloned());
.map(|required_fields_final| {
let mut required_fields_hs = required_fields_final.common.clone();
if let Some(pa) = payment_attempt.as_ref() {
if let Some(_mandate) = &pa.mandate_details {
required_fields_hs
.extend(required_fields_final.mandate.clone());
} else {
required_fields_hs
.extend(required_fields_final.non_mandate.clone());
}
}
{
for (key, val) in &mut required_fields_hs {
let temp = req_val
.as_ref()
.and_then(|r| get_val(key.to_owned(), r));
if let Some(s) = temp {
val.value = Some(s)
};
}
}
let existing_req_fields_hs = required_fields_hm
.get_mut(&payment_method)
.and_then(|inner_hm| inner_hm.get_mut(&payment_method_type));
// If payment_method_type already exist in required_fields_hm, extend the required_fields hs to existing hs.
if let Some(inner_hs) = existing_req_fields_hs {
inner_hs.extend(required_fields_hs);
} else {

View File

@ -1,5 +1,5 @@
use api_models::enums as api_enums;
use common_utils::{crypto::Encryptable, ext_traits::ValueExt};
use common_utils::{crypto::Encryptable, ext_traits::ValueExt, pii};
use diesel_models::enums as storage_enums;
use error_stack::ResultExt;
use masking::{ExposeInterface, PeekInterface};
@ -573,3 +573,34 @@ impl ForeignFrom<api_models::enums::PayoutType> for api_enums::PaymentMethod {
}
}
}
impl
ForeignFrom<(
Option<&storage::PaymentAttempt>,
Option<&domain::Address>,
Option<&domain::Address>,
Option<&domain::Customer>,
)> for api_models::payments::PaymentsRequest
{
fn foreign_from(
value: (
Option<&storage::PaymentAttempt>,
Option<&domain::Address>,
Option<&domain::Address>,
Option<&domain::Customer>,
),
) -> Self {
let (payment_attempt, shipping, billing, customer) = value;
Self {
currency: payment_attempt.map(|pa| pa.currency.unwrap_or_default()),
shipping: shipping.map(api_types::Address::from),
billing: billing.map(api_types::Address::from),
amount: payment_attempt.map(|pa| api_types::Amount::from(pa.amount)),
email: customer
.and_then(|cust| cust.email.as_ref().map(|em| pii::Email::from(em.clone()))),
phone: customer.and_then(|cust| cust.phone.as_ref().map(|p| p.clone().into_inner())),
name: customer.and_then(|cust| cust.name.as_ref().map(|n| n.clone().into_inner())),
..Self::default()
}
}
}

View File

@ -9529,6 +9529,10 @@
},
"field_type": {
"$ref": "#/components/schemas/FieldType"
},
"value": {
"type": "string",
"nullable": true
}
}
},