- Add .env.example file for environment variable setup - Add .gitignore to manage sensitive files and directories - Add Dockerfile.prod for production-ready Docker image - Add PRODUCTION_CHECKLIST.md for pre/post deployment steps - Add PRODUCTION_DEPLOYMENT.md for deployment instructions - Add STRIPE_SETUP.md for Stripe payment configuration - Add config/default.toml for default configuration settings - Add config/local.toml.example for local configuration template
273 lines
8.8 KiB
Rust
273 lines
8.8 KiB
Rust
#![allow(dead_code)] // Database utility functions may not all be used yet
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
use std::collections::HashMap;
|
|
use std::fs;
|
|
use std::path::Path;
|
|
|
|
/// Stored registration data linked to payment intent
|
|
/// This preserves all user form data until company creation after payment success
|
|
/// NOTE: This uses file-based storage until we can add the model to heromodels
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct StoredRegistrationData {
|
|
pub payment_intent_id: String,
|
|
pub company_name: String,
|
|
pub company_type: String,
|
|
pub company_email: String,
|
|
pub company_phone: String,
|
|
pub company_website: Option<String>,
|
|
pub company_address: String,
|
|
pub company_industry: Option<String>,
|
|
pub company_purpose: Option<String>,
|
|
pub fiscal_year_end: Option<String>,
|
|
pub shareholders: String, // JSON string of shareholders array
|
|
pub payment_plan: String,
|
|
pub created_at: i64,
|
|
}
|
|
|
|
/// File path for storing registration data
|
|
const REGISTRATION_DATA_FILE: &str = "data/registration_data.json";
|
|
|
|
/// Ensure data directory exists
|
|
fn ensure_data_directory() -> Result<(), String> {
|
|
let data_dir = Path::new("data");
|
|
if !data_dir.exists() {
|
|
fs::create_dir_all(data_dir)
|
|
.map_err(|e| format!("Failed to create data directory: {}", e))?;
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
/// Load all registration data from file
|
|
fn load_registration_data() -> Result<HashMap<String, StoredRegistrationData>, String> {
|
|
if !Path::new(REGISTRATION_DATA_FILE).exists() {
|
|
return Ok(HashMap::new());
|
|
}
|
|
|
|
let content = fs::read_to_string(REGISTRATION_DATA_FILE)
|
|
.map_err(|e| format!("Failed to read registration data file: {}", e))?;
|
|
|
|
let data: HashMap<String, StoredRegistrationData> = serde_json::from_str(&content)
|
|
.map_err(|e| format!("Failed to parse registration data: {}", e))?;
|
|
|
|
Ok(data)
|
|
}
|
|
|
|
/// Save all registration data to file
|
|
fn save_registration_data(data: &HashMap<String, StoredRegistrationData>) -> Result<(), String> {
|
|
ensure_data_directory()?;
|
|
|
|
let content = serde_json::to_string_pretty(data)
|
|
.map_err(|e| format!("Failed to serialize registration data: {}", e))?;
|
|
|
|
fs::write(REGISTRATION_DATA_FILE, content)
|
|
.map_err(|e| format!("Failed to write registration data file: {}", e))?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
impl StoredRegistrationData {
|
|
/// Create new stored registration data
|
|
pub fn new(
|
|
payment_intent_id: String,
|
|
company_name: String,
|
|
company_type: String,
|
|
company_email: String,
|
|
company_phone: String,
|
|
company_website: Option<String>,
|
|
company_address: String,
|
|
company_industry: Option<String>,
|
|
company_purpose: Option<String>,
|
|
fiscal_year_end: Option<String>,
|
|
shareholders: String,
|
|
payment_plan: String,
|
|
) -> Self {
|
|
Self {
|
|
payment_intent_id,
|
|
company_name,
|
|
company_type,
|
|
company_email,
|
|
company_phone,
|
|
company_website,
|
|
company_address,
|
|
company_industry,
|
|
company_purpose,
|
|
fiscal_year_end,
|
|
shareholders,
|
|
payment_plan,
|
|
created_at: chrono::Utc::now().timestamp(),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Store registration data linked to payment intent
|
|
pub fn store_registration_data(
|
|
payment_intent_id: String,
|
|
data: crate::controllers::payment::CompanyRegistrationData,
|
|
) -> Result<(u32, StoredRegistrationData), String> {
|
|
// Create stored registration data
|
|
let stored_data = StoredRegistrationData::new(
|
|
payment_intent_id.clone(),
|
|
data.company_name,
|
|
data.company_type,
|
|
data.company_email
|
|
.unwrap_or_else(|| "noemail@example.com".to_string()),
|
|
data.company_phone
|
|
.unwrap_or_else(|| "+1234567890".to_string()),
|
|
data.company_website,
|
|
data.company_address
|
|
.unwrap_or_else(|| "No address provided".to_string()),
|
|
data.company_industry,
|
|
data.company_purpose,
|
|
data.fiscal_year_end,
|
|
data.shareholders,
|
|
data.payment_plan,
|
|
);
|
|
|
|
// Load existing data
|
|
let mut all_data = load_registration_data()?;
|
|
|
|
// Add new data
|
|
all_data.insert(payment_intent_id.clone(), stored_data.clone());
|
|
|
|
// Save to file
|
|
save_registration_data(&all_data)?;
|
|
|
|
log::info!(
|
|
"Stored registration data for payment intent {}",
|
|
payment_intent_id
|
|
);
|
|
|
|
// Return with a generated ID (timestamp-based)
|
|
let id = chrono::Utc::now().timestamp() as u32;
|
|
Ok((id, stored_data))
|
|
}
|
|
|
|
/// Retrieve registration data by payment intent ID
|
|
pub fn get_registration_data(
|
|
payment_intent_id: &str,
|
|
) -> Result<Option<StoredRegistrationData>, String> {
|
|
let all_data = load_registration_data()?;
|
|
Ok(all_data.get(payment_intent_id).cloned())
|
|
}
|
|
|
|
/// Get all stored registration data
|
|
pub fn get_all_registration_data() -> Result<Vec<StoredRegistrationData>, String> {
|
|
let all_data = load_registration_data()?;
|
|
Ok(all_data.into_values().collect())
|
|
}
|
|
|
|
/// Delete registration data by payment intent ID
|
|
pub fn delete_registration_data(payment_intent_id: &str) -> Result<bool, String> {
|
|
let mut all_data = load_registration_data()?;
|
|
|
|
if all_data.remove(payment_intent_id).is_some() {
|
|
save_registration_data(&all_data)?;
|
|
log::info!(
|
|
"Deleted registration data for payment intent: {}",
|
|
payment_intent_id
|
|
);
|
|
Ok(true)
|
|
} else {
|
|
log::warn!(
|
|
"Registration data not found for payment intent: {}",
|
|
payment_intent_id
|
|
);
|
|
Ok(false)
|
|
}
|
|
}
|
|
|
|
/// Update registration data
|
|
pub fn update_registration_data(
|
|
payment_intent_id: &str,
|
|
updated_data: StoredRegistrationData,
|
|
) -> Result<Option<StoredRegistrationData>, String> {
|
|
let mut all_data = load_registration_data()?;
|
|
|
|
all_data.insert(payment_intent_id.to_string(), updated_data.clone());
|
|
save_registration_data(&all_data)?;
|
|
|
|
log::info!(
|
|
"Updated registration data for payment intent: {}",
|
|
payment_intent_id
|
|
);
|
|
|
|
Ok(Some(updated_data))
|
|
}
|
|
|
|
/// Convert StoredRegistrationData back to CompanyRegistrationData for processing
|
|
pub fn stored_to_registration_data(
|
|
stored: &StoredRegistrationData,
|
|
) -> crate::controllers::payment::CompanyRegistrationData {
|
|
crate::controllers::payment::CompanyRegistrationData {
|
|
company_name: stored.company_name.clone(),
|
|
company_type: stored.company_type.clone(),
|
|
company_email: Some(stored.company_email.clone()),
|
|
company_phone: Some(stored.company_phone.clone()),
|
|
company_website: stored.company_website.clone(),
|
|
company_address: Some(stored.company_address.clone()),
|
|
company_industry: stored.company_industry.clone(),
|
|
company_purpose: stored.company_purpose.clone(),
|
|
fiscal_year_end: stored.fiscal_year_end.clone(),
|
|
shareholders: stored.shareholders.clone(),
|
|
payment_plan: stored.payment_plan.clone(),
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_stored_registration_data_creation() {
|
|
let data = StoredRegistrationData::new(
|
|
"pi_test123".to_string(),
|
|
"Test Company".to_string(),
|
|
"Single FZC".to_string(),
|
|
"test@example.com".to_string(),
|
|
"+1234567890".to_string(),
|
|
Some("https://example.com".to_string()),
|
|
"123 Test St".to_string(),
|
|
Some("Technology".to_string()),
|
|
Some("Software development".to_string()),
|
|
Some("December".to_string()),
|
|
"[]".to_string(),
|
|
"monthly".to_string(),
|
|
);
|
|
|
|
assert_eq!(data.payment_intent_id, "pi_test123");
|
|
assert_eq!(data.company_name, "Test Company");
|
|
assert_eq!(data.company_type, "Single FZC");
|
|
assert_eq!(data.company_email, "test@example.com");
|
|
assert!(data.created_at > 0);
|
|
}
|
|
|
|
#[test]
|
|
fn test_stored_to_registration_data_conversion() {
|
|
let stored = StoredRegistrationData::new(
|
|
"pi_test123".to_string(),
|
|
"Test Company".to_string(),
|
|
"Single FZC".to_string(),
|
|
"test@example.com".to_string(),
|
|
"+1234567890".to_string(),
|
|
Some("https://example.com".to_string()),
|
|
"123 Test St".to_string(),
|
|
Some("Technology".to_string()),
|
|
Some("Software development".to_string()),
|
|
Some("December".to_string()),
|
|
"[]".to_string(),
|
|
"monthly".to_string(),
|
|
);
|
|
|
|
let registration_data = stored_to_registration_data(&stored);
|
|
|
|
assert_eq!(registration_data.company_name, "Test Company");
|
|
assert_eq!(registration_data.company_type, "Single FZC");
|
|
assert_eq!(
|
|
registration_data.company_email,
|
|
Some("test@example.com".to_string())
|
|
);
|
|
assert_eq!(registration_data.payment_plan, "monthly");
|
|
}
|
|
}
|