mirror of
https://github.com/scsibug/nostr-rs-relay.git
synced 2024-11-09 21:29:06 -05:00
feat: add cln payment processor
This commit is contained in:
parent
0d04b5eefa
commit
5a21890625
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -2,3 +2,4 @@
|
||||||
nostr.db
|
nostr.db
|
||||||
nostr.db-*
|
nostr.db-*
|
||||||
justfile
|
justfile
|
||||||
|
result
|
||||||
|
|
77
Cargo.lock
generated
77
Cargo.lock
generated
|
@ -438,6 +438,26 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitcoin"
|
||||||
|
version = "0.30.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1945a5048598e4189e239d3f809b19bdad4845c4b2ba400d304d2dcf26d2c462"
|
||||||
|
dependencies = [
|
||||||
|
"bech32",
|
||||||
|
"bitcoin-private",
|
||||||
|
"bitcoin_hashes 0.12.0",
|
||||||
|
"hex_lit",
|
||||||
|
"secp256k1 0.27.0",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitcoin-private"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "73290177011694f38ec25e165d0387ab7ea749a4b81cd4c80dae5988229f7a57"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitcoin_hashes"
|
name = "bitcoin_hashes"
|
||||||
version = "0.10.0"
|
version = "0.10.0"
|
||||||
|
@ -456,6 +476,16 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitcoin_hashes"
|
||||||
|
version = "0.12.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5d7066118b13d4b20b23645932dfb3a81ce7e29f95726c2036fa33cd7b092501"
|
||||||
|
dependencies = [
|
||||||
|
"bitcoin-private",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "1.3.2"
|
version = "1.3.2"
|
||||||
|
@ -611,6 +641,24 @@ version = "0.7.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
|
checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cln-rpc"
|
||||||
|
version = "0.1.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "974dac6f40275b7b828087f4f9973c39658f9b4a46cc589c083a2c6c27cf67cb"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"bitcoin 0.30.2",
|
||||||
|
"bytes",
|
||||||
|
"futures-util",
|
||||||
|
"hex",
|
||||||
|
"log",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"tokio",
|
||||||
|
"tokio-util",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cloudabi"
|
name = "cloudabi"
|
||||||
version = "0.0.3"
|
version = "0.0.3"
|
||||||
|
@ -1323,6 +1371,12 @@ version = "0.4.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hex_lit"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hkdf"
|
name = "hkdf"
|
||||||
version = "0.12.4"
|
version = "0.12.4"
|
||||||
|
@ -1773,7 +1827,7 @@ checksum = "35c0446103768cddfb2bc1b87a52e98c35227b82711c2b3ce7098f8d85d9b0ee"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aes",
|
"aes",
|
||||||
"base64 0.21.7",
|
"base64 0.21.7",
|
||||||
"bitcoin",
|
"bitcoin 0.29.2",
|
||||||
"cbc",
|
"cbc",
|
||||||
"getrandom",
|
"getrandom",
|
||||||
"instant",
|
"instant",
|
||||||
|
@ -1797,6 +1851,7 @@ dependencies = [
|
||||||
"bitcoin_hashes 0.10.0",
|
"bitcoin_hashes 0.10.0",
|
||||||
"chrono",
|
"chrono",
|
||||||
"clap",
|
"clap",
|
||||||
|
"cln-rpc",
|
||||||
"config",
|
"config",
|
||||||
"console-subscriber",
|
"console-subscriber",
|
||||||
"const_format",
|
"const_format",
|
||||||
|
@ -2820,6 +2875,17 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "secp256k1"
|
||||||
|
version = "0.27.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "25996b82292a7a57ed3508f052cfff8640d38d32018784acd714758b43da9c8f"
|
||||||
|
dependencies = [
|
||||||
|
"bitcoin_hashes 0.12.0",
|
||||||
|
"secp256k1-sys 0.8.1",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "secp256k1-sys"
|
name = "secp256k1-sys"
|
||||||
version = "0.4.2"
|
version = "0.4.2"
|
||||||
|
@ -2838,6 +2904,15 @@ dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "secp256k1-sys"
|
||||||
|
version = "0.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "70a129b9e9efbfb223753b9163c4ab3b13cff7fd9c7f010fbac25ab4099fa07e"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "security-framework"
|
name = "security-framework"
|
||||||
version = "2.9.2"
|
version = "2.9.2"
|
||||||
|
|
|
@ -58,6 +58,7 @@ nostr = { version = "0.18.0", default-features = false, features = ["base", "nip
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
[target.'cfg(all(not(target_env = "msvc"), not(target_os = "openbsd")))'.dependencies]
|
[target.'cfg(all(not(target_env = "msvc"), not(target_os = "openbsd")))'.dependencies]
|
||||||
tikv-jemallocator = "0.5"
|
tikv-jemallocator = "0.5"
|
||||||
|
cln-rpc = "0.1.9"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
|
|
|
@ -203,18 +203,24 @@ limit_scrapers = false
|
||||||
# Enable pay to relay
|
# Enable pay to relay
|
||||||
#enabled = false
|
#enabled = false
|
||||||
|
|
||||||
|
# Node interface to use
|
||||||
|
#processor = "ClnRest/LNBits"
|
||||||
|
|
||||||
# The cost to be admitted to relay
|
# The cost to be admitted to relay
|
||||||
#admission_cost = 4200
|
#admission_cost = 4200
|
||||||
|
|
||||||
# The cost in sats per post
|
# The cost in sats per post
|
||||||
#cost_per_event = 0
|
#cost_per_event = 0
|
||||||
|
|
||||||
# Url of lnbits api
|
# Url of node api
|
||||||
#node_url = "<node url>"
|
#node_url = "<node url>"
|
||||||
|
|
||||||
# LNBits api secret
|
# LNBits api secret
|
||||||
#api_secret = "<ln bits api>"
|
#api_secret = "<ln bits api>"
|
||||||
|
|
||||||
|
# Path to CLN rune
|
||||||
|
#rune_path = "<rune path>"
|
||||||
|
|
||||||
# Nostr direct message on signup
|
# Nostr direct message on signup
|
||||||
#direct_message=false
|
#direct_message=false
|
||||||
|
|
||||||
|
|
|
@ -98,6 +98,7 @@ pub struct PayToRelay {
|
||||||
pub direct_message: bool, // Send direct message to user with invoice and terms
|
pub direct_message: bool, // Send direct message to user with invoice and terms
|
||||||
pub secret_key: Option<String>,
|
pub secret_key: Option<String>,
|
||||||
pub processor: Processor,
|
pub processor: Processor,
|
||||||
|
pub rune_path: Option<String>, // To access clightning API
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
@ -247,17 +248,25 @@ impl Settings {
|
||||||
|
|
||||||
// Validate pay to relay settings
|
// Validate pay to relay settings
|
||||||
if settings.pay_to_relay.enabled {
|
if settings.pay_to_relay.enabled {
|
||||||
|
if settings.pay_to_relay.processor == Processor::ClnRest {
|
||||||
|
assert!(settings
|
||||||
|
.pay_to_relay
|
||||||
|
.rune_path
|
||||||
|
.as_ref()
|
||||||
|
.is_some_and(|path| path != "<rune path>"));
|
||||||
|
} else if settings.pay_to_relay.processor == Processor::LNBits {
|
||||||
assert_ne!(settings.pay_to_relay.api_secret, "");
|
assert_ne!(settings.pay_to_relay.api_secret, "");
|
||||||
|
}
|
||||||
// Should check that url is valid
|
// Should check that url is valid
|
||||||
assert_ne!(settings.pay_to_relay.node_url, "");
|
assert_ne!(settings.pay_to_relay.node_url, "");
|
||||||
assert_ne!(settings.pay_to_relay.terms_message, "");
|
assert_ne!(settings.pay_to_relay.terms_message, "");
|
||||||
|
|
||||||
if settings.pay_to_relay.direct_message {
|
if settings.pay_to_relay.direct_message {
|
||||||
assert_ne!(
|
assert!(settings
|
||||||
settings.pay_to_relay.secret_key,
|
.pay_to_relay
|
||||||
Some("<nostr nsec>".to_string())
|
.secret_key
|
||||||
);
|
.as_ref()
|
||||||
assert!(settings.pay_to_relay.secret_key.is_some());
|
.is_some_and(|key| key != "<nostr nsec>"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -309,7 +318,7 @@ impl Default for Settings {
|
||||||
event_persist_buffer: 4096,
|
event_persist_buffer: 4096,
|
||||||
event_kind_blacklist: None,
|
event_kind_blacklist: None,
|
||||||
event_kind_allowlist: None,
|
event_kind_allowlist: None,
|
||||||
limit_scrapers: false
|
limit_scrapers: false,
|
||||||
},
|
},
|
||||||
authorization: Authorization {
|
authorization: Authorization {
|
||||||
pubkey_whitelist: None, // Allow any address to publish
|
pubkey_whitelist: None, // Allow any address to publish
|
||||||
|
@ -323,6 +332,7 @@ impl Default for Settings {
|
||||||
terms_message: "".to_string(),
|
terms_message: "".to_string(),
|
||||||
node_url: "".to_string(),
|
node_url: "".to_string(),
|
||||||
api_secret: "".to_string(),
|
api_secret: "".to_string(),
|
||||||
|
rune_path: None,
|
||||||
sign_ups: false,
|
sign_ups: false,
|
||||||
direct_message: false,
|
direct_message: false,
|
||||||
secret_key: None,
|
secret_key: None,
|
||||||
|
|
|
@ -42,7 +42,7 @@ pub enum Error {
|
||||||
CommandUnknownError,
|
CommandUnknownError,
|
||||||
#[error("SQL error")]
|
#[error("SQL error")]
|
||||||
SqlError(rusqlite::Error),
|
SqlError(rusqlite::Error),
|
||||||
#[error("Config error")]
|
#[error("Config error : {0}")]
|
||||||
ConfigError(config::ConfigError),
|
ConfigError(config::ConfigError),
|
||||||
#[error("Data directory does not exist")]
|
#[error("Data directory does not exist")]
|
||||||
DatabaseDirError,
|
DatabaseDirError,
|
||||||
|
|
137
src/payment/cln_rest.rs
Normal file
137
src/payment/cln_rest.rs
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
use std::{fs, str::FromStr};
|
||||||
|
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use cln_rpc::{
|
||||||
|
model::{
|
||||||
|
requests::InvoiceRequest,
|
||||||
|
responses::{InvoiceResponse, ListinvoicesInvoicesStatus, ListinvoicesResponse},
|
||||||
|
},
|
||||||
|
primitives::{Amount, AmountOrAny},
|
||||||
|
};
|
||||||
|
use config::ConfigError;
|
||||||
|
use http::{header::CONTENT_TYPE, HeaderValue, Uri};
|
||||||
|
use hyper::{client::HttpConnector, Client};
|
||||||
|
use hyper_rustls::HttpsConnector;
|
||||||
|
use nostr::Keys;
|
||||||
|
use rand::random;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
config::Settings,
|
||||||
|
error::{Error, Result},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{InvoiceInfo, InvoiceStatus, PaymentProcessor};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct ClnRestPaymentProcessor {
|
||||||
|
client: hyper::Client<HttpsConnector<HttpConnector>, hyper::Body>,
|
||||||
|
settings: Settings,
|
||||||
|
rune_header: HeaderValue,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ClnRestPaymentProcessor {
|
||||||
|
pub fn new(settings: &Settings) -> Result<Self> {
|
||||||
|
let rune_path = settings
|
||||||
|
.pay_to_relay
|
||||||
|
.rune_path
|
||||||
|
.clone()
|
||||||
|
.ok_or(ConfigError::NotFound("rune_path".to_string()))?;
|
||||||
|
let rune = String::from_utf8(fs::read(rune_path)?)
|
||||||
|
.map_err(|_| ConfigError::Message("Rune should be UTF8".to_string()))?;
|
||||||
|
let mut rune_header = HeaderValue::from_str(&rune.trim())
|
||||||
|
.map_err(|_| ConfigError::Message("Invalid Rune header".to_string()))?;
|
||||||
|
rune_header.set_sensitive(true);
|
||||||
|
|
||||||
|
let https = hyper_rustls::HttpsConnectorBuilder::new()
|
||||||
|
.with_native_roots()
|
||||||
|
.https_only()
|
||||||
|
.enable_http1()
|
||||||
|
.build();
|
||||||
|
let client = Client::builder().build::<_, hyper::Body>(https);
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
client,
|
||||||
|
settings: settings.clone(),
|
||||||
|
rune_header,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl PaymentProcessor for ClnRestPaymentProcessor {
|
||||||
|
async fn get_invoice(&self, key: &Keys, amount: u64) -> Result<InvoiceInfo, Error> {
|
||||||
|
let random_number: u16 = random();
|
||||||
|
let memo = format!("{}: {}", random_number, key.public_key());
|
||||||
|
|
||||||
|
let body = InvoiceRequest {
|
||||||
|
cltv: None,
|
||||||
|
deschashonly: None,
|
||||||
|
expiry: None,
|
||||||
|
preimage: None,
|
||||||
|
exposeprivatechannels: None,
|
||||||
|
fallbacks: None,
|
||||||
|
amount_msat: AmountOrAny::Amount(Amount::from_sat(amount)),
|
||||||
|
description: memo.clone(),
|
||||||
|
label: "Nostr".to_string(),
|
||||||
|
};
|
||||||
|
let uri = Uri::from_str(&format!(
|
||||||
|
"{}/v1/invoice",
|
||||||
|
&self.settings.pay_to_relay.node_url
|
||||||
|
))
|
||||||
|
.map_err(|_| ConfigError::Message("Bad node URL".to_string()))?;
|
||||||
|
|
||||||
|
let req = hyper::Request::builder()
|
||||||
|
.method(hyper::Method::POST)
|
||||||
|
.uri(uri)
|
||||||
|
.header(CONTENT_TYPE, HeaderValue::from_static("application/json"))
|
||||||
|
.header("Rune", self.rune_header.clone())
|
||||||
|
.body(hyper::Body::from(serde_json::to_string(&body)?))
|
||||||
|
.expect("request builder");
|
||||||
|
|
||||||
|
let res = self.client.request(req).await?;
|
||||||
|
|
||||||
|
let body = hyper::body::to_bytes(res.into_body()).await?;
|
||||||
|
let invoice_response: InvoiceResponse = serde_json::from_slice(&body)?;
|
||||||
|
|
||||||
|
Ok(InvoiceInfo {
|
||||||
|
pubkey: key.public_key().to_string(),
|
||||||
|
payment_hash: invoice_response.payment_hash.to_string(),
|
||||||
|
bolt11: invoice_response.bolt11,
|
||||||
|
amount,
|
||||||
|
memo,
|
||||||
|
status: InvoiceStatus::Unpaid,
|
||||||
|
confirmed_at: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn check_invoice(&self, payment_hash: &str) -> Result<InvoiceStatus, Error> {
|
||||||
|
let uri = Uri::from_str(&format!(
|
||||||
|
"{}/v1/listinvoices?payment_hash={}",
|
||||||
|
&self.settings.pay_to_relay.node_url, payment_hash
|
||||||
|
))
|
||||||
|
.map_err(|_| ConfigError::Message("Bad node URL".to_string()))?;
|
||||||
|
|
||||||
|
let req = hyper::Request::builder()
|
||||||
|
.method(hyper::Method::POST)
|
||||||
|
.uri(uri)
|
||||||
|
.header(CONTENT_TYPE, HeaderValue::from_static("application/json"))
|
||||||
|
.header("Rune", self.rune_header.clone())
|
||||||
|
.body(hyper::Body::empty())
|
||||||
|
.expect("request builder");
|
||||||
|
|
||||||
|
let res = self.client.request(req).await?;
|
||||||
|
|
||||||
|
let body = hyper::body::to_bytes(res.into_body()).await?;
|
||||||
|
let invoice_response: ListinvoicesResponse = serde_json::from_slice(&body)?;
|
||||||
|
let invoice = invoice_response
|
||||||
|
.invoices
|
||||||
|
.first()
|
||||||
|
.ok_or(Error::CustomError("Invoice not found".to_string()))?;
|
||||||
|
let status = match invoice.status {
|
||||||
|
ListinvoicesInvoicesStatus::PAID => InvoiceStatus::Paid,
|
||||||
|
ListinvoicesInvoicesStatus::UNPAID => InvoiceStatus::Unpaid,
|
||||||
|
ListinvoicesInvoicesStatus::EXPIRED => InvoiceStatus::Expired,
|
||||||
|
};
|
||||||
|
Ok(status)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use crate::event::Event;
|
use crate::event::Event;
|
||||||
|
use crate::payment::cln_rest::ClnRestPaymentProcessor;
|
||||||
use crate::payment::lnbits::LNBitsPaymentProcessor;
|
use crate::payment::lnbits::LNBitsPaymentProcessor;
|
||||||
use crate::repo::NostrRepo;
|
use crate::repo::NostrRepo;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -10,6 +11,7 @@ use async_trait::async_trait;
|
||||||
use nostr::key::{FromPkStr, FromSkStr};
|
use nostr::key::{FromPkStr, FromSkStr};
|
||||||
use nostr::{key::Keys, Event as NostrEvent, EventBuilder};
|
use nostr::{key::Keys, Event as NostrEvent, EventBuilder};
|
||||||
|
|
||||||
|
pub mod cln_rest;
|
||||||
pub mod lnbits;
|
pub mod lnbits;
|
||||||
|
|
||||||
/// Payment handler
|
/// Payment handler
|
||||||
|
@ -41,6 +43,7 @@ pub trait PaymentProcessor: Send + Sync {
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
||||||
pub enum Processor {
|
pub enum Processor {
|
||||||
LNBits,
|
LNBits,
|
||||||
|
ClnRest,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Possible states of an invoice
|
/// Possible states of an invoice
|
||||||
|
@ -109,8 +112,9 @@ impl Payment {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create processor kind defined in settings
|
// Create processor kind defined in settings
|
||||||
let processor = match &settings.pay_to_relay.processor {
|
let processor: Arc<dyn PaymentProcessor> = match &settings.pay_to_relay.processor {
|
||||||
Processor::LNBits => Arc::new(LNBitsPaymentProcessor::new(&settings)),
|
Processor::LNBits => Arc::new(LNBitsPaymentProcessor::new(&settings)),
|
||||||
|
Processor::ClnRest => Arc::new(ClnRestPaymentProcessor::new(&settings)?),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Payment {
|
Ok(Payment {
|
||||||
|
|
|
@ -568,6 +568,11 @@ async fn handle_web_request(
|
||||||
.unwrap());
|
.unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Account is checked async so user will have to refresh the page a couple times after
|
||||||
|
// they have paid.
|
||||||
|
if let Err(e) = payment_tx.send(PaymentMessage::CheckAccount(pubkey.clone())) {
|
||||||
|
warn!("Could not check account: {}", e);
|
||||||
|
}
|
||||||
// Checks if user is already admitted
|
// Checks if user is already admitted
|
||||||
let text =
|
let text =
|
||||||
if let Ok((admission_status, _)) = repo.get_account_balance(&key.unwrap()).await {
|
if let Ok((admission_status, _)) = repo.get_account_balance(&key.unwrap()).await {
|
||||||
|
@ -894,11 +899,17 @@ pub fn start_server(settings: &Settings, shutdown_rx: MpscReceiver<()>) -> Resul
|
||||||
bcast_tx.clone(),
|
bcast_tx.clone(),
|
||||||
settings.clone(),
|
settings.clone(),
|
||||||
);
|
);
|
||||||
if let Ok(mut p) = payment_opt {
|
match payment_opt {
|
||||||
|
Ok(mut p) => {
|
||||||
tokio::task::spawn(async move {
|
tokio::task::spawn(async move {
|
||||||
info!("starting payment process ...");
|
info!("starting payment process ...");
|
||||||
p.run().await;
|
p.run().await;
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
error!("Failed to start payment process {e}");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user