linkthrow / laravel-billing
为 Laravel 5 提供的账单包。
Requires
- php: >=5.4.0
- illuminate/support: ~4.1
- nesbot/carbon: ~1.0
Requires (Dev)
- braintree/braintree_php: 2.33.0
- stripe/stripe-php: ~1.9
Suggests
- braintree/braintree_php: Add the Braintree gateway SDK.
- stripe/stripe-php: Add the Stripe gateway SDK.
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();
此方法还支持可选的withCoupon和withCardToken方法。
$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 命令,并且客户尚未准备好计费(在计费系统中至少创建了一张信用卡),则这些命令将不会与计费网关同步。这允许开发者在等待客户在计费网关中创建时,对模型执行相同的 create、swap 等命令。
如果您在用户添加信用卡时有待处理、正在试用、的订阅,您可以使用 withSubscriptions 标志激活计费网关中的所有待处理、非免费订阅
$user->billing()->withCardToken('token')->withSubscriptions()->create();
交换订阅
要将用户切换到新的订阅,请使用 swap 方法
$website->subscription('premium')->swap();
如果用户处于试用状态,试用将按正常方式维持。此外,如果订阅存在“数量”,该数量也将保持不变。
订阅数量
有时订阅会受到“数量”的影响。例如,您的应用程序可能按每月每位用户10美元的价格收费。要轻松增加或减少您的订阅数量,请使用 increment 和 decrement 方法
$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');
费用对象具有几个您可能发现有用的属性,包括:id、created_at、amount、paid、refunded、card、invoice_id 和 description。
您还可以从费用访问相关的发票对象(如果有的话)
$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).'); });
客户事件
您可能订阅以下与客户相关的计费事件:customerCreated、customerDeleted、creditcardAdded、creditcardRemoved、creditcardUpdated、creditcardChanged、discountAdded、discountRemoved、discountUpdated、discountChanged、invoiceCreated、invoicePaymentSucceeded 和 invoicePaymentFailed。
订阅事件
您可能订阅以下与订阅相关的计费事件:billingActivated、billingCanceled、planSwapped、planChanged、subscriptionIncremented、subscriptionDecremented、billingResumed、trialExtended、trialWillEnd、subscriptionDiscountAdded、subscriptionDiscountRemoved、subscriptionDiscountUpdated 和 subscriptionDiscountChanged。
网关限制
每个计费网关提供不同的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() });