mirror of
https://github.com/JogmansonReal/Radicade.git
synced 2026-10-25 23:18:56 -07:00
171 lines
6.5 KiB
C#
171 lines
6.5 KiB
C#
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using RadicadeServer.API.Notifications;
|
|
using RadicadeServer.DB;
|
|
using RadicadeServer.Extensions;
|
|
using RadicadeServer.RecNet.Enums.Messages;
|
|
using RadicadeServer.RecNet.Accounts;
|
|
using RadicadeServer.RecNet.Auth;
|
|
using RadicadeServer.RecNet.Enums.Store;
|
|
using RadicadeServer.RecNet.Notifications;
|
|
using RadicadeServer.RecNet.Responses;
|
|
using RadicadeServer.RecNet.Storefront;
|
|
|
|
namespace RadicadeServer.API.Storefront
|
|
{
|
|
[Route("api/storefronts/")]
|
|
[Route("econ/api/storefronts/")]
|
|
public class PurchaseAPI(RecDBContext _db) : Controller
|
|
{
|
|
[HttpPost("v1/buyItem"), Authorize]
|
|
public async Task<IActionResult> BuyItem([FromBody] RequestPurchaseItemDTO? request)
|
|
=> await ProcessPurchase(request);
|
|
|
|
[HttpPost("v1/buyTier"), Authorize]
|
|
public async Task<IActionResult> BuyTier([FromBody] RequestPurchaseItemDTO? request)
|
|
=> await ProcessPurchase(request);
|
|
|
|
[HttpPost("v1/buyElite"), Authorize]
|
|
public async Task<IActionResult> BuyElite([FromBody] RequestPurchaseItemDTO? request)
|
|
=> await ProcessPurchase(request);
|
|
|
|
[HttpGet("v2/buyInvention"), Authorize]
|
|
public async Task<IActionResult> BuyInvention([FromQuery] long inventionId, [FromQuery] long requestedPrice)
|
|
=> await ProcessInventionPurchase(inventionId, requestedPrice);
|
|
|
|
private async Task<IActionResult> ProcessPurchase(RequestPurchaseItemDTO? request)
|
|
{
|
|
var userId = User.GetUserId();
|
|
if (userId == null) return Unauthorized();
|
|
|
|
if (request == null)
|
|
{
|
|
return BadRequest(new { error = "invalid_request" });
|
|
}
|
|
|
|
var outcome = await StorefrontShared.ExecutePurchaseAsync(
|
|
_db,
|
|
userId.Value,
|
|
request.StorefrontType,
|
|
request.PurchasableItemId,
|
|
request.CurrencyType,
|
|
request.RequestedPrice,
|
|
request.Gift);
|
|
|
|
if (outcome.Response != null)
|
|
return Ok(outcome.Response);
|
|
return outcome.Error switch
|
|
{
|
|
"cannot_gift_self" or "invalid_recipient" => BadRequest(new { error = outcome.Error, message = outcome.ErrorDetail }),
|
|
_ => StatusCode(StatusCodes.Status500InternalServerError, new { error = outcome.Error, message = outcome.ErrorDetail })
|
|
};
|
|
}
|
|
|
|
private async Task<IActionResult> ProcessInventionPurchase(long inventionId, long requestedPrice)
|
|
{
|
|
var userId = User.GetUserId();
|
|
if (userId == null) return Unauthorized();
|
|
|
|
var invention = await _db.Inventions.FirstOrDefaultAsync(x => x.InventionId == inventionId);
|
|
if (invention == null) return NotFound();
|
|
|
|
var currency = await _db.Currency.FirstOrDefaultAsync(x => x.AccountId == userId);
|
|
if (currency == null) return NotFound();
|
|
|
|
var unlocks = await _db.Unlocks.FirstOrDefaultAsync(x => x.AccountId == userId);
|
|
if (unlocks == null)
|
|
{
|
|
unlocks = new PlayerUnlocks { AccountId = userId.Value };
|
|
await _db.Unlocks.AddAsync(unlocks);
|
|
}
|
|
|
|
var owned = ReadOwnedInventions(unlocks);
|
|
if (owned.Contains(inventionId)) return Ok(new Success(false, "Invention is already owned.", null));
|
|
|
|
var price = invention.Price ?? 0;
|
|
if (requestedPrice != price)
|
|
{
|
|
return Ok(new Success(false, "Invalid requested price.", null));
|
|
}
|
|
|
|
var (balance, delta, sufficient) = TryDeductCurrency(currency, CurrencyType.RecCenterTokens, price);
|
|
|
|
if (!sufficient)
|
|
{
|
|
return Ok(new Success(false, "You do not have enough tokens for this invention.", null));
|
|
}
|
|
|
|
owned.Add(inventionId);
|
|
unlocks.Inventions = owned.ToJson();
|
|
|
|
await _db.SaveChangesAsync();
|
|
|
|
var response = new InventionPurchaseResponseDTO.BalanceResponse
|
|
{
|
|
Balance = balance,
|
|
BalanceAddType = StorefrontBalanceAddTypes.CommercePurchase,
|
|
CurrencyType = CurrencyType.RecCenterTokens,
|
|
Delta = delta,
|
|
Result = new List<InventionPurchaseResponseDTO.BalanceResponse.InventionData>
|
|
{
|
|
new InventionPurchaseResponseDTO.BalanceResponse.InventionData{ Invention = invention.ToInvention(), Status = PurchaseStatus.OK }
|
|
}
|
|
};
|
|
var purchased = new InventionPurchaseResponseDTO
|
|
{
|
|
InventionResponse = invention.ToInvention(),
|
|
BalanceUpdateResponse = response
|
|
};
|
|
|
|
await Notify.SendNotify((long)userId, new Notification { Id = NotificationType.StorefrontBalancePurchase, Msg = purchased });
|
|
await Notify.SendNotify((long)userId, new Notification { Id = NotificationType.StorefrontBalanceUpdate, Msg = response });
|
|
|
|
var buyerProfile = await _db.Profiles.AsNoTracking().FirstOrDefaultAsync(x => x.accountId == userId);
|
|
if (buyerProfile != null)
|
|
await Notify.SendNotify((long)userId, new Notification { Id = NotificationType.RefreshLogin, Msg = buyerProfile });
|
|
|
|
return Ok(new Success(true, null, purchased));
|
|
}
|
|
|
|
private static List<long> ReadOwnedInventions(PlayerUnlocks unlocks)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(unlocks.Inventions) || unlocks.Inventions == "null")
|
|
return new List<long>();
|
|
|
|
try
|
|
{
|
|
return unlocks.Inventions.FromJson<List<long>>() ?? new List<long>();
|
|
}
|
|
catch
|
|
{
|
|
return new List<long>();
|
|
}
|
|
}
|
|
|
|
private static (long balance, long delta, bool sufficient) TryDeductCurrency(
|
|
RecNet.Accounts.Currency currency, CurrencyType type, long cost)
|
|
{ long balance = type switch
|
|
{
|
|
CurrencyType.RecCenterTokens => currency.RecTokens,
|
|
CurrencyType.LaserTagTickets => currency.LaserTagTickets,
|
|
CurrencyType.LostSkullsGold => currency.Gold,
|
|
CurrencyType.DraculaSilver => currency.DraculaSilver,
|
|
_ => 0
|
|
};
|
|
|
|
if (balance < cost) return (balance, 0, false);
|
|
|
|
switch (type)
|
|
{
|
|
case CurrencyType.RecCenterTokens: currency.RecTokens -= cost; break;
|
|
case CurrencyType.LaserTagTickets: currency.LaserTagTickets -= cost; break;
|
|
case CurrencyType.LostSkullsGold: currency.Gold -= cost; break;
|
|
case CurrencyType.DraculaSilver: currency.DraculaSilver -= cost; break;
|
|
}
|
|
|
|
return (balance - cost, cost, true);
|
|
}
|
|
}
|
|
}
|