use crate::database::models::{DBUserId, users_compliance::FormType}; use crate::routes::ApiError; use ariadne::ids::base62_impl::to_base62; use chrono::{Datelike, NaiveDateTime}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::fmt; #[derive(Debug, Serialize, Deserialize)] pub struct DataWrapper { pub data: Data, } #[derive(Debug, Serialize, Deserialize)] pub struct ListWrapper { pub data: Vec>, } #[derive(Debug, Serialize, Deserialize)] pub struct Data { #[serde(rename = "type")] pub r#type: Option, pub id: Option, pub attributes: T, pub links: Option>, } #[derive(Serialize, Deserialize)] pub struct FormResponse { pub form_type: FormType, pub form_id: Option, pub company_id: u32, pub company_name: String, pub company_email: String, pub reference_id: String, pub signed_at: Option, } #[derive(Serialize, Deserialize)] pub struct W9FormsResponse { pub e_delivery_consented_at: Option, pub tin_match_status: Option, pub entry_status: String, } pub async fn request_form( user_id: DBUserId, form_type: FormType, ) -> Result, serde_json::Value>, ApiError> { const DEFAULT_TTL: u32 = 3600; #[derive(Serialize, Deserialize)] struct FormRequest { form_type: FormType, company_id: String, reference_id: String, ttl: u32, } let (request_builder, company_id) = team_request(reqwest::Method::POST, "/form_requests")?; let response = request_builder .json(&DataWrapper { data: Data { r#type: Some("form_request".to_owned()), id: None, attributes: FormRequest { form_type, company_id, ttl: DEFAULT_TTL, reference_id: Reference { user_id, form_type, current_year: chrono::Utc::now().year_ce().1, } .to_string(), }, links: None, }, }) .send() .await?; Ok(if response.status().is_success() { Ok(response.json::>().await?) } else { Err(response.json().await?) }) } pub async fn check_form( reference_id: &str, ) -> Result< Result>, serde_json::Value>, ApiError, > { let (request_builder, _company_id) = team_request( reqwest::Method::GET, &format!( "/w9forms?filter[reference_id_eq]={reference_id}&page[number]=1&page[size]=1" ), )?; let response = request_builder.send().await?; Ok(if response.status().is_success() { let mut list_wrapper = response.json::>().await?; Ok(list_wrapper.data.pop().map(|data| DataWrapper { data })) } else { Err(response.json().await?) }) } fn team_request( method: reqwest::Method, route: &str, ) -> Result<(reqwest::RequestBuilder, String), ApiError> { let key = dotenvy::var("AVALARA_1099_API_KEY")?; let url = dotenvy::var("AVALARA_1099_API_URL")?; let team = dotenvy::var("AVALARA_1099_API_TEAM_ID")?; let company = dotenvy::var("AVALARA_1099_COMPANY_ID")?; let url = url.trim_end_matches('/'); let client = reqwest::Client::new(); Ok(( client .request(method, format!("{url}/v1/{team}{route}")) .header(reqwest::header::USER_AGENT, "Modrinth") .bearer_auth(&key), company, )) } struct Reference { user_id: DBUserId, form_type: FormType, current_year: u32, } impl fmt::Display for Reference { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "{}_{}_{}", to_base62(self.user_id.0 as u64), self.form_type, self.current_year ) } }