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

omnipay/braintree

View on GitHub
Deep Wiki
Context7

Getting Started

omnipay/braintree is a driver that plugs Braintree into the Omnipay abstraction. You still do the hard part (Braintree's Drop-in UI / Hosted Fields) on the frontend to produce a payment_method_nonce; this package is only the server-side call that turns that nonce into a transaction.

  1. Install — composer require omnipay/braintree:^2.0 omnipay/common. The driver pins fairly old major versions of braintree/braintree_php and omnipay/common (last tagged release was 2019), so check composer why-not braintree/braintree_php 6.x early rather than discovering the conflict while wiring the gateway.
  2. Look first at src/Gateway.php (which requests actually exist) and then the corresponding src/Message/*Request.php (which input keys each request accepts). Omnipay's AbstractRequest gives you amount, currency, description, transactionReference, token and card for free; everything Braintree-specific lives in the driver's request classes.
  3. Configure once, in a service provider, from env:
use Omnipay\Omnipay;

$gateway = Omnipay::create('Braintree'); // resolves Omnipay\Braintree\Gateway
$gateway->initialize([
    'merchantId' => config('services.braintree.merchant_id'),
    'publicKey'  => config('services.braintree.public_key'),
    'privateKey' => config('services.braintree.private_key'),
    'testMode'   => app()->environment('production') === false,
]);
  1. First use case — a one-off sale. The nonce from Braintree.js is passed as Omnipay's token:
$response = $gateway->purchase([
    'amount'   => '12.50',
    'currency' => 'USD',
    'token'    => $request->input('payment_method_nonce'),
])->send();

if ($response->isSuccessful()) {
    Order::create([
        'transaction_ref' => $response->getTransactionReference(),
        'status'          => 'paid',
    ]);
} else {
    report(new \RuntimeException($response->getMessage()));
}

In the sandbox, nonces like fake-valid-nonce, fake-valid-visa-nonce and fake-processor-declined-visa-nonce let you exercise success and decline paths without touching the browser.

Implementation Patterns

Authorize / capture. Use authorize() when you need to ship before settling, then capture() against the Braintree transaction ID. A void before capture releases the hold; a refund only applies after settlement.

$auth = $gateway->authorize(['amount' => '40.00', 'currency' => 'USD', 'token' => $nonce])->send();
$ref  = $auth->getTransactionReference();

// later, once fulfilled
$gateway->capture(['amount' => '40.00', 'transactionReference' => $ref])->send();

// or: $gateway->void(['transactionReference' => $ref])->send();

Refunds need the original transactionReference; omit amount for a full refund, supply it for partials. Braintree rejects refunds that exceed the remaining refundable amount, so track that yourself rather than relying on the gateway.

Vaulting (repeat customers). Two-step: create a customer, then attach a payment method. Store the returned Braintree customer ID and payment method token in your users/payment_methods table — the driver is stateless.

$customer = $gateway->createCustomer([
    'firstName' => 'Ada', 'lastName' => 'Lovelace',
    'email' => '[email protected]',
])->send();
$customerRef = $customer->getCustomerReference();

$card = $gateway->createCard([
    'customerReference' => $customerRef,
    'token'             => $nonce, // nonce from the browser
])->send();
$cardToken = $card->getToken();

Subsequent charges then use token => $cardToken (and customerReference where the request requires it) with no browser round-trip.

Subscriptions. createSubscription(), updateSubscription() and cancelSubscription() map to Braintree plans. You'll need a planId configured in the Braintree control panel and a payment method token attached to the customer first — subscriptions are keyed on the stored payment method, not a nonce.

Webhooks are not part of this package. Handle subscription_charged_successfully, subscription_went_past_due, disbursement, etc. separately with braintree/braintree_php's Braintree\Webhook::parse() in a controller that verifies the bt_signature/bt_payload params. Keep that handler isolated from the Omnipay path; the two do not share a response object.

Wrapping it for testability. Since Omnipay::create() returns a concrete gateway, bind it behind an interface or expose a PaymentService that accepts an injected Gateway. Mock PurchaseResponse rather than the SDK so unit tests stay fast and don't need the sandbox.

Multi-gateway apps are where this package earns its keep — the same purchase()->send()->isSuccessful() flow works for Stripe, PayPal, etc. If Braintree is your only processor, consider calling braintree/braintree_php directly; the abstraction costs you type information for little gain.

Gotchas and Tips

  • Maturity risk. Last release 2019-05-08 and zero dependents means it is effectively unmaintained upstream of Omnipay's own release cycle. Budget for a dev-master pin or a fork if you need PHP 8 compatibility, newer braintree_php, or bug fixes.
  • token is overloaded. It means payment method nonce for one-off purchases and stored payment method token for vaulted charges. Confirm per request class in src/Message — this is the single most common source of "why is my stored card charging a nonce" bugs.
  • Do not send raw card data. Braintree declines server-side card numbers for most merchants due to PCI scope. If you're tempted to pass card => [...], stop and build the Drop-in/Hosted Fields integration instead.
  • SCA / 3-D Secure isn't first-class. EU merchants post-2019 need the 3DS-adjusted nonce from Braintree's client SDK; this driver has no clean hook for that flow. Drop down to braintree_php for the 3DS challenge step and only hand the final nonce to Omnipay.
  • Currency is fixed per merchant account. Passing a currency that doesn't match the Braintree account's configured currency fails at the gateway, not at validation. Validate it in your own request class before you spend a round-trip.
  • Void vs refund timing. Same-day unsettled transactions can be voided; settled ones must be refunded. Model this in your order state machine — don't let support staff guess from the Braintree dashboard.
  • Amounts are strings. Braintree expects "12.50". Convert with number_format($cents / 100, 2, '.', '') in one place; float rounding bugs show up as declined or off-by-a-cent transactions.
  • Error handling. isSuccessful() === false covers both declines and transport errors. Inspect getMessage() and getCode() (Braintree's status/reason text) and log both — Braintree's decline reasons like 2000 (do not honor) are far more useful in support tickets than the generic message.
  • Debugging the SDK. Since the driver constructs its own Braintree_Gateway internally, the simplest logging hook is \Braintree\Configuration::setLogger() called before you initialize() the Omnipay gateway, plus setEnvironment() explicitly when running tests. Alternatively instantiate braintree_php directly for a one-off reproduction.
  • Extension points. Adding a request is straightforward: subclass Omnipay\Braintree\Message\AbstractRequest, implement getData(), register a createXxxRequest() method on the Gateway subclass. Reuse the driver's AbstractResponse for consistent success/error mapping rather than rolling your own.
  • Idempotency. Braintree deduplicates on transaction IDs it generates, not on your request. If you retry a purchase() on timeout you can double-charge — persist an intent row first, then reconcile via Braintree's transaction search in a queued job.
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.
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log