bigdropinc / yii2-cashier
Yii2 Cashier 提供了 Stripe 订阅计费服务的接口。
Requires
- dompdf/dompdf: ^0.8.0
- nesbot/carbon: ~1.0
- stripe/stripe-php: ~4.0
- yii2mod/yii2-behaviors: *
- yiisoft/yii2: >=2.0.8
Requires (Dev)
README
Yii 2 的 Cashier 扩展
本扩展是 Laravel Cashier 包的移植版本,可以访问 Laravel 文档中的 Cashier 部分
安装
安装此扩展的首选方式是通过 composer。
运行以下命令
php composer.phar require --prefer-dist bigdropinc/yii2-cashier "*"
或者
"bigdropinc/yii2-cashier": "*"
将以下内容添加到您的 composer.json 文件的 require 部分。
Stripe 配置
本项目受到 Laravel Cashier 包的启发,并试图将它的简洁性带到 Yii 框架中。
数据库迁移
在开始使用 Cashier 之前,我们还需要准备数据库。
$tableOptions = null; if ($this->db->driverName === 'mysql') { $tableOptions = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=InnoDB'; } $this->createTable('subscription', [ 'id' => $this->primaryKey(), 'user_id' => $this->integer()->notNull(), 'name' => $this->string()->notNull(), 'stripe_id' => $this->string()->notNull(), 'stripe_plan' => $this->string()->notNull(), 'quantity' => $this->integer()->notNull(), 'trial_end_at' => $this->timestamp()->null(), 'end_at' => $this->timestamp()->null(), 'created_at' => $this->dateTime()->null(), 'updated_at' => $this->dateTime()->null() ], $tableOptions); $this->addColumn('user', 'stripe_id', $this->string()); $this->addColumn('user', 'card_brand', $this->string()); $this->addColumn('user', 'card_last_four', $this->string()); $this->addColumn('user', 'trial_end_at', $this->timestamp()->null());
您也可以使用以下命令应用迁移
php yii migrate --migrationPath=@vendor/bigdropinc/yii2-cashier/migrations
模型设置
接下来,将 Billable 特性添加到您的 User 模型定义中
use bigdropinc\cashier\Billable; class User extends ActiveRecord implements IdentityInterface { use Billable; }
提供商密钥
接下来,您应该在配置文件中配置您的 params 部分
'params' => [ ..... 'stripe' => [ 'apiKey' => 'Your Secret Api Key' ], ],
订阅
创建订阅
要创建订阅,首先获取您的可计费模型实例,通常将是 models\User 的实例。获取模型实例后,您可以使用 newSubscription 方法来创建模型订阅
$user = User::findOne(1); $user->newSubscription('main', 'monthly')->create($creditCardToken);
传递给 newSubscription 方法的第一个参数是订阅的名称。如果您的应用程序仅提供单个订阅,您可能将其称为 main 或 primary。第二个参数是用户订阅的具体 Stripe 计划。此值应与 Stripe 中计划的标识符相对应。
create 方法将自动创建订阅,并将客户 ID 和其他相关计费信息更新到您的数据库中。
额外用户详情
如果您想指定额外的客户详情,可以在 create 方法的第二个参数中传递它们
$user->newSubscription('main', 'monthly')->create($creditCardToken, [ 'description' => 'Customer for test@example.com' ]);
要了解更多关于 Stripe 支持的额外字段的信息,请查看 Stripe 的 客户创建文档
优惠券
如果您希望在创建订阅时应用优惠券,可以使用 withCoupon 方法
$user->newSubscription('main', 'monthly') ->withCoupon('code') ->create($creditCardToken);
检查订阅状态
一旦用户订阅了您的应用程序,您可以使用各种方便的方法轻松检查他们的订阅状态。首先,subscribed 方法如果用户有活动订阅(即使订阅目前在试用期内)将返回 true
if ($user->subscribed('main')) { // }
如果您想确定用户是否仍在试用期内,可以使用 onTrial 方法。此方法可以用来向用户显示他们仍在试用期的警告
if ($user->subscription('main')->onTrial()) { // }
subscribedToPlan 方法可以用来确定用户是否基于给定的 Stripe 计划 ID 订阅了给定的计划。在这个例子中,我们将确定用户的主要订阅是否正在订阅月度计划
if ($user->subscribedToPlan('monthly', 'main')) { // }
已取消订阅状态
要确定用户曾经是活跃的订阅者,但现在已取消订阅,可以使用 cancelled 方法
if ($user->subscription('main')->cancelled()) { // }
您还可以确定用户是否已取消订阅,但仍在“宽限期”内,直到订阅完全到期。例如,如果用户在3月5日取消了一个原定于3月10日到期的订阅,则用户将在3月10日之前的“宽限期”内。请注意,在此期间订阅方法仍返回true。
if ($user->subscription('main')->onGracePeriod()) { // }
更改计划
用户订阅了您的应用程序后,他们可能偶尔想要更改到新的订阅计划。要切换用户到新的订阅,请使用swap方法。例如,我们可能很容易将用户切换到高级订阅
$user = User::findOne(1); $user->subscription('main')->swap('provider-plan-id');
如果用户处于试用状态,试用期将保持不变。此外,如果订阅中存在“数量”,该数量也将保持不变
$user->subscription('main')->swap('provider-plan-id');
如果您想要切换计划但跳过您要切换到的计划中的试用期,您可以使用skipTrial方法
$user->subscription('main') ->skipTrial() ->swap('provider-plan-id');
订阅数量
有时订阅会受到“数量”的影响。例如,您的应用程序可能按账户中每个用户每月10美元的价格收费。为了轻松增加或减少订阅数量,请使用incrementQuantity和decrementQuantity方法
$user = User::findOne(1); $user->subscription('main')->incrementQuantity(); // Add five to the subscription's current quantity... $user->subscription('main')->incrementQuantity(5); $user->subscription('main')->decrementQuantity(); // Subtract five to the subscription's current quantity... $user->subscription('main')->decrementQuantity(5);
或者,您可以使用updateQuantity方法设置特定数量
$user->subscription('main')->updateQuantity(10);
有关订阅数量的更多信息,请参阅Stripe文档。
订阅税费
使用Cashier,提供给Stripe的tax_percent值非常简单。要指定用户在订阅上支付的税率百分比,在您的可计费模型上实现taxPercentage方法,并返回一个介于0到100之间的数值,最多2位小数。
public function taxPercentage() { return 20; }
这使您能够按模型逐个应用税率,这对于跨越多个国家的用户基础可能很有帮助。
取消订阅
要取消订阅,只需在用户的订阅上调用cancel方法
$user->subscription('main')->cancel();
当订阅被取消时,Cashier将自动设置数据库中的end_at列。该列用于确定订阅方法何时开始返回false。例如,如果客户在3月1日取消了订阅,但订阅原定于3月5日结束,则订阅方法将继续返回true直到3月5日。
您可以使用onGracePeriod方法确定用户是否已取消订阅但仍在“宽限期”内
if ($user->subscription('main')->onGracePeriod()) { // }
恢复订阅
如果用户已取消订阅并且您希望恢复它,请使用resume方法。用户必须仍在他们的宽限期内才能恢复订阅
$user->subscription('main')->resume();
如果用户取消订阅然后在订阅完全到期之前恢复该订阅,他们将不会立即收费。相反,他们的订阅将简单重新激活,并且将在原始计费周期中收费
订阅试用
信用卡预收费
如果您想在收集支付方式信息的同时为客户提供试用期,应在创建订阅时使用trialDays方法
$user = User::findOne(1); $user->newSubscription('main', 'monthly') ->trialDays(10) ->create($creditCardToken);
此方法将在数据库中的订阅记录上设置试用期结束日期,并指示Stripe在此日期之后不开始向客户收费。
如果客户在试用期结束日期之前未取消订阅,他们将在试用期结束后立即收费。因此,您应通知您的用户他们的试用期结束日期。您可以使用用户实例的onTrial方法或订阅实例的onTrial方法确定用户是否处于试用期内。以下两个示例在目的上基本相同
if ($user->onTrial('main')) { // } if ($user->subscription('main')->onTrial()) { // }
信用卡不预收费
如果您想在收集用户支付方式信息之前提供试用期,您只需将用户记录中的 trial_end_at 列设置为所需的试用期结束日期即可。例如,这通常在用户注册期间完成。
$user = new User([ // Populate other user properties... 'trial_end_at' => Carbon::now()->addDays(10), ]);
收银员将此类试用期称为“通用试用期”,因为它不与任何现有订阅相关联。如果当前日期尚未超过 trial_end_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::findOne(1); $user->newSubscription('main', 'monthly')->create($creditCardToken);
处理 Stripe Webhooks
失败的订阅
只需将 WebhookController 添加到您的配置文件中的 controllerMap 即可。
'controllerMap' => [ //Stripe webhook 'webhook' => 'bigdropinc\cashier\controllers\WebhookController', ],
就这样!失败支付将被捕获并由控制器处理。当 Stripe 判断订阅失败时(通常在三次失败支付尝试后),控制器将取消客户的订阅。别忘了:您需要在 Stripe 控制面板设置中配置 webhook URI,例如:在您的 Stripe 控制面板设置中配置为 yoursite.com/webhook/handle-webhook。
单次收费
简单收费
当使用 Stripe 时,charge 方法接受您希望在应用程序使用的货币最低分母中收费的金额。
如果您想对订阅客户的信用卡进行一次性收费,您可以在可计费模型实例上使用 charge 方法。
// Stripe Accepts Charges In Cents... $user->charge(100);
charge 方法接受一个数组作为其第二个参数,允许您将任何选项传递给底层的 Stripe 充值创建。
$user->charge(100, [ 'custom_option' => $value, ]);
如果充值失败,charge 方法将抛出异常。如果充值成功,方法将返回完整的 Stripe 响应。
try { $response = $user->charge(100); } catch (Exception $e) { // }
带有发票的收费
有时您可能需要一次性收费,同时也为收费生成发票,以便您可以向客户提供 PDF 收据。invoiceFor 方法允许您做到这一点。例如,让我们为“一次性费用”向客户开一张 5.00 美元的发票。
// Stripe Accepts Charges In Cents... $user->invoiceFor('One Time Fee', 500);
发票将立即对用户的信用卡进行收费。invoiceFor 方法还接受一个数组作为第三个参数,允许您将任何选项传递给底层的 Stripe 充值创建。
$user->invoiceFor('One Time Fee', 500, [ 'custom-option' => $value, ]);
如果不想让发票重试失败的收费,您需要在第一次失败收费后使用 Stripe API 关闭它们。
发票
您可以使用 invoices 方法轻松检索可计费模型的发票数组。
$invoices = $user->invoices();
在列出客户的发票时,您可以使用发票的帮助方法显示相关的发票信息。例如,您可能希望将每个发票列在 GridView 中,使用户能够轻松下载它们。
$dataProvider = new \yii\data\ArrayDataProvider([ 'allModels' => $invoices, 'pagination' => [ 'pageSize' => 10, ], ]); echo yii\grid\GridView::widget([ 'dataProvider' => $dataProvider, 'columns' => [ [ 'label' => 'Invoice Date', 'value' => function ($model) { return $model->date()->toFormattedDateString(); } ], [ 'label' => 'Total', 'value' => function ($model) { return $model->total(); } ], [ 'header' => 'Action', 'class' => 'yii\grid\ActionColumn', 'template' => '{download}', 'buttons' => [ 'download' => function ($url, $model, $key) { $options = [ 'title' => Yii::t('yii', 'Download Invoice'), 'data-pjax' => '0', ]; $url = ['download-invoice', 'invoiceId' => $model->id]; return \yii\helpers\Html::a('<span class="glyphicon glyphicon-download"></span>', $url, $options); } ], ], ], ]);
生成发票 PDF
在您的控制器中创建 download-invoice 动作,例如。
public function actionDownloadInvoice($invoiceId) { return $user->downloadInvoice($invoiceId, [ 'vendor' => 'Your Company', 'product' => 'Your Product', ]); }
退款
退款对象允许您退款先前已创建但尚未退款的收费。资金将退还到最初被收费的信用卡或借记卡。您最初被收取的费用也将退还。
// Create Invoice $invoice = $user->invoiceFor('Invoice Description', 1000); // Create the refund $refund = $user->refund($invoice->charge); var_dump($refund->amount); // 1000