Refactor WalkService and enhance API account features

Significant updates to WalkService.cs for improved fee handling and voucher processing. Introduced API version 3 in ApiAccountController.cs with new user registration and external login endpoints, supporting Google and Apple. Added functionality for setting user types without creating MangoPay users.
This commit is contained in:
Florian Mihalits 2025-06-30 17:17:09 +02:00
parent 38142f2435
commit b481a3999a
2 changed files with 491 additions and 58 deletions

View File

@ -592,81 +592,84 @@ namespace gehGassi.Core.Services
walk.PaymentStatus = PaymentStatus.Paid; walk.PaymentStatus = PaymentStatus.Paid;
await CommitAsync("System"); await CommitAsync("System");
decimal feeDecimal = 0; if (walk.PaymentType == WalkPaymentType.MangoPay)
long feeFromUser = 0;
long feeFromVoucher = 0;
var gehGassiFee = await gehGassiTransactionService.GetMinGehGassiFeeAsync(walk.Price);
if (gehGassiFee != null)
{ {
if (gehGassiFee.Percent > 0) decimal feeDecimal = 0;
long feeFromUser = 0;
long feeFromVoucher = 0;
var gehGassiFee = await gehGassiTransactionService.GetMinGehGassiFeeAsync(walk.Price);
if (gehGassiFee != null)
{ {
var percent = walk.Price * gehGassiFee.Percent / 100; if (gehGassiFee.Percent > 0)
feeDecimal = percent;
}
if (gehGassiFee.Fixed > 0)
{
feeDecimal += gehGassiFee.Fixed;
}
}
if (feeDecimal > 0)
{
feeFromUser = (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 CommitAsync("System");
var voucher = await voucherCampaignService.GetVoucherAsync(voucherUsed.VoucherId);
if (voucher != null)
{ {
var campaign = await voucherCampaignService.GetAsync(voucher.VoucherCampaignId); var percent = walk.Price * gehGassiFee.Percent / 100;
if (campaign != null) feeDecimal = percent;
}
if (gehGassiFee.Fixed > 0)
{
feeDecimal += gehGassiFee.Fixed;
}
}
if (feeDecimal > 0)
{
feeFromUser = (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 CommitAsync("System");
var voucher = await voucherCampaignService.GetVoucherAsync(voucherUsed.VoucherId);
if (voucher != null)
{ {
var customer = await customerService.GetAsync(campaign.CustomerId); var campaign = await voucherCampaignService.GetAsync(voucher.VoucherCampaignId);
if (customer != null) if (campaign != null)
{ {
var dogOwner = await appUserService.GetAsync(walk.DogOwnerId); var customer = await customerService.GetAsync(campaign.CustomerId);
if (dogOwner != null) if (customer != null)
{ {
var wallet = await walletService.GetAsync(walk.DogOwnerId, WalletType.Fees); var dogOwner = await appUserService.GetAsync(walk.DogOwnerId);
if (wallet != null) if (dogOwner != 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 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 sourceWallet = await walletService.GetAsync(walk.DogOwnerId, WalletType.Fees);
var targetWallet = await walletService.GetAsync(walk.DogWalkerId, WalletType.Credits); var targetWallet = await walletService.GetAsync(walk.DogWalkerId, WalletType.Credits);
if (sourceWallet != null && targetWallet != null) 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 sourceUser = await appUserService.GetAsync(walk.DogOwnerId);
var transcationSuccess = await mangoPayService.TransferMoneyAsync(walk.DogOwnerId, walk.DogWalkerId, walk.Id, ammount, feeFromUser, $"WalkId_Payment_{walk.Id}"); var targetUser = await appUserService.GetAsync(walk.DogWalkerId);
//Wallets aktualisieren if (sourceUser != null && targetUser != null)
await mangoPayService.GetWalletBalanceAsync(sourceUser.Id, WalletType.Credits); {
await mangoPayService.GetWalletBalanceAsync(sourceUser.Id, WalletType.Fees); var ammount = (long)(walk.Price * 100);
var transcationSuccess = await mangoPayService.TransferMoneyAsync(walk.DogOwnerId, walk.DogWalkerId, walk.Id, ammount, feeFromUser, $"WalkId_Payment_{walk.Id}");
await mangoPayService.GetWalletBalanceAsync(targetUser.Id, WalletType.Credits); //Wallets aktualisieren
await mangoPayService.GetWalletBalanceAsync(targetUser.Id, WalletType.Fees); 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);
}
} }
} }
} }

View File

@ -56,6 +56,7 @@ namespace gehGassi.Web.Controllers.Api
[ApiController] [ApiController]
[ApiVersion(1)] [ApiVersion(1)]
[ApiVersion(2)] [ApiVersion(2)]
[ApiVersion(3)]
[Route("api/account")] [Route("api/account")]
[Route("api/v{v:apiVersion}/account")] [Route("api/v{v:apiVersion}/account")]
public class ApiAccountController : ApiBaseController public class ApiAccountController : ApiBaseController
@ -465,6 +466,131 @@ namespace gehGassi.Web.Controllers.Api
return BadRequest(CommunicationErrors.Common_Model_Invalid); return BadRequest(CommunicationErrors.Common_Model_Invalid);
} }
/// <summary>
/// Registrieren eines App-Users
/// </summary>
/// <param name="model">Model</param>
/// <returns>HTTP 200 OK wenn erfolgreich</returns>
[AllowAnonymous]
[HttpPost]
[MapToApiVersion(3)]
[Route("Register")]
public async Task<IActionResult> RegisterV3(RegisterV2Dto model)
{
var clientOffset = GetClientDateOffset();
if (ModelState.IsValid)
{
var user = await _userService.GetByUsernameAsync(model.UserName);
if (user == null)
{
user = _userService.Create();
user.Id = Guid.NewGuid().ToString();
user.UserName = model.UserName;
user.FirstName = model.FirstName;
user.LastName = model.LastName;
user.FullName = $"{model.FirstName} {model.LastName}";
user.RegistrationDate = DateTimeOffset.UtcNow;
user.Photo = string.Empty;
user.Email = model.UserName;
user.EmailConfirmed = !_authOptions.Value.MustConfirmEmail;
user.Permissions = PermissionHelper.GetForRole("AppUser").PackPermissionsIntoString();
var createResult = await _userManager.CreateAsync(user, model.Password);
if (createResult.Succeeded)
{
await _userManager.AddToRoleAsync(user, "AppUser");
user = await _userService.GetAsync(user.Id);
//Nun den AppUser anlegen und dann mit dem Benutzer verknüpfen
var appUser = AppUserService.Create();
appUser.Type = (AppUserType)model.AppUserType;
appUser.Number = AppUserService.GetNextNumber();
appUser.FirstName = model.FirstName;
appUser.LastName = model.LastName;
appUser.Sex = Sex.Undefined;
appUser.BirthDate = model.BirthDate;
appUser.Photo = string.Empty;
appUser.Contact.Email = model.UserName;
appUser.Address.City = model.City;
appUser.Address.Zip = model.Zip;
appUser.Address.CountryCode = model.CountryCode;
appUser.Address.State = model.State;
appUser.TermsAccepted = true;
appUser.TermsAcceptedDate = DateTimeOffset.UtcNow;
appUser.PrivacyAccepted = true;
appUser.PrivacyAcceptedDate = DateTimeOffset.UtcNow;
appUser.Verified = false;
appUser.VerifiedDate = null;
appUser.PaymentTermsAccepted = false;
appUser.PaymentTermsAcceptedDate = null;
appUser.NationalityCode = string.Empty;
appUser.MainResidenceCode = string.Empty;
if (appUser.Type == AppUserType.DogWalker)
{
appUser.PaymentTermsAccepted = false;
appUser.PaymentTermsAcceptedDate = null;
appUser.NationalityCode = model.NationalityCode;
appUser.MainResidenceCode = model.MainResidenceCountryCode;
}
var location = await _geoLocationService.GetLocationAsync($"{model.Zip},{model.City},{model.CountryCode},{model.State}", CultureInfo.CurrentCulture.TwoLetterISOLanguageName);
if (location.Success)
{
var geometryFactory = NtsGeometryServices.Instance.CreateGeometryFactory(srid: 4326);
var geoLocation = geometryFactory.CreatePoint(new Coordinate(location.Longitude, location.Latitude));
appUser.Location = geoLocation;
}
AppUserService.Add(appUser);
await AppUserService.CommitAsync("System");
user.AppUserId = appUser.Id;
await _userService.CommitAsync("System");
if (appUser.Type != AppUserType.DogOwner)
{
await AppUserService.CreateWalkerProfileIfNotExistsAsync(appUser.Id);
await AppUserService.CommitAsync("System");
//Nun für den Walker Mangopay User anlegen
//var mangoPayResult = await _mangoPayService.CreateOwnerAsync(appUser.Id, false);
//if (mangoPayResult.Success)
//{
// //Wallets anlegen
// var walletCreditsResult = await _mangoPayService.CreateWalletAsync(appUser.Id, WalletType.Credits);
// var walletFeesResult = await _mangoPayService.CreateWalletAsync(appUser.Id, WalletType.Fees);
//}
}
if (_authOptions.Value.MustConfirmEmail)
{
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
var callbackUrl = Url.Action("ConfirmEmailApp", "Account", new { userId = user.Id, code = code }, protocol: HttpContext.Request.Scheme);
await _emailSender.SendEmailConfirmationAsync(user.UserName, callbackUrl, _localizer, _licenseOptions);
}
return Ok();
}
else
{
if (createResult.Errors.FirstOrDefault(c => c.Code == "PwnedPassword") != null)
{
return BadRequest(CommunicationErrors.Register_Password_Pwned);
}
if (createResult.Errors.FirstOrDefault(c => c.Code.StartsWith("IdentityError_Password")) != null)
{
return BadRequest(CommunicationErrors.Register_Password_Rules);
}
}
return BadRequest(CommunicationErrors.Register_Failed);
}
return BadRequest(CommunicationErrors.Register_Email_Exists);
}
return BadRequest(CommunicationErrors.Common_Model_Invalid);
}
/// <summary> /// <summary>
/// Registrieren eines App-Users mittels external Provider /// Registrieren eines App-Users mittels external Provider
/// </summary> /// </summary>
@ -958,6 +1084,254 @@ namespace gehGassi.Web.Controllers.Api
return BadRequest(CommunicationErrors.Common_Model_Invalid); return BadRequest(CommunicationErrors.Common_Model_Invalid);
} }
/// <summary>
/// Registrieren eines App-Users mittels external Provider
/// </summary>
/// <param name="model">Model</param>
/// <returns>HTTP 200 OK wenn erfolgreich</returns>
[AllowAnonymous]
[HttpPost]
[MapToApiVersion(3)]
[Route("RegisterExternal")]
public async Task<IActionResult> RegisterExternalV3(RegisterExternalV2Dto model)
{
var clientOffset = GetClientDateOffset();
if (ModelState.IsValid)
{
//Zuerst holen wir nochmal die Infos für den User. Token prüfen... //TODO: Je nach Provider vorgehen. Derzeit nur google. Apple einführen...
var tokenValid = false;
ExternalLoginGoogleResponse userInfo = null;
AppleResponseDto appleResponse = null;
if (model.LoginProvider == "Google")
{
HttpStatusCode tokenResponseCode = HttpStatusCode.OK;
var httpClient = new HttpClient();
try
{
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", model.AccessToken);
var tokenResult = await httpClient.GetAsync($"https://www.googleapis.com/oauth2/v3/userinfo");
if (tokenResult.IsSuccessStatusCode)
{
string jsonString = tokenResult.Content.ReadAsStringAsync().Result;
userInfo = JsonSerializer.Deserialize<ExternalLoginGoogleResponse>(jsonString);
tokenValid = true;
}
else
{
tokenResponseCode = tokenResult.StatusCode;
}
}
catch (Exception ex)
{
tokenValid = false;
}
}
else if (model.LoginProvider == "Apple")
{
var clientSecret = await GenerateAppleClientSecretAsync();
var parameters = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("client_id", _appleOptions.Value.ClientId),
new KeyValuePair<string, string>("client_secret", clientSecret),
new KeyValuePair<string, string>("refresh_token", model.AccessToken),
new KeyValuePair<string, string>("grant_type", "refresh_token")
};
var httpClient = new HttpClient();
try
{
var tokenResult = await httpClient.PostAsync($"https://appleid.apple.com/auth/token", new FormUrlEncodedContent(parameters));
if (tokenResult.IsSuccessStatusCode)
{
var successResponse = await tokenResult.Content.ReadAsStringAsync();
appleResponse = JsonSerializer.Deserialize<AppleResponseDto>(successResponse);
tokenValid = true;
}
else
{
var errorResponse = await tokenResult.Content.ReadAsStringAsync();
_logger.LogError(errorResponse);
}
}
catch (Exception ex)
{
tokenValid = false;
}
}
if (tokenValid)
{
var user = await _userService.GetByUsernameAsync(model.UserName);
if (user == null)
{
user = _userService.Create();
user.Id = Guid.NewGuid().ToString();
user.UserName = model.UserName;
user.FirstName = model.FirstName;
user.LastName = model.LastName;
user.FullName = $"{model.FirstName} {model.LastName}";
user.RegistrationDate = DateTimeOffset.UtcNow;
user.Photo = string.Empty;
user.Email = model.UserName;
user.EmailConfirmed = true; //Weil über externen Provider
user.Permissions = PermissionHelper.GetForRole("AppUser").PackPermissionsIntoString();
IdentityResult createResult;
if (!string.IsNullOrEmpty(model.Password))
createResult = await _userManager.CreateAsync(user, model.Password);
else
createResult = await _userManager.CreateAsync(user);
if (createResult.Succeeded)
{
await _userManager.AddToRoleAsync(user, "AppUser");
user = await _userManager.FindByEmailAsync(user.UserName);
//Nun den AppUser anlegen und dann mit dem Benutzer verknüpfen
var appUser = AppUserService.Create();
appUser.Type = (AppUserType)model.AppUserType;
appUser.Number = AppUserService.GetNextNumber();
appUser.FirstName = model.FirstName;
appUser.LastName = model.LastName;
appUser.Sex = Sex.Undefined;
appUser.BirthDate = model.BirthDate;
appUser.Photo = string.Empty;
appUser.Contact.Email = model.UserName;
appUser.Address.City = model.City;
appUser.Address.Zip = model.Zip;
appUser.Address.CountryCode = model.CountryCode;
appUser.Address.State = model.State;
appUser.TermsAccepted = true;
appUser.TermsAcceptedDate = DateTimeOffset.UtcNow;
appUser.PrivacyAccepted = true;
appUser.PrivacyAcceptedDate = DateTimeOffset.UtcNow;
appUser.Verified = false;
appUser.VerifiedDate = null;
appUser.PaymentTermsAccepted = false;
appUser.PaymentTermsAcceptedDate = null;
appUser.NationalityCode = string.Empty;
appUser.MainResidenceCode = string.Empty;
if (appUser.Type == AppUserType.DogWalker)
{
appUser.PaymentTermsAccepted = false;
appUser.PaymentTermsAcceptedDate = null;
appUser.NationalityCode = model.NationalityCode;
appUser.MainResidenceCode = model.MainResidenceCountryCode;
}
var location = await _geoLocationService.GetLocationAsync($"{model.Zip},{model.City},{model.CountryCode},{model.State}", CultureInfo.CurrentCulture.TwoLetterISOLanguageName);
if (location.Success)
{
var geometryFactory = NtsGeometryServices.Instance.CreateGeometryFactory(srid: 4326);
var geoLocation = geometryFactory.CreatePoint(new Coordinate(location.Longitude, location.Latitude));
appUser.Location = geoLocation;
}
AppUserService.Add(appUser);
await AppUserService.CommitAsync("System");
user.AppUserId = appUser.Id;
await _userManager.UpdateAsync(user);
if (appUser.Type != AppUserType.DogOwner)
{
await AppUserService.CreateWalkerProfileIfNotExistsAsync(appUser.Id);
await AppUserService.CommitAsync("System");
//Nun für den Walker Mangopay User anlegen
//var mangoPayResult = await _mangoPayService.CreateOwnerAsync(appUser.Id, false);
//if (mangoPayResult.Success)
//{
// //Wallets anlegen
// var walletCreditsResult = await _mangoPayService.CreateWalletAsync(appUser.Id, WalletType.Credits);
// var walletFeesResult = await _mangoPayService.CreateWalletAsync(appUser.Id, WalletType.Fees);
//}
}
//Da wir den User gleich einloggen möchten, geben wir alles nötige zurück
var usersClaims = new List<Claim>()
{
new Claim(ClaimTypes.Name, user.UserName),
new Claim(ClaimTypes.GivenName, user.FirstName),
new Claim(ClaimTypes.Surname, user.LastName),
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
new Claim(ClaimTypes.UserData, user.Photo ?? ""),
new Claim(ClaimConstants.AppUserIdClaimType, user.AppUserId ?? ""),
};
var roles = await _userManager.GetRolesAsync(user);
foreach (var role in roles)
{
usersClaims.Add(new Claim(ClaimTypes.Role, role));
}
var token = _tokenService.GenerateAccessToken(usersClaims);
await _refreshTokenService.RemoveExpiredAsync(user.Id, user.UserName);
var refreshToken = _refreshTokenService.Create();
refreshToken.UserId = user.Id;
_refreshTokenService.Add(refreshToken);
await _refreshTokenService.CommitAsync(user.UserName);
var userDto = Mapper.Map<UserDto>(user);
userDto.AccessToken = token;
userDto.AccessTokenExpires = DateTimeOffset.UtcNow.AddMinutes(_jwtTokenOptions.Value.TokenMinutes);
userDto.RefreshToken = refreshToken.Token;
userDto.RefreshTokenExpires = refreshToken.Expires;
userDto.AppUserType = (AppUserTypeDto)appUser.Type;
var roleList = roles.ToList();
userDto.Roles = string.Join(";", roleList);
user.LastLoginDate = DateTimeOffset.UtcNow;
await _userManager.UpdateAsync(user);
if (model.LoginProvider == "Google" && userInfo != null)
{
var createdUser = await _userManager.FindByEmailAsync(user.UserName);
if (createdUser != null)
{
await _userManager.AddLoginAsync(createdUser, new UserLoginInfo("Google", userInfo.Sub, user.FullName));
}
}
else if (model.LoginProvider == "Apple" && appleResponse != null)
{
var createdUser = await _userManager.FindByEmailAsync(user.UserName);
if (createdUser != null)
{
var appleToken = new JwtSecurityToken(appleResponse.IdToken);
if (appleToken != null)
{
var appleUserId = appleToken.Claims.First(c => c.Type == "sub").Value;
await _userManager.AddLoginAsync(createdUser, new UserLoginInfo("Apple", appleUserId, user.FullName));
}
}
}
return Ok(userDto);
}
else
{
if (createResult.Errors.FirstOrDefault(c => c.Code == "PwnedPassword") != null)
{
return BadRequest(CommunicationErrors.Register_Password_Pwned);
}
if (createResult.Errors.FirstOrDefault(c => c.Code.StartsWith("Password")) != null)
{
return BadRequest(CommunicationErrors.Register_Password_Rules);
}
}
return BadRequest(CommunicationErrors.Register_Failed);
}
return BadRequest(CommunicationErrors.Register_Email_Exists);
}
return BadRequest(CommunicationErrors.Register_TokenInvalid);
}
return BadRequest(CommunicationErrors.Common_Model_Invalid);
}
/// <summary> /// <summary>
/// Anmelden via API. Erstellt ein Access-Token und ein Refresh-Token wenn erfolgreich /// Anmelden via API. Erstellt ein Access-Token und ein Refresh-Token wenn erfolgreich
/// </summary> /// </summary>
@ -2041,6 +2415,62 @@ namespace gehGassi.Web.Controllers.Api
return NotFound(CommunicationErrors.Common_NotFound); return NotFound(CommunicationErrors.Common_NotFound);
} }
/// <summary>
/// Setzt den Modus eines App-Users auf "Beides" - von Owner aufgerufen.
/// Erstellt keinen MangoPay User und legt keine Wallets an
/// </summary>
/// <returns>200 OK</returns>
[HttpPost]
[Route("SetAppUserTypeNoMango")]
public async Task<IActionResult> SetAppUserTypeNoMango(SetAppUserTypeExDto 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 appUser = await AppUserService.GetAsync(model.Id);
if (appUser != null)
{
if (model.UpdatedAt > appUser.UpdatedAt)
{
appUser.Type = (AppUserType)model.Type;
appUser.NationalityCode = model.NationalityCode;
appUser.MainResidenceCode = model.MainResidenceCode;
appUser.PaymentTermsAccepted = model.PaymentTermsAccepted;
appUser.PaymentTermsAcceptedDate = model.PaymentTermsAcceptedDate;
appUser.UpdatedAt = model.UpdatedAt;
await AppUserService.CommitAsync(User.Identity.Name);
if (appUser.Type != AppUserType.DogOwner)
{
await AppUserService.CreateWalkerProfileIfNotExistsAsync(appUser.Id);
await AppUserService.CommitAsync(User.Identity.Name);
}
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 NotFound(CommunicationErrors.Common_NotFound);
}
return BadRequest(CommunicationErrors.Common_Model_Invalid);
}
}
return NotFound(CommunicationErrors.Common_NotFound);
}
/// <summary> /// <summary>
/// Hinzufügen eines PaymentUsers zu einem AppUser /// Hinzufügen eines PaymentUsers zu einem AppUser
/// </summary> /// </summary>