linkthrow/laravel-billing

为 Laravel 5 提供的账单包。

v1.1.7 2015-07-05 15:58 UTC

This package is not auto-updated.

Last update: 2024-09-18 18:17:23 UTC


README

本包提供了一个表达式丰富的接口,用于账单服务网关。它处理了管理账单客户、订阅和单个费用的大部分样板代码。用法和功能集受到了流行的 Laravel Cashier 包的极大影响。然而,它有以下主要区别

  • 支持多个账单网关(通过 Facades
  • 支持每个客户多个订阅
  • 支持每个客户多个信用卡
  • 支持单个费用
  • 支持免费订阅计划

目前包含以下账单服务的驱动程序

注意:并非所有功能都由每个账单网关支持。有关更多信息,请参阅下面的 网关限制 部分。

安装

Composer

将以下内容添加到您的 composer.json 文件中的 require 对象

"linkthrow/laravel-billing": "dev-master"

之后,运行 composer install 来安装此包。

服务提供者

在您的 app 配置文件中注册 LinkThrow\Billing\BillingServiceProvider

依赖

以下 composer 依赖项对于所列账单网关是必需的

  • Stripe: stripe/stripe-php
  • Braintree: braintree/braintree_php

配置

配置文件

将默认配置文件发布到您的应用程序,以便您可以进行修改。

$ php artisan config:publish linkthrow/laravel-billing

客户迁移

在开始使用此包之前,我们需要向表示您的账单客户的表中添加几个列。您可以使用 laravel-billing:customer-table Artisan 命令来创建迁移以添加必要的列。

例如,要将列添加到 users 表中,请使用

$ php artisan laravel-billing:customer-table users

订阅迁移

接下来,我们需要向表示您的账单订阅计划的表中添加几个列。您可以使用 laravel-billing:subscription-table Artisan 命令来创建迁移以添加必要的列。

例如,如果您是一家托管公司,并希望允许客户添加一个网站订阅,您可能使用

$ php artisan laravel-billing:subscription-table websites

注意:如果您只为每个客户需要一个订阅,您可以使用相同的表进行客户迁移和订阅迁移(例如 users 表)。

注意:您还可以通过向多个表添加这些字段来支持每个客户多个订阅。

运行迁移

迁移创建后,只需运行 migrate 命令。

客户模型设置

接下来,将 CustomerBillableTrait 添加到您的客户模型定义中

use LinkThrow\Billing\CustomerBillableTrait;

class User extends Eloquent
{
    use CustomerBillableTrait;

}

您还应该定义一个 subscriptionmodels 方法,该方法返回表示客户任何账单订阅的所有模型。这使得客户模型可以传播信用卡更改和状态更改(如果客户被删除)到所有其订阅模型。

public function subscriptionmodels()
{
    // Return an Eloquent relationship.
    return $this->hasMany('Website');
    
    // Or, return an array or collection of models.
    return Website::where('user_id', $this->id)->get();
    
    // Or, return an array of collections.
    return array(
        Website::where('user_id', $this->id)->get(),
        Domain::where('user_id', $this->id)->get(),
    );
}

订阅模型设置

然后,将 SubscriptionBillableTrait 添加到您的订阅模型定义中

use LinkThrow\Billing\SubscriptionBillableTrait;

class Website extends Eloquent
{
    use SubscriptionBillableTrait;

}

您还应该定义一个返回表示拥有此订阅的客户的模型的customermodel方法。这确保了订阅在与其他网关API交互时可以访问必要的客户信息。

public function customermodel()
{
    // Return an Eloquent relationship.
    return $this->belongsTo('User', 'user_id');
    
    // Or, Return an Eloquent model.
    return User::find($this->user_id);
}

模型定义

最后,为了能够根据网关ID(客户ID或订阅ID)查找模型,您需要定义作为客户或订阅的Eloquent模型的数组。

将您的客户类添加到本包的配置文件中的customer_models属性

'customer_models' => array('User')

并将订阅类添加到本包配置文件中的subscription_models属性

'subscription_models' => array('Website')

这些值主要用于WebhookControllers,帮助在接收到新的计费事件时根据网关的ID找到相应的Eloquent模型。

客户

计费客户可以与订阅或费用分开创建和管理。

创建客户

一旦您有了客户模型实例,您可以使用网关特定的信用卡令牌在计费网关中创建客户。

$user = User::find(1);

$user->billing()->withCardToken('token')->create();

如果您希望在创建客户时应用优惠券,可以使用withCoupon方法。

$user->billing()->withCardToken('token')->withCoupon('code')->create();

billing方法将自动将计费网关的客户ID和其他相关计费信息更新到您的数据库。

如果您想指定额外的客户详细信息,可以通过将它们传递到create方法来实现。

$user->billing()->withCardToken('token')->create(array(
    'email' => $email,
));

更新客户

要更新现有客户的默认信用卡信息或向其现有账户添加优惠券,可以使用update方法。

$user->billing()->withCardToken('token')->withCoupon('code')->update();

删除客户

删除客户将删除其在计费网关的账户并删除所有现有订阅。

$user->billing()->delete();

多张信用卡

您可以使用creditcards方法为一个客户添加多张信用卡。

$card = $user->creditcards()->create('credit_card_token');

更新现有信用卡的信息(如到期日期或账单地址)也是可能的。

$card->update(array(
    'exp_month' => '01',
    'exp_year'  => '2017',
));

或删除现有的信用卡

$card->delete();

检索和操作客户信用卡相对简单

// Get all customer credit cards.
$cards = $user->creditcards()->get();

// Get the first card for a customer.
$card = $user->creditcards()->first();

// Find a card by it's ID.
$card = $user->creditcards()->find('card_id');

echo $card->id;
echo "{$card->brand} xxxx-xxxx-xxxx-{$card->last4} Exp: {$card->exp_month}/{$card->exp_year}"

发票

您可以使用invoices方法轻松检索客户的发票数组。

$invoices = $user->invoices()->get();

或获取最新发票

$invoice = $user->invoices()->first();

或通过其ID查找发票

$invoice = $user->invoices()->find('invoice_id');

要显示相关的发票信息,请使用发票属性

$invoice->id;
$invoice->date;
$invoice->amount;
$invoice->items();
//...

使用render方法生成发票的预格式化HTML版本

$invoice->render();

检查客户状态

要验证客户是否已在计费网关中创建,请使用readyForBilling方法。

if ($user->readyForBilling()) {
    //
}

订阅

订阅计划

一旦您有了订阅模型实例,您可以将客户订阅到某个计划。

$user = User::find(1);
$website = Website::find(1);

$user->subscriptions('monthly')->create($website);

您可以使用withCoupon方法专门为订阅应用优惠券。

$user->subscriptions('monthly')->withCoupon('code')->create($website);

您还可以指定用于此订阅的新信用卡令牌。

$user->subscriptions('monthly')->withCardToken('token')->create($website);

或指定现有的信用卡ID

$user->subscriptions('monthly')->withCard('card_id')->create($website);

注意:如果没有指定卡片或卡片令牌,将使用客户关联的默认(初始)卡片。

有时您可能还没有创建客户或当您想要创建订阅时,没有可用客户模型。在这些情况下,您可以直接在订阅模型上使用subscription方法订阅计划。

$website->subscription('monthly')->create();

此方法还支持可选的withCouponwithCardToken方法。

$website->subscription('monthly')->withCoupon('code')->withCardToken('token')->create();

注意:将自动使用您在上述订阅模型中定义的customermodel方法检索客户。

无需 upfront 卡片

如果您的应用程序提供免费试用,且无需 upfront 信用卡,请将模型中的cardUpFront属性设置为false

protected $cardUpFront = false;

在创建模型时,请确保在模型上设置试用结束日期

$website->billing_trial_ends_at = Carbon::now()->addDays(14);

$website->save();

请注意,如果在试用订阅模型上执行任何 subscription 命令,并且客户尚未准备好计费(在计费系统中至少创建了一张信用卡),则这些命令将不会与计费网关同步。这允许开发者在等待客户在计费网关中创建时,对模型执行相同的 createswap 等命令。

如果您在用户添加信用卡时有待处理、正在试用、的订阅,您可以使用 withSubscriptions 标志激活计费网关中的所有待处理、非免费订阅

$user->billing()->withCardToken('token')->withSubscriptions()->create();

交换订阅

要将用户切换到新的订阅,请使用 swap 方法

$website->subscription('premium')->swap();

如果用户处于试用状态,试用将按正常方式维持。此外,如果订阅存在“数量”,该数量也将保持不变。

订阅数量

有时订阅会受到“数量”的影响。例如,您的应用程序可能按每月每位用户10美元的价格收费。要轻松增加或减少您的订阅数量,请使用 incrementdecrement 方法

$website->subscription()->increment();

// Add five to the subscription's current quantity...
$website->subscription()->increment(5);

$website->subscription()->decrement();

// Subtract five from the subscription's current quantity...
$website->subscription()->decrement(5);

取消订阅

要取消订阅,请使用 cancel 方法

$website->subscription()->cancel();

当订阅被取消时,此包将自动将数据库中的 billing_subscription_ends_at 列设置为。该列用于确定 subscribed 方法何时应开始返回 false。例如,如果客户在3月1日取消了订阅,但订阅原计划直到3月5日才结束,则 subscribed 方法将继续返回 true 直到3月5日。

恢复订阅

如果用户取消了他们的订阅并且您希望恢复它,请使用 resume 方法

$website->subscription('monthly')->resume();

您还可以指定新的信用卡令牌

$website->subscription('monthly')->withCardToken('token')->resume();

如果用户取消了订阅然后在该订阅完全过期之前恢复,他们可能不会立即收费,具体取决于所使用的计费网关。

免费订阅计划

有时您想要提供免费订阅计划。您可以使用 isFree 标志轻松做到这一点。任何标记为免费的订阅将不会同步到计费网关。但是,您可以使用与 isFree 标志相同的 subscription 命令,以便于使用,只要您使用 isFree 标志。

创建免费订阅

$user->subscriptions('free-plan')->isFree()->create($website);

您还可以从付费计划切换到免费计划。这将取消计费网关中的订阅并更新本地记录以反映免费状态

$website->subscription('free-plan')->isFree()->swap();

注意:如果您不使用 isFree 标志,则假定该计划不是免费的,并且将在计费网关中创建订阅。

注意:此包将尝试保持计费网关中的订阅 ID 并在从付费切换到免费然后再切换回付费时恢复现有订阅。

处理订阅

要验证模型是否已订阅您的应用程序,请使用 subscribed 命令

if ($website->subscribed()) {
    //
}

要确定模型是否在计费网关中有一个活跃的订阅,请使用 billingIsActive 方法。如果模型当前处于试用状态且 cardUpFront 设置为 false 或他们已取消且处于宽限期,则此方法不会返回 true。

if ($website->billingIsActive()) {
    //
}

您还可以使用 onTrial 方法确定模型是否仍在试用期间(如果适用)

if ($website->onTrial()) {
    //
}

要确定模型曾经是活跃的订阅者但已取消订阅,请使用 canceled 方法

if ($website->canceled()) {
    //
}

您还可以确定模型是否已取消订阅但仍在其“宽限期”内,直到订阅完全过期。例如,如果模型订阅在3月5日被取消,原计划在3月10日结束,则模型将在其“宽限期”内直到3月10日。请注意,在此期间 subscribed 方法仍然返回 true

if ($website->onGracePeriod()) {
    //
}

可以使用 everSubscribed 方法来判定模型是否曾订阅过您应用程序中的任何套餐

if ($website->everSubscribed()) {
    //
}

要获取与订阅关联的客户模型,请使用 customer 方法

$customer = $website->customer();

您还可以检索与客户关联的订阅数组

$subscriptions = $user->subscriptions()->get();

费用

在订阅之外,对客户创建单个费用也是可能的。

创建费用

在客户上创建新的费用非常简单

$charge = $user->charges()->create(499);

注意:费用的金额以分计算。

要使用新的信用卡令牌进行收费,请使用 withCardToken 方法

$charge = $user->charges()->withCardToken('token')->create(499);

您还可以指定现有的信用卡用于收费

$charge = $user->charges()->withCard('card_id')->create(499);

注意:如果没有指定卡片或卡片令牌,将使用客户关联的默认(初始)卡片。

捕获费用

有时您可能希望在捕获费用之前预先授权费用

$charge = $user->charges()->create(499, array('capture' => false));

$charge->capture();

只要预授权的金额大于等于要捕获的金额,您可以选择指定要捕获的金额

$charge = $user->charges()->create(499, array('capture' => false));

$charge->capture(array('amount' => 399));

退款费用

退款费用也是可能的

$charge->refund();

或者也可以指定退款金额

$charge->refund(array('amount' => 399, 'reason' => '...'));

处理费用

您可以检索客户的所有费用数组

$charges = $user->charges()->get();

或者找到客户的最新费用

$charge = $user->charges()->first();

通过其ID查找费用也很简单

$charge = $user->charges()->find('charge_id');

费用对象具有几个您可能发现有用的属性,包括:idcreated_atamountpaidrefundedcardinvoice_iddescription

您还可以从费用访问相关的发票对象(如果有的话)

$invoice = $charge->invoice();

Webhooks

此软件包为每个支持的网关捆绑了一个Webhook控制器,它可以处理诸如失败的或成功的发票支付、已删除的订阅和试用期即将结束的事件。

处理Webhook事件

要启用这些事件,只需将路由指向适当的网关控制器

// Stripe.
Route::post('stripe/webhook', 'LinkThrow\Billing\Gateways\Stripe\WebhookController@handleWebhook');

// Braintree.
Route::post('braintree/webhook', 'LinkThrow\Billing\Gateways\Braintree\WebhookController@handleWebhook');

默认情况下,此软件包不会在尝试支付失败一定次数后尝试删除订阅。大多数计费网关可以自动执行此操作,这将触发已删除订阅的Webhook事件。当发生这种情况时,我们将更新我们的本地模型以记录状态更改。

处理其他事件

如果您想处理额外的Webhook事件,只需扩展Webhook控制器并将路由指向您的控制器。

class WebhookController extends LinkThrow\Billing\Gateways\Stripe\WebhookController
{
    public function handleChargeDisputeCreated($payload)
    {
        // Handle The Event
    }
}

模型事件

为了使与计费相关的事件处理更加容易,此软件包将在Eloquent模型上触发几个方便的事件,您可以将其连接起来,这样您就不必在Webhook控制器中做所有的事情。

例如,要通知模型试用期即将结束,请订阅 trialWillEnd 模型方法

Website::trialWillEnd(function ($website, $args = array()) {
    Log::info('Trial will end in ' . array_get($args, 'days') . ' day(s).');
});

客户事件

您可能订阅以下与客户相关的计费事件:customerCreatedcustomerDeletedcreditcardAddedcreditcardRemovedcreditcardUpdatedcreditcardChangeddiscountAddeddiscountRemoveddiscountUpdateddiscountChangedinvoiceCreatedinvoicePaymentSucceededinvoicePaymentFailed

订阅事件

您可能订阅以下与订阅相关的计费事件:billingActivatedbillingCanceledplanSwappedplanChangedsubscriptionIncrementedsubscriptionDecrementedbillingResumedtrialExtendedtrialWillEndsubscriptionDiscountAddedsubscriptionDiscountRemovedsubscriptionDiscountUpdatedsubscriptionDiscountChanged

网关限制

每个计费网关提供不同的API和功能集。也就是说,并非所有功能都由每个计费网关支持。以下是每个网关不支持的功能的概述。

Stripe

  • 不支持在单独的信用卡上使用多个订阅(尽管这个功能即将推出)。在创建新订阅时将忽略withCard方法,并使用主客户卡。然而,在收费时支持多个信用卡。

Braintree

  • 不支持任何关于信用卡令牌的操作。因此,不支持使用带有令牌创建/更新用户。同样,不支持添加新的信用卡。您必须使用他们的透明重定向流程来完成此操作。完成后,您还需要手动更新模型字段。
  • 不支持订阅数量。
  • 不支持特定客户的折扣。
  • 不支持收费描述。
  • 发票中不返回起始/结束客户余额。
  • 不支持恢复已取消的订阅。然而,将创建一个新的订阅。
  • 不支持修改现有订阅的试用结束日期。然而,现有的订阅将被取消并创建一个新的。

本地驱动器

此软件包附带本地网关驱动程序,用于模拟与真实计费网关的通信。所有数据都存储在位于app/storage/meta/billing-local.sqlite的SQLite数据库中。

添加计划

要将订阅计划添加到本地驱动器数据库,请使用laravel-billing:local:create-plan Artisan命令

$ php artisan laravel-billing:local:create-plan

它将提示您输入计划名称、金额、间隔和试用期限。

添加优惠券

要将优惠券添加到本地驱动器数据库,请使用laravel-billing:local:create-coupon Artisan命令

$ php artisan laravel-billing:local:create-coupon

它将提示您输入优惠券代码、百分比折扣、折扣金额和持续时间。

生成信用卡令牌

尽管这不是真实的计费网关,但它旨在模拟真实网关的响应。因此,您仍然需要生成一个信用卡令牌来附加到客户或订阅。

要为此驱动程序生成有效的令牌,只需将包含您想要存储的信用卡字段的JSON对象进行编码。例如

var token = JSON.stringify({
    last4     : $('#card-number').val().substr(-4),
    exp_month : $('#exp-month').val(),
    exp_year  : $('#exp-year').val()
});