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

Coinbase Laravel Package

shakurov/coinbase

Laravel package for integrating Coinbase Commerce cryptocurrency payments. It wraps the Coinbase Commerce API to create charges and checkouts, manage payment workflows, and handle webhook events, with configurable credentials and a simple facade-based setup for Laravel applications.

View on GitHub
Deep Wiki
Context7

Getting Started

The package is a thin Laravel service-provider/facade wrapper around Coinbase's official coinbase/coinbase-commerce PHP SDK. Treat it as configuration glue plus a facade — not as a full checkout solution.

Install and configure

composer require shakurov/coinbase
php artisan vendor:publish --provider="Shakurov\Coinbase\CoinbaseServiceProvider"

That publishes config/coinbase.php, which is essentially just your API key (and sometimes an API base URL). Add to .env:

COINBASE_API_KEY=your_commerce_api_key

Where to look first

  1. config/coinbase.php — confirm the env key name and any base-URL override.
  2. The published facade alias (Coinbase) in the provider — this tells you exactly which SDK classes are proxied.
  3. Coinbase Commerce's own API docs, not the package README, once you're past the first call. The README is short and the SDK does the real work.

First use case: sell one fixed-price item

use Shakurov\Coinbase\Facades\Coinbase;

$charge = Coinbase::createCharge([
    'name'         => 'Pro plan (1 year)',
    'description'  => 'Annual subscription',
    'local_price'  => ['amount' => '99.00', 'currency' => 'USD'],
    'pricing_type' => 'fixed_price',
    'metadata'     => ['order_id' => $order->id],
    'redirect_url' => route('checkout.thanks'),
    'cancel_url'   => route('checkout.cancelled'),
]);

$order->update(['coinbase_charge_code' => $charge['code']]);

return redirect($charge['hosted_url']);

That's the loop: create charge → persist code → redirect to hosted_url → reconcile via webhook.

Implementation Patterns

Persist the charge, then redirect. Store code, hosted_url, the requested amount, and the currency on your orders table before redirecting. You will need them for reconciliation and for support tickets.

Metadata is your join key, but never your source of truth. Put order_id (and a UUID if you like) in metadata. On webhook, look up the order by metadata, then re-fetch the charge from the API and check payments[].status / timeline before marking paid. Metadata is attacker-controllable in the sense that it's echoed back, not signed in a way you should trust for amounts.

Webhook-driven state machine. Coinbase Commerce emits charge:created, charge:pending, charge:confirmed, charge:failed, charge:delayed, charge:resolved, and checkout equivalents. Recommended mapping:

Event Action
charge:pending mark awaiting_payment
charge:confirmed mark paid_pending_settlement (do not ship goods yet on some flows)
charge:resolved mark paid, fulfill
charge:failed mark failed, release inventory
charge:delayed mark underpaid/overpaid, notify ops

Handle underpaid and overpaid explicitly — they arrive as timeline statuses and are the #1 source of "customer says they paid" disputes.

Route and controller skeleton

Route::post('/webhooks/coinbase', CoinbaseWebhookController::class)
    ->withoutMiddleware([\App\Http\Middleware\VerifyCsrfToken::class]);
public function __invoke(Request $request)
{
    $signature = $request->header('X-CC-Webhook-Signature');
    $payload   = $request->getContent(); // raw body, NOT $request->all()

    abort_unless($this->verify($payload, $signature), 400);

    $event = json_decode($payload, true);
    dispatch(new ProcessCoinbaseEvent($event))->onQueue('webhooks');

    return response()->noContent();
}

Queue the work. Coinbase retries on non-2xx, and a slow handler will get you duplicate retries.

Idempotency. Deduplicate on the event id (and/or charge.code + event type) in a webhook_events table with a unique index. Retries and out-of-order delivery are normal; charge:confirmed can arrive after charge:resolved.

Checkouts vs charges. Use createCharge for one-off invoices and createCheckout (if the wrapper exposes it) when you want a reusable, multi-item hosted page. For most Laravel apps, charges plus metadata is simpler and easier to reconcile.

Testing. The package does not ship a fake. In tests, bind a mock of the underlying SDK client, or use Laravel's HTTP fake against api.commerce.coinbase.com. Assert on the request payload your service builds — that's where your bugs are, not in the wrapper.

Service layer. Wrap the facade in App\Services\CoinbaseGateway so the rest of your code depends on an interface. You will want to swap it (Coinbase Commerce has had deprecation chatter; the wrapper has 0 dependents and a 2023 last release).

Gotchas and Tips

  • Raw body is mandatory for signature verification. $request->getContent() before any middleware mutates it. $request->all() re-serialized will not match.
  • Amounts are strings, not floats. 'amount' => '99.00', not 99.0. Floats will serialize with exponent or truncated decimals and the API will reject or misprice.
  • Commerce API keys ≠ Coinbase exchange keys. If you're getting 401s, check you generated a Commerce API key under Settings → Security.
  • No built-in webhook route or controller. You own the endpoint, the CSRF exemption, and the signature check. If the facade doesn't expose a verifier, use \CoinbaseCommerce\Webhook::verifySignature($sig, $payload, $secret) directly — that's what the wrapper calls anyway.
  • Config caching bites. After changing COINBASE_API_KEY, run php artisan config:clear. The SDK client is constructed once at container resolution; if you resolve it in AppServiceProvider::boot, you'll get stale config in queue workers.
  • Queue workers hold old config. Restart workers (php artisan queue:restart) after any deploy that changes keys or the config file.
  • Don't trust redirect_url as proof of payment. Users hit the redirect URL whether or not they paid. Only the webhook (re-fetched from the API) should flip your order to paid.
  • Expired and cancelled charges. Charges expire; the customer may come back days later. Keep the order in awaiting_payment with a timeout job rather than instantly failing it.
  • charge:delayed is not charge:failed. Underpayment and overpayment both surface as delayed. If you auto-fail on delayed you will lose legitimate customers.
  • Low-adoption risk. 46 stars, 0 dependents, last release Oct 2023. Read the service provider and facade in ten minutes — it's small enough to fork into app/Support/ if you need to. Pin the version in composer.json and don't rely on it for anything the SDK doesn't already do.
  • Extension point: if you need custom base URLs, timeouts, or retries, don't patch the package — resolve \CoinbaseCommerce\ApiClient yourself and call ApiClient::init($key) in a service provider after theirs, or just bypass the facade.
  • Currency rounding. Coinbase Commerce supports crypto settlement; your local_price currency must match your accounting. Don't assume USD — store the currency alongside every charge.
  • Log everything. Persist the full webhook payload (event id, type, charge code, timeline) for 90 days. You will need it when support asks "did this person actually pay?"

Bottom line: a reasonable five-minute shortcut for a small Laravel app that just needs hosted crypto checkout and webhooks, provided you build your own webhook controller, idempotency table, and service layer around it. The package itself is not the risk — treating it as more than a facade over the SDK is.

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.
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
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle