SignalR, Sumup

This commit is contained in:
Adam 2024-05-20 15:19:21 +02:00
parent 96cfebdaaf
commit d4ed125cae
18 changed files with 517 additions and 44 deletions

View File

@ -62,7 +62,8 @@ namespace TIAMMobileApp
builder.Services.AddScoped<IWizardProcessor, WizardProcessor>();
builder.Services.AddScoped<IImageDataService, ImageDataService>();
builder.Services.AddSingleton(x => new ResourceManager("TIAMWebApp.Shared.Application.Resources", typeof(Main).Assembly));
builder.Services.AddSingleton<SignalRService>();
builder.Services.AddSingleton<SumupService>();
builder.Services.Configure<AnimationOptions>(Guid.NewGuid().ToString(), c => { });
return builder.Build();
}

View File

@ -0,0 +1,102 @@
@page "/chat"
@using TIAMWebApp.Shared.Application.Services
@inject SignalRService SignalRService
<h3>Chat</h3>
<div class="container mt-5">
<div class="card">
<div class="card-header">
<h3>Chat</h3>
</div>
<div class="card-body">
<ul class="list-unstyled">
@foreach (var message in messages)
{
<li class="mb-2">
<div class="alert alert-secondary" role="alert">
@message
</div>
</li>
}
</ul>
</div>
<div class="card-footer">
<div class="input-group">
<input @bind="newMessage" @bind:event="oninput" class="form-control" placeholder="Enter your message" />
<button @onclick="SendMessage" class="btn btn-primary">Send</button>
<button @onclick="MuteChat" class="btn btn-danger ms-2">Mute Chat</button>
</div>
</div>
</div>
</div>
<NavLink href="create-payment">
Create Payment
</NavLink>
@code {
private string userName;
private string newMessage;
private List<string> messages = new List<string>();
protected override async Task OnInitializedAsync()
{
userName = Guid.NewGuid().ToString();
SignalRService.OnMessageReceived += (user, message) =>
{
messages.Add($"{user}: {message}");
InvokeAsync(StateHasChanged);
};
SignalRService.OnUserMuted += (user) =>
{
messages.Add($"{user} has been muted.");
InvokeAsync(StateHasChanged);
};
SignalRService.OnUserLoggedIn += (user) =>
{
messages.Add($"{user} has logged in.");
InvokeAsync(StateHasChanged);
};
SignalRService.OnUserLoggedOut += (user) =>
{
messages.Add($"{user} has logged out.");
InvokeAsync(StateHasChanged);
};
await SignalRService.StartConnection(userName);
}
private async Task SendMessage()
{
await SignalRService.SendMessage(userName, newMessage);
newMessage = string.Empty;
}
private async Task MuteChat()
{
await SignalRService.MuteChat(userName);
}
private async Task LoggedInToChat()
{
await SignalRService.LoggedInToChat(userName);
}
private async Task LoggedOutFromChat()
{
await SignalRService.LoggedOutFromChat(userName);
}
public void Dispose()
{
SignalRService.OnMessageReceived -= (user, message) => { };
SignalRService.OnUserMuted -= (user) => { };
SignalRService.OnUserLoggedIn -= (user) => { };
LoggedOutFromChat();
SignalRService.OnUserLoggedOut -= (user) => { };
}
}

View File

@ -0,0 +1,22 @@
@page "/create-payment"
@using TIAM.Entities.Transfers
@using TIAMWebApp.Shared.Application.Services;
@using TIAMWebApp.Shared.Application.Models.ClientSide.Payment;
@inject SumupService SumUpService
<h3>Create Payment</h3>
<button @onclick="CreatePaymentLink">Create Payment Link</button>
@code {
private async Task CreatePaymentLink()
{
Transfer subject = new Transfer();
subject.Id = Guid.NewGuid();
subject.Price = 17000;
subject.ContactEmail = "fyloruta@citmo.net";
subject.OrderId = 1232131;
var paymentLink = await SumUpService.CreatePaymentLinkAsync(subject);
Console.WriteLine($"Payment Link: {paymentLink}");
}
}

View File

@ -1,4 +1,4 @@
@page "/transferdetails"
@page "/mytransfer"
<PageTitle>Transferdetails</PageTitle>
@ -76,5 +76,6 @@
@code {
[Parameter]
public Guid? TransferId { get; set; }
}

View File

@ -209,6 +209,7 @@
public List<string> ignoreList = new List<string>
{
"ReceiverEmailAddress",
"ReceiverFullName",
"ReceiverId",
"SenderEmailAddress",
"SenderFullName",

View File

@ -77,7 +77,16 @@
</ul>
</li>
}
if(enableChat)
{
<li class="nav-item">
<NavLink class="nav-link" href="chat">
Chat
</NavLink>
</li>
}
}
<li class="nav-item">

View File

@ -44,7 +44,7 @@ namespace TIAMSharedUI.Shared.Components
private bool enableTransfer = true;
private bool enableLanguage = false;
private bool enableApi = true;
private bool enableChat = true;
private bool collapseNavMenu = true;

View File

@ -41,6 +41,8 @@ builder.Services.AddScoped<SessionStorageAccessor>();
builder.Services.AddSingleton(x => new ResourceManager("TIAMWebApp.Client.Resources.MyResources", typeof(Program).Assembly));
builder.Services.AddSingleton<IAcLogWriterClientBase, BrowserConsoleLogWriter>();
builder.Services.AddSingleton<IAcLogWriterClientBase, HttpClientLogItemWriter>();
builder.Services.AddSingleton<SignalRService>();
builder.Services.AddSingleton<SumupService>();
//builder.Services.AddScoped<BrowserConsoleLogWriter>();

View File

@ -6,6 +6,8 @@ using AyCode.Entities.Server.LogItems;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json.Linq;
using System.Text.Json;
using TIAM.Database.DataLayers.Admins;
using TIAM.Database.DataLayers.Users;
using TIAM.Entities.Emails;
@ -14,6 +16,8 @@ using TIAM.Services.Server;
using TIAM.Services.Server.Logins;
using TIAMWebApp.Server.Services;
using TIAMWebApp.Shared.Application.Models;
using TIAMWebApp.Shared.Application.Models.ClientSide.Payment;
using TIAMWebApp.Shared.Application.Models.PageModels;
namespace TIAMWebApp.Server.Controllers
{
@ -24,16 +28,18 @@ namespace TIAMWebApp.Server.Controllers
public class PaymentAPIController : ControllerBase
{
private LoginService _loginService;
private AdminDal _adminDal;
private AdminDal _adminDal;
private SumupService _sumupService;
private readonly TIAM.Core.Loggers.ILogger _logger;
private readonly IMessageSenderService _messageSenderService;
public PaymentAPIController(LoginService loginService, AdminDal adminDal, IMessageSenderService messageSenderService, IEnumerable<IAcLogWriterBase> logWriters)
public PaymentAPIController(AdminDal adminDal, SumupService sumupService, IMessageSenderService messageSenderService, IEnumerable<IAcLogWriterBase> logWriters)
{
_loginService = loginService;
_adminDal = adminDal;
_adminDal = adminDal;
_sumupService = sumupService;
_messageSenderService = messageSenderService;
_logger = new TIAM.Core.Loggers.Logger<PaymentAPIController>(logWriters.ToArray());
}
@ -42,9 +48,22 @@ namespace TIAMWebApp.Server.Controllers
[AllowAnonymous]
[HttpPost]
[Route(APIUrls.CreatePaymentRouteName)]
public async Task CreatePayment(Transfer? paymentItem)
public async Task<string> CreatePayment([FromBody] JsonElement paymentItem)
{
throw new NotImplementedException();
_logger.Detail($"CreatePayment called! {paymentItem.ToString()}");
var transferToPay = JObject.Parse(paymentItem.GetRawText()).ToObject<Transfer>();
var paymentRequest = new SumupPaymentRequest
{
CheckoutReference = transferToPay.OrderId.ToString(),
Amount = transferToPay.Price.ToString(),
Currency = "HUF",
PayToEmail = "adam.g@aycode.com",
Description = "Test transfer payment"
};
var result = await _sumupService.CreateCheckout(paymentRequest);
_logger.Detail($"CreatePayment result: {result}");
var checkoutUrl = result.CheckoutUrl;
return checkoutUrl;
}
}

View File

@ -328,6 +328,8 @@ namespace TIAMWebApp.Server.Controllers
{
_logger.Info($"Created transfer, send emailMessage!!!");
var message = new MessageSenderModel<EmailMessage>();
message.Message = new EmailMessage();
message.MessageType = AyCode.Models.Enums.MessageTypesEnum.email;
message.Message.Subject = "[Tour I Am] New transfer in Budapest";
message.Message.ContextId = createdTransfer.Id;
message.Message.SenderId = Guid.Empty;

View File

@ -17,8 +17,9 @@ using Microsoft.Extensions.DependencyInjection;
using TIAMWebApp.Server.Controllers;
using TIAMWebApp.Server.Services;
using Microsoft.AspNetCore.Server.Kestrel.Core;
//using TIAM.Database.DataLayers.ServiceProviders;
using Microsoft.AspNetCore.SignalR;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Hosting;
var builder = WebApplication.CreateBuilder(args);
@ -28,6 +29,7 @@ const string myAllowSpecificOrigins = "_myAllowSpecificOrigins";
builder.Services.AddControllersWithViews();
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
builder.Services.AddSingleton<LoggerToLoggerApiController>();
builder.Services.AddSingleton<IAcLogWriterBase, ConsoleLogWriter>();
@ -38,23 +40,24 @@ builder.Services.AddScoped<AdminDal>();
builder.Services.AddScoped<AuctionDal>();
builder.Services.AddScoped<TransferDestinationDal>();
builder.Services.AddSignalR();
builder.Services.AddCors(options => {
options.AddPolicy(myAllowSpecificOrigins,
policy => {
builder.Services.AddCors(options =>
{
options.AddPolicy(myAllowSpecificOrigins, policy =>
{
policy.AllowAnyOrigin();
});
});
builder.Services.AddSwaggerGen(swagger =>
{
swagger.SwaggerDoc("v1",
new OpenApiInfo
{
Title = "API Title",
Version = "V1",
Description = "API Description"
});
swagger.SwaggerDoc("v1", new OpenApiInfo
{
Title = "API Title",
Version = "V1",
Description = "API Description"
});
var securitySchema = new OpenApiSecurityScheme
{
@ -72,19 +75,19 @@ builder.Services.AddSwaggerGen(swagger =>
swagger.AddSecurityDefinition(securitySchema.Reference.Id, securitySchema);
swagger.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{securitySchema,Array.Empty<string>() }
{ securitySchema, Array.Empty<string>() }
});
});
builder.Services.AddAuthentication(f =>
builder.Services.AddAuthentication(options =>
{
f.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
f.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(k =>
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
var key = Encoding.UTF8.GetBytes(builder.Configuration["JWT:Key"]);
k.SaveToken = true;
k.TokenValidationParameters = new TokenValidationParameters
options.SaveToken = true;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
@ -95,17 +98,31 @@ builder.Services.AddAuthentication(f =>
IssuerSigningKey = new SymmetricSecurityKey(key),
ClockSkew = TimeSpan.Zero
};
});
builder.Services.Configure<FormOptions>(options =>
{
options.MultipartBodyLengthLimit = 200000000; // Increase the limit to 200MB
});
builder.Services.AddHttpClient<SumupService>(client =>
{
//ApiKey: sup_sk_0rt9IFrMpE9qA6328vqMwCtiCntRXZxGR
client.BaseAddress = new Uri("https://api.sumup.com/");
});
builder.Services.AddTransient(provider =>
new SumupService(
provider.GetRequiredService<HttpClient>(),
//"your-client-id",
//"your-client-secret"
"cc_classic_FHVxlhLMqFfX3ZqBe5GW4gEhySnuq",
"cc_sk_classic_SDxnPImsFMmw0ApNk2KlmFwqgHN29FClMvH3op3Kkus6Zn5Ge3",
provider.GetRequiredService<IEnumerable<IAcLogWriterBase>>()
));
builder.Services.AddScoped<IMessageSenderService, MessageSenderService>();
builder.Services.AddScoped<TransferBackendService, TransferBackendService>();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.ConfigureApplicationCookie(options =>
{
@ -124,11 +141,8 @@ builder.WebHost.ConfigureKestrel((context, options) =>
});
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
@ -137,7 +151,6 @@ if (app.Environment.IsDevelopment())
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
@ -147,19 +160,19 @@ app.UseSwaggerUI(c =>
c.SwaggerEndpoint("/swagger/v1/swagger.json", "v1");
});
//app.UseHttpsRedirection();
app.UseBlazorFrameworkFiles();
app.UseStaticFiles();
app.UseAuthentication();
app.UseRouting();
//cors
app.UseCors(myAllowSpecificOrigins);
app.UseAuthorization();
app.UsePathBase("/");
app.MapRazorPages();
app.MapControllers();
app.MapBlazorHub();
app.MapHub<SignalRHub>("/myhub");
app.MapFallbackToFile("index.html");
app.Run();

View File

@ -0,0 +1,34 @@
using Microsoft.AspNetCore.SignalR;
namespace TIAMWebApp.Server.Services;
public class SignalRHub : Hub
{
public async Task SendMessage(string user, string message)
{
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
public async Task MuteChat(string user)
{
await Clients.All.SendAsync("UserMuted", user);
}
public async Task LoggedInToChat(string user)
{
await Clients.All.SendAsync("UserLoggedInToChat", user);
}
public async Task LoggedOutFromChat(string user)
{
await Clients.All.SendAsync("UserLoggedOutFromChat", user);
}
}

View File

@ -0,0 +1,123 @@
using AyCode.Core.Loggers;
using AyCode.Services.Loggers;
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using TIAM.Core.Loggers;
using TIAMWebApp.Shared.Application.Models.ClientSide.Payment;
using TIAMWebApp.Shared.Application.Services;
using TIAMWebApp.Shared.Application.Utility;
namespace TIAMWebApp.Server.Services
{
public class SumupService
{
private readonly HttpClient _httpClient;
private readonly TIAM.Core.Loggers.ILogger _logger;
private readonly string _clientId;
private readonly string _clientSecret;
public SumupService(HttpClient httpClient, string clientId, string clientSecret, IEnumerable<IAcLogWriterBase> logWriters)
{
_httpClient = httpClient;
_clientId = clientId;
_clientSecret = clientSecret;
_logger = new TIAM.Core.Loggers.Logger<SumupService>(logWriters.ToArray());
}
public async Task<string> GetAccessTokenAsync()
{
var authToken = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{_clientId}:{_clientSecret}"));
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", authToken);
var requestContent = new StringContent("grant_type=client_credentials", Encoding.UTF8, "application/x-www-form-urlencoded");
var response = await _httpClient.PostAsync("https://api.sumup.com/token", requestContent);
response.EnsureSuccessStatusCode();
var responseContent = await response.Content.ReadAsStringAsync();
var tokenResponse = JsonSerializer.Deserialize<SumUpTokenResponse>(responseContent);
return tokenResponse.AccessToken;
}
public async Task<string> CreatePaymentLinkAsync(SumupPaymentRequest paymentRequest)
{
var accessToken = await GetAccessTokenAsync();
//_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "sup_sk_0rt9IFrMpE9qA6328vqMwCtiCntRXZxGR");
var requestContent = new StringContent(JsonSerializer.Serialize(paymentRequest), Encoding.UTF8, "application/json");
var response = await _httpClient.PostAsync("https://api.sumup.com/v0.1/checkouts", requestContent);
response.EnsureSuccessStatusCode();
var responseContent = await response.Content.ReadAsStringAsync();
var paymentResponse = JsonSerializer.Deserialize<PaymentResponse>(responseContent);
return paymentResponse.CheckoutUrl;
}
public async Task<PaymentResponse> CreateCheckout(SumupPaymentRequest request)
{
var options = new JsonSerializerOptions
{
PropertyNamingPolicy = new LowercaseNamingPolicy(),
PropertyNameCaseInsensitive = true // To ensure deserialization works regardless of case
};
var checkoutRequest = new HttpRequestMessage(HttpMethod.Post, "https://api.sumup.com/v0.1/checkouts");
checkoutRequest.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "sup_sk_0rt9IFrMpE9qA6328vqMwCtiCntRXZxGR");
var kvaanyad = JsonSerializer.Serialize(request, options);
_logger.Detail($"Request json: {kvaanyad}");
checkoutRequest.Content = new StringContent(kvaanyad, Encoding.UTF8, "application/json");
_logger.Detail($"Request content: { await checkoutRequest.Content.ReadAsStringAsync()}");
var response = await _httpClient.SendAsync(checkoutRequest);
_logger.Detail(await response.Content.ReadAsStringAsync());
if (response.IsSuccessStatusCode)
{
var jsonResponse = await response.Content.ReadAsStringAsync();
var bleh = response.Content.ToString();
var valami = JsonSerializer.Deserialize<PaymentResponse>(jsonResponse);
return valami;
}
var errorResponse = await response.Content.ReadAsStringAsync();
throw new Exception($"Unable to create checkout. Response: {errorResponse}");
}
}
public class SumUpTokenResponse
{
[JsonPropertyName("access_token")]
public string AccessToken { get; set; }
}
// SumUpService.cs (continued)
public class PaymentResponse
{
[JsonPropertyName("checkout_url")]
public string CheckoutUrl { get; set; }
}
public class LowercaseNamingPolicy : JsonNamingPolicy
{
public override string ConvertName(string name)
{
return name.ToLowerInvariant();
}
}
}

View File

@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
namespace TIAMWebApp.Shared.Application.Models.ClientSide.Payment
{
public class SumupPaymentRequest
{
[JsonPropertyName("checkout_reference")]
public string CheckoutReference { get; set; }
[JsonPropertyName("amount")]
public string Amount { get; set; }
[JsonPropertyName("currency")]
public string Currency { get; set; }
[JsonPropertyName("pay_to_email")]
public string PayToEmail { get; set; }
[JsonPropertyName("description")]
public string Description { get; set; }
}
}

View File

@ -38,7 +38,7 @@ namespace TIAMWebApp.Shared.Application.Models.ClientSide.UI.WizardModels
Text = obj.Content,
EmailAddress = obj.SenderEmailAddress,
Recipients = new List<EmailRecipient> { new EmailRecipient(
Guid.NewGuid(), obj.ReceiverId, id, "recipient@aycode.com") }
Guid.NewGuid(), obj.ReceiverId, id, obj.ReceiverEmailAddress) }
};
}

View File

@ -0,0 +1,73 @@
using Microsoft.AspNetCore.SignalR.Client;
using System;
using System.Threading.Tasks;
using TIAMWebApp.Shared.Application.Models.ClientSide;
namespace TIAMWebApp.Shared.Application.Services
{
public class SignalRService
{
private HubConnection _hubConnection;
public event Action<string, string> OnMessageReceived;
public event Action<string> OnUserMuted;
public event Action<string> OnUserLoggedIn;
public event Action<string> OnUserLoggedOut;
public async Task StartConnection(string userName)
{
_hubConnection = new HubConnectionBuilder()
.WithUrl($"{Setting.BaseUrl}/myhub")
.Build();
_hubConnection.On<string, string>("ReceiveMessage", (user, message) =>
{
OnMessageReceived?.Invoke(user, message);
});
_hubConnection.On<string>("UserMuted", (user) =>
{
OnUserMuted?.Invoke(user);
});
_hubConnection.On<string>("UserLoggedInToChat", (user) =>
{
OnUserLoggedIn?.Invoke(user);
});
_hubConnection.On<string>("UserLoggedOutFromChat", (user) =>
{
OnUserLoggedOut?.Invoke(user);
});
await _hubConnection.StartAsync();
await LoggedInToChat(userName);
}
public async Task SendMessage(string user, string message)
{
await _hubConnection.SendAsync("SendMessage", user, message);
}
public async Task MuteChat(string user)
{
await _hubConnection.SendAsync("MuteChat", user);
}
public async Task LoggedInToChat(string user)
{
await _hubConnection.SendAsync("LoggedInToChat", user);
}
public async Task LoggedOutFromChat(string user)
{
await _hubConnection.SendAsync("LoggedOutFromChat", user);
}
public async Task StopConnection()
{
await _hubConnection.StopAsync();
await _hubConnection.DisposeAsync();
}
}
}

View File

@ -0,0 +1,47 @@
using AyCode.Services.Loggers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Json;
using System.Text;
using System.Threading.Tasks;
using TIAM.Entities.Transfers;
using TIAMWebApp.Shared.Application.Models.ClientSide;
using TIAMWebApp.Shared.Application.Models;
using TIAMWebApp.Shared.Application.Utility;
using TIAM.Core.Loggers;
using TIAMWebApp.Shared.Application.Models.ClientSide.Payment;
using static System.Net.WebRequestMethods;
using Newtonsoft.Json;
using TIAM.Models.Dtos.Users;
namespace TIAMWebApp.Shared.Application.Services
{
public class SumupService
{
private readonly HttpClient _http;
private readonly ILogger _logger;
public SumupService(HttpClient http, IEnumerable<IAcLogWriterClientBase> logWriters)
{
_http = http;
_logger = new LoggerClient<TransferDataService>(logWriters.ToArray());
}
public async Task<string> CreatePaymentLinkAsync(Transfer transferToPay)
{
var url = $"{Setting.ApiBaseUrl}/{APIUrls.CreatePayment}";
//var url = $"{APIUrls.GetTransferDestinations}";
_logger.Info(url);
var response = await _http.PostAsJsonAsync(url, transferToPay);
if (response == null)
return "Not ok";
var result = await response.Content.ReadAsStringAsync();
var paymentLink = result;
return paymentLink;
}
}
}

View File

@ -24,6 +24,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="8.0.5" />
<PackageReference Include="Microsoft.AspNetCore.Http.Features" Version="5.0.17" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="8.0.5" />
<PackageReference Include="Microsoft.JSInterop" Version="8.0.5" />
<PackageReference Include="SkiaSharp" Version="2.88.8" />
<PackageReference Include="SkiaSharp.Views.Desktop.Common" Version="2.88.8" />