using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using gehGassi.Dto;
using gehGassi.Dto.Common;
using gehGassi.Dto.Payment;
using gehGassi.Dto.Walks;
using gehGassiApp.Core.Data;
using gehGassiApp.Core.Interfaces;
using gehGassiApp.Core.Interfaces.Synchronization;
using gehGassiApp.Core.Mapper;
using gehGassiApp.Core.Resources;
using gehGassiApp.Domain.Common;
using gehGassiApp.Domain.Payment;
using gehGassiApp.Domain.Users;
using gehGassiApp.Domain.Walks;
namespace gehGassiApp.Core.Services
{
///
/// Service der die Verwaltung von Zahlungen übernimmt
///
public class PaymentService : IPaymentService
{
private readonly IUnitOfWork _unitOfWork;
private readonly ICommunicationService _communicationService;
private readonly IUserService _userService;
private readonly IWalkService _walkService;
private readonly ISyncInfoPullService _syncInfoPullService;
private readonly IRepository _appUserRepository;
private readonly IRepository _payoutRepository;
///
/// Erstellt eine Instanz
///
/// Instanz eines IUnitOfWork
/// Instanz eines ICommunicationService
/// Instanz eines IUserService
/// Instanz eines IWalkService
/// Instanz eines ISyncInfoPullService
public PaymentService(IUnitOfWork unitOfWork, ICommunicationService communicationService, IUserService userService, IWalkService walkService, ISyncInfoPullService syncInfoPullService)
{
_unitOfWork = unitOfWork;
_communicationService = communicationService;
_userService = userService;
_walkService = walkService;
_syncInfoPullService = syncInfoPullService;
_appUserRepository = _unitOfWork.GetRepository();
_payoutRepository = _unitOfWork.GetRepository();
}
///
/// Gibt eine Liste von Wallets eines App-Users zurück
///
/// Id des App-Users
/// Aktuelles Accesstoken
/// CancellationToken
/// CommunicationResult
public async Task>> GetWalletsAsync(string appUserId, string accessToken, CancellationToken token)
{
var result = new CommunicationResult>() { Value = new List() };
var isConnected = await _communicationService.IsConnected();
if (isConnected)
{
var queryResult = await _communicationService.GetWalletsAsync(appUserId, accessToken, token);
if (queryResult.Success)
{
result.Success = true;
result.Value = queryResult.Value;
}
}
else
{
result.Success = false;
result.ErrorCode = CommunicationErrors.ServerNoConnection;
result.ErrorMessage = Errors.Server_NoConnection;
}
return result;
}
///
/// Gibt den Saldo eines Wallets zurück
///
/// Id des App-Users
/// Typ des Wallets
/// Aktuelles Accesstoken
/// CancellationToken
/// CommunicationResult
public async Task> GetWalletBalanceAsync(string appUserId, WalletType walletType, string accessToken, CancellationToken token)
{
var result = new CommunicationResult() { Value = 0 };
var isConnected = await _communicationService.IsConnected();
if (isConnected)
{
var queryResult = await _communicationService.GetWalletBalanceAsync(appUserId, walletType, accessToken, token);
if (queryResult.Success)
{
result.Success = true;
result.Value = queryResult.Value;
}
}
else
{
result.Success = false;
result.ErrorCode = CommunicationErrors.ServerNoConnection;
result.ErrorMessage = Errors.Server_NoConnection;
}
return result;
}
///
/// Gibt die Bankverbindung eines App-Users zurück
///
/// Id des App-Users
/// Aktuelles Accesstoken
/// CancellationToken
/// CommunicationResult
public async Task> GetBankAccountAsync(string appUserId, string accessToken, CancellationToken token)
{
var result = new CommunicationResult() { Value = null };
var isConnected = await _communicationService.IsConnected();
if (isConnected)
{
var queryResult = await _communicationService.GetBankAccountsAsync(appUserId, accessToken, token);
return queryResult;
}
else
{
result.Success = false;
result.ErrorCode = CommunicationErrors.ServerNoConnection;
result.ErrorMessage = Errors.Server_NoConnection;
}
return result;
}
///
/// Anlegen oder Bearbeiten eines Bankkontos
///
/// Bankkonto
/// Aktuelles Accesstoken
/// CancellationToken
/// CommunicationResult mit einem AppUser der die entsprechende BankId gesetzt hat
public async Task> CreateOrUpdateBankAccountAsync(BankAccount bankAccount, string accessToken, CancellationToken token)
{
var result = new CommunicationResult() { Value = null };
var isConnected = await _communicationService.IsConnected();
if (isConnected)
{
var dto = bankAccount.ToDto();
var queryResult = await _communicationService.CreateOrUpdateBankAccountAsync(dto, accessToken, token);
if (queryResult.Success)
{
//jetzt auch lokal die Änderung speichern
var appUser = await _userService.SetBankAccountAsync(queryResult.Value.Id, queryResult.Value.BankId, queryResult.Value.UpdatedAt);
queryResult.Value = appUser;
}
return queryResult;
}
else
{
result.Success = false;
result.ErrorCode = CommunicationErrors.ServerNoConnection;
result.ErrorMessage = Errors.Server_NoConnection;
}
return result;
}
///
/// Authorisierung der Bezahlung eines Walks mit dem Guthaben eines Wallets
/// Das Geld wird vom Guthabenkonto auf das Transaktions-Konto gelegt
///
/// Id des App-Users
/// Id des Walks
/// Zu zahlender Bertrag
/// Aktuelles Accesstoken
/// CancellationToken
/// CommunicationResult
public async Task> AuthorizeWalkWithCreditAsync(string appUserId, string walkId, decimal ammount, string accessToken, CancellationToken token)
{
var result = new CommunicationResult() { Value = null };
var isConnected = await _communicationService.IsConnected();
if (isConnected)
{
var queryResult = await _communicationService.AuthorizeWalkWithCreditAsync(appUserId, walkId, ammount, accessToken, token);
if (queryResult.Success)
{
//jetzt auch lokal die Änderung speichern
var updatedWalk = await _walkService.SetPaymentStatusLocalAsync(queryResult.Value.Id, queryResult.Value.PaymentStatus, queryResult.Value.UpdatedAt);
queryResult.Value = updatedWalk;
}
return queryResult;
}
else
{
result.Success = false;
result.ErrorCode = CommunicationErrors.ServerNoConnection;
result.ErrorMessage = Errors.Server_NoConnection;
}
return result;
}
///
/// Authorisierung der Bezahlung eines Walks komplett mit einem Gutschein
/// Das Geld wird vom Guthabenkonto auf das Transaktions-Konto gelegt
///
/// Id des Gutscheins
/// Code des Gutscheins
/// Id des App-Users
/// Id des Walks
/// Zu zahlender Bertrag
/// Aktuelles Accesstoken
/// CancellationToken
/// CommunicationResult
public async Task> AuthorizeWalkWithVoucherAsync(string voucherId, string voucherCode, string appUserId, string walkId, decimal ammount, string accessToken, CancellationToken token)
{
var result = new CommunicationResult() { Value = null };
var isConnected = await _communicationService.IsConnected();
if (isConnected)
{
var queryResult = await _communicationService.AuthorizeWalkWithVoucherAsync(voucherId, voucherCode, appUserId, walkId, ammount, accessToken, token);
if (queryResult.Success)
{
//jetzt auch lokal die Änderung speichern
var updatedWalk = await _walkService.SetPaymentStatusLocalAsync(queryResult.Value.Id, queryResult.Value.PaymentStatus, queryResult.Value.UpdatedAt);
queryResult.Value = updatedWalk;
}
return queryResult;
}
else
{
result.Success = false;
result.ErrorCode = CommunicationErrors.ServerNoConnection;
result.ErrorMessage = Errors.Server_NoConnection;
}
return result;
}
///
/// Einzahlen einer Summe auf das Transaktions-Konto eines App-Users mit Bezug auf einen Walk.
/// Es können gebühren sofort auf das Transaktions-Konto des Mandanten abgeführt werden
///
/// Id des App-Users
/// Id des Walks
/// Zu zahlender Bertrag
/// Anfallende Gebühren
/// Betrag der zusätzlich vom Guthabenkonto bezahlt werden muss
/// Typ der Einzahlung
/// Aktuelles Accesstoken
/// CancellationToken
/// CommunicationResult mit dem ReturnUrl, wenn erfolgreich
public async Task> PayInAsync(string appUserId, string walkId, decimal ammount, decimal fees, decimal fromCredit, PayInType payInType, string accessToken, CancellationToken token)
{
var result = new CommunicationResult() { Value = null };
var isConnected = await _communicationService.IsConnected();
if (isConnected)
{
var queryResult = await _communicationService.PayInAsync(appUserId, walkId, ammount, fees, fromCredit, payInType, accessToken, token);
return queryResult;
}
else
{
result.Success = false;
result.ErrorCode = CommunicationErrors.ServerNoConnection;
result.ErrorMessage = Errors.Server_NoConnection;
}
return result;
}
///
/// Holen der letzten KYC-Dokumente eines App-Users
///
/// Id des App-Users
/// Aktuelles Accesstoken
/// CancellationToken
/// CommunicationResult mit dem aktuell gültigen KYC-Dokument oder null, wenn keines vorhanden
public async Task> GetLatestKycDocumentAsync(string appUserId, string accessToken, CancellationToken token)
{
var result = new CommunicationResult() { Value = null };
var isConnected = await _communicationService.IsConnected();
if (isConnected)
{
var queryResult = await _communicationService.GetLatestKycDocumentAsync(appUserId, accessToken, token);
return queryResult;
}
else
{
result.Success = false;
result.ErrorCode = CommunicationErrors.ServerNoConnection;
result.ErrorMessage = Errors.Server_NoConnection;
}
return result;
}
///
/// KYC-Dokument für einen App-User erstellen mit einer Seite
///
/// Id des App-Users
/// Dokument-Quelle
/// Dateiname
/// Datei als byte-Array
/// Aktuelles Accesstoken
/// CancellationToken
/// CommunicationResult mit dem aktuell erstellten KYC-Dokument
public async Task> CreateKycDocumentAsync(string appUserId, IdentityDocumentSource source, string fileOneName, byte[] fileOne, string accessToken, CancellationToken token)
{
var result = new CommunicationResult() { Value = null };
var isConnected = await _communicationService.IsConnected();
if (isConnected)
{
var queryResult = await _communicationService.CreateKycDocumentAsync(appUserId, source, fileOneName, fileOne, accessToken, token);
return queryResult;
}
else
{
result.Success = false;
result.ErrorCode = CommunicationErrors.ServerNoConnection;
result.ErrorMessage = Errors.Server_NoConnection;
}
return result;
}
///
/// KYC-Dokument für einen App-User erstellen mit zwei Seiten
///
/// Id des App-Users
/// Dokument-Quelle
/// Dateiname Datei 1
/// Datei 1 als byte-Array
/// Dateiname Datei 2
/// Datei 2 als byte-Array
/// Aktuelles Accesstoken
/// CancellationToken
/// CommunicationResult mit dem aktuell erstellten KYC-Dokument
public async Task> CreateKycDocumentAsync(string appUserId, IdentityDocumentSource source, string fileOneName, byte[] fileOne, string fileTwoName, byte[] fileTwo, string accessToken, CancellationToken token)
{
var result = new CommunicationResult() { Value = null };
var isConnected = await _communicationService.IsConnected();
if (isConnected)
{
var queryResult = await _communicationService.CreateKycDocumentAsync(appUserId, source, fileOneName, fileOne, fileTwoName, fileTwo, accessToken, token);
return queryResult;
}
else
{
result.Success = false;
result.ErrorCode = CommunicationErrors.ServerNoConnection;
result.ErrorMessage = Errors.Server_NoConnection;
}
return result;
}
///
/// Gibt Walks vom Server zurück
///
/// Id App-Users
/// Liste von Sortierangaben
/// Wie viele Walks sollen abgerufen werden? -1 Wenn nicht anwenden.
/// Wie viele Walks sollen ausgelassen werden? -1 Wenn nicht anwenden.
/// Aktuelles Accesstoken
/// CancellationToken
/// Nächster Walk oder null, wenn keiner vorhanden
public async Task>> GetPayoutsAsync(string appUserId, List sortOrders, int take, int skip, string accessToken, CancellationToken token)
{
var result = new ListCommunicationResult>() { Value = new List() };
var isConnected = await _communicationService.IsConnected();
if (isConnected)
{
var query = new PayoutQueryDto()
{
Take = take,
Skip = skip,
LastUpdate = null,
AppUserId = appUserId,
};
query.DynamicSortOrder = sortOrders.ToDto();
var queryResult = await _communicationService.GetPayoutsAsync(query, accessToken, token);
if (queryResult.Success && queryResult.Value.Count >= 1)
{
return queryResult;
}
}
else
{
result.Success = false;
result.ErrorCode = CommunicationErrors.ServerNoConnection;
result.ErrorMessage = Errors.Server_NoConnection;
}
return result;
}
///
/// Erstellen einer Auszahlung
///
/// Id des App-Users
/// Betrag der ausgezahlt werden soll
/// Aktuelles Accesstoken
/// CancellationToken
/// CommunicationResult
public async Task> CreatePayoutAsync(string appUserId, decimal ammount, string accessToken, CancellationToken token)
{
var result = new CommunicationResult() { Value = null };
var isConnected = await _communicationService.IsConnected();
if (isConnected)
{
var queryResult = await _communicationService.CreatePayoutAsync(appUserId, ammount, accessToken, token);
//Auszahlung lokal anlegen wenn erfolgreich
if (queryResult.Success)
{
_payoutRepository.Add(queryResult.Value);
await _unitOfWork.CommitAsync();
}
return queryResult;
}
else
{
result.Success = false;
result.ErrorCode = CommunicationErrors.ServerNoConnection;
result.ErrorMessage = Errors.Server_NoConnection;
}
return result;
}
///
/// Gibt eine Auszahlung eines App-Users zurück
///
/// Id des App-Users
/// Id der Auszahlung
/// Aktuelles Accesstoken
/// CancellationToken
/// CommunicationResult
public async Task> GetPayoutAsync(string appUserId, string payoutId, string accessToken, CancellationToken token)
{
var result = new CommunicationResult() { Value = null };
var isConnected = await _communicationService.IsConnected();
if (isConnected)
{
var queryResult = await _communicationService.GetPayoutAsync(appUserId, payoutId, accessToken, token);
return queryResult;
}
else
{
result.Success = false;
result.ErrorCode = CommunicationErrors.ServerNoConnection;
result.ErrorMessage = Errors.Server_NoConnection;
}
return result;
}
#region Sync Payout
///
/// Holen der Daten vom lokalen Speicher die noch nicht synchronisiert wurden und senden an den Server
///
/// Aktuelles Accesstoken
/// CancellationToken
/// Task
public async Task> PushAsync(string accessToken, CancellationToken token)
{
var result = new SyncResult();
//Es gibt hier kein Push!
await Task.Delay(1);
return result;
}
///
/// Holen der letzten Daten vom Server und Synchronisieren mit den lokalen Daten
///
/// Sprache
/// Aktuelles Accesstoken
/// CancellationToken
/// Task
public async Task> PullAsync(string language, string accessToken, CancellationToken token)
{
var result = new SyncResult();
var isConnected = await _communicationService.IsConnected();
if (isConnected)
{
var user = await _appUserRepository.FirstOrDefaultAsync(c => c.Id != "", false).ConfigureAwait(false);
DateTimeOffset? lastUpdate = null;
var lastSyncInfo = await _syncInfoPullService.GetAsync(nameof(Payout));
if (lastSyncInfo != null)
lastUpdate = lastSyncInfo.LastUpdate;
var ctsQuery = new CancellationTokenSource(Common.Constants.PushPullTimeout);
var payoutResult = await _communicationService.GetPayoutsForSyncAsync(user.Id, lastUpdate, accessToken, token);
if (payoutResult.Success)
{
result = await HandleChangesAsync(payoutResult.Value);
if (result.LastUpdate != null)
await _syncInfoPullService.AddOrUpddateAsync(nameof(Payout), result.LastUpdate.Value).ConfigureAwait(false);
}
result.LastUpdate ??= lastUpdate;
}
return result;
}
#endregion
#region Private
///
/// Behandeln der Liste von Auszahlungen wenn welche vom Online-Store geholt werden.
///
/// Liste der Auszahlungen
/// Task
private async Task> HandleChangesAsync(List payouts)
{
var result = new SyncResult();
//Je Rasse durchgehen ob was gemacht werden soll
foreach (var payout in payouts)
{
var localPayout = await _payoutRepository.FirstOrDefaultAsync(c => c.Id == payout.Id, false);
if (localPayout != null && localPayout.UpdatedAt < payout.UpdatedAt)
{
if (localPayout.UpdatedAt >= payout.UpdatedAt)
{
if (result.LastUpdate == null || result.LastUpdate < localPayout.UpdatedAt)
result.LastUpdate = localPayout.UpdatedAt;
continue;
}
var deleted = localPayout.Deleted == false && payout.Deleted;
localPayout.Version = payout.Version;
localPayout.UpdatedAt = payout.UpdatedAt;
localPayout.Deleted = payout.Deleted;
localPayout.MangoPayUserId = payout.MangoPayUserId;
localPayout.MangoPayId = payout.MangoPayId;
localPayout.WalletId = payout.WalletId;
localPayout.MangoPayWalletId = payout.MangoPayWalletId;
localPayout.MangoPayBankId = payout.MangoPayBankId;
localPayout.Iban = payout.Iban;
localPayout.Bic = payout.Bic;
localPayout.Status = payout.Status;
localPayout.Ammount = payout.Ammount;
localPayout.Currency = payout.Currency;
localPayout.ResultCode = payout.ResultCode;
localPayout.ResultMessage = payout.ResultMessage;
localPayout.ExecutionDate = payout.ExecutionDate;
localPayout.Refunded = payout.Refunded;
localPayout.RefundReasonType = payout.RefundReasonType;
localPayout.RefundReasonMessage = payout.RefundReasonMessage;
localPayout.Created = payout.Created;
if (result.LastUpdate == null || result.LastUpdate < localPayout.UpdatedAt)
result.LastUpdate = localPayout.UpdatedAt;
if (!deleted)
result.Updated.Add(localPayout);
else
result.Deleted.Add(localPayout);
_payoutRepository.Update(localPayout);
}
if (localPayout == null)
{
localPayout = new Payout()
{
Id = payout.Id,
Version = payout.Version,
UpdatedAt = payout.UpdatedAt,
Deleted = payout.Deleted,
AppUserId = payout.AppUserId,
MangoPayUserId = payout.MangoPayUserId,
MangoPayId = payout.MangoPayId,
WalletId = payout.WalletId,
MangoPayWalletId = payout.MangoPayWalletId,
MangoPayBankId = payout.MangoPayBankId,
Iban = payout.Iban,
Bic = payout.Bic,
Status = payout.Status,
Ammount = payout.Ammount,
Currency = payout.Currency,
ResultCode = payout.ResultCode,
ResultMessage = payout.ResultMessage,
ExecutionDate = payout.ExecutionDate,
Refunded = payout.Refunded,
RefundReasonType = payout.RefundReasonType,
RefundReasonMessage = payout.RefundReasonMessage,
Created = payout.Created
};
_payoutRepository.Add(localPayout);
if (result.LastUpdate == null || result.LastUpdate < localPayout.UpdatedAt)
result.LastUpdate = localPayout.UpdatedAt;
result.Added.Add(localPayout);
}
}
if (result.HasChanges)
{
try
{
await _unitOfWork.CommitAsync();
}
catch (Exception ex)
{
var err = ex.Message;
}
}
return result;
}
#endregion
}
}