mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-11-01 11:06:50 +08:00
feat(payout): add dynamic fields for payout links (#5764)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
@ -160,12 +160,13 @@ if (!isTestMode && !isFramed) {
|
||||
theme: payoutDetails.theme,
|
||||
collectorName: payoutDetails.merchant_name,
|
||||
logo: payoutDetails.logo,
|
||||
enabledPaymentMethods: payoutDetails.enabled_payment_methods,
|
||||
enabledPaymentMethods: payoutDetails.enabled_payment_methods_with_required_fields,
|
||||
returnUrl: payoutDetails.return_url,
|
||||
sessionExpiry,
|
||||
amount: payoutDetails.amount,
|
||||
currency: payoutDetails.currency,
|
||||
flow: "PayoutLinkInitiate",
|
||||
formLayout: payoutDetails.form_layout,
|
||||
};
|
||||
payoutWidget = widgets.create("paymentMethodCollect", payoutOptions);
|
||||
|
||||
|
||||
@ -121,10 +121,10 @@ function renderStatusDetails(payoutDetails) {
|
||||
"{{i18n_ref_id_text}}": payoutDetails.payout_id,
|
||||
};
|
||||
if (typeof payoutDetails.error_code === "string") {
|
||||
resourceInfo["{{i18n_error_code_text}}"] = payoutDetails.error_code;
|
||||
// resourceInfo["{{i18n_error_code_text}}"] = payoutDetails.error_code;
|
||||
}
|
||||
if (typeof payoutDetails.error_message === "string") {
|
||||
resourceInfo["{{i18n_error_message}}"] = payoutDetails.error_message;
|
||||
// resourceInfo["{{i18n_error_message}}"] = payoutDetails.error_message;
|
||||
}
|
||||
var resourceNode = document.createElement("div");
|
||||
resourceNode.id = "resource-info-container";
|
||||
|
||||
@ -80,7 +80,7 @@ body {
|
||||
#resource-info-container {
|
||||
width: 100%;
|
||||
border-top: 1px solid rgb(231, 234, 241);
|
||||
padding: 20px 0;
|
||||
padding: 20px 10px;
|
||||
}
|
||||
#resource-info {
|
||||
display: flex;
|
||||
|
||||
@ -21,7 +21,7 @@ use crate::{
|
||||
errors,
|
||||
routes::{app::StorageInterface, SessionState},
|
||||
services,
|
||||
types::domain,
|
||||
types::{api, domain, transformers::ForeignFrom},
|
||||
};
|
||||
|
||||
#[cfg(all(feature = "v2", feature = "customer_v2"))]
|
||||
@ -156,9 +156,31 @@ pub async fn initiate_payout_link(
|
||||
.attach_printable_lazy(|| {
|
||||
format!("customer [{}] not found", payout_link.primary_reference)
|
||||
})?;
|
||||
let address = payout
|
||||
.address_id
|
||||
.as_ref()
|
||||
.async_map(|address_id| async {
|
||||
db.find_address_by_address_id(&(&state).into(), address_id, &key_store)
|
||||
.await
|
||||
})
|
||||
.await
|
||||
.transpose()
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable_lazy(|| {
|
||||
format!(
|
||||
"Failed while fetching address [id - {:?}] for payout [id - {}]",
|
||||
payout.address_id, payout.payout_id
|
||||
)
|
||||
})?;
|
||||
|
||||
let enabled_payout_methods =
|
||||
filter_payout_methods(&state, &merchant_account, &key_store, &payout).await?;
|
||||
let enabled_payout_methods = filter_payout_methods(
|
||||
&state,
|
||||
&merchant_account,
|
||||
&key_store,
|
||||
&payout,
|
||||
address.as_ref(),
|
||||
)
|
||||
.await?;
|
||||
// Fetch default enabled_payout_methods
|
||||
let mut default_enabled_payout_methods: Vec<link_utils::EnabledPaymentMethod> = vec![];
|
||||
for (payment_method, payment_method_types) in
|
||||
@ -188,6 +210,16 @@ pub async fn initiate_payout_link(
|
||||
_ => Ordering::Equal,
|
||||
});
|
||||
|
||||
let required_field_override = api::RequiredFieldsOverrideRequest {
|
||||
billing: address.as_ref().map(From::from),
|
||||
};
|
||||
|
||||
let enabled_payment_methods_with_required_fields = ForeignFrom::foreign_from((
|
||||
&state.conf.payouts.required_fields,
|
||||
enabled_payment_methods.clone(),
|
||||
required_field_override,
|
||||
));
|
||||
|
||||
let js_data = payouts::PayoutLinkDetails {
|
||||
publishable_key: masking::Secret::new(merchant_account.publishable_key),
|
||||
client_secret: link_data.client_secret.clone(),
|
||||
@ -204,9 +236,11 @@ pub async fn initiate_payout_link(
|
||||
.attach_printable("Failed to parse payout status link's return URL")?,
|
||||
ui_config: ui_config_data,
|
||||
enabled_payment_methods,
|
||||
enabled_payment_methods_with_required_fields,
|
||||
amount,
|
||||
currency: payout.destination_currency,
|
||||
locale: locale.clone(),
|
||||
form_layout: link_data.form_layout,
|
||||
test_mode: link_data.test_mode.unwrap_or(false),
|
||||
};
|
||||
|
||||
@ -287,6 +321,7 @@ pub async fn filter_payout_methods(
|
||||
merchant_account: &domain::MerchantAccount,
|
||||
key_store: &domain::MerchantKeyStore,
|
||||
payout: &hyperswitch_domain_models::payouts::payouts::Payouts,
|
||||
address: Option<&domain::Address>,
|
||||
) -> errors::RouterResult<Vec<link_utils::EnabledPaymentMethod>> {
|
||||
use masking::ExposeInterface;
|
||||
|
||||
@ -308,22 +343,6 @@ pub async fn filter_payout_methods(
|
||||
&payout.profile_id,
|
||||
common_enums::ConnectorType::PayoutProcessor,
|
||||
);
|
||||
let address = payout
|
||||
.address_id
|
||||
.as_ref()
|
||||
.async_map(|address_id| async {
|
||||
db.find_address_by_address_id(key_manager_state, address_id, key_store)
|
||||
.await
|
||||
})
|
||||
.await
|
||||
.transpose()
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable_lazy(|| {
|
||||
format!(
|
||||
"Failed while fetching address [id - {:?}] for payout [id - {}]",
|
||||
payout.address_id, payout.payout_id
|
||||
)
|
||||
})?;
|
||||
|
||||
let mut response: Vec<link_utils::EnabledPaymentMethod> = vec![];
|
||||
let mut payment_method_list_hm: HashMap<
|
||||
|
||||
@ -11,11 +11,9 @@ use api_models::{self, enums as api_enums, payouts::PayoutLinkResponse};
|
||||
use common_enums::PayoutRetryType;
|
||||
use common_utils::{
|
||||
consts,
|
||||
crypto::Encryptable,
|
||||
ext_traits::{AsyncExt, ValueExt},
|
||||
id_type::CustomerId,
|
||||
link_utils::{GenericLinkStatus, GenericLinkUiConfig, PayoutLinkData, PayoutLinkStatus},
|
||||
pii,
|
||||
types::MinorUnit,
|
||||
};
|
||||
use diesel_models::{
|
||||
@ -2142,29 +2140,7 @@ pub async fn response_handler(
|
||||
let billing_address = payout_data.billing_address.to_owned();
|
||||
let customer_details = payout_data.customer_details.to_owned();
|
||||
let customer_id = payouts.customer_id;
|
||||
|
||||
let address = billing_address.as_ref().map(|a| {
|
||||
let phone_details = payment_api_types::PhoneDetails {
|
||||
number: a.phone_number.to_owned().map(Encryptable::into_inner),
|
||||
country_code: a.country_code.to_owned(),
|
||||
};
|
||||
let address_details = payment_api_types::AddressDetails {
|
||||
city: a.city.to_owned(),
|
||||
country: a.country.to_owned(),
|
||||
line1: a.line1.to_owned().map(Encryptable::into_inner),
|
||||
line2: a.line2.to_owned().map(Encryptable::into_inner),
|
||||
line3: a.line3.to_owned().map(Encryptable::into_inner),
|
||||
zip: a.zip.to_owned().map(Encryptable::into_inner),
|
||||
first_name: a.first_name.to_owned().map(Encryptable::into_inner),
|
||||
last_name: a.last_name.to_owned().map(Encryptable::into_inner),
|
||||
state: a.state.to_owned().map(Encryptable::into_inner),
|
||||
};
|
||||
api::payments::Address {
|
||||
phone: Some(phone_details),
|
||||
address: Some(address_details),
|
||||
email: a.email.to_owned().map(pii::Email::from),
|
||||
}
|
||||
});
|
||||
let billing = billing_address.as_ref().map(From::from);
|
||||
|
||||
let response = api::PayoutCreateResponse {
|
||||
payout_id: payouts.payout_id.to_owned(),
|
||||
@ -2173,7 +2149,7 @@ pub async fn response_handler(
|
||||
currency: payouts.destination_currency.to_owned(),
|
||||
connector: payout_attempt.connector.to_owned(),
|
||||
payout_type: payouts.payout_type.to_owned(),
|
||||
billing: address,
|
||||
billing,
|
||||
auto_fulfill: payouts.auto_fulfill,
|
||||
customer_id,
|
||||
email: customer_details.as_ref().and_then(|c| c.email.clone()),
|
||||
@ -2746,6 +2722,14 @@ pub async fn create_payout_link(
|
||||
.and_then(|config| config.payout_link_id.clone()),
|
||||
"payout_link",
|
||||
)?;
|
||||
let form_layout = payout_link_config_req
|
||||
.as_ref()
|
||||
.and_then(|config| config.form_layout.to_owned())
|
||||
.or_else(|| {
|
||||
profile_config
|
||||
.as_ref()
|
||||
.and_then(|config| config.form_layout.to_owned())
|
||||
});
|
||||
|
||||
let data = PayoutLinkData {
|
||||
payout_link_id: payout_link_id.clone(),
|
||||
@ -2759,6 +2743,7 @@ pub async fn create_payout_link(
|
||||
amount: MinorUnit::from(*amount),
|
||||
currency: *currency,
|
||||
allowed_domains,
|
||||
form_layout,
|
||||
test_mode: test_mode_in_config,
|
||||
};
|
||||
|
||||
|
||||
@ -1,3 +1,7 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use common_utils::link_utils::EnabledPaymentMethod;
|
||||
|
||||
#[cfg(all(
|
||||
any(feature = "v1", feature = "v2"),
|
||||
not(feature = "customer_v2"),
|
||||
@ -5,7 +9,11 @@
|
||||
))]
|
||||
use crate::types::transformers::ForeignInto;
|
||||
#[cfg(feature = "olap")]
|
||||
use crate::types::{api, domain, storage, transformers::ForeignFrom};
|
||||
use crate::types::{domain, storage};
|
||||
use crate::{
|
||||
settings::PayoutRequiredFields,
|
||||
types::{api, transformers::ForeignFrom},
|
||||
};
|
||||
|
||||
#[cfg(all(feature = "v2", feature = "customer_v2", feature = "olap"))]
|
||||
impl
|
||||
@ -103,3 +111,72 @@ impl
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl
|
||||
ForeignFrom<(
|
||||
&PayoutRequiredFields,
|
||||
Vec<EnabledPaymentMethod>,
|
||||
api::RequiredFieldsOverrideRequest,
|
||||
)> for Vec<api::PayoutEnabledPaymentMethodsInfo>
|
||||
{
|
||||
fn foreign_from(
|
||||
(payout_required_fields, enabled_payout_methods, value_overrides): (
|
||||
&PayoutRequiredFields,
|
||||
Vec<EnabledPaymentMethod>,
|
||||
api::RequiredFieldsOverrideRequest,
|
||||
),
|
||||
) -> Self {
|
||||
let value_overrides = value_overrides.flat_struct();
|
||||
|
||||
enabled_payout_methods
|
||||
.into_iter()
|
||||
.map(|enabled_payout_method| {
|
||||
let payment_method = enabled_payout_method.payment_method;
|
||||
let payment_method_types_info = enabled_payout_method
|
||||
.payment_method_types
|
||||
.into_iter()
|
||||
.filter_map(|pmt| {
|
||||
payout_required_fields
|
||||
.0
|
||||
.get(&payment_method)
|
||||
.and_then(|pmt_info| {
|
||||
pmt_info.0.get(&pmt).map(|connector_fields| {
|
||||
let mut required_fields = HashMap::new();
|
||||
|
||||
for required_field_final in connector_fields.fields.values() {
|
||||
required_fields.extend(required_field_final.common.clone());
|
||||
}
|
||||
|
||||
for (key, value) in &value_overrides {
|
||||
required_fields.entry(key.to_string()).and_modify(
|
||||
|required_field| {
|
||||
required_field.value =
|
||||
Some(masking::Secret::new(value.to_string()));
|
||||
},
|
||||
);
|
||||
}
|
||||
api::PaymentMethodTypeInfo {
|
||||
payment_method_type: pmt,
|
||||
required_fields: if required_fields.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(required_fields)
|
||||
},
|
||||
}
|
||||
})
|
||||
})
|
||||
.or(Some(api::PaymentMethodTypeInfo {
|
||||
payment_method_type: pmt,
|
||||
required_fields: None,
|
||||
}))
|
||||
})
|
||||
.collect();
|
||||
|
||||
api::PayoutEnabledPaymentMethodsInfo {
|
||||
payment_method,
|
||||
payment_method_types_info,
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user