QrRapido/Services/StripeService.cs
Ricardo Carneiro b54aa295ac
All checks were successful
Deploy QR Rapido / test (push) Successful in 44s
Deploy QR Rapido / build-and-push (push) Successful in 12m59s
Deploy QR Rapido / deploy-staging (push) Has been skipped
Deploy QR Rapido / deploy-production (push) Successful in 1m24s
fix: add Node.js to Docker build stage for frontend compilation
- Install Node.js 18.x in Docker build stage
- Add MongoDB DataProtection for Swarm compatibility
- Enables shared authentication keys across multiple replicas

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-22 16:25:54 -03:00

194 lines
7.7 KiB
C#

using Stripe;
using Stripe.Checkout;
using QRRapidoApp.Models;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using System.Collections.Generic;
using System;
namespace QRRapidoApp.Services
{
public class StripeService
{
private readonly IConfiguration _config;
private readonly IUserService _userService;
private readonly ILogger<StripeService> _logger;
public StripeService(IConfiguration config, IUserService userService, ILogger<StripeService> logger)
{
_config = config;
_userService = userService;
_logger = logger;
StripeConfiguration.ApiKey = _config["Stripe:SecretKey"];
}
public async Task<string> CreateCheckoutSessionAsync(string userId, string priceId, string lang = "pt-BR")
{
var user = await _userService.GetUserAsync(userId);
if (user == null)
{
throw new Exception("User not found");
}
var customerId = user.StripeCustomerId;
if (string.IsNullOrEmpty(customerId))
{
var customerOptions = new CustomerCreateOptions
{
Email = user.Email,
Name = user.Name,
Metadata = new Dictionary<string, string> { { "app_user_id", user.Id } }
};
var customerService = new CustomerService();
var customer = await customerService.CreateAsync(customerOptions);
customerId = customer.Id;
await _userService.UpdateUserStripeCustomerIdAsync(userId, customerId);
}
var options = new SessionCreateOptions
{
PaymentMethodTypes = new List<string> { "card" },
Mode = "subscription",
LineItems = new List<SessionLineItemOptions>
{
new SessionLineItemOptions { Price = priceId, Quantity = 1 }
},
Customer = customerId,
ClientReferenceId = userId,
SuccessUrl = $"{_config["App:BaseUrl"]}/Pagamento/Sucesso",
CancelUrl = $"{_config["App:BaseUrl"]}/{lang}/Pagamento/SelecaoPlano",
AllowPromotionCodes = true,
Metadata = new Dictionary<string, string> { { "user_id", userId } }
};
var service = new SessionService();
var session = await service.CreateAsync(options);
_logger.LogInformation($"Created Stripe checkout session {session.Id} for user {userId}");
return session.Url;
}
public async Task HandleWebhookAsync(string json, string signature)
{
var webhookSecret = _config["Stripe:WebhookSecret"];
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?.SubscriptionId != null)
{
var subscriptionService = new SubscriptionService();
var subscription = await subscriptionService.GetAsync(session.SubscriptionId);
await ProcessSubscriptionActivation(session.ClientReferenceId, subscription);
}
break;
case "invoice.finalized":
var invoice = stripeEvent.Data.Object as Invoice;
var subscriptionLineItem = invoice.Lines?.Data
.FirstOrDefault(line =>
!string.IsNullOrEmpty(line.SubscriptionId) ||
line.Subscription != null
);
string subscriptionId = null;
if (subscriptionLineItem != null)
{
// Tenta obter o ID da assinatura de duas formas diferentes
subscriptionId = subscriptionLineItem.SubscriptionId
?? subscriptionLineItem.Subscription?.Id;
}
if (subscriptionId != null)
{
var subscriptionService = new SubscriptionService();
var subscription = await subscriptionService.GetAsync(subscriptionId);
var user = await _userService.GetUserByStripeCustomerIdAsync(subscription.CustomerId);
if (user != null)
{
await ProcessSubscriptionActivation(user.Id, subscription);
}
}
break;
case "customer.subscription.deleted":
var deletedSubscription = stripeEvent.Data.Object as Subscription;
if (deletedSubscription != null)
{
await _userService.DeactivatePremiumStatus(deletedSubscription.Id);
}
break;
default:
_logger.LogWarning($"Unhandled Stripe webhook event type: {stripeEvent.Type}");
break;
}
}
private async Task ProcessSubscriptionActivation(string userId, Subscription subscription)
{
var service = new SubscriptionItemService();
var subItem = service.Get(subscription.Items.Data[0].Id);
if (string.IsNullOrEmpty(userId) || subscription == null)
{
_logger.LogWarning("Could not process subscription activation due to missing userId or subscription data.");
return;
}
var user = await _userService.GetUserAsync(userId);
if (user == null)
{
_logger.LogWarning($"User not found for premium activation: {userId}");
return;
}
if (string.IsNullOrEmpty(user.StripeCustomerId))
{
await _userService.UpdateUserStripeCustomerIdAsync(user.Id, subscription.CustomerId);
}
await _userService.ActivatePremiumStatus(userId, subscription.Id, subItem.CurrentPeriodEnd);
_logger.LogInformation($"Successfully processed premium activation/renewal for user {userId}.");
}
public async Task<string> 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}");
return "Unknown";
}
}
public async Task<bool> CancelSubscriptionAsync(string subscriptionId)
{
try
{
var service = new SubscriptionService();
await service.CancelAsync(subscriptionId, new SubscriptionCancelOptions());
_logger.LogInformation($"Canceled subscription {subscriptionId} via API.");
return true;
}
catch (Exception ex)
{
_logger.LogError(ex, $"Error canceling subscription {subscriptionId}");
return false;
}
}
}
}