Laravel 4 的账单处理包。

v1.1.9 2017-11-11 20:55 UTC

This package is not auto-updated.

Last update: 2024-09-28 16:31:05 UTC


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();

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

$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美元。为了轻松增加或减少您的订阅数量,请使用 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 标志轻松做到这一点。任何标记为免费的订阅将不会同步到计费网关。但是,您可以使用相同的 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', '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).');
});

客户事件

您可以订阅几个与客户相关的计费事件: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()
});