Parsing inputs
The SDK provides a versatile and extensible parsing module designed to process a wide range of input strings and return parsed data in various standardized formats.
Natively supported formats include: BOLT11 invoices, LNURLs of different types, Bitcoin addresses, Spark addresses, and others. For the complete list, consult the API documentation.
Developer note
The amounts returned from calling parse on Lightning based inputs (BOLT11, LNURL) are denominated in millisatoshi.let input = "an input to be parsed...";
match sdk.parse(input).await? {
InputType::BitcoinAddress(details) => {
println!("Input is Bitcoin address {}", details.address);
}
InputType::Bolt11Invoice(details) => {
println!(
"Input is BOLT11 invoice for {} msats",
details
.amount_msat
.map_or("unknown".to_string(), |a| a.to_string())
);
}
InputType::LnurlPay(details) => {
println!(
"Input is LNURL-Pay/Lightning address accepting min/max {}/{} msats",
details.min_sendable, details.max_sendable
);
}
InputType::LnurlWithdraw(details) => {
println!(
"Input is LNURL-Withdraw for min/max {}/{} msats",
details.min_withdrawable, details.max_withdrawable
);
}
// Other input types are available
_ => {}
}
let input = "an input to be parsed..."
do {
let inputType = try await sdk.parse(input: input)
switch inputType {
case .bitcoinAddress(v1: let details):
print("Input is Bitcoin address \(details.address)")
case .bolt11Invoice(v1: let details):
let amount = details.amountMsat.map { String($0) } ?? "unknown"
print("Input is BOLT11 invoice for \(amount) msats")
case .lnurlPay(v1: let details):
print(
"Input is LNURL-Pay/Lightning address accepting min/max \(details.minSendable)/\(details.maxSendable) msats)"
)
case .lnurlWithdraw(v1: let details):
print(
"Input is LNURL-Withdraw for min/max \(details.minWithdrawable)/\(details.maxWithdrawable) msats"
)
default:
break // Other input types are available
}
} catch {
print("Failed to parse input: \(error)")
}
val input = "an input to be parsed..."
try {
val inputType = sdk.parse(input)
when (inputType) {
is InputType.BitcoinAddress -> {
println("Input is Bitcoin address ${inputType.v1.address}")
}
is InputType.Bolt11Invoice -> {
val amountStr = inputType.v1.amountMsat?.toString() ?: "unknown"
println("Input is BOLT11 invoice for $amountStr msats")
}
is InputType.LnurlPay -> {
println("Input is LNURL-Pay/Lightning address accepting min/max " +
"${inputType.v1.minSendable}/${inputType.v1.maxSendable} msats}")
}
is InputType.LnurlWithdraw -> {
println("Input is LNURL-Withdraw for min/max " +
"${inputType.v1.minWithdrawable}/${inputType.v1.maxWithdrawable} msats")
}
else -> {
// Handle other input types
}
}
} catch (e: Exception) {
// handle error
}
const input = 'an input to be parsed...'
const parsed = await sdk.parse(input)
switch (parsed.type) {
case 'bitcoinAddress':
console.log(`Input is Bitcoin address ${parsed.address}`)
break
case 'bolt11Invoice':
console.log(
`Input is BOLT11 invoice for ${
parsed.amountMsat != null ? parsed.amountMsat.toString() : 'unknown'
} msats`
)
break
case 'lnurlPay':
console.log(
`Input is LNURL-Pay/Lightning address accepting min/max ${parsed.minSendable}/${parsed.maxSendable} msats`
)
break
case 'lnurlWithdraw':
console.log(
`Input is LNURL-Withdraw for min/max ${parsed.minWithdrawable}/${parsed.maxWithdrawable} msats`
)
break
default:
// Other input types are available
break
}
const inputStr = 'an input to be parsed...'
const input = await sdk.parse(inputStr)
if (input.tag === InputType_Tags.BitcoinAddress) {
console.log(`Input is Bitcoin address ${input.inner[0].address}`)
} else if (input.tag === InputType_Tags.Bolt11Invoice) {
console.log(
`Input is BOLT11 invoice for ${
input.inner[0].amountMsat != null ? input.inner[0].amountMsat.toString() : 'unknown'
} msats`
)
} else if (input.tag === InputType_Tags.LnurlPay) {
console.log(
'Input is LNURL-Pay/Lightning address accepting min/max ' +
`${input.inner[0].minSendable}/${input.inner[0].maxSendable} msats`
)
} else if (input.tag === InputType_Tags.LnurlWithdraw) {
console.log(
'Input is LNURL-Withdraw for min/max ' +
`${input.inner[0].minWithdrawable}/${input.inner[0].maxWithdrawable} msats`
)
} else {
// Other input types are available
}
String input = "an input to be parsed...";
InputType inputType = await sdk.parse(input: input);
if (inputType is InputType_BitcoinAddress) {
print("Input is Bitcoin address ${inputType.field0.address}");
} else if (inputType is InputType_Bolt11Invoice) {
String amountStr = inputType.field0.amountMsat != null
? inputType.field0.amountMsat.toString()
: "unknown";
print("Input is BOLT11 invoice for $amountStr msats");
} else if (inputType is InputType_LnurlPay) {
print(
"Input is LNURL-Pay/Lightning address accepting min/max ${inputType.field0.minSendable}/${inputType.field0.maxSendable} msats");
} else if (inputType is InputType_LnurlWithdraw) {
print(
"Input is LNURL-Withdraw for min/max ${inputType.field0.minWithdrawable}/${inputType.field0.maxWithdrawable} msats");
} else {
// Other input types are available
}
input_str = "an input to be parsed..."
try:
parsed_input = await sdk.parse(input=input_str)
if isinstance(parsed_input, InputType.BITCOIN_ADDRESS):
details = parsed_input[0]
logging.debug(f"Input is Bitcoin address {details.address}")
elif isinstance(parsed_input, InputType.BOLT11_INVOICE):
details = parsed_input[0]
amount = "unknown"
if details.amount_msat:
amount = str(details.amount_msat)
logging.debug(f"Input is BOLT11 invoice for {amount} msats")
elif isinstance(parsed_input, InputType.LNURL_PAY):
details = parsed_input[0]
logging.debug(
f"Input is LNURL-Pay/Lightning address accepting "
f"min/max {details.min_sendable}/{details.max_sendable} msats"
)
elif isinstance(parsed_input, InputType.LNURL_WITHDRAW):
details = parsed_input[0]
logging.debug(
f"Input is LNURL-Withdraw for min/max "
f"{details.min_withdrawable}/{details.max_withdrawable} msats"
)
# Other input types are available
except Exception as error:
logging.error(error)
raise
inputStr := "an input to be parsed..."
input, err := sdk.Parse(inputStr)
if sdkErr := err.(*breez_sdk_spark.SdkError); sdkErr != nil {
return nil, err
}
switch inputType := input.(type) {
case breez_sdk_common.InputTypeBitcoinAddress:
log.Printf("Input is Bitcoin address %s", inputType.Field0.Address)
case breez_sdk_common.InputTypeBolt11Invoice:
amount := "unknown"
if inputType.Field0.AmountMsat != nil {
amount = strconv.FormatUint(*inputType.Field0.AmountMsat, 10)
}
log.Printf("Input is BOLT11 invoice for %s msats", amount)
case breez_sdk_common.InputTypeLnurlPay:
log.Printf("Input is LNURL-Pay/Lightning address accepting min/max %d/%d msats",
inputType.Field0.MinSendable, inputType.Field0.MaxSendable)
case breez_sdk_common.InputTypeLnurlWithdraw:
log.Printf("Input is LNURL-Withdraw for min/max %d/%d msats",
inputType.Field0.MinWithdrawable, inputType.Field0.MaxWithdrawable)
default:
// Other input types are available
}
Supporting other input formats
The parsing module can be extended using external input parsers provided in the SDK configuration. These will be used when the input is not recognized.
You can implement and provide your own parsers, or use existing public ones.
Configuring external parsers
Configuring external parsers can only be done before initializing and the config cannot be changed through the lifetime of the connection.
Multiple parsers can be configured, and each one is defined by:
- Provider ID: an arbitrary id to identify the provider input type
- Input regex: a regex pattern that should reliably match all inputs that this parser can process, even if it may also match some invalid inputs
- Parser URL: an URL containing the placeholder
<input>
When parsing an input that isn't recognized as one of the native input types, the SDK will check if the input conforms to any of the external parsers regex expressions. If so, it will make an HTTP GET request to the provided URL, replacing the placeholder with the input. If the input is recognized, the response should include in its body a string that can be parsed into one of the natively supported types.
// Create the default config
let mut config = default_config(Network::Mainnet);
config.api_key = Some("<breez api key>".to_string());
// Configure external parsers
config.external_input_parsers = Some(vec![
ExternalInputParser {
provider_id: "provider_a".to_string(),
input_regex: "^provider_a".to_string(),
parser_url: "https://parser-domain.com/parser?input=<input>".to_string(),
},
ExternalInputParser {
provider_id: "provider_b".to_string(),
input_regex: "^provider_b".to_string(),
parser_url: "https://parser-domain.com/parser?input=<input>".to_string(),
},
]);
// Create the default config
var config = defaultConfig(network: Network.mainnet)
config.apiKey = "<breez api key>"
// Configure external parsers
config.externalInputParsers = [
ExternalInputParser(
providerId: "provider_a",
inputRegex: "^provider_a",
parserUrl: "https://parser-domain.com/parser?input=<input>"
),
ExternalInputParser(
providerId: "provider_b",
inputRegex: "^provider_b",
parserUrl: "https://parser-domain.com/parser?input=<input>"
),
]
// Create the default config
val config = defaultConfig(Network.MAINNET)
config.apiKey = "<breez api key>"
// Configure external parsers
config.externalInputParsers = listOf(
ExternalInputParser(
providerId = "provider_a",
inputRegex = "^provider_a",
parserUrl = "https://parser-domain.com/parser?input=<input>"
),
ExternalInputParser(
providerId = "provider_b",
inputRegex = "^provider_b",
parserUrl = "https://parser-domain.com/parser?input=<input>"
)
)
// Create the default config
const config = defaultConfig('mainnet')
config.apiKey = '<breez api key>'
// Configure external parsers
config.externalInputParsers = [
{
providerId: 'provider_a',
inputRegex: '^provider_a',
parserUrl: 'https://parser-domain.com/parser?input=<input>'
},
{
providerId: 'provider_b',
inputRegex: '^provider_b',
parserUrl: 'https://parser-domain.com/parser?input=<input>'
}
]
// Create the default config
const config = defaultConfig(Network.Mainnet)
config.apiKey = '<breez api key>'
// Configure external parsers
config.externalInputParsers = [
{
providerId: 'provider_a',
inputRegex: '^provider_a',
parserUrl: 'https://parser-domain.com/parser?input=<input>'
},
{
providerId: 'provider_b',
inputRegex: '^provider_b',
parserUrl: 'https://parser-domain.com/parser?input=<input>'
}
]
// Create the default config
Config config = defaultConfig(network: Network.mainnet)
.copyWith(apiKey: "<breez api key>");
config = config.copyWith(
externalInputParsers: [
ExternalInputParser(
providerId: "provider_a",
inputRegex: "^provider_a",
parserUrl: "https://parser-domain.com/parser?input=<input>",
),
ExternalInputParser(
providerId: "provider_b",
inputRegex: "^provider_b",
parserUrl: "https://parser-domain.com/parser?input=<input>",
),
],
);
# Create the default config
config = default_config(network=Network.MAINNET)
config.api_key = "<breez api key>"
# Configure external parsers
config.external_input_parsers = [
ExternalInputParser(
provider_id="provider_a",
input_regex="^provider_a",
parser_url="https://parser-domain.com/parser?input=<input>"
),
ExternalInputParser(
provider_id="provider_b",
input_regex="^provider_b",
parser_url="https://parser-domain.com/parser?input=<input>"
)
]
// Create the default config
apiKey := "<breez api key>"
config := breez_sdk_spark.DefaultConfig(breez_sdk_spark.NetworkMainnet)
config.ApiKey = &apiKey
// Configure external parsers
parsers := []breez_sdk_common.ExternalInputParser{
{
ProviderId: "provider_a",
InputRegex: "^provider_a",
ParserUrl: "https://parser-domain.com/parser?input=<input>",
},
{
ProviderId: "provider_b",
InputRegex: "^provider_b",
ParserUrl: "https://parser-domain.com/parser?input=<input>",
},
}
config.ExternalInputParsers = &parsers
Public external parsers
- PicknPay QRs
- Maintainer: MoneyBadger
- Regex:
(.*)(za.co.electrum.picknpay)(.*) - URL:
https://cryptoqr.net/.well-known/lnurlp/<input> - More info: support+breezsdk@moneybadger.co.za
- Bootlegger QRs
- Maintainer: MoneyBadger
- Regex:
(.*)(wigroup\.co|yoyogroup\.co)(.*) - URL:
https://cryptoqr.net/.well-known/lnurlw/<input> - More info: support+breezsdk@moneybadger.co.za
Default external parsers
The SDK ships with some embedded default external parsers. If you prefer not to use them, you can disable them in the SDK's configuration. See the available default parsers in the API Documentation by checking the source of the constant.