@@ -26,7 +26,7 @@ use lightning_invoice::{Bolt11Invoice, Bolt11InvoiceDescription, Description};
2626
2727use bip21:: de:: ParamKind ;
2828use bip21:: { DeserializationError , DeserializeParams , Param , SerializeParams } ;
29- use bitcoin:: address:: { NetworkChecked , NetworkUnchecked } ;
29+ use bitcoin:: address:: NetworkChecked ;
3030use bitcoin:: { Amount , Txid } ;
3131
3232use std:: sync:: Arc ;
@@ -143,54 +143,104 @@ impl UnifiedPayment {
143143 Ok ( format_uri ( uri) )
144144 }
145145
146- /// Sends a payment given a [BIP 21] URI.
146+ /// Sends a payment given a [BIP 21] URI or [BIP 353] HRN .
147147 ///
148148 /// This method parses the provided URI string and attempts to send the payment. If the URI
149149 /// has an offer and or invoice, it will try to pay the offer first followed by the invoice.
150150 /// If they both fail, the on-chain payment will be paid.
151151 ///
152- /// Returns a `QrPaymentResult ` indicating the outcome of the payment. If an error
152+ /// Returns a `PaymentResult ` indicating the outcome of the payment. If an error
153153 /// occurs, an `Error` is returned detailing the issue encountered.
154154 ///
155155 /// [BIP 21]: https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki
156- pub fn send ( & self , uri_str : & str ) -> Result < QrPaymentResult , Error > {
157- let uri: bip21:: Uri < NetworkUnchecked , Extras > =
158- uri_str. parse ( ) . map_err ( |_| Error :: InvalidUri ) ?;
159-
160- let uri_network_checked =
161- uri. clone ( ) . require_network ( self . config . network ) . map_err ( |_| Error :: InvalidNetwork ) ?;
162-
163- if let Some ( offer) = uri_network_checked. extras . bolt12_offer {
164- let offer = maybe_wrap ( offer) ;
165- match self . bolt12_payment . send ( & offer, None , None ) {
166- Ok ( payment_id) => return Ok ( QrPaymentResult :: Bolt12 { payment_id } ) ,
167- Err ( e) => log_error ! ( self . logger, "Failed to send BOLT12 offer: {:?}. This is part of a unified QR code payment. Falling back to the BOLT11 invoice." , e) ,
156+ /// [BIP 353]: https://github.com/bitcoin/bips/blob/master/bip-0353.mediawiki
157+ pub async fn send (
158+ & self , uri_str : & str , amount_msat : Option < u64 > ,
159+ ) -> Result < PaymentResult , Error > {
160+ let instructions = match PaymentInstructions :: parse (
161+ uri_str,
162+ self . config . network ,
163+ self . hrn_resolver . as_ref ( ) ,
164+ true ,
165+ )
166+ . await
167+ {
168+ Ok ( instr) => instr,
169+ Err ( e) => {
170+ log_error ! ( self . logger, "Failed to parse payment instructions: {:?}" , e) ;
171+ return Err ( Error :: UriParameterParsingFailed ) ;
172+ } ,
173+ } ;
174+
175+ let resolved = match instructions {
176+ PaymentInstructions :: ConfigurableAmount ( ref instr) => {
177+ if let Some ( amount) = amount_msat {
178+ let amt = match BPIAmount :: from_sats ( amount) {
179+ Ok ( amt) => amt,
180+ Err ( e) => {
181+ log_error ! ( self . logger, "Error while coverting amount : {:?}" , e) ;
182+ return Err ( Error :: InvalidAmount ) ;
183+ } ,
184+ } ;
185+ match instr. clone ( ) . set_amount ( amt, self . hrn_resolver . as_ref ( ) ) . await {
186+ Ok ( resolved) => resolved,
187+ Err ( e) => {
188+ log_error ! ( self . logger, "Failed to set amount: {:?}" , e) ;
189+ return Err ( Error :: InvalidAmount ) ;
190+ } ,
191+ }
192+ } else {
193+ log_error ! ( self . logger, "No amount specified. Aborting the payment." ) ;
194+ return Err ( Error :: InvalidAmount ) ;
195+ }
196+ } ,
197+ PaymentInstructions :: FixedAmount ( ref instr) => instr. clone ( ) ,
198+ } ;
199+
200+ if let Some ( PaymentMethod :: LightningBolt12 ( offer) ) =
201+ resolved. methods ( ) . iter ( ) . find ( |m| matches ! ( m, PaymentMethod :: LightningBolt12 ( _) ) )
202+ {
203+ let offer = maybe_wrap ( offer. clone ( ) ) ;
204+ if let Some ( amount_msat) = amount_msat {
205+ match self . bolt12_payment . send_using_amount ( & offer, amount_msat, None , None ) {
206+ Ok ( payment_id) => return Ok ( PaymentResult :: Bolt12 { payment_id } ) ,
207+ Err ( e) => log_error ! ( self . logger, "Failed to send BOLT12 offer: {:?}. This is part of a unified payment. Falling back to the BOLT11 invoice." , e) ,
208+ }
209+ } else {
210+ match self . bolt12_payment . send ( & offer, None , None ) {
211+ Ok ( payment_id) => return Ok ( PaymentResult :: Bolt12 { payment_id } ) ,
212+ Err ( e) => log_error ! ( self . logger, "Failed to send BOLT12 offer: {:?}. This is part of a unified payment. Falling back to the BOLT11 invoice." , e) ,
213+ }
168214 }
169215 }
170216
171- if let Some ( invoice) = uri_network_checked. extras . bolt11_invoice {
172- let invoice = maybe_wrap ( invoice) ;
217+ if let Some ( PaymentMethod :: LightningBolt11 ( invoice) ) =
218+ resolved. methods ( ) . iter ( ) . find ( |m| matches ! ( m, PaymentMethod :: LightningBolt11 ( _) ) )
219+ {
220+ let invoice = maybe_wrap ( invoice. clone ( ) ) ;
173221 match self . bolt11_invoice . send ( & invoice, None ) {
174- Ok ( payment_id) => return Ok ( QrPaymentResult :: Bolt11 { payment_id } ) ,
175- Err ( e) => log_error ! ( self . logger, "Failed to send BOLT11 invoice: {:?}. This is part of a unified QR code payment. Falling back to the on-chain transaction." , e) ,
222+ Ok ( payment_id) => return Ok ( PaymentResult :: Bolt11 { payment_id } ) ,
223+ Err ( e) => log_error ! ( self . logger, "Failed to send BOLT11 invoice: {:?}. This is part of a unified payment. Falling back to the on-chain transaction." , e) ,
176224 }
177225 }
178226
179- let amount = match uri_network_checked. amount {
180- Some ( amount) => amount,
181- None => {
182- log_error ! ( self . logger, "No amount specified in the URI. Aborting the payment." ) ;
183- return Err ( Error :: InvalidAmount ) ;
184- } ,
185- } ;
186-
187- let txid = self . onchain_payment . send_to_address (
188- & uri_network_checked. address ,
189- amount. to_sat ( ) ,
190- None ,
191- ) ?;
227+ if let Some ( PaymentMethod :: OnChain ( address) ) =
228+ resolved. methods ( ) . iter ( ) . find ( |m| matches ! ( m, PaymentMethod :: OnChain ( _) ) )
229+ {
230+ let amount = match resolved. onchain_payment_amount ( ) {
231+ Some ( amount) => amount,
232+ None => {
233+ log_error ! ( self . logger, "No amount specified. Aborting the payment." ) ;
234+ return Err ( Error :: InvalidAmount ) ;
235+ } ,
236+ } ;
192237
193- Ok ( QrPaymentResult :: Onchain { txid } )
238+ let txid =
239+ self . onchain_payment . send_to_address ( & address, amount. sats ( ) . unwrap ( ) , None ) ?;
240+ return Ok ( PaymentResult :: Onchain { txid } ) ;
241+ }
242+ log_error ! ( self . logger, "Payable methods not found in URI" ) ;
243+ Err ( Error :: PaymentSendingFailed )
194244 }
195245}
196246
@@ -319,7 +369,7 @@ impl DeserializationError for Extras {
319369mod tests {
320370 use super :: * ;
321371 use crate :: payment:: unified:: Extras ;
322- use bitcoin:: { Address , Network } ;
372+ use bitcoin:: { address :: NetworkUnchecked , Address , Network } ;
323373 use std:: str:: FromStr ;
324374
325375 #[ test]
0 commit comments