Files
social-media/backend/Modules/Memberships/Handlers/SubscribeToCreator.cs

84 lines
2.3 KiB
C#

using Hutopy.Infrastructure.Security;
using Hutopy.Modules.Creators.Contracts;
using Hutopy.Modules.Memberships.Contracts;
using Hutopy.Modules.Memberships.Data;
namespace Hutopy.Modules.Memberships.Handlers;
[PublicAPI]
public class SubscribeRequest
{
public Guid CreatorId { get; set; }
public Guid MembershipTierId { get; set; }
public required string CheckoutSuccessUrl { get; init; }
public required string CheckoutCancelledUrl { get; init; }
}
[PublicAPI]
public record struct SubscriptionResponse(
string StripeCheckoutUrl);
[PublicAPI]
public class SubscribeValidator : Validator<SubscribeRequest>
{
public SubscribeValidator()
{
RuleFor(x => x.MembershipTierId).NotEmpty();
}
}
[PublicAPI]
public class SubscribeHandler(
MembershipsDbContext dbContext,
ICreatorLookup creatorLookup,
IMembershipPaymentProcessor membershipPaymentProcessor)
: Endpoint<SubscribeRequest, SubscriptionResponse>
{
public override void Configure()
{
Post("/api/memberships/subscribe");
Options(o => o.WithTags("Memberships"));
}
public override async Task HandleAsync(
SubscribeRequest req,
CancellationToken ct)
{
MembershipTier? tier = await dbContext
.MembershipTiers
.Where(tier => tier.Id == req.MembershipTierId)
.FirstOrDefaultAsync(ct);
if (tier == null)
{
await SendNotFoundAsync(ct);
return;
}
CreatorReference? creator = await creatorLookup.GetCreatorAsync(tier.CreatorId, ct);
if (creator == null)
{
await SendNotFoundAsync(ct);
return;
}
if (!creator.AcceptCharges)
{
await SendErrorsAsync(StatusCodes.Status400BadRequest, ct);
return;
}
// Process Stripe subscription
MembershipCheckoutSession checkoutSession = await membershipPaymentProcessor.CreateCheckoutSessionAsync(
User.GetUserId(),
creator,
tier.Id,
tier.StripePriceId,
req.CheckoutSuccessUrl,
req.CheckoutCancelledUrl);
await SendOkAsync(
new SubscriptionResponse { StripeCheckoutUrl = checkoutSession.Url },
ct);
}
}