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

Laravel Bitpay Laravel Package

vrajroham/laravel-bitpay

A Laravel wrapper for the BitPay payment gateway that lets your application accept Bitcoin and other cryptocurrencies. Includes a clean facade and configuration for creating invoices, generating payment URLs, and handling crypto transactions within your Laravel app.

View on GitHub
Deep Wiki
Context7

Getting Started

vrajroham/laravel-bitpay is a thin Laravel wrapper around BitPay's PHP SDK (REST API). You get a service provider, a facade, a publishable config file, and a handful of convenience methods for invoices, payouts, refunds and currency rates. v6.0.0 is a major bump with no published changelog beyond the tag, so treat it as a breaking release until proven otherwise.

Minimal steps:

composer require vrajroham/laravel-bitpay:^6.0
php artisan vendor:publish --provider="Vrajroham\LaravelBitpay\LaravelBitpayServiceProvider"

Then look at, in this order:

  1. composer.json constraints inside vendor/vrajroham/laravel-bitpay — this is the fastest way to learn what v6 actually changed (PHP version, illuminate/* ranges, bitpay/sdk version).
  2. config/bitpay.php — network (testnet vs mainnet), token, private key path/password. Every real problem you hit in the first day is a config problem.
  3. src/LaravelBitpay.php (or the facade's accessor) — the actual method list, since the README often lags a major release.
  4. The GitHub compare view v5.3.0...v6.0.0 — with empty release notes, the diff is your changelog.

Environment variables to set before your first call (names may differ slightly in v6 — confirm against the published config):

BITPAY_NETWORK=testnet
BITPAY_TOKEN=your-pairing-token
BITPAY_PRIVATE_KEY_PATH=/secure/path/bitpay.pem
BITPAY_PRIVATE_KEY_PASSWORD=

Generate the key pair and token with BitPay's setup tooling, not by hand. Private keys must live outside the repo and outside public/.

First use case: create an invoice, persist it locally, and redirect the customer to BitPay's hosted checkout. Don't start with payouts or refunds — they require a merchant token with elevated permissions and are far harder to test.

Implementation Patterns

Wrap the facade, don't scatter it. The package is a static facade, which makes it awkward to mock and easy to call from controllers. Put it behind one small service:

final class BitPayGateway
{
    public function createInvoice(Order $order): Invoice
    {
        return BitPay::createInvoice([
            'price' => $order->total,          // string or int, never a float
            'currency' => $order->currency,
            'orderId' => (string) $order->id,
            'notificationUrl' => route('webhooks.bitpay'),
            'redirectUrl' => route('orders.thanks', $order),
            'posData' => json_encode(['order_id' => $order->id]),
        ]);
    }
}

Persistence pattern. Store a local bitpay_invoices row keyed by the BitPay invoice id, with status, orderId, and the raw JSON payload. Every webhook then becomes an upsert against a known record rather than a trust exercise. Keep the raw payload — when status transitions look wrong, the payload is the only source of truth you'll have.

Webhook handling. Register the route outside web middleware or add it to ValidateCsrfToken::$except:

Route::post('/webhooks/bitpay', BitPayWebhookController::class)
    ->withoutMiddleware([\Illuminate\Foundation\Http\Middleware\VerifyCsrfToken::class]);

BitPay posts a data blob with an embedded signature. Pass it through the SDK's signature verification before touching the database, respond 2xx fast, and do the real work in a queued job. BitPay retries on non-2xx — so the handler must be idempotent. Use updateOrCreate on the invoice id and guard status transitions (a paid event arriving twice should not double-credit a wallet).

Never trust the redirect alone. Users can close the tab; the redirectUrl is a UX nicety. Fulfilment must key off the webhook (or a reconciliation poll on getInvoice($id) for anything older than a few minutes).

Pricing with currency rates. For crypto-denominated pricing, use the package's rate call and cache it. Rates move; a per-request call will both slow checkout and burn your API quota. Cache::remember('bitpay:rates:'.$currency, 60, ...) is usually the right granularity.

Testing. Bind a fake implementation of your BitPayGateway interface in tests and assert on the payload you would have sent. For the package itself, BitPay::shouldReceive(...) works if the facade has been registered, but a hand-rolled fake is less brittle across a major version.

Money types. Invoice amounts and crypto amounts come back as strings and are frequently in satoshi-scale integers. Cast to string or int, never (float). Do conversion math with bcmath and explicit precision — this is the single most common source of cents-off bugs.

Gotchas and Tips

  • v6.0.0 shipped with an empty changelog. Do not bump the constraint on a Friday. Diff v5.3.0...v6.0.0 yourself and check: facade class/namespace, config key names, whether the underlying BitPay SDK major changed, and the minimum PHP/Laravel version. A major tag on a wrapper package almost always means "SDK major bump + config renames".
  • Config cache is a classic footgun. After editing .env or config/bitpay.php, run php artisan config:clear. A stale cached token against testnet looks identical to a bad key.
  • Network/token mismatch. A testnet token against the mainnet endpoint returns auth errors that read like credential failures. Assert config('bitpay.network') in a boot-time check or a health command.
  • Private key handling. The key is a PEM file, optionally password-protected. Mount it as a secret, chmod 600, and never commit it. In containerised setups, a relative path in config will resolve differently than locally — prefer an absolute path from env.
  • Missing or malformed notificationUrl silently degrades you to polling-only for that invoice. Validate the route exists (php artisan route:list | grep bitpay) before shipping.
  • Webhook signature verification is the whole security model. Without it, anyone can POST a fake paid event to your endpoint. Verify, then act. Log verification failures at warning level with the invoice id.
  • Idempotency and ordering. BitPay can deliver events out of order and more than once. Model status as a monotonic state machine (new → paid → confirmed → complete, plus terminal expired/invalid/exception) and ignore regressions rather than throwing.
  • Rate limits and retries. Use Laravel's Http::retry() semantics or the SDK's built-in retry, but cap backoff — checkout latency is user-visible. Wrap invoice creation in a short timeout and show a real error rather than a spinner.
  • Extension points are thin. This package is deliberately a passthrough. If you need BitPay features it doesn't wrap, resolve the underlying client from the container and call it directly rather than forking. Keep that escape hatch in your own service class so a future major bump is a one-file change.
  • Version pin deliberately. Given the undocumented major, pin ^6.0 only after your own smoke test passes (create invoice → pay in testnet → receive webhook → verify status), and record in your PR why you bumped.
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.
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle