Sending payments using LNURL-Pay and Lightning address
Preparing LNURL Payments API docs
During the prepare step, the SDK ensures that the inputs are valid with respect to the LNURL-pay request, and also returns the fees related to the payment so they can be confirmed.
Developer note
The minimum and maximum sendable amount returned from calling parse is denominated in millisatoshi.Rust
// Endpoint can also be of the form:
// lnurlp://domain.com/lnurl-pay?key=val
// lnurl1dp68gurn8ghj7mr0vdskc6r0wd6z7mrww4excttsv9un7um9wdekjmmw84jxywf5x43rvv35xgmr2enrxanr2cfcvsmnwe3jxcukvde48qukgdec89snwde3vfjxvepjxpjnjvtpxd3kvdnxx5crxwpjvyunsephsz36jf
let lnurl_pay_url = "lightning@address.com";
if let Ok(InputType::LightningAddress(details)) = parse(lnurl_pay_url).await {
let amount_sats = 5_000;
let optional_comment = Some("<comment>".to_string());
let optional_validate_success_action_url = Some(true);
let prepare_response = sdk
.prepare_lnurl_pay(PrepareLnurlPayRequest {
amount_sats,
pay_request: details.pay_request,
comment: optional_comment,
validate_success_action_url: optional_validate_success_action_url,
})
.await?;
// If the fees are acceptable, continue to create the LNURL Pay
let fee_sats = prepare_response.fee_sats;
info!("Fees: {fee_sats} sats");
}
Swift
// Endpoint can also be of the form:
// lnurlp://domain.com/lnurl-pay?key=val
// lnurl1dp68gurn8ghj7mr0vdskc6r0wd6z7mrww4excttsv9un7um9wdekjmmw84jxywf5x43rvv35xgmr2enrxanr2cfcvsmnwe3jxcukvde48qukgdec89snwde3vfjxvepjxpjnjvtpxd3kvdnxx5crxwpjvyunsephsz36jf
let lnurlPayUrl = "lightning@address.com"
let inputType = try await parse(input: lnurlPayUrl)
if case .lightningAddress(v1: let details) = inputType {
let amountSats: UInt64 = 5_000
let optionalComment = "<comment>"
let payRequest = details.payRequest
let optionalValidateSuccessActionUrl = true
let request = PrepareLnurlPayRequest(
amountSats: amountSats,
payRequest: payRequest,
comment: optionalComment,
validateSuccessActionUrl: optionalValidateSuccessActionUrl
)
let response = try await sdk.prepareLnurlPay(request: request)
// If the fees are acceptable, continue to create the LNURL Pay
let feesSat = response.feeSats
print("Fees: \(feesSat) sats")
}
Kotlin
// Endpoint can also be of the form:
// lnurlp://domain.com/lnurl-pay?key=val
// lnurl1dp68gurn8ghj7mr0vdskc6r0wd6z7mrww4excttsv9un7um9wdekjmmw84jxywf5x43rvv35xgmr2enrxanr2cfcvsmnwe3jxcukvde48qukgdec89snwde3vfjxvepjxpjnjvtpxd3kvdnxx5crxwpjvyunsephsz36jf
val lnurlPayUrl = "lightning@address.com"
try {
val inputType = parse(lnurlPayUrl)
if (inputType is InputType.LightningAddress) {
val amountSats = 5_000.toULong()
val optionalComment = "<comment>"
val payRequest = inputType.v1.payRequest
val optionalValidateSuccessActionUrl = true
val req = PrepareLnurlPayRequest(
amountSats,
payRequest,
optionalComment,
optionalValidateSuccessActionUrl
)
val prepareResponse = sdk.prepareLnurlPay(req)
// If the fees are acceptable, continue to create the LNURL Pay
val feeSats = prepareResponse.feeSats;
// Log.v("Breez", "Fees: ${feeSats} sats")
}
} catch (e: Exception) {
// handle error
}
Javascript
// Endpoint can also be of the
// lnurlp://domain.com/lnurl-pay?key=val
// lnurl1dp68gurn8ghj7mr0vdskc6r0wd6z7mrww4excttsv9un7um9wdekjmmw84jxywf5x43rvv35xgmr2enrxanr2cfcvsmnwe3jxcukvde48qukgdec89snwde3vfjxvepjxpjnjvtpxd3kvdnxx5crxwpjvyunsephsz36jf
const lnurlPayUrl = 'lightning@address.com'
const input = await parse(lnurlPayUrl)
if (input.type === 'lightningAddress') {
const amountSats = 5_000
const optionalComment = '<comment>'
const payRequest = input.payRequest
const optionalValidateSuccessActionUrl = true
const prepareResponse = await sdk.prepareLnurlPay({
amountSats,
payRequest,
comment: optionalComment,
validateSuccessActionUrl: optionalValidateSuccessActionUrl
})
// If the fees are acceptable, continue to create the LNURL Pay
const feeSats = prepareResponse.feeSats
console.log(`Fees: ${feeSats} sats`)
}
React Native
// Endpoint can also be of the
// lnurlp://domain.com/lnurl-pay?key=val
// lnurl1dp68gurn8ghj7mr0vdskc6r0wd6z7mrww4excttsv9un7um9wdekjmmw84jxywf5x43rvv35xgmr2enrxanr2cfcvsmnwe3jxcukvde48qukgdec89snwde3vfjxvepjxpjnjvtpxd3kvdnxx5crxwpjvyunsephsz36jf
const lnurlPayUrl = 'lightning@address.com'
const input = await parse(lnurlPayUrl)
if (input instanceof InputType.LightningAddress) {
const amountSats = BigInt(5_000)
const optionalComment = '<comment>'
const payRequest = input.inner[0].payRequest
const optionalValidateSuccessActionUrl = true
const prepareResponse = await sdk.prepareLnurlPay({
amountSats,
payRequest,
comment: optionalComment,
validateSuccessActionUrl: optionalValidateSuccessActionUrl
})
// If the fees are acceptable, continue to create the LNURL Pay
const feeSats = prepareResponse.feeSats
console.log(`Fees: ${feeSats} sats`)
}
Flutter
/// Endpoint can also be of the form:
/// lnurlp://domain.com/lnurl-pay?key=val
/// lnurl1dp68gurn8ghj7mr0vdskc6r0wd6z7mrww4excttsv9un7um9wdekjmmw84jxywf5x43rvv35xgmr2enrxanr2cfcvsmnwe3jxcukvde48qukgdec89snwde3vfjxvepjxpjnjvtpxd3kvdnxx5crxwpjvyunsephsz36jf
String lnurlPayUrl = "lightning@address.com";
InputType inputType = await parse(input: lnurlPayUrl);
if (inputType is InputType_LightningAddress) {
BigInt amountSats = BigInt.from(5000);
String optionalComment = "<comment>";
bool optionalValidateSuccessActionUrl = true;
PrepareLnurlPayRequest request = PrepareLnurlPayRequest(
amountSats: amountSats,
payRequest: inputType.field0.payRequest,
comment: optionalComment,
validateSuccessActionUrl: optionalValidateSuccessActionUrl,
);
PrepareLnurlPayResponse prepareResponse =
await sdk.prepareLnurlPay(request: request);
// If the fees are acceptable, continue to create the LNURL Pay
BigInt feeSats = prepareResponse.feeSats;
print("Fees: $feeSats sats");
}
Python
# Endpoint can also be of the form:
# lnurlp://domain.com/lnurl-pay?key=val
# lnurl1dp68gurn8ghj7mr0vdskc6r0wd6z7mrww4excttsv9un7um9wdekjmmw84jxywf5x43r
# vv35xgmr2enrxanr2cfcvsmnwe3jxcukvde48qukgdec89snwde3vfjxvepjxpjnjvtpxd3k
# vdnxx5crxwpjvyunsephsz36jf
lnurl_pay_url = "lightning@address.com"
try:
parsed_input = await parse(lnurl_pay_url)
details = parsed_input[0]
if isinstance(parsed_input, InputType.LIGHTNING_ADDRESS):
amount_sats = 5_000
optional_comment = "<comment>"
pay_request = details.pay_request
optional_validate_success_action_url = True
request = PrepareLnurlPayRequest(
amount_sats=amount_sats,
pay_request=pay_request,
comment=optional_comment,
validate_success_action_url=optional_validate_success_action_url,
)
prepare_response = await sdk.prepare_lnurl_pay(request=request)
# If the fees are acceptable, continue to create the LNURL Pay
fee_sats = prepare_response.fee_sats
logging.debug(f"Fees: {fee_sats} sats")
return prepare_response
except Exception as error:
logging.error(error)
raise
Go
// Endpoint can also be of the form:
// lnurlp://domain.com/lnurl-pay?key=val
// lnurl1dp68gurn8ghj7mr0vdskc6r0wd6z7mrww4excttsv9un7um9wdekjmmw84jxywf5x43rvv35xgmr2enrxanr2cfcvsmnwe3jxcukvde48qukgdec89snwde3vfjxvepjxpjnjvtpxd3kvdnxx5crxwpjvyunsephsz36jf
lnurlPayUrl := "lightning@address.com"
input, err := breez_sdk_spark.Parse(lnurlPayUrl)
if sdkErr := err.(*breez_sdk_spark.SdkError); sdkErr != nil {
return nil, err
}
switch inputType := input.(type) {
case breez_sdk_common.InputTypeLightningAddress:
amountSats := uint64(5_000)
optionalComment := "<comment>"
optionalValidateSuccessActionUrl := true
request := breez_sdk_spark.PrepareLnurlPayRequest{
AmountSats: amountSats,
PayRequest: inputType.Field0.PayRequest,
Comment: &optionalComment,
ValidateSuccessActionUrl: &optionalValidateSuccessActionUrl,
}
response, err := sdk.PrepareLnurlPay(request)
if sdkErr := err.(*breez_sdk_spark.SdkError); sdkErr != nil {
return nil, err
}
// If the fees are acceptable, continue to create the LNURL Pay
feeSats := response.FeeSats
log.Printf("Fees: %v sats", feeSats)
return &response, nil
}
LNURL Payments API docs
Once the payment has been prepared and the fees are accepted, all you have to do is pass the prepare response as an argument to the LNURL pay method.
Rust
let response = sdk.lnurl_pay(LnurlPayRequest { prepare_response }).await?;
Swift
let response = try await sdk.lnurlPay(
request: LnurlPayRequest(
prepareResponse: prepareResponse
))
Kotlin
try {
val response = sdk.lnurlPay(LnurlPayRequest(prepareResponse))
} catch (e: Exception) {
// handle error
}
Javascript
const response = await sdk.lnurlPay({
prepareResponse
})
React Native
const response = await sdk.lnurlPay({
prepareResponse
})
Flutter
LnurlPayResponse response = await sdk.lnurlPay(
request: LnurlPayRequest(prepareResponse: prepareResponse),
);
Python
try:
response = await sdk.lnurl_pay(
LnurlPayRequest(prepare_response=prepare_response)
)
except Exception as error:
logging.error(error)
raise
Go
request := breez_sdk_spark.LnurlPayRequest{
PrepareResponse: prepareResponse,
}
response, err := sdk.LnurlPay(request)
if sdkErr := err.(*breez_sdk_spark.SdkError); sdkErr != nil {
return nil, err
}
payment := response.Payment
Developer note
By default when the LNURL-pay results in a success action with a URL, the URL is validated to check if there is a mismatch with the LNURL callback domain. You can disable this behaviour by setting the optional validationPrepareLnurlPayRequest
param to false.