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

Braintree Laravel Package

ptuchik/braintree

Laravel integration for the Braintree payment gateway, wrapping the official Braintree PHP SDK with a service provider, facade, and publishable configuration for accepting payments and managing customers in your PHP application.

View on GitHub
Deep Wiki
Context7

Getting Started

This is not a Laravel package — it is an Omnipay gateway driver (ptuchik/omnipay-braintree) for processing Braintree payments. You can use it inside Laravel, but it has no service providers, facades, or config files of its own. Treat it as a thin adapter around braintree/braintree_php that plugs into the Omnipay request/response pipeline.

Install and inspect first:

composer require ptuchik/omnipay-braintree

Before writing any code, open composer.json and src/ and confirm:

  1. The braintree/braintree_php constraint it requires (this fork may pin an old or overly broad version).
  2. Which requests it actually implements — purchase, authorize, capture, refund, void, createCustomer, createCard, updateCard, deleteCard. Don't assume parity with omnipay/braintree; forks often skip methods.
  3. Whether it supports cardholderAuthentication / verify3dSecure (needed for SCA in the EU/UK).

First use case — take a payment from a client-side nonce:

  1. Render Braintree Drop-in or Hosted Fields on the frontend (Braintree JS SDK, not PHP).
  2. The JS SDK returns a payment_method_nonce.
  3. POST that nonce to Laravel and submit it server-side via Omnipay:
// config/services.php
'braintree' => [
    'merchantId' => env('BRAINTREE_MERCHANT_ID'),
    'publicKey'  => env('BRAINTREE_PUBLIC_KEY'),
    'privateKey' => env('BRAINTREE_PRIVATE_KEY'),
    'testMode'   => env('BRAINTREE_TEST_MODE', true),
],
// AppServiceProvider::register()
$this->app->singleton('omnipay.braintree', fn () =>
    \Omnipay\Omnipay::create('Braintree')->initialize(config('services.braintree'))
);
$response = app('omnipay.braintree')->purchase([
    'amount'        => number_format($order->total, 2, '.', ''), // string, 2 dp
    'currency'      => 'USD',
    'token'         => $request->input('payment_method_nonce'),
    'transactionId' => (string) $order->id,
    'description'   => "Order #{$order->id}",
])->send();

if ($response->isSuccessful()) {
    $order->update(['braintree_txn' => $response->getTransactionReference()]);
} elseif ($response->isRedirect()) {
    return $response->redirect(); // 3DS, PayPal, etc.
} else {
    throw new \RuntimeException($response->getMessage());
}

Implementation Patterns

Bind the gateway once, never per request. Braintree's SDK is heavy; resolve it from the container (or a dedicated BraintreeGateway wrapper class that injects config('services.braintree')) so credentials are read from config, not scattered through controllers.

Authorize → Capture for delayed charges. Use authorize() at checkout and capture() when the order ships. This is the pattern for pre-orders and variable-amount orders:

$auth = $gateway->authorize([...])->send();
$order->update(['auth_ref' => $auth->getTransactionReference()]);
// later
$gateway->capture([
    'amount' => '10.00',
    'transactionReference' => $order->auth_ref,
])->send();

Always store getTransactionReference() — that ID is what every subsequent refund/void/capture call needs. Do not try to reconstruct it from the order; Braintree generates it.

Wrap the call in an event + job. Payment success should emit PaymentSucceeded($order, $response) and dispatch fulfilment work to a queue. This keeps your controller thin and gives you a single place to add idempotency and audit logging.

Handle redirects explicitly. Braintree returns isRedirect() for PayPal, Venmo, and 3D Secure. You must call $response->redirect() (or return the URL for a JS redirect) and add a return/complete route that calls $gateway->completePurchase([...])->send().

Vaulting (repeat customers). Use createCustomer() + createCard() to get a Braintree token, then pass token (the card token, not a nonce) on future purchase() calls. Store only the Braintree token IDs — never the PAN/CVV.

Webhooks are out of scope. Omnipay has no webhook handling. Disputes, subscription billing, and transaction_settlement_declined events must be received by a controller that uses braintree/braintree_php's Webhook::parse() + signature verification directly, in parallel with this gateway.

Testing. Pair testMode => true with Braintree's sandbox merchant credentials and their test card numbers; generate nonces client-side or via braintree/braintree_php's Test\Nonce. Never hit the sandbox with a live nonce.

Gotchas and Tips

Adoption risk is the headline issue. Zero stars, zero dependents, score 0. This is effectively a personal fork of omnipay/braintree. Before production use, diff it against the official omnipay/braintree driver and check the commit history. If it's stale, consider using the official driver or calling braintree/braintree_php directly — you lose very little and gain maintenance. Pin the version and be prepared to vendor the source if upstream disappears.

Nonces are single-use. The payment_method_nonce from the client can only be submitted once, ever. A failed validation (wrong amount format, missing currency) burns it — validate everything before calling purchase(), and ask the client for a fresh nonce on retry.

Amounts are strings, not floats. Braintree rejects 10.0 and any currency with >2 decimals it doesn't recognise. Use number_format($amount, 2, '.', '') and check Braintree's per-currency decimal rules (JPY is 0 dp, for example).

Sandbox and production credentials are not interchangeable. Three separate values (merchantId, publicKey, privateKey) plus a testMode flag; a common failure is mixing a sandbox merchant ID with a production key. Assert in a config validator that all three are present and that testMode matches app()->environment().

SCA / 3D Secure needs explicit handling. If you sell to EU/UK customers, verify this fork sets threeDSecure / cardholderAuthentication on the request and correctly surfaces the redirect. If it doesn't, you'll get hard declines that the sandbox test cards won't reproduce.

Don't log card data. Nonces are safe to store in logs; raw card fields, CVV, and full Braintree responses containing cardholder data are not. Add a log redactor for the request payload — Omnipay's getData() dumps everything.

Error handling is inconsistent across Omnipay versions. $response->getMessage() is human-readable; $response->getCode() is the Braintree status (200, 201, 402, 422). Persist both so support can triage from the order record instead of the app log.

No idempotency by default. Omnipay's purchase() has no idempotency key, so a network hiccup can double-charge. Wrap the call in a DB transaction keyed by your transactionId (order ID), or use braintree/braintree_php's idempotencyKey if this fork exposes it.

Version pinning. braintree/braintree_php has had breaking major bumps; because this fork is rarely updated, composer update can silently pull an incompatible SDK. Lock both packages (composer require ptuchik/omnipay-braintree braintree/braintree_php:x.y.*) and run the Omnipay sandbox flow in CI.

Extension points. The gateway extends Omnipay's AbstractGateway, so custom requests are a matter of subclassing and registering them via getDefaultParameters() / createRequest(). For anything you need beyond the driver — idempotency, webhooks, subscriptions, PayPal — call the Braintree SDK directly from a separate service class rather than patching the fork, so upgrades stay possible.

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