wisdomanthoni/cashier-paystack

此包已被弃用且不再维护。作者建议使用 webong/cashier-paystack 包。

一个 Laravel Cashier 包装器,提供了对 Paystack 订阅发票服务的表达性、流畅的接口。

v1.0.0 2020-03-23 23:05 UTC

This package is auto-updated.

Last update: 2021-01-14 20:59:13 UTC


README

Build Status Latest Stable Version Total Downloads License

介绍

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',
    ]);
});