# unified_uri
[](https://crates.io/crates/unified_uri)
[](https://docs.rs/unified_uri)
A Rust library for parsing unified Bitcoin URIs that support both Lightning Network invoices and Payjoin parameters, based on the BIP21 URI specification.
## Overview
This crate extends the BIP21 URI standard to support unified QR codes that contain both on-chain Bitcoin addresses and Lightning Network payment information, as well as Payjoin parameters. This enables a single QR code to work with both on-chain and Lightning wallets, eliminating the need for separate payment interfaces.
## Features
- **Lightning Network Support**: Parse BOLT11 invoices from BIP21 URIs using the `lightning` parameter
- **Payjoin Integration**: Support for payjoin endpoints (`pj`) and output substitution control (`pjos`)
- **Backwards Compatible**: Works with standard BIP21 URIs (on-chain only)
- **Security**: Validates payjoin endpoints to ensure they use secure protocols (HTTPS or .onion domains)
## Usage
Add this to your `Cargo.toml`:
```toml
[dependencies]
unified_uri = "0.1"
```
### Basic Example
```rust
use unified_uri::UnifiedUri;
use std::str::FromStr;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Parse a unified URI with Lightning and Payjoin support
let uri_str = "bitcoin:BC1QYLH3U67J673H6Y6ALV70M0PL2YZ53TZHVXGG7U?pj=https://payjoin.example.com/payjoin&pjos=1";
let uri = UnifiedUri::from_str(uri_str)?;
// Access the Bitcoin address
println!("Address: {}", uri.address);
// Access Lightning invoice if present
if let Some(invoice) = &uri.extras.lightning {
println!("Lightning Invoice: {:?}", invoice);
}
// Access Payjoin parameters
if let Some(pj_url) = &uri.extras.pj {
println!("Payjoin endpoint: {}", pj_url);
}
// Check if output substitution is disabled
if uri.extras.disable_output_substitution() {
println!("Payjoin output substitution is disabled");
}
Ok(())
}
```
### Building URIs
```rust
use unified_uri::UnifiedUriBuilder;
use bitcoin::{Address, Amount};
use std::str::FromStr;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create a valid Bitcoin address
let address: Address = "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlhfe2".parse()?;
// Build a basic on-chain URI
let basic_uri = UnifiedUriBuilder::new(address.clone()).build();
println!("{}", basic_uri); // "bitcoin:bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlhfe2"
// Build a full unified URI with all parameters
let unified_uri = UnifiedUriBuilder::new(address)
.amount(Amount::from_sat(100_000)?) // 0.001 BTC in satoshis
.label("Payment for services")
.message("Thank you for your business")
.lightning("lnbc10u1p3pj257pp5yz...") // or use lightning_invoice(parsed_invoice)
.payjoin_url("https://payjoin.example.com/payjoin")
.disable_output_substitution(true)
.build();
println!("{}", unified_uri);
// "bitcoin:bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlhfe2?amount=0.001&label=Payment%20for%20services&message=Thank%20you%20for%20your%20business&lightning=lnbc10u1p3pj257pp5yz...&pj=https://payjoin.example.com/payjoin&pjos=1"
Ok(())
}
```
### Parsing Different URI Types
```rust
use unified_uri::UnifiedUri;
// On-chain only (standard BIP21)
let onchain_uri = "bitcoin:1andreas3batLhQa2FawWjeyjCqyBzypd";
// With Lightning invoice
let lightning_uri = "bitcoin:BC1QYLH3U67J673H6Y6ALV70M0PL2YZ53TZHVXGG7U?lightning=LNBC10U1P3PJ257PP5YZTKWJCZ5FTL5LAXKAV23ZMZEKAW37ZK6KMV80PK4XAEV5QHTZ7QDPDWD3XGER9WD5KWM36YPRX7U3QD36KUCMGYP282ETNV3SHJCQZPGXQYZ5VQSP5USYC4LK9CHSFP53KVCNVQ456GANH60D89REYKDNGSMTJ6YW3NHVQ9QYYSSQJCEWM5CJWZ4A6RFJX77C490YCED6PEMK0UPKXHY89CMM7SCT66K8GNEANWYKZGDRWRFJE69H9U5U0W57RRCSYSAS7GADWMZXC8C6T0SPJAZUP6";
// With Payjoin parameters
let payjoin_uri = "bitcoin:BC1QYLH3U67J673H6Y6ALV70M0PL2YZ53TZHVXGG7U?pj=https://payjoin.example.com/payjoin&pjos=1";
// All three can be parsed with UnifiedUri
let unified_uri = UnifiedUri::from_str(lightning_uri)?;
```
## BIP21 Parameters Supported
| `lightning` | String | BOLT11 Lightning invoice |
| `pj` | String | Payjoin endpoint URL |
| `pjos` | String | Payjoin output substitution (`0` = enabled, `1` = disabled) |
## Unified QR Codes
This crate enables the creation and parsing of unified QR codes as described in the [Unified QR Code specification](https://bitcoinqr.dev/). These QR codes allow a single payment request to work with:
- **On-chain only wallets**: Ignore Lightning and Payjoin parameters
- **Lightning wallets**: Use the Lightning invoice when present
- **Payjoin-compatible wallets**: Use the Payjoin endpoint for enhanced privacy
## API Reference
### `UnifiedUri<'a>`
The main type for parsing unified BIP21 URIs. This is a type alias for `Uri<'a, NetworkUnchecked, UnifiedExtras>`.
### `UnifiedExtras`
Contains the extra parameters parsed from the URI:
- `lightning: Option<Bolt11Invoice>` - Lightning invoice if present
- `pj: Option<Url>` - Payjoin endpoint URL if present
- `pjos: Option<bool>` - Payjoin output substitution setting
#### Methods
- `disable_output_substitution() -> bool` - Returns `true` if payjoin output substitution should be disabled
### `UnifiedUriBuilder`
Builder for creating unified BIP21 URI strings with optional Lightning and Payjoin parameters.
#### Methods
- `new(address: Address) -> Self` - Create a new builder instance with required Bitcoin address
- `amount(self, amount: Amount) -> Self` - Set the payment amount in satoshis
- `label<S: Into<String>>(self, label: S) -> Self` - Set the payment label
- `message<S: Into<String>>(self, message: S) -> Self` - Set the payment message
- `lightning_invoice(self, invoice: Bolt11Invoice) -> Self` - Set the Lightning invoice from Bolt11Invoice struct
- `lightning<S: Into<String>>(self, invoice: S) -> Self` - Set the Lightning invoice from string
- `payjoin_url<S: Into<String>>(self, url: S) -> Self` - Set the Payjoin endpoint URL from string
- `payjoin(self, url: Url) -> Self` - Set the Payjoin endpoint URL from Url struct
- `disable_output_substitution(self, disable: bool) -> Self` - Set whether to disable output substitution
- `build(self) -> String` - Build the final URI string
## Security Considerations
- Payjoin endpoints are validated to ensure they use secure protocols (HTTPS or .onion domains)
- Malformed Lightning invoices will result in parsing errors
- Multiple parameters of the same type are not allowed and will cause errors
## Dependencies
- [`bip21`](https://crates.io/crates/bip21) - BIP21 URI parsing
- [`bitcoin`](https://crates.io/crates/bitcoin) - Bitcoin address handling
- [`lightning-invoice`](https://crates.io/crates/lightning-invoice) - BOLT11 invoice parsing
- [`url`](https://crates.io/crates/url) - URL validation
## References
- [BIP21 - URI Scheme](https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki)
- [BOLT11 - Invoice Protocol](https://github.com/lightning/bolts/blob/master/11-payment-encoding.md)
- [Unified QR Codes for Bitcoin](https://bitcoinqr.dev/)
- [Payjoin Specification](https://github.com/bitcoin/bips/blob/master/bip-0078.mediawiki)
## Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
## License
This project is licensed under the MIT License.