Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 68 additions & 4 deletions TransactionProcessing.MerchantPos/Runtime/ApiClient.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
using System.Net.Http.Headers;
using System.Text;
using MerchantPos.EF.Models;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using SecurityService.Client;
using SecurityService.DataTransferObjects.Responses;
using Shared.Logger;
using SimpleResults;
using System.Diagnostics.CodeAnalysis;
using System.Net.Http.Headers;
using System.Text;
using TransactionProcessor.Client;
using TransactionProcessor.DataTransferObjects;
using TransactionProcessor.DataTransferObjects.Requests.Merchant;
Expand All @@ -18,6 +20,9 @@ namespace TransactionProcessing.MerchantPos.Runtime;
public interface IApiClient
{
Task<Result<TokenResponse>> GetToken(String clientId, String clientSecret, MerchantConfig cfg, CancellationToken cancellationToken);

Task<Result<MerchantResponse>> GetMerchant(MerchantConfig cfg, TokenResponse token, CancellationToken cancellationToken);

Task SendLogon(MerchantConfig cfg, TokenResponse token,
Int32 transactionNumber, CancellationToken cancellationToken);
Task<List<Product>> GetProductList(MerchantConfig cfg, TokenResponse token, CancellationToken cancellationToken);
Expand Down Expand Up @@ -49,6 +54,40 @@ public async Task<Result<TokenResponse>> GetToken(String clientId,
return await this.SecurityClient.GetToken(cfg.Username, cfg.Password, clientId, clientSecret, cancellationToken);
}

public async Task<Result<MerchantResponse>> GetMerchant(MerchantConfig cfg,
TokenResponse token,
CancellationToken cancellationToken) {

Guid estateId = cfg.EstateId;
Guid merchantId = cfg.MerchantId;

String requestUri = this.BuildRequestUrl($"api/merchants?applicationVersion={cfg.ApplicationVersion}");

Logger.LogInformation("About to request merchant details");
Logger.LogDebug($"Merchant Request details: Estate Id {estateId} Merchant Id {merchantId} Access Token {token.AccessToken}");

HttpRequestMessage request = new(HttpMethod.Get, requestUri);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token.AccessToken);
var httpResponse = await this.HttpClient.SendAsync(request, cancellationToken);

// Process the response
Result<String> content = await this.HandleResponseX(httpResponse, cancellationToken);

if (content.IsFailed)
{
Logger.LogInformation($"GetMerchant failed {content.Status}");
}

Logger.LogDebug($"Merchant Response details: Status {httpResponse.StatusCode} Payload {content.Data}");
//MerchantResponse

MerchantResponse responseData = JsonConvert.DeserializeObject<MerchantResponse>(content.Data);

Logger.LogDebug($"Merchant Response: [{JsonConvert.SerializeObject(responseData)}]");

return responseData;
}

public async Task SendLogon(MerchantConfig cfg,
TokenResponse token,
Int32 transactionNumber,
Expand Down Expand Up @@ -92,7 +131,7 @@ public async Task<List<Product>> GetProductList(MerchantConfig cfg,
Logger.LogInformation($"GetMerchantContracts failed {content.Status}");
}

Logger.LogDebug($"Transaction Response details: Status {httpResponse.StatusCode} Payload {content.Data}");
Logger.LogDebug($"Merchant Contract Response details: Status {httpResponse.StatusCode} Payload {content.Data}");

List<ContractResponseX>? responseData = JsonConvert.DeserializeObject<List<ContractResponseX>>(content.Data);

Expand Down Expand Up @@ -499,4 +538,29 @@ public enum ProductSubType
BillPaymentPostPay,
BillPaymentPrePay,
Voucher
}
}

[ExcludeFromCodeCoverage]
public class MerchantResponse
{
[JsonProperty("estate_id")]
public Guid EstateId { get; set; }

[JsonProperty("merchant_id")]
public Guid MerchantId { get; set; }

[JsonProperty("merchant_name")]
public string MerchantName { get; set; }

[JsonProperty("opening_hours")]
public Dictionary<DayOfWeek, OpeningHoursResponse> OpeningHours { get; set; }
}

public class OpeningHoursResponse
{
[JsonProperty("opening")]
public string Opening { get; set; }

[JsonProperty("closing")]
public string Closing { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@ public class MerchantConfig
public TimeOnly OpeningTime { get; set; } = new(8, 0);
public List<Product> Products { get; set; }
public Boolean RequiresEndOfDay { get; set; } = true;
public MerchantResponse Merchant { get; set; }
}
22 changes: 20 additions & 2 deletions TransactionProcessing.MerchantPos/Runtime/MerchantRuntime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,10 @@ private async Task StartupSequence(MerchantConfig cfg,
// 3. Balance
decimal balance = await ApiClient.GetBalance(cfg, this.CurrentServiceToken, cancellationToken);
await Repository.UpdateBalance(cfg.MerchantId,cfg.MerchantName, balance);

// 4. get the merchant record
var merchant = await ApiClient.GetMerchant(cfg, this.CurrentUserToken, cancellationToken);
cfg.Merchant = merchant.Data;
}

private async Task RunMainLoop(MerchantConfig cfg,
Expand All @@ -150,8 +154,22 @@ private async Task RunMainLoop(MerchantConfig cfg,
Merchant merchant = await this.Repository.GetMerchant(cfg.MerchantId);
// Wait until the merchant's configured opening time
DateTime currentTime = DateTime.Now;
TimeSpan openingTime = cfg.OpeningTime.ToTimeSpan();
TimeSpan closingTime = cfg.ClosingTime.ToTimeSpan();

// Get the current days opening and closing time
TimeSpan openingTime;
TimeSpan closingTime;
Boolean found = cfg.Merchant.OpeningHours.TryGetValue(DateTime.Now.DayOfWeek, out var openingHours);
if (found == false) {
// fallback to the configured default opening and closing time if no specific hours for the day of week
openingTime = cfg.OpeningTime.ToTimeSpan();
closingTime = cfg.ClosingTime.ToTimeSpan();
}
else {
// We have opening and closing times for the current day of week, so use those
openingTime = TimeSpan.ParseExact(openingHours.Opening, "hhmm", null);
closingTime = TimeSpan.ParseExact(openingHours.Closing, "hhmm", null);
}

if (currentTime.TimeOfDay < openingTime) {
TimeSpan delay = openingTime - currentTime.TimeOfDay;
Logger.LogInformation($"Merchant {cfg.MerchantName} sleeping until opening time {cfg.OpeningTime}");
Expand Down
Loading