feat: fiat payments (revolut)

ref: #24
This commit is contained in:
2025-03-11 12:42:25 +00:00
parent 1c282e460f
commit 45dd0c4398
32 changed files with 822 additions and 151 deletions

View File

@ -0,0 +1,6 @@
alter table vm_payment
add column currency varchar(5) not null default 'BTC',
add column payment_method smallint unsigned not null default 0,
add column external_id varchar(255),
change invoice external_data varchar (4096) NOT NULL,
drop column settle_index;

View File

@ -131,6 +131,9 @@ pub trait LNVpsDb: Sync + Send {
/// Get VM payment by payment id
async fn get_vm_payment(&self, id: &Vec<u8>) -> Result<VmPayment>;
/// Get VM payment by payment id
async fn get_vm_payment_by_ext_id(&self, id: &str) -> Result<VmPayment>;
/// Update a VM payment record
async fn update_vm_payment(&self, vm_payment: &VmPayment) -> Result<()>;

View File

@ -1,8 +1,9 @@
use anyhow::{anyhow, Result};
use anyhow::{anyhow, bail, Result};
use chrono::{DateTime, Utc};
use sqlx::FromRow;
use sqlx::{FromRow, Type};
use std::fmt::{Display, Formatter};
use std::path::PathBuf;
use std::str::FromStr;
use url::Url;
#[derive(FromRow, Clone, Debug)]
@ -309,17 +310,53 @@ impl Display for VmIpAssignment {
#[derive(FromRow, Clone, Debug, Default)]
pub struct VmPayment {
/// Payment hash
pub id: Vec<u8>,
pub vm_id: u64,
pub created: DateTime<Utc>,
pub expires: DateTime<Utc>,
pub amount: u64,
pub invoice: String,
pub currency: String,
pub payment_method: PaymentMethod,
/// External data (invoice / json)
pub external_data: String,
/// External id on other system
pub external_id: Option<String>,
pub is_paid: bool,
/// Exchange rate
/// TODO: handle other base currencies
/// Exchange rate back to base currency (EUR)
pub rate: f32,
/// Number of seconds this payment will add to vm expiry
pub time_value: u64,
pub settle_index: Option<u64>,
}
#[derive(Type, Clone, Copy, Debug, Default, PartialEq)]
#[repr(u16)]
pub enum PaymentMethod {
#[default]
Lightning,
Revolut,
Paypal,
}
impl Display for PaymentMethod {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
PaymentMethod::Lightning => write!(f, "Lightning"),
PaymentMethod::Revolut => write!(f, "Revolut"),
PaymentMethod::Paypal => write!(f, "PayPal"),
}
}
}
impl FromStr for PaymentMethod {
type Err = anyhow::Error;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
match s {
"lightning" => Ok(PaymentMethod::Lightning),
"revolut" => Ok(PaymentMethod::Revolut),
"paypal" => Ok(PaymentMethod::Paypal),
_ => bail!("Unknown payment method: {}", s),
}
}
}

View File

@ -387,16 +387,19 @@ impl LNVpsDb for LNVpsDbMysql {
}
async fn insert_vm_payment(&self, vm_payment: &VmPayment) -> Result<()> {
sqlx::query("insert into vm_payment(id,vm_id,created,expires,amount,invoice,time_value,is_paid,rate) values(?,?,?,?,?,?,?,?,?)")
sqlx::query("insert into vm_payment(id,vm_id,created,expires,amount,currency,payment_method,time_value,is_paid,rate,external_id,external_data) values(?,?,?,?,?,?,?,?,?,?,?,?)")
.bind(&vm_payment.id)
.bind(vm_payment.vm_id)
.bind(vm_payment.created)
.bind(vm_payment.expires)
.bind(vm_payment.amount)
.bind(&vm_payment.invoice)
.bind(&vm_payment.currency)
.bind(&vm_payment.payment_method)
.bind(vm_payment.time_value)
.bind(vm_payment.is_paid)
.bind(vm_payment.rate)
.bind(&vm_payment.external_id)
.bind(&vm_payment.external_data)
.execute(&self.db)
.await
.map_err(Error::new)?;
@ -411,6 +414,14 @@ impl LNVpsDb for LNVpsDbMysql {
.map_err(Error::new)
}
async fn get_vm_payment_by_ext_id(&self, id: &str) -> Result<VmPayment> {
sqlx::query_as("select * from vm_payment where external_id=?")
.bind(id)
.fetch_one(&self.db)
.await
.map_err(Error::new)
}
async fn update_vm_payment(&self, vm_payment: &VmPayment) -> Result<()> {
sqlx::query("update vm_payment set is_paid = ? where id = ?")
.bind(vm_payment.is_paid)
@ -428,8 +439,7 @@ impl LNVpsDb for LNVpsDbMysql {
let mut tx = self.db.begin().await?;
sqlx::query("update vm_payment set is_paid = true, settle_index = ? where id = ?")
.bind(vm_payment.settle_index)
sqlx::query("update vm_payment set is_paid = true where id = ?")
.bind(&vm_payment.id)
.execute(&mut *tx)
.await?;
@ -446,7 +456,7 @@ impl LNVpsDb for LNVpsDbMysql {
async fn last_paid_invoice(&self) -> Result<Option<VmPayment>> {
sqlx::query_as(
"select * from vm_payment where is_paid = true order by settle_index desc limit 1",
"select * from vm_payment where is_paid = true order by created desc limit 1",
)
.fetch_optional(&self.db)
.await