use crate::{
RecordId,
components::{heading::Heading, label::Label},
};
use leptos::prelude::*;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct PaymentExtra {
pub amount_fee: String,
pub amount_gross: String,
pub amount_net: String,
pub custom_int1: String,
pub custom_int2: String,
pub custom_int3: String,
pub custom_int4: String,
pub custom_int5: String,
pub custom_str1: String,
pub custom_str2: String,
pub custom_str3: String,
pub custom_str4: String,
pub custom_str5: String,
pub email_address: String,
pub item_description: String,
pub item_name: String,
pub m_payment_id: String,
pub merchant_id: String,
pub name_first: String,
pub name_last: String,
pub payment_status: String,
pub pf_payment_id: String,
pub signature: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Payment {
pub id: RecordId,
pub order: RecordId,
pub address: String,
pub amount_fee: String,
pub amount_gross: String,
pub amount_net: String,
pub city: String,
pub email_address: String,
pub extra: PaymentExtra,
pub item_description: String,
pub item_name: String,
pub m_payment_id: String,
pub merchant_id: String,
pub name_first: String,
pub name_last: String,
pub payment_status: String,
pub pf_payment_id: String,
#[serde(rename = "phoneNumber")]
pub phone_number: String,
pub postal_code: String,
pub signature: String,
}
#[cfg(feature = "ssr")]
pub async fn get_payment_by_order(order_id: RecordId) -> Result<Option<Payment>, ServerFnError> {
use crate::db_init;
let db = db_init().await?;
let mut query = db
.query("SELECT * FROM payments WHERE order = $order_id LIMIT 1")
.bind(("order_id", order_id))
.await?;
let payment: Option<Payment> = query.take(0)?;
Ok(payment)
}
#[cfg(feature = "ssr")]
pub async fn get_payment_by_id(payment_id: RecordId) -> Result<Option<Payment>, ServerFnError> {
use crate::db_init;
let db = db_init().await?;
let payment: Option<Payment> = db.select(payment_id).await?;
Ok(payment)
}
#[component]
pub fn PaymentView(payment: Payment) -> impl IntoView {
view! {
<div class="pt-5">
<Heading>"Payment Details"</Heading>
<section class="grid grid-cols-2 gap-5 mt-5">
<div>
<Label>"Name"</Label>
<span>{payment.name_first}" "{payment.name_last}</span>
</div>
<div>
<Label>"Phone"</Label>
<span>{payment.phone_number}</span>
</div>
<div>
<Label>"Address"</Label>
<span>{payment.address}</span>
<br />
<span>{payment.city}</span>
<br />
<span>{payment.postal_code}</span>
</div>
</section>
</div>
}
.into_view()
}
use crate::order::order_model::OrderFull;
use crate::payments::payfast::PayFastButton;
#[component]
pub fn PaymentOptions(order: OrderFull) -> impl IntoView {
let Some(delivery_details) = order.delivery_details.clone() else {
return view! { <div>"No delivery details found for this order."</div> }.into_any();
};
view! {
<div>
<div class="flex flex-col gap-4">
<PayFastButton
payment_order_title="ScratchFixPro".to_string()
payment_order_description="PaintKit Order".to_string()
payment_uuid=order.id.to_string().clone()
payment_first_name=delivery_details.first_name.unwrap_or_default()
payment_last_name=delivery_details.last_name.unwrap_or_default()
payment_email=delivery_details.email.to_string()
payment_telephone=delivery_details
.phone
.unwrap_or(delivery_details.telephone.unwrap_or_default())
payment_confirm_amount=order.total.parse().unwrap_or(0.0)
address=delivery_details.address1.unwrap_or_default()
city=delivery_details.address2.unwrap_or_default()
province=delivery_details.address3.unwrap_or_default()
postal_code=delivery_details.postcode.unwrap_or_default()
/>
</div>
</div>
}
.into_any()
}
use crate::components::Button;
use leptos::prelude::*;
use leptos_router::hooks::use_params_map;
use phosphor_leptos::Icon;
#[server]
async fn fetch_payment(payment_id: RecordId) -> Result<Payment, ServerFnError> {
let payment = crate::payments::payment::get_payment_by_order(payment_id).await?;
println!("Fetched payment: {:?}", payment);
let payment = match payment {
Some(p) => p,
None => return Err(ServerFnError::ServerError("Payment not found".into())),
};
Ok(payment)
}
#[component]
pub fn PaymentStatusPage() -> impl IntoView {
let params = use_params_map();
let paymentid = move || params.read().get("paymentid").unwrap_or_default();
let status = move || params.read().get("status").unwrap_or_default();
view! {
<div class="p-20 text-center h-[75vh] flex flex-col justify-center items-center">
<h1 class="text-center text-4xl">"Payment Status "{status().to_uppercase()}</h1>
{match status().as_str() {
"success" => {
view! {
<div class="mx-auto mt-4 text-green-500 flex justify-center items-center gap-2">
<Icon icon=phosphor_leptos::CHECK_CIRCLE size="64px" />
</div>
}
}
"failed" => {
view! {
<div class="mx-auto mt-4 text-red-500 flex justify-center items-center gap-2">
<Icon icon=phosphor_leptos::X_CIRCLE size="64px" />
</div>
}
}
_ => {
view! {
<div class="mx-auto mt-4 text-red-500 flex justify-center items-center gap-2">
<Icon icon=phosphor_leptos::X_CIRCLE size="64px" />
</div>
}
}
}}
<Button href=format!("/order/{}", paymentid()) class="mt-8">
"View Order Details"
</Button>
</div>
}
}