125 lines
3.5 KiB
C#
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,
|
|
string CheckoutSuccessUrl,
|
|
string CheckoutCancelledUrl);
|
|
|
|
internal record Response(
|
|
string Id,
|
|
string 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, checkout.Url),
|
|
cancellation: ct)
|
|
.ConfigureAwait(false);
|
|
}
|
|
}
|
|
}
|