feat: add payout service (#1665)

Co-authored-by: Kashif <mohammed.kashif@juspay.in>
Co-authored-by: Manoj Ghorela <manoj.ghorela@juspay.in>
Co-authored-by: Manoj Ghorela <118727120+manoj-juspay@users.noreply.github.com>
This commit is contained in:
Kashif
2023-07-19 23:32:05 +05:30
committed by GitHub
parent e0f4507b10
commit 763e2df3bd
86 changed files with 9326 additions and 221 deletions

View File

@ -35,6 +35,15 @@ pub struct MerchantAccountCreate {
#[schema(value_type = Option<Object>,example = json!({"type": "single", "data": "stripe"}))]
pub routing_algorithm: Option<serde_json::Value>,
/// 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.
#[schema(default = false, example = false)]
pub sub_merchants_enabled: Option<bool>,
@ -113,6 +122,15 @@ pub struct MerchantAccountUpdate {
#[schema(value_type = Option<Object>,example = json!({"type": "single", "data": "stripe"}))]
pub routing_algorithm: Option<serde_json::Value>,
/// 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.
#[schema(default = false, example = false)]
pub sub_merchants_enabled: Option<bool>,
@ -194,6 +212,15 @@ pub struct MerchantAccountResponse {
#[schema(value_type = Option<RoutingAlgorithm>, max_length = 255, example = "custom")]
pub routing_algorithm: Option<serde_json::Value>,
/// 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.
#[schema(default = false, example = false)]
pub sub_merchants_enabled: Option<bool>,
@ -273,6 +300,117 @@ 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 = serde_json::Value::Object(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["type"] = serde_json::Value::String(routing_type.to_owned());
}
"data" => {
routing_data = map.next_value()?;
output["data"] = serde_json::Value::String(routing_data.to_owned());
}
f => {
output[f] = 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(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, Serialize)]
#[serde(tag = "type", content = "data", rename_all = "snake_case")]
@ -787,3 +925,17 @@ pub struct MerchantConnectorDetails {
#[schema(value_type = Option<Object>,max_length = 255,example = json!({ "city": "NY", "unit": "245" }))]
pub metadata: Option<pii::SecretSerdeValue>,
}
#[cfg(feature = "payouts")]
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(tag = "type", content = "data", rename_all = "snake_case")]
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),
}