mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-30 17:47:54 +08:00
feat(payouts): extend routing capabilities to payout operation (#3531)
Co-authored-by: Kashif <mohammed.kashif@juspay.in>
This commit is contained in:
@ -50,10 +50,6 @@ pub struct MerchantAccountCreate {
|
||||
/// The routing algorithm to be used for routing payouts to desired connectors
|
||||
#[cfg(feature = "payouts")]
|
||||
#[schema(value_type = Option<RoutingAlgorithm>,example = json!({"type": "single", "data": "wise"}))]
|
||||
#[serde(
|
||||
default,
|
||||
deserialize_with = "payout_routing_algorithm::deserialize_option"
|
||||
)]
|
||||
pub payout_routing_algorithm: Option<serde_json::Value>,
|
||||
|
||||
/// A boolean value to indicate if the merchant is a sub-merchant under a master or a parent merchant. By default, its value is false.
|
||||
@ -136,10 +132,6 @@ pub struct MerchantAccountUpdate {
|
||||
/// The routing algorithm to be used to process the incoming request from merchant to outgoing payment processor or payment method. The default is 'Custom'
|
||||
#[cfg(feature = "payouts")]
|
||||
#[schema(value_type = Option<RoutingAlgorithm>,example = json!({"type": "single", "data": "wise"}))]
|
||||
#[serde(
|
||||
default,
|
||||
deserialize_with = "payout_routing_algorithm::deserialize_option"
|
||||
)]
|
||||
pub payout_routing_algorithm: Option<serde_json::Value>,
|
||||
|
||||
/// A boolean value to indicate if the merchant is a sub-merchant under a master or a parent merchant. By default, its value is false.
|
||||
@ -228,10 +220,6 @@ pub struct MerchantAccountResponse {
|
||||
/// The routing algorithm to be used to process the incoming request from merchant to outgoing payment processor or payment method. The default is 'Custom'
|
||||
#[cfg(feature = "payouts")]
|
||||
#[schema(value_type = Option<RoutingAlgorithm>,example = json!({"type": "single", "data": "wise"}))]
|
||||
#[serde(
|
||||
default,
|
||||
deserialize_with = "payout_routing_algorithm::deserialize_option"
|
||||
)]
|
||||
pub payout_routing_algorithm: Option<serde_json::Value>,
|
||||
|
||||
/// A boolean value to indicate if the merchant is a sub-merchant under a master or a parent merchant. By default, its value is false.
|
||||
@ -322,124 +310,6 @@ pub struct MerchantDetails {
|
||||
/// The merchant's address details
|
||||
pub address: Option<AddressDetails>,
|
||||
}
|
||||
#[cfg(feature = "payouts")]
|
||||
pub mod payout_routing_algorithm {
|
||||
use std::{fmt, str::FromStr};
|
||||
|
||||
use serde::{
|
||||
de::{self, Visitor},
|
||||
Deserializer,
|
||||
};
|
||||
use serde_json::Map;
|
||||
|
||||
use super::PayoutRoutingAlgorithm;
|
||||
use crate::enums::PayoutConnectors;
|
||||
struct RoutingAlgorithmVisitor;
|
||||
struct OptionalRoutingAlgorithmVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for RoutingAlgorithmVisitor {
|
||||
type Value = serde_json::Value;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
formatter.write_str("routing algorithm")
|
||||
}
|
||||
|
||||
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
|
||||
where
|
||||
A: de::MapAccess<'de>,
|
||||
{
|
||||
let mut output = Map::new();
|
||||
let mut routing_data: String = "".to_string();
|
||||
let mut routing_type: String = "".to_string();
|
||||
|
||||
while let Some(key) = map.next_key()? {
|
||||
match key {
|
||||
"type" => {
|
||||
routing_type = map.next_value()?;
|
||||
output.insert(
|
||||
"type".to_string(),
|
||||
serde_json::Value::String(routing_type.to_owned()),
|
||||
);
|
||||
}
|
||||
"data" => {
|
||||
routing_data = map.next_value()?;
|
||||
output.insert(
|
||||
"data".to_string(),
|
||||
serde_json::Value::String(routing_data.to_owned()),
|
||||
);
|
||||
}
|
||||
f => {
|
||||
output.insert(f.to_string(), map.next_value()?);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match routing_type.as_ref() {
|
||||
"single" => {
|
||||
let routable_payout_connector = PayoutConnectors::from_str(&routing_data);
|
||||
let routable_conn = match routable_payout_connector {
|
||||
Ok(rpc) => Ok(rpc),
|
||||
Err(_) => Err(de::Error::custom(format!(
|
||||
"Unknown payout connector {routing_data}"
|
||||
))),
|
||||
}?;
|
||||
Ok(PayoutRoutingAlgorithm::Single(routable_conn))
|
||||
}
|
||||
u => Err(de::Error::custom(format!("Unknown routing algorithm {u}"))),
|
||||
}?;
|
||||
Ok(serde_json::Value::Object(output))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Visitor<'de> for OptionalRoutingAlgorithmVisitor {
|
||||
type Value = Option<serde_json::Value>;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
formatter.write_str("routing algorithm")
|
||||
}
|
||||
|
||||
fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
deserializer
|
||||
.deserialize_any(RoutingAlgorithmVisitor)
|
||||
.map(Some)
|
||||
}
|
||||
|
||||
fn visit_none<E>(self) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn visit_unit<E>(self) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn deserialize<'a, D>(deserializer: D) -> Result<serde_json::Value, D::Error>
|
||||
where
|
||||
D: Deserializer<'a>,
|
||||
{
|
||||
deserializer.deserialize_any(RoutingAlgorithmVisitor)
|
||||
}
|
||||
|
||||
pub(crate) fn deserialize_option<'a, D>(
|
||||
deserializer: D,
|
||||
) -> Result<Option<serde_json::Value>, D::Error>
|
||||
where
|
||||
D: Deserializer<'a>,
|
||||
{
|
||||
deserializer.deserialize_option(OptionalRoutingAlgorithmVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, ToSchema, Serialize, PartialEq)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct PrimaryBusinessDetails {
|
||||
@ -971,13 +841,6 @@ pub enum PayoutRoutingAlgorithm {
|
||||
Single(api_enums::PayoutConnectors),
|
||||
}
|
||||
|
||||
#[cfg(feature = "payouts")]
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[serde(tag = "type", content = "data", rename_all = "snake_case")]
|
||||
pub enum PayoutStraightThroughAlgorithm {
|
||||
Single(api_enums::PayoutConnectors),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, ToSchema, Default, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct BusinessProfileCreate {
|
||||
@ -1023,10 +886,6 @@ pub struct BusinessProfileCreate {
|
||||
/// The routing algorithm to be used to process the incoming request from merchant to outgoing payment processor or payment method. The default is 'Custom'
|
||||
#[cfg(feature = "payouts")]
|
||||
#[schema(value_type = Option<RoutingAlgorithm>,example = json!({"type": "single", "data": "wise"}))]
|
||||
#[serde(
|
||||
default,
|
||||
deserialize_with = "payout_routing_algorithm::deserialize_option"
|
||||
)]
|
||||
pub payout_routing_algorithm: Option<serde_json::Value>,
|
||||
|
||||
/// Verified applepay domains for a particular profile
|
||||
@ -1093,10 +952,6 @@ pub struct BusinessProfileResponse {
|
||||
/// The routing algorithm to be used to process the incoming request from merchant to outgoing payment processor or payment method. The default is 'Custom'
|
||||
#[cfg(feature = "payouts")]
|
||||
#[schema(value_type = Option<RoutingAlgorithm>,example = json!({"type": "single", "data": "wise"}))]
|
||||
#[serde(
|
||||
default,
|
||||
deserialize_with = "payout_routing_algorithm::deserialize_option"
|
||||
)]
|
||||
pub payout_routing_algorithm: Option<serde_json::Value>,
|
||||
|
||||
/// Verified applepay domains for a particular profile
|
||||
@ -1155,10 +1010,6 @@ pub struct BusinessProfileUpdate {
|
||||
/// The routing algorithm to be used to process the incoming request from merchant to outgoing payment processor or payment method. The default is 'Custom'
|
||||
#[cfg(feature = "payouts")]
|
||||
#[schema(value_type = Option<RoutingAlgorithm>,example = json!({"type": "single", "data": "wise"}))]
|
||||
#[serde(
|
||||
default,
|
||||
deserialize_with = "payout_routing_algorithm::deserialize_option"
|
||||
)]
|
||||
pub payout_routing_algorithm: Option<serde_json::Value>,
|
||||
|
||||
/// Verified applepay domains for a particular profile
|
||||
|
||||
@ -181,6 +181,28 @@ impl From<PayoutConnectors> for RoutableConnectors {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "payouts")]
|
||||
impl From<PayoutConnectors> for Connector {
|
||||
fn from(value: PayoutConnectors) -> Self {
|
||||
match value {
|
||||
PayoutConnectors::Adyen => Self::Adyen,
|
||||
PayoutConnectors::Wise => Self::Wise,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "payouts")]
|
||||
impl TryFrom<Connector> for PayoutConnectors {
|
||||
type Error = String;
|
||||
fn try_from(value: Connector) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
Connector::Adyen => Ok(Self::Adyen),
|
||||
Connector::Wise => Ok(Self::Wise),
|
||||
_ => Err(format!("Invalid payout connector {}", value)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "frm")]
|
||||
#[derive(
|
||||
Clone,
|
||||
|
||||
@ -7,7 +7,7 @@ use masking::Secret;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use utoipa::ToSchema;
|
||||
|
||||
use crate::{admin, enums as api_enums, payments};
|
||||
use crate::{enums as api_enums, payments};
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Clone, ToSchema)]
|
||||
pub enum PayoutRequest {
|
||||
@ -48,10 +48,6 @@ pub struct PayoutCreateRequest {
|
||||
"type": "single",
|
||||
"data": "adyen"
|
||||
}))]
|
||||
#[serde(
|
||||
default,
|
||||
deserialize_with = "admin::payout_routing_algorithm::deserialize_option"
|
||||
)]
|
||||
pub routing: Option<serde_json::Value>,
|
||||
|
||||
/// This allows the merchant to manually select a connector with which the payout can go through
|
||||
|
||||
@ -12,7 +12,7 @@ pub use euclid::{
|
||||
use serde::{Deserialize, Serialize};
|
||||
use utoipa::ToSchema;
|
||||
|
||||
use crate::enums::{self, RoutableConnectors};
|
||||
use crate::enums::{self, RoutableConnectors, TransactionType};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
|
||||
#[serde(tag = "type", content = "data", rename_all = "snake_case")]
|
||||
@ -85,6 +85,7 @@ pub struct MerchantRoutingAlgorithm {
|
||||
pub algorithm: RoutingAlgorithm,
|
||||
pub created_at: i64,
|
||||
pub modified_at: i64,
|
||||
pub algorithm_for: TransactionType,
|
||||
}
|
||||
|
||||
impl EuclidDirFilter for ConnectorSelection {
|
||||
@ -538,6 +539,7 @@ pub struct RoutingDictionaryRecord {
|
||||
pub description: String,
|
||||
pub created_at: i64,
|
||||
pub modified_at: i64,
|
||||
pub algorithm_for: Option<TransactionType>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)]
|
||||
|
||||
Reference in New Issue
Block a user