309 lines
10 KiB
PHP
309 lines
10 KiB
PHP
<?php
|
|
|
|
/**
|
|
* 文件功能:VIP 支付集成功能测试
|
|
* 用于验证聊天室发起 VIP 支付下单与接收异步回调开通会员的关键链路
|
|
*/
|
|
|
|
namespace Tests\Feature\Feature;
|
|
|
|
use App\Models\Sysparam;
|
|
use App\Models\User;
|
|
use App\Models\VipLevel;
|
|
use App\Models\VipPaymentOrder;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Illuminate\Support\Facades\Http;
|
|
use Illuminate\Support\Facades\Redis;
|
|
use Tests\TestCase;
|
|
|
|
/**
|
|
* VIP 支付集成功能测试
|
|
* 覆盖创建订单、异步开通会员以及聊天室内购买成功喜报等关键链路。
|
|
*/
|
|
class VipPaymentIntegrationTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
/**
|
|
* 每个测试前清空 Redis,避免聊天室在线状态与消息缓存互相污染。
|
|
*/
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
Redis::flushall();
|
|
}
|
|
|
|
/**
|
|
* 测试用户可以发起 VIP 支付并跳转到支付中心页面
|
|
*/
|
|
public function test_user_can_create_vip_payment_order_and_redirect_to_payment_center(): void
|
|
{
|
|
$this->seedVipPaymentConfig();
|
|
|
|
$user = User::factory()->create();
|
|
$vipLevel = VipLevel::factory()->create([
|
|
'name' => '黄金会员',
|
|
'price' => 68,
|
|
'duration_days' => 30,
|
|
]);
|
|
|
|
Http::fake([
|
|
'https://novalink.test/api/open/v1/pay/orders' => Http::response([
|
|
'success' => true,
|
|
'message' => '支付单创建成功',
|
|
'data' => [
|
|
'payment_order_no' => 'PO202604111200001234',
|
|
'pay_url' => 'https://novalink.test/payment/checkout/alipay/PO202604111200001234',
|
|
],
|
|
], 201),
|
|
]);
|
|
|
|
$response = $this->actingAs($user)->post(route('vip.payment.store'), [
|
|
'vip_level_id' => $vipLevel->id,
|
|
'provider' => 'alipay',
|
|
]);
|
|
|
|
$response->assertRedirect('https://novalink.test/payment/checkout/alipay/PO202604111200001234');
|
|
Http::assertSent(function ($request) {
|
|
$data = $request->data();
|
|
|
|
return $request->url() === 'https://novalink.test/api/open/v1/pay/orders'
|
|
&& ($data['provider'] ?? null) === 'alipay';
|
|
});
|
|
$this->assertDatabaseHas('vip_payment_orders', [
|
|
'user_id' => $user->id,
|
|
'vip_level_id' => $vipLevel->id,
|
|
'payment_order_no' => 'PO202604111200001234',
|
|
'status' => 'pending',
|
|
'provider' => 'alipay',
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* 测试用户可以选择微信支付并跳转到平台二维码页面
|
|
*/
|
|
public function test_user_can_create_wechat_vip_payment_order_and_redirect_to_wechat_checkout_page(): void
|
|
{
|
|
$this->seedVipPaymentConfig();
|
|
|
|
$user = User::factory()->create();
|
|
$vipLevel = VipLevel::factory()->create([
|
|
'name' => '黄金会员',
|
|
'price' => 68,
|
|
'duration_days' => 30,
|
|
]);
|
|
|
|
Http::fake([
|
|
'https://novalink.test/api/open/v1/pay/orders' => Http::response([
|
|
'success' => true,
|
|
'message' => '支付单创建成功',
|
|
'data' => [
|
|
'payment_order_no' => 'PO202604131530001234',
|
|
'pay_url' => 'https://novalink.test/payment/checkout/wechat/PO202604131530001234',
|
|
],
|
|
], 201),
|
|
]);
|
|
|
|
$response = $this->actingAs($user)->post(route('vip.payment.store'), [
|
|
'vip_level_id' => $vipLevel->id,
|
|
'provider' => 'wechat',
|
|
]);
|
|
|
|
$response->assertRedirect('https://novalink.test/payment/checkout/wechat/PO202604131530001234');
|
|
Http::assertSent(function ($request) {
|
|
$data = $request->data();
|
|
|
|
return $request->url() === 'https://novalink.test/api/open/v1/pay/orders'
|
|
&& ($data['provider'] ?? null) === 'wechat';
|
|
});
|
|
$this->assertDatabaseHas('vip_payment_orders', [
|
|
'user_id' => $user->id,
|
|
'vip_level_id' => $vipLevel->id,
|
|
'payment_order_no' => 'PO202604131530001234',
|
|
'status' => 'pending',
|
|
'provider' => 'wechat',
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* 测试异步回调只会为同一笔订单开通一次会员,避免重复叠加时长
|
|
*/
|
|
public function test_async_notify_opens_vip_only_once(): void
|
|
{
|
|
$this->seedVipPaymentConfig();
|
|
|
|
$user = User::factory()->create();
|
|
$vipLevel = VipLevel::factory()->create([
|
|
'name' => '钻石会员',
|
|
'price' => 88,
|
|
'duration_days' => 30,
|
|
]);
|
|
|
|
$order = VipPaymentOrder::factory()->create([
|
|
'user_id' => $user->id,
|
|
'vip_level_id' => $vipLevel->id,
|
|
'amount' => 88.00,
|
|
'provider' => 'wechat',
|
|
'vip_name' => $vipLevel->name,
|
|
'vip_duration_days' => $vipLevel->duration_days,
|
|
'payment_order_no' => 'PO202604111530001234',
|
|
'merchant_order_no' => 'VPO202604111530001234',
|
|
]);
|
|
|
|
$payload = [
|
|
'payment_order_no' => $order->payment_order_no,
|
|
'merchant_order_no' => $order->merchant_order_no,
|
|
'status' => 'paid',
|
|
'amount' => '88.00',
|
|
'provider' => 'wechat',
|
|
'provider_trade_no' => '4200002512202604139876543210',
|
|
'paid_at' => '2026-04-11 15:35:12',
|
|
];
|
|
|
|
$rawBody = json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
|
$signature = hash_hmac('sha256', $rawBody, 'chatroom-secret');
|
|
|
|
$firstResponse = $this->call(
|
|
'POST',
|
|
route('vip.payment.notify'),
|
|
[],
|
|
[],
|
|
[],
|
|
[
|
|
'CONTENT_TYPE' => 'application/json',
|
|
'HTTP_X_PAYMENT_SIGNATURE' => $signature,
|
|
],
|
|
$rawBody
|
|
);
|
|
|
|
$firstResponse->assertOk();
|
|
$user->refresh();
|
|
$this->assertSame($vipLevel->id, $user->vip_level_id);
|
|
$this->assertNotNull($user->hy_time);
|
|
$firstExpireAt = $user->hy_time?->toDateTimeString();
|
|
|
|
$secondResponse = $this->call(
|
|
'POST',
|
|
route('vip.payment.notify'),
|
|
[],
|
|
[],
|
|
[],
|
|
[
|
|
'CONTENT_TYPE' => 'application/json',
|
|
'HTTP_X_PAYMENT_SIGNATURE' => $signature,
|
|
],
|
|
$rawBody
|
|
);
|
|
|
|
$secondResponse->assertOk();
|
|
$user->refresh();
|
|
$this->assertSame($firstExpireAt, $user->hy_time?->toDateTimeString());
|
|
$this->assertDatabaseHas('vip_payment_orders', [
|
|
'id' => $order->id,
|
|
'status' => 'paid',
|
|
'provider' => 'wechat',
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* 测试会员中心页面只展示当前用户自己的购买记录
|
|
*/
|
|
public function test_vip_center_only_shows_current_user_records(): void
|
|
{
|
|
$this->seedVipPaymentConfig();
|
|
|
|
$currentUser = User::factory()->create();
|
|
$otherUser = User::factory()->create();
|
|
|
|
VipPaymentOrder::factory()->create([
|
|
'user_id' => $currentUser->id,
|
|
'order_no' => 'VPO_CURRENT_001',
|
|
]);
|
|
|
|
VipPaymentOrder::factory()->create([
|
|
'user_id' => $otherUser->id,
|
|
'order_no' => 'VPO_OTHER_001',
|
|
]);
|
|
|
|
$response = $this->actingAs($currentUser)->get(route('vip.center'));
|
|
|
|
$response->assertOk();
|
|
$response->assertSee('VPO_CURRENT_001');
|
|
$response->assertDontSee('VPO_OTHER_001');
|
|
$response->assertSee('我的购买记录');
|
|
}
|
|
|
|
/**
|
|
* 测试允许自定义的会员可以在会员中心保存自己的欢迎语和离开语。
|
|
*/
|
|
public function test_vip_member_can_update_custom_presence_messages(): void
|
|
{
|
|
$this->seedVipPaymentConfig();
|
|
|
|
$vipLevel = VipLevel::factory()->create([
|
|
'allow_custom_messages' => true,
|
|
]);
|
|
$user = User::factory()->create([
|
|
'vip_level_id' => $vipLevel->id,
|
|
'hy_time' => now()->addDays(30),
|
|
]);
|
|
|
|
$response = $this->actingAs($user)->put(route('vip.center.presence.update'), [
|
|
'custom_join_message' => '{username} 乘着流光闪耀登场',
|
|
'custom_leave_message' => '{username} 留下一缕星辉悄然退场',
|
|
]);
|
|
|
|
$response->assertRedirect(route('vip.center'));
|
|
$this->assertDatabaseHas('users', [
|
|
'id' => $user->id,
|
|
'custom_join_message' => '{username} 乘着流光闪耀登场',
|
|
'custom_leave_message' => '{username} 留下一缕星辉悄然退场',
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* 测试普通用户或不允许自定义的会员无法保存专属语句。
|
|
*/
|
|
public function test_non_customizable_vip_member_cannot_update_custom_presence_messages(): void
|
|
{
|
|
$this->seedVipPaymentConfig();
|
|
|
|
$vipLevel = VipLevel::factory()->create([
|
|
'allow_custom_messages' => false,
|
|
]);
|
|
$user = User::factory()->create([
|
|
'vip_level_id' => $vipLevel->id,
|
|
'hy_time' => now()->addDays(30),
|
|
]);
|
|
|
|
$response = $this->actingAs($user)->put(route('vip.center.presence.update'), [
|
|
'custom_join_message' => '不应被保存',
|
|
'custom_leave_message' => '不应被保存',
|
|
]);
|
|
|
|
$response->assertRedirect(route('vip.center'));
|
|
$this->assertDatabaseMissing('users', [
|
|
'id' => $user->id,
|
|
'custom_join_message' => '不应被保存',
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* 初始化支付中心模拟配置
|
|
*/
|
|
private function seedVipPaymentConfig(): void
|
|
{
|
|
Sysparam::updateOrCreate(['alias' => 'vip_payment_enabled'], ['body' => '1']);
|
|
Sysparam::clearCache('vip_payment_enabled');
|
|
Sysparam::updateOrCreate(['alias' => 'vip_payment_base_url'], ['body' => 'https://novalink.test']);
|
|
Sysparam::updateOrCreate(['alias' => 'vip_payment_app_key'], ['body' => 'chatroom-app']);
|
|
Sysparam::updateOrCreate(['alias' => 'vip_payment_app_secret'], ['body' => 'chatroom-secret']);
|
|
Sysparam::updateOrCreate(['alias' => 'vip_payment_timeout'], ['body' => '10']);
|
|
|
|
Sysparam::clearCache('vip_payment_base_url');
|
|
Sysparam::clearCache('vip_payment_app_key');
|
|
Sysparam::clearCache('vip_payment_app_secret');
|
|
Sysparam::clearCache('vip_payment_timeout');
|
|
}
|
|
}
|