feat(payouts): extend routing capabilities to payout operation (#3531)

Co-authored-by: Kashif <mohammed.kashif@juspay.in>
This commit is contained in:
Kashif
2024-02-26 13:00:10 +05:30
committed by GitHub
parent c79226b9b2
commit 75c633fc7c
39 changed files with 1341 additions and 556 deletions

View File

@ -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

View File

@ -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,

View File

@ -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

View File

@ -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)]