using Stripe; using Stripe.Checkout; using QRRapidoApp.Models; namespace QRRapidoApp.Services { public class StripeService { private readonly IConfiguration _config; private readonly IUserService _userService; private readonly ILogger _logger; public StripeService(IConfiguration config, IUserService userService, ILogger logger) { _config = config; _userService = userService; _logger = logger; } public async Task CreateCheckoutSessionAsync(string userId, string priceId) { try { var options = new SessionCreateOptions { PaymentMethodTypes = new List { "card" }, Mode = "subscription", LineItems = new List { new SessionLineItemOptions { Price = priceId, Quantity = 1 } }, ClientReferenceId = userId, SuccessUrl = $"{_config["App:BaseUrl"]}/Premium/Success?session_id={{CHECKOUT_SESSION_ID}}", CancelUrl = $"{_config["App:BaseUrl"]}/Premium/Cancel", CustomerEmail = await _userService.GetUserEmailAsync(userId), AllowPromotionCodes = true, BillingAddressCollection = "auto", Metadata = new Dictionary { { "userId", userId }, { "product", "QR Rapido Premium" } } }; var service = new SessionService(); var session = await service.CreateAsync(options); _logger.LogInformation($"Created Stripe checkout session for user {userId}: {session.Id}"); return session.Url; } catch (Exception ex) { _logger.LogError(ex, $"Error creating Stripe checkout session for user {userId}: {ex.Message}"); throw; } } public async Task HandleWebhookAsync(string json, string signature) { var webhookSecret = _config["Stripe:WebhookSecret"]; try { var stripeEvent = EventUtility.ConstructEvent(json, signature, webhookSecret); _logger.LogInformation($"Processing Stripe webhook: {stripeEvent.Type}"); switch (stripeEvent.Type) { case "checkout.session.completed": var session = stripeEvent.Data.Object as Session; if (session != null) { await ActivatePremiumAsync(session.ClientReferenceId, session.CustomerId, session.SubscriptionId); } break; case "invoice.payment_succeeded": var invoice = stripeEvent.Data.Object as Invoice; if (invoice != null && invoice.SubscriptionId != null) { await RenewPremiumSubscriptionAsync(invoice.SubscriptionId); } break; case "invoice.payment_failed": var failedInvoice = stripeEvent.Data.Object as Invoice; if (failedInvoice != null && failedInvoice.SubscriptionId != null) { await HandleFailedPaymentAsync(failedInvoice.SubscriptionId); } break; case "customer.subscription.deleted": var deletedSubscription = stripeEvent.Data.Object as Subscription; if (deletedSubscription != null) { await DeactivatePremiumAsync(deletedSubscription); } break; case "customer.subscription.updated": var updatedSubscription = stripeEvent.Data.Object as Subscription; if (updatedSubscription != null) { await UpdateSubscriptionAsync(updatedSubscription); } break; default: _logger.LogWarning($"Unhandled Stripe webhook event type: {stripeEvent.Type}"); break; } } catch (StripeException ex) { _logger.LogError(ex, $"Stripe webhook error: {ex.Message}"); throw; } catch (Exception ex) { _logger.LogError(ex, $"Error processing Stripe webhook: {ex.Message}"); throw; } } private async Task ActivatePremiumAsync(string? userId, string? customerId, string? subscriptionId) { if (string.IsNullOrEmpty(userId)) return; try { var user = await _userService.GetUserAsync(userId); if (user == null) { _logger.LogWarning($"User not found for premium activation: {userId}"); return; } user.IsPremium = true; user.StripeCustomerId = customerId; user.StripeSubscriptionId = subscriptionId; user.PremiumExpiresAt = DateTime.UtcNow.AddDays(32); // Buffer for billing cycles await _userService.UpdateUserAsync(user); _logger.LogInformation($"Activated premium for user {userId}"); } catch (Exception ex) { _logger.LogError(ex, $"Error activating premium for user {userId}: {ex.Message}"); } } private async Task RenewPremiumSubscriptionAsync(string subscriptionId) { try { // Find user by subscription ID var user = await FindUserBySubscriptionIdAsync(subscriptionId); if (user == null) return; // Extend premium expiry user.PremiumExpiresAt = DateTime.UtcNow.AddDays(32); await _userService.UpdateUserAsync(user); _logger.LogInformation($"Renewed premium subscription for user {user.Id}"); } catch (Exception ex) { _logger.LogError(ex, $"Error renewing premium subscription {subscriptionId}: {ex.Message}"); } } private async Task HandleFailedPaymentAsync(string subscriptionId) { try { var user = await FindUserBySubscriptionIdAsync(subscriptionId); if (user == null) return; // Don't immediately deactivate - Stripe will retry _logger.LogWarning($"Payment failed for user {user.Id}, subscription {subscriptionId}"); // Could send notification email here } catch (Exception ex) { _logger.LogError(ex, $"Error handling failed payment for subscription {subscriptionId}: {ex.Message}"); } } private async Task DeactivatePremiumAsync(Subscription subscription) { try { var user = await FindUserBySubscriptionIdAsync(subscription.Id); if (user == null) return; // ADICIONAR: marcar data de cancelamento await _userService.MarkPremiumCancelledAsync(user.Id, DateTime.UtcNow); _logger.LogInformation($"Deactivated premium for user {user.Id}"); } catch (Exception ex) { _logger.LogError(ex, $"Error deactivating premium for subscription {subscription.Id}: {ex.Message}"); } } private async Task UpdateSubscriptionAsync(Subscription subscription) { try { var user = await FindUserBySubscriptionIdAsync(subscription.Id); if (user == null) return; // Update based on subscription status if (subscription.Status == "active") { user.IsPremium = true; user.PremiumExpiresAt = subscription.CurrentPeriodEnd.AddDays(2); // Small buffer } else if (subscription.Status == "canceled" || subscription.Status == "unpaid") { user.IsPremium = false; user.PremiumExpiresAt = DateTime.UtcNow; } await _userService.UpdateUserAsync(user); _logger.LogInformation($"Updated subscription for user {user.Id}: {subscription.Status}"); } catch (Exception ex) { _logger.LogError(ex, $"Error updating subscription {subscription.Id}: {ex.Message}"); } } private async Task FindUserBySubscriptionIdAsync(string subscriptionId) { try { // This would require implementing a method in UserService to find by subscription ID // For now, we'll leave this as a placeholder return null; } catch (Exception ex) { _logger.LogError(ex, $"Error finding user by subscription ID {subscriptionId}: {ex.Message}"); return null; } } public async Task GetSubscriptionStatusAsync(string? subscriptionId) { if (string.IsNullOrEmpty(subscriptionId)) return "None"; try { var service = new SubscriptionService(); var subscription = await service.GetAsync(subscriptionId); return subscription.Status; } catch (Exception ex) { _logger.LogError(ex, $"Error getting subscription status for {subscriptionId}: {ex.Message}"); return "Unknown"; } } public async Task CancelSubscriptionAsync(string subscriptionId) { try { var service = new SubscriptionService(); var subscription = await service.CancelAsync(subscriptionId, new SubscriptionCancelOptions { InvoiceNow = false, Prorate = false }); _logger.LogInformation($"Canceled subscription {subscriptionId}"); return true; } catch (Exception ex) { _logger.LogError(ex, $"Error canceling subscription {subscriptionId}: {ex.Message}"); return false; } } } }