1use crate::prelude::*;
11use nostr_sdk::{PublicKey, Timestamp};
12use serde::{Deserialize, Serialize};
13#[cfg(feature = "sqlx")]
14use sqlx::FromRow;
15#[cfg(feature = "sqlx")]
16use sqlx_crud::SqlxCrud;
17use std::{fmt::Display, str::FromStr};
18use uuid::Uuid;
19use wasm_bindgen::prelude::*;
20
21#[wasm_bindgen]
23#[derive(Debug, Deserialize, Serialize, Clone, Copy, PartialEq, Eq)]
24#[serde(rename_all = "kebab-case")]
25pub enum Kind {
26 Buy,
28 Sell,
30}
31
32impl FromStr for Kind {
33 type Err = ();
34
35 fn from_str(kind: &str) -> std::result::Result<Self, Self::Err> {
39 match kind.to_lowercase().as_str() {
40 "buy" => std::result::Result::Ok(Self::Buy),
41 "sell" => std::result::Result::Ok(Self::Sell),
42 _ => Err(()),
43 }
44 }
45}
46
47impl Display for Kind {
48 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49 match self {
50 Kind::Sell => write!(f, "sell"),
51 Kind::Buy => write!(f, "buy"),
52 }
53 }
54}
55
56#[wasm_bindgen]
61#[derive(Debug, Deserialize, Serialize, Clone, Copy, PartialEq, Eq)]
62#[serde(rename_all = "kebab-case")]
63pub enum Status {
64 Active,
66 Canceled,
68 CanceledByAdmin,
70 SettledByAdmin,
72 CompletedByAdmin,
74 Dispute,
76 Expired,
78 FiatSent,
80 SettledHoldInvoice,
82 Pending,
84 Success,
86 WaitingBuyerInvoice,
88 WaitingPayment,
90 WaitingTakerBond,
95 CooperativelyCanceled,
97 InProgress,
99 WaitingMakerBond,
109}
110
111impl Display for Status {
112 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
113 match self {
114 Status::Active => write!(f, "active"),
115 Status::Canceled => write!(f, "canceled"),
116 Status::CanceledByAdmin => write!(f, "canceled-by-admin"),
117 Status::SettledByAdmin => write!(f, "settled-by-admin"),
118 Status::CompletedByAdmin => write!(f, "completed-by-admin"),
119 Status::Dispute => write!(f, "dispute"),
120 Status::Expired => write!(f, "expired"),
121 Status::FiatSent => write!(f, "fiat-sent"),
122 Status::SettledHoldInvoice => write!(f, "settled-hold-invoice"),
123 Status::Pending => write!(f, "pending"),
124 Status::Success => write!(f, "success"),
125 Status::WaitingBuyerInvoice => write!(f, "waiting-buyer-invoice"),
126 Status::WaitingPayment => write!(f, "waiting-payment"),
127 Status::WaitingTakerBond => write!(f, "waiting-taker-bond"),
128 Status::WaitingMakerBond => write!(f, "waiting-maker-bond"),
129 Status::CooperativelyCanceled => write!(f, "cooperatively-canceled"),
130 Status::InProgress => write!(f, "in-progress"),
131 }
132 }
133}
134
135impl FromStr for Status {
136 type Err = ();
137 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
139 match s.to_lowercase().as_str() {
140 "active" => std::result::Result::Ok(Self::Active),
141 "canceled" => std::result::Result::Ok(Self::Canceled),
142 "canceled-by-admin" => std::result::Result::Ok(Self::CanceledByAdmin),
143 "settled-by-admin" => std::result::Result::Ok(Self::SettledByAdmin),
144 "completed-by-admin" => std::result::Result::Ok(Self::CompletedByAdmin),
145 "dispute" => std::result::Result::Ok(Self::Dispute),
146 "expired" => std::result::Result::Ok(Self::Expired),
147 "fiat-sent" => std::result::Result::Ok(Self::FiatSent),
148 "settled-hold-invoice" => std::result::Result::Ok(Self::SettledHoldInvoice),
149 "pending" => std::result::Result::Ok(Self::Pending),
150 "success" => std::result::Result::Ok(Self::Success),
151 "waiting-buyer-invoice" => std::result::Result::Ok(Self::WaitingBuyerInvoice),
152 "waiting-payment" => std::result::Result::Ok(Self::WaitingPayment),
153 "waiting-taker-bond" => std::result::Result::Ok(Self::WaitingTakerBond),
154 "waiting-maker-bond" => std::result::Result::Ok(Self::WaitingMakerBond),
155 "cooperatively-canceled" => std::result::Result::Ok(Self::CooperativelyCanceled),
156 "in-progress" => std::result::Result::Ok(Self::InProgress),
157 _ => Err(()),
158 }
159 }
160}
161#[cfg_attr(feature = "sqlx", derive(FromRow, SqlxCrud), external_id)]
170#[derive(Debug, Default, Deserialize, Serialize, Clone)]
171pub struct Order {
172 pub id: Uuid,
174 pub kind: String,
177 pub event_id: String,
179 pub hash: Option<String>,
181 pub preimage: Option<String>,
183 pub creator_pubkey: String,
185 pub cancel_initiator_pubkey: Option<String>,
187 pub buyer_pubkey: Option<String>,
189 pub master_buyer_pubkey: Option<String>,
192 pub seller_pubkey: Option<String>,
194 pub master_seller_pubkey: Option<String>,
197 pub status: String,
199 pub price_from_api: bool,
201 pub premium: i64,
203 pub payment_method: String,
205 pub amount: i64,
208 pub min_amount: Option<i64>,
210 pub max_amount: Option<i64>,
212 pub buyer_dispute: bool,
214 pub seller_dispute: bool,
216 pub buyer_cooperativecancel: bool,
218 pub seller_cooperativecancel: bool,
220 pub fee: i64,
222 pub routing_fee: i64,
224 pub dev_fee: i64,
226 pub dev_fee_paid: bool,
228 pub dev_fee_payment_hash: Option<String>,
230 pub fiat_code: String,
232 pub fiat_amount: i64,
234 pub buyer_invoice: Option<String>,
236 pub range_parent_id: Option<Uuid>,
238 pub invoice_held_at: i64,
240 pub taken_at: i64,
242 pub created_at: i64,
244 pub buyer_sent_rate: bool,
246 pub seller_sent_rate: bool,
248 pub failed_payment: bool,
250 pub payment_attempts: i64,
252 pub expires_at: i64,
254 pub trade_index_seller: Option<i64>,
256 pub trade_index_buyer: Option<i64>,
258 pub next_trade_pubkey: Option<String>,
261 pub next_trade_index: Option<i64>,
263 pub cashu_mint_url: Option<String>,
266 pub cashu_escrow_token: Option<String>,
269 pub cashu_escrow_locked_at: Option<i64>,
272}
273
274impl From<SmallOrder> for Order {
275 fn from(small_order: SmallOrder) -> Self {
276 Self {
277 id: Uuid::new_v4(),
278 kind: small_order
280 .kind
281 .map_or_else(|| Kind::Buy.to_string(), |k| k.to_string()),
282 status: small_order
283 .status
284 .map_or_else(|| Status::Active.to_string(), |s| s.to_string()),
285 amount: small_order.amount,
286 fiat_code: small_order.fiat_code,
287 min_amount: small_order.min_amount,
288 max_amount: small_order.max_amount,
289 fiat_amount: small_order.fiat_amount,
290 payment_method: small_order.payment_method,
291 premium: small_order.premium,
292 event_id: String::new(),
293 creator_pubkey: String::new(),
294 price_from_api: false,
295 fee: 0,
296 routing_fee: 0,
297 dev_fee: 0,
298 dev_fee_paid: false,
299 dev_fee_payment_hash: None,
300 invoice_held_at: 0,
301 taken_at: 0,
302 created_at: small_order.created_at.unwrap_or(0),
303 expires_at: small_order.expires_at.unwrap_or(0),
304 payment_attempts: 0,
305 ..Default::default()
306 }
307 }
308}
309
310impl Order {
311 pub fn as_new_order(&self) -> SmallOrder {
317 SmallOrder::new(
318 Some(self.id),
319 Some(Kind::from_str(&self.kind).unwrap()),
320 Some(Status::from_str(&self.status).unwrap()),
321 self.amount,
322 self.fiat_code.clone(),
323 self.min_amount,
324 self.max_amount,
325 self.fiat_amount,
326 self.payment_method.clone(),
327 self.premium,
328 None,
329 None,
330 self.buyer_invoice.clone(),
331 Some(self.created_at),
332 Some(self.expires_at),
333 )
334 }
335 pub fn get_order_kind(&self) -> Result<Kind, ServiceError> {
340 if let Ok(kind) = Kind::from_str(&self.kind) {
341 Ok(kind)
342 } else {
343 Err(ServiceError::InvalidOrderKind)
344 }
345 }
346
347 pub fn get_order_status(&self) -> Result<Status, ServiceError> {
352 if let Ok(status) = Status::from_str(&self.status) {
353 Ok(status)
354 } else {
355 Err(ServiceError::InvalidOrderStatus)
356 }
357 }
358
359 pub fn check_status(&self, status: Status) -> Result<(), CantDoReason> {
364 match Status::from_str(&self.status) {
365 Ok(s) => match s == status {
366 true => Ok(()),
367 false => Err(CantDoReason::InvalidOrderStatus),
368 },
369 Err(_) => Err(CantDoReason::InvalidOrderStatus),
370 }
371 }
372
373 pub fn is_buy_order(&self) -> Result<(), CantDoReason> {
375 if self.kind != Kind::Buy.to_string() {
376 return Err(CantDoReason::InvalidOrderKind);
377 }
378 Ok(())
379 }
380 pub fn is_sell_order(&self) -> Result<(), CantDoReason> {
382 if self.kind != Kind::Sell.to_string() {
383 return Err(CantDoReason::InvalidOrderKind);
384 }
385 Ok(())
386 }
387
388 pub fn sent_from_maker(&self, sender: PublicKey) -> Result<(), CantDoReason> {
392 let sender = sender.to_string();
393 if self.creator_pubkey != sender {
394 return Err(CantDoReason::InvalidPubkey);
395 }
396 Ok(())
397 }
398
399 pub fn not_sent_from_maker(&self, sender: PublicKey) -> Result<(), CantDoReason> {
404 let sender = sender.to_string();
405 if self.creator_pubkey == sender {
406 return Err(CantDoReason::InvalidPubkey);
407 }
408 Ok(())
409 }
410
411 pub fn get_creator_pubkey(&self) -> Result<PublicKey, ServiceError> {
413 match PublicKey::from_str(self.creator_pubkey.as_ref()) {
414 Ok(pk) => Ok(pk),
415 Err(_) => Err(ServiceError::InvalidPubkey),
416 }
417 }
418
419 pub fn get_buyer_pubkey(&self) -> Result<PublicKey, ServiceError> {
424 if let Some(pk) = self.buyer_pubkey.as_ref() {
425 PublicKey::from_str(pk).map_err(|_| ServiceError::InvalidPubkey)
426 } else {
427 Err(ServiceError::InvalidPubkey)
428 }
429 }
430 pub fn get_seller_pubkey(&self) -> Result<PublicKey, ServiceError> {
435 if let Some(pk) = self.seller_pubkey.as_ref() {
436 PublicKey::from_str(pk).map_err(|_| ServiceError::InvalidPubkey)
437 } else {
438 Err(ServiceError::InvalidPubkey)
439 }
440 }
441 pub fn get_master_buyer_pubkey(&self) -> Result<PublicKey, ServiceError> {
443 if let Some(pk) = self.master_buyer_pubkey.as_ref() {
444 PublicKey::from_str(pk).map_err(|_| ServiceError::InvalidPubkey)
445 } else {
446 Err(ServiceError::InvalidPubkey)
447 }
448 }
449 pub fn get_master_seller_pubkey(&self) -> Result<PublicKey, ServiceError> {
451 if let Some(pk) = self.master_seller_pubkey.as_ref() {
452 PublicKey::from_str(pk).map_err(|_| ServiceError::InvalidPubkey)
453 } else {
454 Err(ServiceError::InvalidPubkey)
455 }
456 }
457
458 pub fn is_range_order(&self) -> bool {
461 self.min_amount.is_some() && self.max_amount.is_some()
462 }
463
464 pub fn count_failed_payment(&mut self, retries_number: i64) {
470 if !self.failed_payment {
471 self.failed_payment = true;
472 self.payment_attempts = 1;
473 } else if self.payment_attempts < retries_number {
474 self.payment_attempts += 1;
475 }
476 }
477
478 pub fn has_no_amount(&self) -> bool {
481 self.amount == 0
482 }
483
484 pub fn set_timestamp_now(&mut self) {
486 self.taken_at = Timestamp::now().as_secs() as i64
487 }
488
489 pub fn is_full_privacy_order(&self) -> Result<(Option<String>, Option<String>), ServiceError> {
498 let (mut normal_buyer_idkey, mut normal_seller_idkey) = (None, None);
499
500 let master_buyer_pubkey = self.get_master_buyer_pubkey().ok();
502 let master_seller_pubkey = self.get_master_seller_pubkey().ok();
503
504 if self.buyer_pubkey != master_buyer_pubkey.map(|pk| pk.to_string()) {
506 normal_buyer_idkey = master_buyer_pubkey.map(|pk| pk.to_string());
507 }
508
509 if self.seller_pubkey != master_seller_pubkey.map(|pk| pk.to_string()) {
511 normal_seller_idkey = master_seller_pubkey.map(|pk| pk.to_string());
512 }
513
514 Ok((normal_buyer_idkey, normal_seller_idkey))
515 }
516 pub fn setup_dispute(&mut self, is_buyer_dispute: bool) -> Result<(), CantDoReason> {
524 let is_seller_dispute = !is_buyer_dispute;
526
527 let mut update_seller_dispute = false;
529 let mut update_buyer_dispute = false;
530
531 if is_seller_dispute && !self.seller_dispute {
532 update_seller_dispute = true;
533 self.seller_dispute = update_seller_dispute;
534 } else if is_buyer_dispute && !self.buyer_dispute {
535 update_buyer_dispute = true;
536 self.buyer_dispute = update_buyer_dispute;
537 };
538 self.status = Status::Dispute.to_string();
540
541 if !update_buyer_dispute && !update_seller_dispute {
544 return Err(CantDoReason::DisputeCreationError);
545 }
546
547 Ok(())
548 }
549}
550
551#[derive(Debug, Default, Deserialize, Serialize, Clone)]
560#[serde(deny_unknown_fields)]
561pub struct SmallOrder {
562 #[serde(skip_serializing_if = "Option::is_none")]
564 pub id: Option<Uuid>,
565 pub kind: Option<Kind>,
567 pub status: Option<Status>,
569 pub amount: i64,
572 pub fiat_code: String,
574 pub min_amount: Option<i64>,
576 pub max_amount: Option<i64>,
578 pub fiat_amount: i64,
580 pub payment_method: String,
582 pub premium: i64,
584 #[serde(skip_serializing_if = "Option::is_none")]
586 pub buyer_trade_pubkey: Option<String>,
587 #[serde(skip_serializing_if = "Option::is_none")]
589 pub seller_trade_pubkey: Option<String>,
590 #[serde(skip_serializing_if = "Option::is_none")]
592 pub buyer_invoice: Option<String>,
593 pub created_at: Option<i64>,
595 pub expires_at: Option<i64>,
597}
598
599#[allow(dead_code)]
600impl SmallOrder {
601 #[allow(clippy::too_many_arguments)]
603 pub fn new(
604 id: Option<Uuid>,
605 kind: Option<Kind>,
606 status: Option<Status>,
607 amount: i64,
608 fiat_code: String,
609 min_amount: Option<i64>,
610 max_amount: Option<i64>,
611 fiat_amount: i64,
612 payment_method: String,
613 premium: i64,
614 buyer_trade_pubkey: Option<String>,
615 seller_trade_pubkey: Option<String>,
616 buyer_invoice: Option<String>,
617 created_at: Option<i64>,
618 expires_at: Option<i64>,
619 ) -> Self {
620 Self {
621 id,
622 kind,
623 status,
624 amount,
625 fiat_code,
626 min_amount,
627 max_amount,
628 fiat_amount,
629 payment_method,
630 premium,
631 buyer_trade_pubkey,
632 seller_trade_pubkey,
633 buyer_invoice,
634 created_at,
635 expires_at,
636 }
637 }
638 pub fn from_json(json: &str) -> Result<Self, ServiceError> {
640 serde_json::from_str(json).map_err(|_| ServiceError::MessageSerializationError)
641 }
642
643 pub fn as_json(&self) -> Result<String, ServiceError> {
645 serde_json::to_string(&self).map_err(|_| ServiceError::MessageSerializationError)
646 }
647
648 pub fn sats_amount(&self) -> String {
651 if self.amount == 0 {
652 "Market price".to_string()
653 } else {
654 self.amount.to_string()
655 }
656 }
657 pub fn check_fiat_amount(&self) -> Result<(), CantDoReason> {
661 if self.fiat_amount <= 0 {
662 return Err(CantDoReason::InvalidAmount);
663 }
664 Ok(())
665 }
666
667 pub fn check_amount(&self) -> Result<(), CantDoReason> {
674 if self.amount < 0 {
675 return Err(CantDoReason::InvalidAmount);
676 }
677 Ok(())
678 }
679
680 pub fn check_zero_amount_with_premium(&self) -> Result<(), CantDoReason> {
686 let premium = (self.premium != 0).then_some(self.premium);
687 let sats_amount = (self.amount != 0).then_some(self.amount);
688
689 if premium.is_some() && sats_amount.is_some() {
690 return Err(CantDoReason::InvalidParameters);
691 }
692 Ok(())
693 }
694
695 pub fn check_range_order_limits(&self, amounts: &mut Vec<i64>) -> Result<(), CantDoReason> {
703 if let (Some(min), Some(max)) = (self.min_amount, self.max_amount) {
705 if min < 0 || max < 0 {
706 return Err(CantDoReason::InvalidAmount);
707 }
708 if min >= max {
709 return Err(CantDoReason::InvalidAmount);
710 }
711 if self.amount != 0 {
712 return Err(CantDoReason::InvalidAmount);
713 }
714 amounts.clear();
715 amounts.push(min);
716 amounts.push(max);
717 }
718 Ok(())
719 }
720
721 pub fn check_fiat_currency(
728 &self,
729 fiat_currencies_accepted: &[String],
730 ) -> Result<(), CantDoReason> {
731 if !fiat_currencies_accepted.contains(&self.fiat_code)
732 && !fiat_currencies_accepted.is_empty()
733 {
734 return Err(CantDoReason::InvalidFiatCurrency);
735 }
736 Ok(())
737 }
738}
739
740impl From<Order> for SmallOrder {
741 fn from(order: Order) -> Self {
742 let id = Some(order.id);
743 let kind = Kind::from_str(&order.kind).unwrap();
744 let status = Status::from_str(&order.status).unwrap();
745 let amount = order.amount;
746 let fiat_code = order.fiat_code.clone();
747 let min_amount = order.min_amount;
748 let max_amount = order.max_amount;
749 let fiat_amount = order.fiat_amount;
750 let payment_method = order.payment_method.clone();
751 let premium = order.premium;
752 let buyer_trade_pubkey = order.buyer_pubkey.clone();
753 let seller_trade_pubkey = order.seller_pubkey.clone();
754 let buyer_invoice = order.buyer_invoice.clone();
755
756 Self {
757 id,
758 kind: Some(kind),
759 status: Some(status),
760 amount,
761 fiat_code,
762 min_amount,
763 max_amount,
764 fiat_amount,
765 payment_method,
766 premium,
767 buyer_trade_pubkey,
768 seller_trade_pubkey,
769 buyer_invoice,
770 created_at: Some(order.created_at),
771 expires_at: Some(order.expires_at),
772 }
773 }
774}
775
776#[cfg(test)]
777mod tests {
778 use super::*;
779 use crate::error::CantDoReason;
780 use nostr_sdk::Keys;
781 use uuid::uuid;
782
783 #[test]
784 fn test_status_string() {
785 assert_eq!(Status::Active.to_string(), "active");
786 assert_eq!(Status::CompletedByAdmin.to_string(), "completed-by-admin");
787 assert_eq!(Status::FiatSent.to_string(), "fiat-sent");
788 assert_ne!(Status::Pending.to_string(), "Pending");
789 }
790
791 #[test]
792 fn test_status_waiting_taker_bond_roundtrip() {
793 assert_eq!(Status::WaitingTakerBond.to_string(), "waiting-taker-bond");
794 assert_eq!(
795 Status::from_str("waiting-taker-bond").unwrap(),
796 Status::WaitingTakerBond
797 );
798 let json = serde_json::to_string(&Status::WaitingTakerBond).unwrap();
800 assert_eq!(json, "\"waiting-taker-bond\"");
801 let back: Status = serde_json::from_str(&json).unwrap();
802 assert_eq!(back, Status::WaitingTakerBond);
803 }
804
805 #[test]
806 fn test_status_waiting_maker_bond_roundtrip() {
807 assert_eq!(Status::WaitingMakerBond.to_string(), "waiting-maker-bond");
808 assert_eq!(
809 Status::from_str("waiting-maker-bond").unwrap(),
810 Status::WaitingMakerBond
811 );
812 let json = serde_json::to_string(&Status::WaitingMakerBond).unwrap();
814 assert_eq!(json, "\"waiting-maker-bond\"");
815 let back: Status = serde_json::from_str(&json).unwrap();
816 assert_eq!(back, Status::WaitingMakerBond);
817 }
818
819 #[test]
820 fn test_kind_string() {
821 assert_ne!(Kind::Sell.to_string(), "active");
822 assert_eq!(Kind::Sell.to_string(), "sell");
823 assert_eq!(Kind::Buy.to_string(), "buy");
824 assert_ne!(Kind::Buy.to_string(), "active");
825 }
826
827 #[test]
828 fn test_order_message() {
829 let uuid = uuid!("308e1272-d5f4-47e6-bd97-3504baea9c23");
830 let payment_methods = "SEPA,Bank transfer".to_string();
831 let payload = Payload::Order(SmallOrder::new(
832 Some(uuid),
833 Some(Kind::Sell),
834 Some(Status::Pending),
835 100,
836 "eur".to_string(),
837 None,
838 None,
839 100,
840 payment_methods,
841 1,
842 None,
843 None,
844 None,
845 Some(1627371434),
846 None,
847 ));
848
849 let test_message = Message::Order(MessageKind::new(
850 Some(uuid),
851 Some(1),
852 Some(2),
853 Action::NewOrder,
854 Some(payload),
855 ));
856 let test_message_json = test_message.as_json().unwrap();
857 let sample_message = r#"{"order":{"version":1,"id":"308e1272-d5f4-47e6-bd97-3504baea9c23","request_id":1,"trade_index":2,"action":"new-order","payload":{"order":{"id":"308e1272-d5f4-47e6-bd97-3504baea9c23","kind":"sell","status":"pending","amount":100,"fiat_code":"eur","fiat_amount":100,"payment_method":"SEPA,Bank transfer","premium":1,"created_at":1627371434}}}}"#;
858 let message = Message::from_json(sample_message).unwrap();
859 assert!(message.verify());
860 let message_json = message.as_json().unwrap();
861 assert_eq!(message_json, test_message_json);
862 }
863
864 #[test]
865 fn test_payment_request_payload_message() {
866 let uuid = uuid!("308e1272-d5f4-47e6-bd97-3504baea9c23");
867 let test_message = Message::Order(MessageKind::new(
868 Some(uuid),
869 Some(1),
870 Some(3),
871 Action::PayInvoice,
872 Some(Payload::PaymentRequest(
873 Some(SmallOrder::new(
874 Some(uuid),
875 Some(Kind::Sell),
876 Some(Status::WaitingPayment),
877 100,
878 "eur".to_string(),
879 None,
880 None,
881 100,
882 "Face to face".to_string(),
883 1,
884 None,
885 None,
886 None,
887 Some(1627371434),
888 None,
889 )),
890 "lnbcrt78510n1pj59wmepp50677g8tffdqa2p8882y0x6newny5vtz0hjuyngdwv226nanv4uzsdqqcqzzsxqyz5vqsp5skn973360gp4yhlpmefwvul5hs58lkkl3u3ujvt57elmp4zugp4q9qyyssqw4nzlr72w28k4waycf27qvgzc9sp79sqlw83j56txltz4va44j7jda23ydcujj9y5k6k0rn5ms84w8wmcmcyk5g3mhpqepf7envhdccp72nz6e".to_string(),
891 None,
892 )),
893 ));
894 let sample_message = r#"{"order":{"version":1,"id":"308e1272-d5f4-47e6-bd97-3504baea9c23","request_id":1,"trade_index":3,"action":"pay-invoice","payload":{"payment_request":[{"id":"308e1272-d5f4-47e6-bd97-3504baea9c23","kind":"sell","status":"waiting-payment","amount":100,"fiat_code":"eur","fiat_amount":100,"payment_method":"Face to face","premium":1,"created_at":1627371434},"lnbcrt78510n1pj59wmepp50677g8tffdqa2p8882y0x6newny5vtz0hjuyngdwv226nanv4uzsdqqcqzzsxqyz5vqsp5skn973360gp4yhlpmefwvul5hs58lkkl3u3ujvt57elmp4zugp4q9qyyssqw4nzlr72w28k4waycf27qvgzc9sp79sqlw83j56txltz4va44j7jda23ydcujj9y5k6k0rn5ms84w8wmcmcyk5g3mhpqepf7envhdccp72nz6e",null]}}}"#;
895 let message = Message::from_json(sample_message).unwrap();
896 assert!(message.verify());
897 let message_json = message.as_json().unwrap();
898 let test_message_json = test_message.as_json().unwrap();
899 assert_eq!(message_json, test_message_json);
900 }
901
902 #[test]
903 fn test_message_payload_signature() {
904 let uuid = uuid!("308e1272-d5f4-47e6-bd97-3504baea9c23");
905 let peer = Peer::new(
906 "npub1testjsf0runcqdht5apkfcalajxkf8txdxqqk5kgm0agc38ke4vsfsgzf8".to_string(),
907 None,
908 );
909 let payload = Payload::Peer(peer);
910 let test_message = Message::Order(MessageKind::new(
911 Some(uuid),
912 Some(1),
913 Some(2),
914 Action::FiatSentOk,
915 Some(payload),
916 ));
917 assert!(test_message.verify());
918 let test_message_json = test_message.as_json().unwrap();
919 let trade_keys =
921 Keys::parse("110e43647eae221ab1da33ddc17fd6ff423f2b2f49d809b9ffa40794a2ab996c")
922 .unwrap();
923 let sig = Message::sign(test_message_json.clone(), &trade_keys);
924
925 assert!(Message::verify_signature(
926 test_message_json,
927 trade_keys.public_key(),
928 sig
929 ));
930 }
931
932 #[test]
933 fn test_cant_do_message_serialization() {
934 let reasons = vec![
936 CantDoReason::InvalidSignature,
937 CantDoReason::InvalidTradeIndex,
938 CantDoReason::InvalidAmount,
939 CantDoReason::InvalidInvoice,
940 CantDoReason::InvalidPaymentRequest,
941 CantDoReason::InvalidPeer,
942 CantDoReason::InvalidRating,
943 CantDoReason::InvalidTextMessage,
944 CantDoReason::InvalidOrderStatus,
945 CantDoReason::InvalidPubkey,
946 CantDoReason::InvalidParameters,
947 CantDoReason::OrderAlreadyCanceled,
948 CantDoReason::CantCreateUser,
949 CantDoReason::IsNotYourOrder,
950 CantDoReason::NotAllowedByStatus,
951 CantDoReason::OutOfRangeFiatAmount,
952 CantDoReason::OutOfRangeSatsAmount,
953 CantDoReason::IsNotYourDispute,
954 CantDoReason::NotFound,
955 CantDoReason::InvalidFiatCurrency,
956 CantDoReason::TooManyRequests,
957 ];
958
959 for reason in reasons {
960 let cant_do = Message::CantDo(MessageKind::new(
961 None,
962 None,
963 None,
964 Action::CantDo,
965 Some(Payload::CantDo(Some(reason.clone()))),
966 ));
967 let message = Message::from_json(&cant_do.as_json().unwrap()).unwrap();
968 assert!(message.verify());
969 assert_eq!(message.as_json().unwrap(), cant_do.as_json().unwrap());
970 }
971
972 let cant_do = Message::CantDo(MessageKind::new(
974 None,
975 None,
976 None,
977 Action::CantDo,
978 Some(Payload::CantDo(None)),
979 ));
980 let message = Message::from_json(&cant_do.as_json().unwrap()).unwrap();
981 assert!(message.verify());
982 assert_eq!(message.as_json().unwrap(), cant_do.as_json().unwrap());
983 }
984
985 #[test]
988 fn test_check_fiat_amount_valid() {
989 let order = SmallOrder::new(
991 None,
992 None,
993 None,
994 100,
995 "VES".to_string(),
996 None,
997 None,
998 500,
999 "Bank".to_string(),
1000 1,
1001 None,
1002 None,
1003 None,
1004 None,
1005 None,
1006 );
1007 assert!(order.check_fiat_amount().is_ok());
1008 }
1009
1010 #[test]
1011 fn test_check_fiat_amount_zero() {
1012 let order = SmallOrder::new(
1013 None,
1014 None,
1015 None,
1016 100,
1017 "VES".to_string(),
1018 None,
1019 None,
1020 0,
1021 "Bank".to_string(),
1022 1,
1023 None,
1024 None,
1025 None,
1026 None,
1027 None,
1028 );
1029 let result = order.check_fiat_amount();
1030 assert!(result.is_err());
1031 assert_eq!(result.unwrap_err(), CantDoReason::InvalidAmount);
1032 }
1033
1034 #[test]
1035 fn test_check_fiat_amount_negative() {
1036 let order = SmallOrder::new(
1037 None,
1038 None,
1039 None,
1040 100,
1041 "VES".to_string(),
1042 None,
1043 None,
1044 -100,
1045 "Bank".to_string(),
1046 1,
1047 None,
1048 None,
1049 None,
1050 None,
1051 None,
1052 );
1053 let result = order.check_fiat_amount();
1054 assert!(result.is_err());
1055 assert_eq!(result.unwrap_err(), CantDoReason::InvalidAmount);
1056 }
1057
1058 #[test]
1061 fn test_check_amount_valid() {
1062 let order = SmallOrder::new(
1064 None,
1065 None,
1066 None,
1067 100000,
1068 "VES".to_string(),
1069 None,
1070 None,
1071 500,
1072 "Bank".to_string(),
1073 0,
1074 None,
1075 None,
1076 None,
1077 None,
1078 None,
1079 );
1080 assert!(order.check_amount().is_ok());
1081 }
1082
1083 #[test]
1084 fn test_check_amount_zero() {
1085 let order = SmallOrder::new(
1087 None,
1088 None,
1089 None,
1090 0,
1091 "VES".to_string(),
1092 None,
1093 None,
1094 500,
1095 "Bank".to_string(),
1096 0,
1097 None,
1098 None,
1099 None,
1100 None,
1101 None,
1102 );
1103 assert!(order.check_amount().is_ok());
1104 }
1105
1106 #[test]
1107 fn test_check_amount_negative() {
1108 let order = SmallOrder::new(
1110 None,
1111 None,
1112 None,
1113 -1000,
1114 "VES".to_string(),
1115 None,
1116 None,
1117 500,
1118 "Bank".to_string(),
1119 0,
1120 None,
1121 None,
1122 None,
1123 None,
1124 None,
1125 );
1126 let result = order.check_amount();
1127 assert!(result.is_err());
1128 assert_eq!(result.unwrap_err(), CantDoReason::InvalidAmount);
1129 }
1130}