923 lines
47 KiB
C#
923 lines
47 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using AutoMapper;
|
|
using gehGassi.Core.Interfaces;
|
|
using gehGassi.Core.Services;
|
|
using gehGassi.Domain.Common;
|
|
using gehGassi.Dto.Walks;
|
|
using gehGassi.Dto;
|
|
using gehGassi.Dto.Common;
|
|
using gehGassi.Dto.Payment;
|
|
using gehGassi.Web.Helper;
|
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
|
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.Extensions.Logging;
|
|
using Microsoft.Extensions.Options;
|
|
using gehGassi.Dto.Dogs;
|
|
using gehGassi.Domain.Payment;
|
|
using gehGassi.Dto.Messages;
|
|
using gehGassi.Web.Hubs;
|
|
using Microsoft.AspNetCore.Http;
|
|
using Asp.Versioning;
|
|
|
|
namespace gehGassi.Web.Controllers.Api
|
|
{
|
|
/// <summary>
|
|
/// Controller für die Verwaltung von Zahlungen und Wallets
|
|
/// </summary>
|
|
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
|
|
[ApiController]
|
|
[ApiVersion(1)]
|
|
[Route("api/payments")]
|
|
[Route("api/v{v:apiVersion}/payments")]
|
|
public class ApiPaymentController : ApiBaseController
|
|
{
|
|
private readonly IWalletService _walletService;
|
|
private readonly IMangoPayService _mangoPayService;
|
|
private readonly ITransactionFeeService _transactionFeeService;
|
|
private readonly IUserService _userService;
|
|
private readonly IWalkService _walkService;
|
|
private readonly ISystemMessageService _systemMessageService;
|
|
private readonly IAppHubSender _appHubSender;
|
|
private readonly IIdentityDocumentService _identityDocumentService;
|
|
private readonly IPayoutService _payoutService;
|
|
private readonly IPushNotificationService _pushNotificationService;
|
|
private readonly IVoucherCampaignService _voucherCampaignService;
|
|
|
|
/// <summary>
|
|
/// Erstellt eine Instanz
|
|
/// </summary>
|
|
/// <param name="mapper">Instanz eines IMapper</param>
|
|
/// <param name="logger">Instanz eines ILogger</param>
|
|
/// <param name="localizationOptions">Instanz von LocalizationOptions</param>
|
|
/// <param name="appUserService">Instanz eines IAppUserService</param>
|
|
/// <param name="walletService">Instanz eines IWalletService</param>
|
|
/// <param name="mangoPayService">Instanz eines IMangoPayService</param>
|
|
/// <param name="transactionFeeService">Instanz eines ITransactionFeeService</param>
|
|
/// <param name="userService">Instanz eines IUserService</param>
|
|
/// <param name="walkService">Instanz eines IWalkService</param>
|
|
/// <param name="systemMessageService">Instanz eines ISystemMessageService</param>
|
|
/// <param name="appHubSender">Instanz eines IAppHubSender</param>
|
|
/// <param name="identityDocumentService">Instanz eines IIdentityDocumentService</param>
|
|
/// <param name="payoutService">Instanz eines IPayoutService</param>
|
|
/// <param name="pushNotificationService">Instanz eines IPushNotificationService</param>
|
|
/// <param name="voucherCampaignService">Isntanz eines IVoucherCampaignService</param>
|
|
public ApiPaymentController(IMapper mapper, ILogger<ApiPaymentController> logger, IOptions<LocalizationOptions> localizationOptions, IAppUserService appUserService,
|
|
IWalletService walletService, IMangoPayService mangoPayService, ITransactionFeeService transactionFeeService, IUserService userService, IWalkService walkService,
|
|
ISystemMessageService systemMessageService, IAppHubSender appHubSender, IIdentityDocumentService identityDocumentService, IPayoutService payoutService,
|
|
IPushNotificationService pushNotificationService, IVoucherCampaignService voucherCampaignService) : base(mapper, localizationOptions, appUserService)
|
|
{
|
|
_walletService = walletService;
|
|
_mangoPayService = mangoPayService;
|
|
_transactionFeeService = transactionFeeService;
|
|
_userService = userService;
|
|
_walkService = walkService;
|
|
_systemMessageService = systemMessageService;
|
|
_appHubSender = appHubSender;
|
|
_identityDocumentService = identityDocumentService;
|
|
_payoutService = payoutService;
|
|
_pushNotificationService = pushNotificationService;
|
|
_voucherCampaignService = voucherCampaignService;
|
|
}
|
|
|
|
#region Wallets
|
|
|
|
/// <summary>
|
|
/// Liefert alle Wallets eines App-Users
|
|
/// </summary>
|
|
/// <param name="appUserId">Id des App-Users</param>
|
|
/// <returns>Liste der Wallets</returns>
|
|
[HttpGet]
|
|
[Route("GetWallets")]
|
|
public async Task<IActionResult> GetWallets(string appUserId)
|
|
{
|
|
var clientOffset = GetClientDateOffset();
|
|
|
|
if (User?.Identity != null && User.Identity.IsAuthenticated)
|
|
{
|
|
var wallets = await _walletService.GetAllAsync(appUserId);
|
|
|
|
//Wenn Walltes vorhanden sind, dann den aktuellen Kontostand abfragen
|
|
if (wallets.Any())
|
|
{
|
|
var hasChanges = false;
|
|
foreach (var wallet in wallets)
|
|
{
|
|
var balanceResponse = await _mangoPayService.GetWalletBalanceAsync(wallet.WalletId);
|
|
if (balanceResponse.Success)
|
|
{
|
|
wallet.Balance = balanceResponse.Value;
|
|
hasChanges = true;
|
|
}
|
|
}
|
|
|
|
if (hasChanges)
|
|
await _walletService.CommitAsync("System", true);
|
|
}
|
|
|
|
var dtoList = Mapper.Map<List<WalletDto>>(wallets);
|
|
return Ok(dtoList);
|
|
}
|
|
|
|
return NotFound(CommunicationErrors.Common_NotFound);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Liefert den aktuellen Kontostand eines Wallets
|
|
/// </summary>
|
|
/// <param name="appUserId">Id des App-Users</param>
|
|
/// <param name="walletType">Typ des Wallets</param>
|
|
/// <returns>Aktueller Kontostand</returns>
|
|
[HttpGet]
|
|
[Route("GetWalletBalance")]
|
|
public async Task<IActionResult> GetWalletBalance(string appUserId, WalletTypeDto walletType)
|
|
{
|
|
var clientOffset = GetClientDateOffset();
|
|
|
|
if (User?.Identity != null && User.Identity.IsAuthenticated)
|
|
{
|
|
var wallet = await _walletService.GetAsync(appUserId, (WalletType)walletType);
|
|
if (wallet != null)
|
|
{
|
|
var balanceResponse = await _mangoPayService.GetWalletBalanceAsync(wallet.WalletId);
|
|
if (balanceResponse.Success)
|
|
{
|
|
wallet.Balance = balanceResponse.Value;
|
|
wallet.UpdatedAt = DateTimeOffset.UtcNow;
|
|
await _walletService.CommitAsync("System", true);
|
|
|
|
return Ok(wallet.Balance);
|
|
}
|
|
}
|
|
}
|
|
|
|
return NotFound(CommunicationErrors.Common_NotFound);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Bank-Verbindungen
|
|
|
|
/// <summary>
|
|
/// Anlegen oder Aktualisieren einer Bankverbindung eines AppUsers
|
|
/// </summary>
|
|
/// <param name="model">Model</param>
|
|
/// <returns>200 OK</returns>
|
|
[HttpPost]
|
|
[Route("CreateOrUpdateBankAccount")]
|
|
public async Task<IActionResult> CreateOrUpdateBankAccount(BankAccountDto model)
|
|
{
|
|
var clientOffset = GetClientDateOffset();
|
|
|
|
if (User?.Identity != null && User.Identity.IsAuthenticated)
|
|
{
|
|
var user = await _userService.GetByUsernameAsync(User.Identity.Name);
|
|
if (user != null)
|
|
{
|
|
if (ModelState.IsValid)
|
|
{
|
|
var bankAccount = Mapper.Map<BankAccount>(model);
|
|
|
|
if (bankAccount.IsBicValid())
|
|
{
|
|
if (bankAccount.IsIbanValid())
|
|
{
|
|
var appUser = await AppUserService.GetAsync(model.AppUserId);
|
|
if (appUser != null)
|
|
{
|
|
bool success = false;
|
|
//Wenn es noch kein Bankkonto gibt, dann anlegen
|
|
if (string.IsNullOrWhiteSpace(appUser.BankId))
|
|
{
|
|
var createBankAccountResult = await _mangoPayService.CreateBankAccountAsync(appUser.PaymentId, bankAccount.OwnerName, bankAccount.Address, bankAccount.Iban, bankAccount.Bic);
|
|
if (createBankAccountResult != null && createBankAccountResult.Success && !string.IsNullOrWhiteSpace(createBankAccountResult.Value))
|
|
{
|
|
appUser.BankId = createBankAccountResult.Value;
|
|
appUser.UpdatedAt = DateTimeOffset.UtcNow;
|
|
await AppUserService.CommitAsync(User.Identity.Name);
|
|
success = true;
|
|
}
|
|
|
|
if (createBankAccountResult != null && createBankAccountResult.Success == false)
|
|
{
|
|
success = false;
|
|
if (createBankAccountResult.ErrorMessages.ContainsKey("IBAN"))
|
|
return BadRequest(CommunicationErrors.BankAccount_Iban_Invalid);
|
|
if (createBankAccountResult.ErrorMessages.ContainsKey("BIC"))
|
|
return BadRequest(CommunicationErrors.BankAccount_Bic_Invalid);
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
//Die aktuelle Bankverbindung holen und mit den neuen Daten vergleichen
|
|
//Wenn sich diese geändert haben, ein neues Bankkonto anlegen und dem Benutzer zuweisen, das alte dann deaktivieren
|
|
|
|
var getBankAccountResult = await _mangoPayService.GetBankAccountAsync(appUser.Id, appUser.PaymentId, appUser.BankId);
|
|
if (getBankAccountResult.Success)
|
|
{
|
|
var createNewBankAccount = false;
|
|
var existingBankAccount = getBankAccountResult.Value;
|
|
if (existingBankAccount != null)
|
|
{
|
|
if (existingBankAccount.HasChanged(bankAccount))
|
|
{
|
|
createNewBankAccount = true;
|
|
//Das alte Bankkonto deaktivieren
|
|
await _mangoPayService.DeactivateBankAccountAsync(appUser.PaymentId, appUser.BankId);
|
|
}
|
|
else
|
|
{
|
|
return BadRequest(CommunicationErrors.BankAccount_CreateOrUpdate_Unchanged);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//Es gibt kein Bankkonto mehr, also neu anlegen
|
|
createNewBankAccount = true;
|
|
}
|
|
|
|
if (createNewBankAccount)
|
|
{
|
|
var createBankAccountResult = await _mangoPayService.CreateBankAccountAsync(appUser.PaymentId, bankAccount.OwnerName, bankAccount.Address, bankAccount.Iban, bankAccount.Bic);
|
|
if (createBankAccountResult != null && createBankAccountResult.Success && !string.IsNullOrWhiteSpace(createBankAccountResult.Value))
|
|
{
|
|
appUser.BankId = createBankAccountResult.Value;
|
|
appUser.UpdatedAt = DateTimeOffset.UtcNow;
|
|
await AppUserService.CommitAsync(User.Identity.Name);
|
|
success = true;
|
|
}
|
|
if (createBankAccountResult != null && createBankAccountResult.Success == false)
|
|
{
|
|
success = false;
|
|
if (createBankAccountResult.ErrorMessages.ContainsKey("IBAN"))
|
|
return BadRequest(CommunicationErrors.BankAccount_Iban_Invalid);
|
|
if (createBankAccountResult.ErrorMessages.ContainsKey("BIC"))
|
|
return BadRequest(CommunicationErrors.BankAccount_Bic_Invalid);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (success)
|
|
{
|
|
var appUserDto = Mapper.Map<AppUserDto>(appUser);
|
|
var baseAddress = GetBaseAddress();
|
|
if (!string.IsNullOrWhiteSpace(appUserDto.Photo))
|
|
{
|
|
appUserDto.Photo = $"{baseAddress}/file/documents/thumbnails/{200}/{appUserDto.Photo}";
|
|
}
|
|
|
|
return Ok(appUserDto);
|
|
}
|
|
return BadRequest(CommunicationErrors.BankAccount_CreateOrUpdate_Failed);
|
|
}
|
|
return NotFound(CommunicationErrors.Common_NotFound);
|
|
}
|
|
return BadRequest(CommunicationErrors.BankAccount_Iban_Invalid);
|
|
}
|
|
return BadRequest(CommunicationErrors.BankAccount_Bic_Invalid);
|
|
}
|
|
return BadRequest(CommunicationErrors.Common_Model_Invalid);
|
|
}
|
|
}
|
|
return NotFound(CommunicationErrors.Common_NotFound);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gibt die Bankverbindung eines AppUsers zurück
|
|
/// </summary>
|
|
/// <param name="appUserId">Id des App-Users</param>
|
|
/// <returns>200 OK</returns>
|
|
[HttpGet]
|
|
[Route("GetBankAccount")]
|
|
public async Task<IActionResult> GetBankAccount(string appUserId)
|
|
{
|
|
var clientOffset = GetClientDateOffset();
|
|
|
|
if (User?.Identity != null && User.Identity.IsAuthenticated)
|
|
{
|
|
var appUser = await AppUserService.GetAsync(appUserId);
|
|
if (appUser != null)
|
|
{
|
|
if (!string.IsNullOrWhiteSpace(appUser.BankId))
|
|
{
|
|
var getBankAccountResult = await _mangoPayService.GetBankAccountAsync(appUser.Id, appUser.PaymentId, appUser.BankId);
|
|
if (getBankAccountResult.Success)
|
|
{
|
|
var bankAccount = getBankAccountResult.Value;
|
|
if (bankAccount != null)
|
|
{
|
|
var bankAccountDto = Mapper.Map<BankAccountDto>(bankAccount);
|
|
return Ok(bankAccountDto);
|
|
}
|
|
}
|
|
}
|
|
return NotFound(CommunicationErrors.BankAccount_NotFound);
|
|
}
|
|
return NotFound(CommunicationErrors.Common_NotFound);
|
|
}
|
|
return NotFound(CommunicationErrors.Common_NotFound);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region KYC
|
|
|
|
/// <summary>
|
|
/// Gibt das aktuelle KYC-Dokument eines App-Users zurück
|
|
/// </summary>
|
|
/// <param name="appUserId">Id des App-Users</param>
|
|
/// <returns>200 OK</returns>
|
|
[HttpGet]
|
|
[Route("GetKycDocumentLatest")]
|
|
public async Task<IActionResult> GetKycDocumentLatest(string appUserId)
|
|
{
|
|
var clientOffset = GetClientDateOffset();
|
|
|
|
if (User?.Identity != null && User.Identity.IsAuthenticated)
|
|
{
|
|
var user = await _userService.GetByUsernameAsync(User.Identity.Name);
|
|
if (user != null)
|
|
{
|
|
var appUser = await AppUserService.GetAsync(appUserId);
|
|
if (appUser != null)
|
|
{
|
|
var identityDocument = await _identityDocumentService.GetLatestAsync(appUser.Id);
|
|
if (identityDocument != null)
|
|
{
|
|
//abgleich mit Mangopay-Dokument?
|
|
var kycDocumentResult = await _mangoPayService.GetKycDocumentAsync(identityDocument.MangoPayId);
|
|
if (kycDocumentResult != null && kycDocumentResult.Success)
|
|
{
|
|
if (kycDocumentResult.Value.Status != identityDocument.Status)
|
|
{
|
|
//Aktualisieren...
|
|
identityDocument.Status = kycDocumentResult.Value.Status;
|
|
identityDocument.Processed = kycDocumentResult.Value.ProcessedDate;
|
|
identityDocument.RefusedReasonType = kycDocumentResult.Value.RefusedReasonType;
|
|
identityDocument.RefusedReasonMessage = kycDocumentResult.Value.RefusedReasonMessage;
|
|
if (kycDocumentResult.Value.Flags != null && kycDocumentResult.Value.Flags.Any())
|
|
identityDocument.FlagsValue = string.Join(";", kycDocumentResult.Value.Flags);
|
|
else
|
|
identityDocument.FlagsValue = string.Empty;
|
|
identityDocument.UpdatedAt = DateTimeOffset.UtcNow;
|
|
await _identityDocumentService.CommitAsync(User.Identity.Name);
|
|
|
|
if (identityDocument.Status == IdentityDocumentStatus.VALIDATED && appUser.KycPassed == false)
|
|
{
|
|
appUser.KycPassed = true;
|
|
appUser.KycPassedDate = identityDocument.Processed ?? DateTimeOffset.UtcNow;
|
|
appUser.UpdatedAt = DateTimeOffset.UtcNow;
|
|
await AppUserService.CommitAsync(User.Identity.Name);
|
|
}
|
|
else if ((identityDocument.Status == IdentityDocumentStatus.REFUSED || identityDocument.Status == IdentityDocumentStatus.OUT_OF_DATE) && appUser.KycPassed)
|
|
{
|
|
appUser.KycPassed = false;
|
|
appUser.KycPassedDate = null;
|
|
appUser.UpdatedAt = DateTimeOffset.UtcNow;
|
|
await AppUserService.CommitAsync(User.Identity.Name);
|
|
}
|
|
}
|
|
}
|
|
|
|
var dto = Mapper.Map<IdentityDocumentDto>(identityDocument);
|
|
return Ok(dto);
|
|
}
|
|
return BadRequest(CommunicationErrors.Kyc_NoDocument);
|
|
}
|
|
}
|
|
}
|
|
|
|
return NotFound(CommunicationErrors.Common_NotFound);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Erstellen eines KYC-Dokumentes für die Identitätsprüfung
|
|
/// </summary>
|
|
/// <param name="model">Model mit Basis-Daten</param>
|
|
/// <param name="files">Dateien die eingereicht werden sollen</param>
|
|
/// <returns>200 OK</returns>
|
|
[HttpPost]
|
|
[Route("CreatKycDocument")]
|
|
public async Task<IActionResult> CreatKycDocument([ModelBinder(BinderType = typeof(JsonModelBinder))] IdentityDocumentCreateDto model, List<IFormFile> files)
|
|
{
|
|
var clientOffset = GetClientDateOffset();
|
|
|
|
if (User?.Identity != null && User.Identity.IsAuthenticated)
|
|
{
|
|
var user = await _userService.GetByUsernameAsync(User.Identity.Name);
|
|
if (user != null)
|
|
{
|
|
if (ModelState.IsValid)
|
|
{
|
|
var appUser = await AppUserService.GetAsync(model.AppUserId);
|
|
if (appUser != null)
|
|
{
|
|
if (files.Count > 0)
|
|
{
|
|
var fileList = new List<byte[]>();
|
|
|
|
foreach (var file in files)
|
|
{
|
|
using var ms = new MemoryStream();
|
|
await file.CopyToAsync(ms);
|
|
fileList.Add(ms.ToArray());
|
|
}
|
|
|
|
var createKycDocumentResult = await _mangoPayService.CreateKycDocumentAsync(appUser.PaymentId, IdentityDocumentType.IDENTITY_PROOF, fileList);
|
|
|
|
if (createKycDocumentResult.Success)
|
|
{
|
|
var documentId = createKycDocumentResult.Value;
|
|
var identityDocument = _identityDocumentService.Create(appUser.Id, documentId, appUser.PaymentId, IdentityDocumentType.IDENTITY_PROOF, (IdentityDocumentSource)model.Source, IdentityDocumentStatus.VALIDATION_ASKED, fileList.Count);
|
|
_identityDocumentService.Add(identityDocument);
|
|
await _identityDocumentService.CommitAsync(User.Identity.Name);
|
|
|
|
var dto = Mapper.Map<IdentityDocumentDto>(identityDocument);
|
|
return Ok(dto);
|
|
}
|
|
else
|
|
{
|
|
return BadRequest(CommunicationErrors.Kyc_CreationFailed);
|
|
}
|
|
}
|
|
return BadRequest(CommunicationErrors.Kyc_NoFiles);
|
|
}
|
|
return NotFound(CommunicationErrors.Common_NotFound);
|
|
}
|
|
return BadRequest(CommunicationErrors.Common_Model_Invalid);
|
|
}
|
|
}
|
|
return NotFound(CommunicationErrors.Common_NotFound);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Payment
|
|
|
|
/// <summary>
|
|
/// Authorisierung der Zahlung eines Walks mit einem vorhandenen Guthaben.
|
|
/// Das Geld wird auf das Transaktions-Konto überwiesen
|
|
/// </summary>
|
|
/// <param name="model">Model</param>
|
|
/// <returns>200 OK</returns>
|
|
[HttpPost]
|
|
[Route("AuthorizeWithCredit")]
|
|
public async Task<IActionResult> AuthorizeWithCredit(PayWalkWithCreditDto model)
|
|
{
|
|
var clientOffset = GetClientDateOffset();
|
|
|
|
if (User?.Identity != null && User.Identity.IsAuthenticated)
|
|
{
|
|
if (ModelState.IsValid)
|
|
{
|
|
var appUser = await AppUserService.GetAsync(model.AppUserId);
|
|
if (appUser != null)
|
|
{
|
|
var walk = await _walkService.GetAsync(model.WalkId);
|
|
if (walk != null && !walk.Deleted)
|
|
{
|
|
if (walk.Status != WalkStatus.Cancelled)
|
|
{
|
|
if (walk.PaymentStatus == PaymentStatus.Pending)
|
|
{
|
|
var ammount = (long)(model.Ammount * 100);
|
|
var tag = $"Walk_Authorize_{walk.Id}";
|
|
var transferMoneyResult = await _mangoPayService.TransferMoneyFromCreditToFeeAccountAsync(model.AppUserId, ammount, tag);
|
|
if (transferMoneyResult.Success)
|
|
{
|
|
walk.PaymentStatus = PaymentStatus.Authorized;
|
|
walk.UpdatedAt = DateTimeOffset.UtcNow;
|
|
await _walkService.CommitAsync(User.Identity.Name);
|
|
|
|
//Wallets aktualisieren
|
|
await _mangoPayService.GetWalletBalanceAsync(model.AppUserId, WalletType.Credits);
|
|
await _mangoPayService.GetWalletBalanceAsync(model.AppUserId, WalletType.Fees);
|
|
|
|
//Überlappende Walks die im Status "Request" sind, stornieren
|
|
await _walkService.CancelWalkRequestsAsync(walk.DogWalkerId, walk.Start, walk.End);
|
|
await _walkService.CommitAsync(User.Identity.Name);
|
|
|
|
//Walker informieren. TODO: pushnotification?
|
|
_systemMessageService.Add(walk.DogWalkerId, AppUserType.DogWalker, walk.Id, SystemMessageTables.Walk, SystemMessageType.WalkAdded, DateTimeOffset.UtcNow.AddDays(14));
|
|
await _systemMessageService.CommitAsync(User.Identity.Name);
|
|
await _appHubSender.SystemMessageAddedAsync(walk.DogWalkerId);
|
|
await _pushNotificationService.SendNewWalkAsync(walk.DogWalkerId, walk.Id);
|
|
|
|
var walkDto = Mapper.Map<WalkDto>(walk);
|
|
return Ok(walkDto);
|
|
}
|
|
}
|
|
return BadRequest(CommunicationErrors.Walk_PaymentStatus_Invalid);
|
|
}
|
|
return BadRequest(CommunicationErrors.Walk_Cancelled);
|
|
}
|
|
return NotFound(CommunicationErrors.Walk_NotFound);
|
|
}
|
|
return NotFound(CommunicationErrors.Common_NotFound);
|
|
}
|
|
return BadRequest(CommunicationErrors.Common_Model_Invalid);
|
|
}
|
|
return NotFound(CommunicationErrors.Common_NotFound);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Authorisierung der Zahlung eines komplett Walks mit einem Gutschein
|
|
/// </summary>
|
|
/// <param name="model">Model</param>
|
|
/// <returns>200 OK</returns>
|
|
[HttpPost]
|
|
[Route("AuthorizeWithVoucher")]
|
|
public async Task<IActionResult> AuthorizeWithVoucher(PayWalkWithVoucherDto model)
|
|
{
|
|
var clientOffset = GetClientDateOffset();
|
|
|
|
if (User?.Identity != null && User.Identity.IsAuthenticated)
|
|
{
|
|
if (ModelState.IsValid)
|
|
{
|
|
var appUser = await AppUserService.GetAsync(model.AppUserId);
|
|
if (appUser != null)
|
|
{
|
|
var walk = await _walkService.GetAsync(model.WalkId);
|
|
if (walk != null && !walk.Deleted)
|
|
{
|
|
if (walk.Status != WalkStatus.Cancelled)
|
|
{
|
|
if (walk.PaymentStatus == PaymentStatus.Pending)
|
|
{
|
|
var voucherUsed = await _voucherCampaignService.GetUsageAsync(model.VoucherId, model.AppUserId, model.WalkId);
|
|
|
|
if (voucherUsed != null && voucherUsed.UsageStatus == VoucherUsageStatus.Reserved && voucherUsed.VoucherAmmountUsed >= model.Ammount)
|
|
{
|
|
walk.PaymentStatus = PaymentStatus.Authorized;
|
|
walk.UpdatedAt = DateTimeOffset.UtcNow;
|
|
await _walkService.CommitAsync(User.Identity.Name);
|
|
|
|
//Überlappende Walks die im Status "Request" sind, stornieren
|
|
await _walkService.CancelWalkRequestsAsync(walk.DogWalkerId, walk.Start, walk.End);
|
|
await _walkService.CommitAsync(User.Identity.Name);
|
|
|
|
//Walker informieren. TODO: pushnotification?
|
|
_systemMessageService.Add(walk.DogWalkerId, AppUserType.DogWalker, walk.Id, SystemMessageTables.Walk, SystemMessageType.WalkAdded, DateTimeOffset.UtcNow.AddDays(14));
|
|
await _systemMessageService.CommitAsync(User.Identity.Name);
|
|
await _appHubSender.SystemMessageAddedAsync(walk.DogWalkerId);
|
|
await _pushNotificationService.SendNewWalkAsync(walk.DogWalkerId, walk.Id);
|
|
|
|
var walkDto = Mapper.Map<WalkDto>(walk);
|
|
return Ok(walkDto);
|
|
}
|
|
return BadRequest(CommunicationErrors.Walk_VoucherInvalid);
|
|
}
|
|
return BadRequest(CommunicationErrors.Walk_PaymentStatus_Invalid);
|
|
}
|
|
return BadRequest(CommunicationErrors.Walk_Cancelled);
|
|
}
|
|
return NotFound(CommunicationErrors.Walk_NotFound);
|
|
}
|
|
return NotFound(CommunicationErrors.Common_NotFound);
|
|
}
|
|
return BadRequest(CommunicationErrors.Common_Model_Invalid);
|
|
}
|
|
return NotFound(CommunicationErrors.Common_NotFound);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Startet das Einzahlen eines AppUsers
|
|
/// </summary>
|
|
/// <param name="model">Einzahlungs-einstellungen</param>
|
|
/// <returns>200 OK</returns>
|
|
[HttpPost]
|
|
[Route("PayIn")]
|
|
public async Task<IActionResult> PayIn(PayInDto model)
|
|
{
|
|
var clientOffset = GetClientDateOffset();
|
|
|
|
if (User?.Identity != null && User.Identity.IsAuthenticated)
|
|
{
|
|
if (ModelState.IsValid)
|
|
{
|
|
var appUser = await AppUserService.GetAsync(model.AppUserId);
|
|
if (appUser != null)
|
|
{
|
|
var walk = await _walkService.GetAsync(model.WalkId);
|
|
if (walk != null && !walk.Deleted)
|
|
{
|
|
if (walk.Status != WalkStatus.Cancelled)
|
|
{
|
|
if (walk.PaymentStatus == PaymentStatus.Pending)
|
|
{
|
|
var ammount = (long)(model.Ammount * 100);
|
|
var fees = (long)(model.Fees * 100);
|
|
var fromCredit = (long)(model.FromCredit * 100);
|
|
var returnUrl = Url.Action("PayInReturn", "MangoPay", null, protocol: HttpContext.Request.Scheme);
|
|
#if DEBUG
|
|
returnUrl = "http://10.0.0.31:54763/de/mangopay/payinreturn";// Url.Action("PayInReturn", "MangoPay", , protocol: "http");
|
|
#endif
|
|
|
|
if (model.PayInType == PayInTypeDto.CB_VISA_MASTERCARD)
|
|
{
|
|
var payInResult = await _mangoPayService.CreatePayInCardAsync(appUser.Id, walk.Id, ammount, fees, fromCredit, $"Walk_PayIn_{walk.Id}", returnUrl);
|
|
if (payInResult.Success)
|
|
{
|
|
var paymentResultDto = new PayInResponseDto
|
|
{
|
|
Url = payInResult.Value,
|
|
Success = true
|
|
};
|
|
return Ok(paymentResultDto);
|
|
}
|
|
return BadRequest(CommunicationErrors.PayIn_Failed);
|
|
}
|
|
|
|
if (model.PayInType == PayInTypeDto.MAESTRO)
|
|
{
|
|
var payInResult = await _mangoPayService.CreatePayInMaestroAsync(appUser.Id, walk.Id, ammount, fees, fromCredit, $"Walk_PayIn_{walk.Id}", returnUrl);
|
|
if (payInResult.Success)
|
|
{
|
|
var paymentResultDto = new PayInResponseDto
|
|
{
|
|
Url = payInResult.Value,
|
|
Success = true
|
|
};
|
|
return Ok(paymentResultDto);
|
|
}
|
|
return BadRequest(CommunicationErrors.PayIn_Failed);
|
|
}
|
|
|
|
if (model.PayInType == PayInTypeDto.KLARNA)
|
|
{
|
|
var payInResult = await _mangoPayService.CreatePayInKlarnaAsync(appUser.Id, walk.Id, ammount, fees, fromCredit, $"Walk_PayIn_{walk.Id}", returnUrl);
|
|
if (payInResult.Success)
|
|
{
|
|
var paymentResultDto = new PayInResponseDto
|
|
{
|
|
Url = payInResult.Value,
|
|
Success = true
|
|
};
|
|
return Ok(paymentResultDto);
|
|
}
|
|
return BadRequest(CommunicationErrors.PayIn_Failed);
|
|
}
|
|
|
|
if (model.PayInType == PayInTypeDto.PAYPAL)
|
|
{
|
|
var payInResult = await _mangoPayService.CreatePayInPayPalAsync(appUser.Id, walk.Id, ammount, fees, fromCredit, $"Walk_PayIn_{walk.Id}", returnUrl);
|
|
if (payInResult.Success)
|
|
{
|
|
var paymentResultDto = new PayInResponseDto
|
|
{
|
|
Url = payInResult.Value,
|
|
Success = true
|
|
};
|
|
return Ok(paymentResultDto);
|
|
}
|
|
return BadRequest(CommunicationErrors.PayIn_Failed);
|
|
}
|
|
|
|
}
|
|
return BadRequest(CommunicationErrors.Walk_PaymentStatus_Invalid);
|
|
}
|
|
return BadRequest(CommunicationErrors.Walk_Cancelled);
|
|
}
|
|
return NotFound(CommunicationErrors.Walk_NotFound);
|
|
}
|
|
return NotFound(CommunicationErrors.Common_NotFound);
|
|
}
|
|
return BadRequest(CommunicationErrors.Common_Model_Invalid);
|
|
}
|
|
return NotFound(CommunicationErrors.Common_NotFound);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Transaktionsgebühren
|
|
|
|
/// <summary>
|
|
/// Gibt eine Liste von Transaktionsgebühren für die Synchronisierung mit der App zurück
|
|
/// </summary>
|
|
/// <param name="lastUpdate">Letztes Update oder null, wenn noch keines</param>
|
|
/// <returns>HTTP 200 wenn erfolgreich</returns>
|
|
[Route("GetTransactionFees")]
|
|
[HttpGet]
|
|
public async Task<IActionResult> GetTransactionFees(DateTimeOffset? lastUpdate)
|
|
{
|
|
var clientOffset = GetClientDateOffset();
|
|
|
|
if (User?.Identity != null && User.Identity.IsAuthenticated)
|
|
{
|
|
var transactionFees = await _transactionFeeService.GetForSyncAsync(lastUpdate);
|
|
var transactionFeesDto = Mapper.Map<List<TransactionFeeDto>>(transactionFees);
|
|
|
|
return Ok(transactionFeesDto);
|
|
}
|
|
return NotFound(CommunicationErrors.Common_NotFound);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Payouts / Auszahlungen
|
|
|
|
/// <summary>
|
|
/// Abfrage der Auszahlungen eines AppUsers
|
|
/// </summary>
|
|
/// <param name="appUserId">Id des AppUsers</param>
|
|
/// <param name="lastUpdate">Letztes Update oder null, wenn noch keines</param>
|
|
/// <returns>Liste der Anfragen</returns>
|
|
[HttpGet]
|
|
[Route("GetPayoutsForSync")]
|
|
public async Task<IActionResult> GetPayoutsForSync(string appUserId, DateTimeOffset? lastUpdate)
|
|
{
|
|
var clientOffset = GetClientDateOffset();
|
|
|
|
if (User?.Identity != null && User.Identity.IsAuthenticated)
|
|
{
|
|
var payouts = await _payoutService.GetForSyncAppAsync(appUserId, lastUpdate);
|
|
var dtoList = Mapper.Map<List<PayoutDto>>(payouts);
|
|
return Ok(dtoList);
|
|
}
|
|
|
|
return NotFound(CommunicationErrors.Common_NotFound);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Abfragen von Auszahlungen
|
|
/// </summary>
|
|
/// <param name="model">Abfragemodel</param>
|
|
/// <returns>Liste von Walks</returns>
|
|
[HttpPost]
|
|
[Route("GetPayouts")]
|
|
public async Task<IActionResult> GetPayouts(PayoutQueryDto model)
|
|
{
|
|
var clientOffset = GetClientDateOffset();
|
|
|
|
if (User?.Identity != null && User.Identity.IsAuthenticated)
|
|
{
|
|
var baseAddress = GetBaseAddress();
|
|
var response = new ListResponseDto<PayoutDto>();
|
|
|
|
var sortList = Mapper.Map<List<DynamicSortOrder>>(model.DynamicSortOrder);
|
|
|
|
var queryResult = await _payoutService.GetAsync(model.AppUserId, sortList, model.Skip, model.Take);
|
|
|
|
response.Total = queryResult.total;
|
|
var resultList = queryResult.list;
|
|
|
|
var dtoList = Mapper.Map<List<PayoutDto>>(resultList);
|
|
|
|
response.List = dtoList;
|
|
response.Take = model.Take;
|
|
response.Skip = model.Skip;
|
|
|
|
return Ok(response);
|
|
|
|
}
|
|
return NotFound(CommunicationErrors.Common_NotFound);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Eine Beantragung für eine Auszahlung erstellen
|
|
/// </summary>
|
|
/// <param name="model">Model mit Daten</param>
|
|
/// <returns>200 OK</returns>
|
|
[HttpPost]
|
|
[Route("CreatePayout")]
|
|
public async Task<IActionResult> CreatePayout(PayoutCreateDto model)
|
|
{
|
|
var clientOffset = GetClientDateOffset();
|
|
|
|
if (User?.Identity != null && User.Identity.IsAuthenticated)
|
|
{
|
|
if (ModelState.IsValid)
|
|
{
|
|
var appUser = await AppUserService.GetAsync(model.AppUserId);
|
|
if (appUser != null)
|
|
{
|
|
//Wenn Benutzer gesperrt ist, dann keine Auszahlung
|
|
if (appUser.Locked)
|
|
{
|
|
return BadRequest(CommunicationErrors.PayOut_Failed);
|
|
}
|
|
|
|
//Prüfen ob der letzte Payout nicht älter als 24 Stunden ist
|
|
//TODO: in App als Fehlermeldung implementieren
|
|
var lastPayoutCount = await _payoutService.CountPayoutsInTimeRangeAsync(appUser.Id, DateTimeOffset.UtcNow.AddHours(-24), DateTimeOffset.UtcNow);
|
|
if (lastPayoutCount > 0)
|
|
{
|
|
//Wenn weniger als 24 Stunden, dann nicht erlaubt
|
|
return BadRequest(CommunicationErrors.PayOut_Failed);
|
|
}
|
|
|
|
//Kein KYC-Check, dann keine Auszahlung
|
|
if (!appUser.KycPassed)
|
|
{
|
|
return BadRequest(CommunicationErrors.PayOut_Failed);
|
|
}
|
|
|
|
//Wenn der erfolgreiche KYC-Check eines AppUsers nicht mindestens 48 Stunden zurückliegt, dann keine Auszahlung
|
|
if (appUser.KycPassedDate.HasValue && appUser.KycPassedDate.Value > DateTimeOffset.UtcNow.AddHours(-48))
|
|
{
|
|
return BadRequest(CommunicationErrors.PayOut_Failed);
|
|
}
|
|
|
|
//Wenn mmehr als 200 Euro, dann blockieren
|
|
//TODO: Überprüfen ob das für die App-User passt.
|
|
if (model.Ammount > 200)
|
|
{
|
|
return BadRequest(CommunicationErrors.PayOut_Failed);
|
|
}
|
|
|
|
var creditWallet = await _walletService.GetAsync(appUser.Id, WalletType.Credits);
|
|
if (creditWallet != null)
|
|
{
|
|
var getBankAccountResult = await _mangoPayService.GetBankAccountAsync(appUser.Id, appUser.PaymentId, appUser.BankId);
|
|
if (getBankAccountResult.Success)
|
|
{
|
|
var iban = string.Empty;
|
|
if(!string.IsNullOrWhiteSpace(getBankAccountResult.Value.Iban))
|
|
iban = getBankAccountResult.Value.Iban;
|
|
|
|
var bic = string.Empty;
|
|
if (!string.IsNullOrWhiteSpace(getBankAccountResult.Value.Bic))
|
|
bic = getBankAccountResult.Value.Bic;
|
|
|
|
if (iban.Length >= 4)
|
|
{
|
|
iban = iban.Substring(iban.Length - 4);
|
|
}
|
|
|
|
if (bic.Length >= 4)
|
|
{
|
|
bic = bic.Substring(bic.Length - 4);
|
|
}
|
|
|
|
var ammount = (long)(model.Ammount * 100);
|
|
|
|
var payout = _payoutService.Create(appUser.Id, appUser.PaymentId, "", creditWallet.Id, creditWallet.WalletId, appUser.BankId, iban, bic, ammount, "EUR");
|
|
|
|
var payoutResult = await _mangoPayService.CreatePayoutAsync(appUser.PaymentId, creditWallet.WalletId, appUser.BankId, ammount, $"Payout_{payout.Id}");
|
|
if (payoutResult.Success)
|
|
{
|
|
payout.MangoPayId = payoutResult.Value.PayoutId;
|
|
payout.Status = payoutResult.Value.Status;
|
|
payout.ResultCode = payoutResult.Value.ResultCode;
|
|
payout.ResultMessage = payoutResult.Value.ResultMessage;
|
|
payout.ExecutionDate = payoutResult.Value.ExecutionDate;
|
|
payout.UpdatedAt = DateTimeOffset.UtcNow;
|
|
|
|
_payoutService.Add(payout);
|
|
await _payoutService.CommitAsync(User.Identity.Name);
|
|
|
|
var dto = Mapper.Map<PayoutDto>(payout);
|
|
return Ok(dto);
|
|
}
|
|
return BadRequest(CommunicationErrors.PayOut_Failed);
|
|
}
|
|
}
|
|
}
|
|
return NotFound(CommunicationErrors.Common_NotFound);
|
|
}
|
|
return BadRequest(CommunicationErrors.Common_Model_Invalid);
|
|
}
|
|
return NotFound(CommunicationErrors.Common_NotFound);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gibt eine Auszahlung eines AppUsers zurück
|
|
/// </summary>
|
|
/// <param name="appUserId">Id des App-Users</param>
|
|
/// <param name="payoutId">ID der Auszahlung</param>
|
|
/// <returns>200 OK</returns>
|
|
[HttpGet]
|
|
[Route("GetPayout")]
|
|
public async Task<IActionResult> GetPayout(string appUserId, string payoutId)
|
|
{
|
|
var clientOffset = GetClientDateOffset();
|
|
|
|
if (User?.Identity != null && User.Identity.IsAuthenticated)
|
|
{
|
|
var appUser = await AppUserService.GetAsync(appUserId);
|
|
if (appUser != null)
|
|
{
|
|
var payout = await _payoutService.GetAsync(payoutId);
|
|
if (payout != null)
|
|
{
|
|
var dto = Mapper.Map<PayoutDto>(payout);
|
|
return Ok(dto);
|
|
}
|
|
return NotFound(CommunicationErrors.PayOut_NotFound);
|
|
}
|
|
return NotFound(CommunicationErrors.Common_NotFound);
|
|
}
|
|
return NotFound(CommunicationErrors.Common_NotFound);
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|