uniffi_lipalightninglib/lightning/bolt11.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
use crate::amount::AsSats;
use crate::errors::map_send_payment_error;
use crate::locker::Locker;
use crate::support::Support;
use crate::{
InvoiceCreationMetadata, InvoiceDetails, PayErrorCode, PayResult, PaymentMetadata,
RuntimeErrorCode,
};
use breez_sdk_core::error::SendPaymentError;
use breez_sdk_core::{OpeningFeeParams, SendPaymentRequest};
use perro::{ensure, runtime_error, MapToError};
use std::sync::Arc;
pub struct Bolt11 {
support: Arc<Support>,
}
impl Bolt11 {
pub(crate) fn new(support: Arc<Support>) -> Self {
Self { support }
}
/// Create a bolt11 invoice to receive a payment with.
///
/// Parameters:
/// * `amount_sat` - the smallest amount of sats required for the node to accept the incoming
/// payment (sender will have to pay fees on top of that amount)
/// * `lsp_fee_params` - the params that will be used to determine the lsp fee.
/// Can be obtained from [`Lightning::calculate_lsp_fee_for_amount`](crate::Lightning::calculate_lsp_fee_for_amount)
/// to guarantee predicted fees are the ones charged.
/// * `description` - a description to be embedded into the created invoice
/// * `metadata` - additional data about the invoice creation used for analytics purposes,
/// used to improve the user experience
///
/// Requires network: **yes**
pub fn create(
&self,
amount_sat: u64,
lsp_fee_params: Option<OpeningFeeParams>,
description: String,
metadata: InvoiceCreationMetadata,
) -> crate::Result<InvoiceDetails> {
let response = self
.support
.rt
.handle()
.block_on(
self.support
.sdk
.receive_payment(breez_sdk_core::ReceivePaymentRequest {
amount_msat: amount_sat.as_sats().msats,
description,
preimage: None,
opening_fee_params: lsp_fee_params,
use_description_hash: None,
expiry: None,
cltv: None,
}),
)
.map_to_runtime_error(
RuntimeErrorCode::NodeUnavailable,
"Failed to create an invoice",
)?;
self.support
.store_payment_info(&response.ln_invoice.payment_hash, None);
self.support
.data_store
.lock_unwrap()
.store_created_invoice(
&response.ln_invoice.payment_hash,
&response.ln_invoice.bolt11,
&response.opening_fee_msat,
response.ln_invoice.timestamp + response.ln_invoice.expiry,
)
.map_to_permanent_failure("Failed to persist created invoice")?;
self.support.analytics_interceptor.request_initiated(
response.clone(),
self.support.get_exchange_rate(),
metadata,
);
Ok(InvoiceDetails::from_ln_invoice(
response.ln_invoice,
&self.support.get_exchange_rate(),
))
}
/// Start an attempt to pay an invoice. Can immediately fail, meaning that the payment couldn't be started.
/// If successful, it doesn't mean that the payment itself was successful (funds received by the payee).
/// After this method returns, the consumer of this library will learn about a successful/failed payment through the
/// callbacks [`EventsCallback::payment_sent`](crate::EventsCallback::payment_sent) and
/// [`EventsCallback::payment_failed`](crate::EventsCallback::payment_failed).
///
/// Parameters:
/// * `invoice_details` - details of an invoice decode by [`LightningNode::decode_data`](crate::LightningNode::decode_data)
/// * `metadata` - additional meta information about the payment, used by analytics to improve the user experience.
///
/// Requires network: **yes**
pub fn pay(&self, invoice_details: InvoiceDetails, metadata: PaymentMetadata) -> PayResult<()> {
self.pay_open_amount(invoice_details, 0, metadata)
}
/// Similar to [`Bolt11::pay`] with the difference that the passed in invoice
/// does not have any payment amount specified, and allows the caller of the method to
/// specify an amount instead.
///
/// Additional Parameters:
/// * `amount_sat` - amount in sats to be paid
///
/// Requires network: **yes**
pub fn pay_open_amount(
&self,
invoice_details: InvoiceDetails,
amount_sat: u64,
metadata: PaymentMetadata,
) -> PayResult<()> {
let amount_msat = if amount_sat == 0 {
None
} else {
Some(amount_sat.as_sats().msats)
};
self.support
.store_payment_info(&invoice_details.payment_hash, None);
let node_state = self
.support
.sdk
.node_info()
.map_to_runtime_error(PayErrorCode::NodeUnavailable, "Failed to read node info")?;
ensure!(
node_state.id != invoice_details.payee_pub_key,
runtime_error(
PayErrorCode::PayingToSelf,
"A locally issued invoice tried to be paid"
)
);
self.support.analytics_interceptor.pay_initiated(
invoice_details.clone(),
metadata,
amount_msat,
self.support.get_exchange_rate(),
);
let result = self
.support
.rt
.handle()
.block_on(self.support.sdk.send_payment(SendPaymentRequest {
bolt11: invoice_details.invoice,
use_trampoline: true,
amount_msat,
label: None,
}));
if matches!(
result,
Err(SendPaymentError::Generic { .. }
| SendPaymentError::PaymentFailed { .. }
| SendPaymentError::PaymentTimeout { .. }
| SendPaymentError::RouteNotFound { .. }
| SendPaymentError::RouteTooExpensive { .. }
| SendPaymentError::ServiceConnectivity { .. })
) {
self.support
.report_send_payment_issue(invoice_details.payment_hash);
}
result.map_err(map_send_payment_error)?;
Ok(())
}
}