diff --git a/Directory.Packages.props b/Directory.Packages.props index 74620e0..90a35bf 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -37,7 +37,7 @@ - + \ No newline at end of file diff --git a/src/Application/Application.csproj b/src/Application/Application.csproj index 8544947..e01512a 100644 --- a/src/Application/Application.csproj +++ b/src/Application/Application.csproj @@ -10,6 +10,7 @@ + diff --git a/src/Application/Common/Interfaces/IStripeService.cs b/src/Application/Common/Interfaces/IStripeService.cs index 75edbe9..ca32184 100644 --- a/src/Application/Common/Interfaces/IStripeService.cs +++ b/src/Application/Common/Interfaces/IStripeService.cs @@ -1,7 +1,11 @@ +using Hutopy.Application.Common.Models; +using Hutopy.Application.Stripe.Commands; + namespace Hutopy.Application.Common.Interfaces; public interface IStripeService { public Task CreateCheckoutSession(int amount, string currency); + public Result ValidateTransaction(ConfirmStripeTransactionCommand request); } diff --git a/src/Application/Stripe/Commands/ConfirmStripeTransaction.cs b/src/Application/Stripe/Commands/ConfirmStripeTransaction.cs index 7aa8a3e..ea5a61c 100644 --- a/src/Application/Stripe/Commands/ConfirmStripeTransaction.cs +++ b/src/Application/Stripe/Commands/ConfirmStripeTransaction.cs @@ -1,24 +1,75 @@ using Hutopy.Application.Common.Interfaces; +using Stripe; + namespace Hutopy.Application.Stripe.Commands; -public record ConfirmStripeTransactionCommand : IRequest +public class ConfirmStripeTransactionCommand : IRequest { - public required Guid UserTransactionId { get; init; } - public required bool IsConfirmed { get; init; } + public string Id { get; set; } + 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( - IApplicationDbContext dbContext + IApplicationDbContext dbContext, + IStripeService stripeService ) : IRequestHandler { public async Task Handle(ConfirmStripeTransactionCommand request, CancellationToken cancellationToken) { - var transaction = await dbContext.UserTransactions.FirstOrDefaultAsync(x => x.Id == request.UserTransactionId, cancellationToken); - if (transaction is null) return ""; - transaction.IsConfirmed = request.IsConfirmed; - dbContext.UserTransactions.Update(transaction); + var lastTransaction = await dbContext.UserTransactions.OrderBy(x => x.Created).LastAsync(cancellationToken); + var stripeConfirmation = stripeService.ValidateTransaction(request); - 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 ""; } } diff --git a/src/Application/Stripe/Commands/CreateSessionCheckoutCommand.cs b/src/Application/Stripe/Commands/CreateSessionCheckoutCommand.cs index b428733..28e9d72 100644 --- a/src/Application/Stripe/Commands/CreateSessionCheckoutCommand.cs +++ b/src/Application/Stripe/Commands/CreateSessionCheckoutCommand.cs @@ -22,9 +22,7 @@ public class CreateSessionCheckoutCommandHandler( // ReSharper disable once PossibleLossOfFraction 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 { Currency = request.Currency, Amount = priceInDollars, TipMessage = request.TipMessage, ApplicationUserId = request.CreatorId diff --git a/src/Domain/Entities/UserTransaction.cs b/src/Domain/Entities/UserTransaction.cs index b879fd2..5ab7314 100644 --- a/src/Domain/Entities/UserTransaction.cs +++ b/src/Domain/Entities/UserTransaction.cs @@ -8,5 +8,13 @@ public class UserTransaction : BaseAuditableEntity // Foreign key to ApplicationUser 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; } } diff --git a/src/Infrastructure/Migrations/20240509215538_AddMoreInformationToTransaction.Designer.cs b/src/Infrastructure/Migrations/20240509215538_AddMoreInformationToTransaction.Designer.cs new file mode 100644 index 0000000..f8c9e81 --- /dev/null +++ b/src/Infrastructure/Migrations/20240509215538_AddMoreInformationToTransaction.Designer.cs @@ -0,0 +1,418 @@ +// +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 + { + /// + 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("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Created") + .HasColumnType("datetimeoffset"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("EmailAddress") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("LastModified") + .HasColumnType("datetimeoffset"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ReasonToJoin") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("SocialNetworkAccount") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("FutureCreators"); + }); + + modelBuilder.Entity("Hutopy.Domain.Entities.UserTransaction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Amount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("ApplicationUserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("Created") + .HasColumnType("datetimeoffset"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Currency") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("IsConfirmed") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetimeoffset"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Paid") + .HasColumnType("bit"); + + b.Property("StripeBillingDetailEmail") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StripeBillingDetailName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StripeChargeId") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StripeEventId") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StripePaymentIntent") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StripePaymentMethod") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StripeReceiptUrl") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("TipMessage") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationUserId"); + + b.ToTable("UserTransactions"); + }); + + modelBuilder.Entity("Hutopy.Infrastructure.Identity.ApplicationUser", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("bit"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("LockoutEnabled") + .HasColumnType("bit"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("bit"); + + b.Property("SecurityStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("TwoFactorEnabled") + .HasColumnType("bit"); + + b.Property("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("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("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", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderKey") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderDisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("RoleId") + .HasColumnType("nvarchar(450)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("Name") + .HasColumnType("nvarchar(450)"); + + b.Property("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", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Hutopy.Infrastructure.Identity.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Hutopy.Infrastructure.Identity.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", 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", b => + { + b.HasOne("Hutopy.Infrastructure.Identity.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Infrastructure/Migrations/20240509215538_AddMoreInformationToTransaction.cs b/src/Infrastructure/Migrations/20240509215538_AddMoreInformationToTransaction.cs new file mode 100644 index 0000000..d91d3cf --- /dev/null +++ b/src/Infrastructure/Migrations/20240509215538_AddMoreInformationToTransaction.cs @@ -0,0 +1,106 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Hutopy.Infrastructure.Migrations +{ + /// + public partial class AddMoreInformationToTransaction : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Paid", + table: "UserTransactions", + type: "bit", + nullable: false, + defaultValue: false); + + migrationBuilder.AddColumn( + name: "StripeBillingDetailEmail", + table: "UserTransactions", + type: "nvarchar(max)", + nullable: false, + defaultValue: ""); + + migrationBuilder.AddColumn( + name: "StripeBillingDetailName", + table: "UserTransactions", + type: "nvarchar(max)", + nullable: false, + defaultValue: ""); + + migrationBuilder.AddColumn( + name: "StripeChargeId", + table: "UserTransactions", + type: "nvarchar(max)", + nullable: false, + defaultValue: ""); + + migrationBuilder.AddColumn( + name: "StripeEventId", + table: "UserTransactions", + type: "nvarchar(max)", + nullable: false, + defaultValue: ""); + + migrationBuilder.AddColumn( + name: "StripePaymentIntent", + table: "UserTransactions", + type: "nvarchar(max)", + nullable: false, + defaultValue: ""); + + migrationBuilder.AddColumn( + name: "StripePaymentMethod", + table: "UserTransactions", + type: "nvarchar(max)", + nullable: false, + defaultValue: ""); + + migrationBuilder.AddColumn( + name: "StripeReceiptUrl", + table: "UserTransactions", + type: "nvarchar(max)", + nullable: false, + defaultValue: ""); + } + + /// + 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"); + } + } +} diff --git a/src/Infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs b/src/Infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs index dedb234..cb3b55f 100644 --- a/src/Infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/src/Infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs @@ -102,6 +102,37 @@ namespace Hutopy.Infrastructure.Migrations b.Property("LastModifiedBy") .HasColumnType("nvarchar(max)"); + b.Property("Paid") + .HasColumnType("bit"); + + b.Property("StripeBillingDetailEmail") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StripeBillingDetailName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StripeChargeId") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StripeEventId") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StripePaymentIntent") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StripePaymentMethod") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StripeReceiptUrl") + .IsRequired() + .HasColumnType("nvarchar(max)"); + b.Property("TipMessage") .IsRequired() .HasColumnType("nvarchar(max)"); diff --git a/src/Infrastructure/Stripe/StripeService.cs b/src/Infrastructure/Stripe/StripeService.cs index 881b6c4..dc84e34 100644 --- a/src/Infrastructure/Stripe/StripeService.cs +++ b/src/Infrastructure/Stripe/StripeService.cs @@ -1,13 +1,20 @@ using Stripe; using Stripe.Checkout; using Hutopy.Application.Common.Interfaces; +using Microsoft.AspNetCore.Http; +using Hutopy.Application.Common.Models; +using Hutopy.Application.Stripe.Commands; namespace Hutopy.Infrastructure.Stripe; public class StripeService : IStripeService { - public StripeService() + const string EndpointSecret = ""; + private readonly IHttpContextAccessor _httpContextAccessor; + + public StripeService(IHttpContextAccessor httpContextAccessor) { + _httpContextAccessor = httpContextAccessor; StripeConfiguration.ApiKey = ""; } @@ -31,7 +38,7 @@ public class StripeService : IStripeService ], Mode = "payment", UiMode = "embedded", - ReturnUrl = "https://zealous-bay-08204590f.5.azurestaticapps.net/paymentcompleted", + ReturnUrl = "https://hutopy.ca/paymentcompleted", }; var service = new SessionService(); @@ -39,4 +46,27 @@ public class StripeService : IStripeService return session.ClientSecret; } + + public Result ValidateTransaction(ConfirmStripeTransactionCommand request) + { + try + { + if (request.Data.Object.Status is "succeeded") + { + return new Result(true, new List()); + } + + return new Result(false, new List()); + + } + catch (StripeException e) + { + Console.WriteLine("Error: {0}", e.Message); + return new Result(false, new List{e.Message}); + } + catch (Exception e) + { + return new Result(false, new List{e.Message}); + } + } } diff --git a/src/Web/Endpoints/Stripe.cs b/src/Web/Endpoints/Stripe.cs index b944419..264f4c8 100644 --- a/src/Web/Endpoints/Stripe.cs +++ b/src/Web/Endpoints/Stripe.cs @@ -16,8 +16,8 @@ public class Stripe : EndpointGroupBase return sender.Send(command); } - private static Task ConfirmTransaction(ISender sender, ConfirmStripeTransactionCommand command) + private async static Task ConfirmTransaction(ISender sender, ConfirmStripeTransactionCommand command) { - return sender.Send(command); + return await sender.Send(command); } } diff --git a/src/Web/appsettings.Development.json b/src/Web/appsettings.Development.json index 84308c9..b98033c 100644 --- a/src/Web/appsettings.Development.json +++ b/src/Web/appsettings.Development.json @@ -2,9 +2,28 @@ "Logging": { "LogLevel": { "Default": "Information", - "Microsoft": "Warning", + "Microsoft": "Information", "Microsoft.AspNetCore.SpaProxy": "Information", "Microsoft.Hosting.Lifetime": "Information" } + }, + "Google": { + "ClientId": "468391910875-78sfopq1t12ulrv4f5vj227j45guuj66.apps.googleusercontent.com", + "ClientSecret": "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", + "ProjectId": "hutopy-420016", + "AuthUri": "https://accounts.google.com/o/oauth2/auth", + "TokenUri": "https://oauth2.googleapis.com/token", + "AuthProviderX509CertUrl": "https://www.googleapis.com/oauth2/v1/certs", + "RedirectUris": [ + "https://hutopy.ca", + "https://hutopy.com", + "http://localhost" + ], + "JavascriptOrigins": [ + "https://hutopy.ca", + "https://hutopy.com", + "http://localhost" + ] } } + diff --git a/src/Web/wwwroot/api/specification.json b/src/Web/wwwroot/api/specification.json index 5a1838c..9c7a80c 100644 --- a/src/Web/wwwroot/api/specification.json +++ b/src/Web/wwwroot/api/specification.json @@ -723,12 +723,94 @@ "type": "object", "additionalProperties": false, "properties": { - "userTransactionId": { - "type": "string", - "format": "guid" + "id": { + "type": "string" }, - "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" + }, + "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" } } },