mirror of
https://github.com/creating2morrow/neveko.git
synced 2024-12-22 19:49:24 +00:00
add cancel order flow - untested
This commit is contained in:
parent
92ccd3836e
commit
bb492fce8c
7 changed files with 208 additions and 27 deletions
|
@ -1,3 +1,5 @@
|
|||
#![allow(non_snake_case)]
|
||||
|
||||
use rocket::{
|
||||
get,
|
||||
http::Status,
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#![allow(non_snake_case)]
|
||||
|
||||
use rocket::{
|
||||
get,
|
||||
http::Status,
|
||||
|
|
|
@ -5,15 +5,11 @@ use crate::{
|
|||
db,
|
||||
gpg,
|
||||
i2p,
|
||||
|
||||
models::*,
|
||||
monero,
|
||||
order,
|
||||
product,
|
||||
reqres::{
|
||||
self,
|
||||
FinalizeOrderResponse,
|
||||
},
|
||||
reqres,
|
||||
utils,
|
||||
};
|
||||
use log::{
|
||||
|
@ -24,7 +20,7 @@ use log::{
|
|||
use rocket::serde::json::Json;
|
||||
|
||||
pub enum StatusType {
|
||||
_Cancelled,
|
||||
Cancelled,
|
||||
Delivered,
|
||||
MultisigMissing,
|
||||
MulitsigComplete,
|
||||
|
@ -34,7 +30,7 @@ pub enum StatusType {
|
|||
impl StatusType {
|
||||
pub fn value(&self) -> String {
|
||||
match *self {
|
||||
StatusType::_Cancelled => String::from("Cancelled"),
|
||||
StatusType::Cancelled => String::from("Cancelled"),
|
||||
StatusType::Delivered => String::from("Delivered"),
|
||||
StatusType::MultisigMissing => String::from("MultisigMissing"),
|
||||
StatusType::MulitsigComplete => String::from("MulitsigComplete"),
|
||||
|
@ -163,7 +159,10 @@ pub fn find_all_backup() -> Vec<Order> {
|
|||
let mut orders: Vec<Order> = Vec::new();
|
||||
for o in i_v {
|
||||
let order: Order = find(&o);
|
||||
if order.orid != utils::empty_string() {
|
||||
let visible = order.orid != utils::empty_string()
|
||||
&& order.status != order::StatusType::Delivered.value()
|
||||
&& order.status != order::StatusType::Cancelled.value();
|
||||
if visible {
|
||||
orders.push(order);
|
||||
}
|
||||
}
|
||||
|
@ -256,6 +255,39 @@ pub async fn secure_retrieval(orid: &String, signature: &String) -> Order {
|
|||
m_order
|
||||
}
|
||||
|
||||
/// In order for the order (...ha) to only be cancelled by the customer
|
||||
///
|
||||
/// they must sign the order id with their NEVEKO wallet instance.
|
||||
pub async fn cancel_order(orid: &String, signature: &String) -> Order {
|
||||
info!("cancel order {}", orid);
|
||||
// get customer address for NEVEKO NOT order wallet
|
||||
let mut m_order: Order = find(&orid);
|
||||
let mut xmr_address: String = String::new();
|
||||
let a_customers: Vec<Contact> = contact::find_all();
|
||||
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()
|
||||
let id: String = String::from(&m_order.orid);
|
||||
let sig: String = String::from(signature);
|
||||
let wallet_password =
|
||||
std::env::var(crate::MONERO_WALLET_PASSWORD).unwrap_or(String::from("password"));
|
||||
let wallet_name = String::from(crate::APP_NAME);
|
||||
monero::open_wallet(&wallet_name, &wallet_password).await;
|
||||
let is_valid_signature = monero::verify(xmr_address, id, sig).await;
|
||||
monero::close_wallet(&wallet_name, &wallet_password).await;
|
||||
if !is_valid_signature {
|
||||
return Default::default();
|
||||
}
|
||||
// update the order status and send to customer
|
||||
m_order.status = order::StatusType::Cancelled.value();
|
||||
order::modify(Json(m_order));
|
||||
order::find(&orid)
|
||||
}
|
||||
|
||||
/// Check for import multisig info, validate block time and that the
|
||||
///
|
||||
/// order wallet has been funded properly. Update the order to multisig complete
|
||||
|
@ -375,7 +407,7 @@ pub async fn upload_delivery_info(
|
|||
if nasr_order.is_err() {
|
||||
return Default::default();
|
||||
}
|
||||
FinalizeOrderResponse {
|
||||
reqres::FinalizeOrderResponse {
|
||||
delivery_info: delivery_info.to_vec(),
|
||||
orid: String::from(orid),
|
||||
vendor_update_success: false,
|
||||
|
@ -390,7 +422,6 @@ pub async fn upload_delivery_info(
|
|||
pub async fn finalize_order(orid: &String) -> reqres::FinalizeOrderResponse {
|
||||
info!("finalizing order: {}", orid);
|
||||
|
||||
|
||||
reqres::FinalizeOrderResponse {
|
||||
..Default::default()
|
||||
}
|
||||
|
@ -433,7 +464,7 @@ pub async fn transmit_ship_request(
|
|||
contact: &String,
|
||||
jwp: &String,
|
||||
orid: &String,
|
||||
) -> Result<FinalizeOrderResponse, Box<dyn Error>> {
|
||||
) -> Result<reqres::FinalizeOrderResponse, Box<dyn Error>> {
|
||||
info!("executing transmit_ship_request");
|
||||
let host = utils::get_i2p_http_proxy();
|
||||
let proxy = reqwest::Proxy::http(&host)?;
|
||||
|
@ -445,7 +476,7 @@ pub async fn transmit_ship_request(
|
|||
.await
|
||||
{
|
||||
Ok(response) => {
|
||||
let res = response.json::<FinalizeOrderResponse>().await;
|
||||
let res = response.json::<reqres::FinalizeOrderResponse>().await;
|
||||
debug!("ship request response: {:?}", res);
|
||||
match res {
|
||||
Ok(r) => Ok(r),
|
||||
|
@ -520,6 +551,28 @@ pub async fn trigger_ship_request(contact: &String, jwp: &String, orid: &String)
|
|||
unwrap_order
|
||||
}
|
||||
|
||||
/// A post-decomposition trigger for the cancel request so that the logic
|
||||
///
|
||||
/// can be executed from the gui.
|
||||
pub async fn trigger_cancel_request(contact: &String, jwp: &String, orid: &String) -> Order {
|
||||
info!("executing trigger_cancel_request");
|
||||
let data = String::from(orid);
|
||||
let wallet_password =
|
||||
std::env::var(crate::MONERO_WALLET_PASSWORD).unwrap_or(String::from("password"));
|
||||
monero::open_wallet(&String::from(crate::APP_NAME), &wallet_password).await;
|
||||
let pre_sign = monero::sign(data).await;
|
||||
monero::close_wallet(&String::from(crate::APP_NAME), &wallet_password).await;
|
||||
let order = transmit_cancel_request(contact, jwp, orid, &pre_sign.result.signature).await;
|
||||
// cache order request to db
|
||||
if order.is_err() {
|
||||
log::error!("failed to trigger cancel request");
|
||||
return Default::default();
|
||||
}
|
||||
let unwrap_order: Order = order.unwrap();
|
||||
backup(&unwrap_order);
|
||||
unwrap_order
|
||||
}
|
||||
|
||||
/// Decomposition trigger for the shipping request
|
||||
pub async fn d_trigger_ship_request(contact: &String, orid: &String) -> Order {
|
||||
// ugh, sorry seems we need to get jwp for vendor from fts cache
|
||||
|
@ -544,6 +597,66 @@ pub async fn d_trigger_ship_request(contact: &String, orid: &String) -> Order {
|
|||
trigger
|
||||
}
|
||||
|
||||
/// Executes POST /order/cancel/orid/signature
|
||||
///
|
||||
/// cancelling the order on the vendor side.
|
||||
///
|
||||
/// Customer needs to verify the response and update their lmdb.
|
||||
///
|
||||
/// see `cancel_order`
|
||||
pub async fn transmit_cancel_request(
|
||||
contact: &String,
|
||||
jwp: &String,
|
||||
orid: &String,
|
||||
signature: &String,
|
||||
) -> Result<Order, Box<dyn Error>> {
|
||||
info!("executing transmit_cancel_request");
|
||||
let host = utils::get_i2p_http_proxy();
|
||||
let proxy = reqwest::Proxy::http(&host)?;
|
||||
let client = reqwest::Client::builder().proxy(proxy).build();
|
||||
match client?
|
||||
.post(format!(
|
||||
"http://{}/market/order/cancel/{}/{}",
|
||||
contact, orid, signature
|
||||
))
|
||||
.header("proof", jwp)
|
||||
.send()
|
||||
.await
|
||||
{
|
||||
Ok(response) => {
|
||||
let res = response.json::<Order>().await;
|
||||
debug!("cancel order response: {:?}", res);
|
||||
match res {
|
||||
Ok(r) => Ok(r),
|
||||
_ => Ok(Default::default()),
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
error!("failed to cancel order due to: {:?}", e);
|
||||
Ok(Default::default())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Decomposition trigger for the shipping request
|
||||
pub async fn d_trigger_cancel_request(contact: &String, orid: &String) -> Order {
|
||||
// ugh, sorry seems we need to get jwp for vendor from fts cache
|
||||
// get jwp from db
|
||||
let s = db::Interface::async_open().await;
|
||||
let k = format!("{}-{}", crate::FTS_JWP_DB_KEY, &contact);
|
||||
let jwp = db::Interface::async_read(&s.env, &s.handle, &k).await;
|
||||
info!("executing d_trigger_cancel_request");
|
||||
// request cancel if the order status is not MultisigComplete
|
||||
let order: Order = order::find(&orid);
|
||||
if order.status != order::StatusType::MulitsigComplete.value() {
|
||||
let trigger = trigger_cancel_request(contact, &jwp, orid).await;
|
||||
if trigger.status == order::StatusType::Cancelled.value() {
|
||||
return trigger;
|
||||
}
|
||||
}
|
||||
Default::default()
|
||||
}
|
||||
|
||||
pub async fn init_mediator_wallet(orid: &String) {
|
||||
let password = utils::empty_string();
|
||||
let m_wallet = monero::create_wallet(orid, &password).await;
|
||||
|
|
|
@ -113,6 +113,8 @@ pub struct MarketApp {
|
|||
submit_order_rx: Receiver<models::Order>,
|
||||
_submit_txset_tx: Sender<bool>,
|
||||
_submit_txset_rx: Receiver<bool>,
|
||||
cancel_request_tx: Sender<models::Order>,
|
||||
cancel_request_rx: Receiver<models::Order>,
|
||||
// ship_request_tx: Sender<models::Order>,
|
||||
// ship_request_rx: Receiver<models::Order>,
|
||||
s_contact: models::Contact,
|
||||
|
@ -137,6 +139,7 @@ impl Default for MarketApp {
|
|||
let (get_vendor_product_tx, get_vendor_product_rx) = std::sync::mpsc::channel();
|
||||
let (submit_order_tx, submit_order_rx) = std::sync::mpsc::channel();
|
||||
// let (ship_request_tx, ship_request_rx) = std::sync::mpsc::channel();
|
||||
let (cancel_request_tx, cancel_request_rx) = std::sync::mpsc::channel();
|
||||
let (our_prepare_info_tx, our_prepare_info_rx) = std::sync::mpsc::channel();
|
||||
let (our_make_info_tx, our_make_info_rx) = std::sync::mpsc::channel();
|
||||
let (order_xmr_address_tx, order_xmr_address_rx) = std::sync::mpsc::channel();
|
||||
|
@ -208,6 +211,8 @@ impl Default for MarketApp {
|
|||
_refresh_on_delete_product_rx,
|
||||
s_contact: Default::default(),
|
||||
s_order: Default::default(),
|
||||
cancel_request_rx,
|
||||
cancel_request_tx,
|
||||
// ship_request_rx,
|
||||
// ship_request_tx,
|
||||
submit_order_rx,
|
||||
|
@ -320,6 +325,13 @@ impl eframe::App for MarketApp {
|
|||
// self.is_loading = false;
|
||||
// }
|
||||
|
||||
if let Ok(cancelled) = self.cancel_request_rx.try_recv() {
|
||||
if cancelled.status != order::StatusType::Cancelled.value() {
|
||||
log::error!("failure to cancel shipment please contact vendor")
|
||||
}
|
||||
self.is_loading = false;
|
||||
}
|
||||
|
||||
// Vendor status window
|
||||
//-----------------------------------------------------------------------------------
|
||||
let mut is_showing_vendor_status = self.is_showing_vendor_status;
|
||||
|
@ -693,7 +705,7 @@ impl eframe::App for MarketApp {
|
|||
utils::empty_string(),
|
||||
ctx.clone(),
|
||||
utils::empty_string(),
|
||||
self._submit_txset_tx.clone()
|
||||
self._submit_txset_tx.clone(),
|
||||
);
|
||||
}
|
||||
if ui.button("Check").clicked() {
|
||||
|
@ -833,11 +845,13 @@ impl eframe::App for MarketApp {
|
|||
if ui.button("DINFO").clicked() {
|
||||
let e_info: String = utils::search_gui_db(
|
||||
String::from(neveko_core::DELIVERY_INFO_DB_KEY),
|
||||
String::from(&o.orid)
|
||||
String::from(&o.orid),
|
||||
);
|
||||
let bytes = hex::decode(e_info.into_bytes()).unwrap_or(Vec::new());
|
||||
let bytes =
|
||||
hex::decode(e_info.into_bytes()).unwrap_or(Vec::new());
|
||||
let d_bytes = gpg::decrypt(&String::from(&o.orid), &bytes);
|
||||
let dinfo: String = String::from_utf8(d_bytes.unwrap_or(Vec::new()))
|
||||
let dinfo: String =
|
||||
String::from_utf8(d_bytes.unwrap_or(Vec::new()))
|
||||
.unwrap_or(utils::empty_string());
|
||||
self.decrypted_delivery_info = dinfo;
|
||||
self.is_showing_decrypted_delivery_info = true;
|
||||
|
@ -847,7 +861,18 @@ impl eframe::App for MarketApp {
|
|||
ui.style_mut().wrap = Some(false);
|
||||
ui.horizontal(|ui| {
|
||||
if ui.button("Cancel").clicked() {
|
||||
// TODO(c2m): Cancel order logic
|
||||
let vendor_prefix = String::from(crate::GUI_OVL_DB_KEY);
|
||||
let vendor = utils::search_gui_db(
|
||||
vendor_prefix,
|
||||
self.m_order.orid.clone(),
|
||||
);
|
||||
self.is_loading = true;
|
||||
cancel_order_req(
|
||||
self.cancel_request_tx.clone(),
|
||||
ctx.clone(),
|
||||
&self.m_order.orid,
|
||||
&vendor,
|
||||
)
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -1704,7 +1729,6 @@ fn send_prepare_info_req(
|
|||
);
|
||||
// Request mediator and vendor while we're at it
|
||||
// Will coordinating send this on make requests next
|
||||
|
||||
let s = db::Interface::async_open().await;
|
||||
let m_msig_key = format!(
|
||||
"{}-{}-{}",
|
||||
|
@ -2217,7 +2241,6 @@ fn send_import_info_req(tx: Sender<String>, ctx: egui::Context, orid: &String, v
|
|||
ctx.request_repaint();
|
||||
}
|
||||
|
||||
|
||||
// TODO(c2m): do we need a manual trigger for the shipping request?
|
||||
|
||||
// fn shipping_req(
|
||||
|
@ -2239,6 +2262,23 @@ fn send_import_info_req(tx: Sender<String>, ctx: egui::Context, orid: &String, v
|
|||
// }
|
||||
// End Async fn requests
|
||||
|
||||
fn cancel_order_req(
|
||||
tx: Sender<models::Order>,
|
||||
ctx: egui::Context,
|
||||
orid: &String,
|
||||
contact: &String,
|
||||
) {
|
||||
let ship_orid = String::from(orid);
|
||||
let vendor_i2p = String::from(contact);
|
||||
tokio::spawn(async move {
|
||||
log::info!("cancel order req: {}", ship_orid);
|
||||
let order = order::d_trigger_cancel_request(&vendor_i2p, &ship_orid).await;
|
||||
let _ = tx.send(order);
|
||||
ctx.request_repaint();
|
||||
});
|
||||
}
|
||||
// End Async fn requests
|
||||
|
||||
fn validate_msig_step(
|
||||
mediator: &String,
|
||||
orid: &String,
|
||||
|
@ -2271,7 +2311,7 @@ fn release_txset(
|
|||
let _ = tx.send(false);
|
||||
return;
|
||||
}
|
||||
// TODO(next commit): we need to build an API that tells the vendor
|
||||
// TODO(c2m): we need to build an API that tells the vendor
|
||||
// to verify the txset was submitted successfully
|
||||
// return boolean.
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#![allow(non_snake_case)]
|
||||
|
||||
use rocket::{
|
||||
get,
|
||||
http::Status,
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#![allow(non_snake_case)]
|
||||
|
||||
use rocket::{
|
||||
get,
|
||||
http::Status,
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#![allow(non_snake_case)]
|
||||
|
||||
use rocket::{
|
||||
catch,
|
||||
get,
|
||||
|
@ -219,6 +221,24 @@ pub async fn trigger_nasr(
|
|||
Custom(Status::Ok, Json(order))
|
||||
}
|
||||
|
||||
/// Customer cancel order logic. Must send `signature`
|
||||
///
|
||||
/// which is the order id signed by the NEVEKO wallet.
|
||||
///
|
||||
/// Protected: true
|
||||
#[post("/order/cancel/<orid>/<signature>")]
|
||||
pub async fn cancel_order(
|
||||
orid: String,
|
||||
signature: String,
|
||||
_jwp: proof::PaymentProof,
|
||||
) -> Custom<Json<models::Order>> {
|
||||
let m_order = order::cancel_order(&orid, &signature).await;
|
||||
if m_order.cid == utils::empty_string() {
|
||||
return Custom(Status::BadRequest, Json(Default::default()));
|
||||
}
|
||||
Custom(Status::Created, Json(m_order))
|
||||
}
|
||||
|
||||
/// Create a dispute (customer)
|
||||
#[post("/create", data = "<dispute>")]
|
||||
pub async fn create_dispute(
|
||||
|
|
Loading…
Reference in a new issue