MEGA POG STRIPE WORKS
This commit is contained in:
83
src/Imprink.WebApi/Controllers/StripeController.cs
Normal file
83
src/Imprink.WebApi/Controllers/StripeController.cs
Normal file
@@ -0,0 +1,83 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Stripe;
|
||||
|
||||
namespace Imprink.WebApi.Controllers;
|
||||
|
||||
public record CreatePaymentIntentRequest(int Amount, string OrderId);
|
||||
|
||||
[ApiController]
|
||||
[Route("api/stripe")]
|
||||
public class StripeController : ControllerBase
|
||||
{
|
||||
[HttpPost("create-payment-intent")]
|
||||
public async Task<IActionResult> CreatePaymentIntent([FromBody] CreatePaymentIntentRequest request)
|
||||
{
|
||||
try
|
||||
{
|
||||
var options = new PaymentIntentCreateOptions
|
||||
{
|
||||
Amount = request.Amount,
|
||||
Currency = "usd",
|
||||
PaymentMethodTypes = ["card"],
|
||||
Metadata = new Dictionary<string, string>
|
||||
{
|
||||
{ "order_id", request.OrderId }
|
||||
}
|
||||
};
|
||||
|
||||
var service = new PaymentIntentService();
|
||||
var paymentIntent = await service.CreateAsync(options);
|
||||
|
||||
return Ok(new { clientSecret = paymentIntent.ClientSecret });
|
||||
}
|
||||
catch (StripeException ex)
|
||||
{
|
||||
return BadRequest(new { error = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost("webhook")]
|
||||
public async Task<IActionResult> HandleWebhook()
|
||||
{
|
||||
var json = await new StreamReader(Request.Body).ReadToEndAsync();
|
||||
|
||||
try
|
||||
{
|
||||
var stripeEvent = EventUtility.ConstructEvent(
|
||||
json,
|
||||
Request.Headers["Stripe-Signature"],
|
||||
"whsec_9HyZxZ2HseAkiuRvr4MEP4ntcns9n7FA"
|
||||
);
|
||||
|
||||
Console.WriteLine($"Received Stripe event: {stripeEvent.Type}");
|
||||
|
||||
if (stripeEvent.Type == "payment_intent.succeeded")
|
||||
{
|
||||
var paymentIntent = stripeEvent.Data.Object as PaymentIntent;
|
||||
var orderId = paymentIntent?.Metadata?.GetValueOrDefault("order_id") ?? "Unknown";
|
||||
|
||||
Console.WriteLine($"✅ Order {orderId} confirmed");
|
||||
}
|
||||
else if (stripeEvent.Type == "payment_intent.payment_failed")
|
||||
{
|
||||
var paymentIntent = stripeEvent.Data.Object as PaymentIntent;
|
||||
var orderId = paymentIntent?.Metadata?.GetValueOrDefault("order_id") ?? "Unknown";
|
||||
|
||||
Console.WriteLine($"❌ Order {orderId} payment failed");
|
||||
}
|
||||
|
||||
return Ok();
|
||||
}
|
||||
catch (StripeException ex)
|
||||
{
|
||||
Console.WriteLine($"Webhook error: {ex.Message}");
|
||||
return BadRequest();
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("health")]
|
||||
public IActionResult HealthCheck()
|
||||
{
|
||||
return Ok(new { status = "OK" });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using Imprink.Application;
|
||||
using Imprink.Application.Services;
|
||||
using Imprink.Domain.Repositories;
|
||||
using Imprink.Infrastructure;
|
||||
using Imprink.Infrastructure.Database;
|
||||
using Imprink.Infrastructure.Repositories;
|
||||
using Imprink.Infrastructure.Services;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Imprink.WebApi.Extensions;
|
||||
|
||||
public static class StartupApplicationExtensions
|
||||
{
|
||||
public static void AddBusinessLogic(this IServiceCollection services)
|
||||
{
|
||||
services.AddScoped<IProductRepository, ProductRepository>();
|
||||
services.AddScoped<IProductVariantRepository, ProductVariantRepository>();
|
||||
services.AddScoped<ICategoryRepository, CategoryRepository>();
|
||||
services.AddScoped<IRoleRepository, RoleRepository>();
|
||||
services.AddScoped<IUserRepository, UserRepository>();
|
||||
services.AddScoped<IUserRoleRepository, UserRoleRepository>();
|
||||
services.AddScoped<IOrderRepository, OrderRepository>();
|
||||
services.AddScoped<IOrderItemRepository, OrderItemRepository>();
|
||||
services.AddScoped<IUnitOfWork, UnitOfWork>();
|
||||
services.AddScoped<ICurrentUserService, CurrentUserService>();
|
||||
services.AddScoped<Seeder>();
|
||||
}
|
||||
|
||||
public static void AddDatabaseContexts(this IServiceCollection services, IConfiguration configuration)
|
||||
{
|
||||
services.AddDbContext<ApplicationDbContext>(options =>
|
||||
options.UseSqlServer(
|
||||
configuration.GetConnectionString("DefaultConnection"),
|
||||
sqlOptions => sqlOptions.MigrationsAssembly(typeof(ApplicationDbContext).Assembly.FullName)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
namespace Imprink.WebApi.Extensions;
|
||||
|
||||
public static class StartupEnvironmentExtensions
|
||||
{
|
||||
public static void ConfigureEnvironment(this IApplicationBuilder app, IWebHostEnvironment env)
|
||||
{
|
||||
if (app is not WebApplication) return;
|
||||
|
||||
if (env.IsDevelopment())
|
||||
{
|
||||
app.UseSwagger();
|
||||
app.UseSwaggerUI();
|
||||
}
|
||||
else
|
||||
{
|
||||
app.UseExceptionHandler("/Error");
|
||||
app.UseHsts();
|
||||
app.UseHttpsRedirection();
|
||||
}
|
||||
}
|
||||
}
|
||||
24
src/Imprink.WebApi/Extensions/StartupMigrationExtensions.cs
Normal file
24
src/Imprink.WebApi/Extensions/StartupMigrationExtensions.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using Imprink.Infrastructure.Database;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Imprink.WebApi.Extensions;
|
||||
|
||||
public static class StartupMigrationExtensions
|
||||
{
|
||||
public static void ApplyMigrations(this IApplicationBuilder app)
|
||||
{
|
||||
if (app is not WebApplication application) return;
|
||||
|
||||
using var scope = application.Services.CreateScope();
|
||||
var dbContext = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
|
||||
try
|
||||
{
|
||||
dbContext.Database.Migrate();
|
||||
Console.WriteLine("Database migrations applied successfully");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"An error occurred while applying migrations: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
46
src/Imprink.WebApi/Extensions/StartupSecurityExtensions.cs
Normal file
46
src/Imprink.WebApi/Extensions/StartupSecurityExtensions.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
using System.Security.Claims;
|
||||
using Imprink.Infrastructure.Database;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
|
||||
namespace Imprink.WebApi.Extensions;
|
||||
|
||||
public static class StartupSecurityExtensions
|
||||
{
|
||||
public static void AddJwtAuthentication(this IServiceCollection services, IConfiguration configuration)
|
||||
{
|
||||
services.AddAuthentication(options =>
|
||||
{
|
||||
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||
})
|
||||
.AddJwtBearer(options =>
|
||||
{
|
||||
options.Authority = configuration["Auth0:Authority"];
|
||||
options.Audience = configuration["Auth0:Audience"];
|
||||
|
||||
options.Events = new JwtBearerEvents
|
||||
{
|
||||
OnTokenValidated = context =>
|
||||
{
|
||||
var dbContext = context.HttpContext.RequestServices.GetService<ApplicationDbContext>();
|
||||
var userId = context.Principal?.FindFirst(ClaimTypes.NameIdentifier)?.Value
|
||||
?? context.Principal?.FindFirst("sub")?.Value;
|
||||
|
||||
if (string.IsNullOrEmpty(userId)) return Task.CompletedTask;
|
||||
var identity = context.Principal!.Identity as ClaimsIdentity;
|
||||
|
||||
var roles = (
|
||||
from ur in dbContext?.UserRole
|
||||
join r in dbContext?.Roles on ur.RoleId equals r.Id
|
||||
where ur.UserId == userId
|
||||
select r.RoleName).ToList();
|
||||
|
||||
foreach (var role in roles) identity!.AddClaim(new Claim(ClaimTypes.Role, role));
|
||||
identity!.AddClaim(new Claim(ClaimTypes.Role, "User"));
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
43
src/Imprink.WebApi/Extensions/StartupSwaggerExtensions.cs
Normal file
43
src/Imprink.WebApi/Extensions/StartupSwaggerExtensions.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
using Microsoft.OpenApi.Models;
|
||||
|
||||
namespace Imprink.WebApi.Extensions;
|
||||
|
||||
public static class StartupSwaggerExtensions
|
||||
{
|
||||
public static void AddSwaggerWithJwtSecurity(this IServiceCollection services)
|
||||
{
|
||||
services.AddSwaggerGen(options =>
|
||||
{
|
||||
options.SwaggerDoc("v1", new OpenApiInfo
|
||||
{
|
||||
Title = "Imprink API",
|
||||
Version = "v1",
|
||||
});
|
||||
|
||||
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
|
||||
{
|
||||
Description = "JWT Authorization header using the Bearer scheme.",
|
||||
Name = "Authorization",
|
||||
In = ParameterLocation.Header,
|
||||
Type = SecuritySchemeType.Http,
|
||||
Scheme = "Bearer",
|
||||
BearerFormat = "JWT"
|
||||
});
|
||||
|
||||
options.AddSecurityRequirement(new OpenApiSecurityRequirement
|
||||
{
|
||||
{
|
||||
new OpenApiSecurityScheme
|
||||
{
|
||||
Reference = new OpenApiReference
|
||||
{
|
||||
Type = ReferenceType.SecurityScheme,
|
||||
Id = "Bearer"
|
||||
}
|
||||
},
|
||||
[]
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@
|
||||
</PackageReference>
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Seq" Version="9.0.0" />
|
||||
<PackageReference Include="Stripe.net" Version="48.2.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="8.1.1" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -32,8 +33,4 @@
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Extensions\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Imprink.WebApi;
|
||||
using Serilog;
|
||||
using Stripe;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
@@ -10,6 +11,8 @@ Startup.ConfigureServices(builder);
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
StripeConfiguration.ApiKey = "sk_test_51RaxJBRrcXIyofFGsAGYs1umsvqQVmc6stk3R5lumc1qO2Aq6G0EXgCgDeaJ6aHHJ0pyOz4YDglnceKK7eeNUCOx00VBoIIn2z";
|
||||
|
||||
Startup.Configure(app, app.Environment);
|
||||
|
||||
app.Run();
|
||||
|
||||
@@ -1,19 +1,12 @@
|
||||
using System.Security.Claims;
|
||||
using FluentValidation;
|
||||
using Imprink.Application;
|
||||
using Imprink.Application.Commands.Products;
|
||||
using Imprink.Application.Services;
|
||||
using Imprink.Application.Validation.Users;
|
||||
using Imprink.Domain.Repositories;
|
||||
using Imprink.Infrastructure;
|
||||
using Imprink.Infrastructure.Database;
|
||||
using Imprink.Infrastructure.Repositories;
|
||||
using Imprink.Infrastructure.Services;
|
||||
using Imprink.WebApi.Extensions;
|
||||
using Imprink.WebApi.Filters;
|
||||
using Imprink.WebApi.Middleware;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.OpenApi.Models;
|
||||
|
||||
namespace Imprink.WebApi;
|
||||
|
||||
@@ -23,142 +16,35 @@ public static class Startup
|
||||
{
|
||||
var services = builder.Services;
|
||||
|
||||
services.AddScoped<IProductRepository, ProductRepository>();
|
||||
services.AddScoped<IProductVariantRepository, ProductVariantRepository>();
|
||||
services.AddScoped<ICategoryRepository, CategoryRepository>();
|
||||
services.AddScoped<IRoleRepository, RoleRepository>();
|
||||
services.AddScoped<IUserRepository, UserRepository>();
|
||||
services.AddScoped<IUserRoleRepository, UserRoleRepository>();
|
||||
services.AddScoped<IOrderRepository, OrderRepository>();
|
||||
services.AddScoped<IOrderItemRepository, OrderItemRepository>();
|
||||
services.AddScoped<IUnitOfWork, UnitOfWork>();
|
||||
services.AddScoped<ICurrentUserService, CurrentUserService>();
|
||||
services.AddScoped<Seeder>();
|
||||
services.AddBusinessLogic();
|
||||
|
||||
services.AddAutoMapper(typeof(MappingProfile).Assembly);
|
||||
|
||||
services.AddHttpContextAccessor();
|
||||
|
||||
services.AddDbContext<ApplicationDbContext>(options =>
|
||||
options.UseSqlServer(
|
||||
builder.Configuration.GetConnectionString("DefaultConnection"),
|
||||
b => b.MigrationsAssembly(typeof(ApplicationDbContext).Assembly.FullName)));
|
||||
services.AddDatabaseContexts(builder.Configuration);
|
||||
|
||||
services.AddMediatR(cfg =>
|
||||
{
|
||||
cfg.RegisterServicesFromAssembly(typeof(CreateProductHandler).Assembly);
|
||||
});
|
||||
services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(typeof(CreateProductHandler).Assembly));
|
||||
|
||||
services.AddValidatorsFromAssembly(typeof(Auth0UserValidator).Assembly);
|
||||
|
||||
services.AddAuthentication(options =>
|
||||
{
|
||||
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||
})
|
||||
.AddJwtBearer(options =>
|
||||
{
|
||||
options.Authority = builder.Configuration["Auth0:Authority"];
|
||||
options.Audience = builder.Configuration["Auth0:Audience"];
|
||||
|
||||
options.Events = new JwtBearerEvents
|
||||
{
|
||||
OnTokenValidated = context =>
|
||||
{
|
||||
var dbContext = context.HttpContext.RequestServices.GetService<ApplicationDbContext>();
|
||||
var userId = context.Principal?.FindFirst(ClaimTypes.NameIdentifier)?.Value
|
||||
?? context.Principal?.FindFirst("sub")?.Value;
|
||||
|
||||
if (string.IsNullOrEmpty(userId)) return Task.CompletedTask;
|
||||
var identity = context.Principal!.Identity as ClaimsIdentity;
|
||||
|
||||
var roles = (
|
||||
from ur in dbContext?.UserRole
|
||||
join r in dbContext?.Roles on ur.RoleId equals r.Id
|
||||
where ur.UserId == userId
|
||||
select r.RoleName).ToList();
|
||||
|
||||
foreach (var role in roles) identity!.AddClaim(new Claim(ClaimTypes.Role, role));
|
||||
identity!.AddClaim(new Claim(ClaimTypes.Role, "User"));
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
};
|
||||
});
|
||||
services.AddJwtAuthentication(builder.Configuration);
|
||||
|
||||
services.AddAuthorization();
|
||||
|
||||
services.AddControllers(options =>
|
||||
{
|
||||
options.Filters.Add<ValidationActionFilter>();
|
||||
});
|
||||
|
||||
services.AddSwaggerGen(options =>
|
||||
{
|
||||
options.SwaggerDoc("v1", new OpenApiInfo
|
||||
{
|
||||
Title = "Imprink API",
|
||||
Version = "v1",
|
||||
});
|
||||
|
||||
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
|
||||
{
|
||||
Description = "JWT Authorization header using the Bearer scheme.",
|
||||
Name = "Authorization",
|
||||
In = ParameterLocation.Header,
|
||||
Type = SecuritySchemeType.Http,
|
||||
Scheme = "Bearer",
|
||||
BearerFormat = "JWT"
|
||||
});
|
||||
|
||||
options.AddSecurityRequirement(new OpenApiSecurityRequirement
|
||||
{
|
||||
{
|
||||
new OpenApiSecurityScheme
|
||||
{
|
||||
Reference = new OpenApiReference
|
||||
{
|
||||
Type = ReferenceType.SecurityScheme,
|
||||
Id = "Bearer"
|
||||
}
|
||||
},
|
||||
[]
|
||||
}
|
||||
});
|
||||
});
|
||||
services.AddControllers(options => options.Filters.Add<ValidationActionFilter>());
|
||||
|
||||
services.AddSwaggerWithJwtSecurity();
|
||||
}
|
||||
|
||||
public static void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
||||
{
|
||||
if (app is WebApplication application)
|
||||
{
|
||||
using var scope = application.Services.CreateScope();
|
||||
var dbContext = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
|
||||
try
|
||||
{
|
||||
dbContext.Database.Migrate();
|
||||
Console.WriteLine("Database migrations applied successfully");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"An error occurred while applying migrations: {ex.Message}");
|
||||
}
|
||||
}
|
||||
app.ApplyMigrations();
|
||||
|
||||
app.UseGlobalExceptionHandling();
|
||||
app.UseRequestTiming();
|
||||
|
||||
if (env.IsDevelopment())
|
||||
{
|
||||
app.UseSwagger();
|
||||
app.UseSwaggerUI();
|
||||
}
|
||||
else
|
||||
{
|
||||
app.UseExceptionHandler("/Error");
|
||||
app.UseHsts();
|
||||
app.UseHttpsRedirection();
|
||||
}
|
||||
app.ConfigureEnvironment(env);
|
||||
|
||||
app.UseRouting();
|
||||
app.UseAuthentication();
|
||||
|
||||
Reference in New Issue
Block a user