Skip to content

Commit d35dec7

Browse files
committed
Implemented stripe webhook and updated appSettings
1 parent 5dc68e1 commit d35dec7

File tree

7 files changed

+81
-5
lines changed

7 files changed

+81
-5
lines changed

src/Abstractions/IPaymentService.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@
22
public interface IPaymentService
33
{
44
Task<string> CreatePaymentIntentAsync(int orderId, decimal ammount, CancellationToken cancellationToken);
5+
Task HandleWebhookAsync(HttpRequest request);
56
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
namespace dotnet_qrshop.Features.Payments.Webhook;
2+
3+
public enum StripeEventType
4+
{
5+
CheckoutSessionCompleted,
6+
PaymentIntentPaymentFailed,
7+
Unknown
8+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
namespace dotnet_qrshop.Features.Payments.Webhook;
2+
3+
public static class StripeEventTypeParser
4+
{
5+
public static StripeEventType Parse(string eventType)
6+
{
7+
return eventType switch
8+
{
9+
"checkout.session.completed" => StripeEventType.CheckoutSessionCompleted,
10+
"payment_intent.payment_failed" => StripeEventType.PaymentIntentPaymentFailed,
11+
_ => StripeEventType.Unknown
12+
};
13+
}
14+
}

src/Program.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
using Microsoft.AspNetCore.Identity;
1515
using Microsoft.EntityFrameworkCore;
1616
using Microsoft.IdentityModel.Tokens;
17+
using Stripe;
1718
using System.Reflection;
1819
using System.Text;
1920

@@ -76,6 +77,11 @@
7677
// Authorization
7778
builder.Services.AddAuthorization();
7879

80+
// Stripe
81+
StripeConfiguration.ApiKey = builder.Configuration["StripeApiKey"];
82+
83+
// Remaining Services
84+
7985
builder.Services.AddHttpContextAccessor();
8086
builder.Services.AddScoped<IUserContext, UserContext>();
8187
builder.Services.AddScoped<ITokenProvider, TokenProvider>();
@@ -84,6 +90,7 @@
8490
builder.Services.AddScoped<IOrderService, OrderService>();
8591
builder.Services.AddScoped<IPaymentService, StripePaymentService>();
8692

93+
// CORS
8794
builder.Services.AddCors(options =>
8895
{
8996
options.AddPolicy("AllowAll", policy =>
@@ -96,6 +103,7 @@
96103
});
97104
});
98105

106+
//
99107
var app = builder.Build();
100108

101109
// Run migrations on startup (we need to ensure that db's up)

src/Services/StripePaymentService.cs

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,23 @@
11
using dotnet_qrshop.Abstractions;
2+
using dotnet_qrshop.Features.Payments.Webhook;
23
using Stripe;
34

45
namespace dotnet_qrshop.Services;
56

6-
public class StripePaymentService(
7-
IConfiguration _configuration) : IPaymentService
7+
public class StripePaymentService : IPaymentService
88
{
9+
private readonly string _apiSecretKey;
10+
private readonly string _webhookSecret;
11+
12+
public StripePaymentService(IConfiguration configuration)
13+
{
14+
_apiSecretKey = configuration["Stripe:SecretKey"] ?? throw new InvalidOperationException("Stripe Api secret not configured.");
15+
_webhookSecret = configuration["Stripe:WebhookSecret"] ?? throw new InvalidOperationException("Stripe Webhook secret not configured.");
16+
}
17+
918
public async Task<string> CreatePaymentIntentAsync(int orderId, decimal amount, CancellationToken cancellationToken)
1019
{
11-
StripeConfiguration.ApiKey = _configuration["StripeApiKey"];
20+
StripeConfiguration.ApiKey = _apiSecretKey;
1221

1322
var options = new PaymentIntentCreateOptions
1423
{
@@ -22,4 +31,36 @@ public async Task<string> CreatePaymentIntentAsync(int orderId, decimal amount,
2231

2332
return paymentIntent.ClientSecret;
2433
}
34+
35+
public async Task HandleWebhookAsync(HttpRequest request)
36+
{
37+
var json = await new StreamReader(request.Body).ReadToEndAsync();
38+
Event stripeEvent;
39+
40+
try
41+
{
42+
stripeEvent = EventUtility.ConstructEvent(
43+
json,
44+
request.Headers["Stripe-Signature"],
45+
_webhookSecret
46+
);
47+
}
48+
catch (StripeException e)
49+
{
50+
// logger.LogError(e, "⚠️ Webhook signature verification failed."); TODO DYLAN: Add Logger
51+
throw new BadHttpRequestException("Invalid signature");
52+
}
53+
54+
var parsedEvent = StripeEventTypeParser.Parse(stripeEvent.Type);
55+
56+
switch (parsedEvent)
57+
{
58+
case StripeEventType.CheckoutSessionCompleted:
59+
// handle
60+
break;
61+
case StripeEventType.PaymentIntentPaymentFailed:
62+
// handle
63+
break;
64+
}
65+
}
2566
}

src/appsettings.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,9 @@
1515
"Audience": "ApplicationUser",
1616
"ExpirationInMinutes": 60
1717
},
18-
"StripeApiKey": "..."
18+
"Stripe": {
19+
"SecretKey": "...",
20+
"WebhookSecret": "..."
21+
22+
}
1923
}

src/dotnet-qrshop.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@
8484
</PackageReference>
8585
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" />
8686
<PackageReference Include="Scrutor" Version="6.0.1" />
87-
<PackageReference Include="Stripe.net" Version="48.5.0" />
87+
<PackageReference Include="Stripe.net" Version="49.0.0" />
8888
<PackageReference Include="Swashbuckle.AspNetCore" Version="8.1.1" />
8989
<PackageReference Include="System.Collections" Version="4.3.0" />
9090
</ItemGroup>

0 commit comments

Comments
 (0)