QrRapido/Services/StripeService.cs
Ricardo Carneiro 16a9720a12
All checks were successful
Deploy QR Rapido / test (push) Successful in 59s
Deploy QR Rapido / build-and-push (push) Successful in 9m57s
Deploy QR Rapido / deploy-staging (push) Has been skipped
Deploy QR Rapido / deploy-production (push) Successful in 2m11s
feat: qrcode por creditos.
2026-01-26 20:13:45 -03:00

184 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")
{
// Legacy subscription method - Kept for compatibility but likely unused in Credit model
var user = await _userService.GetUserAsync(userId);
if (user == null) throw new Exception("User not found");
var options = new SessionCreateOptions
{
PaymentMethodTypes = new List<string> { "card" },
Mode = "subscription",
LineItems = new List<SessionLineItemOptions>
{
new SessionLineItemOptions { Price = priceId, Quantity = 1 }
},
Customer = user.StripeCustomerId, // Might be null, legacy logic handled creation
ClientReferenceId = userId,
SuccessUrl = $"{_config["App:BaseUrl"]}/Pagamento/Sucesso",
CancelUrl = $"{_config["App:BaseUrl"]}/Pagamento/SelecaoPlano",
};
var service = new SessionService();
var session = await service.CreateAsync(options);
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":
if (stripeEvent.Data.Object is Session session)
{
// 1. Handle One-Time Payment (Credits)
if (session.Mode == "payment" && session.PaymentStatus == "paid")
{
await ProcessCreditPayment(session);
}
// 2. Handle Subscription (Legacy)
else if (session.SubscriptionId != null)
{
var subscriptionService = new SubscriptionService();
var subscription = await subscriptionService.GetAsync(session.SubscriptionId);
var userId = session.ClientReferenceId ??
(session.Metadata != null && session.Metadata.ContainsKey("user_id") ? session.Metadata["user_id"] : null);
if (!string.IsNullOrEmpty(userId))
{
await ProcessSubscriptionActivation(userId, subscription);
}
}
}
break;
case "invoice.finalized":
// Legacy subscription logic
if (stripeEvent.Data.Object is Invoice invoice)
{
var subscriptionLineItem = invoice.Lines?.Data
.FirstOrDefault(line => !string.IsNullOrEmpty(line.SubscriptionId));
if (subscriptionLineItem != null)
{
var subscriptionService = new SubscriptionService();
var subscription = await subscriptionService.GetAsync(subscriptionLineItem.SubscriptionId);
var user = await _userService.GetUserByStripeCustomerIdAsync(subscription.CustomerId);
if (user != null)
{
await ProcessSubscriptionActivation(user.Id, subscription);
}
}
}
break;
case "customer.subscription.deleted":
if (stripeEvent.Data.Object is Subscription deletedSubscription)
{
await _userService.DeactivatePremiumStatus(deletedSubscription.Id);
}
break;
}
}
private async Task ProcessCreditPayment(Session session)
{
if (session.Metadata != null &&
session.Metadata.TryGetValue("user_id", out var userId) &&
session.Metadata.TryGetValue("credits_amount", out var creditsStr) &&
int.TryParse(creditsStr, out var credits))
{
var success = await _userService.AddCreditsAsync(userId, credits);
if (success)
{
_logger.LogInformation($"✅ Credits added via Stripe: {credits} credits for user {userId}");
}
else
{
_logger.LogError($"❌ Failed to add credits for user {userId}");
}
}
else
{
_logger.LogWarning("⚠️ Payment received but missing metadata (user_id or credits_amount)");
}
}
private async Task ProcessSubscriptionActivation(string userId, Subscription subscription)
{
var service = new SubscriptionItemService();
var subItem = service.Get(subscription.Items.Data[0].Id);
var user = await _userService.GetUserAsync(userId);
if (user == null) return;
if (string.IsNullOrEmpty(user.StripeCustomerId))
{
await _userService.UpdateUserStripeCustomerIdAsync(user.Id, subscription.CustomerId);
}
await _userService.ActivatePremiumStatus(userId, subscription.Id, subItem.CurrentPeriodEnd);
}
// Helper methods for legacy support
public async Task<bool> CancelSubscriptionAsync(string subscriptionId)
{
try {
var service = new SubscriptionService();
await service.CancelAsync(subscriptionId, new SubscriptionCancelOptions());
return true;
} catch { return false; }
}
public async Task DeactivatePremiumStatus(string subscriptionId) => await _userService.DeactivatePremiumStatus(subscriptionId);
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
{
return "Unknown";
}
}
public async Task<(bool success, string message)> CancelAndRefundSubscriptionAsync(string userId)
{
// Legacy method - no longer applicable for credit system
return (false, "Sistema migrado para créditos. Entre em contato com o suporte.");
}
}
}