Adding “Bitcoin payments” to an application is not one function call. A wallet integration must connect several responsibilities that may live in different processes, devices, or organizations:
- Define who controls spending keys.
- Derive scripts and addresses.
- Discover transactions relevant to those scripts.
- Track confirmed and unconfirmed UTXOs.
- Construct recipients, change, fees, sequences, and locktimes.
- Obtain approvals and signatures.
- Finalize and broadcast transactions.
- Reconcile mempool, confirmation, replacement, conflict, and reorganization state.
- Preserve enough information to recover the wallet and the application’s records.
The important question is not whether an application “has a wallet.” It is where each responsibility sits and what evidence the application accepts.
Custodial, self-custodial, watch-only, and signing-service models
In a custodial architecture, an operator controls the keys or spending policy on behalf of users. User balances may exist partly or entirely in an internal ledger. A withdrawal request becomes an application instruction to the custodian, not direct proof that a specific UTXO belongs to the user. Custody requires strong authorization, accounting, withdrawal review, segregation, and incident-response controls.
In a self-custodial architecture, the user or user-controlled device holds the spending authority. The application may help derive addresses, build transactions, or find chain data, but it cannot spend without the user’s keys or signer. Self-custody reduces operator control over funds while increasing the importance of backup, compatibility, user verification, and device security.
A watch-only architecture contains scripts, descriptors, or extended public keys but no private signing material. It can derive addresses, identify relevant transactions, and construct unsigned transactions. Watch-only systems are useful for accounting, monitoring, and separation of duties, but they can still expose sensitive wallet activity.
A signing-service architecture keeps keys in a separate service, hardware security module, hardware wallet, or offline system. An online coordinator prepares a transaction or PSBT, while the signer applies policy and authorizes signatures. This separation can reduce key exposure, but only if authentication, approval logic, and transaction verification are designed correctly.
These models can be combined. A company may use a watch-only online wallet, a policy service, and multiple hardware signers. A mobile wallet may hold keys locally while using a hosted indexer. A custodial service may use external signing devices internally.
Embedded libraries versus external wallet services
An embedded wallet library runs wallet logic inside the application or a closely related process. It may manage descriptors, derivation, transaction graphs, persistence, coin selection, and PSBT construction. This can reduce network dependencies and allow application-specific control. It also places wallet-state migrations, dependency security, concurrency, and possibly secrets inside the application boundary.
An external wallet service exposes wallet operations over an authenticated interface. Bitcoin Core wallet RPC is one example of an external process boundary. A custom signing or custody service is another. External services can isolate permissions and reduce the number of components that handle keys, but they introduce network, availability, authorization, versioning, and operational dependencies.
The relevant questions are which process holds keys, which process can change destinations or fees, how requests are authenticated, how duplicate operations are handled, and what state must be restored after failure.
Synchronization models
A wallet must identify chain activity relevant to its scripts. Common synchronization models include full-node-backed, Electrum-backed, Esplora-backed, and compact-filter-backed designs.
A full-node-backed wallet can use Bitcoin Core wallet functionality or custom logic connected to a validating node. The node validates blocks and transactions, but custom wallet software still needs a method for finding relevant scripts. Bitcoin Core’s wallet maintains its own wallet state; a bare full node does not automatically answer arbitrary address-history queries.
An Electrum-backed wallet queries or subscribes to script hashes through an Electrum-protocol server. The server maintains an external index. The wallet can obtain histories, balances, and transaction data efficiently, but it depends on the server’s chain tip, index correctness, protocol version, privacy practices, and availability. A client that validates headers and Merkle proofs gains evidence of transaction inclusion, but it does not independently validate every block and transaction rule as a full node does.
An Esplora-backed wallet calls HTTP endpoints backed by an indexer. It may retrieve addresses, scripts, transactions, UTXOs, blocks, fee estimates, and mempool information. The same trust questions apply: what validates the chain, which fields are indexed or cached, how reorganizations are represented, and what requests are logged?
A compact-filter-backed wallet obtains block headers and BIP 157/158 compact filters, matches wallet scripts locally, then downloads candidate blocks. This can reduce direct disclosure of individual addresses or script hashes. It still requires correct filter-header handling, false-positive handling, block retrieval, chain selection, and local scanning. A malicious source can omit or delay data unless the client cross-checks appropriately.
Key-generation boundaries
Private keys and seed material should be generated inside the component intended to hold signing authority. A browser frontend, application log, analytics event, crash report, public fixture, or ordinary environment dump should not become an accidental key-management system.
Seed material is not the same as every key, descriptor, or wallet record. Hierarchical deterministic wallets derive many keys from a root according to defined paths. BIP 32 extended keys combine a key with chain code and derivation metadata. Wallets may also depend on script types, derivation paths, key origins, checksums, multisignature ordering, Miniscript policies, and application labels.
A seed can reproduce keys under the same derivation scheme. It may not reproduce all information needed to identify which scripts to scan, how a multisignature policy was assembled, which descriptors were imported, which accounts were used, or which application records were reserved.
Key-generation boundaries should therefore be documented alongside descriptor and recovery data, not only as “we have the seed.”
Extended public keys and privacy
An extended public key, or xpub in common terminology, can derive a branch of public keys without exposing the corresponding private keys. This is useful for watch-only wallets and address generation.
It is also privacy-sensitive. Anyone who obtains an extended public key and knows the derivation scheme may derive many wallet addresses and observe related transactions. Sharing an xpub with a hosted service can reveal a broad view of wallet activity rather than one payment request.
Applications should minimize where extended public keys are stored, restrict access, separate account scopes where appropriate, and avoid placing them in analytics, support tickets, or client logs. An xpub is not a spending secret, but it is not harmless public metadata.
Descriptor wallets and script types
Output descriptors describe the scripts a wallet watches or controls. They can include key origins, derivation paths, branches, and script wrappers. Descriptor wallets make the wallet’s script policy more explicit than an undocumented assumption about one address type.
Common script types include legacy P2PKH, wrapped SegWit, native SegWit, and Taproot output forms. Each has different address encodings, fee characteristics, signing data, and compatibility. Multisignature and Miniscript policies add further structure.
An integration must enforce network and address compatibility. A syntactically valid address for mainnet must not be accepted as a testnet destination. Some libraries represent addresses as unchecked until the network is confirmed. Applications should not silently reinterpret one network as another.
Script compatibility also extends to signers. A wallet library may construct a Taproot or Miniscript spend that a selected hardware wallet or external signer does not support. Compatibility should be tested against exact software and firmware versions.
Address derivation, change, and discovery
Wallets typically derive receiving and change scripts from separate branches. A receive address can be shown before funds arrive. That does not mean payment has been detected.
Payment detection requires scanning the relevant chain and mempool data. Confirmation requires inclusion in a block on the wallet’s accepted chain. More confirmations reduce ordinary reorganization risk but do not create absolute irreversibility.
Wallets that generate addresses ahead of observed use may apply a gap limit or other discovery rule. If an application derives far beyond the expected range without preserving the last-used state, recovery software may stop scanning before it reaches used addresses. Descriptor range data and last-revealed indexes may therefore matter.
Change addresses require the same care as recipient addresses. The wallet must identify which output is change, derive it from the intended descriptor, preserve it during signing, and later track it. A signer should not assume an unlabeled second output is legitimate change.
Address reuse can connect payments and reduce privacy. Hosted synchronization can further link queries by address, script hash, xpub, account, and IP address.
Watch-only wallets and external signing
A watch-only wallet can track scripts and build unsigned transactions while keys remain elsewhere. This architecture can separate online observation from signing, but the unsigned transaction remains security-sensitive.
An online coordinator can lie about the recipient, amount, fee, inputs, change, or application context. A signer that only checks signature validity may authorize a transaction the user did not intend. Hardware wallets and offline signers should verify as much transaction and policy information as they can, and users should review displayed details.
Air-gapped workflows can reduce network exposure, but parsing, firmware, transfer-media, and display risks remain.
PSBT version and signer compatibility
PSBT allows a coordinator to collect transaction and signing information without exposing private keys. Version 0 is defined by BIP 174. Version 2 is defined by BIP 370. Taproot and MuSig2 use additional fields defined by later BIPs.
A wallet integration must know which PSBT versions and fields each signer preserves and understands. Some software can sign version 0 but not version 2. Some can convert formats but may not preserve proprietary or unknown fields. Some hardware devices support a script type only under specific key-origin or descriptor conditions.
The integration should test:
- PSBT version acceptance
- Unknown-field preservation
- Taproot and multisignature fields
- Input UTXO requirements
- Key-origin information
- Change detection
- Finalization behavior
- Multiple signer ordering
- Partial failure and retry
A PSBT that receives signatures is not automatically safe. Signatures prove authorization by keys under Bitcoin’s signature rules. They do not prove that the application selected the correct recipient, amount, fee, or change output.
Transaction construction
Transaction construction begins with a set of intended payments and available UTXOs. The wallet chooses inputs, creates recipient outputs, usually creates change, sets version and locktime, assigns input sequences, and calculates fees.
Coin selection balances fee cost, privacy, UTXO consolidation, confirmation status, script types, and application policy. Different algorithms can produce different valid transactions.
UTXO locking or reservation prevents two concurrent requests from spending the same input. A reservation is application state, not Bitcoin consensus. It can expire incorrectly, be lost after a crash, or conflict with another wallet instance. The application must reconcile reservations against actual mempool and chain state.
Recipient outputs must match the user’s intended address and amount. Address parsing should be network-aware and checksum-validated.
Change should return excess value to a wallet-controlled script. The wallet and signer need enough descriptor information to recognize it.
Fees depend on transaction weight and fee rate. Applications must use explicit units such as satoshis per virtual byte. Floating-point bitcoin amounts and ambiguous “bytes” can produce serious errors.
Dust is primarily a policy concept in this context. Bitcoin Core’s current dust threshold calculations affect standard transaction relay and wallet construction. Dust policy can change by implementation and version; it is not a universal fixed consensus minimum for all outputs.
A transaction may satisfy consensus rules yet fail a node’s current mempool policy. Construction tests should therefore include the exact node and network policy expected in production.
Signing, finalization, and broadcast
Signing applies one or more signatures according to the scripts and sighash rules of the selected inputs. Finalization assembles the witness and script data required for network serialization.
A valid signature proves that the relevant key authorized the signed transaction data under the selected sighash. It does not prove the transaction matches human intent. Signers should verify recipients, amounts, fees, change, and policy before signing.
Broadcast sends the finalized transaction to a node or service. A successful RPC or API response may mean only that the service received or accepted the request. It does not guarantee that every node accepts the transaction, that peers relay it, that miners include it, or that it confirms.
Applications should distinguish:
- Constructed
- Partially signed
- Fully signed
- Finalized
- Submitted
- Accepted by a particular mempool
- Observed by other nodes
- Confirmed in a block
- Reorganized or conflicted
Collapsing those states into “paid” creates operational and accounting errors.
Mempool acceptance and local policy
A node’s mempool contains transactions it currently accepts under local policy. Policy covers standardness, fees, package limits, replacement, resource limits, and other implementation behavior.
Two nodes can have different mempools. A transaction may be accepted by the broadcasting node and absent elsewhere. It can be evicted, replaced, expire, conflict with another transaction, or be mined after disappearing from one observed mempool.
Applications should record which node or service produced a mempool status and when. They should not treat a hosted “seen” flag as consensus state.
Confirmations and reorganizations
A transaction is confirmed when it is included in a block on the wallet’s current accepted chain. Confirmation count is derived from the transaction’s block height and the current chain tip.
A reorganization can disconnect the block. The transaction may return to a mempool, confirm in a different block, be replaced by a conflict, or disappear. Application balances, invoices, reservations, and fulfillment records must be able to move backward as well as forward.
Confirmation is therefore not absolute irreversibility. Applications should define risk-sensitive confirmation policies without presenting them as guarantees. High-value or externally irreversible actions may require more evidence and operational review than low-risk actions.
Replacement and fee bumping
Replace-by-fee, commonly called RBF, allows some unconfirmed transactions to be replaced under node policy. BIP 125 documents the original opt-in replacement policy, but current Bitcoin Core replacement behavior must be checked against the current implementation and release because policy has evolved.
Child-pays-for-parent, or CPFP, spends an output from an unconfirmed parent with a fee that raises the effective package incentive. Whether a package is accepted or mined depends on current policy, package relationships, and miner behavior.
Wallet integrations should treat RBF and CPFP as policy-sensitive mechanisms. They should test replacement eligibility, fee calculations, recipient preservation, change handling, conflicts, and UI status. A fee bump does not guarantee confirmation.
Abandonment, rebroadcast, and conflicts
A wallet may mark an unconfirmed transaction as abandoned locally, but local abandonment does not erase the transaction from peers or prevent later confirmation if it remains valid and available elsewhere.
Rebroadcast behavior is implementation-specific and can leak timing or wallet information. Applications should avoid blind multi-provider rebroadcast loops.
Conflict handling should identify transactions that spend the same inputs. The application must decide which state is provisional, which invoices remain open, and how to reconcile a confirmed conflict. Internal records should preserve history rather than overwrite the first transaction without explanation.
Balance categories
A single “balance” number can hide important distinctions.
Confirmed funds are UTXOs in blocks on the current accepted chain.
Unconfirmed funds are associated with transactions not yet confirmed and may depend on local mempool observations.
Immature funds include coinbase outputs that cannot yet be spent under consensus maturity rules.
Available funds are funds the wallet currently considers selectable after applying confirmations, policy, locks, and application rules.
Reserved funds are UTXOs the application has assigned to an in-progress operation. Reservation is local state and must be reconciled after failures.
Wallet libraries and services may define balance categories differently. Applications should map exact source fields into their own explicit model rather than assume names are universal.
Backup and recovery
A wallet backup should be tested, not merely created. Recovery needs vary by architecture.
A seed may restore deterministic keys but not necessarily:
- Descriptor checksums and ranges
- Script or Miniscript policy
- Multisignature cosigner keys and ordering
- Key origins and derivation paths
- Imported watch-only scripts
- Labels and transaction metadata
- UTXO reservations
- Approval records
- Invoice mappings
- Hardware device configuration
- External signer policy
- Application database state
Descriptor wallets can make script recovery more explicit, but descriptors must themselves be backed up and protected according to whether they contain private or public key material.
Multisignature recovery requires enough cosigner and policy information to reconstruct the same spending scripts. A set of seeds without the correct policy and derivation can be insufficient.
Recovery testing should occur on an isolated environment using exact documented software versions. The test should verify address derivation, history discovery, balance reconstruction, transaction creation, signer compatibility, and reorganization handling.
Authentication, authorization, and approval controls
Wallet operations should not be exposed as unrestricted node or wallet administration.
Authentication establishes which user or service is making a request. Authorization determines which wallets, amounts, destinations, and operations that identity may use. Approval controls can require multiple people, delayed review, destination allowlists, transaction limits, or independent signer confirmation.
Rate limiting reduces abuse and accidental request storms. Idempotency prevents a retried withdrawal request from creating multiple transactions. Audit logs should record decisions and identifiers without exposing private keys, seed phrases, authentication secrets, or unnecessary PSBT data.
Secret redaction must cover application logs, RPC traces, exception reports, analytics, support exports, and monitoring systems. Sensitive wallet metadata can remain dangerous even when private keys are absent.
Privacy boundaries
Wallet integration leaks can occur through address reuse, xpub exposure, hosted address queries, script-hash subscriptions, IP addresses, transaction broadcasts, timing patterns, and centralized logs.
Local nodes and compact filters can reduce some hosted-query leakage, but peer and broadcast behavior can still reveal information.
Privacy claims should be scoped to a defined attacker, architecture, and configuration. “Self-hosted” and “non-custodial” do not automatically mean private.
Testing and monitoring
Regtest should cover address derivation, payments, change, coin selection, PSBT exchange, signer rejection, replacement, CPFP, abandonment, conflict, and recovery.
Reorganization tests should confirm that:
- Confirmations can decrease.
- Payments can return to unconfirmed state.
- Reserved or spent UTXOs are recalculated.
- Application fulfillment does not silently remain final.
- Indexers and wallet databases roll back correctly.
- Conflicting transactions are surfaced.
Production monitoring should compare the wallet’s chain tip with its validating node or backend, track sync lag, record failed broadcasts, detect signer incompatibility, and alert on unexplained balance or UTXO changes.
Monitoring is evidence about components, not consensus itself. An application database can be internally consistent and still follow the wrong chain tip or stale index.
The working model
A Bitcoin wallet integration is a state machine across multiple trust boundaries.
Keys or signers authorize spends. Descriptors and derivation define scripts. Chain backends provide validated or indexed data. Wallet logic discovers transactions and maintains UTXOs. Transaction builders choose inputs, outputs, change, and fees. Signers verify and authorize. Nodes apply mempool policy and broadcast. Blocks create confirmation state. Reorganizations can reverse that state. Application databases map all of it to users, invoices, approvals, and accounting.
Address generation is not payment detection. Detection is not confirmation. Confirmation is not absolute irreversibility. A signature is not proof of transaction intent. A seed is not always a complete recovery package. A hosted backend is not self-validation.
A reliable integration keeps each statement precise, preserves the information needed to recover, and tests how state moves backward when assumptions fail.