Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Bitcoind Bundle Laravel Package

nbobtc/bitcoind-bundle

Symfony bundle that integrates the nbobtc/bitcoind-php client with Bitcoin Core’s JSON-RPC API, simplifying connection configuration and service injection so Symfony apps can query the daemon for blockchain and wallet operations.

View on GitHub
Deep Wiki
Context7

Getting Started

Do not composer require this bundle in a Laravel app. nbobtc/bitcoind-bundle registers a Symfony Bundle class, ships Resources/config/services.xml, and expects the Symfony kernel to boot it. Laravel's container will never see it. Its last activity is Symfony 2.x era (EOL 2018); its Guzzle-era transitive dependencies will fight anything modern.

What you actually get from it is a reference for how to wire a bitcoind JSON-RPC client into a framework, plus the underlying client library nbobtc/bitcoind-php. The bundle itself is roughly four files: a DependencyInjection/Extension, a DependencyInjection/Configuration, a service definition, and a Bundle class. That is the whole package. Read those two DI files first — Configuration.php tells you the config keys the author considered necessary (host, port, user, password, and a scheme/URL knob), and services.xml shows you exactly how the client is constructed, which is the honest documentation of the underlying library's constructor signature.

Minimal path for a Laravel developer:

  1. composer require nbobtc/bitcoind-php (the client, not the bundle). Pin it; it is dormant, so vendor a fork if you need patches.
  2. Create config/bitcoind.php reading from env().
  3. Write one BitcoinServiceProvider that binds a singleton.
  4. Run a Bitcoin Core node locally on regtest and point at it.
// config/bitcoind.php
return [
    'url'  => env('BITCOIND_URL', 'http://127.0.0.1:8332'),
    'user' => env('BITCOIND_USER'),
    'pass' => env('BITCOIND_PASS'),
    'timeout' => (int) env('BITCOIND_TIMEOUT', 30),
];
// app/Providers/BitcoinServiceProvider.php
public function register(): void
{
    $this->app->singleton(BitcoinRpcClient::class, function ($app) {
        $cfg = $app['config']['bitcoind'];
        return new NbobtcAdapter($cfg['url'], $cfg['user'], $cfg['pass'], $cfg['timeout']);
    });
}

First use case to prove out: read-only chain queries against regtest — getblockchaininfo, getblockcount, then getrawtransaction for a txid you mined yourself. That exercises auth, HTTP transport, timeout, and the RPC surface in ten minutes, and it fails loudly if any of the four is wrong. Do not start with wallet RPCs; they drag in unlocked-wallet and signing semantics you have not scoped yet.

Implementation Patterns

Bind an interface, not the vendor class. This is the single decision that pays for itself. The vendor client is abandoned; your interface is not.

interface BitcoinRpcClient
{
    public function getBlockchainInfo(): array;
    public function getBlockCount(): int;
    public function getRawTransaction(string $txid, bool $verbose = true): array;
    public function getBalance(): float;
}

Your NbobtcAdapter implements it; everything else in the app type-hints the interface. Swapping to denpa/php-bitcoinrpc or a managed provider's SDK becomes a one-class change instead of a grep-and-replace across the codebase. This is also the one place to add logging, metrics, retry, and the circuit breaker.

Config from the secret manager, never .env in source control. The bundle offers no opinion on credentials; neither should you leave them in a committed file. On Laravel, pull from config backed by a vault or a secrets manager, and use a Unix socket (http://unix:/path/to/bitcoin.sock) or TLS if the node is not strictly on localhost. Never expose the RPC port.

Wrap call sites, don't call RPC from controllers. The pattern that survives contact with production:

class ChainReader
{
    public function __construct(private BitcoinRpcClient $rpc, private Cache $cache) {}

    public function confirmedBalance(): float
    {
        return $this->cache->remember('btc:balance', 30, fn () => $this->rpc->getBalance());
    }
}

Controllers and jobs depend on ChainReader, not on the RPC client. That's where your confirmation policy, idempotency, and caching live.

Cache immutable data aggressively. Confirmed transactions below the reorg horizon never change. Key your cache on (txid, blockhash) and treat it as permanent. This is the cheapest throughput win available, because you will hit the node's thread pool (rpcthreads) and disk long before you hit anything PHP-related.

Separate read paths from write paths. Reads (getblockchaininfo, getblockcount, getrawtransaction, getbalance) are low blast radius and safe to build first. Writes (sendtoaddress, sendrawtransaction) move funds and need their own review, idempotency keys, and probably a human-in-the-loop. Don't let a send sneak into a general-purpose service because it was convenient.

Payment reconciliation workflow, the realistic Laravel shape: a scheduled job polls getblockcount, checks the tip against the last processed height, walks new blocks, and matches outputs against your invoice addresses; confirmations are counted from the block height and re-checked on reorg. Store the processed height in the database so restarts are safe. This is the pattern most teams actually need, and it is read-only until the moment you decide to sweep funds.

Expose a health check. A route that calls getblockchaininfo and returns the node's initialblockdownload flag plus the chain tip height. Gate your reconciliation job on initialblockdownload === false — an IBD node answers RPC happily, and every balance it reports is wrong.

Gotchas and Tips

It is not a Laravel package — this is the gotcha. Every hour spent trying to make the Symfony bundle boot inside Laravel is wasted. Copy the wiring pattern, take the client library, delete the bundle reference. The end state should have zero mentions of nbobtc/bitcoind-bundle except in a commit message.

Verify the vendor client before writing anything. Check its Guzzle major version, its PHP syntax level, and whether it loads under PHP 8 without deprecation noise. These are the usual hard blockers and they surface at composer install, so check first rather than after you've written an adapter.

RPC drift is the failure mode that bites two years in. Bitcoin Core removes and renames RPCs on a rolling basis: getinfo, getaccount*, move, sendfrom are gone or deprecated; signrawtransaction became signrawtransactionwithwallet; account-based wallets are superseded by descriptors. The abandoned client's methods may map to methods your Core version no longer has. Pin your Core version, record the mapping of every RPC you call in the repo (a markdown table is fine), and nominate an owner for "Core upgrade review" as a standing responsibility.

-txindex is the silent one. getrawtransaction for an arbitrary txid fails unless the node has -txindex. Teams discover this in production, usually right after the feature ships. Enable it at node setup, or document that you only look up transactions you already know the block for.

Reorgs regress confirmation counts. Anything you derive from confirmations — "paid", "settled", "released" — must tolerate going backwards. Define the confirmation policy (how many, and what happens when a block is orphaned) before writing the reconciliation job, not after the first surprise.

Timeouts must be explicit. getblock on a large block can exceed default HTTP timeouts. The bundle sets nothing; set timeout in your config and pass it through to the client. Also verify that your adapter actually passes timeout to the transport — Guzzle's default is indefinite in some configurations.

Auth errors are opaque. Rotated credentials, rpcauth confusion, and cookie-file permissions all produce the same unhelpful 401. Test with a known-good bitcoin-cli -rpcconnect from the node itself before blaming your PHP.

Wallet RPCs fail at the worst moment. A wallet can be locked or (worse) unencrypted, and signing paths will throw exactly when you need them. Only relevant if you take on write paths, but if you do, health-check the wallet state alongside the chain state.

Debugging order: bitcoin-cli -rpcuser=... -rpcpassword=... getblockchaininfo first. If that fails, the problem is the node, not your code. If it succeeds and PHP fails, the problem is credentials, transport, or URL scheme — in that order. Log the full request URL (redacted) and the response body on non-2xx; the client will not do it for you.

Extension points are your own. The bundle has nothing worth extending — no eventing, no batching helper, no middleware. Your adapter is the extension point. Put retries with backoff for 5xx, a short circuit breaker so a dead node doesn't hang your request workers, and a metric per RPC method there. That's ten lines each and it is the difference between a node outage being a page and being an incident.

Testing: run regtest in CI. Mine a block, send to an address, query it back. Do not point tests at testnet or mainnet; a regtest node starts in seconds and gives you deterministic reorgs when you need to prove the confirmation logic actually handles them.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core