laravel/cashier-braintree
Laravel Cashier driver for Braintree: a simple subscription billing integration for Laravel apps. Manage customers, plans, subscriptions, coupons/discounts, and webhooks with an expressive API built on top of the Braintree PHP SDK.
Installation:
composer require laravel/cashier-braintree
Ensure braintree/braintree_php is also installed (dependency).
Configuration: Publish the config file:
php artisan vendor:publish --provider="Laravel\Cashier\CashierServiceProvider"
Update .env with Braintree credentials:
BRAINTREE_MERCHANT_ID=your_merchant_id
BRAINTREE_PUBLIC_KEY=your_public_key
BRAINTREE_PRIVATE_KEY=your_private_key
First Use Case: Subscribe a user in a controller:
use Laravel\Cashier\Cashier;
use App\Models\User;
$user = User::find(1);
$user->newSubscription('main', 'price_id_from_braintree')
->create($paymentMethodId);
config/cashier.php: Gateway settings, webhook handling.app/Models/User.php: Ensure HasSubscriptions trait is used.routes/web.php: Webhook routes for Braintree events (e.g., braintree.webhook).Subscription Management:
$user->newSubscription('main', 'braintree_price_id')
->quantity(2) // Optional
->trialDays(7) // Optional
->create($paymentMethodId);
$user->subscription('main')->cancel(); // Immediate
$user->subscription('main')->suspend(); // Grace period
$user->subscription('main')->resume();
Payment Handling:
$user->charge(1000); // 10.00 USD
$user->updateDefaultPaymentMethod($newPaymentMethodId);
Webhooks:
routes/web.php:
Route::post('/braintree/webhook', [WebhookController::class, 'handleWebhook']);
WebhookController:
public function handleWebhook(Request $request) {
$payload = $request->getContent();
event(new BraintreeWebhookReceived($payload));
}
Billing Portal:
$portalUrl = $user->subscription('main')->portalUrl();
return redirect()->away($portalUrl);
sync() to ensure local subscriptions match Braintree’s state:
$user->subscriptions()->sync();
$user->newSubscription('main', 'price_id')
->withMetadata(['plan' => 'premium'])
->create($paymentMethodId);
.env):
BRAINTREE_ENVIRONMENT=sandbox
Webhook Delays:
idempotency keys in your handler to avoid duplicate processing.public function handleWebhook(Request $request) {
$payload = $request->getContent();
$event = Braintree_WebhookParser::parse($payload);
if ($event->type === 'subscription_charged') {
$user = User::find($event->subscription->customer->id);
$user->syncSubscriptions();
}
}
Subscription Sync Issues:
sync() after manual operations (e.g., canceling via Braintree dashboard) to avoid state mismatches.fresh() to reload the subscription model if stale data is suspected.Price ID Mismatches:
price_id in Laravel matches Braintree’s id (not plan_id or product_id). Verify via Braintree’s Customer Portal.Payment Method Validation:
if (!$user->hasValidPaymentMethod()) {
return redirect()->route('payment.methods');
}
handleWebhook for troubleshooting:
\Log::debug('Braintree Webhook', ['payload' => $payload]);
4111 1111 1111 1111 (Visa)
5555 5555 5555 4444 (MasterCard)
illuminate.auth.events.SubscriptionCreated):
Event::listen(SubscriptionCreated::class, function ($user, $subscription) {
// Send welcome email
});
Laravel\Cashier\Cashier or using middleware:
public function createSubscription($name, $plan, $options = []) {
// Custom logic before creating subscription
return parent::createSubscription($name, $plan, $options);
}
Route::post('/braintree/webhook', function (Request $request) {
$request->merge(['payload' => Braintree_WebhookParser::parse($request->getContent())]);
return $this->handleWebhook($request);
})->middleware('validate.braintree.signature');
BRAINTREE_ENVIRONMENT in .env matches your Braintree account (e.g., sandbox vs. production).webhook_signing_secret in config/cashier.php:
'webhook_signing_secret' => env('BRAINTREE_WEBHOOK_SIGNING_SECRET'),
How can I help you explore Laravel packages today?