wisdomanthoni / cashier-paystack
一个 Laravel Cashier 包装器,提供了对 Paystack 订阅发票服务的表达性、流畅的接口。
Requires
- php: ^7.1.3
- dompdf/dompdf: ^0.8.0
- illuminate/database: ~5.7.0|~5.8.0
- illuminate/support: ~5.7.0|~5.8.0
- nesbot/carbon: ^1.26.3 || ^2.0
- symfony/http-kernel: ~4.0
- unicodeveloper/laravel-paystack: ^1.0
Requires (Dev)
- illuminate/http: ~5.7.0|~5.8.0
- illuminate/routing: ~5.7.0|~5.8.0
- illuminate/view: ~5.7.0|~5.8.0
- mockery/mockery: ~1.0
- orchestra/testbench: ~3.0
- phpunit/phpunit: ^7.0
This package is auto-updated.
Last update: 2021-01-14 20:59:13 UTC
README
介绍
Cashier Paystack 提供了对 Paystack 订阅计费服务的表达性、流畅的接口。它处理了你讨厌编写的大部分样板订阅计费代码。
Composer
首先,将 Cashier 包添加到 Paystack 的依赖项中
composer require wisdomanthoni/cashier-paystack
配置
可以使用此命令发布配置文件
php artisan vendor:publish --provider="Unicodeveloper\Paystack\PaystackServiceProvider"
将一个名为 paystack.php 的配置文件(包含一些合理的默认值)放置在配置目录中
<?php return [ /** * Public Key From Paystack Dashboard * */ 'publicKey' => getenv('PAYSTACK_PUBLIC_KEY'), /** * Secret Key From Paystack Dashboard * */ 'secretKey' => getenv('PAYSTACK_SECRET_KEY'), /** * Paystack Payment URL * */ 'paymentUrl' => getenv('PAYSTACK_PAYMENT_URL'), /** * Optional email address of the merchant * */ 'merchantEmail' => getenv('MERCHANT_EMAIL'), /** * User model for customers * */ 'model' => getenv('PAYSTACK_MODEL'), ];
更新 .env 文件中的用户模型
PAYSTACK_MODEL='App\Model\User'
数据库迁移
在使用 Cashier 之前,我们还需要准备数据库。我们需要向用户表添加几个列,并创建一个新的订阅表来存储所有客户的订阅。
Schema::table('users', function ($table) { $table->string('paystack_id')->nullable(); $table->string('paystack_code')->nullable(); $table->string('card_brand')->nullable(); $table->string('card_last_four', 4)->nullable(); $table->timestamp('trial_ends_at')->nullable(); });
Schema::create('subscriptions', function ($table) { $table->increments('id'); $table->unsignedInteger('user_id'); $table->string('name'); $table->string('paystack_id')->nullable(); $table->string('paystack_code')->nullable(); $table->string('paystack_plan'); $table->integer('quantity'); $table->timestamp('trial_ends_at')->nullable(); $table->timestamp('ends_at')->nullable(); $table->timestamps(); });
迁移创建完成后,运行 migrate Artisan 命令。
可计费模型
接下来,将 Billable 特性添加到模型定义中。此特性提供各种方法,允许你执行常见的计费任务,例如创建订阅、应用优惠券和更新信用卡信息。
use Wisdomanthoni\Cashier\Billable; class User extends Authenticatable { use Billable; }
货币配置
Cashier 的默认货币是尼日利亚奈拉(NGN)。您可以通过在您的服务提供商的 boot 方法中调用 Cashier::useCurrency 方法来更改默认货币。useCurrency 方法接受两个字符串参数:货币和货币的符号。
use Wisdomanthoni\Cashier\Cashier; Cashier::useCurrency('ngn', '₦'); Cashier::useCurrency('ghs', 'GH₵');
订阅
创建订阅
要创建订阅,首先检索你的可计费模型实例,通常是 App\Model\User 的实例。检索模型实例后,你可以使用 newSubscription 方法来创建模型的订阅。
$user = User::find(1); $plan_name = // Paystack plan name e.g default, main, yakata $plan_code = // Paystack plan code e.g PLN_gx2wn530m0i3w3m $auth_token = // Paystack card auth token for customer // Accepts an card authorization authtoken for the customer $user->newSubscription($plan_name, $plan_code)->create($auth_token); // The customer's most recent authorization would be used to charge subscription $user->newSubscription($plan_name, $plan_code)->create(); // Initialize a new charge for a subscription $user->newSubscription($plan_name, $plan_code)->charge();
传递给 newSubscription 方法的第一个参数应该是订阅的名称。如果你的应用程序只提供单个订阅,你可能将其称为主要或主要。第二个参数是用户订阅的特定 Paystack Paystack 代码。此值应与 Paystack 中的代码标识符相对应。
接受 Paystack 授权令牌的 create 方法将开始订阅,并更新数据库中的客户/用户 ID 和其他相关计费信息。
charge 方法初始化一个事务,返回一个包含付款授权 URL 和访问代码的响应。
附加用户详细信息 如果你想要指定额外的客户详细信息,你可以通过将它们作为 create 方法的第二个参数传递来做到这一点
$user->newSubscription('main', 'PLN_cgumntiwkkda3cw')->create($auth_token, [ 'data' => 'More Customer Data', ],[ 'data' => 'More Subscription Data', ]);
要了解 Paystack 支持的附加字段,请查看 paystack 的客户创建文档或相应的 Paystack 文档。
检查订阅状态
用户订阅了您的应用程序后,您可以使用多种便捷的方法轻松检查其订阅状态。首先,如果用户有活跃的订阅,即使订阅目前处于试用期内,订阅方法也会返回 true
// Paystack plan name e.g default, main, yakata if ($user->subscribed('main')) { // }
订阅方法也是一个很好的路由中间件候选者,允许您根据用户的订阅状态过滤对路由和控制器访问
public function handle($request, Closure $next) { if ($request->user() && ! $request->user()->subscribed('main')) { // This user is not a paying customer... return redirect('billing'); } return $next($request); }
如果您想确定用户是否仍在试用期内,可以使用 onTrial 方法。此方法可用于向用户显示警告,告知他们仍在试用期内
if ($user->subscription('main')->onTrial()) { // }
可以使用 subscribedToPaystack 方法根据给定的 Paystack 代码确定用户是否订阅了特定的 Paystack。在这个例子中,我们将确定用户的主要订阅是否已活跃订阅了每月的 Paystack
$plan_name = // Paystack plan name e.g default, main, yakata $plan_code = // Paystack Paystack Code e.g PLN_gx2wn530m0i3w3m if ($user->subscribedToPlan($plan_code, $plan_code)) { // }
已取消订阅状态
要确定用户是否曾经是活跃的订阅者,但现在已取消订阅,可以使用 cancelled 方法
if ($user->subscription('main')->cancelled()) { // }
您还可以确定用户是否已取消订阅,但仍在“宽限期”内,直到订阅完全到期。例如,如果用户在3月5日取消了一个原定于3月10日到期的订阅,则用户将在3月10日之前处于“宽限期”。请注意,在此期间订阅方法仍然返回 true
if ($user->subscription('main')->onGracePeriod()) { // }
取消订阅
要取消订阅,请在用户的订阅上调用 cancel 方法
$user->subscription('main')->cancel();
当订阅被取消时,Cashier 会自动将您的数据库中的 ends_at 列设置为。此列用于确定订阅方法何时应开始返回 false。例如,如果客户在3月1日取消了一个原定于3月5日结束的订阅,则订阅方法将继续返回 true 直到3月5日。
您可以使用 onGracePeriod 方法确定用户是否已取消订阅但仍在“宽限期”内
if ($user->subscription('main')->onGracePeriod()) { // }
如果您希望立即取消订阅,请在该用户的订阅上调用 cancelNow 方法
$user->subscription('main')->cancelNow();
恢复订阅
如果用户已取消订阅,并且您希望恢复它,请使用 resume 方法。用户必须仍在他们的宽限期才能恢复订阅
$user->subscription('main')->resume();
如果用户取消订阅然后在订阅完全到期之前恢复该订阅,他们不会立即被收费。相反,他们的订阅将被重新激活,他们将在原始账单周期中被收费。
订阅试用
预先收费
如果您想在收集付款方式信息的同时向客户提供试用期间,您应该在创建订阅时使用 trialDays 方法
$user = User::find(1); $user->newSubscription('main', 'PLN_gx2wn530m0i3w3m') ->trialDays(10) ->create($auth_token);
此方法将在数据库中的订阅记录上设置试用期间结束日期,并指示 Paystack 在此日期之后不开始向客户收费。
如果客户在试用期结束前未取消订阅,他们将在试用期结束时立即被收费,因此请务必通知用户他们的试用期结束日期。
您可以使用用户实例的 onTrial 方法或订阅实例的 onTrial 方法来确定用户是否处于试用期。以下两个示例是相同的
if ($user->onTrial('main')) { // } if ($user->subscription('main')->onTrial()) { // }
无需预收费
如果您希望在不需要收集用户支付方式信息的情况下提供试用期,您可以设置用户记录中的 trial_ends_at 列为您希望试用期结束的日期。这通常在用户注册时完成
$user = User::create([ // Populate other user properties... 'trial_ends_at' => now()->addDays(10), ]);
请确保在您的模型定义中添加 trial_ends_at 的日期转换器。
Cashier 将这种试用期称为“通用试用期”,因为它不附属于任何现有的订阅。如果当前日期不是 trial_ends_at 的值,则用户实例上的 onTrial 方法将返回 true
if ($user->onTrial()) { // User is within their trial period... }
如果您希望确切知道用户是否处于“通用”试用期且尚未创建实际订阅,您也可以使用 onGenericTrial 方法
if ($user->onGenericTrial()) { // User is within their "generic" trial period... }
一旦您准备好为用户创建实际订阅,您可以像往常一样使用 newSubscription 方法
$user = User::find(1); $plan_code = // Paystack Paystack Code e.g PLN_gx2wn530m0i3w3m // With Paystack card auth token for customer $user->newSubscription('main', $plan_code)->create($auth_token); $user->newSubscription('main', $plan_code)->create();
客户
创建客户
偶尔,您可能希望创建一个 Paystack 客户而不开始订阅。您可以使用 createAsPaystackCustomer 方法完成此操作
$user->createAsPaystackCustomer();
一旦在 Paystack 中创建了客户,您可以在稍后日期开始订阅。
支付方式
检索认证的支付方式
账单模型实例上的 cards 方法返回一个 Wisdomanthoni\Cashier\Card
实例的集合
$cards = $user->cards();
删除支付方式
要删除一张卡,您应首先使用 card 方法检索客户的认证。然后,您可以在要删除的实例上调用 delete 方法
foreach ($user->cards() as $card) { $card->delete(); }
要删除客户的全部卡支付认证
$user->deleteCards();
处理 Paystack Webhooks
Paystack 可以通过 webhooks 通知您的应用程序各种事件。要处理 Paystack webhooks,定义一个指向 Cashier 的 webhook 控制器的路由。该控制器将处理所有传入的 webhook 请求并将它们分派到适当的控制器方法
Route::post( 'paystack/webhook', '\Wisdomanthoni\Cashier\Http\Controllers\WebhookController@handleWebhook' );
注册您的路由后,请确保在 Paystack 控制台设置中配置 webhook URL。
默认情况下,此控制器将自动处理因失败次数过多而取消订阅(如您的 paystack 设置定义的那样)、收费成功、转账成功或失败、发票更新和订阅更改;然而,我们将很快发现,您可以扩展此控制器以处理您喜欢的任何 webhook 事件。
请确保使用 Cashier 内置的 webhook 签名验证中间件保护传入请求。
Webhooks & CSRF 保护
由于 Paystack webhooks 需要绕过 Laravel 的 CSRF 保护,请确保在 VerifyCsrfToken 中间件中将 URI 列为异常或在 web 中间件组外列出路由
protected $except = [ 'paystack/*', ];
定义 Webhook 事件处理器
如果您想处理额外的 Paystack Webhook 事件,请扩展 Webhook 控制器。您的方法名称应与 Cashier 预期的约定一致,具体来说,方法应以前缀 handle 和您想要处理的 Paystack Webhook 事件的“驼峰式”名称开头。
<?php namespace App\Http\Controllers; use Wisdomanthoni\Cashier\Http\Controllers\WebhookController as CashierController; class WebhookController extends CashierController { /** * Handle invoice payment succeeded. * * @param array $payload * @return \Symfony\Component\HttpFoundation\Response */ public function handleInvoiceUpdate($payload) { // Handle The Event } }
接下来,在 routes/web.php 文件中定义指向您的 Cashier 控制器的路由。
Route::post( 'paystack/webhook', '\App\Http\Controllers\WebhookController@handleWebhook' );
单次收费
简单收费
当使用 Paystack 时,charge 方法接受您想要在应用程序使用的货币最低分母中收费的金额。
如果您想对已订阅客户的信用卡进行一次性的收费,您可以在可收费模型实例上使用 charge 方法。
// Paystack Accepts Charges In Kobo for Naira... $PaystackCharge = $user->charge(10000);
charge 方法接受一个数组作为其第二个参数,允许您将任何选项传递给底层的 Paystack 收费创建。有关创建收费时可用选项的详细信息,请参阅 Paystack 文档。
$user->charge(100, [ 'more_option' => $value, ]);
如果收费失败,charge 方法将抛出异常。如果收费成功,方法将从返回完整的 Paystack 响应。
try { // Paystack Accepts Charges In Kobo for Naira... $response = $user->charge(10000); } catch (Exception $e) { // }
带有发票的收费
有时您可能需要一次性收费并生成发票,以便您可以向客户提供 PDF 收据。invoiceFor 方法可以让您做到这一点。例如,让我们为客户开具一张 2000.00 的“一次性费用”发票。
// Paystack Accepts Charges In Kobo for Naira... $user->invoiceFor('One Time Fee', 200000);
发票将立即对用户的信用卡进行收费。invoiceFor 方法也接受一个数组作为其第三个参数。该数组包含发票项目的计费选项。该方法接受的第四个参数也是一个数组。该最终参数接受发票本身的计费选项。
$user->invoiceFor('Stickers', 50000, [ 'line_items' => [ ], 'tax' => [{"name":"VAT", "amount":2000}] ]);
要了解 Paystack 支持的附加字段,请查看 paystack 的客户创建文档或相应的 Paystack 文档。
退款收费如果您需要退款给 Paystack 收费,您可以使用 refund 方法。此方法接受 Paystack 收费 ID 作为其唯一参数。
$paystackCharge = $user->charge(100); $user->refund($paystackCharge->reference);
发票您可以使用 invoices 方法轻松检索可收费模型的一组发票。
$invoices = $user->invoices(); // Include only pending invoices in the results... $invoices = $user->invoicesOnlyPending(); // Include only paid invoices in the results... $invoices = $user->invoicesOnlyPaid();
在列出客户的发票时,您可以使用发票的辅助方法来显示相关发票信息。例如,您可能希望将每个发票列在表格中,使用户可以轻松下载其中任何一个。
<table> @foreach ($invoices as $invoice) <tr> <td>{{ $invoice->date()->toFormattedDateString() }}</td> <td>{{ $invoice->total() }}</td> <td><a href="/user/invoice/{{ $invoice->id }}">Download</a></td> </tr> @endforeach </table>
生成发票 PDF 从路由或控制器内部,使用 downloadInvoice 方法生成发票的 PDF 下载。此方法将自动生成适当的 HTTP 响应以将下载发送到浏览器。
use Illuminate\Http\Request; Route::get('user/invoice/{invoice}', function (Request $request, $invoiceId) { return $request->user()->downloadInvoice($invoiceId, [ 'vendor' => 'Your Company', 'product' => 'Your Product', ]); });