From dc36f91353c85126117180181af9e3ef6df25595 Mon Sep 17 00:00:00 2001 From: Derek Holloway Date: Sun, 29 Jun 2025 19:00:25 -0700 Subject: [PATCH] Start work for stripe payments --- .env_Template | 3 +- docker-compose.yml | 3 +- src/MistoxWebsite.Client/package-lock.json | 10 +++ src/MistoxWebsite.Client/package.json | 1 + .../pages/store/catalog/catalog.component.ts | 2 +- .../pages/store/payment/payment.component.css | 24 +++++++ .../store/payment/payment.component.html | 8 +++ .../pages/store/payment/payment.component.ts | 62 +++++++++++++++++++ .../Controllers/PaymentController.cs | 12 +++- .../Controllers/PaymentMethods/IPayment.cs | 1 + src/MistoxWebsite.Server/Program.cs | 53 ++++++++++++---- 11 files changed, 164 insertions(+), 15 deletions(-) create mode 100644 src/MistoxWebsite.Client/src/app/pages/store/payment/payment.component.css create mode 100644 src/MistoxWebsite.Client/src/app/pages/store/payment/payment.component.html create mode 100644 src/MistoxWebsite.Client/src/app/pages/store/payment/payment.component.ts diff --git a/.env_Template b/.env_Template index dac4027..98f082d 100755 --- a/.env_Template +++ b/.env_Template @@ -1,6 +1,7 @@ Payment_Service=StripeIntent # Options are [ StripeIntent ] -Stripe_Key= +Stripe_PublicKey= +Stripe_PublicKey= Stripe_Endpoint_Secret= MySQL_Server=mistox-database diff --git a/docker-compose.yml b/docker-compose.yml index e755aad..d6a2a42 100755 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,7 +6,8 @@ services: restart: always environment: - PaymentService=${Payment_Service} - - StripeKey=${Stripe_Key} + - StripePublicKey=${Stripe_PublicKey} + - StripeApiKey=${Stripe_ApiKey} - StripeEndpointSecret=&{Stripe_Endpoint_Secret} - MySQLServer=${MySQL_Server} - MySQLUser=${MySQL_User} diff --git a/src/MistoxWebsite.Client/package-lock.json b/src/MistoxWebsite.Client/package-lock.json index a741f68..f4ea280 100644 --- a/src/MistoxWebsite.Client/package-lock.json +++ b/src/MistoxWebsite.Client/package-lock.json @@ -14,6 +14,7 @@ "@angular/forms": "^20.0.0", "@angular/platform-browser": "^20.0.0", "@angular/router": "^20.0.0", + "@stripe/stripe-js": "^7.4.0", "rxjs": "~7.8.0", "tslib": "^2.3.0", "zone.js": "~0.15.0" @@ -3196,6 +3197,15 @@ "dev": true, "license": "MIT" }, + "node_modules/@stripe/stripe-js": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@stripe/stripe-js/-/stripe-js-7.4.0.tgz", + "integrity": "sha512-lQHQPfXPTBeh0XFjq6PqSBAyR7umwcJbvJhXV77uGCUDD6ymXJU/f2164ydLMLCCceNuPlbV9b+1smx98efwWQ==", + "license": "MIT", + "engines": { + "node": ">=12.16" + } + }, "node_modules/@tufjs/canonical-json": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@tufjs/canonical-json/-/canonical-json-2.0.0.tgz", diff --git a/src/MistoxWebsite.Client/package.json b/src/MistoxWebsite.Client/package.json index afafcbb..bf61fc6 100644 --- a/src/MistoxWebsite.Client/package.json +++ b/src/MistoxWebsite.Client/package.json @@ -16,6 +16,7 @@ "@angular/forms": "^20.0.0", "@angular/platform-browser": "^20.0.0", "@angular/router": "^20.0.0", + "@stripe/stripe-js": "^7.4.0", "rxjs": "~7.8.0", "tslib": "^2.3.0", "zone.js": "~0.15.0" diff --git a/src/MistoxWebsite.Client/src/app/pages/store/catalog/catalog.component.ts b/src/MistoxWebsite.Client/src/app/pages/store/catalog/catalog.component.ts index f97a88c..4ee173c 100644 --- a/src/MistoxWebsite.Client/src/app/pages/store/catalog/catalog.component.ts +++ b/src/MistoxWebsite.Client/src/app/pages/store/catalog/catalog.component.ts @@ -8,7 +8,7 @@ import { Authentication } from '../../../services/Authentication'; import { Product } from 'app/models/Product'; @Component({ - selector: 'catalog', + selector: 'store-catalog', templateUrl: './catalog.component.html', styleUrl: './catalog.component.css', imports: [ FormsModule, CommonModule, RouterModule ] diff --git a/src/MistoxWebsite.Client/src/app/pages/store/payment/payment.component.css b/src/MistoxWebsite.Client/src/app/pages/store/payment/payment.component.css new file mode 100644 index 0000000..30d2f6c --- /dev/null +++ b/src/MistoxWebsite.Client/src/app/pages/store/payment/payment.component.css @@ -0,0 +1,24 @@ +#payment-form { + max-width: 500px; + margin: 0 auto; +} + +.form-group { + margin-bottom: 20px; +} + +#card-element { + padding: 12px; + border: 1px solid #ccc; + border-radius: 4px; + background: #fff; +} + +#submit { + background-color: #5469d4; + color: white; + padding: 12px 20px; + border: none; + border-radius: 4px; + cursor: pointer; +} \ No newline at end of file diff --git a/src/MistoxWebsite.Client/src/app/pages/store/payment/payment.component.html b/src/MistoxWebsite.Client/src/app/pages/store/payment/payment.component.html new file mode 100644 index 0000000..52aca46 --- /dev/null +++ b/src/MistoxWebsite.Client/src/app/pages/store/payment/payment.component.html @@ -0,0 +1,8 @@ +
+
+ +
+ +
+ +
\ No newline at end of file diff --git a/src/MistoxWebsite.Client/src/app/pages/store/payment/payment.component.ts b/src/MistoxWebsite.Client/src/app/pages/store/payment/payment.component.ts new file mode 100644 index 0000000..8c285f2 --- /dev/null +++ b/src/MistoxWebsite.Client/src/app/pages/store/payment/payment.component.ts @@ -0,0 +1,62 @@ +import { Component } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { FormsModule } from '@angular/forms'; +import { Router, ActivatedRoute, RouterModule } from '@angular/router'; +import { Title } from '@angular/platform-browser'; +import { CommonModule } from '@angular/common'; +import { Authentication } from '../../../services/Authentication'; +import { loadStripe, Stripe, StripeElements } from '@stripe/stripe-js'; +import { firstValueFrom } from 'rxjs'; + +@Component({ + selector: 'store-payment', + templateUrl: './payment.component.html', + styleUrl: './payment.component.css', + imports: [ FormsModule, CommonModule, RouterModule ] +}) +export class PaymentComponent { + + stripe: Stripe | null = null; + elements: StripeElements | null = null; + + async ngOnInit(){ + + let ApiKey = await firstValueFrom(this.http.get("/api/payment/publickey")); + + this.stripe = await loadStripe(ApiKey); + if (this.stripe){ + this.elements = this.stripe?.elements(); + } + } + + ngAfterViewInit(){ + if (this.elements){ + const cardStyle = { + base: { + color: '#32325d', + fontFamily: '"Helvetica Neue", Helvetica, sans-serif', + fontSize: '16px', + '::placeholder': { + color: '#aab7c4', + } + }, + invalid: { + color: '#fa755a', + } + } + const card = this.elements.create('card', { style: cardStyle }); + card.mount('#card-element'); + } + } + + constructor( private http: HttpClient, private router: Router, private route: ActivatedRoute, private title: Title, public auth: Authentication ) { + this.title.setTitle("Payment | Mistox"); + + http.post("api/product/getall", null).subscribe( + response => { + + } + ) + }; + +} \ No newline at end of file diff --git a/src/MistoxWebsite.Server/Controllers/PaymentController.cs b/src/MistoxWebsite.Server/Controllers/PaymentController.cs index 38f025b..130a052 100755 --- a/src/MistoxWebsite.Server/Controllers/PaymentController.cs +++ b/src/MistoxWebsite.Server/Controllers/PaymentController.cs @@ -44,8 +44,18 @@ namespace MistoxWebsite.Server.Controllers { return "Unable to find account"; } } + + [Route("/api/payment/publickey")] + [HttpGet] + public IActionResult GetPaymentKey() { + try { + return Ok(IPayment._PublicKey); + } catch (Exception ex) { + return NotFound(ex.ToString()); + } + } - [Route( "/api/payment/response" )] + [Route("/api/payment/response")] [HttpPost] public async Task paymentWebhook() { try { diff --git a/src/MistoxWebsite.Server/Controllers/PaymentMethods/IPayment.cs b/src/MistoxWebsite.Server/Controllers/PaymentMethods/IPayment.cs index 2d21310..f0cb018 100644 --- a/src/MistoxWebsite.Server/Controllers/PaymentMethods/IPayment.cs +++ b/src/MistoxWebsite.Server/Controllers/PaymentMethods/IPayment.cs @@ -6,6 +6,7 @@ namespace MistoxWebsite.Server.Controllers.Payment { public static PaymentType _PaymentType; public static string _EndpointSecret = ""; + public static string _PublicKey = ""; public Task<(bool, string)> TryGetCheckoutToken(string OrderNumber, Account user, Cart[] cart); public Task ValidatePurchase(string WebHookData, string Headers); diff --git a/src/MistoxWebsite.Server/Program.cs b/src/MistoxWebsite.Server/Program.cs index b5d14b9..1418cc5 100755 --- a/src/MistoxWebsite.Server/Program.cs +++ b/src/MistoxWebsite.Server/Program.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Authentication.Cookies; -using MistoxWebsite.Server.Controllers; using MistoxWebsite.Server.Controllers.Payment; using MistoxWebsite.Server.Services; using MistoxWebsite.Server.Services.DatabaseService; @@ -11,40 +10,72 @@ var builder = WebApplication.CreateBuilder(args); #pragma warning disable CS8600 #pragma warning disable CS8604 -// Database Service +//////////////////////////////// +/////// Database Service /////// +//////////////////////////////// + +// Address string? _dbserver = Environment.GetEnvironmentVariable("MySQLServer"); string dbserver = !string.IsNullOrEmpty(_dbserver) ? _dbserver : "localhost"; -string? _dbuser = Environment.GetEnvironmentVariable("MySQLUser"); -string dbUser = !string.IsNullOrEmpty(_dbuser) ? _dbuser : "root"; + +// Database string? _dbdatabase = Environment.GetEnvironmentVariable("MySQLDatabase"); string dbdatabase = !string.IsNullOrEmpty(_dbdatabase) ? _dbdatabase : "mistox"; + +// UserName +string? _dbuser = Environment.GetEnvironmentVariable("MySQLUser"); +string dbUser = !string.IsNullOrEmpty(_dbuser) ? _dbuser : "root"; + +// Password string? _dbpass = Environment.GetEnvironmentVariable("MySQLPass"); string dbPass = !string.IsNullOrEmpty(_dbpass) ? _dbpass : "oasv34$8gpv023dd"; -string connStr = "server=" + dbserver + ";user=" + dbUser + ";database=" + dbdatabase + ";password=" + dbPass + ";port=3306;"; -DatabaseService databaseService = new DatabaseService( connectionString: connStr ); + +// Create the database serivice +DatabaseService databaseService = new DatabaseService(connectionString: "server=" + dbserver + ";user=" + dbUser + ";database=" + dbdatabase + ";password=" + dbPass + ";port=3306;"); builder.Services.Add( new ServiceDescriptor( typeof( DatabaseService ), databaseService ) ); -// Email Service +//////////////////////////////// +///////// Email Service //////// +//////////////////////////////// + +// Address string? _eServer = Environment.GetEnvironmentVariable("EmailServer"); string EmailServer = !string.IsNullOrEmpty(_eServer) ? _eServer : "mail.mistox.com"; + +// Port string? _ePort = Environment.GetEnvironmentVariable("EmailPort"); int EmailPort = !string.IsNullOrEmpty(_ePort) ? Convert.ToInt32(_ePort) : 587; + +// User string? _eAddress = Environment.GetEnvironmentVariable("EmailAddress"); string EmailAddress = !string.IsNullOrEmpty(_eAddress) ? _eAddress : "no-reply@mistox.com"; + +// Password string? _ePassword = Environment.GetEnvironmentVariable("EmailPassword"); string EmailPassword = !string.IsNullOrEmpty(_ePassword) ? _ePassword : ""; + +// Create the email service EmailService Emailservice = new EmailService( EmailServer, EmailPort, EmailAddress, EmailPassword ); builder.Services.Add( new ServiceDescriptor( typeof( EmailService ), Emailservice )); -// Payment Service +//////////////////////////////// +/////// Payment Service //////// +//////////////////////////////// + +// Payment service name -> must be name of PaymentType enum string? PaymentService = Environment.GetEnvironmentVariable("PaymentService"); IPayment._PaymentType = (PaymentType)Enum.Parse(typeof(PaymentType), PaymentService, true); if (IPayment._PaymentType == PaymentType.StripeIntent) { - string? StripeKey = Environment.GetEnvironmentVariable("StripeKey"); - StripeConfiguration.ApiKey = StripeKey; + // Get PublicKey + string? StripePublicKey = Environment.GetEnvironmentVariable("StripePublicKey"); + IPayment._PublicKey = string.IsNullOrEmpty(StripePublicKey) ? "" : StripePublicKey; + // Get PrivateKey + string? StripeAPIKey = Environment.GetEnvironmentVariable("StripeApiKey"); + StripeConfiguration.ApiKey = StripeAPIKey; + // Get Endpoint secret string? StripeEndpointKey = Environment.GetEnvironmentVariable("StripeEndpointSecret"); - IPayment._EndpointSecret = string.IsNullOrEmpty(StripeEndpointKey) ? "" : StripeEndpointKey ; + IPayment._EndpointSecret = string.IsNullOrEmpty(StripeEndpointKey) ? "" : StripeEndpointKey; } // Authentication Service