nova-carnivore/bolt12-php
Composer 安装命令:
composer require nova-carnivore/bolt12-php
包简介
Modern PHP 8.3+ BOLT 12 Lightning Network offer/invoice encoder/decoder with BIP-340 Schnorr signatures
README 文档
README
Modern PHP 8.3+ BOLT 12 Lightning Network offer/invoice encoder/decoder with BIP-340 Schnorr signatures. Full spec compliance, production-ready.
Features
- ⚡ Full BOLT 12 spec compliance — Offers, Invoice Requests, Invoices, Invoice Errors
- 🔐 BIP-340 Schnorr signatures — Pure PHP implementation with Merkle tree construction
- 🏗️ Modern PHP 8.3+ — Enums, readonly classes, named arguments, match expressions
- 🔍 PHPStan level 9 — Maximum static analysis strictness
- 🌐 All message types — Offer (lno), Invoice Request (lnr), Invoice (lni), Invoice Error
- 🏷️ All TLV fields — chains, metadata, currency, amounts, paths, features, BIP-353, and more
- 🔄 Round-trip safe — Encode → decode preserves all data
- 📏 PSR-12 code style — Enforced with PHP-CS-Fixer
- 🔒 Constant-time crypto — Uses GMP for big integer operations
- 🔗 Blinded paths — Full support for multi-hop blinded payment paths
Installation
composer require nova-carnivore/bolt12-php
Requirements
- PHP 8.3 or higher
- ext-gmp (required for big integers and BIP-340 crypto)
Quick Start
Decode (Auto-detect Type)
use Nova\Bitcoin\Bolt12\Decoder; $offer = Decoder::decode('lno1...'); // Returns Offer object $invReq = Decoder::decode('lnr1...'); // Returns InvoiceRequest object $invoice = Decoder::decode('lni1...'); // Returns Invoice object
Encode an Offer
use Nova\Bitcoin\Bolt12\Encoder; use Nova\Bitcoin\Bolt12\Signer; $encoded = Encoder::encodeOffer( issuerId: $myPublicKeyHex, description: 'Buy a coffee', amountMsat: gmp_init(100000), issuer: 'CoffeeShop', ); // Returns: 'lno1...'
Encode an Invoice Request (auto-signed)
$invReq = Encoder::encodeInvoiceRequest( invreqMetadata: bin2hex(random_bytes(32)), payerId: $myPublicKeyHex, payerPrivateKey: $myPrivateKeyHex, offerDescription: 'Buy a coffee', offerIssuerId: $merchantPubkey, offerAmountMsat: gmp_init(100000), ); // Returns: 'lnr1...' (signed with BIP-340 Schnorr)
Encode an Invoice (auto-signed)
use Nova\Bitcoin\Bolt12\BlindedPath; use Nova\Bitcoin\Bolt12\BlindedPayInfo; use Nova\Bitcoin\Bolt12\OnionMessageHop; $invoice = Encoder::encodeInvoice( nodeId: $myNodePubkey, nodePrivateKey: $myNodePrivkey, createdAt: gmp_init(time()), paymentHash: hash('sha256', $preimage), amountMsat: gmp_init(100000), invoicePaths: [ new BlindedPath($blindingKey, [ new OnionMessageHop($nodeId, $encryptedData), ]), ], blindedPayInfo: [ new BlindedPayInfo( feeBaseMsat: 1000, feeProportionalMillionths: 100, cltvExpiryDelta: 144, htlcMinimumMsat: gmp_init(1000), htlcMaximumMsat: gmp_init(1000000000), ), ], ); // Returns: 'lni1...' (signed with BIP-340 Schnorr)
Verify Signatures
use Nova\Bitcoin\Bolt12\Signer; $invReq = Decoder::decode('lnr1...'); $valid = Signer::verifyInvoiceRequest($invReq); // Returns bool $invoice = Decoder::decode('lni1...'); $valid = Signer::verifyInvoice($invoice); // Returns bool
Invoice Errors
// Encode $errorBytes = Encoder::encodeInvoiceError( error: 'Amount too low', erroneousField: gmp_init(82), // invreq_amount suggestedValue: '0186a0', // 100000 as tu64 hex ); // Decode $invoiceError = Decoder::decodeInvoiceError($errorBytes); echo $invoiceError->error; // 'Amount too low'
Full Payment Flow Example
use Nova\Bitcoin\Bolt12\{Decoder, Encoder, Signer, BlindedPath, BlindedPayInfo, OnionMessageHop}; // 1. Merchant creates an offer $offer = Encoder::encodeOffer( issuerId: $merchantPubkey, description: 'Buy a coffee', amountMsat: gmp_init(100000), ); $decoded = Decoder::decode($offer); // 2. Payer creates invoice request (mirrors offer fields) $invReq = Encoder::encodeInvoiceRequest( invreqMetadata: bin2hex(random_bytes(32)), payerId: $payerPubkey, payerPrivateKey: $payerPrivkey, offerDescription: $decoded->description, offerIssuerId: $decoded->issuerId, offerAmountMsat: $decoded->amountMsat, ); // 3. Merchant creates and signs invoice $invoice = Encoder::encodeInvoice( nodeId: $merchantPubkey, nodePrivateKey: $merchantPrivkey, createdAt: gmp_init(time()), paymentHash: hash('sha256', $preimage), amountMsat: gmp_init(100000), invoicePaths: [$blindedPath], blindedPayInfo: [$payInfo], ); // 4. Payer verifies invoice signature $inv = Decoder::decode($invoice); assert(Signer::verifyInvoice($inv));
API Reference
Decoder::decode(string $bolt12String): Offer|InvoiceRequest|Invoice
Decodes any BOLT 12 bech32 string (auto-detects type by prefix).
Decoder::decodeInvoiceError(array $bytes): InvoiceError
Decodes a BOLT 12 Invoice Error from raw TLV bytes.
Encoder::encodeOffer(...): string
Encodes a BOLT 12 Offer (lno1...). Not signed per spec.
Encoder::encodeInvoiceRequest(...): string
Encodes and signs a BOLT 12 Invoice Request (lnr1...).
Encoder::encodeInvoice(...): string
Encodes and signs a BOLT 12 Invoice (lni1...).
Encoder::encodeInvoiceError(...): array
Encodes a BOLT 12 Invoice Error as raw TLV bytes.
Signer::verifyInvoiceRequest(InvoiceRequest $invReq): bool
Verifies a BIP-340 Schnorr signature on an invoice request.
Signer::verifyInvoice(Invoice $invoice): bool
Verifies a BIP-340 Schnorr signature on an invoice.
Signer::getPublicKey(string $privateKeyHex): string
Derives a compressed public key from a private key.
Data Classes
| Class | Description |
|---|---|
Offer |
Decoded offer with all fields as readonly properties |
InvoiceRequest |
Decoded invoice request with signature |
Invoice |
Decoded invoice with paths, payinfo, and signature |
InvoiceError |
Decoded invoice error |
BlindedPath |
Blinded path with hops |
BlindedPayInfo |
Payment info for blinded paths |
FallbackAddress |
On-chain fallback address |
OnionMessageHop |
Single hop in a blinded path |
Bip353Name |
BIP-353 name (user@domain) |
TlvEntry |
Raw TLV type-value pair |
BOLT 12 Spec Coverage
Message Types
| Type | Prefix | Encode | Decode | Sign | Verify |
|---|---|---|---|---|---|
| Offer | lno |
✅ | ✅ | ✅* | ✅* |
| Invoice Request | lnr |
✅ | ✅ | ✅ | ✅ |
| Invoice | lni |
✅ | ✅ | ✅ | ✅ |
| Invoice Error | — | ✅ | ✅ | N/A | N/A |
* Offers are optionally signed per spec
TLV Fields
| Type | Field | Offer | InvReq | Invoice |
|---|---|---|---|---|
| 0 | invreq_metadata |
— | ✅ | ✅ |
| 2 | offer_chains |
✅ | ✅ | ✅ |
| 4 | offer_metadata |
✅ | ✅ | ✅ |
| 6 | offer_currency |
✅ | ✅ | ✅ |
| 8 | offer_amount |
✅ | ✅ | ✅ |
| 10 | offer_description |
✅ | ✅ | ✅ |
| 12 | offer_features |
✅ | ✅ | ✅ |
| 14 | offer_absolute_expiry |
✅ | ✅ | ✅ |
| 16 | offer_paths |
✅ | ✅ | ✅ |
| 18 | offer_issuer |
✅ | ✅ | ✅ |
| 20 | offer_quantity_max |
✅ | ✅ | ✅ |
| 22 | offer_issuer_id |
✅ | ✅ | ✅ |
| 80 | invreq_chain |
— | ✅ | ✅ |
| 82 | invreq_amount |
— | ✅ | ✅ |
| 84 | invreq_features |
— | ✅ | ✅ |
| 86 | invreq_quantity |
— | ✅ | ✅ |
| 88 | invreq_payer_id |
— | ✅ | ✅ |
| 89 | invreq_payer_note |
— | ✅ | ✅ |
| 90 | invreq_paths |
— | ✅ | ✅ |
| 91 | invreq_bip_353_name |
— | ✅ | ✅ |
| 160 | invoice_paths |
— | — | ✅ |
| 162 | invoice_blindedpay |
— | — | ✅ |
| 164 | invoice_created_at |
— | — | ✅ |
| 166 | invoice_relative_expiry |
— | — | ✅ |
| 168 | invoice_payment_hash |
— | — | ✅ |
| 170 | invoice_amount |
— | — | ✅ |
| 172 | invoice_fallbacks |
— | — | ✅ |
| 174 | invoice_features |
— | — | ✅ |
| 176 | invoice_node_id |
— | — | ✅ |
| 240 | signature |
✅* | ✅ | ✅ |
Encoding
- Bech32 without checksum — per BOLT 12 spec
- BigSize TLV type/length encoding
- tu64 truncated unsigned 64-bit values
+concatenation support for long strings
Cryptography
- BIP-340 Schnorr signatures (64-byte raw, not DER)
- Tagged hashes —
SHA256(SHA256(tag) || SHA256(tag) || msg) - Merkle tree for multi-TLV signatures with nonce leaves
- secp256k1 elliptic curve arithmetic
Security
- BIP-340 Schnorr signatures use pure PHP with GMP for big integer arithmetic
- All elliptic curve operations use constant-time modular arithmetic via GMP
- The Merkle tree construction includes nonce leaves to prevent revealing adjacent fields
- Private keys are only used in signing operations and never stored
Exception Handling
use Nova\Bitcoin\Bolt12\Exception\{ Bolt12Exception, // Base exception DecodeException, // Malformed input EncodeException, // Missing/invalid fields SignatureException, // Signature issues }; try { $offer = Decoder::decode($bolt12String); } catch (DecodeException $e) { // Malformed string or TLV data } catch (Bolt12Exception $e) { // Any BOLT 12 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 # Run all CI checks composer ci
Architecture
src/
├── Decoder.php # Main decoder (auto-detect type)
├── Encoder.php # Main encoder (all message types)
├── Signer.php # BIP-340 Schnorr + Merkle tree
├── Bech32.php # Bech32 WITHOUT checksum (per BOLT 12)
├── TlvStream.php # BigSize TLV encoding/decoding
├── TlvEntry.php # TLV entry value object
├── Offer.php # Offer value object (readonly)
├── InvoiceRequest.php # Invoice request value object (readonly)
├── Invoice.php # Invoice value object (readonly)
├── InvoiceError.php # Invoice error value object (readonly)
├── BlindedPath.php # Blinded path with encoding/decoding
├── BlindedPayInfo.php # Blinded payment info
├── FallbackAddress.php # On-chain fallback address
├── OnionMessageHop.php # Blinded path hop
├── Bip353Name.php # BIP-353 name resolution
├── Helpers.php # Byte/hex/string utilities
└── Exception/
├── Bolt12Exception.php
├── DecodeException.php
├── EncodeException.php
└── SignatureException.php
Key Differences from BOLT 11
| Feature | BOLT 11 | BOLT 12 |
|---|---|---|
| Encoding | Bech32 with checksum | Bech32 without checksum |
| Crypto | ECDSA recovery | BIP-340 Schnorr |
| Signatures | Simple message hash | Merkle tree construction |
| TLV encoding | 5-bit continuation | BigSize encoding |
| Message types | 1 (invoice only) | 4 (offer, invreq, invoice, error) |
| Privacy | Direct node ID | Blinded paths |
License
MIT — see LICENSE.
nova-carnivore/bolt12-php 适用场景与选型建议
nova-carnivore/bolt12-php 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 0 次下载、GitHub Stars 达 0, 最近一次更新时间为 2026 年 02 月 15 日, 在 PHP 生态内属于活跃度较高的组件。
它主要适用于以下技术方向: 「invoice」 「bitcoin」 「offer」 「Lightning」 「lightning-network」 「schnorr」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。
我们在过去多个企业项目中使用过 nova-carnivore/bolt12-php 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 nova-carnivore/bolt12-php 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
与 nova-carnivore/bolt12-php 相关的其它包
同方向 / 同关键字的高下载量 PHP Composer 包推荐,方便对比选型:
The BlockTrail PHP SDK, for integration of Bitcoin functionality through the BlockTrail API
Easy invoice generation using Laravel Eloquent
Laravel MPdf : Easily generate PDF files with arabic support
Szamlazz.hu integration for Laravel
This extension allows you to create and manage job listings, featuring filtering, watchlist, detail view with optional application form or link to external one, and compatibility with Google for Jobs structured data.
Modern PHP 8.3+ BOLT 11 Lightning Network invoice encoder/decoder
统计信息
- 总下载量: 0
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 36
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-02-15