Payouts fees changes (#478)

* Payouts fees changes

* Update src/queue/payouts.rs

Co-authored-by: triphora <emmaffle@modrinth.com>

Co-authored-by: triphora <emmaffle@modrinth.com>
This commit is contained in:
Geometrically
2022-11-16 13:46:36 -07:00
committed by GitHub
parent ecc500fc91
commit da19a07943
9 changed files with 141 additions and 39 deletions

View File

@@ -1,5 +1,6 @@
use crate::routes::ApiError;
use chrono::{DateTime, Duration, Utc};
use rust_decimal::Decimal;
use serde::{Deserialize, Serialize};
use serde_json::json;
use std::collections::HashMap;
@@ -26,10 +27,11 @@ pub struct PayoutItem {
pub sender_item_id: String,
}
#[derive(Serialize)]
#[derive(Serialize, Deserialize)]
pub struct PayoutAmount {
pub currency: String,
pub value: String,
#[serde(with = "rust_decimal::serde::str")]
pub value: Decimal,
}
// Batches payouts and handles token refresh
@@ -85,8 +87,8 @@ impl PayoutsQueue {
pub async fn send_payout(
&mut self,
payout: PayoutItem,
) -> Result<(), ApiError> {
mut payout: PayoutItem,
) -> Result<Decimal, ApiError> {
if self.credential_expires < Utc::now() {
self.refresh_token().await.map_err(|_| {
ApiError::Payments(
@@ -95,6 +97,22 @@ impl PayoutsQueue {
})?;
}
let fee = std::cmp::min(
std::cmp::max(
Decimal::ONE / Decimal::from(4),
(Decimal::from(2) / Decimal::ONE_HUNDRED) * payout.amount.value,
),
Decimal::from(20),
);
payout.amount.value -= fee;
if payout.amount.value <= Decimal::ZERO {
return Err(ApiError::InvalidInput(
"You do not have enough funds to make this payout!".to_string(),
));
}
let client = reqwest::Client::new();
let res = client.post(&format!("{}payments/payouts", dotenvy::var("PAYPAL_API_URL")?))
@@ -130,8 +148,58 @@ impl PayoutsQueue {
"Error while registering payment in PayPal: {}",
body.body.message
)));
} else {
#[derive(Deserialize)]
struct PayPalLink {
href: String,
}
#[derive(Deserialize)]
struct PayoutsResponse {
pub links: Vec<PayPalLink>,
}
#[derive(Deserialize)]
struct PayoutDataItem {
payout_item_fee: PayoutAmount,
}
#[derive(Deserialize)]
struct PayoutData {
pub items: Vec<PayoutDataItem>,
}
// Calculate actual fee + refund if we took too big of a fee.
if let Some(res) = res.json::<PayoutsResponse>().await.ok() {
if let Some(link) = res.links.first() {
if let Some(res) = client
.get(&link.href)
.header(
"Authorization",
format!(
"{} {}",
self.credential.token_type,
self.credential.access_token
),
)
.send()
.await
.ok()
{
if let Some(res) = res.json::<PayoutData>().await.ok() {
if let Some(data) = res.items.first() {
if (fee - data.payout_item_fee.value)
> Decimal::ZERO
{
return Ok(fee - data.payout_item_fee.value);
}
}
}
}
}
}
}
Ok(())
Ok(Decimal::ZERO)
}
}