LNURL-Pay

Preparing LNURL Payments

During the prepare step, the SDK ensures that the inputs are valid with respect to the LNURL-pay request, and also returns the relative fees related to the payment so they can be confirmed. If the LNURL-pay invoice includes a Magic Routing Hint for a direct Liquid payment, the fees will reflect this.

Rust
// Endpoint can also be of the form:
// lnurlp://domain.com/lnurl-pay?key=val
// lnurl1dp68gurn8ghj7mr0vdskc6r0wd6z7mrww4excttsv9un7um9wdekjmmw84jxywf5x43rvv35xgmr2enrxanr2cfcvsmnwe3jxcukvde48qukgdec89snwde3vfjxvepjxpjnjvtpxd3kvdnxx5crxwpjvyunsephsz36jf
let lnurl_pay_url = "lightning@address.com";

if let Ok(InputType::LnUrlPay { data: pd }) = parse(lnurl_pay_url).await {
    let amount_msat = pd.min_sendable;
    let optional_comment = Some("<comment>".to_string());
    let optional_validate_success_action_url = Some(true);

    let prepare_response = sdk.prepare_lnurl_pay(PrepareLnUrlPayRequest {
        data: pd,
        amount_msat,
        comment: optional_comment,
        validate_success_action_url: optional_validate_success_action_url,
    })
    .await?;

    // If the fees are acceptable, continue to create the LNURL Pay
    let fees_sat = prepare_response.fees_sat;
    info!("Fees: {} sats", fees_sat);    
}
Swift
// Endpoint can also be of the form:
// lnurlp://domain.com/lnurl-pay?key=val
// lnurl1dp68gurn8ghj7mr0vdskc6r0wd6z7mrww4excttsv9un7um9wdekjmmw84jxywf5x43rvv35xgmr2enrxanr2cfcvsmnwe3jxcukvde48qukgdec89snwde3vfjxvepjxpjnjvtpxd3kvdnxx5crxwpjvyunsephsz36jf
var response: LnUrlPayResult?
let lnurlPayUrl = "lightning@address.com"
if let inputType = try? parse(input: lnurlPayUrl) {
    if case.lnUrlPay(let `data`) = inputType {
        let amountMsat = data.minSendable
        let optionalComment = "<comment>"
        let optionalValidateSuccessActionUrl = true

        let req = PrepareLnUrlPayRequest(
            data: data, 
            amountMsat: amountMsat, 
            comment: optionalComment, 
            validateSuccessActionUrl: optionalValidateSuccessActionUrl
        )
        let prepareResponse = try? sdk.prepareLnurlPay(req: req)
            
        // If the fees are acceptable, continue to create the LNURL Pay
        let feesSat = prepareResponse!.feesSat
        print("Fees: {} sats", feesSat);
    }
}
Kotlin
// Endpoint can also be of the form:
// lnurlp://domain.com/lnurl-pay?key=val
// lnurl1dp68gurn8ghj7mr0vdskc6r0wd6z7mrww4excttsv9un7um9wdekjmmw84jxywf5x43rvv35xgmr2enrxanr2cfcvsmnwe3jxcukvde48qukgdec89snwde3vfjxvepjxpjnjvtpxd3kvdnxx5crxwpjvyunsephsz36jf
val lnurlPayUrl = "lightning@address.com";
try {
    val inputType = parse(lnurlPayUrl)
    if (inputType is InputType.LnUrlPay) {
        val requestData = inputType.data
        val amountMsat = requestData.minSendable
        val optionalComment = "<comment>";
        val optionalValidateSuccessActionUrl = true;

        val req = PrepareLnUrlPayRequest(
            requestData, 
            amountMsat, 
            optionalComment, 
            optionalValidateSuccessActionUrl)
        val prepareResponse = sdk.prepareLnurlPay(req)

        // If the fees are acceptable, continue to create the LNURL Pay
        val feesSat = prepareResponse.feesSat;
        // Log.v("Breez", "Fees: ${feesSat} sats")
    }
} catch (e: Exception) {
    // handle error
}
React Native
// Endpoint can also be of the
// lnurlp://domain.com/lnurl-pay?key=val
// lnurl1dp68gurn8ghj7mr0vdskc6r0wd6z7mrww4excttsv9un7um9wdekjmmw84jxywf5x43rvv35xgmr2enrxanr2cfcvsmnwe3jxcukvde48qukgdec89snwde3vfjxvepjxpjnjvtpxd3kvdnxx5crxwpjvyunsephsz36jf
const lnurlPayUrl = 'lightning@address.com'

const input = await parse(lnurlPayUrl)
if (input.type === InputTypeVariant.LN_URL_PAY) {
  const amountMsat = input.data.minSendable
  const optionalComment = '<comment>'
  const optionalValidateSuccessActionUrl = true

  const prepareResponse = await prepareLnurlPay({
    data: input.data,
    amountMsat,
    comment: optionalComment,
    validateSuccessActionUrl: optionalValidateSuccessActionUrl
  })

  // If the fees are acceptable, continue to create the LNURL Pay
  const feesSat = prepareResponse.feesSat
  console.log(`Fees: ${feesSat} sats`)
}
Dart
/// Endpoint can also be of the form:
/// lnurlp://domain.com/lnurl-pay?key=val
/// lnurl1dp68gurn8ghj7mr0vdskc6r0wd6z7mrww4excttsv9un7um9wdekjmmw84jxywf5x43rvv35xgmr2enrxanr2cfcvsmnwe3jxcukvde48qukgdec89snwde3vfjxvepjxpjnjvtpxd3kvdnxx5crxwpjvyunsephsz36jf
String lnurlPayUrl = "lightning@address.com";

InputType inputType = await parse(input: lnurlPayUrl);
if (inputType is InputType_LnUrlPay) {
  BigInt amountMsat = inputType.data.minSendable;
  String optionalComment = "<comment>";
  bool optionalValidateSuccessActionUrl = true;
  
  PrepareLnUrlPayRequest req = PrepareLnUrlPayRequest(
    data: inputType.data,
    amountMsat: amountMsat,
    comment: optionalComment,
    validateSuccessActionUrl: optionalValidateSuccessActionUrl,
  );
  PrepareLnUrlPayResponse prepareResponse = await breezSDKLiquid.instance!.prepareLnurlPay(req: req);
  
  // If the fees are acceptable, continue to create the LNURL Pay
  BigInt feesSat = prepareResponse.feesSat;
  print("Fees: $feesSat sats");
}
Python
# Endpoint can also be of the form:
# lnurlp://domain.com/lnurl-pay?key=val
# lnurl1dp68gurn8ghj7mr0vdskc6r0wd6z7mrww4excttsv9un7um9wdekjmmw84jxywf5x43rvv35xgmr2enrxanr2cfcvsmnwe3jxcukvde48qukgdec89snwde3vfjxvepjxpjnjvtpxd3kvdnxx5crxwpjvyunsephsz36jf
lnurl_pay_url = "lightning@address.com"
try: 
    parsed_input = breez_sdk_liquid.parse(lnurl_pay_url)
    if isinstance(parsed_input, InputType.LN_URL_PAY):
        amount_msat = parsed_input.data.min_sendable
        optional_comment = "<comment>"
        optional_validate_success_action_url = True

        req = PrepareLnUrlPayRequest(parsed_input.data,
                                     amount_msat, 
                                     optional_comment, 
                                     optional_validate_success_action_url)
        prepare_response = sdk.prepare_lnurl_pay(req)

        # If the fees are acceptable, continue to create the LNURL Pay
        fees_sat = prepare_response.fees_sat
        logging.debug("Fees: ", fees_sat, " sats")
        return prepare_response
except Exception as error:
    logging.error(error)
    raise 
Go
// Endpoint can also be of the form:
// lnurlp://domain.com/lnurl-pay?key=val
// lnurl1dp68gurn8ghj7mr0vdskc6r0wd6z7mrww4excttsv9un7um9wdekjmmw84jxywf5x43rvv35xgmr2enrxanr2cfcvsmnwe3jxcukvde48qukgdec89snwde3vfjxvepjxpjnjvtpxd3kvdnxx5crxwpjvyunsephsz36jf
lnurlPayUrl := "lightning@address.com"

if input, err := breez_sdk_liquid.Parse(lnurlPayUrl); err != nil {
    switch inputType := input.(type) {
    case breez_sdk_liquid.InputTypeLnUrlPay:
        amountMsat := inputType.Data.MinSendable
        optionalComment := "<comment>"
        optionalValidateSuccessActionUrl := true

        req := breez_sdk_liquid.PrepareLnUrlPayRequest{
            Data:                     inputType.Data,
            AmountMsat:               amountMsat,
            Comment:                  &optionalComment,
            ValidateSuccessActionUrl: &optionalValidateSuccessActionUrl,
        }
        prepareResponse, err := sdk.PrepareLnurlPay(req)
        if err != nil {
            log.Printf("Error: %#v", err)
            return
        }

        // If the fees are acceptable, continue to create the LNURL Pay
        feesSat := prepareResponse.FeesSat
        log.Printf("Fees: %v sats", feesSat)
    }
}
C#
// Endpoint can also be of the form:
// lnurlp://domain.com/lnurl-pay?key=val
// lnurl1dp68gurn8ghj7mr0vdskc6r0wd6z7mrww4excttsv9un7um9wdekjmmw84jxywf5x43rvv35xgmr2enrxanr2cfcvsmnwe3jxcukvde48qukgdec89snwde3vfjxvepjxpjnjvtpxd3kvdnxx5crxwpjvyunsephsz36jf
var lnurlPayUrl = "lightning@address.com";

try
{
    var input = BreezSdkLiquidMethods.Parse(lnurlPayUrl);
    if (input is InputType.LnUrlPay lnurlp)
    {
        var amountMsat = lnurlp.data.minSendable;
        var optionalComment = "<comment>";
        var optionalValidateSuccessActionUrl = true;
        
        var req = new PrepareLnUrlPayRequest(
            lnurlp.data, 
            amountMsat, 
            optionalComment, 
            optionalValidateSuccessActionUrl);
        var prepareResponse = sdk.PrepareLnurlPay(req);

        // If the fees are acceptable, continue to create the LNURL Pay
        var feesSat = prepareResponse.feesSat;
        Console.WriteLine($"Fees: {feesSat} sats");
    }
}
catch (Exception)
{
    // Handle error
}

LNURL Payments

Once the payment has been prepared and the fees are accepted, all you have to do is pass the prepare response as an argument to the LNURL pay method.

Rust
let result = sdk
    .lnurl_pay(LnUrlPayRequest { prepare_response })
    .await?;
Swift
let result = try? sdk.lnurlPay(req: LnUrlPayRequest (
    prepareResponse: prepareResponse
))
Kotlin
try {
    val result = sdk.lnurlPay(LnUrlPayRequest(prepareResponse))
} catch (e: Exception) {
    // handle error
}
React Native
const result = await lnurlPay({
  prepareResponse
})
Dart
LnUrlPayResult result = await breezSDKLiquid.instance!.lnurlPay(
  req: LnUrlPayRequest(prepareResponse: prepareResponse),
);
Python
try:
    result = sdk.lnurl_pay(LnUrlPayRequest(prepare_response))
except Exception as error:
    logging.error(error)
    raise
Go
req := breez_sdk_liquid.LnUrlPayRequest{
    PrepareResponse: prepareResponse,
}
if result, err := sdk.LnurlPay(req); err != nil {
    log.Printf("Result: %#v", result)
}
C#
try
{
    var result = sdk.LnurlPay(new LnUrlPayRequest(prepareResponse));
}
catch (Exception)
{
    // Handle error
}

Developer note

By default when the LNURL-pay results in a success action with a URL, the URL is validated to check if there is a mismatch with the LNURL callback domain. You can disable this behaviour by setting the optional validation PrepareLnUrlPayRequest param to false.

Supported Specs

  • LUD-01 LNURL bech32 encoding
  • LUD-06 payRequest spec
  • LUD-09 successAction field for payRequest
  • LUD-16 LN Address
  • LUD-17 Support for lnurlp prefix with non-bech32-encoded LNURL URLs