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.

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