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.
// 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");
}
// 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")
}
// 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
}
// 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");
}
// 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`)
}
// 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`)
}
// 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");
}
# 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
// 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.
// 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
// 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
// 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
// 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
// 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
// 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
// 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
# 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
// 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.
let optional_idempotency_key = Some("<idempotency key uuid>".to_string());
let response = sdk
.lnurl_pay(LnurlPayRequest {
prepare_response,
idempotency_key: optional_idempotency_key,
})
.await?;
let optionalIdempotencyKey = "<idempotency key uuid>"
let response = try await sdk.lnurlPay(
request: LnurlPayRequest(
prepareResponse: prepareResponse,
idempotencyKey: optionalIdempotencyKey
))
try {
val optionalIdempotencyKey = "<idempotency key uuid>"
val response = sdk.lnurlPay(LnurlPayRequest(prepareResponse, optionalIdempotencyKey))
} catch (e: Exception) {
// handle error
}
var optionalIdempotencyKey = "<idempotency key uuid>";
var response = await sdk.LnurlPay(
new LnurlPayRequest(
prepareResponse: prepareResponse,
idempotencyKey: optionalIdempotencyKey
)
);
const optionalIdempotencyKey = '<idempotency key uuid>'
const response = await sdk.lnurlPay({
prepareResponse,
idempotencyKey: optionalIdempotencyKey
})
const optionalIdempotencyKey = '<idempotency key uuid>'
const response = await sdk.lnurlPay({
prepareResponse,
idempotencyKey: optionalIdempotencyKey
})
String? optionalIdempotencyKey = "<idempotency key uuid>";
LnurlPayResponse response = await sdk.lnurlPay(
request: LnurlPayRequest(
prepareResponse: prepareResponse,
idempotencyKey: optionalIdempotencyKey),
);
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
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 validationPrepareLnurlPayRequest param to false.
Managing contacts
You can save frequently used Lightning addresses as contacts for quick access. See Managing contacts for details.