nova-carnivore/bolt11-php

Modern PHP 8.3+ BOLT 11 Lightning Network invoice encoder/decoder

Installs: 0

Dependents: 0

Suggesters: 0

Security: 0

Stars: 0

Watchers: 0

Forks: 0

Open Issues: 0

pkg:composer/nova-carnivore/bolt11-php

v0.1.0 2026-02-14 22:13 UTC

This package is auto-updated.

Last update: 2026-02-14 22:23:32 UTC


README

PHP Version License CI PHPStan Level Latest Version

Modern PHP 8.3+ BOLT 11 Lightning Network invoice encoder/decoder. Full spec compliance, production-ready.

Features

  • โšก Full BOLT 11 spec compliance โ€” All 12 spec test vectors pass
  • ๐Ÿ” Encode, sign & decode โ€” Complete lifecycle support
  • ๐Ÿ—๏ธ Modern PHP 8.3+ โ€” Enums, readonly classes, named arguments, match expressions
  • ๐Ÿ” PHPStan level 9 โ€” Maximum static analysis strictness
  • ๐ŸŒ All networks โ€” Bitcoin, Testnet, Signet, Regtest
  • ๐Ÿท๏ธ All tag types โ€” payment_hash, description, route hints, feature bits, metadata, and more
  • ๐Ÿ”„ Round-trip safe โ€” Encode โ†’ sign โ†’ decode preserves all data
  • ๐Ÿ“ PSR-12 code style โ€” Enforced with PHP-CS-Fixer
  • ๐Ÿ”’ Security-hardened crypto โ€” Uses paragonie/ecc with constant-time operations

Installation

composer require nova-carnivore/bolt11-php

Requirements

  • PHP 8.3 or higher
  • ext-gmp (required by paragonie/ecc)

Quick Start

Decode an Invoice

use Nova\Bitcoin\Bolt11\Decoder;

$invoice = Decoder::decode('lnbc2500u1pvjluez...');

$invoice->satoshis;          // 250000
$invoice->millisatoshis;     // '250000000'
$invoice->network;           // Network::Bitcoin
$invoice->timestamp;         // 1496314658
$invoice->payeeNodeKey;      // '03e7156ae33b...'

// Access tags
$invoice->getPaymentHash();   // hex string
$invoice->getDescription();   // 'string'
$invoice->getPaymentSecret(); // hex string
$invoice->isExpired();        // bool
$invoice->getTag('payment_hash'); // Tag object

Encode an Invoice

use Nova\Bitcoin\Bolt11\Encoder;
use Nova\Bitcoin\Bolt11\Network;
use Nova\Bitcoin\Bolt11\Tag;

$unsigned = Encoder::encode(
    network: Network::Bitcoin,
    satoshis: 1000,
    tags: [
        Tag::paymentHash('0001020304050607...'),
        Tag::paymentSecret('1111111111111111...'),
        Tag::description('test payment'),
        Tag::expiry(3600),
    ],
);

Sign an Invoice

use Nova\Bitcoin\Bolt11\Signer;

$signed = Signer::sign($unsigned, $privateKeyHex);

$signed->paymentRequest; // 'lnbc10u1...'
$signed->complete;       // true
$signed->payeeNodeKey;   // compressed public key

Amount Helpers

use Nova\Bitcoin\Bolt11\Helpers;

Helpers::satToHrp(250000);       // '2500u'
Helpers::hrpToSat('2500u');      // '250000'
Helpers::millisatToHrp('1000');  // '10n'
Helpers::hrpToMillisat('10n');   // '1000'

API Reference

Decoder::decode(string $paymentRequest): Invoice

Decodes a BOLT 11 payment request string into an Invoice object. Handles both lowercase and UPPERCASE invoices.

Encoder::encode(...): Invoice

Creates an unsigned invoice. Parameters:

Parameter Type Default Description
network Network Network::Bitcoin Target network
satoshis ?int null Amount in satoshis
millisatoshis ?string null Amount in millisatoshis
tags array<Tag> [] Tagged fields
timestamp ?int null Unix timestamp (defaults to now)

Signer::sign(Invoice $invoice, string $privateKeyHex): Invoice

Signs an unsigned invoice with a secp256k1 private key.

Invoice (Value Object)

Property Type Description
complete bool Whether the invoice is fully signed
prefix string Full HRP (e.g. lnbc2500u)
network ?Network Bitcoin network enum
satoshis ?int Amount in satoshis
millisatoshis ?string Amount in millisatoshis
timestamp int Unix timestamp
payeeNodeKey ?string Compressed public key (hex)
signature string 64-byte compact signature (hex)
recoveryFlag int Signature recovery flag (0-3)
tags array<Tag> All tagged fields
paymentRequest ?string Full bech32-encoded string

Tag Factory Methods

Tag::paymentHash(string $hex): Tag
Tag::paymentSecret(string $hex): Tag
Tag::description(string $text): Tag
Tag::descriptionHash(string $hex): Tag
Tag::payeeNodeKey(string $hex): Tag
Tag::expiry(int $seconds): Tag
Tag::minFinalCltvExpiry(int $blocks): Tag
Tag::fallbackAddress(int $code, string $addressHash): Tag
Tag::routeHint(array $hops): Tag
Tag::featureBits(FeatureBits $bits): Tag
Tag::metadata(string $hex): Tag

Enums

enum Network: string {
    case Bitcoin = 'bc';
    case Testnet = 'tb';
    case Signet  = 'tbs';
    case Regtest = 'bcrt';
}

enum TagType: int {
    case PaymentHash = 1;
    case PaymentSecret = 16;
    case Description = 13;
    // ... and more
}

enum Multiplier: string {
    case Milli = 'm';
    case Micro = 'u';
    case Nano  = 'n';
    case Pico  = 'p';
}

BOLT 11 Spec Compliance

All 12 official test vectors pass:

# Test Status
1 Donation (any amount) โœ…
2 $3 coffee (2500ยต, 60s expiry) โœ…
3 UTF-8 description (ใƒŠใƒณใ‚ปใƒณใ‚น 1ๆฏ) โœ…
4 Hashed description (20m) โœ…
5 Testnet with P2PKH fallback โœ…
6 Mainnet with P2PKH + route hints โœ…
7 Feature bits (8, 14, 99) โœ…
8 Uppercase invoice โœ…
9 Metadata (0x01fafaf0) โœ…
10 Pico-BTC amount (9678785340p) โœ…
11 High-S signature recovery โœ…
12 Unknown tags (silently ignored) โœ…

Supported Tagged Fields

Code Letter Field Supported
1 p payment_hash โœ…
16 s payment_secret โœ…
13 d description โœ…
27 m metadata โœ…
19 n payee node key โœ…
23 h description_hash โœ…
6 x expiry โœ…
24 c min_final_cltv_expiry โœ…
9 f fallback address โœ…
3 r route hints โœ…
5 9 feature bits โœ…

Exception Handling

use Nova\Bitcoin\Bolt11\Exception\{
    Bolt11Exception,           // Base exception
    InvalidInvoiceException,   // Malformed invoice
    InvalidChecksumException,  // Bad bech32 checksum
    InvalidSignatureException, // Signature issues
    InvalidAmountException,    // Bad amount format
    UnsupportedNetworkException, // Unknown network
};

try {
    $invoice = Decoder::decode($paymentRequest);
} catch (InvalidChecksumException $e) {
    // Bad checksum
} catch (InvalidInvoiceException $e) {
    // Malformed invoice
} catch (Bolt11Exception $e) {
    // Any BOLT 11 error
}

Development

# Install dependencies
composer install

# Run tests
vendor/bin/phpunit

# Static analysis
vendor/bin/phpstan analyse

# Code style check
vendor/bin/php-cs-fixer fix --dry-run --diff

# Fix code style
vendor/bin/php-cs-fixer fix

Architecture

src/
โ”œโ”€โ”€ Decoder.php            # Main decoder
โ”œโ”€โ”€ Encoder.php            # Main encoder
โ”œโ”€โ”€ Signer.php             # Invoice signing (secp256k1)
โ”œโ”€โ”€ Secp256k1Recovery.php  # ECDSA public key recovery
โ”œโ”€โ”€ Invoice.php            # Immutable value object
โ”œโ”€โ”€ Tag.php                # Tag value object with factory methods
โ”œโ”€โ”€ TagType.php            # Tag type enum
โ”œโ”€โ”€ Network.php            # Network enum (Bitcoin, Testnet, Signet, Regtest)
โ”œโ”€โ”€ Multiplier.php         # Amount multiplier enum (m, u, n, p)
โ”œโ”€โ”€ Amount.php             # Amount value object
โ”œโ”€โ”€ RouteHint.php          # Route hint value object
โ”œโ”€โ”€ FallbackAddress.php    # Fallback address value object
โ”œโ”€โ”€ FeatureBits.php        # Feature bits handler
โ”œโ”€โ”€ Bech32.php             # Bech32 encoder/decoder
โ”œโ”€โ”€ Helpers.php            # Static helper methods
โ””โ”€โ”€ Exception/
    โ”œโ”€โ”€ Bolt11Exception.php
    โ”œโ”€โ”€ InvalidInvoiceException.php
    โ”œโ”€โ”€ InvalidChecksumException.php
    โ”œโ”€โ”€ InvalidSignatureException.php
    โ”œโ”€โ”€ InvalidAmountException.php
    โ””โ”€โ”€ UnsupportedNetworkException.php

License

MIT โ€” see LICENSE.