diff --git a/src/Services/Payment/Payment.API/.editorconfig b/src/Services/Payment/Payment.API/.editorconfig
new file mode 100644
index 000000000..a2816621e
--- /dev/null
+++ b/src/Services/Payment/Payment.API/.editorconfig
@@ -0,0 +1,132 @@
+###############################
+# Core EditorConfig Options #
+###############################
+root = true
+# All files
+[*]
+indent_style = space
+
+# XML project files
+[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}]
+indent_size = 2
+
+# XML config files
+[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}]
+indent_size = 2
+
+# Code files
+[*.{cs,csx,vb,vbx}]
+indent_size = 4
+insert_final_newline = true
+charset = utf-8-bom
+###############################
+# .NET Coding Conventions #
+###############################
+[*.{cs,vb}]
+# Organize usings
+dotnet_sort_system_directives_first = true
+# this. preferences
+dotnet_style_qualification_for_field = false:silent
+dotnet_style_qualification_for_property = false:silent
+dotnet_style_qualification_for_method = false:silent
+dotnet_style_qualification_for_event = false:silent
+# Language keywords vs BCL types preferences
+dotnet_style_predefined_type_for_locals_parameters_members = true:silent
+dotnet_style_predefined_type_for_member_access = true:silent
+# Parentheses preferences
+dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent
+dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent
+dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent
+dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent
+# Modifier preferences
+dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent
+dotnet_style_readonly_field = true:suggestion
+# Expression-level preferences
+dotnet_style_object_initializer = true:suggestion
+dotnet_style_collection_initializer = true:suggestion
+dotnet_style_explicit_tuple_names = true:suggestion
+dotnet_style_null_propagation = true:suggestion
+dotnet_style_coalesce_expression = true:suggestion
+dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent
+dotnet_style_prefer_inferred_tuple_names = true:suggestion
+dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
+dotnet_style_prefer_auto_properties = true:silent
+dotnet_style_prefer_conditional_expression_over_assignment = true:silent
+dotnet_style_prefer_conditional_expression_over_return = true:silent
+###############################
+# Naming Conventions #
+###############################
+# Style Definitions
+dotnet_naming_style.pascal_case_style.capitalization = pascal_case
+# Use PascalCase for constant fields
+dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion
+dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields
+dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style
+dotnet_naming_symbols.constant_fields.applicable_kinds = field
+dotnet_naming_symbols.constant_fields.applicable_accessibilities = *
+dotnet_naming_symbols.constant_fields.required_modifiers = const
+###############################
+# C# Coding Conventions #
+###############################
+[*.cs]
+# var preferences
+csharp_style_var_for_built_in_types = true:silent
+csharp_style_var_when_type_is_apparent = true:silent
+csharp_style_var_elsewhere = true:silent
+# Expression-bodied members
+csharp_style_expression_bodied_methods = false:silent
+csharp_style_expression_bodied_constructors = false:silent
+csharp_style_expression_bodied_operators = false:silent
+csharp_style_expression_bodied_properties = true:silent
+csharp_style_expression_bodied_indexers = true:silent
+csharp_style_expression_bodied_accessors = true:silent
+# Pattern matching preferences
+csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
+csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
+# Null-checking preferences
+csharp_style_throw_expression = true:suggestion
+csharp_style_conditional_delegate_call = true:suggestion
+# Modifier preferences
+csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion
+# Expression-level preferences
+csharp_prefer_braces = true:silent
+csharp_style_deconstructed_variable_declaration = true:suggestion
+csharp_prefer_simple_default_expression = true:suggestion
+csharp_style_prefer_local_over_anonymous_function = true:suggestion
+csharp_style_inlined_variable_declaration = true:suggestion
+###############################
+# C# Formatting Rules #
+###############################
+# New line preferences
+csharp_new_line_before_open_brace = all
+csharp_new_line_before_else = true
+csharp_new_line_before_catch = true
+csharp_new_line_before_finally = true
+csharp_new_line_before_members_in_object_initializers = true
+csharp_new_line_before_members_in_anonymous_types = true
+csharp_new_line_between_query_expression_clauses = true
+# Indentation preferences
+csharp_indent_case_contents = true
+csharp_indent_switch_labels = true
+csharp_indent_labels = flush_left
+# Space preferences
+csharp_space_after_cast = false
+csharp_space_after_keywords_in_control_flow_statements = true
+csharp_space_between_method_call_parameter_list_parentheses = false
+csharp_space_between_method_declaration_parameter_list_parentheses = false
+csharp_space_between_parentheses = false
+csharp_space_before_colon_in_inheritance_clause = true
+csharp_space_after_colon_in_inheritance_clause = true
+csharp_space_around_binary_operators = before_and_after
+csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
+csharp_space_between_method_call_name_and_opening_parenthesis = false
+csharp_space_between_method_call_empty_parameter_list_parentheses = false
+# Wrapping preferences
+csharp_preserve_single_line_statements = true
+csharp_preserve_single_line_blocks = true
+###############################
+# VB Coding Conventions #
+###############################
+[*.vb]
+# Modifier preferences
+visual_basic_preferred_modifier_order = Partial,Default,Private,Protected,Public,Friend,NotOverridable,Overridable,MustOverride,Overloads,Overrides,MustInherit,NotInheritable,Static,Shared,Shadows,ReadOnly,WriteOnly,Dim,Const,WithEvents,Widening,Narrowing,Custom,Async:suggestion
\ No newline at end of file
diff --git a/src/Services/Payment/Payment.API/GlobalUsings.cs b/src/Services/Payment/Payment.API/GlobalUsings.cs
index fb2a84e1b..01ba3682b 100644
--- a/src/Services/Payment/Payment.API/GlobalUsings.cs
+++ b/src/Services/Payment/Payment.API/GlobalUsings.cs
@@ -25,4 +25,5 @@ global using Serilog;
global using System.Threading.Tasks;
global using System;
global using System.IO;
-global using Microsoft.AspNetCore.Hosting;
\ No newline at end of file
+global using Microsoft.AspNetCore.Hosting;
+global using Microsoft.Extensions.Hosting;
diff --git a/src/Services/Payment/Payment.API/Payment.API.csproj b/src/Services/Payment/Payment.API/Payment.API.csproj
index 61cfd2050..cd40ebdca 100644
--- a/src/Services/Payment/Payment.API/Payment.API.csproj
+++ b/src/Services/Payment/Payment.API/Payment.API.csproj
@@ -35,4 +35,12 @@
+
+
+
+
+
+
+
+
diff --git a/src/Services/Payment/Payment.API/Program.cs b/src/Services/Payment/Payment.API/Program.cs
index 3e49d1ee5..acfc73217 100644
--- a/src/Services/Payment/Payment.API/Program.cs
+++ b/src/Services/Payment/Payment.API/Program.cs
@@ -1,14 +1,98 @@
-var configuration = GetConfiguration();
+var appName = "Payment.API";
+var builder = WebApplication.CreateBuilder(new WebApplicationOptions
+{
+ Args = args,
+ ApplicationName = typeof(Program).Assembly.FullName,
+ ContentRootPath = Directory.GetCurrentDirectory()
+});
+if (builder.Configuration.GetValue("UseVault", false))
+{
+ TokenCredential credential = new ClientSecretCredential(
+ builder.Configuration["Vault:TenantId"],
+ builder.Configuration["Vault:ClientId"],
+ builder.Configuration["Vault:ClientSecret"]);
+ builder.Configuration.AddAzureKeyVault(new Uri($"https://{builder.Configuration["Vault:Name"]}.vault.azure.net/"), credential);
+}
+builder.Configuration.SetBasePath(Directory.GetCurrentDirectory());
+builder.Configuration.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
+builder.Configuration.AddEnvironmentVariables();
+builder.WebHost.CaptureStartupErrors(false);
+builder.Host.UseSerilog(CreateSerilogLogger(builder.Configuration));
+builder.Services
+ .AddCustomHealthCheck(builder.Configuration);
+builder.Services.Configure(builder.Configuration);
+builder.Services.AddApplicationInsightsTelemetry(builder.Configuration);
+if (builder.Configuration.GetValue("AzureServiceBusEnabled"))
+{
+ builder.Services.AddSingleton(sp =>
+ {
+ var serviceBusConnectionString = builder.Configuration["EventBusConnection"];
+ var subscriptionClientName = builder.Configuration["SubscriptionClientName"];
-Log.Logger = CreateSerilogLogger(configuration);
+ return new DefaultServiceBusPersisterConnection(serviceBusConnectionString);
+ });
+}
+else
+{
+ builder.Services.AddSingleton(sp =>
+ {
+ var logger = sp.GetRequiredService>();
+ var factory = new ConnectionFactory()
+ {
+ HostName = builder.Configuration["EventBusConnection"],
+ DispatchConsumersAsync = true
+ };
-try
+ if (!string.IsNullOrEmpty(builder.Configuration["EventBusUserName"]))
+ {
+ factory.UserName = builder.Configuration["EventBusUserName"];
+ }
+
+ if (!string.IsNullOrEmpty(builder.Configuration["EventBusPassword"]))
+ {
+ factory.Password = builder.Configuration["EventBusPassword"];
+ }
+
+ var retryCount = 5;
+ if (!string.IsNullOrEmpty(builder.Configuration["EventBusRetryCount"]))
+ {
+ retryCount = int.Parse(builder.Configuration["EventBusRetryCount"]);
+ }
+
+ return new DefaultRabbitMQPersistentConnection(factory, logger, retryCount);
+ });
+}
+RegisterEventBus(builder.Services);
+var app = builder.Build();
+if (app.Environment.IsDevelopment())
+{
+ app.UseDeveloperExceptionPage();
+}
+else
+{
+ app.UseExceptionHandler("/Home/Error");
+}
+var pathBase = app.Configuration["PATH_BASE"];
+if (!string.IsNullOrEmpty(pathBase))
{
- Log.Information("Configuring web host ({ApplicationContext})...", Program.AppName);
- var host = BuildWebHost(configuration, args);
+ app.UsePathBase(pathBase);
+}
+ConfigureEventBus(app);
+app.UseRouting();
+app.MapHealthChecks("/hc", new HealthCheckOptions()
+{
+ Predicate = _ => true,
+ ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
+});
+app.MapHealthChecks("/liveness", new HealthCheckOptions
+{
+ Predicate = r => r.Name.Contains("self")
+});
+try
+{
Log.Information("Starting web host ({ApplicationContext})...", Program.AppName);
- host.Run();
+ await app.RunAsync();
return 0;
}
@@ -22,15 +106,49 @@ finally
Log.CloseAndFlush();
}
+void RegisterEventBus(IServiceCollection services)
+{
+ if (builder.Configuration.GetValue("AzureServiceBusEnabled"))
+ {
+ services.AddSingleton(sp =>
+ {
+ var serviceBusPersisterConnection = sp.GetRequiredService();
+ var logger = sp.GetRequiredService>();
+ var eventBusSubcriptionsManager = sp.GetRequiredService();
+ string subscriptionName = builder.Configuration["SubscriptionClientName"];
+
+ return new EventBusServiceBus(serviceBusPersisterConnection, logger,
+ eventBusSubcriptionsManager, sp, subscriptionName);
+ });
+ }
+ else
+ {
+ services.AddSingleton(sp =>
+ {
+ var subscriptionClientName = builder.Configuration["SubscriptionClientName"];
+ var rabbitMQPersistentConnection = sp.GetRequiredService();
+ var logger = sp.GetRequiredService>();
+ var eventBusSubcriptionsManager = sp.GetRequiredService();
-IWebHost BuildWebHost(IConfiguration configuration, string[] args) =>
- WebHost.CreateDefaultBuilder(args)
- .CaptureStartupErrors(false)
- .ConfigureAppConfiguration(x => x.AddConfiguration(configuration))
- .UseStartup()
- .UseContentRoot(Directory.GetCurrentDirectory())
- .UseSerilog()
- .Build();
+ var retryCount = 5;
+ if (!string.IsNullOrEmpty(builder.Configuration["EventBusRetryCount"]))
+ {
+ retryCount = int.Parse(builder.Configuration["EventBusRetryCount"]);
+ }
+
+ return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, sp, eventBusSubcriptionsManager, subscriptionClientName, retryCount);
+ });
+ }
+
+ services.AddTransient();
+ services.AddSingleton();
+}
+
+void ConfigureEventBus(IApplicationBuilder app)
+{
+ var eventBus = app.ApplicationServices.GetRequiredService();
+ eventBus.Subscribe();
+}
Serilog.ILogger CreateSerilogLogger(IConfiguration configuration)
{
@@ -70,6 +188,35 @@ IConfiguration GetConfiguration()
public partial class Program
{
- public static string Namespace = typeof(Startup).Namespace;
+ public static string Namespace = typeof(Program).Assembly.GetName().Name;
public static string AppName = Namespace.Substring(Namespace.LastIndexOf('.', Namespace.LastIndexOf('.') - 1) + 1);
-}
\ No newline at end of file
+}
+public static class CustomExtensionMethods
+{
+ public static IServiceCollection AddCustomHealthCheck(this IServiceCollection services, IConfiguration configuration)
+ {
+ var hcBuilder = services.AddHealthChecks();
+
+ hcBuilder.AddCheck("self", () => HealthCheckResult.Healthy());
+
+ if (configuration.GetValue("AzureServiceBusEnabled"))
+ {
+ hcBuilder
+ .AddAzureServiceBusTopic(
+ configuration["EventBusConnection"],
+ topicName: "eshop_event_bus",
+ name: "payment-servicebus-check",
+ tags: new string[] { "servicebus" });
+ }
+ else
+ {
+ hcBuilder
+ .AddRabbitMQ(
+ $"amqp://{configuration["EventBusConnection"]}",
+ name: "payment-rabbitmqbus-check",
+ tags: new string[] { "rabbitmqbus" });
+ }
+
+ return services;
+ }
+}