You've already forked AstralRinth
forked from didirus/AstralRinth
* wip: muralpay integration * Basic Mural Pay API bindings * Fix clippy * use dotenvy in muralpay example * Refactor payout creation code * wip: muralpay payout requests * Mural Pay payouts work * Fix clippy * add mural pay fees API * Work on payout fee API * Fees API for more payment methods * Fix CI * Temporarily disable Venmo and PayPal methods from frontend * wip: counterparties * Start on counterparties and payment methods API * Mural Pay multiple methods when fetching * Don't send supported_countries to frontend * Add countries to muralpay fiat methods * Compile fix * Add exchange rate info to fees endpoint * Add fees to premium Tremendous options * Add delivery email field to Tremendous payouts * Add Tremendous product category to payout methods * Add bank details API to muralpay * Fix CI * Fix CI * Remove prepaid visa, compute fees properly for Tremendous methods * Add more details to Tremendous errors * Add fees to Mural * Payout history route and bank details * Re-add legacy PayPal/Venmo options for US * move the mural bank details route * Add utoipa support to payout endpoints * address some PR comments * add CORS to new utoipa routes * Immediately approve mural payouts * Add currency support to Tremendous payouts * Currency forex * add forex to tremendous fee request * Add Mural balance to bank balance info * Add more Tremendous currencies support * Transaction payouts available use the correct date * Address my own review comment * Address PR comments * Change Mural withdrawal limit to 3k * maybe fix tremendous gift cards * Change how Mural minimum withdrawals are calculated * Tweak min/max withdrawal values --------- Co-authored-by: Calum H. <contact@cal.engineer> Co-authored-by: Alejandro González <me@alegon.dev>
198 lines
5.3 KiB
Rust
198 lines
5.3 KiB
Rust
use crate::database::models::DBUserId;
|
|
use chrono::{DateTime, Utc};
|
|
use serde::{Deserialize, Serialize};
|
|
use sqlx::{query, query_scalar};
|
|
use std::fmt;
|
|
|
|
#[derive(
|
|
Debug,
|
|
Default,
|
|
Clone,
|
|
Copy,
|
|
PartialEq,
|
|
Eq,
|
|
Hash,
|
|
Serialize,
|
|
Deserialize,
|
|
utoipa::ToSchema,
|
|
)]
|
|
pub enum FormType {
|
|
#[serde(rename = "W-8BEN")]
|
|
ForeignIndividual,
|
|
#[serde(rename = "W-8BEN-E")]
|
|
ForeignEntity,
|
|
#[default]
|
|
#[serde(rename = "W-9")]
|
|
DomesticPerson,
|
|
}
|
|
|
|
impl FormType {
|
|
pub fn as_str(&self) -> &'static str {
|
|
match self {
|
|
FormType::ForeignIndividual => "W8-BEN",
|
|
FormType::ForeignEntity => "W8-BEN-E",
|
|
FormType::DomesticPerson => "W-9",
|
|
}
|
|
}
|
|
|
|
pub fn from_str_or_default(s: &str) -> Self {
|
|
match s {
|
|
"W8-BEN" => FormType::ForeignIndividual,
|
|
"W8-BEN-E" => FormType::ForeignEntity,
|
|
"W-9" => FormType::DomesticPerson,
|
|
_ => FormType::default(),
|
|
}
|
|
}
|
|
|
|
pub fn requires_domestic_tin_match(self) -> bool {
|
|
match self {
|
|
FormType::ForeignIndividual | FormType::ForeignEntity => false,
|
|
FormType::DomesticPerson => true,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for FormType {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
write!(f, "{}", self.as_str())
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct UserCompliance {
|
|
pub id: i64,
|
|
pub user_id: DBUserId,
|
|
pub requested: DateTime<Utc>,
|
|
pub signed: Option<DateTime<Utc>>,
|
|
pub e_delivery_consented: bool,
|
|
pub tin_matched: bool,
|
|
pub last_checked: DateTime<Utc>,
|
|
pub external_request_id: String,
|
|
pub reference_id: String,
|
|
pub form_type: Option<FormType>,
|
|
pub requires_manual_review: bool,
|
|
}
|
|
|
|
impl UserCompliance {
|
|
pub async fn get_by_user_id<'a, E>(
|
|
exec: E,
|
|
id: DBUserId,
|
|
) -> sqlx::Result<Option<Self>>
|
|
where
|
|
E: sqlx::PgExecutor<'a>,
|
|
{
|
|
let maybe_compliance = query!(
|
|
r#"SELECT * FROM users_compliance WHERE user_id = $1"#,
|
|
id.0
|
|
)
|
|
.fetch_optional(exec)
|
|
.await?
|
|
.map(|row| UserCompliance {
|
|
id: row.id,
|
|
user_id: id,
|
|
requested: row.requested,
|
|
signed: row.signed,
|
|
e_delivery_consented: row.e_delivery_consented,
|
|
tin_matched: row.tin_matched,
|
|
last_checked: row.last_checked,
|
|
external_request_id: row.external_request_id,
|
|
reference_id: row.reference_id,
|
|
form_type: row
|
|
.form_type
|
|
.as_deref()
|
|
.map(FormType::from_str_or_default),
|
|
requires_manual_review: row.requires_manual_review,
|
|
});
|
|
|
|
Ok(maybe_compliance)
|
|
}
|
|
|
|
/// This either inserts the row into the table or updates the row, *except the requires_manual_review* column.
|
|
pub async fn upsert_partial<'a, E>(&mut self, exec: E) -> sqlx::Result<()>
|
|
where
|
|
E: sqlx::PgExecutor<'a>,
|
|
{
|
|
let id = query_scalar!(
|
|
r#"
|
|
INSERT INTO users_compliance
|
|
(
|
|
user_id,
|
|
requested,
|
|
signed,
|
|
e_delivery_consented,
|
|
tin_matched,
|
|
last_checked,
|
|
external_request_id,
|
|
reference_id,
|
|
form_type,
|
|
requires_manual_review
|
|
)
|
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
|
|
ON CONFLICT (user_id)
|
|
DO UPDATE SET
|
|
requested = EXCLUDED.requested,
|
|
signed = EXCLUDED.signed,
|
|
e_delivery_consented = EXCLUDED.e_delivery_consented,
|
|
tin_matched = EXCLUDED.tin_matched,
|
|
last_checked = EXCLUDED.last_checked,
|
|
external_request_id = EXCLUDED.external_request_id,
|
|
reference_id = EXCLUDED.reference_id,
|
|
form_type = EXCLUDED.form_type
|
|
RETURNING id
|
|
"#,
|
|
self.user_id.0,
|
|
self.requested,
|
|
self.signed,
|
|
self.e_delivery_consented,
|
|
self.tin_matched,
|
|
self.last_checked,
|
|
self.external_request_id,
|
|
self.reference_id,
|
|
self.form_type.map(|s| s.as_str()),
|
|
self.requires_manual_review,
|
|
)
|
|
.fetch_one(exec)
|
|
.await?;
|
|
|
|
self.id = id;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn update<'a, E>(&self, exec: E) -> sqlx::Result<()>
|
|
where
|
|
E: sqlx::PgExecutor<'a>,
|
|
{
|
|
query!(
|
|
r#"
|
|
UPDATE users_compliance
|
|
SET
|
|
requested = $2,
|
|
signed = $3,
|
|
e_delivery_consented = $4,
|
|
tin_matched = $5,
|
|
last_checked = $6,
|
|
external_request_id = $7,
|
|
reference_id = $8,
|
|
form_type = $9,
|
|
requires_manual_review = $10
|
|
WHERE id = $1
|
|
"#,
|
|
self.id,
|
|
self.requested,
|
|
self.signed,
|
|
self.e_delivery_consented,
|
|
self.tin_matched,
|
|
self.last_checked,
|
|
self.external_request_id,
|
|
self.reference_id,
|
|
self.form_type.map(|s| s.as_str()),
|
|
self.requires_manual_review,
|
|
)
|
|
.execute(exec)
|
|
.await?;
|
|
|
|
Ok(())
|
|
}
|
|
}
|