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 } }