add order validations for shipping

This commit is contained in:
creating2morrow 2023-06-05 06:07:01 -04:00
parent 3255b15c9a
commit 60786f23d4
7 changed files with 123 additions and 63 deletions

View file

@ -236,8 +236,8 @@ pub struct Product {
pub image: Vec<u8>,
pub in_stock: bool,
pub name: String,
pub price: i64,
pub qty: i64,
pub price: u64,
pub qty: u64,
}
impl Default for Product {
@ -272,11 +272,11 @@ impl Product {
Err(_) => false,
};
let name = v.remove(0);
let price = match v.remove(0).parse::<i64>() {
let price = match v.remove(0).parse::<u64>() {
Ok(p) => p,
Err(_) => 0,
};
let qty = match v.remove(0).parse::<i64>() {
let qty = match v.remove(0).parse::<u64>() {
Ok(q) => q,
Err(_) => 0,
};

View file

@ -4,7 +4,7 @@ use crate::{
models::*,
monero,
reqres,
utils,
utils, product, gpg, i2p,
};
use log::{
debug,
@ -15,16 +15,14 @@ use rocket::serde::json::Json;
/*
TODOs(c2m):
- API to validate payment and import multisig info, update to multisig complete
- API to upload gpg encrypted tracking number, update order to shipped
- release tracking (locker code?) when txset is released, update to delivered
*/
enum StatusType {
_Delivered,
MultisigMissing,
_MulitsigComplete,
_Shipped,
MulitsigComplete,
Shipped,
}
impl StatusType {
@ -32,8 +30,8 @@ impl StatusType {
match *self {
StatusType::_Delivered => String::from("Delivered"),
StatusType::MultisigMissing => String::from("MultisigMissing"),
StatusType::_MulitsigComplete => String::from("MulitsigComplete"),
StatusType::_Shipped => String::from("Shipped"),
StatusType::MulitsigComplete => String::from("MulitsigComplete"),
StatusType::Shipped => String::from("Shipped"),
}
}
}
@ -175,7 +173,7 @@ pub async fn sign_and_submit_multisig(
/// that the mediator can see order id for disputes without being able to access
///
/// the details of said order.
pub async fn retrieve_order(orid: &String, signature: &String) -> Order {
pub async fn secure_retrieval(orid: &String, signature: &String) -> Order {
// get customer address for NEVEKO NOT order wallet
let m_order: Order = find(&orid);
let mut xmr_address: String = String::new();
@ -183,6 +181,7 @@ pub async fn retrieve_order(orid: &String, signature: &String) -> Order {
for customer in a_customers {
if customer.i2p_address == m_order.cid {
xmr_address = customer.xmr_address;
break;
}
}
// send address, orid and signature to verify()
@ -195,12 +194,57 @@ pub async fn retrieve_order(orid: &String, signature: &String) -> Order {
m_order
}
pub async fn validate_order_for_ship() -> bool {
/// Check for import multisig info, validate block time and that the
///
/// order wallet has been funded properly. Update the order to multisig complete
pub async fn validate_order_for_ship(orid: &String) -> bool {
info!("validating order for shipment");
let mut m_order: Order = find(orid);
let m_product: Product = product::find(&m_order.pid);
let price = m_product.price;
let total = price * m_order.quantity;
// import multisig info
let s = db::Interface::open();
let key = format!("export-{}-{}", orid, &m_order.cid);
let info_str = db::Interface::async_read(&s.env, &s.handle, &key).await;
let info_split = info_str.split(":");
let v_info: Vec<String> = info_split.map(|s| String::from(s)).collect();
let wallet_password = utils::empty_string();
monero::open_wallet(&orid, &wallet_password).await;
let r_import = monero::import_multisig_info(v_info).await;
// check balance and unlock_time
let r_balance = monero::get_balance().await;
// update the order status to multisig complete
return false;
let ready_to_ship: bool = r_import.result.n_outputs > 0
&& r_balance.result.balance >= total as u128
&& r_balance.result.blocks_to_unlock < monero::LockTimeLimit::Blocks.value();
if ready_to_ship {
m_order.status = StatusType::MulitsigComplete.value();
db::Interface::async_delete(&s.env, &s.handle, &m_order.orid).await;
db::Interface::async_write(&s.env, &s.handle, &m_order.orid, &Order::to_db(&m_order)).await;
}
ready_to_ship
}
/// Write encrypted delivery info to lmdb. Once the customer releases the signed txset
///
/// they will have access to this information (tracking number, locker code, etc.)
pub async fn upload_delivery_info(orid: &String, delivery_info: &Vec<u8>) {
info!("uploading delivery info");
let name = i2p::get_destination(None);
let e_delivery_info: Vec<u8> = gpg::encrypt(name, &delivery_info)
.unwrap_or(Vec::new());
if e_delivery_info.is_empty() {
error!("unable to encrypt delivery info");
}
// write delivery info {delivery}-{order id}
let s = db::Interface::async_open().await;
let k = format!("delivery-{}", orid);
let data = hex::encode(e_delivery_info);
db::Interface::async_write(&s.env, &s.handle, &k, &data).await;
// update the order
let mut m_order: Order = find(orid);
m_order.status = StatusType::Shipped.value();
db::Interface::async_delete(&s.env, &s.handle, &m_order.orid).await;
db::Interface::async_write(&s.env, &s.handle, &m_order.orid, &Order::to_db(&m_order)).await;
}

View file

@ -368,7 +368,7 @@ pub struct SubAddressInfo {
pub label: String,
pub num_unspent_outputs: u64,
pub time_to_unlock: u128,
pub blocks_to_unlock: u128,
pub blocks_to_unlock: u64,
}
#[derive(Deserialize, Debug)]
@ -391,7 +391,7 @@ pub struct XmrRpcBalanceResult {
pub unlocked_balance: u128,
pub multisig_import_needed: bool,
pub time_to_unlock: u128,
pub blocks_to_unlock: u128,
pub blocks_to_unlock: u64,
pub per_subaddress: Vec<SubAddressInfo>,
}

View file

@ -12,7 +12,7 @@ pub const LOCK_SCREEN_TIMEOUT_SECS: u64 = 60 * 5;
/// interval to search for credential on initial gui load
pub const CRED_CHECK_INTERVAL: u64 = 5;
/// monero estimated block time in seconds
pub const BLOCK_TIME_IN_SECS_EST: u128 = 0x78;
pub const BLOCK_TIME_IN_SECS_EST: u64 = 0x78;
/// time to wait before giving up on adding a contact
pub const ADD_CONTACT_TIMEOUT_SECS: u64 = 0x5A;
/// time to wait before giving up on neveko core

View file

@ -52,7 +52,7 @@ pub async fn get_order(orid: String, _token: auth::BearerToken) -> Custom<Json<m
Custom(Status::Ok, Json(m_order))
}
/// Get a order by passing id
/// Get all orders
#[get("/")]
pub async fn get_orders(_token: auth::BearerToken) -> Custom<Json<Vec<models::Order>>> {
let m_orders: Vec<models::Order> = order::find_all();
@ -86,7 +86,7 @@ pub async fn get_dispute(_token: auth::BearerToken, did: String) -> Custom<Json<
Custom(Status::Ok, Json(m_dispute))
}
/// Create a dispute
/// Sign and submit multisig
#[post("/sign/submit", data = "<r_data>")]
pub async fn sign_and_submit_multisig(
r_data: Json<reqres::SignAndSubmitRequest>,

View file

@ -3,7 +3,7 @@ use rocket::{
http::Status,
post,
response::status::Custom,
serde::json::Json,
serde::json::Json, catch,
};
use neveko_core::*;
@ -109,7 +109,7 @@ pub async fn retrieve_order(
signature: String,
_jwp: proof::PaymentProof,
) -> Custom<Json<models::Order>> {
let m_order = order::retrieve_order(&orid, &signature).await;
let m_order = order::secure_retrieval(&orid, &signature).await;
if m_order.cid == utils::empty_string() {
return Custom(Status::BadRequest, Json(Default::default()));
}
@ -156,8 +156,55 @@ pub async fn rx_multisig_message(
/// multisig info, check balance and sanity check `unlock_time`.
///
/// Protected: true
#[post("/")]
pub async fn request_shipment(_jwp: proof::PaymentProof) -> Custom<Json<models::Message>> {
order::validate_order_for_ship().await;
#[post("/<orid>")]
pub async fn request_shipment(orid: String, _jwp: proof::PaymentProof) -> Custom<Json<models::Message>> {
let is_ready: bool = order::validate_order_for_ship(&orid).await;
if !is_ready {
return Custom(Status::BadRequest, Json(Default::default()));
}
Custom(Status::Ok, Json(Default::default()))
}
/// Create a dispute (customer)
#[post("/create", data = "<dispute>")]
pub async fn create_dispute(
dispute: Json<models::Dispute>,
_token: auth::BearerToken,
) -> Custom<Json<models::Dispute>> {
let m_dispute: models::Dispute = dispute::create(dispute);
Custom(Status::Ok, Json(m_dispute))
}
// Catchers
//----------------------------------------------------------------
#[catch(402)]
pub fn payment_required() -> Custom<Json<reqres::ErrorResponse>> {
Custom(
Status::PaymentRequired,
Json(reqres::ErrorResponse {
error: String::from("Payment required"),
}),
)
}
#[catch(404)]
pub fn not_found() -> Custom<Json<reqres::ErrorResponse>> {
Custom(
Status::NotFound,
Json(reqres::ErrorResponse {
error: String::from("Resource does not exist"),
}),
)
}
#[catch(500)]
pub fn internal_error() -> Custom<Json<reqres::ErrorResponse>> {
Custom(
Status::InternalServerError,
Json(reqres::ErrorResponse {
error: String::from("Internal server error"),
}),
)
}

View file

@ -1,44 +1,9 @@
#[macro_use]
extern crate rocket;
use rocket::{
http::Status,
response::status::Custom,
serde::json::Json,
};
use neveko::*;
use neveko_core::*;
#[catch(402)]
fn payment_required() -> Custom<Json<reqres::ErrorResponse>> {
Custom(
Status::PaymentRequired,
Json(reqres::ErrorResponse {
error: String::from("Payment required"),
}),
)
}
#[catch(404)]
fn not_found() -> Custom<Json<reqres::ErrorResponse>> {
Custom(
Status::NotFound,
Json(reqres::ErrorResponse {
error: String::from("Resource does not exist"),
}),
)
}
#[catch(500)]
fn internal_error() -> Custom<Json<reqres::ErrorResponse>> {
Custom(
Status::InternalServerError,
Json(reqres::ErrorResponse {
error: String::from("Internal server error"),
}),
)
}
// The only changes below here should be mounting new controller methods
#[launch]
async fn rocket() -> _ {
@ -51,7 +16,11 @@ async fn rocket() -> _ {
env_logger::init();
utils::start_up().await;
rocket::custom(&config)
.register("/", catchers![internal_error, not_found, payment_required])
.register("/", catchers![
controller::internal_error,
controller::not_found,
controller::payment_required
])
.mount("/multisig/info", routes![controller::get_multisig_info])
.mount("/invoice", routes![controller::gen_invoice])
.mount("/message/rx", routes![controller::rx_message])
@ -65,6 +34,6 @@ async fn rocket() -> _ {
.mount("/xmr/rpc", routes![controller::get_version])
.mount(
"/market",
routes![controller::create_order, controller::get_products],
routes![controller::create_order, controller::get_products, controller::create_dispute],
)
}