mirror of
https://github.com/creating2morrow/neveko.git
synced 2025-01-18 08:44:46 +00:00
add order validations for shipping
This commit is contained in:
parent
3255b15c9a
commit
60786f23d4
7 changed files with 123 additions and 63 deletions
|
@ -236,8 +236,8 @@ pub struct Product {
|
||||||
pub image: Vec<u8>,
|
pub image: Vec<u8>,
|
||||||
pub in_stock: bool,
|
pub in_stock: bool,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub price: i64,
|
pub price: u64,
|
||||||
pub qty: i64,
|
pub qty: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Product {
|
impl Default for Product {
|
||||||
|
@ -272,11 +272,11 @@ impl Product {
|
||||||
Err(_) => false,
|
Err(_) => false,
|
||||||
};
|
};
|
||||||
let name = v.remove(0);
|
let name = v.remove(0);
|
||||||
let price = match v.remove(0).parse::<i64>() {
|
let price = match v.remove(0).parse::<u64>() {
|
||||||
Ok(p) => p,
|
Ok(p) => p,
|
||||||
Err(_) => 0,
|
Err(_) => 0,
|
||||||
};
|
};
|
||||||
let qty = match v.remove(0).parse::<i64>() {
|
let qty = match v.remove(0).parse::<u64>() {
|
||||||
Ok(q) => q,
|
Ok(q) => q,
|
||||||
Err(_) => 0,
|
Err(_) => 0,
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,7 +4,7 @@ use crate::{
|
||||||
models::*,
|
models::*,
|
||||||
monero,
|
monero,
|
||||||
reqres,
|
reqres,
|
||||||
utils,
|
utils, product, gpg, i2p,
|
||||||
};
|
};
|
||||||
use log::{
|
use log::{
|
||||||
debug,
|
debug,
|
||||||
|
@ -15,16 +15,14 @@ use rocket::serde::json::Json;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
TODOs(c2m):
|
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
|
- release tracking (locker code?) when txset is released, update to delivered
|
||||||
*/
|
*/
|
||||||
|
|
||||||
enum StatusType {
|
enum StatusType {
|
||||||
_Delivered,
|
_Delivered,
|
||||||
MultisigMissing,
|
MultisigMissing,
|
||||||
_MulitsigComplete,
|
MulitsigComplete,
|
||||||
_Shipped,
|
Shipped,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StatusType {
|
impl StatusType {
|
||||||
|
@ -32,8 +30,8 @@ impl StatusType {
|
||||||
match *self {
|
match *self {
|
||||||
StatusType::_Delivered => String::from("Delivered"),
|
StatusType::_Delivered => String::from("Delivered"),
|
||||||
StatusType::MultisigMissing => String::from("MultisigMissing"),
|
StatusType::MultisigMissing => String::from("MultisigMissing"),
|
||||||
StatusType::_MulitsigComplete => String::from("MulitsigComplete"),
|
StatusType::MulitsigComplete => String::from("MulitsigComplete"),
|
||||||
StatusType::_Shipped => String::from("Shipped"),
|
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
|
/// that the mediator can see order id for disputes without being able to access
|
||||||
///
|
///
|
||||||
/// the details of said order.
|
/// 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
|
// get customer address for NEVEKO NOT order wallet
|
||||||
let m_order: Order = find(&orid);
|
let m_order: Order = find(&orid);
|
||||||
let mut xmr_address: String = String::new();
|
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 {
|
for customer in a_customers {
|
||||||
if customer.i2p_address == m_order.cid {
|
if customer.i2p_address == m_order.cid {
|
||||||
xmr_address = customer.xmr_address;
|
xmr_address = customer.xmr_address;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// send address, orid and signature to verify()
|
// send address, orid and signature to verify()
|
||||||
|
@ -195,12 +194,57 @@ pub async fn retrieve_order(orid: &String, signature: &String) -> Order {
|
||||||
m_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");
|
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
|
// 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
|
// check balance and unlock_time
|
||||||
|
let r_balance = monero::get_balance().await;
|
||||||
// update the order status to multisig complete
|
// 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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -368,7 +368,7 @@ pub struct SubAddressInfo {
|
||||||
pub label: String,
|
pub label: String,
|
||||||
pub num_unspent_outputs: u64,
|
pub num_unspent_outputs: u64,
|
||||||
pub time_to_unlock: u128,
|
pub time_to_unlock: u128,
|
||||||
pub blocks_to_unlock: u128,
|
pub blocks_to_unlock: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
|
@ -391,7 +391,7 @@ pub struct XmrRpcBalanceResult {
|
||||||
pub unlocked_balance: u128,
|
pub unlocked_balance: u128,
|
||||||
pub multisig_import_needed: bool,
|
pub multisig_import_needed: bool,
|
||||||
pub time_to_unlock: u128,
|
pub time_to_unlock: u128,
|
||||||
pub blocks_to_unlock: u128,
|
pub blocks_to_unlock: u64,
|
||||||
pub per_subaddress: Vec<SubAddressInfo>,
|
pub per_subaddress: Vec<SubAddressInfo>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ pub const LOCK_SCREEN_TIMEOUT_SECS: u64 = 60 * 5;
|
||||||
/// interval to search for credential on initial gui load
|
/// interval to search for credential on initial gui load
|
||||||
pub const CRED_CHECK_INTERVAL: u64 = 5;
|
pub const CRED_CHECK_INTERVAL: u64 = 5;
|
||||||
/// monero estimated block time in seconds
|
/// 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
|
/// time to wait before giving up on adding a contact
|
||||||
pub const ADD_CONTACT_TIMEOUT_SECS: u64 = 0x5A;
|
pub const ADD_CONTACT_TIMEOUT_SECS: u64 = 0x5A;
|
||||||
/// time to wait before giving up on neveko core
|
/// time to wait before giving up on neveko core
|
||||||
|
|
|
@ -52,7 +52,7 @@ pub async fn get_order(orid: String, _token: auth::BearerToken) -> Custom<Json<m
|
||||||
Custom(Status::Ok, Json(m_order))
|
Custom(Status::Ok, Json(m_order))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a order by passing id
|
/// Get all orders
|
||||||
#[get("/")]
|
#[get("/")]
|
||||||
pub async fn get_orders(_token: auth::BearerToken) -> Custom<Json<Vec<models::Order>>> {
|
pub async fn get_orders(_token: auth::BearerToken) -> Custom<Json<Vec<models::Order>>> {
|
||||||
let m_orders: Vec<models::Order> = order::find_all();
|
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))
|
Custom(Status::Ok, Json(m_dispute))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a dispute
|
/// Sign and submit multisig
|
||||||
#[post("/sign/submit", data = "<r_data>")]
|
#[post("/sign/submit", data = "<r_data>")]
|
||||||
pub async fn sign_and_submit_multisig(
|
pub async fn sign_and_submit_multisig(
|
||||||
r_data: Json<reqres::SignAndSubmitRequest>,
|
r_data: Json<reqres::SignAndSubmitRequest>,
|
||||||
|
|
|
@ -3,7 +3,7 @@ use rocket::{
|
||||||
http::Status,
|
http::Status,
|
||||||
post,
|
post,
|
||||||
response::status::Custom,
|
response::status::Custom,
|
||||||
serde::json::Json,
|
serde::json::Json, catch,
|
||||||
};
|
};
|
||||||
|
|
||||||
use neveko_core::*;
|
use neveko_core::*;
|
||||||
|
@ -109,7 +109,7 @@ pub async fn retrieve_order(
|
||||||
signature: String,
|
signature: String,
|
||||||
_jwp: proof::PaymentProof,
|
_jwp: proof::PaymentProof,
|
||||||
) -> Custom<Json<models::Order>> {
|
) -> 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() {
|
if m_order.cid == utils::empty_string() {
|
||||||
return Custom(Status::BadRequest, Json(Default::default()));
|
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`.
|
/// multisig info, check balance and sanity check `unlock_time`.
|
||||||
///
|
///
|
||||||
/// Protected: true
|
/// Protected: true
|
||||||
#[post("/")]
|
#[post("/<orid>")]
|
||||||
pub async fn request_shipment(_jwp: proof::PaymentProof) -> Custom<Json<models::Message>> {
|
pub async fn request_shipment(orid: String, _jwp: proof::PaymentProof) -> Custom<Json<models::Message>> {
|
||||||
order::validate_order_for_ship().await;
|
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()))
|
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"),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
43
src/main.rs
43
src/main.rs
|
@ -1,44 +1,9 @@
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate rocket;
|
extern crate rocket;
|
||||||
use rocket::{
|
|
||||||
http::Status,
|
|
||||||
response::status::Custom,
|
|
||||||
serde::json::Json,
|
|
||||||
};
|
|
||||||
|
|
||||||
use neveko::*;
|
use neveko::*;
|
||||||
use neveko_core::*;
|
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
|
// The only changes below here should be mounting new controller methods
|
||||||
#[launch]
|
#[launch]
|
||||||
async fn rocket() -> _ {
|
async fn rocket() -> _ {
|
||||||
|
@ -51,7 +16,11 @@ async fn rocket() -> _ {
|
||||||
env_logger::init();
|
env_logger::init();
|
||||||
utils::start_up().await;
|
utils::start_up().await;
|
||||||
rocket::custom(&config)
|
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("/multisig/info", routes![controller::get_multisig_info])
|
||||||
.mount("/invoice", routes![controller::gen_invoice])
|
.mount("/invoice", routes![controller::gen_invoice])
|
||||||
.mount("/message/rx", routes![controller::rx_message])
|
.mount("/message/rx", routes![controller::rx_message])
|
||||||
|
@ -65,6 +34,6 @@ async fn rocket() -> _ {
|
||||||
.mount("/xmr/rpc", routes![controller::get_version])
|
.mount("/xmr/rpc", routes![controller::get_version])
|
||||||
.mount(
|
.mount(
|
||||||
"/market",
|
"/market",
|
||||||
routes![controller::create_order, controller::get_products],
|
routes![controller::create_order, controller::get_products, controller::create_dispute],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue