mirror of
https://github.com/djdevin/recflare.git
synced 2026-10-25 15:09:18 -07:00
[econ] fix room owner key purchase
This commit is contained in:
@@ -6517,8 +6517,10 @@ const app = new Hono<App>({ strict: false })
|
|||||||
'Looks the key up by `RoomKeyId`, confirms the client’s `RequestedPrice` matches its',
|
'Looks the key up by `RoomKeyId`, confirms the client’s `RequestedPrice` matches its',
|
||||||
'stored `Price`, debits the buyer that price in RecCenterTokens, pays it to the',
|
'stored `Price`, debits the buyer that price in RecCenterTokens, pays it to the',
|
||||||
'room’s OWNER (its `CreatorAccountId`), and records the buyer as holding the key',
|
'room’s OWNER (its `CreatorAccountId`), and records the buyer as holding the key',
|
||||||
'(`room_key_player`). A free key, and an owner buying their own room’s key, move no',
|
'(`room_key_player`). A free key moves no tokens, and nor does a key bought by the',
|
||||||
'tokens. A GET because that is how the client sends it.',
|
'room’s creator or a CO-OWNER: they get their own room’s keys free, and the client',
|
||||||
|
'asks them `RequestedPrice=0` (the stored price is accepted too). A GET because that',
|
||||||
|
'is how the client sends it.',
|
||||||
'',
|
'',
|
||||||
'Answers the client’s `RoomKeyPurchaseResponseDTO`: `RoomKeyResponse` is the',
|
'Answers the client’s `RoomKeyPurchaseResponseDTO`: `RoomKeyResponse` is the',
|
||||||
'`{ Status, RoomKey }` the create answers in, and `BalanceUpdateResponse` carries one',
|
'`{ Status, RoomKey }` the create answers in, and `BalanceUpdateResponse` carries one',
|
||||||
@@ -6588,17 +6590,21 @@ const app = new Hono<App>({ strict: false })
|
|||||||
if (key === null || key.PurchaseCurrencyId !== null) {
|
if (key === null || key.PurchaseCurrencyId !== null) {
|
||||||
return answer(UpdateResponse.NoItemAvailable)
|
return answer(UpdateResponse.NoItemAvailable)
|
||||||
}
|
}
|
||||||
if (requestedPrice !== key.Price) return answer(UpdateResponse.RequestedPriceDoesNotMatch)
|
|
||||||
const ownerId = await getRoomOwnerId(c.env.DB, key.RoomId)
|
const ownerId = await getRoomOwnerId(c.env.DB, key.RoomId)
|
||||||
if (ownerId === null) return answer(UpdateResponse.NoItemAvailable)
|
if (ownerId === null) return answer(UpdateResponse.NoItemAvailable)
|
||||||
|
// The room's creator and co-owners get their own room's keys free — the client asks
|
||||||
|
// them `RequestedPrice=0` — and are never charged, whatever price they name.
|
||||||
|
const canManage = (await canManageRoomById(c.env.DB, key.RoomId, id)) === true
|
||||||
|
if (requestedPrice !== key.Price && !(canManage && requestedPrice === 0)) {
|
||||||
|
return answer(UpdateResponse.RequestedPriceDoesNotMatch)
|
||||||
|
}
|
||||||
|
|
||||||
// Claim the key BEFORE charging: the insert is atomic, so of two concurrent buys only
|
// Claim the key BEFORE charging: the insert is atomic, so of two concurrent buys only
|
||||||
// one gets past here and only one is charged. A buyer who cannot pay gives it back.
|
// one gets past here and only one is charged. A buyer who cannot pay gives it back.
|
||||||
if (!(await claimRoomKey(c.env.DB, id, key.RoomKeyId))) {
|
if (!(await claimRoomKey(c.env.DB, id, key.RoomKeyId))) {
|
||||||
return answer(UpdateResponse.AlreadyOwned)
|
return answer(UpdateResponse.AlreadyOwned)
|
||||||
}
|
}
|
||||||
// The owner paying themselves would be a debit and a credit of the same tokens.
|
if (key.Price > 0 && !canManage) {
|
||||||
if (key.Price > 0 && ownerId !== id) {
|
|
||||||
if (!(await spendCurrency(c.env.DB, id, currencyType, key.Price, startingTokens))) {
|
if (!(await spendCurrency(c.env.DB, id, currencyType, key.Price, startingTokens))) {
|
||||||
await releaseRoomKey(c.env.DB, id, key.RoomKeyId)
|
await releaseRoomKey(c.env.DB, id, key.RoomKeyId)
|
||||||
return answer(UpdateResponse.NotEnoughCredit)
|
return answer(UpdateResponse.NotEnoughCredit)
|
||||||
|
|||||||
@@ -3054,7 +3054,8 @@ describe('econ endpoints', () => {
|
|||||||
expect(await (await buy('7301', key.RoomKeyId, 300)).json()).toEqual(refused(3, key, 200))
|
expect(await (await buy('7301', key.RoomKeyId, 300)).json()).toEqual(refused(3, key, 200))
|
||||||
expect([await tokens(1), await tokens(7301)]).toEqual([1300, 200])
|
expect([await tokens(1), await tokens(7301)]).toEqual([1300, 200])
|
||||||
|
|
||||||
// A free key moves nothing; nor does the owner buying their own room's key.
|
// A free key moves nothing; nor does a room manager buying their own room's key —
|
||||||
|
// the owner at the stored price, or a co-owner at the 0 the client asks them.
|
||||||
const gratis = (await (await buy('7302', free.RoomKeyId)).json()) as {
|
const gratis = (await (await buy('7302', free.RoomKeyId)).json()) as {
|
||||||
RoomKeyResponse: { Status: number }
|
RoomKeyResponse: { Status: number }
|
||||||
BalanceUpdateResponse: { Balance: number }
|
BalanceUpdateResponse: { Balance: number }
|
||||||
@@ -3064,7 +3065,12 @@ describe('econ endpoints', () => {
|
|||||||
const own = (await (await buy('1', key.RoomKeyId, 300)).json()) as typeof gratis
|
const own = (await (await buy('1', key.RoomKeyId, 300)).json()) as typeof gratis
|
||||||
expect(own.RoomKeyResponse.Status).toBe(0)
|
expect(own.RoomKeyResponse.Status).toBe(0)
|
||||||
expect(own.BalanceUpdateResponse.Balance).toBe(1300)
|
expect(own.BalanceUpdateResponse.Balance).toBe(1300)
|
||||||
expect(await holders(key.RoomKeyId)).toEqual([1, 7301])
|
await setTokens(2, 50)
|
||||||
|
const coOwner = (await (await buy('2', key.RoomKeyId, 0)).json()) as typeof gratis
|
||||||
|
expect(coOwner.RoomKeyResponse.Status).toBe(0)
|
||||||
|
expect(coOwner.BalanceUpdateResponse.Balance).toBe(50)
|
||||||
|
expect(await holders(key.RoomKeyId)).toEqual([1, 2, 7301])
|
||||||
|
expect([await tokens(1), await tokens(2)]).toEqual([1300, 50])
|
||||||
expect(await drainFrames()).toEqual([])
|
expect(await drainFrames()).toEqual([])
|
||||||
|
|
||||||
// A key priced in a room currency is not sold for tokens.
|
// A key priced in a room currency is not sold for tokens.
|
||||||
|
|||||||
Reference in New Issue
Block a user