mostro_client/cli/
add_invoice.rs

1use crate::util::{send_dm, wait_for_dm};
2use crate::{cli::Context, db::Order, lightning::is_valid_invoice};
3use anyhow::Result;
4use lnurl::lightning_address::LightningAddress;
5use mostro_core::prelude::*;
6use nostr_sdk::prelude::*;
7use std::str::FromStr;
8use uuid::Uuid;
9
10pub async fn execute_add_invoice(order_id: &Uuid, invoice: &str, ctx: &Context) -> Result<()> {
11    let order = Order::get_by_id(&ctx.pool, &order_id.to_string()).await?;
12    let trade_keys = order
13        .trade_keys
14        .clone()
15        .ok_or(anyhow::anyhow!("Missing trade keys"))?;
16    let order_trade_keys = Keys::parse(&trade_keys)?;
17    println!(
18        "Order trade keys: {:?}",
19        order_trade_keys.public_key().to_hex()
20    );
21
22    println!(
23        "Sending a lightning invoice for order {} to mostro pubId {}",
24        order_id, ctx.mostro_pubkey
25    );
26    // Check invoice string
27    let ln_addr = LightningAddress::from_str(invoice);
28    let payload = if ln_addr.is_ok() {
29        Some(Payload::PaymentRequest(None, invoice.to_string(), None))
30    } else {
31        match is_valid_invoice(invoice) {
32            Ok(i) => Some(Payload::PaymentRequest(None, i.to_string(), None)),
33            Err(e) => {
34                return Err(anyhow::anyhow!("Invalid invoice: {}", e));
35            }
36        }
37    };
38
39    // Create request id
40    let request_id = Uuid::new_v4().as_u128() as u64;
41    // Create AddInvoice message
42    let add_invoice_message = Message::new_order(
43        Some(*order_id),
44        Some(request_id),
45        None,
46        Action::AddInvoice,
47        payload,
48    );
49
50    // Serialize the message
51    let message_json = add_invoice_message
52        .as_json()
53        .map_err(|_| anyhow::anyhow!("Failed to serialize message"))?;
54
55    // Subscribe to gift wrap events - ONLY NEW ONES WITH LIMIT 0
56    let subscription = Filter::new()
57        .pubkey(order_trade_keys.clone().public_key())
58        .kind(nostr_sdk::Kind::GiftWrap)
59        .limit(0);
60
61    let opts = SubscribeAutoCloseOptions::default().exit_policy(ReqExitPolicy::WaitForEvents(1));
62    ctx.client.subscribe(subscription, Some(opts)).await?;
63
64    // Clone the keys and client for the async call
65    let identity_keys_clone = ctx.identity_keys.clone();
66    let client_clone = ctx.client.clone();
67    let mostro_pubkey_clone = ctx.mostro_pubkey;
68    let order_trade_keys_clone = order_trade_keys.clone();
69
70    // Spawn a new task to send the DM
71    // This is so we can wait for the gift wrap event in the main thread
72    tokio::spawn(async move {
73        let _ = send_dm(
74            &client_clone,
75            Some(&identity_keys_clone),
76            &order_trade_keys,
77            &mostro_pubkey_clone,
78            message_json,
79            None,
80            false,
81        )
82        .await;
83    });
84
85    // Wait for the DM to be sent from mostro and update the order
86    wait_for_dm(
87        &ctx.client,
88        &order_trade_keys_clone,
89        request_id,
90        None,
91        Some(order),
92        &ctx.pool,
93    )
94    .await?;
95    Ok(())
96}