initafricahq / cashier-paystack
一个 Laravel Cashier 包装器,提供了对 Paystack 订阅发票服务的表达性和流畅的接口。
Requires
- php: ^8.1
- dompdf/dompdf: ^2.0
- guzzlehttp/guzzle: ^7.8
- illuminate/database: ^10.0
- illuminate/http: ^10.30
- illuminate/routing: ^10.30
- illuminate/support: ^10.0
- illuminate/view: ^10.30
- nesbot/carbon: ^2.0
- symfony/http-kernel: ^6.0
Requires (Dev)
- laravel/pint: ^1.13
- mockery/mockery: ^1.6
- orchestra/testbench: ^8.14
- pestphp/pest: ^2.24
This package is not auto-updated.
Last update: 2024-09-30 22:10:16 UTC
README
介绍
Cashier Paystack 提供了对 Paystack 订阅计费服务的表达性和流畅的接口。它处理了几乎所有您所畏惧编写的样板订阅计费代码。
Composer
首先,将 Paystack 的 Cashier 包添加到您的依赖项中
composer require initafricahq/cashier-paystack
配置
您可以使用此命令发布配置文件
php artisan vendor:publish --provider="InitAfricaHQ\Cashier\CashierServiceProvider"
一个名为 cashier-paystack.php 的配置文件,其中包含一些合理的默认值,将被放置在您的配置目录中
<?php return [ /** * Public Key From Paystack Dashboard * */ 'public_key' => env('PAYSTACK_PUBLIC_KEY'), /** * Secret Key From Paystack Dashboard * */ 'secret_key' => env('PAYSTACK_SECRET_KEY'), /** * Paystack Payment URL * */ 'path' => env('PAYSTACK_PATH'), /** * Optional email address of the merchant * */ 'merchant_email' => env('MERCHANT_EMAIL'), /** * User model for customers * */ 'model' => env('PAYSTACK_MODEL'), ];
数据库迁移
在使用 Cashier 之前,我们还需要准备数据库。您可以简单地发布迁移文件并运行迁移命令
可计费模型
接下来,将 Billable 特性添加到您的模型定义中。这个特性提供了各种方法,允许您执行常见的计费任务,如创建订阅、应用优惠券和更新信用卡信息
use InitAfricaHQ\Cashier\Billable; class User extends Authenticatable { use Billable; }
货币配置
Cashier 默认货币是尼日利亚奈拉(NGN)。您可以通过在某个服务提供商的 boot 方法中调用 Cashier::useCurrency 方法来更改默认货币。useCurrency 方法接受两个字符串参数:货币和货币符号
use InitAfricaHQ\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 方法的第一个参数应该是订阅的名称。如果您应用程序仅提供单个订阅,您可以将其称为 main 或 primary。第二个参数是用户订阅的具体 Paystack 代码。此值应与 Paystack 中的代码标识符相对应。
接受 Paystack 授权令牌的 create 方法将开始订阅并更新您的数据库,包括客户/用户 ID 和其他相关计费信息。
charge 方法初始化一个事务,返回一个包含付款授权 URL 和访问代码的响应。
如果需要指定额外的客户详细信息,可以将它们作为 create 方法的第二个参数传递
$user->newSubscription('default', 'PLN_cgumntiwkkda3cw')->create($auth_token, [ ',etadata' => json_encode(['pass_through' => 'customer data']), ]);
要了解 Paystack 支持的附加字段,请参阅 Paystack 关于创建客户的文档或相应的 Paystack 文档。
检查订阅状态
一旦用户订阅了您的应用程序,您可以使用各种方便的方法轻松检查他们的订阅状态。首先,subscribed 方法返回 true,如果用户有活动订阅,即使订阅目前处于试用期
// Paystack plan name e.g default, main, yakata if ($user->subscribed('default')) { // }
subscribed 方法也是路由中间件的好候选,允许您根据用户的订阅状态过滤对路由和控制器访问
public function handle($request, Closure $next) { if ($request->user() && ! $request->user()->subscribed('default')) { // This user is not a paying customer... return redirect('billing'); } return $next($request); }
如果您想确定用户是否仍在试用期,可以使用 onTrial 方法。这个方法对于向用户显示他们仍在试用期警告非常有用
if ($user->subscription('default')->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('default')->cancelled()) { // }
您还可以确定用户是否已取消订阅,但仍在“宽限期”内,直到订阅完全过期。例如,如果用户在3月5日取消了一个原定于3月10日到期的订阅,则用户在“宽限期”内直到3月10日。请注意,在此期间,subscribed方法仍然返回true。
if ($user->subscription('default')->onGracePeriod()) { // }
取消订阅
要取消订阅,请调用用户订阅上的cancel方法。
$user->subscription('default')->cancel();
当订阅被取消时,Cashier会自动设置数据库中的ends_at列。此列用于知道subscribed方法何时应开始返回false。例如,如果客户在3月1日取消了一个原定于3月5日到期的订阅,则subscribed方法将继续返回true直到3月5日。
您可以使用onGracePeriod方法确定用户是否已取消订阅但仍在“宽限期”内。
if ($user->subscription('default')->onGracePeriod()) { // }
如果您希望立即取消订阅,请调用用户订阅上的cancelNow方法。
$user->subscription('default')->cancelNow();
恢复订阅
如果用户已取消订阅并且您希望恢复它,请使用resume方法。用户必须仍然处于他们的宽限期才能恢复订阅。
$user->subscription('default')->resume();
如果用户取消订阅然后在订阅完全过期之前恢复该订阅,他们不会立即被收费。相反,他们的订阅将被重新激活,并且将在原始计费周期中收费。
订阅试用期
前端计费
如果您希望在收集客户付款方式信息的同时为客户提供试用期,您应该在创建订阅时使用trialDays方法。
$user = User::find(1); $user->newSubscription('default', 'PLN_gx2wn530m0i3w3m') ->trialDays(10) ->create($auth_token);
此方法将在数据库中的订阅记录上设置试用期结束日期,并指示Paystack在此日期之后开始向客户收费。
如果客户在试用期结束日期之前没有取消订阅,则试用期结束后将立即收费,因此您应确保通知您的用户他们的试用期结束日期。
您可以使用用户实例的onTrial方法或订阅实例的onTrial方法来确定用户是否处于试用期。以下两个示例是相同的。
if ($user->onTrial('default')) { // } if ($user->subscription('default')->onTrial()) { // }
前端不计费
如果您希望在收集客户付款方式信息之前提供试用期,您可以将用户记录上的trial_ends_at列设置为所需的试用期结束日期。这通常在用户注册期间完成。
$user = Customer::create([ // Populate other user properties... 'billable_id' => $user->getKey(), 'billable_type' => $user->getMorphClass(), '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('default', $plan_code)->create($auth_token); $user->newSubscription('default', $plan_code)->create();
客户
创建客户
有时,您可能希望创建一个不开始订阅的Paystack客户。您可以使用createAsCustomer方法完成此操作。
$user->createAsCustomer();
一旦客户在Paystack中创建,您可以在稍后日期开始订阅。
支付方式
检索认证支付方式
账单模型实例上的卡片方法返回一个InitAfricaHQ\Cashier\Card实例的集合
$cards = $user->cards();
删除支付方式
要删除一张卡,您首先应该使用卡片方法检索客户的身份验证。然后,您可以调用要删除的实例上的删除方法
foreach ($user->cards() as $card) { $card->delete(); }
删除客户的全部卡片支付身份验证
$user->deleteCards();
处理Paystack Webhooks
Paystack可以通过webhooks通知您的应用程序各种事件。要处理Paystack webhooks,定义一个指向Cashier webhook控制器的路由。如果已配置,则默认为/paystack。此控制器将处理所有传入的webhook请求并将它们派发到适当的控制器方法:您可以设置PAYSTACK_PATH环境变量以使用自己的路径,或者创建一个新的完整路由来覆盖此设置。
Route::post( 'paystack/webhook', '\InitAfricaHQ\Cashier\Http\Controllers\WebhookController' );
注册您的路由后,请务必在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事件的"camel case"名称开头。
<?php namespace App\Http\Controllers; use InitAfricaHQ\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' );
单次收费
简单收费
当使用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', ]); });