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.

Payments can be sent without holding Bitcoin by converting on-the-fly as a step before sending a payment. See Converting tokens for more information.

Setting the receiver amount

When you want the payment recipient to receive a specific amount.

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)) = sdk.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);
    // Optionally set to use token funds to pay via token conversion
    let optional_max_slippage_bps = Some(50);
    let optional_completion_timeout_secs = Some(30);
    let optional_conversion_options = Some(ConversionOptions {
        conversion_type: ConversionType::ToBitcoin {
            from_token_identifier: "<token identifier>".to_string(),
        },
        max_slippage_bps: optional_max_slippage_bps,
        completion_timeout_secs: optional_completion_timeout_secs,
    });

    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,
            conversion_options: optional_conversion_options,
            fee_policy: None,
        })
        .await?;

    // If the fees are acceptable, continue to create the LNURL Pay
    if let Some(conversion_estimate) = &prepare_response.conversion_estimate {
        info!("Estimated conversion amount: {} token base units", conversion_estimate.amount);
        info!("Estimated conversion fee: {} token base units", conversion_estimate.fee);
    }

    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 sdk.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
    // Optionally set to use token funds to pay via token conversion
    let optionalMaxSlippageBps = UInt32(50)
    let optionalCompletionTimeoutSecs = UInt32(30)
    let conversionOptions = ConversionOptions(
        conversionType: ConversionType.toBitcoin(
            fromTokenIdentifier: "<token identifier>"
        ),
        maxSlippageBps: optionalMaxSlippageBps,
        completionTimeoutSecs: optionalCompletionTimeoutSecs
    )

    let request = PrepareLnurlPayRequest(
        amountSats: amountSats,
        payRequest: payRequest,
        comment: optionalComment,
        validateSuccessActionUrl: optionalValidateSuccessActionUrl,
        conversionOptions: conversionOptions,
        feePolicy: nil
    )
    let prepareResponse = try await sdk.prepareLnurlPay(request: request)

    // If the fees are acceptable, continue to create the LNURL Pay
    if let conversionEstimate = prepareResponse.conversionEstimate {
        print("Estimated conversion amount: \(conversionEstimate.amount) token base units")
        print("Estimated conversion fee: \(conversionEstimate.fee) token base units")
    }

    let feeSats = prepareResponse.feeSats
    print("Fees: \(feeSats) 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 = sdk.parse(lnurlPayUrl)
    if (inputType is InputType.LightningAddress) {
        val amountSats = 5_000.toULong()
        val optionalComment = "<comment>"
        val payRequest = inputType.v1.payRequest
        val optionalValidateSuccessActionUrl = true
        // Optionally set to use token funds to pay via token conversion
        val optionalMaxSlippageBps = 50u
        val optionalCompletionTimeoutSecs = 30u
        val optionalConversionOptions = ConversionOptions(
            conversionType = ConversionType.ToBitcoin(
                "<token identifier>"
            ),
            maxSlippageBps = optionalMaxSlippageBps,
            completionTimeoutSecs = optionalCompletionTimeoutSecs
        )

        val req = PrepareLnurlPayRequest(
            amountSats = amountSats,
            payRequest = payRequest,
            comment = optionalComment,
            validateSuccessActionUrl = optionalValidateSuccessActionUrl,
            conversionOptions = optionalConversionOptions,
            feePolicy = null,
        )
        val prepareResponse = sdk.prepareLnurlPay(req)

        // If the fees are acceptable, continue to create the LNURL Pay
        prepareResponse.conversionEstimate?.let { conversionEstimate ->
            // Log.v("Breez", "Estimated conversion amount: ${conversionEstimate.amount} token base units")
            // Log.v("Breez", "Estimated conversion fee: ${conversionEstimate.fee} token base units")
        }

        val feeSats = prepareResponse.feeSats;
        // Log.v("Breez", "Fees: ${feeSats} sats")
    }
} catch (e: Exception) {
    // handle error
}
C#
// Endpoint can also be of the form:
// lnurlp://domain.com/lnurl-pay?key=val
// lnurl1dp68gurn8ghj7mr0vdskc6r0wd6z7mrww4excttsv9un7um9wdekjmmw84jxywf5x43r
//     vv35xgmr2enrxanr2cfcvsmnwe3jxcukvde48qukgdec89snwde3vfjxvepjxpjnjvtpxd3k
//     vdnxx5crxwpjvyunsephsz36jf
var lnurlPayUrl = "lightning@address.com";
var parsedInput = await sdk.Parse(lnurlPayUrl);
if (parsedInput is InputType.LightningAddress lightningAddress)
{
    var details = lightningAddress.v1;
    var amountSats = 5_000UL;
    var optionalComment = "<comment>";
    var payRequest = details.payRequest;
    var optionalValidateSuccessActionUrl = true;
    // Optionally set to use token funds to pay via token conversion
    var optionalMaxSlippageBps = 50U;
    var optionalCompletionTimeoutSecs = 30U;
    var optionalConversionOptions = new ConversionOptions(
        conversionType: new ConversionType.ToBitcoin(
            fromTokenIdentifier: "<token identifier>"
        ),
        maxSlippageBps: optionalMaxSlippageBps,
        completionTimeoutSecs: optionalCompletionTimeoutSecs
    );

    var request = new PrepareLnurlPayRequest(
        amountSats: amountSats,
        payRequest: payRequest,
        comment: optionalComment,
        validateSuccessActionUrl: optionalValidateSuccessActionUrl,
        conversionOptions: optionalConversionOptions,
        feePolicy: null
    );
    var prepareResponse = await sdk.PrepareLnurlPay(request: request);

    // If the fees are acceptable, continue to create the LNURL Pay
    if (prepareResponse.conversionEstimate != null)
    {
        Console.WriteLine("Estimated conversion amount: " +
            $"{prepareResponse.conversionEstimate.amount} token base units");
        Console.WriteLine("Estimated conversion fee: " +
            $"{prepareResponse.conversionEstimate.fee} token base units");
    }
    var feeSats = prepareResponse.feeSats;
    Console.WriteLine($"Fees: {feeSats} sats");
}
Javascript
// Endpoint can also be of the form:
// lnurlp://domain.com/lnurl-pay?key=val
// lnurl1dp68gurn8ghj7mr0vdskc6r0wd6z7mrww4excttsv9un7um9wdekjmmw84jxywf5x43rvv35xgmr2enrxanr2cfcvsmnwe3jxcukvde48qukgdec89snwde3vfjxvepjxpjnjvtpxd3kvdnxx5crxwpjvyunsephsz36jf
const lnurlPayUrl = 'lightning@address.com'

const input = await sdk.parse(lnurlPayUrl)
if (input.type === 'lightningAddress') {
  const amountSats = 5_000
  const optionalComment = '<comment>'
  const payRequest = input.payRequest
  const optionalValidateSuccessActionUrl = true
  // Optionally set to use token funds to pay via token conversion
  const optionalMaxSlippageBps = 50
  const optionalCompletionTimeoutSecs = 30
  const optionalConversionOptions: ConversionOptions = {
    conversionType: {
      type: 'toBitcoin',
      fromTokenIdentifier: '<token identifier>'
    },
    maxSlippageBps: optionalMaxSlippageBps,
    completionTimeoutSecs: optionalCompletionTimeoutSecs
  }

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

  // If the fees are acceptable, continue to create the LNURL Pay
  if (prepareResponse.conversionEstimate !== undefined) {
    const conversionEstimate = prepareResponse.conversionEstimate
    console.debug(`Estimated conversion amount: ${conversionEstimate.amount} token base units`)
    console.debug(`Estimated conversion fee: ${conversionEstimate.fee} token base units`)
  }

  const feeSats = prepareResponse.feeSats
  console.log(`Fees: ${feeSats} sats`)
}
React Native
// Endpoint can also be of the form:
// lnurlp://domain.com/lnurl-pay?key=val
// lnurl1dp68gurn8ghj7mr0vdskc6r0wd6z7mrww4excttsv9un7um9wdekjmmw84jxywf5x43rvv35xgmr2enrxanr2cfcvsmnwe3jxcukvde48qukgdec89snwde3vfjxvepjxpjnjvtpxd3kvdnxx5crxwpjvyunsephsz36jf
const lnurlPayUrl = 'lightning@address.com'

const input = await sdk.parse(lnurlPayUrl)
if (input.tag === InputType_Tags.LightningAddress) {
  const amountSats = BigInt(5_000)
  const optionalComment = '<comment>'
  const payRequest = input.inner[0].payRequest
  const optionalValidateSuccessActionUrl = true
  // Optionally set to use token funds to pay via token conversion
  const optionalMaxSlippageBps = 50
  const optionalCompletionTimeoutSecs = 30
  const optionalConversionOptions = {
    conversionType: new ConversionType.ToBitcoin({
      fromTokenIdentifier: '<token identifier>'
    }),
    maxSlippageBps: optionalMaxSlippageBps,
    completionTimeoutSecs: optionalCompletionTimeoutSecs
  }

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

  // If the fees are acceptable, continue to create the LNURL Pay
  if (prepareResponse.conversionEstimate !== undefined) {
    const conversionEstimate = prepareResponse.conversionEstimate
    console.debug(`Estimated conversion amount: ${conversionEstimate.amount} token base units`)
    console.debug(`Estimated conversion fee: ${conversionEstimate.fee} token base units`)
  }

  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 sdk.parse(input: lnurlPayUrl);
if (inputType is InputType_LightningAddress) {
  BigInt amountSats = BigInt.from(5000);
  String optionalComment = "<comment>";
  bool optionalValidateSuccessActionUrl = true;
  // Optionally set to use token funds to pay via token conversion
  int optionalMaxSlippageBps = 50;
  int optionalCompletionTimeoutSecs = 30;
  final optionalConversionOptions = ConversionOptions(
    conversionType: ConversionType.toBitcoin(
      fromTokenIdentifier: "<token identifier>",
    ),
    maxSlippageBps: optionalMaxSlippageBps,
    completionTimeoutSecs: optionalCompletionTimeoutSecs,
  );

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

  // If the fees are acceptable, continue to create the LNURL Pay
  if (prepareResponse.conversionEstimate != null) {
    print(
        "Estimated conversion amount: ${prepareResponse.conversionEstimate!.amount} token base units");
    print(
        "Estimated conversion fee: ${prepareResponse.conversionEstimate!.fee} token base units");
  }

  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 sdk.parse(lnurl_pay_url)
    if isinstance(parsed_input, InputType.LIGHTNING_ADDRESS):
        details = parsed_input[0]
        amount_sats = 5_000
        optional_comment = "<comment>"
        pay_request = details.pay_request
        optional_validate_success_action_url = True
        # Optionally set to use token funds to pay via token conversion
        optional_max_slippage_bps = 50
        optional_completion_timeout_secs = 30
        optional_conversion_options = ConversionOptions(
            conversion_type=ConversionType.TO_BITCOIN(
                from_token_identifier="<token identifier>"
            ),
            max_slippage_bps=optional_max_slippage_bps,
            completion_timeout_secs=optional_completion_timeout_secs,
        )

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

        # If the fees are acceptable, continue to create the LNURL Pay
        if prepare_response.conversion_estimate is not None:
            conversion_estimate = prepare_response.conversion_estimate
            logging.debug(
                f"Estimated conversion amount: {conversion_estimate.amount} token base units"
            )
            logging.debug(
                f"Estimated conversion fee: {conversion_estimate.fee} token base units"
            )

        logging.debug(f"Fees: {prepare_response.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 := sdk.Parse(lnurlPayUrl)

if err != nil {
	var sdkErr *breez_sdk_spark.SdkError
	if errors.As(err, &sdkErr) {
		// Handle SdkError - can inspect specific variants if needed
		// e.g., switch on sdkErr variant for InsufficientFunds, NetworkError, etc.
	}
	return nil, err
}

switch inputType := input.(type) {
case breez_sdk_spark.InputTypeLightningAddress:
	amountSats := uint64(5_000)
	optionalComment := "<comment>"
	optionalValidateSuccessActionUrl := true
	// Optionally set to use token funds to pay via token conversion
	optionalMaxSlippageBps := uint32(50)
	optionalCompletionTimeoutSecs := uint32(30)
	optionalConversionOptions := breez_sdk_spark.ConversionOptions{
		ConversionType: breez_sdk_spark.ConversionTypeToBitcoin{
			FromTokenIdentifier: "<token identifier>",
		},
		MaxSlippageBps:        &optionalMaxSlippageBps,
		CompletionTimeoutSecs: &optionalCompletionTimeoutSecs,
	}

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

	prepareResponse, err := sdk.PrepareLnurlPay(request)

	if err != nil {
		var sdkErr *breez_sdk_spark.SdkError
		if errors.As(err, &sdkErr) {
			// Handle SdkError - can inspect specific variants if needed
			// e.g., switch on sdkErr variant for InsufficientFunds, NetworkError, etc.
		}
		return nil, err
	}

	// If the fees are acceptable, continue to create the LNURL Pay
	if prepareResponse.ConversionEstimate != nil {
		log.Printf("Estimated conversion amount: %v token base units", prepareResponse.ConversionEstimate.Amount)
		log.Printf("Estimated conversion fee: %v token base units", prepareResponse.ConversionEstimate.Fee)
	}

	feeSats := prepareResponse.FeeSats
	log.Printf("Fees: %v sats", feeSats)
	return &prepareResponse, nil
}

Setting the fee policy

By default, fees are added on top of the amount (FeePolicy::FeesExcludedFeePolicy.FEES_EXCLUDEDFeePolicy.feesExcludedFeePolicy.FeesExcludedFeePolicy.FeesExcludedFeePolicy.FeesExcludedFeePolicy.FeesExcludedFeePolicyFeesExcludedFeePolicy.FeesExcluded). Use FeePolicy::FeesIncludedFeePolicy.FEES_INCLUDEDFeePolicy.feesIncludedFeePolicy.FeesIncludedFeePolicy.FeesIncludedFeePolicy.FeesIncludedFeePolicy.FeesIncludedFeePolicyFeesIncludedFeePolicy.FeesIncluded to deduct fees from the amount instead—the receiver gets the amount minus fees.

This is particularly useful when you want to spend your entire balance in a single payment—simply provide your full balance as the amount.

Rust
// By default (FeePolicy::FeesExcluded), fees are added on top of the amount.
// Use FeePolicy::FeesIncluded to deduct fees from the amount instead.
// The receiver gets amount minus fees.
let optional_comment = Some("<comment>".to_string());
let optional_validate_success_action_url = Some(true);
let amount_sats = 5_000;

let prepare_response = sdk
    .prepare_lnurl_pay(PrepareLnurlPayRequest {
        amount_sats,
        pay_request,
        comment: optional_comment,
        validate_success_action_url: optional_validate_success_action_url,
        conversion_options: None,
        fee_policy: Some(FeePolicy::FeesIncluded),
    })
    .await?;

// If the fees are acceptable, continue to create the LNURL Pay
let fee_sats = prepare_response.fee_sats;
info!("Fees: {fee_sats} sats");
// The receiver gets amount_sats - fee_sats
Swift
// By default (.feesExcluded), fees are added on top of the amount.
// Use .feesIncluded to deduct fees from the amount instead.
// The receiver gets amount minus fees.
let amountSats: UInt64 = 5_000
let optionalComment = "<comment>"
let optionalValidateSuccessActionUrl = true

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

// If the fees are acceptable, continue to create the LNURL Pay
let feeSats = response.feeSats
print("Fees: \(feeSats) sats")
// The receiver gets amountSats - feeSats
Kotlin
// By default (FeePolicy.FEES_EXCLUDED), fees are added on top of the amount.
// Use FeePolicy.FEES_INCLUDED to deduct fees from the amount instead.
// The receiver gets amount minus fees.
val optionalComment = "<comment>"
val optionalValidateSuccessActionUrl = true
val amountSats = 5_000.toULong()

val req = PrepareLnurlPayRequest(
    amountSats = amountSats,
    payRequest = payRequest,
    comment = optionalComment,
    validateSuccessActionUrl = optionalValidateSuccessActionUrl,
    conversionOptions = null,
    feePolicy = FeePolicy.FEES_INCLUDED,
)
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")
// The receiver gets amountSats - feeSats
C#
// By default (FeePolicy.FeesExcluded), fees are added on top of the amount.
// Use FeePolicy.FeesIncluded to deduct fees from the amount instead.
// The receiver gets amount minus fees.
var amountSats = 5_000UL;
var optionalComment = "<comment>";
var optionalValidateSuccessActionUrl = true;

var request = new PrepareLnurlPayRequest(
    amountSats: amountSats,
    payRequest: payRequest,
    comment: optionalComment,
    validateSuccessActionUrl: optionalValidateSuccessActionUrl,
    conversionOptions: null,
    feePolicy: FeePolicy.FeesIncluded
);
var prepareResponse = await sdk.PrepareLnurlPay(request: request);

// If the fees are acceptable, continue to create the LNURL Pay
var feeSats = prepareResponse.feeSats;
Console.WriteLine($"Fees: {feeSats} sats");
// The receiver gets amountSats - feeSats
Javascript
// By default ({ type: 'feesExcluded' }), fees are added on top of the amount.
// Use { type: 'feesIncluded' } to deduct fees from the amount instead.
// The receiver gets amount minus fees.
const optionalComment = '<comment>'
const optionalValidateSuccessActionUrl = true
const amountSats = 5_000
const feePolicy: FeePolicy = 'feesIncluded'

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

// If the fees are acceptable, continue to create the LNURL Pay
const feeSats = prepareResponse.feeSats
console.log(`Fees: ${feeSats} sats`)
// The receiver gets amountSats - feeSats
React Native
// By default (FeePolicy.FeesExcluded), fees are added on top of the amount.
// Use FeePolicy.FeesIncluded to deduct fees from the amount instead.
// The receiver gets amount minus fees.
const optionalComment = '<comment>'
const optionalValidateSuccessActionUrl = true
const amountSats = BigInt(5_000)

const prepareResponse = await sdk.prepareLnurlPay({
  amountSats,
  payRequest,
  comment: optionalComment,
  validateSuccessActionUrl: optionalValidateSuccessActionUrl,
  conversionOptions: undefined,
  feePolicy: FeePolicy.FeesIncluded
})

// If the fees are acceptable, continue to create the LNURL Pay
const feeSats = prepareResponse.feeSats
console.log(`Fees: ${feeSats} sats`)
// The receiver gets amountSats - feeSats
Flutter
// By default (FeePolicy.feesExcluded), fees are added on top of the amount.
// Use FeePolicy.feesIncluded to deduct fees from the amount instead.
// The receiver gets amount minus fees.
String optionalComment = "<comment>";
bool optionalValidateSuccessActionUrl = true;
BigInt amountSats = BigInt.from(5000);

PrepareLnurlPayRequest request = PrepareLnurlPayRequest(
  amountSats: amountSats,
  payRequest: payRequest,
  comment: optionalComment,
  validateSuccessActionUrl: optionalValidateSuccessActionUrl,
  conversionOptions: null,
  feePolicy: FeePolicy.feesIncluded,
);
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");
// The receiver gets amountSats - feeSats
Python
# By default (FeePolicy.FEES_EXCLUDED), fees are added on top of the amount.
# Use FeePolicy.FEES_INCLUDED to deduct fees from the amount instead.
# The receiver gets amount minus fees.
amount_sats = 5_000
optional_comment = "<comment>"
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,
    conversion_options=None,
    fee_policy=FeePolicy.FEES_INCLUDED,
)
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")
# The receiver gets amount_sats - fee_sats
Go
// By default (FeePolicyFeesExcluded), fees are added on top of the amount.
// Use FeePolicyFeesIncluded to deduct fees from the amount instead.
// The receiver gets amount minus fees.
amountSats := uint64(5_000)
optionalComment := "<comment>"
optionalValidateSuccessActionUrl := true
feePolicy := breez_sdk_spark.FeePolicyFeesIncluded

request := breez_sdk_spark.PrepareLnurlPayRequest{
	AmountSats:               amountSats,
	PayRequest:               payRequest,
	Comment:                  &optionalComment,
	ValidateSuccessActionUrl: &optionalValidateSuccessActionUrl,
	ConversionOptions:        nil,
	FeePolicy:                &feePolicy,
}

response, err := sdk.PrepareLnurlPay(request)
if err != nil {
	return nil, err
}

// If the fees are acceptable, continue to create the LNURL Pay
feeSats := response.FeeSats
log.Printf("Fees: %v sats", feeSats)
// The receiver gets amountSats - feeSats

LNURL Payments API docs

Once the payment has been prepared and the fees are accepted, the payment can be sent by passing:

  • Prepare Response - The response from the Preparing LNURL Payments step.
  • Idempotency Key - An optional UUID that identifies the payment. If set, providing the same idempotency key for multiple requests will ensure that only one payment is made.
Rust
let optional_idempotency_key = Some("<idempotency key uuid>".to_string());
let response = sdk
    .lnurl_pay(LnurlPayRequest {
        prepare_response,
        idempotency_key: optional_idempotency_key,
    })
    .await?;
Swift
let optionalIdempotencyKey = "<idempotency key uuid>"
let response = try await sdk.lnurlPay(
    request: LnurlPayRequest(
        prepareResponse: prepareResponse,
        idempotencyKey: optionalIdempotencyKey
    ))
Kotlin
try {
    val optionalIdempotencyKey = "<idempotency key uuid>"
    val response = sdk.lnurlPay(LnurlPayRequest(prepareResponse, optionalIdempotencyKey))
} catch (e: Exception) {
    // handle error
}
C#
var optionalIdempotencyKey = "<idempotency key uuid>";
var response = await sdk.LnurlPay(
    new LnurlPayRequest(
        prepareResponse: prepareResponse,
        idempotencyKey: optionalIdempotencyKey
    )
);
Javascript
const optionalIdempotencyKey = '<idempotency key uuid>'
const response = await sdk.lnurlPay({
  prepareResponse,
  idempotencyKey: optionalIdempotencyKey
})
React Native
const optionalIdempotencyKey = '<idempotency key uuid>'
const response = await sdk.lnurlPay({
  prepareResponse,
  idempotencyKey: optionalIdempotencyKey
})
Flutter
String? optionalIdempotencyKey = "<idempotency key uuid>";
LnurlPayResponse response = await sdk.lnurlPay(
  request: LnurlPayRequest(
    prepareResponse: prepareResponse,
    idempotencyKey: optionalIdempotencyKey),
);
Python
try:
    optional_idempotency_key = "<idempotency key uuid>"
    response = await sdk.lnurl_pay(
        LnurlPayRequest(
            prepare_response=prepare_response,
            idempotency_key=optional_idempotency_key,
        )
    )
except Exception as error:
    logging.error(error)
    raise
Go
optionalIdempotencyKey := "<idempotency key uuid>"
request := breez_sdk_spark.LnurlPayRequest{
	PrepareResponse: prepareResponse,
	IdempotencyKey:  &optionalIdempotencyKey,
}

response, err := sdk.LnurlPay(request)
if err != nil {
	var sdkErr *breez_sdk_spark.SdkError
	if errors.As(err, &sdkErr) {
		// Handle SdkError - can inspect specific variants if needed
		// e.g., switch on sdkErr variant for InsufficientFunds, NetworkError, etc.
	}
	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.

Managing contacts

You can save frequently used Lightning addresses as contacts for quick access. See Managing contacts for details.

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