Fiber LogoFiber Docs
Payments

Invoice

How to create, send, parse, and manage invoices in the Fiber network

TL;DR

  • Create an invoice: fnn-cli invoice new_invoice --amount 1000 --currency fibt
  • Pay an invoice: fnn-cli payment send_payment --invoice "fibt1..."
  • Check invoice status: fnn-cli invoice get_invoice --payment-hash 0x...
  • Invoices use bech32m encoding with network-specific prefixes (fibb, fibt, fibd)
  • Invoice states: OpenReceivedPaid, or → Cancelled / Expired

An invoice is a payment request generated by a recipient in the Fiber network. It encodes everything a sender needs to make a payment — the amount, the recipient's identity, cryptographic proof of payment, and optional metadata such as a description or expiry time.

Think of an invoice as a "payment QR code." The recipient generates one, and the sender scans it (or copies the encoded string) to initiate a payment through send_payment.

Anatomy of an Invoice

Every Fiber invoice carries the following core fields:

FieldRequiredDescription
CurrencyYesNetwork identifier: fibb (mainnet), fibt (testnet), or fibd (devnet)
AmountNoPayment amount in shannons (1 CKB = 10⁸ shannons). Omit for open-ended invoices (e.g., donations)
Payment hashYesA 32-byte cryptographic commitment. For regular invoices it's derived from a random preimage; for hold invoices, the recipient provides the preimage and the hash is computed from it
TimestampYesInvoice creation time in milliseconds since the UNIX epoch
SignatureYesA secp256k1 recoverable signature over the invoice data, proving the invoice was created by the holder of the payee's private key

In addition, invoices can carry optional attributes:

AttributeDescription
DescriptionA human-readable note, up to 639 characters (e.g., "Coffee")
Expiry timeHow long the invoice remains valid, in seconds from the timestamp
Payee public keyThe recipient's compressed secp256k1 public key (33 bytes). Enables signature verification without external lookup
Fallback addressA CKB on-chain address to fall back to if the off-chain payment fails
UDT type scriptA CKB type script identifying a User Defined Token, for non-CKB asset payments
Final expiry deltaMinimum TLC (Time-Locked Contract) expiry delta for the final hop, in milliseconds (min ~160 minutes, max 14 days)
Hash algorithmHash function used for the payment hash: ckb_hash (blake2b-256, default) or sha256
Feature flagsCapability flags: multi-part payments (MPP), trampoline routing
Payment secretA 32-byte secret required for multi-part payments; auto-generated when MPP is enabled

Invoice Format

Fiber invoices use bech32m encoding — the same checksum scheme used by modern Bitcoin addresses, but with a different data layout (not compatible with Lightning's BOLT 11). The design draws inspiration from BOLT 11 while adapting to the CKB ecosystem: invoice data is serialized using molecule (the standard serialization format in CKB projects), and cross-chain compatibility is handled through Fiber's built-in Cross-Chain Hub rather than through the invoice format itself.

Human-Readable Part (HRP)

The HRP combines a network prefix with an optional amount:

PrefixNetwork
fibbCKB mainnet ("fiber bytes", since 1 CKB = 1 Byte)
fibtCKB testnet
fibdCKB devnet

The amount is appended directly as a decimal number in shannons. For example, fibb1280 means mainnet, 1280 shannons. If no amount is specified, the HRP is just the prefix (e.g., fibb), indicating an open-ended invoice.

Data Part

The data part is encoded as base32 (u5) values and contains:

  1. A flag byte indicating whether the invoice is signed
  2. The invoice data, serialized and compressed (see below)
  3. The signature (if signed): 104 u5 values encoding a 65-byte secp256k1 recoverable signature

The maximum decompressed invoice data size is 16 KB.

Data Fields

The serialized invoice data contains the following fields:

#FieldRequiredSizeDescription
1timestampYes128 bitsMilliseconds since the UNIX epoch
2payment_hashYes256 bitsUnique identifier of the invoice. Derived from blake2b_256(preimage) for hold invoices, or randomly generated for AMP invoices
3expiryNo64 bitsValidity period in seconds; timestamp + expiry gives the expiration time
4descriptionNoVariableUTF-8 text (e.g., "a cup of coffee"), max 639 characters
5final_expiry_deltaNo64 bitsFinal TLC expiry delta in milliseconds
6fallback_addressNoVariableCKB on-chain address for fallback if payment fails
7featureNoVariableFeature flags (MPP, trampoline routing, etc.)
8payee_public_keyNo33 bytesCompressed secp256k1 public key of the payee
9udt_scriptNoVariableCKB type script for UDT token payments
10hash_algorithmNo1 byte0 = ckb_hash (blake2b-256, default), 1 = sha256
11payment_secretNo32 bytesSecret required for multi-part payments

Encoding Pipeline

Raw molecule-serialized bytes tend to contain consecutive zeros when optional fields are empty, making the bech32m output relatively long. To address this, Fiber compresses the molecule bytes using arithmetic coding (lossless), which roughly halves the encoded length:

data = arcode_compress(molecule_bytes(invoice_data)) ++ signature
encode(hrp, data, Variant::Bech32m)

Decoding performs the inverse: bech32m decode, split signature from data, then arithmetic decompression, then molecule deserialization.

Signature

The signature is a 65-byte secp256k1 recoverable signature ([u8; 65] = 520 bits = 104 u5 values). It proves the invoice was generated by the holder of the payee's private key and can be used to verify integrity and correctness.

The signing process:

message_hash = SHA256(hrp_bytes ++ base32_decode(data_without_signature))
signature    = Secp256k1::sign_ecdsa_recoverable(message_hash, &private_key)

The signature is appended after the compressed data before the final bech32m encoding. An unsigned invoice (flag byte = 0) omits the signature entirely.

Example

fibt1000...encoded_data...checksum

Creating an Invoice

Using fnn-cli

Create a basic invoice with a specified amount:

fnn-cli invoice new_invoice \
  --amount 1000 \
  --currency fibt \
  --description "Coffee"

The amount is specified in shannons (1 CKB = 10⁸ shannons). The currency must match your node's network configuration.

To create an invoice with an explicit expiry:

fnn-cli invoice new_invoice \
  --amount 500000000 \
  --currency fibt \
  --description "Monthly subscription" \
  --expiry 86400

The --expiry value is in seconds (86400 = 24 hours).

Using RPC

{
  "jsonrpc": "2.0",
  "method": "new_invoice",
  "params": [
    {
      "amount": "0x3E8",
      "currency": "Fibt",
      "description": "Coffee",
      "expiry": "0xE10"
    }
  ],
  "id": 1
}

All numeric fields in the RPC are hex-encoded. The amount is in shannons (0x3E8 = 1000), and expiry is in seconds (0xE10 = 3600, i.e. 1 hour).

Full parameter reference:

ParameterTypeRequiredDescription
amountu128 (hex)YesInvoice amount in shannons
currencyCurrencyYesFibb (mainnet), Fibt (testnet), or Fibd (devnet)
descriptionstringNoHuman-readable description (max 639 chars)
payment_preimageHash256NoSettlement preimage. Mutually exclusive with payment_hash. If both are omitted, a random preimage is generated
payment_hashHash256NoPayment hash only (creates a hold invoice). Mutually exclusive with payment_preimage
expiryu64 (hex)NoInvoice validity period in seconds
fallback_addressstringNoCKB on-chain fallback address
final_expiry_deltau64 (hex)NoFinal TLC expiry delta in milliseconds (min ~160 minutes, max 14 days)
udt_type_scriptScriptNoCKB type script for UDT (non-CKB) assets
hash_algorithmHashAlgorithmNockb_hash (default) or sha256
allow_mppboolNoEnable multi-part payments
allow_trampoline_routingboolNoEnable trampoline routing

Response:

{
  "invoice_address": "fibt1000...encoded_invoice...",
  "invoice": {
    "currency": "Fibt",
    "amount": "0x3E8",
    "signature": "0x...",
    "data": {
      "timestamp": "0x...",
      "payment_hash": "0x...",
      "attrs": [...]
    }
  }
}

The invoice_address is the bech32m-encoded string you share with the sender. The invoice object contains the full parsed invoice.

Paying an Invoice

Once you have an encoded invoice string, you can pay it using send_payment.

Using fnn-cli

fnn-cli payment send_payment --invoice "fibt1..."

Using RPC

{
  "jsonrpc": "2.0",
  "method": "send_payment",
  "params": [
    {
      "invoice": "fibt1..."
    }
  ],
  "id": 1
}

The payment module will validate the invoice, check for expiry, find a route through the network, and lock funds in TLCs along the path. For details on what happens after the payment is initiated, see Payment Lifecycle.

Parsing an Invoice

Before paying, you may want to inspect an invoice's contents — for example, to verify the amount or check the recipient's public key.

Using fnn-cli

fnn-cli invoice parse_invoice --invoice "fibt1..."

Using RPC

{
  "jsonrpc": "2.0",
  "method": "parse_invoice",
  "params": ["fibt1..."],
  "id": 1
}

This returns the full CkbInvoice object without paying it. Both signed and unsigned invoices can be parsed.

Invoice States

An invoice progresses through the following states:

StateDescription
OpenThe invoice has been created and is waiting to be paid
ReceivedA TLC matching the invoice's payment hash has arrived at the recipient's node, but the invoice has not yet been settled. This is the typical intermediate state for hold invoices
PaidThe preimage has been revealed and the payment is fully settled. The recipient has received the funds
CancelledThe invoice was manually cancelled by the recipient via cancel_invoice
ExpiredThe invoice has passed its expiry time (timestamp + expiry). This state is detected automatically
TransitionTrigger
OpenReceivedA TLC matching the invoice's payment hash arrives at the recipient's node
ReceivedPaidThe preimage is revealed (via settle_invoice or automatic settlement)
ReceivedCancelledThe recipient calls cancel_invoice — held TLCs are rejected
ReceivedExpiredHold timeout fires before settle or cancel — held TLCs are released
OpenCancelledThe recipient calls cancel_invoice before any payment arrives
OpenExpiredCurrent time exceeds timestamp + expiry (detected automatically)

For regular invoices, the status changes from Open directly to Paid almost instantly — skipping the Received state entirely. The Received state is typically only observable for hold invoices, where the TLC is held until the recipient explicitly calls settle_invoice.

Checking Invoice Status

Retrieve the current state of an invoice using its payment hash.

Using fnn-cli

fnn-cli invoice get_invoice --payment-hash 0x...

Using RPC

{
  "jsonrpc": "2.0",
  "method": "get_invoice",
  "params": ["0x..."],
  "id": 1
}

The response includes the invoice address, the full invoice object, and the current status. If the invoice has passed its expiry time, the status is automatically reported as Expired even if it was previously Open.

Cancelling an Invoice

You can cancel an invoice that has not yet been paid. This is useful when a payment is no longer expected or when a hold invoice's condition was not met.

Using fnn-cli

fnn-cli invoice cancel_invoice --payment-hash 0x...

Using RPC

{
  "jsonrpc": "2.0",
  "method": "cancel_invoice",
  "params": ["0x..."],
  "id": 1
}

You cannot cancel an invoice that has already been paid (Paid) or is already Cancelled. Cancelling a Received invoice will release any held TLCs back to the sender.

Settling a Hold Invoice

For hold invoices, settlement is a manual step. After the recipient has verified that the payment condition is met, they call settle_invoice with the preimage.

{
  "jsonrpc": "2.0",
  "method": "settle_invoice",
  "params": [
    {
      "payment_hash": "0x...",
      "payment_preimage": "0x..."
    }
  ],
  "id": 1
}

The node validates that hash_algorithm(preimage) == payment_hash, stores the preimage, and fulfills all held TLCs for this payment hash across all channels. Only invoices in the Received state can be settled.

Always store the preimage securely when creating a hold invoice. If you lose the preimage, the funds remain locked until the invoice expires or the TLC times out.

For the full hold invoice workflow — including creation, settlement, cancellation, and cross-chain swap usage — see Hold Invoice.

User Defined Tokens (UDT)

Fiber invoices support payments in assets other than native CKB by including a UDT type script — a CKB type script that identifies the token on-chain.

To create a UDT invoice, pass the udt_type_script parameter:

{
  "jsonrpc": "2.0",
  "method": "new_invoice",
  "params": [
    {
      "amount": "0x2710",
      "currency": "Fibt",
      "description": "10000 units of MyToken",
      "udt_type_script": {
        "code_hash": "0x...",
        "hash_type": "type",
        "args": "0x..."
      }
    }
  ],
  "id": 1
}

When a UDT type script is present, the amount refers to the UDT's base units rather than shannons. The sender's node must have a channel that supports the specified UDT to complete the payment.

Advanced Features

Multi-Path Payments (MPP)

When allow_mpp is set to true, the invoice signals that it supports receiving payments split across multiple routes. This is useful for large payments that exceed the capacity of any single channel path.

fnn-cli invoice new_invoice \
  --amount 10000000 \
  --currency fibt \
  --allow-mpp true

When MPP is enabled, a payment_secret is automatically generated and included in the invoice. The sender's payment module uses this secret to coordinate the partial payments. See Payment Lifecycle for details on how multi-path routing works, and Multi-Hop Payments for how payments traverse intermediate nodes.

Trampoline Routing

When allow_trampoline_routing is set to true, the invoice indicates the recipient supports trampoline routing — a routing optimization where the sender delegates part of the route-finding to intermediate "trampoline" nodes. See Trampoline Routing for a detailed explanation.

fnn-cli invoice new_invoice \
  --amount 5000 \
  --currency fibt \
  --allow-trampoline-routing true

Hash Algorithm

By default, Fiber uses blake2b-256 (ckb_hash) for payment hashes — the same hash function used throughout CKB. For cross-chain interoperability with networks that use SHA-256 (such as the Bitcoin Lightning Network), you can specify sha256:

fnn-cli invoice new_invoice \
  --amount 1000 \
  --currency fibt \
  --hash-algorithm sha256

This is particularly relevant for cross-chain atomic swaps where both sides of the swap must use the same hash algorithm.

Quick Reference

ActionRPC Methodfnn-cli
Create invoicenew_invoicefnn-cli invoice new_invoice
Parse invoiceparse_invoicefnn-cli invoice parse_invoice
Get invoice statusget_invoicefnn-cli invoice get_invoice
Cancel invoicecancel_invoicefnn-cli invoice cancel_invoice
Settle hold invoicesettle_invoicefnn-cli invoice settle_invoice
Pay an invoicesend_paymentfnn-cli payment send_payment
  • Hold Invoice — deferred settlement for conditional payments and atomic swaps
  • Multi-Hop Payments — how payments are routed through intermediate nodes
  • Trampoline Routing — delegating pathfinding to trampoline nodes for lightweight clients
  • Payment Lifecycle — how payments are routed and settled after an invoice is paid
  • Cross-Chain HTLC — how hold invoices enable cross-chain swaps between Fiber and Lightning

Fiber AI Assistant

Ask me anything

I can answer questions about Fiber Network using our documentation.

AI answers are based on Fiber documentation