Laravel
Cashier-shaped billing layer with a Billable trait, an Eloquent Subscription model and a verified webhook route.
Install
Then run the migration. It creates billkit_subscriptions and adds billkit_customer_id to your billable table.
The service provider is auto-discovered. Publish the config with php artisan vendor:publish --tag=billkit-config if you want to change it, and set your credentials.
BILLKIT_API_KEY=bk_test_...
BILLKIT_WEBHOOK_SECRET=bkwhsec_... The package wraps billkit-eu/billkit-php, which it pulls in for you. Reach the raw client any time with $user->billkitClient().
Make a model billable
<?php
use BillKit\Laravel\Billable;
class User extends Authenticatable
{
use Billable;
} Start a subscription
BillKit provisions subscriptions from the inbound payment webhook. There is no synchronous “create subscription”
call, so the flow is checkout, then redirect, then your webhook creates the local Subscription. That is the
Cashier model exactly.
checkout() returns a Checkout, which is Responsable, so a controller can return it and Laravel does the redirect.
return $request->user()->checkout('price_9XKp2vQ1', [
'success_url' => route('billing.done'),
'cancel_url' => route('pricing'),
'trial_days' => 14,
'coupon_code' => 'LAUNCH',
]); One-off charges
charge() is the mandate-less single payment, Cashier’s charge() in shape. It is redirect-based too, and settles through the one_shot_payment.succeeded and one_shot_payment.failed webhooks.
return $request->user()->charge(1999, 'EUR', 'creditcard', [
'success_url' => route('thanks'),
'cancel_url' => route('cart'),
'description' => 'One premium widget',
'refund_window_days' => 30, // 0 disables refunds; default 30; max 365
]);
// Later, within the window:
$user->refundOneShot('osp_9XKp2vQ1'); // full
$user->refundOneShot('osp_9XKp2vQ1', ['amount_cents' => 500]); // partial Gate access
if ($user->subscribed()) {
// active, trialing, past_due, or inside a cancellation grace period
}
$user->onTrial();
$user->onGracePeriod(); Manage a subscription
$sub = $user->subscription(); // BillKit\Laravel\Subscription
$sub->previewSwap('price_pro_yearly'); // quote the proration first
$sub->swap('price_pro_yearly'); // change plan
$sub->cancel(); // at period end
$sub->reactivate(); // undo a scheduled cancellation
$sub->pause();
$sub->resume();
// Both return a URL to redirect to:
return redirect($sub->updatePaymentMethod(route('billing')));
return $sub->redirectToBillingPortal(route('billing')); Webhooks
The package registers POST /billkit/webhook, verifies the signature and keeps the local Subscription rows in sync. Point a BillKit webhook endpoint at that URL and set BILLKIT_WEBHOOK_SECRET.
For anything the package does not handle itself, listen for the dispatched event.
use BillKit\Laravel\Events\WebhookReceived;
Event::listen(function (WebhookReceived $event) {
if ($event->payload['type'] === 'invoice.paid') {
// $event->payload['data'] is the invoice
}
}); Logging
Off unless you name a channel from config/logging.php. A dedicated channel keeps billing traffic out of your main log.
BILLKIT_LOG_CHANNEL=billkit You then get one debug record per HTTP attempt and per response, and one warning per retry. The channel’s own level still applies. API keys, bodies and query strings are never written.
BillKit