feat(cdk): add payment request and proof to transaction records (#1155)

Add payment_request and payment_proof fields to Transaction model to store Lightning invoice details and payment preimages. Update database migrations and all transaction creation points across wallet operations (mint, melt, send, receive) to populate these fields.
This commit is contained in:
tsk
2025-10-06 14:40:21 +02:00
committed by GitHub
parent c15a79f313
commit c5e5d71701
11 changed files with 54 additions and 9 deletions

View File

@@ -204,6 +204,10 @@ pub struct Transaction {
pub metadata: HashMap<String, String>, pub metadata: HashMap<String, String>,
/// Quote ID if this is a mint or melt transaction /// Quote ID if this is a mint or melt transaction
pub quote_id: Option<String>, pub quote_id: Option<String>,
/// Payment request (e.g., BOLT11 invoice, BOLT12 offer)
pub payment_request: Option<String>,
/// Payment proof (e.g., preimage for Lightning melt transactions)
pub payment_proof: Option<String>,
} }
impl Transaction { impl Transaction {

View File

@@ -35,6 +35,10 @@ pub struct Transaction {
pub metadata: HashMap<String, String>, pub metadata: HashMap<String, String>,
/// Quote ID if this is a mint or melt transaction /// Quote ID if this is a mint or melt transaction
pub quote_id: Option<String>, pub quote_id: Option<String>,
/// Payment request (e.g., BOLT11 invoice, BOLT12 offer)
pub payment_request: Option<String>,
/// Payment proof (e.g., preimage for Lightning melt transactions)
pub payment_proof: Option<String>,
} }
impl From<cdk::wallet::types::Transaction> for Transaction { impl From<cdk::wallet::types::Transaction> for Transaction {
@@ -51,6 +55,8 @@ impl From<cdk::wallet::types::Transaction> for Transaction {
memo: tx.memo, memo: tx.memo,
metadata: tx.metadata, metadata: tx.metadata,
quote_id: tx.quote_id, quote_id: tx.quote_id,
payment_request: tx.payment_request,
payment_proof: tx.payment_proof,
} }
} }
} }
@@ -75,6 +81,8 @@ impl TryFrom<Transaction> for cdk::wallet::types::Transaction {
memo: tx.memo, memo: tx.memo,
metadata: tx.metadata, metadata: tx.metadata,
quote_id: tx.quote_id, quote_id: tx.quote_id,
payment_request: tx.payment_request,
payment_proof: tx.payment_proof,
}) })
} }
} }

View File

@@ -0,0 +1,3 @@
-- Add payment_request and payment_proof to transactions table
ALTER TABLE transactions ADD COLUMN payment_request TEXT;
ALTER TABLE transactions ADD COLUMN payment_proof TEXT;

View File

@@ -0,0 +1,3 @@
-- Add payment_request and payment_proof to transactions table
ALTER TABLE transactions ADD COLUMN payment_request TEXT;
ALTER TABLE transactions ADD COLUMN payment_proof TEXT;

View File

@@ -969,9 +969,9 @@ ON CONFLICT(id) DO UPDATE SET
query( query(
r#" r#"
INSERT INTO transactions INSERT INTO transactions
(id, mint_url, direction, unit, amount, fee, ys, timestamp, memo, metadata, quote_id) (id, mint_url, direction, unit, amount, fee, ys, timestamp, memo, metadata, quote_id, payment_request, payment_proof)
VALUES VALUES
(:id, :mint_url, :direction, :unit, :amount, :fee, :ys, :timestamp, :memo, :metadata, :quote_id) (:id, :mint_url, :direction, :unit, :amount, :fee, :ys, :timestamp, :memo, :metadata, :quote_id, :payment_request, :payment_proof)
ON CONFLICT(id) DO UPDATE SET ON CONFLICT(id) DO UPDATE SET
mint_url = excluded.mint_url, mint_url = excluded.mint_url,
direction = excluded.direction, direction = excluded.direction,
@@ -982,7 +982,9 @@ ON CONFLICT(id) DO UPDATE SET
timestamp = excluded.timestamp, timestamp = excluded.timestamp,
memo = excluded.memo, memo = excluded.memo,
metadata = excluded.metadata, metadata = excluded.metadata,
quote_id = excluded.quote_id quote_id = excluded.quote_id,
payment_request = excluded.payment_request,
payment_proof = excluded.payment_proof
; ;
"#, "#,
)? )?
@@ -1000,6 +1002,8 @@ ON CONFLICT(id) DO UPDATE SET
serde_json::to_string(&transaction.metadata).map_err(Error::from)?, serde_json::to_string(&transaction.metadata).map_err(Error::from)?,
) )
.bind("quote_id", transaction.quote_id) .bind("quote_id", transaction.quote_id)
.bind("payment_request", transaction.payment_request)
.bind("payment_proof", transaction.payment_proof)
.execute(&*conn) .execute(&*conn)
.await?; .await?;
@@ -1024,7 +1028,9 @@ ON CONFLICT(id) DO UPDATE SET
timestamp, timestamp,
memo, memo,
metadata, metadata,
quote_id quote_id,
payment_request,
payment_proof
FROM FROM
transactions transactions
WHERE WHERE
@@ -1059,7 +1065,9 @@ ON CONFLICT(id) DO UPDATE SET
timestamp, timestamp,
memo, memo,
metadata, metadata,
quote_id quote_id,
payment_request,
payment_proof
FROM FROM
transactions transactions
"#, "#,
@@ -1297,7 +1305,9 @@ fn sql_row_to_transaction(row: Vec<Column>) -> Result<Transaction, Error> {
timestamp, timestamp,
memo, memo,
metadata, metadata,
quote_id quote_id,
payment_request,
payment_proof
) = row ) = row
); );
@@ -1321,5 +1331,7 @@ fn sql_row_to_transaction(row: Vec<Column>) -> Result<Transaction, Error> {
}) })
.unwrap_or_default(), .unwrap_or_default(),
quote_id: column_as_nullable_string!(quote_id), quote_id: column_as_nullable_string!(quote_id),
payment_request: column_as_nullable_string!(payment_request),
payment_proof: column_as_nullable_string!(payment_proof),
}) })
} }

View File

@@ -329,6 +329,8 @@ impl Wallet {
memo: None, memo: None,
metadata: HashMap::new(), metadata: HashMap::new(),
quote_id: Some(quote_id.to_string()), quote_id: Some(quote_id.to_string()),
payment_request: Some(quote_info.request),
payment_proof: None,
}) })
.await?; .await?;

View File

@@ -232,6 +232,8 @@ impl Wallet {
memo: None, memo: None,
metadata: HashMap::new(), metadata: HashMap::new(),
quote_id: Some(quote_id.to_string()), quote_id: Some(quote_id.to_string()),
payment_request: Some(quote_info.request),
payment_proof: None,
}) })
.await?; .await?;

View File

@@ -54,7 +54,7 @@ impl Wallet {
let invoice = Bolt11Invoice::from_str(&request)?; let invoice = Bolt11Invoice::from_str(&request)?;
let quote_request = MeltQuoteBolt11Request { let quote_request = MeltQuoteBolt11Request {
request: Bolt11Invoice::from_str(&request)?, request: invoice.clone(),
unit: self.unit.clone(), unit: self.unit.clone(),
options, options,
}; };
@@ -248,6 +248,8 @@ impl Wallet {
None => None, None => None,
}; };
let payment_preimage = melt_response.payment_preimage.clone();
let melted = Melted::from_proofs( let melted = Melted::from_proofs(
melt_response.state, melt_response.state,
melt_response.payment_preimage, melt_response.payment_preimage,
@@ -298,6 +300,8 @@ impl Wallet {
memo: None, memo: None,
metadata, metadata,
quote_id: Some(quote_id.to_string()), quote_id: Some(quote_id.to_string()),
payment_request: Some(quote_info.request),
payment_proof: payment_preimage,
}) })
.await?; .await?;

View File

@@ -60,6 +60,7 @@ impl Wallet {
let pending_proofs = self.get_pending_proofs().await?; let pending_proofs = self.get_pending_proofs().await?;
let proofs_total = pending_proofs.total_amount().unwrap_or_default(); let proofs_total = pending_proofs.total_amount().unwrap_or_default();
let change_total = response.change_amount().unwrap_or_default(); let change_total = response.change_amount().unwrap_or_default();
self.localstore self.localstore
.add_transaction(Transaction { .add_transaction(Transaction {
mint_url: self.mint_url.clone(), mint_url: self.mint_url.clone(),
@@ -75,6 +76,8 @@ impl Wallet {
memo: None, memo: None,
metadata: HashMap::new(), metadata: HashMap::new(),
quote_id: Some(quote.id.clone()), quote_id: Some(quote.id.clone()),
payment_request: Some(quote.request.clone()),
payment_proof: response.payment_preimage.clone(),
}) })
.await?; .await?;
} }

View File

@@ -167,7 +167,9 @@ impl Wallet {
timestamp: unix_time(), timestamp: unix_time(),
memo, memo,
metadata: opts.metadata, metadata: opts.metadata,
quote_id: None, // Receive transactions don't have a quote_id quote_id: None,
payment_request: None,
payment_proof: None,
}) })
.await?; .await?;

View File

@@ -350,7 +350,9 @@ impl PreparedSend {
timestamp: unix_time(), timestamp: unix_time(),
memo: memo.clone(), memo: memo.clone(),
metadata: self.options.metadata, metadata: self.options.metadata,
quote_id: None, // Send transactions don't have a quote_id quote_id: None,
payment_request: None,
payment_proof: None,
}) })
.await?; .await?;