Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Sending payments using LNURL-Pay and Lightning address

Preparing LNURL Payments API docs

During the prepare step, the SDK ensures that the inputs are valid with respect to the LNURL-pay request, and also returns the fees related to the payment so they can be confirmed.

Developer note

The minimum and maximum sendable amount returned from calling parse is denominated in millisatoshi.
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::LightningAddress(details)) = parse(lnurl_pay_url).await {
    let amount_sats = 5_000;
    let optional_comment = Some("<comment>".to_string());
    let optional_validate_success_action_url = Some(true);

    let prepare_response = sdk
        .prepare_lnurl_pay(PrepareLnurlPayRequest {
            amount_sats,
            pay_request: details.pay_request,
            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 fee_sats = prepare_response.fee_sats;
    info!("Fees: {fee_sats} sats");
}
Swift
// Endpoint can also be of the form:
// lnurlp://domain.com/lnurl-pay?key=val
// lnurl1dp68gurn8ghj7mr0vdskc6r0wd6z7mrww4excttsv9un7um9wdekjmmw84jxywf5x43rvv35xgmr2enrxanr2cfcvsmnwe3jxcukvde48qukgdec89snwde3vfjxvepjxpjnjvtpxd3kvdnxx5crxwpjvyunsephsz36jf
let lnurlPayUrl = "lightning@address.com"

let inputType = try await parse(input: lnurlPayUrl) 
if case .lightningAddress(v1: let details) = inputType {
    let amountSats: UInt64 = 5_000
    let optionalComment = "<comment>"
    let payRequest = details.payRequest
    let optionalValidateSuccessActionUrl = true

    let request = PrepareLnurlPayRequest(
        amountSats: amountSats,
        payRequest: payRequest,
        comment: optionalComment,
        validateSuccessActionUrl: optionalValidateSuccessActionUrl
    )
    let response = try await sdk.prepareLnurlPay(request: request)

    // If the fees are acceptable, continue to create the LNURL Pay
    let feesSat = response.feeSats
    print("Fees: \(feesSat) sats")
}

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.LightningAddress) {
        val amountSats = 5_000.toULong()
        val optionalComment = "<comment>"
        val payRequest = inputType.v1.payRequest
        val optionalValidateSuccessActionUrl = true

        val req = PrepareLnurlPayRequest(
            amountSats,
            payRequest,
            optionalComment,
            optionalValidateSuccessActionUrl
        )
        val prepareResponse = sdk.prepareLnurlPay(req)

        // If the fees are acceptable, continue to create the LNURL Pay
        val feeSats = prepareResponse.feeSats;
        // Log.v("Breez", "Fees: ${feeSats} sats")
    }
} catch (e: Exception) {
    // handle error
}
Javascript
// 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 === 'lightningAddress') {
  const amountSats = 5_000
  const optionalComment = '<comment>'
  const payRequest = input.payRequest
  const optionalValidateSuccessActionUrl = true

  const prepareResponse = await sdk.prepareLnurlPay({
    amountSats,
    payRequest,
    comment: optionalComment,
    validateSuccessActionUrl: optionalValidateSuccessActionUrl
  })

  // If the fees are acceptable, continue to create the LNURL Pay
  const feeSats = prepareResponse.feeSats
  console.log(`Fees: ${feeSats} sats`)
}
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 instanceof InputType.LightningAddress) {
  const amountSats = BigInt(5_000)
  const optionalComment = '<comment>'
  const payRequest = input.inner[0].payRequest
  const optionalValidateSuccessActionUrl = true

  const prepareResponse = await sdk.prepareLnurlPay({
    amountSats,
    payRequest,
    comment: optionalComment,
    validateSuccessActionUrl: optionalValidateSuccessActionUrl
  })

  // If the fees are acceptable, continue to create the LNURL Pay
  const feeSats = prepareResponse.feeSats
  console.log(`Fees: ${feeSats} sats`)
}
Flutter
/// 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_LightningAddress) {
  BigInt amountSats = BigInt.from(5000);
  String optionalComment = "<comment>";
  bool optionalValidateSuccessActionUrl = true;

  PrepareLnurlPayRequest request = PrepareLnurlPayRequest(
    amountSats: amountSats,
    payRequest: inputType.field0.payRequest,
    comment: optionalComment,
    validateSuccessActionUrl: optionalValidateSuccessActionUrl,
  );
  PrepareLnurlPayResponse prepareResponse =
      await sdk.prepareLnurlPay(request: request);

  // If the fees are acceptable, continue to create the LNURL Pay
  BigInt feeSats = prepareResponse.feeSats;
  print("Fees: $feeSats sats");
}
Python
# Endpoint can also be of the form:
# lnurlp://domain.com/lnurl-pay?key=val
# lnurl1dp68gurn8ghj7mr0vdskc6r0wd6z7mrww4excttsv9un7um9wdekjmmw84jxywf5x43r
#     vv35xgmr2enrxanr2cfcvsmnwe3jxcukvde48qukgdec89snwde3vfjxvepjxpjnjvtpxd3k
#     vdnxx5crxwpjvyunsephsz36jf
lnurl_pay_url = "lightning@address.com"
try:
    parsed_input = await parse(lnurl_pay_url)
    details = parsed_input[0]
    if isinstance(parsed_input, InputType.LIGHTNING_ADDRESS):
        amount_sats = 5_000
        optional_comment = "<comment>"
        pay_request = details.pay_request
        optional_validate_success_action_url = True

        request = PrepareLnurlPayRequest(
            amount_sats=amount_sats,
            pay_request=pay_request,
            comment=optional_comment,
            validate_success_action_url=optional_validate_success_action_url,
        )
        prepare_response = await sdk.prepare_lnurl_pay(request=request)

        # If the fees are acceptable, continue to create the LNURL Pay
        fee_sats = prepare_response.fee_sats
        logging.debug(f"Fees: {fee_sats} 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"

input, err := breez_sdk_spark.Parse(lnurlPayUrl)

if sdkErr := err.(*breez_sdk_spark.SdkError); sdkErr != nil {
    return nil, err
}

switch inputType := input.(type) {
case breez_sdk_common.InputTypeLightningAddress:
    amountSats := uint64(5_000)
    optionalComment := "<comment>"
    optionalValidateSuccessActionUrl := true

    request := breez_sdk_spark.PrepareLnurlPayRequest{
        AmountSats:               amountSats,
        PayRequest:               inputType.Field0.PayRequest,
        Comment:                  &optionalComment,
        ValidateSuccessActionUrl: &optionalValidateSuccessActionUrl,
    }

    response, err := sdk.PrepareLnurlPay(request)

    if sdkErr := err.(*breez_sdk_spark.SdkError); sdkErr != nil {
        return nil, err
    }

    // If the fees are acceptable, continue to create the LNURL Pay
    feeSats := response.FeeSats
    log.Printf("Fees: %v sats", feeSats)
    return &response, nil
}

LNURL Payments API docs

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 response = sdk.lnurl_pay(LnurlPayRequest { prepare_response }).await?;
Swift
let response = try await sdk.lnurlPay(
    request: LnurlPayRequest(
        prepareResponse: prepareResponse
    ))
Kotlin
try {
    val response = sdk.lnurlPay(LnurlPayRequest(prepareResponse))
} catch (e: Exception) {
    // handle error
}
Javascript
const response = await sdk.lnurlPay({
  prepareResponse
})
React Native
const response = await sdk.lnurlPay({
  prepareResponse
})
Flutter
LnurlPayResponse response = await sdk.lnurlPay(
  request: LnurlPayRequest(prepareResponse: prepareResponse),
);
Python
try:
    response = await sdk.lnurl_pay(
        LnurlPayRequest(prepare_response=prepare_response)
    )
except Exception as error:
    logging.error(error)
    raise


Go
request := breez_sdk_spark.LnurlPayRequest{
    PrepareResponse: prepareResponse,
}

response, err := sdk.LnurlPay(request)

if sdkErr := err.(*breez_sdk_spark.SdkError); sdkErr != nil {
    return nil, err
}

payment := response.Payment

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