Make payments dependency injected based on the .env settings

This commit is contained in:
2025-06-20 18:31:18 -07:00
parent c4ad0f2c10
commit fb2a2c6ae9
8 changed files with 111 additions and 83 deletions
+1
View File
@@ -13,3 +13,4 @@ trim_trailing_whitespace = false
csharp_new_line_before_open_brace = none csharp_new_line_before_open_brace = none
csharp_new_line_before_catch = false csharp_new_line_before_catch = false
csharp_new_line_before_finally = false csharp_new_line_before_finally = false
csharp_new_line_after_else = false
+3
View File
@@ -1,4 +1,7 @@
Payment_Service=StripeIntent # Options are [ StripeIntent ]
Stripe_Key= Stripe_Key=
Stripe_Endpoint_Secret=
MySQL_Server=mistox-database MySQL_Server=mistox-database
MySQL_User=root MySQL_User=root
+1 -6
View File
@@ -1,5 +1,4 @@
Fix stripe payments *Updated API* Stripe payments aren't currently tested
Havent Tested
After a new account is created notify a user that they need to verify their email before logging in After a new account is created notify a user that they need to verify their email before logging in
@@ -30,9 +29,5 @@ DTO
Cannot send dotnet List -> must be arrays Cannot send dotnet List -> must be arrays
Need to update local server to use www-formdata instead of json Need to update local server to use www-formdata instead of json
PaymentController
PaymentResponse is still tied to Stripe.
Need to change to Interface so different services can be interchanged
ProductController ProductController
Need to figure out new way to download purchased items as there is currently no way Need to figure out new way to download purchased items as there is currently no way
+2
View File
@@ -5,7 +5,9 @@ services:
image: mistox-website:latest image: mistox-website:latest
restart: always restart: always
environment: environment:
- PaymentService=${Payment_Service}
- StripeKey=${Stripe_Key} - StripeKey=${Stripe_Key}
- StripeEndpointSecret=&{Stripe_Endpoint_Secret}
- MySQLServer=${MySQL_Server} - MySQLServer=${MySQL_Server}
- MySQLUser=${MySQL_User} - MySQLUser=${MySQL_User}
- MySQLPass=${MySQL_Pass} - MySQLPass=${MySQL_Pass}
@@ -2,107 +2,59 @@
using MistoxWebsite.Server.Controllers.Payment; using MistoxWebsite.Server.Controllers.Payment;
using MistoxWebsite.Server.Services.DatabaseService; using MistoxWebsite.Server.Services.DatabaseService;
using MistoxWebsite.Server.Entities; using MistoxWebsite.Server.Entities;
using Microsoft.Extensions.Primitives;
namespace MistoxWebsite.Server.Controllers { namespace MistoxWebsite.Server.Controllers {
[ApiController] [ApiController]
public class PaymentController : ControllerBase { public class PaymentController : ControllerBase {
DatabaseService _databaseService; DatabaseService _databaseService;
IPayment _paymentService;
public PaymentController( DatabaseService databaseService ) { public PaymentController(DatabaseService databaseService) {
_databaseService = databaseService; _databaseService = databaseService;
if (IPayment._PaymentType == PaymentType.StripeIntent) {
_paymentService = new StripeIntent(_databaseService);
} else {
// Fallback
_paymentService = new StripeIntent(_databaseService);
}
// Add new payment plugins here
} }
// Charges [Route("api/getCheckoutToken")]
[Route( "api/getCheckoutToken" )]
[HttpPost] [HttpPost]
public async Task<string> GetPaymentKey( [FromQuery] string userID ) { public async Task<string> GetPaymentKey( [FromQuery] string userID ) {
string OrderNumber = Guid.NewGuid().ToString().Substring(0,10); string OrderNumber = Guid.NewGuid().ToString().Substring(0,10);
Account? acc = await _databaseService.GetAccount(userID); Account? acc = await _databaseService.GetAccount(userID);
if (acc != null) { if (acc != null) {
List<Cart> cart = await _databaseService.GetCart(acc); List<Cart> cart = await _databaseService.GetCart(acc);
(bool, string) PaymentResponse = await _paymentService.TryGetCheckoutToken(OrderNumber, acc, cart);
IPayment PaymentPlugin = new StripeIntent(_databaseService);
(bool, string) PaymentResponse = await PaymentPlugin.Purchase(OrderNumber, acc, cart);
if (PaymentResponse.Item1) { if (PaymentResponse.Item1) {
// Returns client secret
return PaymentResponse.Item2; return PaymentResponse.Item2;
} } else {
else {
Console.WriteLine("An error has occured in the payment plugin\n\n"); Console.WriteLine("An error has occured in the payment plugin\n\n");
Console.WriteLine(PaymentResponse.Item2); Console.WriteLine(PaymentResponse.Item2);
Console.WriteLine("\n"); Console.WriteLine("\n");
return "0"; return "An error has occured in the payment plugin";
} }
} else {
return "Unable to find account";
} }
return "0";
} }
[Route( "/api/payment/response" )] [Route( "/api/payment/response" )]
[HttpPost] [HttpPost]
public async Task<IActionResult> paymentWebhook() { public async Task<IActionResult> paymentWebhook() {
try { try {
const string endpointSecret = "whsec_HCO7uv2BPIPmUPOiSg9tfwLZul8usCGG";
string body = await new StreamReader(Request.Body).ReadToEndAsync(); string body = await new StreamReader(Request.Body).ReadToEndAsync();
Stripe.Event e = Stripe.EventUtility.ConstructEvent( body, Request.Headers["Stripe-Signature"], endpointSecret ); await _paymentService.ValidatePurchase(body, Request.Headers["Stripe-Signature"].ToString());
if( e.Type == "payment_intent.succeeded" ) {
// Extract Data from payment confirm
Stripe.PaymentIntent intent = (Stripe.PaymentIntent)e.Data.Object;
string orderNumber = "";
int userID = 0;
List<int> productIDs = new List<int>();
int subtotal = 0;
int total = 0;
KeyValuePair<string, string>[] y = intent.Metadata.ToArray();
foreach( KeyValuePair<string, string> cur in y ) {
string val = cur.Key;
if( val == "ordernumber" ) {
orderNumber = cur.Value;
} else if( val == "user" ) {
userID = int.Parse( cur.Value );
} else if( val == "products" ) {
string[] products = cur.Value.Split(',');
foreach( string product in products ) {
if ( !string.IsNullOrEmpty(product) ) {
productIDs.Add( Convert.ToInt32( product ) );
}
}
} else if( val == "subtotal" ) {
subtotal = int.Parse( cur.Value );
} else if( val == "total" ) {
total = int.Parse( cur.Value );
}
}
// Clear the cart
Account account = new() {
ID = userID
};
await _databaseService.ClearCart( account );
// Add data to misox receipt
for( int i = 0; i < productIDs.Count; i++ ) {
int product = productIDs[i];
await _databaseService.NewReceipt( new Receipt {
AccountID = userID,
ProductID = product,
ReceiptID = orderNumber,
Time = DateTime.Now,
TaxAmount = total - subtotal,
TotalCost = total,
LineItem = i
} );
}
} else {
Console.WriteLine( "Unhandled event type: {0}", e.Type );
}
return Ok(); return Ok();
} catch( Exception ex ) { } catch (Exception ex) {
return Content(ex.ToString()); return NotFound(ex.ToString());
} }
} }
@@ -4,8 +4,16 @@ namespace MistoxWebsite.Server.Controllers.Payment {
public interface IPayment { public interface IPayment {
public Task<(bool, string)> Purchase(string OrderNumber, Account user, List<Cart> cart); public static PaymentType _PaymentType;
public static string _EndpointSecret = "";
public Task<(bool, string)> TryGetCheckoutToken(string OrderNumber, Account user, List<Cart> cart);
public Task ValidatePurchase(string WebHookData, string Headers);
} }
public enum PaymentType {
StripeIntent
}
} }
@@ -8,11 +8,11 @@ namespace MistoxWebsite.Server.Controllers {
DatabaseService _databaseService; DatabaseService _databaseService;
public StripeIntent( DatabaseService databaseService ) { public StripeIntent(DatabaseService databaseService) {
_databaseService = databaseService; _databaseService = databaseService;
} }
public async Task<(bool, string)> Purchase(string OrderNumber, Account user, List<Cart> cart) { public async Task<(bool, string)> TryGetCheckoutToken(string OrderNumber, Account user, List<Cart> cart) {
try { try {
// build Recipt and calculate Tax // build Recipt and calculate Tax
var options = new Stripe.Tax.CalculationCreateOptions { var options = new Stripe.Tax.CalculationCreateOptions {
@@ -72,12 +72,71 @@ namespace MistoxWebsite.Server.Controllers {
Stripe.PaymentIntent x = await intentService.CreateAsync(paymentIntent); Stripe.PaymentIntent x = await intentService.CreateAsync(paymentIntent);
return (true, x.ClientSecret); return (true, x.ClientSecret);
} catch(Exception e) { } catch (Exception e) {
return (false, e.ToString()); return (false, e.ToString());
} }
} }
public async Task ValidatePurchase(string WebHookData, string Headers) {
Stripe.Event e = Stripe.EventUtility.ConstructEvent( WebHookData, Headers, IPayment._EndpointSecret );
if (e.Type == "payment_intent.succeeded") {
// Extract Data from payment confirm
Stripe.PaymentIntent intent = (Stripe.PaymentIntent)e.Data.Object;
string orderNumber = "";
int userID = 0;
List<int> productIDs = new List<int>();
int subtotal = 0;
int total = 0;
KeyValuePair<string, string>[] y = intent.Metadata.ToArray();
foreach (KeyValuePair<string, string> cur in y) {
string val = cur.Key;
if (val == "ordernumber") {
orderNumber = cur.Value;
}
else if (val == "user") {
userID = int.Parse(cur.Value);
}
else if (val == "products") {
string[] products = cur.Value.Split(',');
foreach (string product in products) {
if (!string.IsNullOrEmpty(product)) {
productIDs.Add(Convert.ToInt32(product));
}
}
}
else if (val == "subtotal") {
subtotal = int.Parse(cur.Value);
}
else if (val == "total") {
total = int.Parse(cur.Value);
}
}
// Clear the cart
Account account = new() {
ID = userID
};
await _databaseService.ClearCart(account);
// Add data to misox receipt
for (int i = 0; i < productIDs.Count; i++) {
int product = productIDs[i];
await _databaseService.NewReceipt(new Receipt {
AccountID = userID,
ProductID = product,
ReceiptID = orderNumber,
Time = DateTime.Now,
TaxAmount = total - subtotal,
TotalCost = total,
LineItem = i
});
}
} else {
Console.WriteLine("Unhandled event type: {0}", e.Type);
}
}
} }
} }
+10 -2
View File
@@ -1,5 +1,6 @@
using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authentication.Cookies;
using MistoxWebsite.Server.Controllers; using MistoxWebsite.Server.Controllers;
using MistoxWebsite.Server.Controllers.Payment;
using MistoxWebsite.Server.Services; using MistoxWebsite.Server.Services;
using MistoxWebsite.Server.Services.DatabaseService; using MistoxWebsite.Server.Services.DatabaseService;
using Stripe; using Stripe;
@@ -37,8 +38,15 @@ EmailService Emailservice = new EmailService( EmailServer, EmailPort, EmailAddre
builder.Services.Add( new ServiceDescriptor( typeof( EmailService ), Emailservice )); builder.Services.Add( new ServiceDescriptor( typeof( EmailService ), Emailservice ));
// Payment Service // Payment Service
string? StripeKey = Environment.GetEnvironmentVariable("StripeKey"); string? PaymentService = Environment.GetEnvironmentVariable("PaymentService");
StripeConfiguration.ApiKey = StripeKey; IPayment._PaymentType = (PaymentType)Enum.Parse(typeof(PaymentType), PaymentService, true);
if (IPayment._PaymentType == PaymentType.StripeIntent) {
string? StripeKey = Environment.GetEnvironmentVariable("StripeKey");
StripeConfiguration.ApiKey = StripeKey;
string? StripeEndpointKey = Environment.GetEnvironmentVariable("StripeEndpointSecret");
IPayment._EndpointSecret = string.IsNullOrEmpty(StripeEndpointKey) ? "" : StripeEndpointKey ;
}
// Authentication Service // Authentication Service
builder.Services.AddAuthentication( options => { builder.Services.AddAuthentication( options => {