Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 23 commits into from
Feb 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
42fbf3d
refactor(payouts): replace payout routing types with existing types
kashif-m Jan 29, 2024
528168d
feat(payouts): extend routing capabilities to payout operations
kashif-m Feb 1, 2024
02735c7
fix: cargo hack
kashif-m Feb 2, 2024
9e42268
feat(payouts): provision for toggling routing rules for payout operat…
kashif-m Feb 2, 2024
6c616bd
refactor(payouts): update connector_data formation for fulfill, cance…
kashif-m Feb 5, 2024
7ac92b4
refactor(payouts): clippy and fmt
kashif-m Feb 5, 2024
9d05fd6
refactor(payouts): persist routing info in payout_attempt
kashif-m Feb 5, 2024
f57f5c0
fix(payouts): cargo hack
kashif-m Feb 7, 2024
4d145bf
refactor(payouts): cargo hack again
kashif-m Feb 7, 2024
d64b65c
Merge remote-tracking branch 'origin/main' into payout_routing
kashif-m Feb 8, 2024
dea2d6c
fix(payouts): cargo clippy
kashif-m Feb 8, 2024
0a31336
Merge remote-tracking branch 'origin/main' into payout_routing
kashif-m Feb 14, 2024
bc23048
Merge remote-tracking branch 'origin/main' into backup-po-routing
kashif-m Feb 15, 2024
9e716a3
refactor(payouts): separate endpoints for routing config creation and…
kashif-m Feb 15, 2024
7de6abd
refactor(payouts): cargo hack
kashif-m Feb 16, 2024
b1ff72c
fix(payouts): update down db query
kashif-m Feb 16, 2024
f26df3d
Merge remote-tracking branch 'origin/main' into payout_routing
kashif-m Feb 20, 2024
92e0f3c
refactor(routing): add default fallback configs for payout operations
kashif-m Feb 21, 2024
3974698
chore: re-generate open API spec
kashif-m Feb 21, 2024
895f718
Merge remote-tracking branch 'origin/main' into payout_routing
kashif-m Feb 21, 2024
3153875
chore(openapi): re-generate API reference
kashif-m Feb 22, 2024
a423e97
refactor: clippy fixes
kashif-m Feb 22, 2024
82a2202
refactor: cargo hack
kashif-m Feb 22, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/validate-openapi-spec.yml
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,6 @@ jobs:
shell: bash
run: |
if ! git diff --quiet --exit-code -- openapi/openapi_spec.json ; then
echo '::error::The OpenAPI spec file is not up-to-date. Please re-generate the OpenAPI spec file using `cargo run --features openapi -- generate-openapi-spec` and commit it.'
echo '::error::The OpenAPI spec file is not up-to-date. Please re-generate the OpenAPI spec file using `cargo run -p openapi` and commit it.'
exit 1
fi
149 changes: 0 additions & 149 deletions crates/api_models/src/admin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
22 changes: 22 additions & 0 deletions crates/api_models/src/enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
6 changes: 1 addition & 5 deletions crates/api_models/src/payouts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand Down
4 changes: 3 additions & 1 deletion crates/api_models/src/routing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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)]
Expand Down
1 change: 1 addition & 0 deletions crates/common_enums/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ license.workspace = true
[features]
dummy_connector = []
openapi = []
payouts = []

[dependencies]
diesel = { version = "2.1.0", features = ["postgres"] }
Expand Down
23 changes: 23 additions & 0 deletions crates/common_enums/src/enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2168,6 +2168,29 @@ pub enum ConnectorStatus {
Active,
}

#[derive(
Clone,
Copy,
Debug,
Eq,
PartialEq,
strum::Display,
strum::EnumString,
serde::Deserialize,
serde::Serialize,
ToSchema,
Default,
)]
#[router_derive::diesel_enum(storage_type = "db_enum")]
#[strum(serialize_all = "snake_case")]
#[serde(rename_all = "snake_case")]
pub enum TransactionType {
#[default]
Payment,
#[cfg(feature = "payouts")]
Payout,
}

#[derive(
Clone,
Copy,
Expand Down
2 changes: 1 addition & 1 deletion crates/diesel_models/src/enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ pub mod diesel_exports {
DbRefundStatus as RefundStatus, DbRefundType as RefundType,
DbRequestIncrementalAuthorization as RequestIncrementalAuthorization,
DbRoleScope as RoleScope, DbRoutingAlgorithmKind as RoutingAlgorithmKind,
DbUserStatus as UserStatus,
DbTransactionType as TransactionType, DbUserStatus as UserStatus,
};
}
pub use common_enums::*;
Expand Down
Loading
Loading