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

Add balance route #3107

Merged
merged 1 commit into from
Dec 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
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,
})))
}
Loading