mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 17:19:15 +08:00
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:
@ -1,4 +1,4 @@
|
|||||||
use std::collections::HashSet;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use cards::CardNumber;
|
use cards::CardNumber;
|
||||||
use common_utils::{crypto::OptionalEncryptableName, pii};
|
use common_utils::{crypto::OptionalEncryptableName, pii};
|
||||||
@ -220,7 +220,7 @@ pub struct ResponsePaymentMethodTypes {
|
|||||||
pub bank_transfers: Option<BankTransferTypes>,
|
pub bank_transfers: Option<BankTransferTypes>,
|
||||||
|
|
||||||
/// Required fields for the payment_method_type.
|
/// 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
|
/// Required fields info used while listing the payment_method_data
|
||||||
@ -235,6 +235,8 @@ pub struct RequiredFieldInfo {
|
|||||||
/// Possible field type of required field
|
/// Possible field type of required field
|
||||||
#[schema(value_type = FieldType)]
|
#[schema(value_type = FieldType)]
|
||||||
pub field_type: api_enums::FieldType,
|
pub field_type: api_enums::FieldType,
|
||||||
|
|
||||||
|
pub value: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize, ToSchema)]
|
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize, ToSchema)]
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -276,7 +276,14 @@ pub struct PaymentMethodType(pub HashMap<enums::PaymentMethodType, ConnectorFiel
|
|||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
pub struct ConnectorFields {
|
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>(
|
fn string_set_deser<'a, D>(
|
||||||
|
|||||||
@ -48,7 +48,7 @@ use crate::{
|
|||||||
api::{self, PaymentMethodCreateExt},
|
api::{self, PaymentMethodCreateExt},
|
||||||
domain::{self, types::decrypt},
|
domain::{self, types::decrypt},
|
||||||
storage::{self, enums},
|
storage::{self, enums},
|
||||||
transformers::ForeignInto,
|
transformers::{ForeignFrom, ForeignInto},
|
||||||
},
|
},
|
||||||
utils::{self, ConnectorResponseExt, OptionExt},
|
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(
|
pub async fn list_payment_methods(
|
||||||
state: &routes::AppState,
|
state: &routes::AppState,
|
||||||
merchant_account: domain::MerchantAccount,
|
merchant_account: domain::MerchantAccount,
|
||||||
@ -743,7 +750,7 @@ pub async fn list_payment_methods(
|
|||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let address = payment_intent
|
let shipping_address = payment_intent
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.async_map(|pi| async {
|
.async_map(|pi| async {
|
||||||
helpers::get_address_by_id(db, pi.shipping_address_id.clone(), &key_store).await
|
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()?
|
.transpose()?
|
||||||
.flatten();
|
.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
|
let payment_attempt = payment_intent
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.async_map(|pi| async {
|
.async_map(|pi| async {
|
||||||
@ -795,7 +830,7 @@ pub async fn list_payment_methods(
|
|||||||
&mut response,
|
&mut response,
|
||||||
payment_intent.as_ref(),
|
payment_intent.as_ref(),
|
||||||
payment_attempt.as_ref(),
|
payment_attempt.as_ref(),
|
||||||
address.as_ref(),
|
shipping_address.as_ref(),
|
||||||
mca.connector_name,
|
mca.connector_name,
|
||||||
pm_config_mapping,
|
pm_config_mapping,
|
||||||
&state.conf.mandates.supported_payment_methods,
|
&state.conf.mandates.supported_payment_methods,
|
||||||
@ -803,6 +838,13 @@ pub async fn list_payment_methods(
|
|||||||
.await?;
|
.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);
|
logger::debug!(filtered_payment_methods=?response);
|
||||||
|
|
||||||
let mut payment_experiences_consolidated_hm: HashMap<
|
let mut payment_experiences_consolidated_hm: HashMap<
|
||||||
@ -826,7 +868,7 @@ pub async fn list_payment_methods(
|
|||||||
|
|
||||||
let mut required_fields_hm = HashMap::<
|
let mut required_fields_hm = HashMap::<
|
||||||
api_enums::PaymentMethod,
|
api_enums::PaymentMethod,
|
||||||
HashMap<api_enums::PaymentMethodType, HashSet<RequiredFieldInfo>>,
|
HashMap<api_enums::PaymentMethodType, HashMap<String, RequiredFieldInfo>>,
|
||||||
>::new();
|
>::new();
|
||||||
|
|
||||||
for element in response.clone() {
|
for element in response.clone() {
|
||||||
@ -853,15 +895,34 @@ pub async fn list_payment_methods(
|
|||||||
required_fields_hm_for_each_connector
|
required_fields_hm_for_each_connector
|
||||||
.fields
|
.fields
|
||||||
.get(&connector_variant)
|
.get(&connector_variant)
|
||||||
.map(|required_fields_vec| {
|
.map(|required_fields_final| {
|
||||||
// If payment_method_type already exist in required_fields_hm, extend the required_fields hs to existing hs.
|
let mut required_fields_hs = required_fields_final.common.clone();
|
||||||
let required_fields_hs =
|
if let Some(pa) = payment_attempt.as_ref() {
|
||||||
HashSet::from_iter(required_fields_vec.iter().cloned());
|
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
|
let existing_req_fields_hs = required_fields_hm
|
||||||
.get_mut(&payment_method)
|
.get_mut(&payment_method)
|
||||||
.and_then(|inner_hm| inner_hm.get_mut(&payment_method_type));
|
.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 {
|
if let Some(inner_hs) = existing_req_fields_hs {
|
||||||
inner_hs.extend(required_fields_hs);
|
inner_hs.extend(required_fields_hs);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
use api_models::enums as api_enums;
|
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 diesel_models::enums as storage_enums;
|
||||||
use error_stack::ResultExt;
|
use error_stack::ResultExt;
|
||||||
use masking::{ExposeInterface, PeekInterface};
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -9529,6 +9529,10 @@
|
|||||||
},
|
},
|
||||||
"field_type": {
|
"field_type": {
|
||||||
"$ref": "#/components/schemas/FieldType"
|
"$ref": "#/components/schemas/FieldType"
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"type": "string",
|
||||||
|
"nullable": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user