set('checkout.fraud.window_hours', 12); $tenant = Tenant::factory()->create(); $user = User::factory()->for($tenant)->create(); $package = Package::factory()->create([ 'price' => 120, ]); $coupon = Coupon::factory()->create([ 'code' => 'SAVE10', 'paddle_discount_id' => 'dsc_123', ]); $coupon->packages()->attach($package); $session = CheckoutSession::create([ 'id' => (string) Str::uuid(), 'user_id' => $user->id, 'tenant_id' => $tenant->id, 'package_id' => $package->id, 'package_snapshot' => [ 'id' => $package->id, 'name' => $package->name, 'type' => $package->type, 'price' => (float) $package->price, 'currency' => $package->currency ?? 'EUR', 'features' => $package->features, 'limits' => $package->limits, ], 'coupon_id' => $coupon->id, 'coupon_code' => $coupon->code, 'coupon_snapshot' => [ 'coupon' => [ 'id' => $coupon->id, 'code' => $coupon->code, 'type' => $coupon->type?->value, ], 'pricing' => [ 'subtotal' => 120, 'discount' => 12, 'total' => 108, ], ], 'status' => CheckoutSession::STATUS_PROCESSING, 'provider' => CheckoutSession::PROVIDER_PADDLE, 'currency' => 'EUR', 'amount_subtotal' => 120, 'amount_total' => 108, 'amount_discount' => 12, 'ip_address' => '203.0.113.10', 'device_id' => 'device-123', 'user_agent' => 'Mozilla/5.0', ]); $giftVouchers = $this->createMock(GiftVoucherService::class); $giftVouchers->expects($this->once()) ->method('markRedeemed') ->with($this->callback(fn (Coupon $provided) => $provided->is($coupon)), 'txn_123'); $service = new CouponRedemptionService($giftVouchers); $service->recordSuccess($session, ['id' => 'txn_123']); $this->assertDatabaseHas('coupon_redemptions', [ 'coupon_id' => $coupon->id, 'checkout_session_id' => $session->id, 'ip_address' => '203.0.113.10', 'device_id' => 'device-123', 'user_agent' => 'Mozilla/5.0', 'status' => CouponRedemption::STATUS_SUCCESS, ]); $redemption = CouponRedemption::query() ->where('checkout_session_id', $session->id) ->first(); $this->assertNotNull($redemption); $this->assertSame(12, $redemption->metadata['fraud']['window_hours'] ?? null); $this->assertSame('203.0.113.10', $redemption->metadata['fraud']['ip']['value'] ?? null); $this->assertSame('device-123', $redemption->metadata['fraud']['device']['value'] ?? null); } }