Skip to content
Merged
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
156 changes: 85 additions & 71 deletions TransactionProcessor.BusinessLogic/Services/SettlementDomainService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,82 +41,96 @@ public async Task<Result<Guid>> ProcessSettlement(SettlementCommands.ProcessSett
IAsyncPolicy<Result<Guid>> retryPolicy = PolicyFactory.CreatePolicy<Result<Guid>>(policyTag: "SettlementDomainService - ProcessSettlement");

try {
return await PolicyFactory.ExecuteWithPolicyAsync(async () => {

Guid settlementAggregateId = Helpers.CalculateSettlementAggregateId(command.SettlementDate, command.MerchantId, command.EstateId);

Result<SettlementAggregate> getSettlementResult = await DomainServiceHelper.GetAggregateOrFailure(ct => this.AggregateService.GetLatest<SettlementAggregate>(settlementAggregateId, ct), settlementAggregateId, cancellationToken, false);
if (getSettlementResult.IsFailed)
return ResultHelpers.CreateFailure(getSettlementResult);

SettlementAggregate settlementAggregate = getSettlementResult.Data;

if (settlementAggregate.IsCreated == false) {
Logger.LogInformation($"No pending settlement for {command.SettlementDate:yyyy-MM-dd}");
// Not pending settlement for this date
return Result.Success();
}

Result<MerchantAggregate> getMerchantResult = await DomainServiceHelper.GetAggregateOrFailure(ct => this.AggregateService.Get<MerchantAggregate>(command.MerchantId, ct), command.MerchantId, cancellationToken, false);
if (getMerchantResult.IsFailed)
return ResultHelpers.CreateFailure(getMerchantResult);

Result settlementSaveResult = Result.Success();
MerchantAggregate merchant = getMerchantResult.Data;
if (merchant.SettlementSchedule == SettlementSchedule.Immediate) {
// Mark the settlement as completed
settlementAggregate.StartProcessing(DateTime.Now);
settlementAggregate.ManuallyComplete();
settlementSaveResult = await this.AggregateService.Save(settlementAggregate, cancellationToken);
}

List<(Guid transactionId, Guid merchantId, CalculatedFee calculatedFee)> feesToBeSettled = settlementAggregate.GetFeesToBeSettled();

if (feesToBeSettled.Any()) {
// Record the process call
Result stateResult = settlementAggregate.StartProcessing(DateTime.Now);
if (stateResult.IsFailed)
return ResultHelpers.CreateFailure(stateResult);
settlementSaveResult = await this.AggregateService.Save(settlementAggregate, cancellationToken);
}

// Settlement updates done, now we need to update the transactions
if (settlementSaveResult.IsFailed)
return ResultHelpers.CreateFailure(settlementSaveResult);

List<Result> failedResults = new();
foreach ((Guid transactionId, Guid merchantId, CalculatedFee calculatedFee) feeToSettle in feesToBeSettled) {
try {

Result<TransactionAggregate> getTransactionResult = await this.AggregateService.GetLatest<TransactionAggregate>(feeToSettle.transactionId, cancellationToken);
if (getTransactionResult.IsFailed)
return ResultHelpers.CreateFailure(getTransactionResult);
var transactionAggregate = getTransactionResult.Data;

transactionAggregate.AddSettledFee(feeToSettle.calculatedFee, command.SettlementDate, settlementAggregateId);

Result saveResult = await this.AggregateService.Save(transactionAggregate, cancellationToken);
if (saveResult.IsFailed)
return ResultHelpers.CreateFailure(saveResult);
}
catch (Exception ex) {
Logger.LogError($"Failed to process transaction {feeToSettle.transactionId} for settlement {settlementAggregateId}", ex);
failedResults.Add(Result.Failure($"Failed to process transaction {feeToSettle.transactionId} for settlement {settlementAggregateId}: {ex.Message}"));
}
}

if (failedResults.Any()) {
return Result.Failure($"Not all fees were processed successfully {failedResults.Count} have failed");
}

return Result.Success(settlementAggregateId);
}, retryPolicy, "SettlementDomainService - ProcessSettlement");
return await PolicyFactory.ExecuteWithPolicyAsync(() => this.ProcessSettlementInternal(command, cancellationToken), retryPolicy, "SettlementDomainService - ProcessSettlement");
}
catch (Exception ex) {
return Result.Failure(ex.GetExceptionMessages());
}
}

private async Task<Result<Guid>> ProcessSettlementInternal(SettlementCommands.ProcessSettlementCommand command,
CancellationToken cancellationToken) {
Guid settlementAggregateId = Helpers.CalculateSettlementAggregateId(command.SettlementDate, command.MerchantId, command.EstateId);

Result<SettlementAggregate> getSettlementResult = await DomainServiceHelper.GetAggregateOrFailure(ct => this.AggregateService.GetLatest<SettlementAggregate>(settlementAggregateId, ct), settlementAggregateId, cancellationToken, false);
if (getSettlementResult.IsFailed)
return ResultHelpers.CreateFailure(getSettlementResult);

SettlementAggregate settlementAggregate = getSettlementResult.Data;

if (settlementAggregate.IsCreated == false) {
Logger.LogInformation($"No pending settlement for {command.SettlementDate:yyyy-MM-dd}");
return Result.Success();
}

Result<MerchantAggregate> getMerchantResult = await DomainServiceHelper.GetAggregateOrFailure(ct => this.AggregateService.Get<MerchantAggregate>(command.MerchantId, ct), command.MerchantId, cancellationToken, false);
if (getMerchantResult.IsFailed)
return ResultHelpers.CreateFailure(getMerchantResult);

Result<List<(Guid transactionId, Guid merchantId, CalculatedFee calculatedFee)>> settlementPreparationResult = await this.PrepareSettlementForProcessing(settlementAggregate, getMerchantResult.Data, cancellationToken);
if (settlementPreparationResult.IsFailed)
return ResultHelpers.CreateFailure(settlementPreparationResult);

return await this.ProcessSettlementFees(settlementPreparationResult.Data, command.SettlementDate, settlementAggregateId, cancellationToken);
}

private async Task<Result<List<(Guid transactionId, Guid merchantId, CalculatedFee calculatedFee)>>> PrepareSettlementForProcessing(SettlementAggregate settlementAggregate,
MerchantAggregate merchantAggregate,
CancellationToken cancellationToken) {
Result settlementSaveResult = Result.Success();
if (merchantAggregate.SettlementSchedule == SettlementSchedule.Immediate) {
settlementAggregate.StartProcessing(DateTime.Now);
settlementAggregate.ManuallyComplete();
settlementSaveResult = await this.AggregateService.Save(settlementAggregate, cancellationToken);
}

List<(Guid transactionId, Guid merchantId, CalculatedFee calculatedFee)> feesToBeSettled = settlementAggregate.GetFeesToBeSettled();

if (feesToBeSettled.Any()) {
Result stateResult = settlementAggregate.StartProcessing(DateTime.Now);
if (stateResult.IsFailed)
return ResultHelpers.CreateFailure(stateResult);

settlementSaveResult = await this.AggregateService.Save(settlementAggregate, cancellationToken);
}

if (settlementSaveResult.IsFailed)
return ResultHelpers.CreateFailure(settlementSaveResult);

return Result.Success(feesToBeSettled);
}

private async Task<Result<Guid>> ProcessSettlementFees(List<(Guid transactionId, Guid merchantId, CalculatedFee calculatedFee)> feesToBeSettled,
DateTime settlementDate,
Guid settlementAggregateId,
CancellationToken cancellationToken) {
List<Result> failedResults = new();
foreach ((Guid transactionId, Guid merchantId, CalculatedFee calculatedFee) feeToSettle in feesToBeSettled) {
try {
Result<TransactionAggregate> getTransactionResult = await this.AggregateService.GetLatest<TransactionAggregate>(feeToSettle.transactionId, cancellationToken);
if (getTransactionResult.IsFailed)
return ResultHelpers.CreateFailure(getTransactionResult);

TransactionAggregate transactionAggregate = getTransactionResult.Data;
transactionAggregate.AddSettledFee(feeToSettle.calculatedFee, settlementDate, settlementAggregateId);

Result saveResult = await this.AggregateService.Save(transactionAggregate, cancellationToken);
if (saveResult.IsFailed)
return ResultHelpers.CreateFailure(saveResult);
}
catch (Exception ex) {
Logger.LogError($"Failed to process transaction {feeToSettle.transactionId} for settlement {settlementAggregateId}", ex);
failedResults.Add(Result.Failure($"Failed to process transaction {feeToSettle.transactionId} for settlement {settlementAggregateId}: {ex.Message}"));
}
}

if (failedResults.Any()) {
return Result.Failure($"Not all fees were processed successfully {failedResults.Count} have failed");
}

return Result.Success(settlementAggregateId);
}

public async Task<Result> AddMerchantFeePendingSettlement(SettlementCommands.AddMerchantFeePendingSettlementCommand command,
CancellationToken cancellationToken) {

Expand Down Expand Up @@ -214,4 +228,4 @@ public SettlementDomainService(Func<IAggregateService> aggregateService)
this.AggregateService = aggregateService();
}
}
}
}
Loading