Radicade Public Code

This commit is contained in:
JogmansonReal
2026-10-16 23:33:13 -07:00
commit 55175acdf6
832 changed files with 363261 additions and 0 deletions
+170
View File
@@ -0,0 +1,170 @@
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);
}
}
}