mirror of
https://github.com/djdevin/recflare.git
synced 2026-09-09 15:11:29 -07:00
support for 202507 endpoints (#37)
* [auth][api] accept the 20250424.01 client Version check now answers "current" for a set of builds rather than one: SUPPORTED_GAME_VERSIONS carries 20230414 and 20250424.01. GAME_VERSION is unchanged and still what the server reports for itself (presence, rn.ver). Adds GET /api/versioncheck/islandedversions, always [] — we never island a build off into its own matchmaking pool. The 2025 build POSTs /cachedlogin/forplatformid/:platform/:id with a deviceId/platformAuth/time form body where the 2023 build GETs it, so that route now takes both methods. The body is accepted and ignored for now. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * [2025] unstable * 20250718.0 * correct one this time * stubs * more stubs * more stubs * [lists] add worker * [ai] route stubs * [api] player photo setting * [econ] add roomEconConfig route * [infra] update worker generators * [worker] add cards/moderation/platformnotification workers * [lists] updates to some endpoints * [clubs] stub out announcement endpoint, for now * [econ] stub out season endpoints for now * [chat] apps/chat stub out party endpoint not sure the shape yet * [api] stub out statsig and lockeditems * [doc] new services * [lists] stub the bulk endpoint * [datacollection] add placeholder service until we can kill it * [api] set gifting to lvl5 * update lock * [cdn] enable cache * [match] matchmake v2 * [lists] stub some lists * [ai] stubs * [rooms] new subroom save endpoint * [econ] add bulk purchase endpoint * [discovery] update featured creator to 1 for fun * [api] add photo settings flag * [chat] fixup chat permissions (sorta) * [auth] restrictions endpoint * [rooms] contributed endpoint * [api] fix outfit endpoint * [discovery] attempt to fix store * [chat] privacy endpoints * [api] cheered images * [rooms] add xp endpoint (disbaled) * [rooms] add xp endpoint (disabled) * update images-db for cheers * [rooms] add autocomplete endpoint * [cdn/img] increase cache ttl for statics * [api] bulk route for images * [accounts] add banner image * [api] add misc missing endpoints * [discovery] remove AI tab * [platformnotifications] stub some endpoints * [lists] add some more lists * [rooms] additional endpoints * [chat] stub a few privacy endpoints * [econ] stub some endpoints * misc db fixes * [api] tweak shape for images v6 * [rooms] dont show trending RROs --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -42,6 +42,7 @@ missing/invalid). `~` = optional auth: served to anyone, personalised for a vali
|
||||
| GET | `/api/storefronts/v4/balance/:currencyType` | ✓ | Currency balance |
|
||||
| GET | `/api/storefronts/v3/giftdropstore/:id` | | Gift-drop storefront catalog |
|
||||
| POST | `/api/storefronts/v2/buyItem` | ✓ | Buy a storefront item |
|
||||
| POST | `/api/items/bulkpurchase` | ✓ | Buy a bag of storefront items |
|
||||
| GET | `/api/storefronts/v1/adcarouselitems` | | Ad-carousel items (static) |
|
||||
| GET | `/api/challenge/v2/getCurrent` | ~ | Weekly rotation + the caller's progress |
|
||||
| POST | `/api/challenge/v2/updateProgress` | ✓ | Report challenge progress |
|
||||
@@ -87,6 +88,85 @@ Two things are easy to get wrong:
|
||||
A `Gift` block routes the item (and box) to another player, but the caller always pays.
|
||||
A self-buy or anonymous gift is attributed to the "Coach" system account (id 1).
|
||||
|
||||
### The shopping bag (`POST /api/items/bulkpurchase`)
|
||||
|
||||
The same purchase, many items at once. The client posts every line in the bag — an item id,
|
||||
`DuplicateItemCount` copies, the unit `RequestedPrice` it rendered, an optional `Gift` — plus
|
||||
the one `StorefrontType` and one `CurrencyType` they all share.
|
||||
|
||||
**The response is not buyItem's.** It is `{ Success, Error, error_id, Value }` — `error_id`
|
||||
lowercase because the client renames that one member, the other three PascalCase — wrapping a
|
||||
BalanceUpdateResponse:
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"Success": true,
|
||||
"Error": null,
|
||||
"error_id": null,
|
||||
"Value": {
|
||||
"Balance": 9500, // the RESULTING total, not buyItem's change
|
||||
"CurrencyType": 2,
|
||||
"Platform": -2, // the bucket that total belongs to — a renamed BalanceType
|
||||
"BalanceUpdates": [
|
||||
// one entry per REQUESTED item, in request order
|
||||
{
|
||||
"UpdateResponse": 0,
|
||||
"Data": { "GiftPackage": { … }, "PurchasableItemId": 2182, "CustomAvatarItem": null },
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
What is deliberate here:
|
||||
|
||||
- **`Value.Balance` is the resulting total**, unlike buyItem's change — `{ Balance,
|
||||
CurrencyType, Platform }` is the BalanceResponse triple a `StorefrontBalance*` frame also
|
||||
carries, and the one frame this pushes reports the identical number, so body and frame
|
||||
agree instead of compounding.
|
||||
- **`Platform` is `-2`, not the capture's `4`.** It is the client's `BalanceType` under a
|
||||
`[DataMember]` rename — the bucket, not a store. The reference server said `RecNetPurchased`
|
||||
because it kept a wallet per store; this server keeps ONE account-wide bucket and the client
|
||||
SUMS its buckets, so naming any other platform invents a second balance beside the real one
|
||||
— the bug that showed a player 34,100 tokens.
|
||||
- **One entry per REQUESTED item, and `UpdateResponse` is where it reports.** A line that
|
||||
didn't sell comes back non-OK with a null `GiftPackage`, and `AllowPartialSuccess` is what
|
||||
lets those sit beside successful ones while `Success` stays true. Codes: 0 OK,
|
||||
1 TooManyRequests, 2 NotEnoughCredit, 3 AlreadyOwned, 4 NoItemAvailable,
|
||||
5 CouponNotApplicable, 6 RequestedPriceDoesNotMatch, 7 RequestedAmountNotAllowed,
|
||||
8 PlayerNotEligible, 9 RequestCannotBeRefunded, 10 PlayerNotApproved. This server can
|
||||
produce 0/2/4/5/6/7; the rest are rate limiting, entitlements and refunds, none of which
|
||||
exist here. `AlreadyOwned` is left unused on purpose — buyItem lets a player re-buy what
|
||||
they own, and one purchase path refusing what the other allows would be worse than either.
|
||||
- **One entry per item means one box per item.** So `DuplicateItemCount` above 1 is only
|
||||
allowed for a consumable (they stack, and the count rides on the one box);
|
||||
anything owned once is refused with `RequestedAmountNotAllowed` rather than charged twice
|
||||
for a grant that would collapse into itself.
|
||||
- **One catalog read and one debit for the whole bag.** Lines resolve in memory against a
|
||||
single `sf{N}.json` read (sf3 is 1161 items; the cap is 200 copies), and the total is spent
|
||||
in ONE atomic `spendCurrency`. Charging line by line would let a bag half-succeed against a
|
||||
concurrent spend and would push a balance frame per line.
|
||||
- **Nothing sold ⇒ a refusal, not an empty success.** `Success: false`, the first failure's
|
||||
reason in `Error`, and a null `Value` — legal here, since the client's validator only
|
||||
cascades into a non-null one (unlike the matchmaking DTO, which throws on a null payload).
|
||||
Without `AllowPartialSuccess`, one bad line refuses the bag the same way and nothing is
|
||||
charged. `400` is reserved for a request that can't be evaluated at all (no lines, a
|
||||
non-spendable currency, more than `Econ.BulkPurchaseCap` — the same 200 the client reads
|
||||
from its game config), and even those answer the envelope.
|
||||
- **`GiftPackage` is its own 20-key DTO**, not the stored box: buyItem's `Data` entry plus
|
||||
`PlayerId` / `CustomAvatarItemId` / `Signature` / `IsSignatureValid`, minus its `Level`.
|
||||
Its `Platform`/`PlatformsToSpawnOn` are the platform MASK (-1) — the balance bucket is the
|
||||
`BalanceType` beside them, which is the opposite arrangement from `Value.Platform` above.
|
||||
`Signature` is null and `IsSignatureValid` false: a box the server minted was never signed
|
||||
for peer-to-peer transfer.
|
||||
- **`BypassGiftPackages` skips the boxes, not the grant.** Ownership never depended on the
|
||||
box, so the items are granted either way; `GiftPackage` is then null — exactly what the
|
||||
captured response shows.
|
||||
- **Guid-keyed ids and coupons are refused.** `ItemPurchaseMethodId` is a discriminated id
|
||||
(`Type` 0 = a storefront `PurchasableItemId` in `NumberId`; a `Guid` names a UGC item no
|
||||
catalog here sells, and `Data.PurchasableItemId` is null for it), and nothing issues the
|
||||
coupon a `CouponConsumablePlayerMappingId` would spend.
|
||||
|
||||
## Query drops — the loot boxes (`IsQuery`)
|
||||
|
||||
A gift-drop with `IsQuery: true` is not an item, it is a **roll**: all of its item fields
|
||||
|
||||
Reference in New Issue
Block a user