定制 veekthoven/laravel-cashier-bachs 二次开发

按需修改功能、优化性能、对接业务系统,提供一站式技术支持

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

veekthoven/laravel-cashier-bachs

Composer 安装命令:

composer require veekthoven/laravel-cashier-bachs

包简介

Laravel Cashier Bachs provides an expressive, fluent interface to Bachs' subscription billing services.

README 文档

README

Latest Version on Packagist GitHub Tests Action Status GitHub Code Style Action Status Total Downloads

Laravel Cashier Bachs provides an expressive, fluent interface to Bachs' subscription billing services — modeled after Laravel Cashier for Stripe. It handles Bachs customers, hosted checkouts, one-time payments, subscriptions with trials, plan swaps with proration, cancellation grace periods, and webhook-driven state syncing.

Note

In Bachs, subscriptions are always started by the customer completing a hosted checkout for a recurring product. This package creates the checkout session and then keeps your local subscription records in sync via webhooks.

Installation

Install the package via composer:

composer require veekthoven/laravel-cashier-bachs

Run Cashier's migrations, which add a bachs_id and trial_ends_at column to your users table and create the subscriptions table:

php artisan migrate

You may publish the migrations to customize them:

php artisan vendor:publish --tag="cashier-migrations"

And optionally the config file:

php artisan vendor:publish --tag="cashier-config"

Configuration

Add your Bachs credentials to your .env file:

BACHS_API_KEY=sk_sandbox_...
BACHS_WEBHOOK_SECRET=whsec_...

Keys prefixed with sk_sandbox_ are automatically routed to the Bachs sandbox API and sk_live_ keys to production — no extra configuration needed.

Billable model

Add the Billable trait to your model:

use Veekthoven\CashierBachs\Billable;

class User extends Authenticatable
{
    use Billable;
}

If your billable model is not App\Models\User, register it in a service provider:

use Veekthoven\CashierBachs\Cashier;

Cashier::useCustomerModel(Team::class);

Customers

// Create the customer in Bachs (uses the model's email, name and phone)...
$user->createAsBachsCustomer();

// Or with overrides...
$user->createAsBachsCustomer(['name' => 'Jane Doe']);

$user->hasBachsId();
$user->bachsId();

// Retrieve the raw Bachs customer payload...
$customer = $user->asBachsCustomer();

// Push local changes to Bachs...
$user->syncBachsCustomerDetails();

The trait reads email, name and phone from your model by default. Override bachsEmail(), bachsName() or bachsPhone() to customize.

Subscriptions

Creating subscriptions

Subscriptions start with a hosted checkout for a recurring Bachs product:

use Illuminate\Http\Request;

Route::get('/subscribe', function (Request $request) {
    return $request->user()
        ->newSubscription('default', 'prod_abc123')
        ->checkout([
            'success_url' => route('billing.success'),
            'cancel_url' => route('billing.cancel'),
        ]);
});

The Checkout object is responsable — returning it redirects the customer to the Bachs-hosted checkout page. You can also grab the URL yourself with $checkout->url().

Once the customer pays, Bachs sends a customer.subscription.created webhook and Cashier creates the local subscription record automatically. The subscription "type" (default above) travels through the checkout session's metadata.

Checking subscription status

$user->subscribed();                          // has a valid "default" subscription
$user->subscribed('default', 'prod_abc123');  // ...to a specific product
$user->subscribedToProduct('prod_abc123');

$subscription = $user->subscription();

$subscription->valid();          // active, trialing, past_due, or on grace period
$subscription->active();
$subscription->onTrial();
$subscription->pastDue();
$subscription->paused();
$subscription->canceled();
$subscription->onGracePeriod();  // canceled but still within the paid period
$subscription->ended();

Trials

Recurring products configured with a trial in Bachs start subscriptions in the trialing state — no extra code needed. You can also manage trials directly:

$subscription->extendTrial(now()->addDays(30));
$subscription->endTrial(); // ends the trial and bills immediately

For trials without requiring a checkout ("generic" trials), set trial_ends_at on the billable model when creating it:

$user = User::create([
    // ...
    'trial_ends_at' => now()->addDays(14),
]);

$user->onTrial();        // true (generic or subscription trial)
$user->onGenericTrial(); // true (generic trial only)

Swapping plans

$subscription->swap('prod_premium');                 // prorated, invoiced now (Bachs default)
$subscription->swapWithoutProration('prod_premium'); // no proration
$subscription->swap('prod_premium', [
    'proration_behavior' => 'next_cycle',            // settle the difference next cycle
]);

Cancelling

$subscription->cancel();                  // at period end; enters a grace period
$subscription->cancelNow();               // immediately
$subscription->cancel('Too expensive');   // with a reason

While on the grace period, $subscription->valid() remains true until the paid period runs out.

Payment method

$subscription->updatePaymentMethod('pm_abc123');

One-time payments

Sell one-time products through a hosted checkout:

// Single product...
return $user->checkout('prod_ebook', [
    'success_url' => route('shop.success'),
]);

// Multiple products with quantities...
return $user->checkout([
    ['product_id' => 'prod_course', 'quantity' => 2],
    'prod_ebook',
]);

Refunds

$user->refund('chr_1a2b3c4d5e6f');
$user->refund('chr_1a2b3c4d5e6f', ['amount' => '10.00', 'reason' => 'Requested by customer']);

Webhooks

Cashier registers a webhook route at /bachs/webhook that keeps subscriptions in sync (customer.subscription.created, .updated, .deleted). Create the Bachs webhook endpoint with:

php artisan cashier:webhook

The command prints the signing secret — add it to your .env as BACHS_WEBHOOK_SECRET. Incoming webhooks are verified against the X-Bachs-Signature header (HMAC-SHA256) and stale deliveries are rejected.

Important

Make sure the webhook route is excluded from CSRF verification. In bootstrap/app.php:

->withMiddleware(function (Middleware $middleware) {
    $middleware->validateCsrfTokens(except: ['bachs/*']);
})

Handling other events

Listen for any Bachs event via the WebhookReceived event:

use Veekthoven\CashierBachs\Events\WebhookReceived;

Event::listen(function (WebhookReceived $event) {
    if ($event->payload['type'] === 'invoice.payment_failed') {
        // ...
    }
});

A WebhookHandled event fires after Cashier has processed one of the events it manages itself.

Low-level API access

Anything not covered by the fluent interface is available through the API client:

use Veekthoven\CashierBachs\Cashier;

$products = Cashier::api()->listProducts();
$payment = Cashier::api()->getPayment('pay_1a2b3c4d5e');
$response = Cashier::api()->get('/accounts/balances');

Failed requests throw Veekthoven\CashierBachs\Exceptions\BachsApiError, exposing the stable errorCode, validation errors, and the requestId for support.

Customization

use Veekthoven\CashierBachs\Cashier;

Cashier::ignoreRoutes();      // don't register the webhook route
Cashier::ignoreMigrations();  // don't load the package migrations
Cashier::useSubscriptionModel(CustomSubscription::class);
Cashier::formatCurrencyUsing(fn ($amount, $currency) => /* ... */);

Testing

composer test

Changelog

Please see CHANGELOG for more information on what has changed recently.

Credits

License

The MIT License (MIT). Please see License File for more information.

统计信息

  • 总下载量: 0
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 1
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-07-14

承接程序开发

PHP开发

VUE

Vue开发

前端开发

小程序开发

公众号开发

系统定制

数据库设计

云部署

网站建设

安全加固