Laravel 4 的账单处理包。
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.
README
此包提供了一种表达性强、流畅的接口来访问账单服务网关。它处理了管理账单客户、订阅和个别收费的大部分样板代码。其用法和功能集受到了流行的 Laravel Cashier 包的很大影响。然而,它有以下主要区别
- 支持多个账单网关(通过 Facades)
- 支持每个客户多个订阅
- 支持每个客户多个信用卡
- 支持个别收费
- 支持免费订阅计划
它目前包含以下账单服务的驱动程序
注意:并非每个账单网关都支持所有功能。有关更多信息,请参阅下面的
网关限制
部分。
安装
Composer
将以下内容添加到您的 composer.json 文件中的 require 对象
"mmanos/laravel-billing": "dev-master"
之后,运行 composer install 安装包。
服务提供商
在您的 app
配置文件中注册 Mmanos\Billing\BillingServiceProvider
。
依赖项
以下 composer 依赖项对于所列账单网关是必需的
- Stripe:
stripe/stripe-php
- Braintree:
braintree/braintree_php
配置
配置文件
将默认配置文件发布到您的应用程序中,以便您可以进行修改。
$ php artisan config:publish mmanos/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 Mmanos\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 Mmanos\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
方法检索客户。
无需预先支付信用卡
如果您的应用程序提供免费试用,不预付信用卡,请将您的模型上的cardUpFront
属性设置为false
protected $cardUpFront = false;
在模型创建时,请确保在模型上设置试用结束日期
$website->billing_trial_ends_at = Carbon::now()->addDays(14); $website->save();
请注意,如果客户尚未准备好计费(即在计费系统中创建并至少拥有一张信用卡),则在试用订阅模式下执行的任何 订阅
命令都不会与计费网关同步。这允许开发者在对客户在计费网关中创建进行等待时,对模型执行相同的 创建
、交换
等命令。
如果当用户添加信用卡时,您有一个或多个待处理、试用订阅,您可以使用 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
标志轻松做到这一点。任何标记为免费的订阅将不会同步到计费网关。但是,您可以使用相同的 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', 'Mmanos\Billing\Gateways\Stripe\WebhookController@handleWebhook'); // Braintree. Route::post('braintree/webhook', 'Mmanos\Billing\Gateways\Braintree\WebhookController@handleWebhook');
默认情况下,此软件包不会尝试在多次失败的付款尝试后删除订阅。大多数计费网关可以自动执行此操作,这将触发已删除订阅的Webhook事件。当发生这种情况时,我们将更新本地模型以记录状态变化。
处理其他事件
如果您有其他希望处理Webhook事件,只需扩展Webhook控制器并将路由指向您的控制器。
class WebhookController extends Mmanos\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() });