Bitcoin & Lightning
Guide for integrating T402 with Bitcoin on-chain (PSBT) and Lightning Network (BOLT11) payments.
Overview
T402 supports Bitcoin through two payment methods:
| Method | Network ID | Payload | Settlement | Best For |
|---|---|---|---|---|
| On-chain (PSBT) | bip122:000000000019d6689c085ae165831e93 | Signed PSBT | ~10 min (1 confirmation) | Larger payments |
| Lightning (BOLT11) | lightning:mainnet | Preimage | Instant (~seconds) | Micropayments |
Network Identifiers
Bitcoin uses BIP-122 chain genesis block hashes as CAIP-2 identifiers:
| Network | CAIP-2 ID |
|---|---|
| BTC Mainnet | bip122:000000000019d6689c085ae165831e93 |
| BTC Testnet | bip122:000000000933ea01ad0ee984209779ba |
| Lightning Mainnet | lightning:mainnet |
| Lightning Testnet | lightning:testnet |
On-Chain (PSBT)
Bitcoin on-chain payments use BIP-174 Partially Signed Bitcoin Transactions (PSBT). The client creates and signs a PSBT, the facilitator verifies the outputs and broadcasts it.
Payment Flow
- Server returns 402 with
network: "bip122:000000000019d6689c085ae165831e93"andpayTo(Bitcoin address) - Client builds a PSBT with the required output (payTo address + amount in satoshis)
- Client signs the PSBT and submits it as the payment payload
- Facilitator verifies the PSBT outputs match requirements
- Facilitator finalizes and broadcasts the transaction
- Facilitator waits for confirmation and returns the txid
Supported Address Types
- P2WPKH (bc1q…) — Native SegWit
- P2TR (bc1p…) — Taproot
- P2PKH (1…) — Legacy
- P2SH (3…) — Script Hash
Dust Limit
The minimum payment amount is 546 satoshis (Bitcoin dust limit). Payments below this threshold are rejected.
Integration
import { ExactBtcScheme } from '@t402/btc/exact/client';
import { registerExactBtcScheme } from '@t402/btc/exact/server';
// Client — sign PSBTs
const btcClient = new ExactBtcScheme(btcSigner);
client.register('bip122:000000000019d6689c085ae165831e93', btcClient);
// Server — accept BTC payments
registerExactBtcScheme(server, {});Signer Interface
Clients implement the ClientBtcSigner interface to sign PSBTs:
type ClientBtcSigner interface {
// SignPsbt signs a PSBT and returns the base64-encoded signed PSBT
SignPsbt(psbt string) (string, error)
// GetAddress returns the signer's Bitcoin address
GetAddress() string
// GetPublicKey returns the signer's public key as hex
GetPublicKey() string
}Facilitators implement FacilitatorBtcSigner to verify and broadcast:
type FacilitatorBtcSigner interface {
GetAddresses() []string
VerifyPsbt(ctx context.Context, signedPsbt, expectedPayTo, expectedAmount string) (valid bool, reason string, payer string, err error)
BroadcastPsbt(ctx context.Context, signedPsbt string) (txID string, err error)
WaitForConfirmation(ctx context.Context, txID string, confirmations int) (confirmed bool, blockHash string, confs int, err error)
}Lightning Network (BOLT11)
Lightning payments use BOLT11 invoices. The server provides a BOLT11 invoice, the client pays it and returns the preimage as proof of payment.
Payment Flow
- Server returns 402 with
network: "lightning:mainnet"and a BOLT11 invoice inextra.bolt11Invoice - Client pays the BOLT11 invoice via their Lightning node
- Client receives the preimage and payment hash
- Client submits preimage + payment hash as the payment payload
- Facilitator looks up the payment hash to verify settlement
- Payment confirmed instantly
Integration
import { LightningScheme } from '@t402/btc/lightning/client';
import { registerLightningScheme } from '@t402/btc/lightning/server';
// Client — pay BOLT11 invoices
const lnClient = new LightningScheme(lightningSigner);
client.register('lightning:mainnet', lnClient);
// Server — generate BOLT11 invoices
registerLightningScheme(server, { invoiceGenerator });Lightning Signer Interface
type ClientLightningSigner interface {
// PayInvoice pays a BOLT11 invoice and returns the preimage and payment hash
PayInvoice(bolt11 string) (preimage string, paymentHash string, err error)
// GetNodePubKey returns the Lightning node's public key as hex
GetNodePubKey() string
}BOLT11 Invoice Formats
| Prefix | Network |
|---|---|
lnbc | Bitcoin Mainnet |
lntb | Bitcoin Testnet |
lnbcrt | Bitcoin Regtest |
Payload Types
PSBT Payload
{
"t402Version": 2,
"payload": {
"signedPsbt": "cHNidP8BAH..."
},
"accepted": {
"scheme": "exact",
"network": "bip122:000000000019d6689c085ae165831e93",
"asset": "BTC",
"amount": "100000",
"payTo": "bc1q..."
}
}Lightning Payload
{
"t402Version": 2,
"payload": {
"paymentHash": "abc123...",
"preimage": "def456...",
"bolt11Invoice": "lnbc10u1p..."
},
"accepted": {
"scheme": "exact",
"network": "lightning:mainnet",
"asset": "BTC",
"amount": "10000",
"payTo": "02abc..."
}
}SDK Support
| SDK | On-chain (PSBT) | Lightning (BOLT11) |
|---|---|---|
TypeScript (@t402/btc) | Client, Server, Facilitator | Client, Server, Facilitator |
Go (mechanisms/btc) | Client, Server, Facilitator | Client |
Python (schemes.btc) | Client, Server, Facilitator | Client, Server |
Java (io.t402.schemes.btc) | Client, Server, Facilitator | Client, Facilitator |
Server-Side Route Configuration
Go Server with Bitcoin
routes := t402http.RoutesConfig{
"GET /api/data": {
Accepts: t402http.PaymentOptions{
{Scheme: "exact", PayTo: "bc1q...", Price: "10000", Network: btc.BtcMainnetCAIP2},
},
Description: "Data endpoint accepting BTC",
MimeType: "application/json",
},
}Bitcoin amounts are specified in satoshis (1 BTC = 100,000,000 satoshis). The minimum payment amount is 546 satoshis (dust limit).
Accepting Both BTC and EVM
routes := t402http.RoutesConfig{
"GET /api/data": {
Accepts: t402http.PaymentOptions{
{Scheme: "exact", PayTo: "bc1q...", Price: "10000", Network: btc.BtcMainnetCAIP2},
{Scheme: "exact", PayTo: "0x...", Price: "$0.01", Network: "eip155:8453"},
},
},
}Related
- @t402/btc Reference - TypeScript package reference
- Non-EVM Chains - Other non-EVM integrations
- EVM Chains - EVM chain guide