feat: add vip payment and member center
This commit is contained in:
@@ -0,0 +1,189 @@
|
||||
<?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 Tests\TestCase;
|
||||
|
||||
class VipPaymentIntegrationTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
/**
|
||||
* 测试用户可以发起 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,
|
||||
]);
|
||||
|
||||
$response->assertRedirect('https://novalink.test/payment/checkout/alipay/PO202604111200001234');
|
||||
$this->assertDatabaseHas('vip_payment_orders', [
|
||||
'user_id' => $user->id,
|
||||
'vip_level_id' => $vipLevel->id,
|
||||
'payment_order_no' => 'PO202604111200001234',
|
||||
'status' => 'pending',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试异步回调只会为同一笔订单开通一次会员,避免重复叠加时长
|
||||
*/
|
||||
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,
|
||||
'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' => 'alipay',
|
||||
'provider_trade_no' => '2026041122001499999999999999',
|
||||
'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',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试会员中心页面只展示当前用户自己的购买记录
|
||||
*/
|
||||
public function test_vip_center_only_shows_current_user_records(): void
|
||||
{
|
||||
$currentUser = User::factory()->create(['username' => 'current-user']);
|
||||
$otherUser = User::factory()->create(['username' => 'other-user']);
|
||||
$vipLevel = VipLevel::factory()->create([
|
||||
'name' => '星耀会员',
|
||||
'price' => 66,
|
||||
'duration_days' => 30,
|
||||
]);
|
||||
|
||||
VipPaymentOrder::factory()->create([
|
||||
'user_id' => $currentUser->id,
|
||||
'vip_level_id' => $vipLevel->id,
|
||||
'vip_name' => $vipLevel->name,
|
||||
'status' => 'paid',
|
||||
'order_no' => 'VPO_CURRENT_001',
|
||||
'merchant_order_no' => 'MER_CURRENT_001',
|
||||
]);
|
||||
|
||||
VipPaymentOrder::factory()->create([
|
||||
'user_id' => $otherUser->id,
|
||||
'vip_level_id' => $vipLevel->id,
|
||||
'vip_name' => $vipLevel->name,
|
||||
'status' => 'paid',
|
||||
'order_no' => 'VPO_OTHER_001',
|
||||
'merchant_order_no' => 'MER_OTHER_001',
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($currentUser)->get(route('vip.center'));
|
||||
|
||||
$response->assertOk();
|
||||
$response->assertSee('VPO_CURRENT_001');
|
||||
$response->assertDontSee('VPO_OTHER_001');
|
||||
$response->assertSee('我的购买记录');
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入测试所需的支付中心配置
|
||||
*/
|
||||
private function seedVipPaymentConfig(): void
|
||||
{
|
||||
// 这些配置与聊天室后台保存的数据结构保持一致,方便直接复用真实业务代码。
|
||||
Sysparam::query()->updateOrCreate(['alias' => 'vip_payment_enabled'], ['body' => '1']);
|
||||
Sysparam::query()->updateOrCreate(['alias' => 'vip_payment_base_url'], ['body' => 'https://novalink.test']);
|
||||
Sysparam::query()->updateOrCreate(['alias' => 'vip_payment_app_key'], ['body' => 'chatroom-app']);
|
||||
Sysparam::query()->updateOrCreate(['alias' => 'vip_payment_app_secret'], ['body' => 'chatroom-secret']);
|
||||
Sysparam::query()->updateOrCreate(['alias' => 'vip_payment_timeout'], ['body' => '10']);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user