From ab539a313f712efbff0fb886823a1d736fe158e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois-Xavier=20Talbot?= <108630700+fetchfern@users.noreply.github.com> Date: Thu, 28 Aug 2025 05:36:31 -0400 Subject: [PATCH] Add tax compliance form related fields to GET /payout (#4274) * Add form fields to GET payout * Fix TIN match status never being updated * Fmt + clippy * Remove unnecessary borrow --- apps/labrinth/src/routes/v3/payouts.rs | 54 ++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 3 deletions(-) diff --git a/apps/labrinth/src/routes/v3/payouts.rs b/apps/labrinth/src/routes/v3/payouts.rs index 04cc339d..1e9a5b45 100644 --- a/apps/labrinth/src/routes/v3/payouts.rs +++ b/apps/labrinth/src/routes/v3/payouts.rs @@ -876,6 +876,16 @@ pub struct MethodFilter { pub country: Option, } +#[derive(Serialize, Deserialize)] +#[serde(rename_all = "kebab-case")] +pub enum FormCompletionStatus { + Unknown, + Unrequested, + Unsigned, + TinMismatch, + Complete, +} + #[get("methods")] pub async fn payment_methods( payouts_queue: web::Data, @@ -925,15 +935,53 @@ pub async fn get_balance( .await? .1; + #[derive(Serialize)] + struct Response { + #[serde(flatten)] + balance: UserBalance, + requested_form_type: Option, + form_completion_status: Option, + } + let balance = get_user_balance(user.id.into(), &pool).await?; - Ok(HttpResponse::Ok().json(balance)) + let mut requested_form_type = None; + let mut form_completion_status = None; + + // Only check compliance status if the compliance check is enabled (by having a value set for it) + if tax_compliance_payout_threshold().is_some() { + form_completion_status = Some( + update_compliance_status(&pool, user.id.into()) + .await? + .map_or(FormCompletionStatus::Unrequested, |compliance| { + requested_form_type = Some(compliance.model.form_type); + + if compliance.compliance_api_check_failed { + FormCompletionStatus::Unknown + } else if compliance.model.signed.is_some() { + if compliance.model.tin_matched { + FormCompletionStatus::Complete + } else { + FormCompletionStatus::TinMismatch + } + } else { + FormCompletionStatus::Unsigned + } + }), + ); + } + + Ok(HttpResponse::Ok().json(Response { + balance, + requested_form_type, + form_completion_status, + })) } async fn get_user_balance( user_id: crate::database::models::ids::DBUserId, pool: &PgPool, -) -> Result { +) -> Result { let payouts = sqlx::query!( " SELECT date_available, SUM(amount) sum @@ -1007,7 +1055,7 @@ async fn update_compliance_status( return Ok(None); }; - if compliance.signed.is_some() + if (compliance.signed.is_some() && compliance.tin_matched) || Utc::now().signed_duration_since(compliance.last_checked) < COMPLIANCE_CHECK_DEBOUNCE {