raisfast 0.2.23

The last backend you'll ever need. Rust-powered headless CMS with built-in blog, ecommerce, wallet, payment and 4 plugin engines.
use serde::{Deserialize, Serialize};
#[cfg(feature = "export-types")]
use ts_rs::TS;
use utoipa::ToSchema;
use validator::Validate;

use crate::models::payment_order::PaymentStatus;

fn validate_url(url: &str) -> Result<(), validator::ValidationError> {
    if url.starts_with("http://") || url.starts_with("https://") {
        Ok(())
    } else {
        Err(validator::ValidationError::new("invalid_url"))
    }
}

#[cfg_attr(feature = "export-types", derive(TS))]
#[derive(Debug, Deserialize, Validate, ToSchema)]
pub struct CreatePaymentOrderRequest {
    #[validate(length(min = 1))]
    pub order_id: String,
    pub channel_id: Option<String>,
    pub method: Option<String>,
    pub country: Option<String>,
    pub language: Option<String>,
    #[validate(custom(
        function = "validate_url",
        message = "return_url must start with http:// or https://"
    ))]
    pub return_url: Option<String>,
    pub metadata: Option<String>,
}

#[cfg_attr(feature = "export-types", derive(TS))]
#[derive(Debug, Deserialize, Validate, ToSchema)]
pub struct CreateRefundRequest {
    #[validate(range(min = 1))]
    pub amount: i64,
    pub reason: Option<String>,
}

#[cfg_attr(feature = "export-types", derive(TS))]
#[derive(Debug, Deserialize, Validate, ToSchema)]
pub struct CreatePaymentChannelRequest {
    #[validate(length(min = 1, max = 50))]
    pub provider: String,
    #[validate(length(min = 1, max = 200))]
    pub name: String,
    pub is_live: Option<bool>,
    #[validate(length(min = 1))]
    pub credentials: String,
    pub webhook_secret: Option<String>,
    pub settings: Option<String>,
    pub sort_order: Option<i64>,
}

#[cfg_attr(feature = "export-types", derive(TS))]
#[derive(Debug, Deserialize, Validate, ToSchema)]
pub struct UpdatePaymentChannelRequest {
    pub name: Option<String>,
    pub is_live: Option<bool>,
    pub credentials: Option<String>,
    pub webhook_secret: Option<String>,
    pub settings: Option<String>,
    pub is_active: Option<bool>,
    pub sort_order: Option<i64>,
    pub version: i64,
}

#[cfg_attr(feature = "export-types", derive(TS))]
#[derive(Debug, Serialize, ToSchema)]
pub struct PaymentOrderResponse {
    pub id: String,
    pub order_id: Option<String>,
    pub title: String,
    #[cfg_attr(feature = "export-types", ts(type = "number"))]
    pub amount: i64,
    pub currency: String,
    pub provider: String,
    pub provider_order_id: Option<String>,
    pub provider_method: Option<String>,
    pub status: PaymentStatus,
    pub return_url: Option<String>,
    #[cfg_attr(feature = "export-types", ts(type = "number"))]
    pub version: i64,
    pub provider_data: Option<String>,
    pub client_ip: Option<String>,
    pub client_language: Option<String>,
    pub client_country: Option<String>,
    pub client_user_agent: Option<String>,
    pub channel_selected_by: Option<String>,
    pub metadata: Option<String>,
    pub redirect_url: Option<String>,
    pub qr_code: Option<String>,
    pub client_secret: Option<String>,
    pub paid_at: Option<String>,
    pub cancelled_at: Option<String>,
    pub expired_at: Option<String>,
    pub created_at: String,
    pub updated_at: String,
}

#[cfg_attr(feature = "export-types", derive(TS))]
#[derive(Debug, Serialize, ToSchema)]
pub struct PaymentChannelResponse {
    pub id: String,
    pub provider: String,
    pub name: String,
    pub is_live: bool,
    pub credentials_masked: String,
    pub webhook_secret_set: bool,
    pub settings: Option<String>,
    pub is_active: bool,
    #[cfg_attr(feature = "export-types", ts(type = "number"))]
    pub sort_order: i64,
    #[cfg_attr(feature = "export-types", ts(type = "number"))]
    pub version: i64,
    pub created_at: String,
    pub updated_at: String,
}

impl From<crate::models::payment_channel::PaymentChannel> for PaymentChannelResponse {
    fn from(ch: crate::models::payment_channel::PaymentChannel) -> Self {
        Self {
            id: ch.id.to_string(),
            provider: ch.provider,
            name: ch.name,
            is_live: ch.is_live != 0,
            credentials_masked: mask_credentials(&ch.credentials),
            webhook_secret_set: ch.webhook_secret.is_some(),
            settings: ch.settings,
            is_active: ch.is_active != 0,
            sort_order: ch.sort_order,
            version: ch.version,
            created_at: ch.created_at.to_string(),
            updated_at: ch.updated_at.to_string(),
        }
    }
}

fn mask_credentials(_creds: &str) -> String {
    "[encrypted]".to_string()
}

#[cfg_attr(feature = "export-types", derive(TS))]
#[derive(Debug, Serialize, ToSchema)]
pub struct PaymentTransactionResponse {
    pub id: String,
    pub order_id: Option<String>,
    pub tx_type: String,
    #[cfg_attr(feature = "export-types", ts(type = "number"))]
    pub amount: i64,
    pub currency: String,
    pub provider_tx_id: String,
    pub status: String,
    pub created_at: String,
}

impl From<crate::models::payment_transaction::PaymentTransaction> for PaymentTransactionResponse {
    fn from(tx: crate::models::payment_transaction::PaymentTransaction) -> Self {
        Self {
            id: tx.id.to_string(),
            order_id: tx.order_id,
            tx_type: tx.tx_type,
            amount: tx.amount,
            currency: tx.currency,
            provider_tx_id: tx.provider_tx_id,
            status: tx.status,
            created_at: tx.created_at.to_string(),
        }
    }
}

#[cfg_attr(feature = "export-types", derive(TS))]
#[derive(Debug, Serialize, ToSchema)]
pub struct PaymentRefundResponse {
    pub id: String,
    pub order_id: Option<String>,
    #[cfg_attr(feature = "export-types", ts(type = "number"))]
    pub amount: i64,
    pub currency: String,
    pub reason: Option<String>,
    pub provider_refund_id: Option<String>,
    pub status: String,
    pub created_at: String,
    pub updated_at: String,
}

impl From<crate::models::payment_refund::PaymentRefund> for PaymentRefundResponse {
    fn from(r: crate::models::payment_refund::PaymentRefund) -> Self {
        Self {
            id: r.id.to_string(),
            order_id: r.order_id,
            amount: r.amount,
            currency: r.currency,
            reason: r.reason,
            provider_refund_id: r.provider_refund_id,
            status: r.status,
            created_at: r.created_at.to_string(),
            updated_at: r.updated_at.to_string(),
        }
    }
}

#[cfg_attr(feature = "export-types", derive(TS))]
#[derive(Debug, Serialize, ToSchema)]
pub struct AvailableChannelItem {
    pub channel_id: String,
    pub provider: String,
    pub name: String,
    pub is_recommended: bool,
    pub sort_order: i64,
}

#[cfg_attr(feature = "export-types", derive(TS))]
#[derive(Debug, Serialize, ToSchema)]
pub struct AvailableChannelsResponse {
    pub recommended_channel_id: Option<String>,
    pub channels: Vec<AvailableChannelItem>,
}

#[cfg_attr(feature = "export-types", derive(TS))]
#[derive(Debug, Deserialize, ToSchema)]
pub struct AvailableChannelsQuery {
    pub order_id: String,
    pub country: Option<String>,
    pub language: Option<String>,
}