以下是详细的实现方案概览,你可以根据它来构建你的系统:
实现环节 | 核心任务 | 关键方法/技术点 |
---|---|---|
「 数据库与模型」 | 扩展租户模型,记录订阅信息。 | 在 tenants 表中添加 trial_ends_at 、subscribed_at 等字段,并在 Tenant 模型中创建状态检查方法(如 isOnTrial )。 |
「 状态检查与更新」 | 自动检查并更新到期租户的状态。 | 使用 Laravel 任务调度器(Schedule )创建每日执行的命令,查询并暂停过期租户。 |
「 访问控制」 | 拦截到期租户的访问请求。 | 创建中间件(Middleware )验证当前租户订阅状态,对过期租户重定向至续费页面。 |
「 续费与恢复」 | 提供续费通道并恢复租户访问。 | 集成 Laravel Cashier 处理支付,续费成功后更新租户状态(如 subscribed_at )。 |
下面我们详细拆解每个步骤。
数据库与模型设计
首先,需要为租户(Tenant)添加与订阅相关的字段,以跟踪其订阅状态。
- 「创建迁移文件」:新建一个迁移文件,为
tenants
表添加必要的字段。php artisan make:migration add_subscription_fields_to_tenants_table
在迁移文件中添加字段:// database/migrations/xxxx_xx_xx_xxxxxx_add_subscription_fields_to_tenants_table.php
运行迁移:
public function up()
{
Schema::table('tenants', function (Blueprint $table) {
// 试用期结束时间
$table->timestamp('trial_ends_at')->nullable();
// 订阅开始时间
$table->timestamp('subscribed_at')->nullable();
// 订阅结束时间
$table->timestamp('subscription_ends_at')->nullable();
// 标记订阅是否已暂停
$table->boolean('is_suspended')->default(false);
});
}php artisan migrate
。 - 「扩展租户模型」:在
App\Models\Tenant
模型中添加用于检查订阅状态的方法。// app/Models/Tenant.php
public function isOnTrial()
{
return $this->trial_ends_at && $this->trial_ends_at->isFuture();
}
public function isSubscribed()
{
// 检查是否在订阅期内,例如订阅后一年内有效
return $this->subscribed_at && $this->subscription_ends_at && $this->subscription_ends_at->isFuture();
}
public function isActive()
{
// 综合判断:在试用期内或有效订阅期内,都视为活跃状态
return $this->isOnTrial() || $this->isSubscribed();
}
public function suspend()
{
$this->update(['is_suspended' => true]);
// 这里可以添加其他暂停逻辑,例如记录日志
}
public function activate()
{
$this->update(['is_suspended' => false]);
// 激活租户
}
自动化状态检查与更新
使用 Laravel 的任务调度(Task Scheduler)定期检查租户订阅状态,并自动暂停过期的租户。
- 「创建 Artisan 命令」:
php artisan make:command CheckTenantSubscriptions
- 「编写命令逻辑」:在生成的命令类中,实现检查并更新过期租户状态的逻辑。
// app/Console/Commands/CheckTenantSubscriptions.php
protected $signature = 'tenants:check-subscriptions';
protected $description = '检查租户订阅状态并暂停过期租户';
public function handle()
{
$now = now();
// 查找订阅已过期且未被暂停的租户
$expiredTenants = Tenant::where('subscription_ends_at', '<=', $now)
->where('is_suspended', false)
->get();
foreach ($expiredTenants as $tenant) {
$tenant->suspend();
$this->info("已暂停租户: {$tenant->id}");
// 这里可以触发邮件通知,告知租户已过期
}
$this->info('订阅状态检查完成。');
} - 「注册定时任务」:在
app/Console/Kernel.php
的schedule
方法中注册该命令,例如每天凌晨执行。// app/Console/Kernel.php
确保服务器上设置了 Cron 任务来触发 Laravel 调度器:
protected function schedule(Schedule $schedule)
{
$schedule->command('tenants:check-subscriptions')->daily();
}* * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1
。
实现访问控制中间件
创建一个中间件,在租户尝试访问其 WordPress 应用时进行拦截。
- 「创建中间件」:
php artisan make:middleware CheckTenantSubscription
- 「编写中间件逻辑」:在中间件中判断当前租户的订阅状态。
// app/Http/Middleware/CheckTenantSubscription.php
public function handle(Request $request, Closure $next)
{
// 获取当前请求对应的租户,这取决于你的多租户包如何识别租户
$tenant = tenancy()->getCurrentTenant(); // 请根据你使用的多租户包调整此行
if ($tenant && $tenant->is_suspended) {
// 如果租户已被暂停,重定向到续费页面
return redirect()->route('subscription.renewal', $tenant->id);
}
// 如果租户活跃,继续访问请求
return $next($request);
} - 「注册并使用中间件」:将中间件注册到
app/Http/Kernel.php
的$routeMiddleware
数组中。// app/Http/Kernel.php
然后,在租户应用的路由组中应用此中间件。
protected $routeMiddleware = [
// ... 其他中间件
'check.tenant.subscription' => \App\Http\Middleware\CheckTenantSubscription::class,
];// routes/tenant.php 或类似的路由文件
Route::middleware(['web', 'check.tenant.subscription'])->group(function () {
// 所有租户的 WordPress 应用路由都应包含在此路由组内
// 例如,通配路由,将请求转发给 WordPress 索引文件
Route::get('/{any}', function () {
// 这里引入你的租户专属 WordPress 应用的 index.php
require public_path("tenants/{$tenant->id}/wordpress/index.php");
})->where('any', '.*');
});
集成支付与续费流程
当租户被暂停后,需要提供续费通道以恢复访问。
- 「创建续费页面路由和控制器」:展示续费信息并处理支付。
- 「集成支付系统」:使用如 「Laravel Cashier」 可以极大简化与 Stripe 或 Paddle 的集成,处理订阅支付。
// 在续费控制器中
public function processRenewal(Request $request, Tenant $tenant)
{
// 假设使用 Cashier,并已关联用户模型
try {
$paymentMethod = $request->payment_method; // 从前端获取的支付令牌
// 创建或交换订阅
$tenant->user->newSubscription('default', 'your_plan_id')->create($paymentMethod);
// 更新租户状态
$tenant->update([
'subscribed_at' => now(),
'subscription_ends_at' => now()->addYear(), // 假设年付
'is_suspended' => false,
]);
return redirect()->back()->with('success', '续费成功!您的站点已恢复访问。');
} catch (\Exception $e) {
return redirect()->back()->with('error', '支付失败: ' . $e->getMessage());
}
} - 「处理 Webhooks」:配置 Stripe 等支付平台的 Webhook,以便在用户取消订阅或支付失败时,能及时更新你数据库中的租户状态,确保状态同步。
最佳实践与优化建议
- 「状态缓存」:频繁检查租户状态可能影响性能。可以考虑使用 Laravel 的缓存来存储租户的活跃状态,并只在状态变更时更新缓存。
- 「通知提醒」:在租户订阅到期前,通过邮件或站内通知发送提醒,提升用户体验并减少非故意过期。
- 「数据备份与保留策略」:明确制定策略,规定过期租户的数据保留期限以及最终清理流程。
- 「测试」:务必对各种场景(新租户试用、正常续费、过期暂停、续费恢复等)进行充分测试。
通过以上步骤,你就可以在 Laravel 多租户系统中建立起一套完整的 WordPress 用户订阅到期控制逻辑。这套系统能自动管理租户的生命周期,确保只有付费租户才能正常使用服务。