Add check-expired-invoice command
This commit is contained in:
parent
109168b5eb
commit
fde8309bb9
9 changed files with 115 additions and 13 deletions
|
@ -85,3 +85,9 @@ Create Monero wallet:
|
||||||
```shell
|
```shell
|
||||||
mitractl create-monero-wallet "mitra-wallet" "passw0rd"
|
mitractl create-monero-wallet "mitra-wallet" "passw0rd"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Check expired invoice:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
mitractl check-expired-invoice 0184b062-d8d5-cbf1-a71b-6d1aafbae2ab
|
||||||
|
```
|
||||||
|
|
|
@ -36,6 +36,7 @@ async fn main() {
|
||||||
SubCommand::UpdateCurrentBlock(cmd) => cmd.execute(&config, db_client).await.unwrap(),
|
SubCommand::UpdateCurrentBlock(cmd) => cmd.execute(&config, db_client).await.unwrap(),
|
||||||
SubCommand::ResetSubscriptions(cmd) => cmd.execute(&config, db_client).await.unwrap(),
|
SubCommand::ResetSubscriptions(cmd) => cmd.execute(&config, db_client).await.unwrap(),
|
||||||
SubCommand::CreateMoneroWallet(cmd) => cmd.execute(&config).await.unwrap(),
|
SubCommand::CreateMoneroWallet(cmd) => cmd.execute(&config).await.unwrap(),
|
||||||
|
SubCommand::CheckExpiredInvoice(cmd) => cmd.execute(&config, db_client).await.unwrap(),
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
30
src/cli.rs
30
src/cli.rs
|
@ -29,7 +29,10 @@ use crate::models::users::queries::{
|
||||||
get_user_by_id,
|
get_user_by_id,
|
||||||
set_user_password,
|
set_user_password,
|
||||||
};
|
};
|
||||||
use crate::monero::wallet::create_monero_wallet;
|
use crate::monero::{
|
||||||
|
helpers::check_expired_invoice,
|
||||||
|
wallet::create_monero_wallet,
|
||||||
|
};
|
||||||
use crate::utils::{
|
use crate::utils::{
|
||||||
crypto_rsa::{
|
crypto_rsa::{
|
||||||
generate_rsa_key,
|
generate_rsa_key,
|
||||||
|
@ -64,6 +67,7 @@ pub enum SubCommand {
|
||||||
UpdateCurrentBlock(UpdateCurrentBlock),
|
UpdateCurrentBlock(UpdateCurrentBlock),
|
||||||
ResetSubscriptions(ResetSubscriptions),
|
ResetSubscriptions(ResetSubscriptions),
|
||||||
CreateMoneroWallet(CreateMoneroWallet),
|
CreateMoneroWallet(CreateMoneroWallet),
|
||||||
|
CheckExpiredInvoice(CheckExpiredInvoice),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate RSA private key
|
/// Generate RSA private key
|
||||||
|
@ -404,3 +408,27 @@ impl CreateMoneroWallet {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check expired invoice
|
||||||
|
#[derive(Parser)]
|
||||||
|
pub struct CheckExpiredInvoice {
|
||||||
|
id: Uuid,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CheckExpiredInvoice {
|
||||||
|
pub async fn execute(
|
||||||
|
&self,
|
||||||
|
config: &Config,
|
||||||
|
db_client: &impl GenericClient,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let monero_config = config.blockchain()
|
||||||
|
.and_then(|conf| conf.monero_config())
|
||||||
|
.ok_or(anyhow!("monero configuration not found"))?;
|
||||||
|
check_expired_invoice(
|
||||||
|
monero_config,
|
||||||
|
db_client,
|
||||||
|
&self.id,
|
||||||
|
).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -156,6 +156,6 @@ mod tests {
|
||||||
assert_eq!(invoice.chain_id, chain_id);
|
assert_eq!(invoice.chain_id, chain_id);
|
||||||
assert_eq!(invoice.payment_address, payment_address);
|
assert_eq!(invoice.payment_address, payment_address);
|
||||||
assert_eq!(invoice.amount, amount);
|
assert_eq!(invoice.amount, amount);
|
||||||
assert!(matches!(invoice.invoice_status, InvoiceStatus::Open));
|
assert_eq!(invoice.invoice_status, InvoiceStatus::Open);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ use crate::database::int_enum::{int_enum_from_sql, int_enum_to_sql};
|
||||||
use crate::errors::ConversionError;
|
use crate::errors::ConversionError;
|
||||||
use crate::utils::caip2::ChainId;
|
use crate::utils::caip2::ChainId;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum InvoiceStatus {
|
pub enum InvoiceStatus {
|
||||||
Open,
|
Open,
|
||||||
Paid,
|
Paid,
|
||||||
|
|
59
src/monero/helpers.rs
Normal file
59
src/monero/helpers.rs
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use monero_rpc::TransferType;
|
||||||
|
use monero_rpc::monero::Address;
|
||||||
|
use tokio_postgres::GenericClient;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::config::MoneroConfig;
|
||||||
|
use crate::models::{
|
||||||
|
invoices::queries::{
|
||||||
|
get_invoice_by_id,
|
||||||
|
set_invoice_status,
|
||||||
|
},
|
||||||
|
invoices::types::InvoiceStatus,
|
||||||
|
};
|
||||||
|
use super::wallet::{
|
||||||
|
open_monero_wallet,
|
||||||
|
DEFAULT_ACCOUNT,
|
||||||
|
MoneroError,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub async fn check_expired_invoice(
|
||||||
|
config: &MoneroConfig,
|
||||||
|
db_client: &impl GenericClient,
|
||||||
|
invoice_id: &Uuid,
|
||||||
|
) -> Result<(), MoneroError> {
|
||||||
|
let wallet_client = open_monero_wallet(config).await?;
|
||||||
|
let invoice = get_invoice_by_id(db_client, invoice_id).await?;
|
||||||
|
if invoice.chain_id != config.chain_id ||
|
||||||
|
invoice.invoice_status != InvoiceStatus::Timeout
|
||||||
|
{
|
||||||
|
return Err(MoneroError::OtherError("can't process invoice"));
|
||||||
|
};
|
||||||
|
let address = Address::from_str(&invoice.payment_address)?;
|
||||||
|
let address_index = wallet_client.get_address_index(address).await?;
|
||||||
|
let transfers = wallet_client.incoming_transfers(
|
||||||
|
TransferType::Available,
|
||||||
|
Some(DEFAULT_ACCOUNT),
|
||||||
|
Some(vec![address_index.minor]),
|
||||||
|
).await?
|
||||||
|
.transfers
|
||||||
|
.unwrap_or_default();
|
||||||
|
if transfers.is_empty() {
|
||||||
|
log::info!("no incoming transfers");
|
||||||
|
} else {
|
||||||
|
for transfer in transfers {
|
||||||
|
if transfer.subaddr_index != address_index {
|
||||||
|
return Err(MoneroError::WalletRpcError("unexpected transfer"));
|
||||||
|
};
|
||||||
|
log::info!(
|
||||||
|
"received payment for invoice {}: {}",
|
||||||
|
invoice.id,
|
||||||
|
transfer.amount,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
set_invoice_status(db_client, &invoice.id, InvoiceStatus::Paid).await?;
|
||||||
|
};
|
||||||
|
Ok(())
|
||||||
|
}
|
|
@ -1,2 +1,3 @@
|
||||||
|
pub mod helpers;
|
||||||
pub mod subscriptions;
|
pub mod subscriptions;
|
||||||
pub mod wallet;
|
pub mod wallet;
|
||||||
|
|
|
@ -2,7 +2,7 @@ use std::convert::TryInto;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use chrono::{Duration, Utc};
|
use chrono::{Duration, Utc};
|
||||||
use monero_rpc::{RpcClient, TransferType};
|
use monero_rpc::TransferType;
|
||||||
use monero_rpc::monero::{Address, Amount};
|
use monero_rpc::monero::{Address, Amount};
|
||||||
|
|
||||||
use crate::config::{Instance, MoneroConfig};
|
use crate::config::{Instance, MoneroConfig};
|
||||||
|
@ -28,6 +28,7 @@ use crate::models::{
|
||||||
use super::wallet::{
|
use super::wallet::{
|
||||||
get_single_item,
|
get_single_item,
|
||||||
get_subaddress_balance,
|
get_subaddress_balance,
|
||||||
|
open_monero_wallet,
|
||||||
send_monero,
|
send_monero,
|
||||||
DEFAULT_ACCOUNT,
|
DEFAULT_ACCOUNT,
|
||||||
MoneroError,
|
MoneroError,
|
||||||
|
@ -41,12 +42,7 @@ pub async fn check_monero_subscriptions(
|
||||||
db_pool: &Pool,
|
db_pool: &Pool,
|
||||||
) -> Result<(), MoneroError> {
|
) -> Result<(), MoneroError> {
|
||||||
let db_client = &mut **get_database_client(db_pool).await?;
|
let db_client = &mut **get_database_client(db_pool).await?;
|
||||||
|
let wallet_client = open_monero_wallet(config).await?;
|
||||||
let wallet_client = RpcClient::new(config.wallet_url.clone()).wallet();
|
|
||||||
wallet_client.open_wallet(
|
|
||||||
config.wallet_name.clone(),
|
|
||||||
config.wallet_password.clone(),
|
|
||||||
).await?;
|
|
||||||
|
|
||||||
// Invoices waiting for payment
|
// Invoices waiting for payment
|
||||||
let mut address_waitlist = vec![];
|
let mut address_waitlist = vec![];
|
||||||
|
@ -98,7 +94,11 @@ pub async fn check_monero_subscriptions(
|
||||||
invoice.id,
|
invoice.id,
|
||||||
transfer.amount,
|
transfer.amount,
|
||||||
);
|
);
|
||||||
|
if invoice.invoice_status == InvoiceStatus::Open {
|
||||||
set_invoice_status(db_client, &invoice.id, InvoiceStatus::Paid).await?;
|
set_invoice_status(db_client, &invoice.id, InvoiceStatus::Paid).await?;
|
||||||
|
} else {
|
||||||
|
log::warn!("invoice has already been paid");
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -48,14 +48,21 @@ pub async fn create_monero_wallet(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn create_monero_address(
|
pub async fn open_monero_wallet(
|
||||||
config: &MoneroConfig,
|
config: &MoneroConfig,
|
||||||
) -> Result<Address, MoneroError> {
|
) -> Result<WalletClient, MoneroError> {
|
||||||
let wallet_client = RpcClient::new(config.wallet_url.clone()).wallet();
|
let wallet_client = RpcClient::new(config.wallet_url.clone()).wallet();
|
||||||
wallet_client.open_wallet(
|
wallet_client.open_wallet(
|
||||||
config.wallet_name.clone(),
|
config.wallet_name.clone(),
|
||||||
config.wallet_password.clone(),
|
config.wallet_password.clone(),
|
||||||
).await?;
|
).await?;
|
||||||
|
Ok(wallet_client)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn create_monero_address(
|
||||||
|
config: &MoneroConfig,
|
||||||
|
) -> Result<Address, MoneroError> {
|
||||||
|
let wallet_client = open_monero_wallet(config).await?;
|
||||||
let (address, address_index) =
|
let (address, address_index) =
|
||||||
wallet_client.create_address(DEFAULT_ACCOUNT, None).await?;
|
wallet_client.create_address(DEFAULT_ACCOUNT, None).await?;
|
||||||
log::info!("created monero address {}/{}", DEFAULT_ACCOUNT, address_index);
|
log::info!("created monero address {}/{}", DEFAULT_ACCOUNT, address_index);
|
||||||
|
|
Loading…
Reference in a new issue