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.
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);
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 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
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 = sdk.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
}
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;
var request = new PrepareLnurlPayRequest(
amountSats: amountSats,
payRequest: payRequest,
comment: optionalComment,
validateSuccessActionUrl: optionalValidateSuccessActionUrl
);
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");
}
Javascript
// Endpoint can also be of the
// 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
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 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
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 sdk.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 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
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 := sdk.Parse(lnurlPayUrl)
if sdkErr := err.(*breez_sdk_spark.SdkError); sdkErr != nil {
return nil, err
}
switch inputType := input.(type) {
case breez_sdk_spark.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, 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 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 validationPrepareLnurlPayRequest param to false.