Merged PR 46: #27 added more info to transaction + callback from stripe
#27 added more info to transaction + callback from stripe Related work items: #27
This commit is contained in:
5
.gitignore
vendored
5
.gitignore
vendored
@@ -485,3 +485,8 @@ $RECYCLE.BIN/
|
|||||||
# Other IDE files
|
# Other IDE files
|
||||||
.vscode/
|
.vscode/
|
||||||
.idea/
|
.idea/
|
||||||
|
|
||||||
|
|
||||||
|
#AppSettings dev
|
||||||
|
*/appsettings.Development.json
|
||||||
|
/src/Web/appsettings.Development.json
|
||||||
|
|||||||
@@ -37,7 +37,7 @@
|
|||||||
<PackageVersion Include="NUnit.Analyzers" Version="3.9.0" />
|
<PackageVersion Include="NUnit.Analyzers" Version="3.9.0" />
|
||||||
<PackageVersion Include="NUnit3TestAdapter" Version="4.5.0" />
|
<PackageVersion Include="NUnit3TestAdapter" Version="4.5.0" />
|
||||||
<PackageVersion Include="Respawn" Version="6.1.0" />
|
<PackageVersion Include="Respawn" Version="6.1.0" />
|
||||||
<PackageVersion Include="Stripe.net" Version="43.19.0" />
|
<PackageVersion Include="Stripe.net" Version="44.5.0" />
|
||||||
<PackageVersion Include="Testcontainers.MsSql" Version="3.6.0" />
|
<PackageVersion Include="Testcontainers.MsSql" Version="3.6.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
@@ -1,7 +1,11 @@
|
|||||||
|
using Hutopy.Application.Common.Models;
|
||||||
|
using Hutopy.Application.Stripe.Commands;
|
||||||
|
|
||||||
namespace Hutopy.Application.Common.Interfaces;
|
namespace Hutopy.Application.Common.Interfaces;
|
||||||
|
|
||||||
|
|
||||||
public interface IStripeService
|
public interface IStripeService
|
||||||
{
|
{
|
||||||
public Task<string> CreateCheckoutSession(int amount, string currency);
|
public Task<string> CreateCheckoutSession(int amount, string currency);
|
||||||
|
public Result ValidateTransaction(ConfirmStripeTransactionCommand request);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,73 @@
|
|||||||
using Hutopy.Application.Common.Interfaces;
|
using Hutopy.Application.Common.Interfaces;
|
||||||
|
|
||||||
namespace Hutopy.Application.Stripe.Commands;
|
namespace Hutopy.Application.Stripe.Commands;
|
||||||
public record ConfirmStripeTransactionCommand : IRequest<string>
|
public class ConfirmStripeTransactionCommand : IRequest<string>
|
||||||
{
|
{
|
||||||
public required Guid UserTransactionId { get; init; }
|
public string Id { get; set; }
|
||||||
public required bool IsConfirmed { get; init; }
|
public string Object { get; set; }
|
||||||
|
public int Created { get; set; }
|
||||||
|
public Data Data { get; set; }
|
||||||
|
public Request Request { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Data
|
||||||
|
{
|
||||||
|
public Object Object { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Object
|
||||||
|
{
|
||||||
|
public string Id { get; set; } = String.Empty;
|
||||||
|
public int Amount { get; set; }
|
||||||
|
public BillingDetails Billing_details { get; set; } = new();
|
||||||
|
public string Calculated_statement_descriptor { get; set; } = String.Empty;
|
||||||
|
public string Currency { get; set; } = String.Empty;
|
||||||
|
public bool Paid { get; set; }
|
||||||
|
public string Payment_intent { get; set; } = String.Empty;
|
||||||
|
public string Payment_method { get; set; } = String.Empty;
|
||||||
|
public string Receipt_url { get; set; } = String.Empty;
|
||||||
|
public string Status { get; set; } = String.Empty;
|
||||||
|
public string Failure_message { get; set; } = String.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class BillingDetails
|
||||||
|
{
|
||||||
|
public string Email { get; set; } = String.Empty;
|
||||||
|
public string Name { get; set; } = String.Empty;
|
||||||
|
public string Phone { get; set; } = String.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Request
|
||||||
|
{
|
||||||
|
public string Id { get; set; } = String.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ConfirmStripeTransactionCommandHandler(
|
public class ConfirmStripeTransactionCommandHandler(
|
||||||
IApplicationDbContext dbContext
|
IApplicationDbContext dbContext,
|
||||||
|
IStripeService stripeService
|
||||||
)
|
)
|
||||||
: IRequestHandler<ConfirmStripeTransactionCommand, string>
|
: IRequestHandler<ConfirmStripeTransactionCommand, string>
|
||||||
{
|
{
|
||||||
public async Task<string> Handle(ConfirmStripeTransactionCommand request, CancellationToken cancellationToken)
|
public async Task<string> Handle(ConfirmStripeTransactionCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var transaction = await dbContext.UserTransactions.FirstOrDefaultAsync(x => x.Id == request.UserTransactionId, cancellationToken);
|
var lastTransaction = await dbContext.UserTransactions.OrderBy(x => x.Created).LastAsync(cancellationToken);
|
||||||
if (transaction is null) return "";
|
var stripeConfirmation = stripeService.ValidateTransaction(request);
|
||||||
transaction.IsConfirmed = request.IsConfirmed;
|
|
||||||
dbContext.UserTransactions.Update(transaction);
|
|
||||||
|
|
||||||
return transaction.Id.ToString();
|
if (stripeConfirmation.Succeeded)
|
||||||
|
{
|
||||||
|
lastTransaction.IsConfirmed = true;
|
||||||
|
lastTransaction.Paid = request.Data.Object.Paid;
|
||||||
|
lastTransaction.StripeChargeId = request.Data.Object.Id;
|
||||||
|
lastTransaction.StripeEventId = request.Id;
|
||||||
|
lastTransaction.StripeReceiptUrl = request.Data.Object.Receipt_url;
|
||||||
|
lastTransaction.StripePaymentIntent = request.Data.Object.Payment_intent;
|
||||||
|
lastTransaction.StripePaymentMethod = request.Data.Object.Payment_method;
|
||||||
|
lastTransaction.StripeBillingDetailEmail = request.Data.Object.Billing_details.Email;
|
||||||
|
lastTransaction.StripeBillingDetailName = request.Data.Object.Billing_details.Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
await dbContext.SaveChangesAsync(cancellationToken);
|
||||||
|
|
||||||
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,9 +22,7 @@ public class CreateSessionCheckoutCommandHandler(
|
|||||||
|
|
||||||
// ReSharper disable once PossibleLossOfFraction
|
// ReSharper disable once PossibleLossOfFraction
|
||||||
decimal priceInDollars = (request.Amount / 100);
|
decimal priceInDollars = (request.Amount / 100);
|
||||||
|
|
||||||
|
|
||||||
//todo: Need to add this transaction after the confirmation from stripe in the frontEnd ( redirect, re-call backend )
|
|
||||||
var userTransaction = new UserTransaction
|
var userTransaction = new UserTransaction
|
||||||
{
|
{
|
||||||
Currency = request.Currency, Amount = priceInDollars, TipMessage = request.TipMessage, ApplicationUserId = request.CreatorId
|
Currency = request.Currency, Amount = priceInDollars, TipMessage = request.TipMessage, ApplicationUserId = request.CreatorId
|
||||||
|
|||||||
@@ -8,5 +8,13 @@ public class UserTransaction : BaseAuditableEntity
|
|||||||
|
|
||||||
// Foreign key to ApplicationUser
|
// Foreign key to ApplicationUser
|
||||||
public string ApplicationUserId { get; set; } = string.Empty;
|
public string ApplicationUserId { get; set; } = string.Empty;
|
||||||
public bool IsConfirmed { get; set; } = false;
|
public bool IsConfirmed { get; set; }
|
||||||
|
public string StripeEventId { get; set; } = string.Empty;
|
||||||
|
public string StripeChargeId { get; set; } = string.Empty;
|
||||||
|
public string StripePaymentIntent { get; set; } = string.Empty;
|
||||||
|
public string StripePaymentMethod { get; set; } = string.Empty;
|
||||||
|
public string StripeReceiptUrl { get; set; } = string.Empty;
|
||||||
|
public string StripeBillingDetailEmail { get; set; } = string.Empty;
|
||||||
|
public string StripeBillingDetailName { get; set; } = string.Empty;
|
||||||
|
public bool Paid { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
418
src/Infrastructure/Migrations/20240509215538_AddMoreInformationToTransaction.Designer.cs
generated
Normal file
418
src/Infrastructure/Migrations/20240509215538_AddMoreInformationToTransaction.Designer.cs
generated
Normal file
@@ -0,0 +1,418 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Hutopy.Infrastructure.Data;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Hutopy.Infrastructure.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(ApplicationDbContext))]
|
||||||
|
[Migration("20240509215538_AddMoreInformationToTransaction")]
|
||||||
|
partial class AddMoreInformationToTransaction
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "8.0.3")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||||
|
|
||||||
|
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity("Hutopy.Domain.Entities.FutureCreator", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("Created")
|
||||||
|
.HasColumnType("datetimeoffset");
|
||||||
|
|
||||||
|
b.Property<string>("CreatedBy")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("EmailAddress")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("FirstName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("LastModified")
|
||||||
|
.HasColumnType("datetimeoffset");
|
||||||
|
|
||||||
|
b.Property<string>("LastModifiedBy")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("LastName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("PhoneNumber")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("ReasonToJoin")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("SocialNetworkAccount")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("FutureCreators");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Hutopy.Domain.Entities.UserTransaction", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<decimal>("Amount")
|
||||||
|
.HasPrecision(18, 2)
|
||||||
|
.HasColumnType("decimal(18,2)");
|
||||||
|
|
||||||
|
b.Property<string>("ApplicationUserId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("Created")
|
||||||
|
.HasColumnType("datetimeoffset");
|
||||||
|
|
||||||
|
b.Property<string>("CreatedBy")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("Currency")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<bool>("IsConfirmed")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("LastModified")
|
||||||
|
.HasColumnType("datetimeoffset");
|
||||||
|
|
||||||
|
b.Property<string>("LastModifiedBy")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<bool>("Paid")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<string>("StripeBillingDetailEmail")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("StripeBillingDetailName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("StripeChargeId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("StripeEventId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("StripePaymentIntent")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("StripePaymentMethod")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("StripeReceiptUrl")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("TipMessage")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ApplicationUserId");
|
||||||
|
|
||||||
|
b.ToTable("UserTransactions");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Hutopy.Infrastructure.Identity.ApplicationUser", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<int>("AccessFailedCount")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("ConcurrencyStamp")
|
||||||
|
.IsConcurrencyToken()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
|
b.Property<bool>("EmailConfirmed")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<string>("FirstName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("LastName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<bool>("LockoutEnabled")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset?>("LockoutEnd")
|
||||||
|
.HasColumnType("datetimeoffset");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedEmail")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedUserName")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("PhoneNumber")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<bool>("PhoneNumberConfirmed")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<string>("SecurityStamp")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<bool>("TwoFactorEnabled")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<string>("UserName")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedEmail")
|
||||||
|
.HasDatabaseName("EmailIndex");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedUserName")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("UserNameIndex")
|
||||||
|
.HasFilter("[NormalizedUserName] IS NOT NULL");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUsers", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<string>("ConcurrencyStamp")
|
||||||
|
.IsConcurrencyToken()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedName")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedName")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("RoleNameIndex")
|
||||||
|
.HasFilter("[NormalizedName] IS NOT NULL");
|
||||||
|
|
||||||
|
b.ToTable("AspNetRoles", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("ClaimType")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimValue")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("RoleId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("RoleId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetRoleClaims", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("ClaimType")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimValue")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserClaims", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("LoginProvider")
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderKey")
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderDisplayName")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.HasKey("LoginProvider", "ProviderKey");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserLogins", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<string>("RoleId")
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "RoleId");
|
||||||
|
|
||||||
|
b.HasIndex("RoleId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserRoles", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<string>("LoginProvider")
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<string>("Value")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "LoginProvider", "Name");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserTokens", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Hutopy.Domain.Entities.UserTransaction", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Hutopy.Infrastructure.Identity.ApplicationUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("ApplicationUserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RoleId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Hutopy.Infrastructure.Identity.ApplicationUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Hutopy.Infrastructure.Identity.ApplicationUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RoleId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Hutopy.Infrastructure.Identity.ApplicationUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Hutopy.Infrastructure.Identity.ApplicationUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Hutopy.Infrastructure.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddMoreInformationToTransaction : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<bool>(
|
||||||
|
name: "Paid",
|
||||||
|
table: "UserTransactions",
|
||||||
|
type: "bit",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: false);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "StripeBillingDetailEmail",
|
||||||
|
table: "UserTransactions",
|
||||||
|
type: "nvarchar(max)",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: "");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "StripeBillingDetailName",
|
||||||
|
table: "UserTransactions",
|
||||||
|
type: "nvarchar(max)",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: "");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "StripeChargeId",
|
||||||
|
table: "UserTransactions",
|
||||||
|
type: "nvarchar(max)",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: "");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "StripeEventId",
|
||||||
|
table: "UserTransactions",
|
||||||
|
type: "nvarchar(max)",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: "");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "StripePaymentIntent",
|
||||||
|
table: "UserTransactions",
|
||||||
|
type: "nvarchar(max)",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: "");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "StripePaymentMethod",
|
||||||
|
table: "UserTransactions",
|
||||||
|
type: "nvarchar(max)",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: "");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "StripeReceiptUrl",
|
||||||
|
table: "UserTransactions",
|
||||||
|
type: "nvarchar(max)",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "Paid",
|
||||||
|
table: "UserTransactions");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "StripeBillingDetailEmail",
|
||||||
|
table: "UserTransactions");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "StripeBillingDetailName",
|
||||||
|
table: "UserTransactions");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "StripeChargeId",
|
||||||
|
table: "UserTransactions");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "StripeEventId",
|
||||||
|
table: "UserTransactions");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "StripePaymentIntent",
|
||||||
|
table: "UserTransactions");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "StripePaymentMethod",
|
||||||
|
table: "UserTransactions");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "StripeReceiptUrl",
|
||||||
|
table: "UserTransactions");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -102,6 +102,37 @@ namespace Hutopy.Infrastructure.Migrations
|
|||||||
b.Property<string>("LastModifiedBy")
|
b.Property<string>("LastModifiedBy")
|
||||||
.HasColumnType("nvarchar(max)");
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<bool>("Paid")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<string>("StripeBillingDetailEmail")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("StripeBillingDetailName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("StripeChargeId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("StripeEventId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("StripePaymentIntent")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("StripePaymentMethod")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("StripeReceiptUrl")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
b.Property<string>("TipMessage")
|
b.Property<string>("TipMessage")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("nvarchar(max)");
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|||||||
@@ -1,13 +1,20 @@
|
|||||||
using Stripe;
|
using Stripe;
|
||||||
using Stripe.Checkout;
|
using Stripe.Checkout;
|
||||||
using Hutopy.Application.Common.Interfaces;
|
using Hutopy.Application.Common.Interfaces;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Hutopy.Application.Common.Models;
|
||||||
|
using Hutopy.Application.Stripe.Commands;
|
||||||
|
|
||||||
namespace Hutopy.Infrastructure.Stripe;
|
namespace Hutopy.Infrastructure.Stripe;
|
||||||
|
|
||||||
public class StripeService : IStripeService
|
public class StripeService : IStripeService
|
||||||
{
|
{
|
||||||
public StripeService()
|
const string EndpointSecret = "";
|
||||||
|
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||||
|
|
||||||
|
public StripeService(IHttpContextAccessor httpContextAccessor)
|
||||||
{
|
{
|
||||||
|
_httpContextAccessor = httpContextAccessor;
|
||||||
StripeConfiguration.ApiKey = "";
|
StripeConfiguration.ApiKey = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,7 +38,7 @@ public class StripeService : IStripeService
|
|||||||
],
|
],
|
||||||
Mode = "payment",
|
Mode = "payment",
|
||||||
UiMode = "embedded",
|
UiMode = "embedded",
|
||||||
ReturnUrl = "https://zealous-bay-08204590f.5.azurestaticapps.net/paymentcompleted",
|
ReturnUrl = "https://hutopy.ca/paymentcompleted",
|
||||||
};
|
};
|
||||||
|
|
||||||
var service = new SessionService();
|
var service = new SessionService();
|
||||||
@@ -39,4 +46,27 @@ public class StripeService : IStripeService
|
|||||||
|
|
||||||
return session.ClientSecret;
|
return session.ClientSecret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Result ValidateTransaction(ConfirmStripeTransactionCommand request)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (request.Data.Object.Status is "succeeded")
|
||||||
|
{
|
||||||
|
return new Result(true, new List<string>());
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Result(false, new List<string>());
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (StripeException e)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Error: {0}", e.Message);
|
||||||
|
return new Result(false, new List<string>{e.Message});
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
return new Result(false, new List<string>{e.Message});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ public class Stripe : EndpointGroupBase
|
|||||||
return sender.Send(command);
|
return sender.Send(command);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Task<string> ConfirmTransaction(ISender sender, ConfirmStripeTransactionCommand command)
|
private async static Task<string> ConfirmTransaction(ISender sender, ConfirmStripeTransactionCommand command)
|
||||||
{
|
{
|
||||||
return sender.Send(command);
|
return await sender.Send(command);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
29
src/Web/appsettings.Development.dist
Normal file
29
src/Web/appsettings.Development.dist
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft": "Information",
|
||||||
|
"Microsoft.AspNetCore.SpaProxy": "Information",
|
||||||
|
"Microsoft.Hosting.Lifetime": "Information"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Google": {
|
||||||
|
"ClientId": "",
|
||||||
|
"ClientSecret": "",
|
||||||
|
"ProjectId": "",
|
||||||
|
"AuthUri": "",
|
||||||
|
"TokenUri": "",
|
||||||
|
"AuthProviderX509CertUrl": "",
|
||||||
|
"RedirectUris": [
|
||||||
|
"https://hutopy.ca",
|
||||||
|
"https://hutopy.com",
|
||||||
|
"http://localhost"
|
||||||
|
],
|
||||||
|
"JavascriptOrigins": [
|
||||||
|
"https://hutopy.ca",
|
||||||
|
"https://hutopy.com",
|
||||||
|
"http://localhost"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
{
|
|
||||||
"Logging": {
|
|
||||||
"LogLevel": {
|
|
||||||
"Default": "Information",
|
|
||||||
"Microsoft": "Warning",
|
|
||||||
"Microsoft.AspNetCore.SpaProxy": "Information",
|
|
||||||
"Microsoft.Hosting.Lifetime": "Information"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -723,12 +723,94 @@
|
|||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"properties": {
|
"properties": {
|
||||||
"userTransactionId": {
|
"id": {
|
||||||
"type": "string",
|
"type": "string"
|
||||||
"format": "guid"
|
|
||||||
},
|
},
|
||||||
"isConfirmed": {
|
"object": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"created": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int32"
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"$ref": "#/components/schemas/Data"
|
||||||
|
},
|
||||||
|
"request": {
|
||||||
|
"$ref": "#/components/schemas/Request"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Data": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"object": {
|
||||||
|
"$ref": "#/components/schemas/Object"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Object": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"amount": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int32"
|
||||||
|
},
|
||||||
|
"billing_details": {
|
||||||
|
"$ref": "#/components/schemas/BillingDetails"
|
||||||
|
},
|
||||||
|
"calculated_statement_descriptor": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"currency": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"paid": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"payment_intent": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"payment_method": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"receipt_url": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"failure_message": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"BillingDetails": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"email": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"phone": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Request": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "string"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user