feat: Add Stripe payment integration and subscription management

- Add Stripe.net SDK for payment processing
- Implement StripeService with checkout sessions, customer portal, webhooks
- Implement SubscriptionService for quota management
- Add quota enforcement to CVCheckService
- Create Pricing, Billing, Settings pages
- Add checkout success/cancel pages
- Update Check and Dashboard with usage indicators
- Add ResetMonthlyUsageJob for billing cycle resets
- Add database migration for subscription fields

Plan tiers: Free (3 checks), Professional £49/mo (30), Enterprise £199/mo (unlimited)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-21 12:03:24 +00:00
parent 6f384f8d09
commit 28d7d41b25
27 changed files with 2427 additions and 1 deletions

View File

@@ -8,6 +8,7 @@ using TrueCV.Application.Interfaces;
using TrueCV.Application.Models;
using TrueCV.Domain.Entities;
using TrueCV.Domain.Enums;
using TrueCV.Domain.Exceptions;
using TrueCV.Infrastructure.Data;
using TrueCV.Infrastructure.Jobs;
@@ -19,6 +20,7 @@ public sealed class CVCheckService : ICVCheckService
private readonly IFileStorageService _fileStorageService;
private readonly IBackgroundJobClient _backgroundJobClient;
private readonly IAuditService _auditService;
private readonly ISubscriptionService _subscriptionService;
private readonly ILogger<CVCheckService> _logger;
public CVCheckService(
@@ -26,12 +28,14 @@ public sealed class CVCheckService : ICVCheckService
IFileStorageService fileStorageService,
IBackgroundJobClient backgroundJobClient,
IAuditService auditService,
ISubscriptionService subscriptionService,
ILogger<CVCheckService> logger)
{
_dbContext = dbContext;
_fileStorageService = fileStorageService;
_backgroundJobClient = backgroundJobClient;
_auditService = auditService;
_subscriptionService = subscriptionService;
_logger = logger;
}
@@ -42,6 +46,13 @@ public sealed class CVCheckService : ICVCheckService
_logger.LogDebug("Creating CV check for user {UserId}, file: {FileName}", userId, fileName);
// Check quota before proceeding
if (!await _subscriptionService.CanPerformCheckAsync(userId))
{
_logger.LogWarning("User {UserId} quota exceeded - CV check denied", userId);
throw new QuotaExceededException();
}
// Upload file to blob storage
var blobUrl = await _fileStorageService.UploadAsync(file, fileName);
@@ -71,6 +82,9 @@ public sealed class CVCheckService : ICVCheckService
await _auditService.LogAsync(userId, AuditActions.CVUploaded, "CVCheck", cvCheck.Id, $"File: {fileName}");
// Increment usage after successful creation
await _subscriptionService.IncrementUsageAsync(userId);
return cvCheck.Id;
}