Files
social-media/backend/Modules/Tipping/Handlers/SendTip.cs

125 lines
3.5 KiB
C#

using Hutopy.Infrastructure.Security;
using Hutopy.Modules.Creators.Contracts;
using Hutopy.Modules.Tipping.Contracts;
using Hutopy.Modules.Tipping.Data;
namespace Hutopy.Modules.Tipping.Handlers;
[PublicAPI]
internal static class SendTip
{
internal record Request(
Guid CreatorId,
decimal Amount,
string Currency,
string Message,
Uri CheckoutSuccessUrl,
Uri CheckoutCancelledUrl);
internal record Response(
string Id,
Uri Url);
internal class Validator : Validator<Request>
{
public Validator()
{
RuleFor(x => x.Amount)
.GreaterThan(0)
.WithMessage("Tip amount must be greater than 0");
RuleFor(x => x.CreatorId)
.NotEmpty()
.WithMessage("Creator ID is required");
RuleFor(x => x.CheckoutSuccessUrl)
.NotEmpty()
.WithMessage("CheckoutSuccessUrl is required");
RuleFor(x => x.CheckoutCancelledUrl)
.NotEmpty()
.WithMessage("CheckoutCancelledUrl is required");
}
}
internal class Handler(
TippingDbContext dbContext,
ITipProcessor tipProcessor,
ICreatorLookup creatorLookup)
: Endpoint<Request, Response>
{
private static readonly Guid AnonymousUserId = Guid.Parse("AAAAAAAA-0000-0000-0000-000000000000");
public override void Configure()
{
Post("/api/tips");
Options(o => o.WithTags("Memberships"));
AllowAnonymous();
}
public override async Task HandleAsync(
Request req,
CancellationToken ct)
{
ArgumentNullException.ThrowIfNull(req);
var userId = User.Identity?.IsAuthenticated == true
? User.GetUserId()
: AnonymousUserId;
var creator = await creatorLookup
.GetCreatorAsync(req.CreatorId, ct)
.ConfigureAwait(false);
if (creator == null)
{
await SendNotFoundAsync(ct).ConfigureAwait(false);
return;
}
if (!creator.AcceptCharges)
{
await SendErrorsAsync(StatusCodes.Status400BadRequest, ct).ConfigureAwait(false);
return;
}
var tipId = Guid.CreateVersion7();
var checkout = await tipProcessor
.CreateCheckoutSessionAsync(
tipId,
creator,
req.Amount,
req.Currency,
req.Message,
req.CheckoutSuccessUrl,
req.CheckoutCancelledUrl,
ct)
.ConfigureAwait(false);
Tip tip = new()
{
Id = tipId,
CreatedAt = DateTimeOffset.UtcNow,
StripeSessionId = checkout.Id,
CreatedBy = userId,
CreatorId = req.CreatorId,
Status = TipStatus.Pending,
Amount = req.Amount,
Currency = req.Currency,
Message = req.Message
};
dbContext.Tips.Add(tip);
await dbContext.SaveChangesAsync(ct).ConfigureAwait(false);
await SendAsync(
new Response(checkout.Id, new Uri(checkout.Url)),
cancellation: ct)
.ConfigureAwait(false);
}
}
}