[econ] key purchasing

This commit is contained in:
Devin Zuczek
2026-10-01 14:35:34 -04:00
parent b09f014b6d
commit 1458df1664
6 changed files with 1039 additions and 0 deletions
+102
View File
@@ -425,6 +425,68 @@ export const BuyInventionResponse = z.object({
.describe('The same envelope `POST /api/inventions/v6/save` returns'),
})
/**
* `GET /api/storefronts/v1/buyRoomKey` — the client's `RoomKeyPurchaseResponseDTO`, recovered
* from its decoder: the room-key envelope `/api/roomkeys/v1/create` answers in, beside a
* BalanceUpdateResponse whose bucket key is `Platform` (NOT the `BalanceType` the invention
* buys echo) and whose entry `Data` is the key again.
*
* Every enum here reads an OMITTED key as its 0 member — `Status` 0 is success,
* `UpdateResponse` 0 is OK, `Platform` 0 is SteamPurchased — so all three are always sent.
*/
export const BuyRoomKeyResponse = z.object({
RoomKeyResponse: z.object({
Status: z.int().describe('0 on success; 1 on a refusal'),
RoomKey: JsonObject.nullable().describe(
'The bought key (the 10-key room key); null on a refusal'
),
}),
BalanceUpdateResponse: z.object({
BalanceUpdates: z.array(
z.object({
UpdateResponse: z
.int()
.describe(
'0 OK, 2 NotEnoughCredit, 3 AlreadyOwned, 4 NoItemAvailable, ' +
'6 RequestedPriceDoesNotMatch'
),
Data: JsonObject.nullable().describe('The key the request named; null when it named none'),
})
),
Balance: z.int().describe('The buyer’s RESULTING token total, not the change'),
CurrencyType: z.int().describe('2 = RecCenterTokens'),
Platform: z.int().describe('-2 — the one account-wide bucket every balance surface names'),
}),
})
/** `POST /api/storefronts/v1/PurchaseRoomKeyWithCurrency` — form-encoded. */
export const PurchaseRoomKeyWithCurrencyRequest = z.object({
RoomKeyId: z.string().describe('The key to buy — a `room_key` id'),
RequestedPrice: z.string().describe('The price the client rendered; must equal the key’s'),
RequestedPurchaseCurrencyId: z
.string()
.describe('The room currency to pay in; must be the key’s own `PurchaseCurrencyId`'),
})
/**
* `POST /api/storefronts/v1/PurchaseRoomKeyWithCurrency` — the client's
* `RoomKeyPurchaseWithCurrencyResponseDTO`, recovered from its decoder. A BARE object, no
* envelope, and NOT buyRoomKey's shape: the outer `Balance` is an OBJECT (the 4-key
* room-currency balance) holding a numeric `Balance`, and there is no `Platform` anywhere.
*/
export const PurchaseRoomKeyWithCurrencyResponse = z.object({
Balance: z.object({
AccountId: z.int().describe('The buyer'),
CurrencyId: z.string().describe('The room currency paid in'),
Balance: z.int().describe('What the buyer holds of it afterwards — the RESULTING total'),
ModifiedAt: z.string().describe('ISO-8601 UTC'),
}),
RoomKeyResponse: z.object({
Status: z.int().describe('0 on success; 1 on a refusal. Always sent — omitted reads as 0'),
RoomKey: JsonObject.nullable().describe('The bought key; null on a refusal'),
}),
})
/**
* `POST /api/storefronts/v3/buyInvention` — the purchase result the 2025 client wants,
* which is NOT v2's despite settling the identical purchase. Two differences, both
@@ -974,6 +1036,46 @@ export const AwardRoomCurrencyResult = z.object({
export const AwardRoomCurrencyResultList = z.array(AwardRoomCurrencyResult)
/**
* `POST /api/roomCurrencies/v2/purchase` — form-encoded
* (`PurchaseOfferId=ffab4157-…&RequestedAmount=444&RequestedPrice=555`). The offer alone says
* which currency is being bought; the two numbers are what the client rendered, and must match
* the offer.
*/
export const PurchaseRoomCurrencyRequest = z.object({
PurchaseOfferId: z.string().describe('The offer — a `CurrencyPurchaseOfferId`'),
RequestedAmount: z.string().describe('How much room currency; must equal the offer’s'),
RequestedPrice: z.string().describe('What it costs in tokens; must equal the offer’s'),
})
/**
* What `POST /api/roomCurrencies/v2/purchase` answers: both balances the purchase moved,
* inside the `{ Value, Success, Error, error_id }` envelope the room-currency writes use.
* Each `Balance` is a RESULTING total. Only the success form has been observed; a refusal's
* null `Value` is an assumption.
*/
export const PurchaseRoomCurrencyEnvelope = z.object({
Value: z
.object({
CurrencyBalanceResponse: z.object({
AccountId: z.int().describe('The buyer'),
CurrencyId: z.string().describe('The room currency bought'),
Balance: z.int().describe('What the buyer now holds of it'),
ModifiedAt: z.string().describe('ISO-8601 UTC'),
}),
TokenBalanceResponse: z.object({
Balance: z.int().describe('The buyer’s resulting token total'),
CurrencyType: z.int().describe('2 = RecCenterTokens'),
Platform: z.int().describe('-2 — the one account-wide bucket every balance surface names'),
}),
})
.nullable()
.describe('Null on a refusal'),
Success: z.boolean(),
Error: z.string().nullable().describe('Null on success; the refusal otherwise'),
error_id: z.null().describe('Always null. Present as a key, and lowercase'),
})
/**
* `POST /api/roomcurrencies/v1/updateCurrency` — form-encoded. Names the currency by id
* rather than by room: the room it belongs to is whichever one minted it, and an edit cannot