Add payment type handling for walks

- Updated `Walk` class to include `PaymentType` property.
- Introduced `WalkPaymentTypeDto` enum for payment types.
- Modified DTOs (`PublicWalkRequestAcceptDto`, `WalkCreateDto`, `WalkDto`, `WalkWithNamesDto`) to include `PaymentType`.
- Enhanced `ApiWalksController` with new endpoints for accepting and canceling walks, and confirming walks with ratings.
- Updated `MappingProfile` for seamless conversion between `WalkPaymentType` and `WalkPaymentTypeDto`.
This commit is contained in:
Florian Mihalits 2025-06-30 15:08:57 +02:00
parent ada8b7a200
commit 38142f2435
8 changed files with 882 additions and 0 deletions

View File

@ -50,6 +50,7 @@ namespace gehGassi.Core.Services
var walk = new Walk()
{
Id = Guid.NewGuid().ToString("N"),
PaymentType = WalkPaymentType.MangoPay,
Created = DateTimeOffset.UtcNow
};
return walk;

View File

@ -584,6 +584,21 @@ namespace gehGassi.Dto.Common
RefundendAndPayed = 60
}
/// <summary>
/// Zahlungsart für einen Walk
/// </summary>
public enum WalkPaymentTypeDto
{
/// <summary>
/// Mit MangoPay bezahlen (z.B. Kreditkarte)
/// </summary>
MangoPay = 1,
/// <summary>
/// Barzahlung
/// </summary>
Cash = 10
}
/// <summary>
/// Sortierreihenfolge
/// </summary>

View File

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Text;
using gehGassi.Dto.Common;
namespace gehGassi.Dto.Walks
{
@ -36,5 +37,9 @@ namespace gehGassi.Dto.Walks
/// </summary>
public DateTimeOffset UpdatedAt { get; set; }
/// <summary>
/// Zahlungsart für den Walk
/// </summary>
public WalkPaymentTypeDto PaymentType { get; set; }
}
}

View File

@ -101,5 +101,10 @@ namespace gehGassi.Dto.Walks
/// </summary>
[MaxLength(500)]
public string Info { get; set; }
/// <summary>
/// Zahlungsart für den Walk
/// </summary>
public WalkPaymentTypeDto PaymentType { get; set; }
}
}

View File

@ -179,6 +179,11 @@ namespace gehGassi.Dto.Walks
[Required]
public PaymentStatusDto PaymentStatus { get; set; }
/// <summary>
/// Zahlungsart für den Walk
/// </summary>
public WalkPaymentTypeDto PaymentType { get; set; }
/// <summary>
/// Datum der Erstellung des Walks
/// </summary>

View File

@ -204,6 +204,11 @@ namespace gehGassi.Dto.Walks
/// </summary>
public PaymentStatusDto PaymentStatus { get; set; }
/// <summary>
/// Zahlungsart für den Walk
/// </summary>
public WalkPaymentTypeDto PaymentType { get; set; }
/// <summary>
/// Id des Gutscheins der verwendet wurde, wenn einer verwendet wurde
/// </summary>

View File

@ -43,6 +43,7 @@ namespace gehGassi.Web.Controllers.Api
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[ApiController]
[ApiVersion(1)]
[ApiVersion(2)]
[Route("api/walks")]
[Route("api/v{v:apiVersion}/walks")]
public class ApiWalksController : ApiBaseController
@ -954,6 +955,120 @@ namespace gehGassi.Web.Controllers.Api
return NotFound(CommunicationErrors.Common_NotFound);
}
/// <summary>
/// Akzeptieren einer Antwort zu einer öffentlichen Anfrage
/// </summary>
/// <param name="model">PublicWalkRequestDto</param>
/// <returns>HTTP 200 OK, Felher sonst</returns>
[HttpPost]
[Route("AcceptPublicWalkRequest")]
[MapToApiVersion(2)]
public async Task<IActionResult> AcceptPublicWalkRequestV2(PublicWalkRequestAcceptDto model)
{
var clientOffset = GetClientDateOffset();
if (User?.Identity != null && User.Identity.IsAuthenticated)
{
if (ModelState.IsValid)
{
var request = await _publicWalkRequestService.GetAsync(model.PublicWalkRequestId);
if (request != null && request.DogOwnerId == model.DogOwnerId)
{
var response = await _publicWalkResponseService.GetAsync(model.PublicWalkResponseId);
if (response != null && response.PublicWalkRequestId == request.Id)
{
response.Status = PublicWalkResponseStatus.Accepted;
response.UpdatedAt = model.UpdatedAt;
await _publicWalkResponseService.CommitAsync(User.Identity.Name);
//Nun Completed setzen für alle anderen die Warten...
await _publicWalkResponseService.SetCompletedForWaitingAsync(request.Id);
await _publicWalkResponseService.CommitAsync(User.Identity.Name);
request.Status = PublicWalkRequestStatus.Placed;
request.PlacedDate = model.UpdatedAt;
request.PublicWalkResponseId = response.Id;
request.DogWalkerId = response.DogWalkerId;
request.UpdatedAt = model.UpdatedAt;
await _publicWalkResponseService.CommitAsync(User.Identity.Name);
//Nachricht an alle Angebote senden...
var responses = await _publicWalkResponseService.FindAsync(c => c.PublicWalkRequestId == request.Id);
foreach (var publicWalkResponse in responses.Where(c => c.DogWalkerId != request.DogWalkerId))
{
_systemMessageService.Add(publicWalkResponse.DogWalkerId, AppUserType.DogWalker, request.Id, SystemMessageTables.PublicWalkRequest, SystemMessageType.PublicWalkRequestChanged, DateTimeOffset.UtcNow.AddDays(14));
await _pushNotificationService.SendPublicWalkRequestChangedAsync(publicWalkResponse.DogWalkerId, request.Id);
}
await _systemMessageService.CommitAsync(User.Identity.Name);
foreach (var publicWalkResponse in responses.Where(c => c.DogWalkerId != request.DogWalkerId))
{
await _appHubSender.SystemMessageAddedAsync(publicWalkResponse.DogWalkerId);
}
//Nun einen Walk anlgegen!
var geometryFactory = NtsGeometryServices.Instance.CreateGeometryFactory(srid: 4326);
var geoLocation = geometryFactory.CreatePoint(new Coordinate(request.Location.X, request.Location.Y));
var walk = _walkService.Create();
walk.Type = WalkType.OpenRequest;
walk.ServiceType = request.RequestType == PublicWalkRequestType.Sitting ? WalkServiceType.Sitting : WalkServiceType.Walking;
walk.DogWalkerId = request.DogWalkerId;
walk.DogOwnerId = request.DogOwnerId;
walk.PublicWalkRequestId = request.Id;
walk.DogsJson = request.DogsJson;
walk.DogCount = request.DogCount;
walk.Start = request.StartDate;
walk.End = request.EndDate;
walk.PickupAddress = request.PickupAddress.Clone();
walk.PickupLocation = geoLocation;
walk.ReturnAddress = request.PickupAddress.Clone();
walk.ReturnLocation = geoLocation;
walk.Price = response.Price;
walk.Info = request.Info;
walk.Status = WalkStatus.Accepted;
walk.Started = null;
walk.Completed = null;
walk.Confirmed = null;
walk.Complained = null;
walk.ComplainReason = string.Empty;
walk.DeclinedReason = string.Empty;
walk.CancelledReason = string.Empty;
walk.CancelledBy = CancellationSource.None;
walk.CompletedInfo = string.Empty;
walk.Defactation = false;
walk.PaymentStatus = PaymentStatus.Pending;
walk.PaymentType = (WalkPaymentType)model.PaymentType;
walk.UpdatedAt = DateTimeOffset.UtcNow;
walk.Created = DateTimeOffset.UtcNow;
if (walk.Price == 0 || walk.PaymentType == WalkPaymentType.Cash)
walk.PaymentStatus = PaymentStatus.Authorized;
_walkService.Add(walk);
await _walkService.CommitAsync(User.Identity.Name);
if (walk.PaymentStatus is PaymentStatus.Authorized or PaymentStatus.Paid)
{
//Nachricht an den dogWalker senden
_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 NotFound(CommunicationErrors.Common_NotFound);
}
return BadRequest(CommunicationErrors.Common_Model_Invalid);
}
return NotFound(CommunicationErrors.Common_NotFound);
}
/// <summary>
/// Stornieren einer öffentlichen Anfrage
/// </summary>
@ -2224,6 +2339,128 @@ namespace gehGassi.Web.Controllers.Api
return NotFound(CommunicationErrors.Common_NotFound);
}
/// <summary>
/// Stornieren eines Walks
/// </summary>
/// <param name="model">PublicWalkRequestCancelDto</param>
/// <returns>HTTP 200 OK, Fehler sonst</returns>
[HttpPost]
[Route("CancelWalk")]
[MapToApiVersion(2)]
public async Task<IActionResult> CancelWalkV2(WalkCancelDto model)
{
var clientOffset = GetClientDateOffset();
if (User?.Identity != null && User.Identity.IsAuthenticated)
{
if (ModelState.IsValid)
{
var walk = await _walkService.GetAsync(model.WalkId);
if (walk != null)
{
if (!string.IsNullOrWhiteSpace(walk.VoucherId) && walk.VoucherAmmount > 0 && walk.PaymentType == WalkPaymentType.MangoPay)
{
var usage = await _voucherCampaignService.GetUsageAsync(walk.VoucherId, walk.DogOwnerId, walk.Id);
if (usage != null)
{
var voucher = await _voucherCampaignService.GetVoucherAsync(usage.VoucherId);
if (voucher != null && voucher.Id == usage.VoucherId)
{
var campaign = await _voucherCampaignService.GetAsync(voucher.VoucherCampaignId);
if (campaign != null)
{
var customer = await _customerService.GetAsync(campaign.CustomerId);
if (customer != null)
{
//Geld vom Transferkonto der Kamapgne auf das Guthabenkonto überweisen mit Verweis auf den Walk und Transaktion erfassen
var ammount = (int)(usage.VoucherAmmountUsed * 100);
var transferResult = await _mangoPayService.TransferMoneyAsync(customer.MangopayPaymentId, campaign.WalletFeeId,
customer.MangopayPaymentId, campaign.WalletCreditId, walk.Id, ammount, 0, $"VoucherId_{voucher.Id}_WalkId_{walk.Id}", "", "");
if (transferResult.Success)
{
//Usage beim voucher zurücksetzen (UsedCount, IsValid wenn Einzelgutschein)
voucher.UsedCount -= 1;
if (voucher.UsedCount < 0)
voucher.UsedCount = 0;
voucher.IsValid = true;
//Usage in der Kampagne zurücksetzen (VouchersUsed, BudgetUsed)
campaign.VouchersUsed -= 1;
if (campaign.VouchersUsed < 0)
campaign.VouchersUsed = 0;
campaign.BudgetUsed -= usage.VoucherAmmountUsed;
if (campaign.BudgetUsed < 0)
campaign.BudgetUsed = 0;
walk.VoucherId = null;
walk.VoucherAmmount = 0;
//Reservieren durch die Anlage einer VoucherUsage
_voucherCampaignService.RemoveUsage(usage);
}
}
}
}
}
}
walk.Status = WalkStatus.Cancelled;
walk.UpdatedAt = model.UpdatedAt;
walk.CancelledBy = (CancellationSource)model.CancellationSource;
walk.CancelledReason = model.CancelledReason;
//Jetzt noch wegen Zahlungsstatus prüfen
if (walk.PaymentStatus == PaymentStatus.Paid || walk.PaymentStatus == PaymentStatus.Authorized)
{
//Zurücküberweisen der Summe vom Transaktionskonto auf das Guthabenkonto
if (walk.Price > 0 && walk.PaymentType == WalkPaymentType.MangoPay)
{
var ammount = (long)(walk.Price * 100);
var tag = $"Walk_Cancel_{walk.Id}";
var refundResult = await _mangoPayService.TransferMoneyFromFeeToCreditAccountAsync(walk.DogOwnerId, ammount, tag);
if (refundResult.Success)
walk.PaymentStatus = PaymentStatus.Refunded;
else
walk.PaymentStatus = PaymentStatus.Voided;
}
else
walk.PaymentStatus = PaymentStatus.Voided;
}
await _walkService.CommitAsync(User.Identity.Name);
//Wallets aktualisieren
if (walk.PaymentType == WalkPaymentType.MangoPay)
{
await _mangoPayService.GetWalletBalanceAsync(walk.DogOwnerId, WalletType.Credits);
await _mangoPayService.GetWalletBalanceAsync(walk.DogOwnerId, WalletType.Fees);
}
if (walk.CancelledBy == CancellationSource.DogWalker)
{
_systemMessageService.Add(walk.DogOwnerId, AppUserType.DogOwner, walk.Id, SystemMessageTables.Walk, SystemMessageType.WalkCancelled, DateTimeOffset.UtcNow.AddDays(14));
await _systemMessageService.CommitAsync(User.Identity.Name);
await _appHubSender.SystemMessageAddedAsync(walk.DogOwnerId);
await _pushNotificationService.SendWalkCancelledAsync(walk.DogOwnerId, walk.Id, AppMode.DogOwner);
}
else
{
_systemMessageService.Add(walk.DogWalkerId, AppUserType.DogWalker, walk.Id, SystemMessageTables.Walk, SystemMessageType.WalkCancelled, DateTimeOffset.UtcNow.AddDays(14));
await _systemMessageService.CommitAsync(User.Identity.Name);
await _appHubSender.SystemMessageAddedAsync(walk.DogWalkerId);
await _pushNotificationService.SendWalkCancelledAsync(walk.DogWalkerId, walk.Id, AppMode.DogWalker);
}
return Ok();
}
return NotFound(CommunicationErrors.Common_NotFound);
}
return BadRequest(CommunicationErrors.Common_Model_Invalid);
}
return NotFound(CommunicationErrors.Common_NotFound);
}
/// <summary>
/// Starten eines Walks
/// </summary>
@ -2604,6 +2841,190 @@ namespace gehGassi.Web.Controllers.Api
return NotFound(CommunicationErrors.Common_NotFound);
}
/// <summary>
/// Bestätigen eines Walks
/// </summary>
/// <param name="model">WalkConfirmDto</param>
/// <returns>HTTP 200 OK, Felher sonst</returns>
[HttpPost]
[Route("ConfirmWalk")]
[MapToApiVersion(2)]
public async Task<IActionResult> ConfirmWalkV2(WalkConfirmDto model)
{
var clientOffset = GetClientDateOffset();
if (User?.Identity != null && User.Identity.IsAuthenticated)
{
if (ModelState.IsValid)
{
var walk = await _walkService.GetAsync(model.WalkId);
if (walk != null && walk.DogOwnerId == model.AppUserId)
{
var appUser = await AppUserService.GetAsync(walk.DogOwnerId);
if (appUser is { Locked: true })
{
return BadRequest(CommunicationErrors.Login_LockedOut);
}
var dogWalker = await AppUserService.GetAsync(walk.DogWalkerId);
if (dogWalker is { Locked: true })
{
return BadRequest(CommunicationErrors.Login_LockedOut);
}
if (walk.Status == WalkStatus.Completed && walk.Deleted == false)
{
walk.Status = WalkStatus.Confirmed;
walk.Confirmed = DateTimeOffset.UtcNow;
walk.UpdatedAt = DateTimeOffset.UtcNow;
walk.PaymentStatus = PaymentStatus.Paid;
await _walkService.CommitAsync(User.Identity.Name);
//Wenn der Dogwalker in 30 Minuten mehr als 4 Walks bestätigt bekommt, dann Sperren wegen Betrugsverdacht
var numberOfConfirmedWalksTwentyMinutes = await _walkService.CountConfirmedByTimeRangeAsync(walk.DogWalkerId, DateTimeOffset.UtcNow.AddMinutes(-30), DateTimeOffset.UtcNow);
if (numberOfConfirmedWalksTwentyMinutes >= 4)
{
if (dogWalker != null)
{
dogWalker.Locked = true;
dogWalker.LockedReason = "Suspicious behavior - verächtiges Verhalten";
dogWalker.LockedUntil = DateTimeOffset.UtcNow.AddYears(5);
await AppUserService.CommitAsync("System");
//Echten Benutzer sperren
var user = await _userService.GetByAppUserAsync(dogWalker.Id);
if (user != null)
{
user.LockoutEnd = DateTimeOffset.UtcNow.AddYears(5);
await _userService.CommitAsync("System");
await _ticketStore.RemoveAsync(user.UserName);
await _refreshTokenService.RemoveAllAsync(user.Id, User.Identity.Name);
await _refreshTokenService.CommitAsync("System");
}
}
}
if (walk.PaymentType == WalkPaymentType.MangoPay)
{
//Zahlung veranlassen....
if (walk.Price > 0)
{
decimal feeDecimal = 0;
long fee = 0;
var gehGassiFee = await _transactionFeeService.GetMinGehGassiFeeAsync(walk.Price);
if (gehGassiFee != null)
{
if (gehGassiFee.Percent > 0)
{
var percent = walk.Price * gehGassiFee.Percent / 100;
feeDecimal = percent;
}
if (gehGassiFee.Fixed > 0)
{
feeDecimal += gehGassiFee.Fixed;
}
}
if (feeDecimal > 0)
{
fee = (long)(feeDecimal * 100);
}
//Wenn es einen gutschein gibt, dann Geld vom Transferkontos des Gutscheins auf das transferkonto des Hundebesitzers überweisen
if (!string.IsNullOrWhiteSpace(walk.VoucherId) && walk.VoucherAmmount > 0)
{
var voucherUsed = await _voucherCampaignService.GetUsageAsync(walk.VoucherId, walk.DogOwnerId, walk.Id);
if (voucherUsed != null)
{
var ammountFromVoucher = (long)(voucherUsed.VoucherAmmountUsed * 100);
voucherUsed.UsageStatus = VoucherUsageStatus.Used;
await _voucherCampaignService.CommitAsync(User.Identity.Name);
var voucher = await _voucherCampaignService.GetVoucherAsync(voucherUsed.VoucherId);
if (voucher != null)
{
var campaign = await _voucherCampaignService.GetAsync(voucher.VoucherCampaignId);
if (campaign != null)
{
var customer = await _customerService.GetAsync(campaign.CustomerId);
if (customer != null)
{
var dogOwner = await AppUserService.GetAsync(walk.DogOwnerId);
if (dogOwner != null)
{
var wallet = await _walletService.GetAsync(walk.DogOwnerId, WalletType.Fees);
if (wallet != null)
{
var transferResult = await _mangoPayService.TransferMoneyAsync(customer.MangopayPaymentId, campaign.WalletFeeId, dogOwner.PaymentId, wallet.Id, walk.Id, ammountFromVoucher, 0, $"VoucherId_{voucher.Id}_WalkId_{walk.Id}", targetAppUserId: dogOwner.Id);
}
}
}
}
}
}
}
var sourceWallet = await _walletService.GetAsync(walk.DogOwnerId, WalletType.Fees);
var targetWallet = await _walletService.GetAsync(walk.DogWalkerId, WalletType.Credits);
if (sourceWallet != null && targetWallet != null)
{
var sourceUser = await AppUserService.GetAsync(walk.DogOwnerId);
var targetUser = await AppUserService.GetAsync(walk.DogWalkerId);
if (sourceUser != null && targetUser != null)
{
var ammount = (long)(walk.Price * 100);
var transcationSuccess = await _mangoPayService.TransferMoneyAsync(walk.DogOwnerId, walk.DogWalkerId, walk.Id, ammount, fee, $"WalkId_Payment_{walk.Id}");
//Wallets aktualisieren
await _mangoPayService.GetWalletBalanceAsync(sourceUser.Id, WalletType.Credits);
await _mangoPayService.GetWalletBalanceAsync(sourceUser.Id, WalletType.Fees);
await _mangoPayService.GetWalletBalanceAsync(targetUser.Id, WalletType.Credits);
await _mangoPayService.GetWalletBalanceAsync(targetUser.Id, WalletType.Fees);
}
}
}
}
//Coins für den Walker gutschreiben
var coins = walk.ServiceType switch
{
WalkServiceType.Walking => _coinsOptions.Value.WalkForDogwalker,
WalkServiceType.Sitting => _coinsOptions.Value.SittingForDowgWalker,
WalkServiceType.DayCare => _coinsOptions.Value.DayCareForDowgWalker,
_ => 0m
};
await _coinsService.DepositAsync(walk.DogWalkerId, AppUserType.DogWalker, coins, walk.Id);
//Coins für den Besitzer gutschreiben
var coinsOwner = walk.ServiceType switch
{
WalkServiceType.Walking => _coinsOptions.Value.WalkForOwner,
WalkServiceType.Sitting => _coinsOptions.Value.SittingForOwner,
WalkServiceType.DayCare => _coinsOptions.Value.DayCareForOwner,
_ => 0m
};
await _coinsService.DepositAsync(walk.DogOwnerId, AppUserType.DogOwner, coinsOwner, walk.Id);
_systemMessageService.Add(walk.DogWalkerId, AppUserType.DogWalker, walk.Id, SystemMessageTables.Walk, SystemMessageType.WalkConfirmed, DateTimeOffset.UtcNow.AddDays(14));
await _systemMessageService.CommitAsync(User.Identity.Name);
await _appHubSender.SystemMessageAddedAsync(walk.DogWalkerId);
await _pushNotificationService.SendWalkConfirmedAsync(walk.DogWalkerId, walk.Id);
var walkDto = Mapper.Map<WalkDto>(walk);
return Ok(walkDto);
}
}
return NotFound(CommunicationErrors.Common_NotFound);
}
return BadRequest(CommunicationErrors.Common_Model_Invalid);
}
return NotFound(CommunicationErrors.Common_NotFound);
}
/// <summary>
/// Bestätigen eines Walks mit Rating
/// </summary>
@ -2798,6 +3219,205 @@ namespace gehGassi.Web.Controllers.Api
return NotFound(CommunicationErrors.Common_NotFound);
}
/// <summary>
/// Bestätigen eines Walks mit Rating
/// </summary>
/// <param name="model">WalkConfirmDto</param>
/// <returns>HTTP 200 OK, Felher sonst</returns>
[HttpPost]
[Route("ConfirmWalkWithRating")]
[MapToApiVersion(2)]
public async Task<IActionResult> ConfirmWalkWithRatingV2(WalkConfirmWithRatingDto model)
{
var clientOffset = GetClientDateOffset();
if (User?.Identity != null && User.Identity.IsAuthenticated)
{
if (ModelState.IsValid)
{
var walk = await _walkService.GetAsync(model.WalkId);
if (walk != null && walk.DogOwnerId == model.AppUserId)
{
var appUser = await AppUserService.GetAsync(walk.DogOwnerId);
if (appUser is { Locked: true })
{
return BadRequest(CommunicationErrors.Login_LockedOut);
}
var dogWalker = await AppUserService.GetAsync(walk.DogWalkerId);
if (dogWalker is { Locked: true })
{
return BadRequest(CommunicationErrors.Login_LockedOut);
}
if (walk.Status == WalkStatus.Completed && walk.Deleted == false)
{
walk.Status = WalkStatus.Confirmed;
walk.Confirmed = DateTimeOffset.UtcNow;
walk.UpdatedAt = DateTimeOffset.UtcNow;
walk.PaymentStatus = PaymentStatus.Paid;
await _walkService.CommitAsync(User.Identity.Name);
//Wenn der Dogwalker in 30 Minuten mehr als 4 Walks bestätigt bekommt, dann Sperren wegen Betrugsverdacht
var numberOfConfirmedWalksTwentyMinutes = await _walkService.CountConfirmedByTimeRangeAsync(walk.DogWalkerId, DateTimeOffset.UtcNow.AddMinutes(-30), DateTimeOffset.UtcNow);
if (numberOfConfirmedWalksTwentyMinutes >= 4)
{
if (dogWalker != null)
{
dogWalker.Locked = true;
dogWalker.LockedReason = "Suspicious behavior - verächtiges Verhalten";
dogWalker.LockedUntil = DateTimeOffset.UtcNow.AddYears(5);
await AppUserService.CommitAsync("System");
//Echten Benutzer sperren
var user = await _userService.GetByAppUserAsync(dogWalker.Id);
if (user != null)
{
user.LockoutEnd = DateTimeOffset.UtcNow.AddYears(5);
await _userService.CommitAsync("System");
await _ticketStore.RemoveAsync(user.UserName);
await _refreshTokenService.RemoveAllAsync(user.Id, User.Identity.Name);
await _refreshTokenService.CommitAsync("System");
}
}
}
if (walk.PaymentType == WalkPaymentType.MangoPay)
{
//Zahlung veranlassen....
if (walk.Price > 0)
{
decimal feeDecimal = 0;
long fee = 0;
var gehGassiFee = await _transactionFeeService.GetMinGehGassiFeeAsync(walk.Price);
if (gehGassiFee != null)
{
if (gehGassiFee.Percent > 0)
{
var percent = walk.Price * gehGassiFee.Percent / 100;
feeDecimal = percent;
}
if (gehGassiFee.Fixed > 0)
{
feeDecimal += gehGassiFee.Fixed;
}
}
if (feeDecimal > 0)
{
fee = (long)(feeDecimal * 100);
}
//Wenn es einen gutschein gibt, dann Geld vom Transferkontos des Gutscheins auf das transferkonto des Hundebesitzers überweisen
if (!string.IsNullOrWhiteSpace(walk.VoucherId) && walk.VoucherAmmount > 0)
{
var voucherUsed = await _voucherCampaignService.GetUsageAsync(walk.VoucherId, walk.DogOwnerId, walk.Id);
if (voucherUsed != null)
{
var ammountFromVoucher = (long)(voucherUsed.VoucherAmmountUsed * 100);
voucherUsed.UsageStatus = VoucherUsageStatus.Used;
await _voucherCampaignService.CommitAsync(User.Identity.Name);
var voucher = await _voucherCampaignService.GetVoucherAsync(voucherUsed.VoucherId);
if (voucher != null)
{
var campaign = await _voucherCampaignService.GetAsync(voucher.VoucherCampaignId);
if (campaign != null)
{
var customer = await _customerService.GetAsync(campaign.CustomerId);
if (customer != null)
{
var dogOwner = await AppUserService.GetAsync(walk.DogOwnerId);
if (dogOwner != null)
{
var wallet = await _walletService.GetAsync(walk.DogOwnerId, WalletType.Fees);
if (wallet != null)
{
var transferResult = await _mangoPayService.TransferMoneyAsync(customer.MangopayPaymentId, campaign.WalletFeeId, dogOwner.PaymentId, wallet.Id, walk.Id, ammountFromVoucher, 0, $"VoucherId_{voucher.Id}_WalkId_{walk.Id}", targetAppUserId: dogOwner.Id);
}
}
}
}
}
}
}
var sourceWallet = await _walletService.GetAsync(walk.DogOwnerId, WalletType.Fees);
var targetWallet = await _walletService.GetAsync(walk.DogWalkerId, WalletType.Credits);
if (sourceWallet != null && targetWallet != null)
{
var sourceUser = await AppUserService.GetAsync(walk.DogOwnerId);
var targetUser = await AppUserService.GetAsync(walk.DogWalkerId);
if (sourceUser != null && targetUser != null)
{
var ammount = (long)(walk.Price * 100);
var transcationSuccess = await _mangoPayService.TransferMoneyAsync(walk.DogOwnerId, walk.DogWalkerId, walk.Id, ammount, fee, $"WalkId_Payment_{walk.Id}");
//Wallets aktualisieren
await _mangoPayService.GetWalletBalanceAsync(sourceUser.Id, WalletType.Credits);
await _mangoPayService.GetWalletBalanceAsync(sourceUser.Id, WalletType.Fees);
await _mangoPayService.GetWalletBalanceAsync(targetUser.Id, WalletType.Credits);
await _mangoPayService.GetWalletBalanceAsync(targetUser.Id, WalletType.Fees);
}
}
}
}
//Coins für den Walker gutschreiben
var coins = walk.ServiceType switch
{
WalkServiceType.Walking => _coinsOptions.Value.WalkForDogwalker,
WalkServiceType.Sitting => _coinsOptions.Value.SittingForDowgWalker,
WalkServiceType.DayCare => _coinsOptions.Value.DayCareForDowgWalker,
_ => 0m
};
await _coinsService.DepositAsync(walk.DogWalkerId, AppUserType.DogWalker, coins, walk.Id);
//Coins für den Besitzer gutschreiben
var coinsOwner = walk.ServiceType switch
{
WalkServiceType.Walking => _coinsOptions.Value.WalkForOwner,
WalkServiceType.Sitting => _coinsOptions.Value.SittingForOwner,
WalkServiceType.DayCare => _coinsOptions.Value.DayCareForOwner,
_ => 0m
};
await _coinsService.DepositAsync(walk.DogOwnerId, AppUserType.DogOwner, coinsOwner, walk.Id);
//Rating erstellen
//var appUser = await AppUserService.GetAsync(walk.DogOwnerId);
if (appUser != null)
{
var rating = _ratingService.Create(walk.DogOwnerId, appUser.Type, walk.DogWalkerId, RatingTarget.Walker, model.RatingPoints, model.RatingInfo);
_ratingService.Add(rating);
await _ratingService.CommitAsync(User.Identity.Name);
//Statistik Hundebesitzer aktualisieren
await _ratingService.UpdateRatingStatisticsAsync(walk.DogWalkerId, RatingTarget.Walker);
await _ratingService.CommitAsync(User.Identity.Name);
}
_systemMessageService.Add(walk.DogWalkerId, AppUserType.DogWalker, walk.Id, SystemMessageTables.Walk, SystemMessageType.WalkConfirmed, DateTimeOffset.UtcNow.AddDays(14));
await _systemMessageService.CommitAsync(User.Identity.Name);
await _appHubSender.SystemMessageAddedAsync(walk.DogWalkerId);
await _pushNotificationService.SendWalkConfirmedAsync(walk.DogWalkerId, walk.Id);
var walkDto = Mapper.Map<WalkDto>(walk);
return Ok(walkDto);
}
}
return NotFound(CommunicationErrors.Common_NotFound);
}
return BadRequest(CommunicationErrors.Common_Model_Invalid);
}
return NotFound(CommunicationErrors.Common_NotFound);
}
/// <summary>
/// Zahlungsstatus eines Walks setzen
/// </summary>
@ -2947,6 +3567,102 @@ namespace gehGassi.Web.Controllers.Api
return BadRequest(CommunicationErrors.Common_Model_Invalid);
}
/// <summary>
/// Anlegen eines Walks wenn DIREKT! buchen möglich ist
/// </summary>
/// <param name="model">WalkCreateDto</param>
/// <returns>HTTP 200 OK, Felher sonst</returns>
[HttpPost]
[Route("CreateWalkDirect")]
[MapToApiVersion(2)]
public async Task<IActionResult> CreateWalkDirectV2(WalkCreateDto model)
{
var clientOffset = GetClientDateOffset();
var result = new CreateResponseDto<WalkDto>
{
Status = CreateStatusDto.Error,
Value = null
};
if (User?.Identity != null && User.Identity.IsAuthenticated)
{
if (ModelState.IsValid)
{
var appUserId = User.AppUserId();
var isLocked = await AppUserService.IsLockedAsync(appUserId);
var isBlocked = await AppUserService.IsBlockedAsync(model.DogWalkerId, appUserId);
var walkPossible = await _walkService.IsWalkPossibleAsync(model.DogWalkerId, model.Start, model.End);
if (walkPossible && !isLocked && !isBlocked)
{
//Nun einen Walk anlgegen!
var geometryFactory = NtsGeometryServices.Instance.CreateGeometryFactory(srid: 4326);
var geoLocationPickup = geometryFactory.CreatePoint(new Coordinate(model.PickupAddressLng, model.PickupAddressLat));
var geoLocationReturn = geometryFactory.CreatePoint(new Coordinate(model.ReturnAddressLng, model.ReturnAddressLat));
var walk = _walkService.Create();
walk.Type = (WalkType)model.Type;
walk.ServiceType = (WalkServiceType)model.ServiceType;
walk.DogWalkerId = model.DogWalkerId;
walk.DogOwnerId = model.DogOwnerId;
walk.PublicWalkRequestId = string.Empty;
walk.DogsJson = model.DogsJson;
walk.DogCount = model.DogCount;
walk.Start = model.Start;
walk.End = model.End;
walk.PickupAddress = Mapper.Map<Address>(model.PickupAddress);
walk.PickupLocation = geoLocationPickup;
walk.ReturnAddress = Mapper.Map<Address>(model.ReturnAddress);
walk.ReturnLocation = geoLocationReturn;
walk.Price = model.Price;
walk.Info = model.Info;
walk.Status = WalkStatus.Accepted;
walk.Started = null;
walk.Completed = null;
walk.Confirmed = null;
walk.Complained = null;
walk.ComplainReason = string.Empty;
walk.DeclinedReason = string.Empty;
walk.CancelledReason = string.Empty;
walk.CancelledBy = CancellationSource.None;
walk.CompletedInfo = string.Empty;
walk.Defactation = false;
walk.PaymentStatus = PaymentStatus.Pending;
walk.PaymentType = (WalkPaymentType)model.PaymentType;
walk.UpdatedAt = DateTimeOffset.UtcNow;
walk.Created = DateTimeOffset.UtcNow;
if (walk.Price == 0 || walk.PaymentType == WalkPaymentType.Cash)
walk.PaymentStatus = PaymentStatus.Authorized;
_walkService.Add(walk);
await _walkService.CommitAsync(User.Identity.Name);
//Überlappende Walks die im Status "Request" sind, stornieren
if (walk.PaymentStatus is PaymentStatus.Authorized or PaymentStatus.Paid)
{
//Wenn der Preis eines Walks 0 ist, dann wird dieser direkt angelegt und gilt.
await _walkService.CancelWalkRequestsAsync(walk.DogWalkerId, walk.Start, walk.End);
await _walkService.CommitAsync(User.Identity.Name);
_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);
}
result.Status = CreateStatusDto.Success;
result.Value = Mapper.Map<WalkDto>(walk);
return Ok(result);
}
return NotFound(CommunicationErrors.Walk_NotPossible);
}
}
return BadRequest(CommunicationErrors.Common_Model_Invalid);
}
/// <summary>
/// Anlegen eines Walks als Anfrage
/// </summary>
@ -3031,6 +3747,92 @@ namespace gehGassi.Web.Controllers.Api
return BadRequest(CommunicationErrors.Common_Model_Invalid);
}
/// <summary>
/// Anlegen eines Walks als Anfrage
/// </summary>
/// <param name="model">WalkCreateDto</param>
/// <returns>HTTP 200 OK, Felher sonst</returns>
[HttpPost]
[Route("CreateWalkRequest")]
[MapToApiVersion(2)]
public async Task<IActionResult> CreateWalkRequestV2(WalkCreateDto model)
{
var clientOffset = GetClientDateOffset();
var result = new CreateResponseDto<WalkDto>
{
Status = CreateStatusDto.Error,
Value = null
};
if (User?.Identity != null && User.Identity.IsAuthenticated)
{
if (ModelState.IsValid)
{
var appUserId = User.AppUserId();
var isLocked = await AppUserService.IsLockedAsync(appUserId);
var isBlocked = await AppUserService.IsBlockedAsync(model.DogWalkerId, appUserId);
var walkPossible = await _walkService.IsWalkPossibleAsync(model.DogWalkerId, model.Start, model.End);
if (walkPossible && !isLocked && !isBlocked)
{
//Nun einen Walk anlgegen!
var geometryFactory = NtsGeometryServices.Instance.CreateGeometryFactory(srid: 4326);
var geoLocationPickup = geometryFactory.CreatePoint(new Coordinate(model.PickupAddressLng, model.PickupAddressLat));
var geoLocationReturn = geometryFactory.CreatePoint(new Coordinate(model.ReturnAddressLng, model.ReturnAddressLat));
var walk = _walkService.Create();
walk.Type = (WalkType)model.Type;
walk.ServiceType = (WalkServiceType)model.ServiceType;
walk.DogWalkerId = model.DogWalkerId;
walk.DogOwnerId = model.DogOwnerId;
walk.PublicWalkRequestId = string.Empty;
walk.DogsJson = model.DogsJson;
walk.DogCount = model.DogCount;
walk.Start = model.Start;
walk.End = model.End;
walk.PickupAddress = Mapper.Map<Address>(model.PickupAddress);
walk.PickupLocation = geoLocationPickup;
walk.ReturnAddress = Mapper.Map<Address>(model.ReturnAddress);
walk.ReturnLocation = geoLocationReturn;
walk.Price = model.Price;
walk.Info = model.Info;
walk.Status = WalkStatus.Requested;
walk.Started = null;
walk.Completed = null;
walk.Confirmed = null;
walk.Complained = null;
walk.ComplainReason = string.Empty;
walk.DeclinedReason = string.Empty;
walk.CancelledReason = string.Empty;
walk.CancelledBy = CancellationSource.None;
walk.CompletedInfo = string.Empty;
walk.Defactation = false;
walk.PaymentStatus = PaymentStatus.None;
walk.PaymentType = (WalkPaymentType)model.PaymentType;
walk.UpdatedAt = DateTimeOffset.UtcNow;
walk.Created = DateTimeOffset.UtcNow;
_walkService.Add(walk);
await _walkService.CommitAsync(User.Identity.Name);
_systemMessageService.Add(walk.DogWalkerId, AppUserType.DogWalker, walk.Id, SystemMessageTables.Walk, SystemMessageType.WalkRequested, DateTimeOffset.UtcNow.AddDays(14));
await _systemMessageService.CommitAsync(User.Identity.Name);
await _appHubSender.SystemMessageAddedAsync(walk.DogWalkerId);
await _pushNotificationService.SendWalkRequestedAsync(walk.DogWalkerId, walk.Id);
result.Status = CreateStatusDto.Success;
result.Value = Mapper.Map<WalkDto>(walk);
return Ok(result);
}
return NotFound(CommunicationErrors.Walk_NotPossible);
}
}
return BadRequest(CommunicationErrors.Common_Model_Invalid);
}
/// <summary>
/// Akzeptieren eines Walks
/// </summary>
@ -3071,6 +3873,47 @@ namespace gehGassi.Web.Controllers.Api
return NotFound(CommunicationErrors.Common_NotFound);
}
/// <summary>
/// Akzeptieren eines Walks
/// </summary>
/// <param name="model">WalkAcceptDto</param>
/// <returns>HTTP 200 OK, Felher sonst</returns>
[HttpPost]
[Route("AcceptWalk")]
[MapToApiVersion(2)]
public async Task<IActionResult> AcceptWalkV2(WalkAcceptDto model)
{
var clientOffset = GetClientDateOffset();
if (User?.Identity != null && User.Identity.IsAuthenticated)
{
if (ModelState.IsValid)
{
var walk = await _walkService.GetAsync(model.WalkId);
if (walk != null)
{
walk.Status = WalkStatus.Accepted;
walk.PaymentStatus = PaymentStatus.Pending;
walk.UpdatedAt = model.UpdatedAt;
if (walk.Price == 0 || walk.PaymentType == WalkPaymentType.Cash)
walk.PaymentStatus = PaymentStatus.Authorized;
await _walkService.CommitAsync(User.Identity.Name);
_systemMessageService.Add(walk.DogOwnerId, AppUserType.DogOwner, walk.Id, SystemMessageTables.Walk, SystemMessageType.WalkAccepted, DateTimeOffset.UtcNow.AddDays(14));
await _systemMessageService.CommitAsync(User.Identity.Name);
await _appHubSender.SystemMessageAddedAsync(walk.DogOwnerId);
await _pushNotificationService.SendWalkAcceptedAsync(walk.DogOwnerId, walk.Id);
return Ok();
}
return NotFound(CommunicationErrors.Common_NotFound);
}
return BadRequest(CommunicationErrors.Common_Model_Invalid);
}
return NotFound(CommunicationErrors.Common_NotFound);
}
/// <summary>
/// Ablehnen eines Walks
/// </summary>

View File

@ -641,6 +641,9 @@ namespace gehGassi.Web.Mapper
CreateMap<PaymentStatus, PaymentStatusDto>();
CreateMap<PaymentStatusDto, PaymentStatus>();
CreateMap<WalkPaymentType, WalkPaymentTypeDto>();
CreateMap<WalkPaymentTypeDto, WalkPaymentType>();
CreateMap<RatingTarget, RatingTargetDto>();
CreateMap<RatingTargetDto, RatingTarget>();