Skip to content

Commit

Permalink
Add balance route
Browse files Browse the repository at this point in the history
  • Loading branch information
Geometrically committed Dec 31, 2024
1 parent 01fe08f commit 76c8bf9
Show file tree
Hide file tree
Showing 10 changed files with 178 additions and 11 deletions.
8 changes: 7 additions & 1 deletion apps/labrinth/.env
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ PAYPAL_API_URL=https://api-m.sandbox.paypal.com/v1/
PAYPAL_WEBHOOK_ID=none
PAYPAL_CLIENT_ID=none
PAYPAL_CLIENT_SECRET=none
PAYPAL_NVP_USERNAME=none
PAYPAL_NVP_PASSWORD=none
PAYPAL_NVP_SIGNATURE=none

STEAM_API_KEY=none

Expand Down Expand Up @@ -106,4 +109,7 @@ STRIPE_WEBHOOK_SECRET=none

ADITUDE_API_KEY=none

PYRO_API_KEY=none
PYRO_API_KEY=none

BREX_API_URL=https://platform.brexapis.com/v2/
BREX_API_KEY=none

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion apps/labrinth/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,9 @@ pub fn check_env_vars() -> bool {
failed |= check_var::<String>("PAYPAL_WEBHOOK_ID");
failed |= check_var::<String>("PAYPAL_CLIENT_ID");
failed |= check_var::<String>("PAYPAL_CLIENT_SECRET");
failed |= check_var::<String>("PAYPAL_NVP_USERNAME");
failed |= check_var::<String>("PAYPAL_NVP_PASSWORD");
failed |= check_var::<String>("PAYPAL_NVP_SIGNATURE");

failed |= check_var::<String>("HCAPTCHA_SECRET");

Expand Down Expand Up @@ -482,9 +485,11 @@ pub fn check_env_vars() -> bool {
failed |= check_var::<String>("STRIPE_API_KEY");
failed |= check_var::<String>("STRIPE_WEBHOOK_SECRET");

failed |= check_var::<u64>("ADITUDE_API_KEY");
failed |= check_var::<String>("ADITUDE_API_KEY");

failed |= check_var::<String>("PYRO_API_KEY");

failed |= check_var::<String>("BREX_API_KEY");

failed
}
138 changes: 137 additions & 1 deletion apps/labrinth/src/queue/payouts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ pub struct PayoutsQueue {
payout_options: RwLock<Option<PayoutMethods>>,
}

#[derive(Clone)]
#[derive(Clone, Debug)]
struct PayPalCredentials {
access_token: String,
token_type: String,
Expand All @@ -36,6 +36,12 @@ struct PayoutMethods {
expires: DateTime<Utc>,
}

#[derive(Serialize)]
pub struct AccountBalance {
pub available: Decimal,
pub pending: Decimal,
}

impl Default for PayoutsQueue {
fn default() -> Self {
Self::new()
Expand Down Expand Up @@ -545,6 +551,136 @@ impl PayoutsQueue {

Ok(options.options)
}

pub async fn get_brex_balance() -> Result<Option<AccountBalance>, ApiError>
{
#[derive(Deserialize)]
struct BrexBalance {
pub amount: i64,
// pub currency: String,
}

#[derive(Deserialize)]
struct BrexAccount {
pub current_balance: BrexBalance,
pub available_balance: BrexBalance,
}

#[derive(Deserialize)]
struct BrexResponse {
pub items: Vec<BrexAccount>,
}

let client = reqwest::Client::new();
let res = client
.get(format!("{}accounts/cash", dotenvy::var("BREX_API_URL")?))
.bearer_auth(&dotenvy::var("BREX_API_KEY")?)
.send()
.await?
.json::<BrexResponse>()
.await?;

Ok(Some(AccountBalance {
available: Decimal::from(
res.items
.iter()
.map(|x| x.available_balance.amount)
.sum::<i64>(),
) / Decimal::from(100),
pending: Decimal::from(
res.items
.iter()
.map(|x| {
x.current_balance.amount - x.available_balance.amount
})
.sum::<i64>(),
) / Decimal::from(100),
}))
}

pub async fn get_paypal_balance() -> Result<Option<AccountBalance>, ApiError>
{
let api_username = dotenvy::var("PAYPAL_NVP_USERNAME")?;
let api_password = dotenvy::var("PAYPAL_NVP_PASSWORD")?;
let api_signature = dotenvy::var("PAYPAL_NVP_SIGNATURE")?;

let mut params = HashMap::new();
params.insert("METHOD", "GetBalance");
params.insert("VERSION", "204");
params.insert("USER", &api_username);
params.insert("PWD", &api_password);
params.insert("SIGNATURE", &api_signature);
params.insert("RETURNALLCURRENCIES", "1");

let endpoint = "https://api-3t.paypal.com/nvp";

let client = reqwest::Client::new();
let response = client.post(endpoint).form(&params).send().await?;

let text = response.text().await?;
let body = urlencoding::decode(&text).unwrap_or_default();

let mut key_value_map = HashMap::new();

for pair in body.split('&') {
let mut iter = pair.splitn(2, '=');
if let (Some(key), Some(value)) = (iter.next(), iter.next()) {
key_value_map.insert(key.to_string(), value.to_string());
}
}

if let Some(amount) = key_value_map
.get("L_AMT0")
.and_then(|x| Decimal::from_str_exact(x).ok())
{
Ok(Some(AccountBalance {
available: amount,
pending: Decimal::ZERO,
}))
} else {
Ok(None)
}
}

pub async fn get_tremendous_balance(
&self,
) -> Result<Option<AccountBalance>, ApiError> {
#[derive(Deserialize)]
struct FundingSourceMeta {
available_cents: u64,
pending_cents: u64,
}

#[derive(Deserialize)]
struct FundingSource {
method: String,
meta: FundingSourceMeta,
}

#[derive(Deserialize)]
struct FundingSourceRequest {
pub funding_sources: Vec<FundingSource>,
}

let val = self
.make_tremendous_request::<(), FundingSourceRequest>(
Method::GET,
"funding_sources",
None,
)
.await?;

Ok(val
.funding_sources
.into_iter()
.find(|x| x.method == "balance")
.map(|x| AccountBalance {
available: Decimal::from(x.meta.available_cents)
/ Decimal::from(100),
pending: Decimal::from(x.meta.pending_cents)
/ Decimal::from(100),
}))
}
}

#[derive(Deserialize)]
Expand Down
24 changes: 22 additions & 2 deletions apps/labrinth/src/routes/internal/admin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ use crate::models::ids::ProjectId;
use crate::models::pats::Scopes;
use crate::queue::analytics::AnalyticsQueue;
use crate::queue::maxmind::MaxMindIndexer;
use crate::queue::payouts::PayoutsQueue;
use crate::queue::session::AuthQueue;
use crate::routes::ApiError;
use crate::search::SearchConfig;
use crate::util::date::get_current_tenths_of_ms;
use crate::util::guards::admin_key_guard;
use actix_web::{patch, post, web, HttpRequest, HttpResponse};
use actix_web::{get, patch, post, web, HttpRequest, HttpResponse};
use serde::Deserialize;
use sqlx::PgPool;
use std::collections::HashMap;
Expand All @@ -21,7 +22,8 @@ pub fn config(cfg: &mut web::ServiceConfig) {
cfg.service(
web::scope("admin")
.service(count_download)
.service(force_reindex),
.service(force_reindex)
.service(get_balances),
);
}

Expand Down Expand Up @@ -158,3 +160,21 @@ pub async fn force_reindex(
index_projects(pool.as_ref().clone(), redis.clone(), &config).await?;
Ok(HttpResponse::NoContent().finish())
}

#[get("/_balances", guard = "admin_key_guard")]
pub async fn get_balances(
payouts: web::Data<PayoutsQueue>,
) -> Result<HttpResponse, ApiError> {
let (paypal, brex, tremendous) = futures::future::try_join3(
PayoutsQueue::get_paypal_balance(),
PayoutsQueue::get_brex_balance(),
payouts.get_tremendous_balance(),
)
.await?;

Ok(HttpResponse::Ok().json(serde_json::json!({
"paypal": paypal,
"brex": brex,
"tremendous": tremendous,
})))
}

0 comments on commit 76c8bf9

Please sign in to comment.