Files
laralog/tests/Feature/MembershipFlowTest.php
T
ak ec087fe2a8 feat: Payment 订单商品化——多态 payable 关联购买实体
- payments 表新增 payable_type/payable_id(nullableMorphs),订单可关联文章/套餐等可购买实体
- createOrder 接收 $payable 模型,取代 description 字符串解析
- membership 支付回调按 payable instanceof 分发(激活订阅 / 解锁单篇)
- Post/MembershipPlan 提供 payableLabel/payableUrl 约定方法;Payment 用约定方法生成展示标签与后台链接(皮肤等新实体可直接接入)
- 订单表格新增「类型」徽章(文章/会员套餐),商品列链接到对应后台编辑页;无 payable 的旧订单显示 —
2026-08-12 00:22:50 +08:00

151 lines
5.0 KiB
PHP

<?php
namespace Tests\Feature;
use App\Models\Post;
use App\Models\Setting;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Plugins\Neatstudio\Membership\Models\MembershipPlan;
use Plugins\Neatstudio\Membership\Models\Subscription;
use Plugins\Neatstudio\Payment\Models\Payment;
use Tests\TestCase;
class MembershipFlowTest extends TestCase
{
use RefreshDatabase;
private User $user;
private MembershipPlan $plan;
protected function setUp(): void
{
parent::setUp();
$this->seed();
Setting::set('pay_sandbox', '1');
$this->user = User::create([
'name' => '测试会员',
'email' => 'member@test.com',
'password' => bcrypt('password'),
]);
$this->plan = MembershipPlan::create([
'name' => '月付会员',
'slug' => 'monthly',
'price' => 9900,
'duration_days' => 30,
'permissions' => ['read_members_only'],
'active' => true,
]);
}
public function test_membership_page_lists_plans(): void
{
$this->get('/membership')->assertOk()->assertSee('月付会员');
}
public function test_subscribe_creates_payment_and_redirects(): void
{
$this->actingAs($this->user)
->post('/membership/'.$this->plan->id.'/subscribe')
->assertRedirect();
$payment = Payment::query()->where('user_id', $this->user->id)->first();
$this->assertNotNull($payment);
$this->assertSame('pending', $payment->status);
$this->assertTrue($payment->payable->is($this->plan));
$this->assertNull($payment->description);
}
public function test_sandbox_payment_activates_subscription(): void
{
$this->actingAs($this->user)->post('/membership/'.$this->plan->id.'/subscribe');
$payment = Payment::query()->where('user_id', $this->user->id)->first();
$this->actingAs($this->user)->get('/pay/checkout/'.$payment->id)->assertRedirect();
$this->actingAs($this->user)->post('/pay/sandbox/'.$payment->order_no.'/confirm')->assertRedirect();
$payment->refresh();
$this->assertSame('paid', $payment->status);
$subscription = Subscription::query()->where('user_id', $this->user->id)->first();
$this->assertNotNull($subscription);
$this->assertTrue($subscription->isActive());
}
public function test_paid_content_hidden_for_guest_and_visible_for_member(): void
{
$post = Post::create([
'title' => '付费文章',
'slug' => 'paid-post',
'content' => "公开内容\n\n[paid]\n付费隐藏内容\n[/paid]",
'content_format' => 'markdown',
'status' => 'published',
'published_at' => now(),
]);
// 游客:付费部分被替换
$this->get('/posts/paid-post.shtml')
->assertOk()
->assertDontSee('付费隐藏内容')
->assertSee('付费内容已隐藏');
// 会员:可见
$this->actingAs($this->user)->post('/membership/'.$this->plan->id.'/subscribe');
$payment = Payment::query()->where('user_id', $this->user->id)->first();
$this->actingAs($this->user)->post('/pay/sandbox/'.$payment->order_no.'/confirm');
$this->actingAs($this->user)->get('/posts/paid-post.shtml')
->assertOk()
->assertSee('付费隐藏内容');
}
public function test_unlock_post_creates_payment_with_payable_and_grants_access(): void
{
$post = Post::create([
'title' => '单篇付费',
'slug' => 'single-paid',
'content' => '付费内容正文',
'content_format' => 'markdown',
'status' => 'published',
'published_at' => now(),
'meta' => ['price' => 500],
]);
$this->actingAs($this->user)
->post('/posts/'.$post->id.'/unlock')
->assertRedirect();
$payment = Payment::query()->where('user_id', $this->user->id)->first();
$this->assertNotNull($payment);
$this->assertTrue($payment->payable->is($post));
$this->assertSame(500, $payment->amount);
$this->assertStringContainsString('单篇付费', $payment->payable_label);
$this->assertNotNull($payment->payable_url);
// 沙箱支付后写入解锁用户
$this->actingAs($this->user)->post('/pay/sandbox/'.$payment->order_no.'/confirm');
$post->refresh();
$this->assertContains($this->user->id, $post->meta['unlocked_user_ids']);
}
public function test_legacy_order_without_payable_shows_placeholder(): void
{
$payment = Payment::create([
'order_no' => 'LEGACY0001',
'user_id' => $this->user->id,
'subject' => '历史订单',
'amount' => 100,
'status' => 'paid',
'paid_at' => now(),
]);
$this->assertSame('—', $payment->payable_label);
$this->assertNull($payment->payable_url);
}
}