controlla / conekta-cashier
Controlla Cashier 为 Laravel 提供了一个用于通过 Conketa 收费订阅的界面。
Requires
- php: ^7.3|^8.0
- conekta/conekta-php: dev-master
- illuminate/contracts: ^6.0|^7.0|^8.0
- illuminate/filesystem: ^6.0|^7.0|^8.0
- illuminate/routing: ^6.0|^7.0|^8.0
- illuminate/support: ^6.0|^7.0|^8.0
- illuminate/view: ^6.0|^7.0|^8.0
- moneyphp/money: 3.x-dev
Requires (Dev)
- mockery/mockery: ^1.2
- orchestra/testbench: ^6.0
- phpunit/phpunit: 9.5.x-dev
README
Conekta Cashier 是 Laravel Cashier 的 Conketa 版本 Laravel Cashier,用于 Conketa。
请注意,Laravel Cashier 的最新版本支持 Laravel 5+,如果您正在寻找 Laravel 4 的实现,请参阅 1.0 分支。
Conekta Cashier
简介
Conekta Cashier 提供了一个表达性、流畅的接口来访问 Conekta 的订阅计费服务。它处理了几乎所有您害怕编写的样板化订阅计费代码。除了基本的订阅管理外,Cashier 还可以处理优惠券、交换订阅、订阅 "数量"、取消宽限期,甚至生成发票 PDF。
安装
首先,使用 Composer 包管理器安装 Conekta 的 Cashier 包
composer require controlla/conekta-cashier
数据库迁移
Conekta 的服务提供者注册了自己的数据库迁移目录,因此在安装包后请务必迁移您的数据库。Cashier 迁移将在您的 users
表中添加几个列,并创建一个新的订阅表以存储所有客户的 subscriptions
php artisan migrate
如果您需要覆盖 Cashier 附加的迁移,您可以使用 vendor:publish
Artisan 命令来发布它们
php artisan vendor:publish --tag="cashier-migrations"
如果您想完全阻止 Cashier 的迁移运行,您可以使用 Cashier 提供的 ignoreMigrations
方法。通常,此方法应在 AppServiceProvider
的 register
方法中调用
use Controlla\ConektaCashier\Cashier;
/**
* Register any application services.
*
* @return void
*/
public function register()
{
Cashier::ignoreMigrations();
}
配置
可计费模型
在使用 Cashier 之前,请将 Billable
特性添加到您的可计费模型定义中。通常,这将是一个 App\Models\User
模型。此特性提供了各种方法,使您能够执行常见的计费任务,例如创建订阅、应用优惠券以及更新支付方式信息
use Controlla\ConektaCashier\Billable;
class User extends Authenticatable
{
use Billable;
}
Cashier 假设您的计费模型将是 Laravel 附加的 App\Models\User
类。如果您希望更改此设置,您可以通过 useCustomerModel
方法指定不同的模型。此方法通常应在 AppServiceProvider
类的 boot
方法中调用
use App\Models\Cashier\User;
use Controlla\ConektaCashier\Cashier;
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
Cashier::useCustomerModel(User::class);
}
❗如果您使用的是 Laravel 提供的
App\Models\User
模型以外的其他模型,您需要发布并修改提供的 Cashier 迁移 以匹配您的替代模型的表
API 密钥
接下来,您应该在应用程序的 .env
文件中配置您的 Conekta API 密钥。您可以从 Conekta 控制面板中检索您的 Conekta API 密钥
CONEKTA_KEY=your-conekta-key
CONEKTA_SECRET=your-conekta-secret
货币配置
Cashier 的默认货币是墨西哥比索 (MXN)。您可以通过在应用程序的 .env
文件中设置 CASHIER_CURRENCY
环境变量来更改默认货币
CASHIER_CURRENCY=eur
除了配置 Cashier 的货币外,您还可以指定用于在发票上显示货币值时使用的区域设置。内部,Cashier 使用 PHP 的 NumberFormatter 类
设置货币区域设置
CASHIER_CURRENCY_LOCALE=nl_BE
客户
检索客户
您可以使用Cashier类的findBillable方法通过Conekta ID检索客户。此方法将返回可计费模型的实例。
use Controlla\ConektaCashier\Cashier;
$user = Cashier::findBillable($conektaId);
创建客户
有时,您可能希望在开始订阅之前创建一个Conekta客户。您可以使用createAsConektaCustomer方法完成此操作。
$conektaCustomer = $user->createAsConektaCustomer();
订阅计划
一旦您拥有一个模型实例,就可以轻松地将该用户订阅到给定的Conekta计划。
$user = User::find(1);
$user->subscription('monthly')->create($creditCardToken);
您还可以扩展订阅试用期。
$subscription = $user->subscription('monthly')->create($creditCardToken);
$user->extendTrial(Carbon::now()->addMonth());
subscription
方法将自动创建Conekta订阅,并将Conekta客户ID和其他相关计费信息更新到您的数据库中。如果您的计划在Conekta中配置了试用期,用户的试用期结束日期也将自动设置。
如果您的计划在Conekta中未配置试用期,您必须在订阅后手动设置试用期结束日期。
$user->trial_ends_at = Carbon::now()->addDays(14);
$user->save();
指定额外用户详情
如果您想指定额外的客户详情,您可以将它们作为第二个参数传递给create
方法。
$user->subscription('monthly')->create($creditCardToken, [
'email' => $email, 'name' => 'Joe Doe'
]);
要了解更多关于Conekta支持的增加字段,请参阅Conekta关于客户创建的文档。
单次收费
如果您想对已订阅客户的信用卡进行一次性收费,可以使用charge
方法。
$user->charge(100);
charge
方法接受您要收取的金额,金额以货币的最小分母为单位。例如,上面的例子将收取100美分,即1.00美元。
charge
方法接受一个数组作为其第二个参数,允许您将任何选项传递给底层的Conekta收费创建。
$user->charge(100, [
'card' => $token,
]);
如果收费失败,charge
方法将返回false
。这通常表示收费被拒绝。
if ( ! $user->charge(100))
{
// The charge was denied...
}
如果收费成功,方法将从返回完整的Conekta响应。
交换订阅
要切换用户到新的订阅,请使用swap
方法。
$user->subscription('premium')->swap();
如果用户处于试用期,试用期将按正常方式保持。此外,如果订阅存在“数量”,该数量也将保持不变。
取消订阅
取消订阅是一件轻而易举的事。
$user->subscription()->cancel();
当订阅被取消时,Cashier会自动设置数据库中的subscription_ends_at
列。该列用于确定何时subscribed
方法应开始返回false
。例如,如果客户在3月1日取消订阅,但订阅原计划在3月5日结束,则subscribed
方法将继续在3月5日之前返回true
。
恢复订阅
如果用户已取消订阅,但您希望恢复订阅,请使用resume
方法。
$user->subscription('monthly')->resume($creditCardToken);
如果用户在订阅完全过期之前取消并恢复该订阅,他们不会立即收费。他们的订阅将被重新激活,并且将在原始计费周期中收费。
检查订阅状态
要验证用户是否已订阅您的应用程序,请使用subscribed
命令。
if ($user->subscribed())
{
//
}
subscribed
方法非常适合用作路由中间件。
public function handle($request, Closure $next)
{
if ($request->user() && ! $request->user()->subscribed())
{
return redirect('billing');
}
return $next($request);
}
您还可以使用onTrial
方法确定用户是否处于试用期(如果适用)。
if ($user->onTrial())
{
//
}
要确定用户是否曾是活跃订阅者但已取消订阅,请使用cancelled
方法。
if ($user->cancelled())
{
//
}
您还可以确定用户是否已取消订阅,但仍处于“宽限期”内,直到订阅完全到期。例如,如果用户在3月5日取消了一个计划于3月10日结束的订阅,那么用户将处于“宽限期”直至3月10日。请注意,在此期间,subscribed
方法仍然返回true
。
if ($user->onGracePeriod())
{
//
}
可以使用everSubscribed
方法来确定用户是否曾经订阅过您的应用程序中的任何计划。
if ($user->everSubscribed())
{
//
}
可以使用onPlan
方法来确定用户是否根据其ID订阅了某个计划。
if ($user->onPlan('monthly'))
{
//
}
处理 Conkta Webhooks
Conekta可以通过webhook通知您的应用程序各种事件。默认情况下,Cashier服务提供者会自动注册指向Cashier webhook控制器的路由。此控制器将处理所有传入的webhook请求。
默认情况下,Conekta Cashier webhook控制器将自动处理因失败次数过多而取消的订阅(根据您的Conekta设置定义)、客户更新、客户删除、订阅更新和支付方式更改;然而,正如我们很快将发现的,您可以扩展此控制器以处理您喜欢的任何Conekta webhook事件。
为确保您的应用程序可以处理Conekta webhook,请确保在Conekta控制面板中配置webhook URL。默认情况下,Cashier的webhook控制器响应于/conekta/webhook
URL路径。
为了方便,Cashier包含一个cashier:webhook
Artisan命令。此命令将在Conekta中创建一个webhook,以监听Cashier所需的所有事件。
php artisan cashier:webhook
默认情况下,创建的webhook将指向由APP_URL
环境变量和Cashier中包含的cashier.webhook
路由定义的URL。如果您希望在调用命令时使用不同的URL,可以提供--url
选项。
php artisan cashier:webhook --url "https://example.com/conekta/webhook"
定义Webhook事件处理器
Cashier自动处理因失败费用和其他常见Conekta webhook事件导致的订阅取消。但是,如果您想要处理其他webhook事件,您可以通过监听Cashier发出的以下事件来实现
Controlla\ConektaCashier\Events\WebhookReceived
Controlla\ConektaCashier\Events\WebhookHandled
这两个事件都包含Conekta webhook的完整有效载荷。例如,如果您希望处理charge.paid
webhook,您可以注册一个监听器来处理此事件。
<?php
namespace App\Listeners;
use Controlla\ConektaCashier\Events\WebhookReceived;
class ConektaEventListener
{
/**
* Handle received Conekta webhooks.
*
* @param \Controlla\ConektaCashier\Events\WebhookReceived $event
* @return void
*/
public function handle(WebhookReceived $event)
{
if ($event->payload['type'] === 'charge.paid') {
// Handle the incoming event...
}
}
}
一旦定义了监听器,您可以在应用程序的EventServiceProvider
中注册它。
<?php
namespace App\Providers;
use App\Listeners\ConektaEventListener;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
use Controlla\ConektaCashier\Events\WebhookReceived;
class EventServiceProvider extends ServiceProvider
{
protected $listen = [
WebhookReceived::class => [
ConektaEventListener::class,
],
];
}
测试
在测试使用Cashier的应用程序时,您可能需要模拟对Conekta API的实际HTTP请求;然而,这需要您部分重新实现Cashier的行为。因此,我们建议允许您的测试击中实际Conekta API。虽然这较慢,但它提供了更多信心,表明您的应用程序按预期工作,任何慢速测试都可以放在它们自己的PHPUnit测试组中。
在测试时,请记住Cashier本身已经有一个很好的测试套件,因此您应该只关注测试您应用程序的订阅和支付流程,而不仅仅是Cashier的每个底层行为。
要开始,将您的Conekta密钥的测试版本添加到您的phpunit.xml
文件中。
<env name="CONEKTA_SECRET" value="<your-key>"/>
现在,每当您在测试时与Cashier交互,它将向您的Conekta测试环境发送实际的API请求。