feat: added notifications

This commit is contained in:
Damien Ostler 2024-02-22 20:36:37 -05:00
parent 04861b6b58
commit 76623668ad
15 changed files with 1245 additions and 19 deletions

View File

@ -10,6 +10,8 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;
using Novu;
using Novu.DTO.Events;
namespace comissions.app.api.Controllers;
@ -20,10 +22,11 @@ public class ArtistController : Controller
private readonly ApplicationDbContext _dbContext;
private readonly IStorageService _storageService;
private readonly IPaymentService _paymentService;
private readonly NovuClient _client;
public ArtistController(ApplicationDbContext dbContext, IPaymentService paymentService, IStorageService storageService)
public ArtistController(ApplicationDbContext dbContext, IPaymentService paymentService, IStorageService storageService, NovuClient client)
{
_client = client;
_paymentService = paymentService;
_storageService = storageService;
_dbContext = dbContext;
@ -67,6 +70,16 @@ public class ArtistController : Controller
updatedArtist = _dbContext.UserArtists.Update(updatedArtist).Entity;
await _dbContext.SaveChangesAsync();
var result = updatedArtist.ToModel();
_client.Event.Trigger(new EventCreateData()
{
EventName = "ArtistUpdated",
To =
{
SubscriberId = userId,
}
});
return Ok(result);
}

View File

@ -3,10 +3,13 @@ using comissions.app.api.Services.Payment;
using comissions.app.api.Services.Storage;
using comissions.app.database;
using comissions.app.database.Entities;
using comissions.app.database.Enums;
using comissions.app.database.Models.Request;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Stripe;
using Stripe.Checkout;
namespace comissions.app.api.Controllers;
@ -18,14 +21,643 @@ public class RequestsController : Controller
private readonly IStorageService _storageService;
private readonly IPaymentService _paymentService;
private readonly string _webHookSecret;
public RequestsController(ApplicationDbContext dbContext, IPaymentService paymentService, IStorageService storageService)
public RequestsController(ApplicationDbContext dbContext, IPaymentService paymentService, IStorageService storageService, IConfiguration configuration)
{
_webHookSecret = configuration.GetValue<string>("Stripe:WebHookSecret");
_paymentService = paymentService;
_storageService = storageService;
_dbContext = dbContext;
}
[Route("PaymentWebhook")]
[HttpPost("PaymentWebhook")]
[AllowAnonymous]
public async Task<IActionResult> ProcessWebhookEvent()
{
var json = await new StreamReader(HttpContext.Request.Body).ReadToEndAsync();
// If you are testing your webhook locally with the Stripe CLI you
// can find the endpoint's secret by running `stripe listen`
// Otherwise, find your endpoint's secret in your webhook settings
// in the Developer Dashboard
var stripeEvent = EventUtility.ConstructEvent(json, Request.Headers["Stripe-Signature"], _webHookSecret);
if (stripeEvent.Type == Events.CheckoutSessionExpired)
{
var session = stripeEvent.Data.Object as Session;
var connectedAccountId = stripeEvent.Account;
var requestId = session.LineItems.First().Price.Product.Name;
var request = await _dbContext.Requests
.Include(x=>x.Artist)
.Include(x=>x.User)
.FirstOrDefaultAsync(x=>x.Id==int.Parse(requestId));
if (request != null && request.Accepted && !request.Declined && !request.Completed &&
request.Artist.StripeAccountId == connectedAccountId)
{
request.PaymentUrl = null;
}
}
else if (stripeEvent.Type == Events.CheckoutSessionCompleted)
{
var session = stripeEvent.Data.Object as Session;
var connectedAccountId = stripeEvent.Account;
var requestId = session.Metadata["/OrderId"];
var request = await _dbContext.Requests
.Include(x=>x.Artist)
.FirstOrDefaultAsync(x=>x.Id==int.Parse(requestId));
if (request != null && request.Accepted && !request.Declined && !request.Completed &&
request.Artist.StripeAccountId == connectedAccountId)
{
request.Paid = true;
request.PaidDate = DateTime.UtcNow;
}
}
else if (stripeEvent.Type == Events.AccountUpdated)
{
}
else if (stripeEvent.Type == Events.AccountApplicationAuthorized)
{
}
else if (stripeEvent.Type == Events.AccountApplicationDeauthorized)
{
}
else if (stripeEvent.Type == Events.AccountExternalAccountCreated)
{
}
else if (stripeEvent.Type == Events.AccountExternalAccountDeleted)
{
}
else if (stripeEvent.Type == Events.AccountExternalAccountUpdated)
{
}
else if (stripeEvent.Type == Events.ApplicationFeeCreated)
{
}
else if (stripeEvent.Type == Events.ApplicationFeeRefunded)
{
}
else if (stripeEvent.Type == Events.ApplicationFeeRefundUpdated)
{
}
else if (stripeEvent.Type == Events.BalanceAvailable)
{
}
else if (stripeEvent.Type == Events.BillingPortalConfigurationCreated)
{
}
else if (stripeEvent.Type == Events.BillingPortalConfigurationUpdated)
{
}
else if (stripeEvent.Type == Events.BillingPortalSessionCreated)
{
}
else if (stripeEvent.Type == Events.CapabilityUpdated)
{
}
else if (stripeEvent.Type == Events.CashBalanceFundsAvailable)
{
}
else if (stripeEvent.Type == Events.ChargeCaptured)
{
}
else if (stripeEvent.Type == Events.ChargeExpired)
{
}
else if (stripeEvent.Type == Events.ChargeFailed)
{
}
else if (stripeEvent.Type == Events.ChargePending)
{
}
else if (stripeEvent.Type == Events.ChargeRefunded)
{
}
else if (stripeEvent.Type == Events.ChargeSucceeded)
{
}
else if (stripeEvent.Type == Events.ChargeUpdated)
{
}
else if (stripeEvent.Type == Events.ChargeDisputeClosed)
{
}
else if (stripeEvent.Type == Events.ChargeDisputeCreated)
{
}
else if (stripeEvent.Type == Events.ChargeDisputeFundsReinstated)
{
}
else if (stripeEvent.Type == Events.ChargeDisputeFundsWithdrawn)
{
}
else if (stripeEvent.Type == Events.ChargeDisputeUpdated)
{
}
else if (stripeEvent.Type == Events.ChargeRefundUpdated)
{
}
else if (stripeEvent.Type == Events.CheckoutSessionAsyncPaymentFailed)
{
}
else if (stripeEvent.Type == Events.CheckoutSessionAsyncPaymentSucceeded)
{
}
else if (stripeEvent.Type == Events.CheckoutSessionCompleted)
{
}
else if (stripeEvent.Type == Events.CheckoutSessionExpired)
{
}
else if (stripeEvent.Type == Events.ClimateOrderCanceled)
{
}
else if (stripeEvent.Type == Events.ClimateOrderCreated)
{
}
else if (stripeEvent.Type == Events.ClimateOrderDelayed)
{
}
else if (stripeEvent.Type == Events.ClimateOrderDelivered)
{
}
else if (stripeEvent.Type == Events.ClimateOrderProductSubstituted)
{
}
else if (stripeEvent.Type == Events.ClimateProductCreated)
{
}
else if (stripeEvent.Type == Events.ClimateProductPricingUpdated)
{
}
else if (stripeEvent.Type == Events.CouponCreated)
{
}
else if (stripeEvent.Type == Events.CouponDeleted)
{
}
else if (stripeEvent.Type == Events.CouponUpdated)
{
}
else if (stripeEvent.Type == Events.CreditNoteCreated)
{
}
else if (stripeEvent.Type == Events.CreditNoteUpdated)
{
}
else if (stripeEvent.Type == Events.CreditNoteVoided)
{
}
else if (stripeEvent.Type == Events.CustomerCreated)
{
}
else if (stripeEvent.Type == Events.CustomerDeleted)
{
}
else if (stripeEvent.Type == Events.CustomerUpdated)
{
}
else if (stripeEvent.Type == Events.CustomerDiscountCreated)
{
}
else if (stripeEvent.Type == Events.CustomerDiscountDeleted)
{
}
else if (stripeEvent.Type == Events.CustomerDiscountUpdated)
{
}
else if (stripeEvent.Type == Events.CustomerSourceCreated)
{
}
else if (stripeEvent.Type == Events.CustomerSourceDeleted)
{
}
else if (stripeEvent.Type == Events.CustomerSourceExpiring)
{
}
else if (stripeEvent.Type == Events.CustomerSourceUpdated)
{
}
else if (stripeEvent.Type == Events.CustomerSubscriptionCreated)
{
}
else if (stripeEvent.Type == Events.CustomerSubscriptionDeleted)
{
}
else if (stripeEvent.Type == Events.CustomerSubscriptionPaused)
{
}
else if (stripeEvent.Type == Events.CustomerSubscriptionPendingUpdateApplied)
{
}
else if (stripeEvent.Type == Events.CustomerSubscriptionPendingUpdateExpired)
{
}
else if (stripeEvent.Type == Events.CustomerSubscriptionResumed)
{
}
else if (stripeEvent.Type == Events.CustomerSubscriptionTrialWillEnd)
{
}
else if (stripeEvent.Type == Events.CustomerSubscriptionUpdated)
{
}
else if (stripeEvent.Type == Events.CustomerTaxIdCreated)
{
}
else if (stripeEvent.Type == Events.CustomerTaxIdDeleted)
{
}
else if (stripeEvent.Type == Events.CustomerTaxIdUpdated)
{
}
else if (stripeEvent.Type == Events.CustomerCashBalanceTransactionCreated)
{
}
else if (stripeEvent.Type == Events.FileCreated)
{
}
else if (stripeEvent.Type == Events.FinancialConnectionsAccountCreated)
{
}
else if (stripeEvent.Type == Events.FinancialConnectionsAccountDeactivated)
{
}
else if (stripeEvent.Type == Events.FinancialConnectionsAccountDisconnected)
{
}
else if (stripeEvent.Type == Events.FinancialConnectionsAccountReactivated)
{
}
else if (stripeEvent.Type == Events.FinancialConnectionsAccountRefreshedBalance)
{
}
else if (stripeEvent.Type == Events.FinancialConnectionsAccountRefreshedTransactions)
{
}
else if (stripeEvent.Type == Events.IdentityVerificationSessionCanceled)
{
}
else if (stripeEvent.Type == Events.IdentityVerificationSessionCreated)
{
}
else if (stripeEvent.Type == Events.IdentityVerificationSessionProcessing)
{
}
else if (stripeEvent.Type == Events.IdentityVerificationSessionRequiresInput)
{
}
else if (stripeEvent.Type == Events.IdentityVerificationSessionVerified)
{
}
else if (stripeEvent.Type == Events.InvoiceCreated)
{
}
else if (stripeEvent.Type == Events.InvoiceDeleted)
{
}
else if (stripeEvent.Type == Events.InvoiceFinalizationFailed)
{
}
else if (stripeEvent.Type == Events.InvoiceFinalized)
{
}
else if (stripeEvent.Type == Events.InvoiceMarkedUncollectible)
{
}
else if (stripeEvent.Type == Events.InvoicePaid)
{
}
else if (stripeEvent.Type == Events.InvoicePaymentActionRequired)
{
}
else if (stripeEvent.Type == Events.InvoicePaymentFailed)
{
}
else if (stripeEvent.Type == Events.InvoicePaymentSucceeded)
{
}
else if (stripeEvent.Type == Events.InvoiceSent)
{
}
else if (stripeEvent.Type == Events.InvoiceUpcoming)
{
}
else if (stripeEvent.Type == Events.InvoiceUpdated)
{
}
else if (stripeEvent.Type == Events.InvoiceVoided)
{
}
else if (stripeEvent.Type == Events.IssuingAuthorizationCreated)
{
}
else if (stripeEvent.Type == Events.IssuingAuthorizationUpdated)
{
}
else if (stripeEvent.Type == Events.IssuingCardCreated)
{
}
else if (stripeEvent.Type == Events.IssuingCardUpdated)
{
}
else if (stripeEvent.Type == Events.IssuingCardholderCreated)
{
}
else if (stripeEvent.Type == Events.IssuingCardholderUpdated)
{
}
else if (stripeEvent.Type == Events.IssuingDisputeClosed)
{
}
else if (stripeEvent.Type == Events.IssuingDisputeCreated)
{
}
else if (stripeEvent.Type == Events.IssuingDisputeFundsReinstated)
{
}
else if (stripeEvent.Type == Events.IssuingDisputeSubmitted)
{
}
else if (stripeEvent.Type == Events.IssuingDisputeUpdated)
{
}
else if (stripeEvent.Type == Events.IssuingTokenCreated)
{
}
else if (stripeEvent.Type == Events.IssuingTokenUpdated)
{
}
else if (stripeEvent.Type == Events.IssuingTransactionCreated)
{
}
else if (stripeEvent.Type == Events.IssuingTransactionUpdated)
{
}
else if (stripeEvent.Type == Events.MandateUpdated)
{
}
else if (stripeEvent.Type == Events.PaymentIntentAmountCapturableUpdated)
{
}
else if (stripeEvent.Type == Events.PaymentIntentCanceled)
{
}
else if (stripeEvent.Type == Events.PaymentIntentCreated)
{
}
else if (stripeEvent.Type == Events.PaymentIntentPartiallyFunded)
{
}
else if (stripeEvent.Type == Events.PaymentIntentPaymentFailed)
{
}
else if (stripeEvent.Type == Events.PaymentIntentProcessing)
{
}
else if (stripeEvent.Type == Events.PaymentIntentRequiresAction)
{
}
else if (stripeEvent.Type == Events.PaymentIntentSucceeded)
{
}
else if (stripeEvent.Type == Events.PaymentLinkCreated)
{
}
else if (stripeEvent.Type == Events.PaymentLinkUpdated)
{
}
else if (stripeEvent.Type == Events.PaymentMethodAttached)
{
}
else if (stripeEvent.Type == Events.PaymentMethodAutomaticallyUpdated)
{
}
else if (stripeEvent.Type == Events.PaymentMethodDetached)
{
}
else if (stripeEvent.Type == Events.PaymentMethodUpdated)
{
}
else if (stripeEvent.Type == Events.PayoutCanceled)
{
}
else if (stripeEvent.Type == Events.PayoutCreated)
{
}
else if (stripeEvent.Type == Events.PayoutFailed)
{
}
else if (stripeEvent.Type == Events.PayoutPaid)
{
}
else if (stripeEvent.Type == Events.PayoutReconciliationCompleted)
{
}
else if (stripeEvent.Type == Events.PayoutUpdated)
{
}
else if (stripeEvent.Type == Events.PersonCreated)
{
}
else if (stripeEvent.Type == Events.PersonDeleted)
{
}
else if (stripeEvent.Type == Events.PersonUpdated)
{
}
else if (stripeEvent.Type == Events.PlanCreated)
{
}
else if (stripeEvent.Type == Events.PlanDeleted)
{
}
else if (stripeEvent.Type == Events.PlanUpdated)
{
}
else if (stripeEvent.Type == Events.PriceCreated)
{
}
else if (stripeEvent.Type == Events.PriceDeleted)
{
}
else if (stripeEvent.Type == Events.PriceUpdated)
{
}
else if (stripeEvent.Type == Events.ProductCreated)
{
}
else if (stripeEvent.Type == Events.ProductDeleted)
{
}
else if (stripeEvent.Type == Events.ProductUpdated)
{
}
else if (stripeEvent.Type == Events.PromotionCodeCreated)
{
}
else if (stripeEvent.Type == Events.PromotionCodeUpdated)
{
}
else if (stripeEvent.Type == Events.QuoteAccepted)
{
}
else if (stripeEvent.Type == Events.QuoteCanceled)
{
}
else if (stripeEvent.Type == Events.QuoteCreated)
{
}
else if (stripeEvent.Type == Events.QuoteFinalized)
{
}
else if (stripeEvent.Type == Events.RadarEarlyFraudWarningCreated)
{
}
else if (stripeEvent.Type == Events.RadarEarlyFraudWarningUpdated)
{
}
else if (stripeEvent.Type == Events.RefundCreated)
{
}
else if (stripeEvent.Type == Events.RefundUpdated)
{
}
else if (stripeEvent.Type == Events.ReportingReportRunFailed)
{
}
else if (stripeEvent.Type == Events.ReportingReportRunSucceeded)
{
}
else if (stripeEvent.Type == Events.ReviewClosed)
{
}
else if (stripeEvent.Type == Events.ReviewOpened)
{
}
else if (stripeEvent.Type == Events.SetupIntentCanceled)
{
}
else if (stripeEvent.Type == Events.SetupIntentCreated)
{
}
else if (stripeEvent.Type == Events.SetupIntentRequiresAction)
{
}
else if (stripeEvent.Type == Events.SetupIntentSetupFailed)
{
}
else if (stripeEvent.Type == Events.SetupIntentSucceeded)
{
}
else if (stripeEvent.Type == Events.SigmaScheduledQueryRunCreated)
{
}
else if (stripeEvent.Type == Events.SourceCanceled)
{
}
else if (stripeEvent.Type == Events.SourceChargeable)
{
}
else if (stripeEvent.Type == Events.SourceFailed)
{
}
else if (stripeEvent.Type == Events.SourceMandateNotification)
{
}
else if (stripeEvent.Type == Events.SourceRefundAttributesRequired)
{
}
else if (stripeEvent.Type == Events.SourceTransactionCreated)
{
}
else if (stripeEvent.Type == Events.SourceTransactionUpdated)
{
}
else if (stripeEvent.Type == Events.SubscriptionScheduleAborted)
{
}
else if (stripeEvent.Type == Events.SubscriptionScheduleCanceled)
{
}
else if (stripeEvent.Type == Events.SubscriptionScheduleCompleted)
{
}
else if (stripeEvent.Type == Events.SubscriptionScheduleCreated)
{
}
else if (stripeEvent.Type == Events.SubscriptionScheduleExpiring)
{
}
else if (stripeEvent.Type == Events.SubscriptionScheduleReleased)
{
}
else if (stripeEvent.Type == Events.SubscriptionScheduleUpdated)
{
}
else if (stripeEvent.Type == Events.TaxSettingsUpdated)
{
}
else if (stripeEvent.Type == Events.TaxRateCreated)
{
}
else if (stripeEvent.Type == Events.TaxRateUpdated)
{
}
else if (stripeEvent.Type == Events.TerminalReaderActionFailed)
{
}
else if (stripeEvent.Type == Events.TerminalReaderActionSucceeded)
{
}
else if (stripeEvent.Type == Events.TestHelpersTestClockAdvancing)
{
}
else if (stripeEvent.Type == Events.TestHelpersTestClockCreated)
{
}
else if (stripeEvent.Type == Events.TestHelpersTestClockDeleted)
{
}
else if (stripeEvent.Type == Events.TestHelpersTestClockInternalFailure)
{
}
else if (stripeEvent.Type == Events.TestHelpersTestClockReady)
{
}
else if (stripeEvent.Type == Events.TopupCanceled)
{
}
else if (stripeEvent.Type == Events.TopupCreated)
{
}
else if (stripeEvent.Type == Events.TopupFailed)
{
}
else if (stripeEvent.Type == Events.TopupReversed)
{
}
else if (stripeEvent.Type == Events.TopupSucceeded)
{
}
else if (stripeEvent.Type == Events.TransferCreated)
{
}
else if (stripeEvent.Type == Events.TransferReversed)
{
}
else if (stripeEvent.Type == Events.TransferUpdated)
{
}
// ... handle other event types
else
{
Console.WriteLine("Unhandled event type: {0}", stripeEvent.Type);
}
return Ok();
}
[Authorize("read:request")]
[HttpGet]
[Route("Customer/Requests")]
@ -140,7 +772,7 @@ public class RequestsController : Controller
if(request==null)
return NotFound();
var paymentUrl = _paymentService.Charge(request.Id,request.Artist.StripeAccountId,Convert.ToDouble(request.Amount));
request.Accepted = true;
request.AcceptedDate = DateTime.UtcNow;
_dbContext.Entry(request).State = EntityState.Modified;

View File

@ -12,6 +12,9 @@ public class Request
public DateTime? AcceptedDate { get; set; }
public bool Declined { get; set; } = false;
public DateTime? DeclinedDate { get; set; }
public string? PaymentUrl { get; set; }
public bool Paid { get; set; } = false;
public DateTime? PaidDate { get; set; } = null!;
public bool Completed { get; set; } = false;
public DateTime? CompletedDate { get; set; }

View File

@ -3,6 +3,10 @@ using comissions.app.api.Services.Payment;
using comissions.app.database;
using comissions.app.database.Entities;
using Microsoft.EntityFrameworkCore;
using Novu;
using Novu.Interfaces;
using Novu.DTO;
using Novu.DTO.Subscribers;
namespace comissions.app.api.Middleware;
@ -10,10 +14,11 @@ namespace comissions.app.api.Middleware;
public class UserMiddleware
{
private readonly RequestDelegate _next;
public UserMiddleware(RequestDelegate next)
private readonly NovuClient _client;
public UserMiddleware(RequestDelegate next, NovuClient client)
{
_next = next;
_client = client;
}
public async Task InvokeAsync(HttpContext context, ApplicationDbContext dbContext, IPaymentService paymentService)
@ -43,6 +48,15 @@ public class UserMiddleware
await dbContext.SaveChangesAsync();
}
var newSubscriberDto = new SubscriberCreateData()
{
SubscriberId = userId, //replace with system_internal_user_id
FirstName = user.DisplayName,
LastName = "",
Email = user.Email
};
var subscriber = await _client.Subscriber.Create(newSubscriberDto);
if (user.Suspended)
{
if (user.UnsuspendDate < DateTime.UtcNow)

View File

@ -0,0 +1,493 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using comissions.app.database;
#nullable disable
namespace comissions.app.api.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20240223013625_moar")]
partial class moar
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "8.0.1")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("comissions.app.database.Entities.ArtistPageSettings", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int>("ArtistId")
.HasColumnType("integer");
b.Property<string>("BackgroundColor")
.IsRequired()
.HasColumnType("text");
b.Property<string>("DescriptionBackgroundColor")
.IsRequired()
.HasColumnType("text");
b.Property<string>("DescriptionHeaderColor")
.IsRequired()
.HasColumnType("text");
b.Property<string>("DescriptionHeaderImageUrl")
.IsRequired()
.HasColumnType("text");
b.Property<int>("DescriptionHeaderSize")
.HasColumnType("integer");
b.Property<string>("DescriptionHeaderText")
.IsRequired()
.HasColumnType("text");
b.Property<bool>("DescriptionHeaderUseImage")
.HasColumnType("boolean");
b.Property<string>("DescriptionTextColor")
.IsRequired()
.HasColumnType("text");
b.Property<int>("DescriptionTextSize")
.HasColumnType("integer");
b.Property<string>("HeaderColor")
.IsRequired()
.HasColumnType("text");
b.Property<string>("HeaderImageUrl")
.IsRequired()
.HasColumnType("text");
b.Property<int>("HeaderTextSize")
.HasColumnType("integer");
b.Property<bool>("HeaderUseImage")
.HasColumnType("boolean");
b.Property<string>("PortfolioBackgroundColor")
.IsRequired()
.HasColumnType("text");
b.Property<int>("PortfolioColumns")
.HasColumnType("integer");
b.Property<bool>("PortfolioEnabledScrolling")
.HasColumnType("boolean");
b.Property<bool>("PortfolioMasonry")
.HasColumnType("boolean");
b.Property<int>("PortfolioMaximumSize")
.HasColumnType("integer");
b.Property<string>("PortfolionHeaderColor")
.IsRequired()
.HasColumnType("text");
b.Property<string>("PortfolionHeaderImageUrl")
.IsRequired()
.HasColumnType("text");
b.Property<int>("PortfolionHeaderSize")
.HasColumnType("integer");
b.Property<string>("PortfolionHeaderText")
.IsRequired()
.HasColumnType("text");
b.Property<bool>("PortfolionHeaderUseImage")
.HasColumnType("boolean");
b.Property<string>("RequestBackgroundColor")
.IsRequired()
.HasColumnType("text");
b.Property<string>("RequestButtonBGColor")
.IsRequired()
.HasColumnType("text");
b.Property<string>("RequestButtonHoverBGColor")
.IsRequired()
.HasColumnType("text");
b.Property<string>("RequestButtonHoverTextColor")
.IsRequired()
.HasColumnType("text");
b.Property<string>("RequestButtonTextColor")
.IsRequired()
.HasColumnType("text");
b.Property<string>("RequestHeaderColor")
.IsRequired()
.HasColumnType("text");
b.Property<string>("RequestHeaderImageUrl")
.IsRequired()
.HasColumnType("text");
b.Property<int>("RequestHeaderSize")
.HasColumnType("integer");
b.Property<string>("RequestHeaderText")
.IsRequired()
.HasColumnType("text");
b.Property<bool>("RequestHeaderUseImage")
.HasColumnType("boolean");
b.Property<string>("RequestTermsColor")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("ArtistId")
.IsUnique();
b.ToTable("ArtistPageSettings");
});
modelBuilder.Entity("comissions.app.database.Entities.ArtistPortfolioPiece", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int>("ArtistId")
.HasColumnType("integer");
b.Property<string>("FileReference")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("ArtistId");
b.ToTable("ArtistPortfolioPieces");
});
modelBuilder.Entity("comissions.app.database.Entities.ArtistRequest", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<bool>("Accepted")
.HasColumnType("boolean");
b.Property<DateTime?>("AcceptedDate")
.HasColumnType("timestamp with time zone");
b.Property<string>("Message")
.IsRequired()
.HasColumnType("text");
b.Property<DateTime>("RequestDate")
.HasColumnType("timestamp with time zone");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("ArtistRequests");
});
modelBuilder.Entity("comissions.app.database.Entities.Request", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<bool>("Accepted")
.HasColumnType("boolean");
b.Property<DateTime?>("AcceptedDate")
.HasColumnType("timestamp with time zone");
b.Property<decimal>("Amount")
.HasColumnType("numeric");
b.Property<int>("ArtistId")
.HasColumnType("integer");
b.Property<bool>("Completed")
.HasColumnType("boolean");
b.Property<DateTime?>("CompletedDate")
.HasColumnType("timestamp with time zone");
b.Property<bool>("Declined")
.HasColumnType("boolean");
b.Property<DateTime?>("DeclinedDate")
.HasColumnType("timestamp with time zone");
b.Property<string>("Message")
.IsRequired()
.HasColumnType("text");
b.Property<bool>("Paid")
.HasColumnType("boolean");
b.Property<DateTime?>("PaidDate")
.HasColumnType("timestamp with time zone");
b.Property<string>("PaymentUrl")
.HasColumnType("text");
b.Property<DateTime>("RequestDate")
.HasColumnType("timestamp with time zone");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("ArtistId");
b.HasIndex("UserId");
b.ToTable("Requests");
});
modelBuilder.Entity("comissions.app.database.Entities.User", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<string>("BanAdminId")
.HasColumnType("text");
b.Property<bool>("Banned")
.HasColumnType("boolean");
b.Property<DateTime?>("BannedDate")
.HasColumnType("timestamp with time zone");
b.Property<string>("BannedReason")
.HasColumnType("text");
b.Property<string>("Biography")
.IsRequired()
.HasColumnType("text");
b.Property<string>("DisplayName")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Email")
.IsRequired()
.HasColumnType("text");
b.Property<string>("SuspendAdminId")
.HasColumnType("text");
b.Property<bool>("Suspended")
.HasColumnType("boolean");
b.Property<DateTime?>("SuspendedDate")
.HasColumnType("timestamp with time zone");
b.Property<string>("SuspendedReason")
.HasColumnType("text");
b.Property<DateTime?>("UnbanDate")
.HasColumnType("timestamp with time zone");
b.Property<DateTime?>("UnsuspendDate")
.HasColumnType("timestamp with time zone");
b.Property<int?>("UserArtistId")
.HasColumnType("integer");
b.HasKey("Id");
b.ToTable("Users");
});
modelBuilder.Entity("comissions.app.database.Entities.UserArtist", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<bool>("AgeRestricted")
.HasColumnType("boolean");
b.Property<int>("ArtistPageSettingsId")
.HasColumnType("integer");
b.Property<string>("Description")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<bool>("PrepaymentRequired")
.HasColumnType("boolean");
b.Property<string>("RequestGuidelines")
.IsRequired()
.HasColumnType("text");
b.Property<string>("SocialMediaLink1")
.IsRequired()
.HasColumnType("text");
b.Property<string>("SocialMediaLink2")
.IsRequired()
.HasColumnType("text");
b.Property<string>("SocialMediaLink3")
.IsRequired()
.HasColumnType("text");
b.Property<string>("SocialMediaLink4")
.IsRequired()
.HasColumnType("text");
b.Property<string>("StripeAccountId")
.HasColumnType("text");
b.Property<string>("SuspendAdminId")
.HasColumnType("text");
b.Property<bool>("Suspended")
.HasColumnType("boolean");
b.Property<DateTime?>("SuspendedDate")
.HasColumnType("timestamp with time zone");
b.Property<string>("SuspendedReason")
.HasColumnType("text");
b.Property<DateTime?>("UnsuspendDate")
.HasColumnType("timestamp with time zone");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("UserId")
.IsUnique();
b.ToTable("UserArtists");
});
modelBuilder.Entity("comissions.app.database.Entities.ArtistPageSettings", b =>
{
b.HasOne("comissions.app.database.Entities.UserArtist", "Artist")
.WithOne("ArtistPageSettings")
.HasForeignKey("comissions.app.database.Entities.ArtistPageSettings", "ArtistId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Artist");
});
modelBuilder.Entity("comissions.app.database.Entities.ArtistPortfolioPiece", b =>
{
b.HasOne("comissions.app.database.Entities.UserArtist", "Artist")
.WithMany("PortfolioPieces")
.HasForeignKey("ArtistId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Artist");
});
modelBuilder.Entity("comissions.app.database.Entities.ArtistRequest", b =>
{
b.HasOne("comissions.app.database.Entities.User", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("comissions.app.database.Entities.Request", b =>
{
b.HasOne("comissions.app.database.Entities.UserArtist", "Artist")
.WithMany()
.HasForeignKey("ArtistId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("comissions.app.database.Entities.User", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Artist");
b.Navigation("User");
});
modelBuilder.Entity("comissions.app.database.Entities.UserArtist", b =>
{
b.HasOne("comissions.app.database.Entities.User", "User")
.WithOne("UserArtist")
.HasForeignKey("comissions.app.database.Entities.UserArtist", "UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("comissions.app.database.Entities.User", b =>
{
b.Navigation("UserArtist");
});
modelBuilder.Entity("comissions.app.database.Entities.UserArtist", b =>
{
b.Navigation("ArtistPageSettings")
.IsRequired();
b.Navigation("PortfolioPieces");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,50 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace comissions.app.api.Migrations
{
/// <inheritdoc />
public partial class moar : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<bool>(
name: "Paid",
table: "Requests",
type: "boolean",
nullable: false,
defaultValue: false);
migrationBuilder.AddColumn<DateTime>(
name: "PaidDate",
table: "Requests",
type: "timestamp with time zone",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "PaymentUrl",
table: "Requests",
type: "text",
nullable: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "Paid",
table: "Requests");
migrationBuilder.DropColumn(
name: "PaidDate",
table: "Requests");
migrationBuilder.DropColumn(
name: "PaymentUrl",
table: "Requests");
}
}
}

View File

@ -254,6 +254,15 @@ namespace comissions.app.api.Migrations
.IsRequired()
.HasColumnType("text");
b.Property<bool>("Paid")
.HasColumnType("boolean");
b.Property<DateTime?>("PaidDate")
.HasColumnType("timestamp with time zone");
b.Property<string>("PaymentUrl")
.HasColumnType("text");
b.Property<DateTime>("RequestDate")
.HasColumnType("timestamp with time zone");

View File

@ -12,6 +12,11 @@ using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.FileProviders;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using Novu;
using Novu.DTO;
using Novu.Extensions;
using Novu.Interfaces;
using Novu.Models;
var builder = WebApplication.CreateBuilder(args);
@ -75,6 +80,7 @@ builder.Services.AddSwaggerGen(options =>
options.IncludeXmlComments(xmlPath);
});
builder.Services.RegisterNovuClients(builder.Configuration);
builder.Services.AddControllers()
.AddJsonOptions(options=>

View File

@ -7,5 +7,5 @@ public interface IPaymentService
string CreateArtistAccount();
string CreateArtistAccountOnboardingUrl(string accountId);
bool ArtistAccountIsOnboarded(string accountId);
string ChargeForService(int orderArtistServiceId, string? sellerStripeAccountId, double orderPrice);
string Charge(int orderArtistServiceId, string? sellerStripeAccountId, double orderPrice);
}

View File

@ -71,8 +71,8 @@ public class StripePaymentServiceProvider:IPaymentService
var options = new AccountLinkCreateOptions
{
Account = accountId,
RefreshUrl = $"{_baseUiUrl}/artistDashboard",
ReturnUrl = $"{_baseUiUrl}/artistDashboard",
RefreshUrl = $"{_baseUiUrl}/dashboard",
ReturnUrl = $"{_baseUiUrl}/dashboard",
Type = "account_onboarding",
};
var service = new AccountLinkService();
@ -87,10 +87,10 @@ public class StripePaymentServiceProvider:IPaymentService
return account.Requirements.CurrentlyDue.Count == 0 && account.ChargesEnabled==true && account.DetailsSubmitted==true;
}
public string ChargeForService(int orderArtistServiceOrderId, string? sellerStripeAccountId,
double orderPrice)
public string Charge(int requestId, string? sellerStripeAccountId,
double requestAmount)
{
var feeAmount = (long)Math.Round((orderPrice*0.05) * 100);
var feeAmount = (long)Math.Round((requestAmount*0.05) * 100);
var options = new Stripe.Checkout.SessionCreateOptions
{
LineItems = new List<Stripe.Checkout.SessionLineItemOptions> {
@ -98,7 +98,7 @@ public class StripePaymentServiceProvider:IPaymentService
{
PriceData = new Stripe.Checkout.SessionLineItemPriceDataOptions
{
UnitAmount = (long)Math.Round(orderPrice * 100),
UnitAmount = (long)Math.Round(requestAmount * 100),
Currency = "usd",
ProductData = new Stripe.Checkout.SessionLineItemPriceDataProductDataOptions
{
@ -113,11 +113,11 @@ public class StripePaymentServiceProvider:IPaymentService
ApplicationFeeAmount = feeAmount,
},
Mode = "payment",
SuccessUrl = "https://example.com/success",
CancelUrl = "https://example.com/failure",
SuccessUrl = $"{_baseUiUrl}/dashboard/requests/{requestId}",
CancelUrl = $"{_baseUiUrl}/dashboard/requests/{requestId}",
Metadata = new Dictionary<string, string>()
{
["orderId"] = orderArtistServiceOrderId.ToString()
["orderId"] = requestId.ToString()
}
};
var requestOptions = new RequestOptions

View File

@ -2,6 +2,9 @@
"UI": {
"BaseUrl": "http://localhost:3000"
},
"Novu": {
"ApiKey": "287ad99d1cbeba7f96a7d2637b022e6f"
},
"Database": {
"Database": "comissionsapp",
"Host": "localhost",

View File

@ -20,6 +20,9 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="8.0.1" />
<PackageReference Include="Novu" Version="0.3.3" />
<PackageReference Include="Novu.Extensions" Version="0.3.3" />
<PackageReference Include="Novu.Sync" Version="0.3.3" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.0" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL.Design" Version="1.1.0" />
<PackageReference Include="Stripe.net" Version="43.12.0" />

View File

@ -13,7 +13,7 @@ using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("comissions.app.database.migrator")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+8f8b18a46c0d2d56708815efa9b6359a92aa9bdc")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+04861b6b58dd552f5a309afc249a59707799da37")]
[assembly: System.Reflection.AssemblyProductAttribute("comissions.app.database.migrator")]
[assembly: System.Reflection.AssemblyTitleAttribute("comissions.app.database.migrator")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]

View File

@ -1 +1 @@
f400a3f8c8fe637929ec13e1f2608e512902861c6e8ba22bc4291585fdb672de
f6d1d126af9b4482ad74b31893663f1ef7fa48b4f281e3af0a28cd303ade7752

View File

@ -1 +1 @@
17077084151680581
17086505533886000