@gamecore-api/sdk 0.12.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +72 -17
- package/dist/client.d.ts +230 -20
- package/dist/index.js +51 -5
- package/dist/types.d.ts +378 -9
- package/package.json +1 -2
package/README.md
CHANGED
|
@@ -1,17 +1,29 @@
|
|
|
1
|
-
# @gamecore/sdk
|
|
1
|
+
# @gamecore-api/sdk
|
|
2
2
|
|
|
3
3
|
TypeScript SDK for GameCore API — zero external dependencies, browser-safe.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
npm install @gamecore/sdk
|
|
8
|
+
npm install @gamecore-api/sdk
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
+
## What's new in 0.14.0
|
|
12
|
+
|
|
13
|
+
- **BREAKING** `giftCards.purchase()` signature changed: first arg is now `amountRub` (was `amountUsd`). GiftCard payload fields renamed — `amount_usd` → `amount_rub`, added `currency`, `remainingBalance`, `expiresAt`. `denomination` is now optional (legacy)
|
|
14
|
+
- `cart.merge(items)` — guest → authed cart handoff; new response fields `quantity`, `addedAt`, `gameIcon`
|
|
15
|
+
- `auth.linkEmail(email, password)` — add email identity to an existing Telegram/VK account
|
|
16
|
+
- `profile.getConversations / getConversationMessages / submitCode / submitScreenshot` — in-profile support chat
|
|
17
|
+
- `profile.getPushPublicKey / subscribePush / unsubscribePush` — web push subscriptions
|
|
18
|
+
- `referrals.getPopularProducts(limit)` + `referrals.getPerformance({ from, to })` — affiliate analytics
|
|
19
|
+
- `site.requestGame({ gameName, contact })` — public "request a game" lead capture
|
|
20
|
+
- `site.getSitemapData()` — data source for `sitemap.xml`
|
|
21
|
+
- `checkout.completeWithBalance` now returns `{ newBalance }`
|
|
22
|
+
|
|
11
23
|
## Quick Start
|
|
12
24
|
|
|
13
25
|
```typescript
|
|
14
|
-
import { GameCoreClient } from "@gamecore/sdk";
|
|
26
|
+
import { GameCoreClient } from "@gamecore-api/sdk";
|
|
15
27
|
|
|
16
28
|
const gc = new GameCoreClient({
|
|
17
29
|
apiKey: "gc_live_YOUR_KEY",
|
|
@@ -50,6 +62,23 @@ console.log("Logged in:", user.firstName);
|
|
|
50
62
|
const { user } = await gc.auth.verifyVk(vkAccessToken);
|
|
51
63
|
```
|
|
52
64
|
|
|
65
|
+
### Email + Password
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
await gc.auth.register(email, password, firstName, ref);
|
|
69
|
+
await gc.auth.login(email, password);
|
|
70
|
+
await gc.auth.changePassword(currentPassword, newPassword);
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Link additional identity
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
// Add email to an existing Telegram/VK account
|
|
77
|
+
await gc.auth.linkEmail(email, password);
|
|
78
|
+
// Or link a VK access token
|
|
79
|
+
await gc.auth.linkVk(vkAccessToken);
|
|
80
|
+
```
|
|
81
|
+
|
|
53
82
|
### Session
|
|
54
83
|
|
|
55
84
|
```typescript
|
|
@@ -88,10 +117,14 @@ const { suggestions } = await gc.catalog.searchSuggestions("rob");
|
|
|
88
117
|
```typescript
|
|
89
118
|
// Cart
|
|
90
119
|
const items = await gc.cart.get();
|
|
91
|
-
|
|
120
|
+
// items: [{ id, productId, quantity, addedAt, gameIcon, ... }]
|
|
121
|
+
await gc.cart.add({ productId: 10, gameId: "roblox", gameName: "Roblox", productName: "800 Robux", price: 799, deliveryData: { username: "player123" }, quantity: 2 });
|
|
92
122
|
await gc.cart.remove(itemId);
|
|
93
123
|
await gc.cart.clear();
|
|
94
124
|
|
|
125
|
+
// Merge guest cart into authed session (on login)
|
|
126
|
+
await gc.cart.merge(guestItems);
|
|
127
|
+
|
|
95
128
|
// Checkout (auto-generates idempotency key)
|
|
96
129
|
const checkout = await gc.checkout.create({
|
|
97
130
|
items: [{ productId: 10, deliveryData: { username: "player123" } }],
|
|
@@ -144,6 +177,17 @@ const notifications = await gc.profile.getNotifications();
|
|
|
144
177
|
const { count } = await gc.profile.getUnreadCount();
|
|
145
178
|
await gc.profile.markRead(notificationId);
|
|
146
179
|
await gc.profile.markAllRead();
|
|
180
|
+
|
|
181
|
+
// Support conversations (in-profile chat)
|
|
182
|
+
const conversations = await gc.profile.getConversations();
|
|
183
|
+
const messages = await gc.profile.getConversationMessages(conversationId);
|
|
184
|
+
await gc.profile.submitCode(conversationId, requestId, "ABC-123");
|
|
185
|
+
await gc.profile.submitScreenshot(conversationId, requestId, file);
|
|
186
|
+
|
|
187
|
+
// Web push subscriptions
|
|
188
|
+
const { publicKey } = await gc.profile.getPushPublicKey();
|
|
189
|
+
await gc.profile.subscribePush({ endpoint, keys: { p256dh, auth } });
|
|
190
|
+
await gc.profile.unsubscribePush(endpoint);
|
|
147
191
|
```
|
|
148
192
|
|
|
149
193
|
## Favorites
|
|
@@ -185,8 +229,10 @@ await gc.coupons.remove();
|
|
|
185
229
|
const active = await gc.coupons.getActive();
|
|
186
230
|
|
|
187
231
|
// Gift cards
|
|
188
|
-
const card = await gc.giftCards.purchase(
|
|
232
|
+
const card = await gc.giftCards.purchase(500, "Happy birthday!"); // amountRub + optional message
|
|
189
233
|
await gc.giftCards.redeem("GC-XXXX-XXXX-XXXX");
|
|
234
|
+
const mine = await gc.giftCards.getMine();
|
|
235
|
+
// { code, amount_rub, currency, remainingBalance, expiresAt, ... }
|
|
190
236
|
```
|
|
191
237
|
|
|
192
238
|
## Referrals
|
|
@@ -198,6 +244,15 @@ const link = await gc.referrals.createLink({ label: "YouTube", slug: "my-channel
|
|
|
198
244
|
await gc.referrals.updateLink(link.id, { label: "Updated" });
|
|
199
245
|
const linkStats = await gc.referrals.getLinkStats(link.id);
|
|
200
246
|
const commissions = await gc.referrals.getCommissions();
|
|
247
|
+
|
|
248
|
+
// Popular products referred by this user
|
|
249
|
+
const popular = await gc.referrals.getPopularProducts(10);
|
|
250
|
+
|
|
251
|
+
// Performance over a date range
|
|
252
|
+
const perf = await gc.referrals.getPerformance({
|
|
253
|
+
from: "2026-04-01",
|
|
254
|
+
to: "2026-04-30",
|
|
255
|
+
});
|
|
201
256
|
```
|
|
202
257
|
|
|
203
258
|
## Balance Top-up
|
|
@@ -236,7 +291,7 @@ const schema = await gc.seo.getSchema("product", 42);
|
|
|
236
291
|
|
|
237
292
|
```typescript
|
|
238
293
|
// Import from server entrypoint (uses node:crypto)
|
|
239
|
-
import { verifyWebhookSignature, parseWebhookPayload } from "@gamecore/sdk/server";
|
|
294
|
+
import { verifyWebhookSignature, parseWebhookPayload } from "@gamecore-api/sdk/server";
|
|
240
295
|
|
|
241
296
|
const isValid = verifyWebhookSignature(
|
|
242
297
|
requestBody,
|
|
@@ -253,7 +308,7 @@ if (isValid) {
|
|
|
253
308
|
## Utilities
|
|
254
309
|
|
|
255
310
|
```typescript
|
|
256
|
-
import { convertPrice, formatPrice, generateIdempotencyKey } from "@gamecore/sdk";
|
|
311
|
+
import { convertPrice, formatPrice, generateIdempotencyKey } from "@gamecore-api/sdk";
|
|
257
312
|
|
|
258
313
|
const rub = convertPrice(1.99, 92.5); // 184.08
|
|
259
314
|
const formatted = formatPrice(rub, "RUB"); // "184 ₽"
|
|
@@ -266,7 +321,7 @@ const key = generateIdempotencyKey(); // UUID v4
|
|
|
266
321
|
|
|
267
322
|
```typescript
|
|
268
323
|
// app/catalog/page.tsx
|
|
269
|
-
import { GameCoreClient } from "@gamecore/sdk";
|
|
324
|
+
import { GameCoreClient } from "@gamecore-api/sdk";
|
|
270
325
|
|
|
271
326
|
const gc = new GameCoreClient({
|
|
272
327
|
apiKey: process.env.GAMECORE_API_KEY!,
|
|
@@ -297,7 +352,7 @@ export function CartWidget() {
|
|
|
297
352
|
|
|
298
353
|
```typescript
|
|
299
354
|
// app/api/webhooks/gamecore/route.ts
|
|
300
|
-
import { verifyWebhookSignature, parseWebhookPayload } from "@gamecore/sdk/server";
|
|
355
|
+
import { verifyWebhookSignature, parseWebhookPayload } from "@gamecore-api/sdk/server";
|
|
301
356
|
|
|
302
357
|
export async function POST(req: Request) {
|
|
303
358
|
const body = await req.text();
|
|
@@ -317,16 +372,16 @@ export async function POST(req: Request) {
|
|
|
317
372
|
|
|
318
373
|
| Namespace | Methods |
|
|
319
374
|
|-----------|---------|
|
|
320
|
-
| `gc.site` | getConfig, getRates, getLegal |
|
|
321
|
-
| `gc.auth` | initTelegram, pollTelegramStatus, verifyVk, getMe, logout, getIdentities, linkVk, unlinkProvider |
|
|
322
|
-
| `gc.catalog` | getGames, getHomepageGames, getGame, getProducts, getProductsGrouped, search, searchSuggestions, getProduct |
|
|
323
|
-
| `gc.cart` | get, add, sync, remove, clear |
|
|
375
|
+
| `gc.site` | getConfig, getRates, getLegal, getStats, getSocialProof, getThemeConfig, getTranslations, getUIConfig, getCookieConsent, getCatalogSections, getBanners, getAnnouncementBar, getSitemapData, requestGame |
|
|
376
|
+
| `gc.auth` | initTelegram, pollTelegramStatus, verifyMiniApp, verifyTelegramWidget, getVkAuthUrl, vkCallback, verifyVk, register, login, changePassword, getMe, logout, getIdentities, linkVk, linkEmail, unlinkProvider, mergePreview, mergeConfirm |
|
|
377
|
+
| `gc.catalog` | getGames, getHomepageGames, getGame, getRecommendations, getCategories, getProducts, getProductsGrouped, search, searchSuggestions, getProduct |
|
|
378
|
+
| `gc.cart` | get, add, merge, sync, remove, clear |
|
|
324
379
|
| `gc.checkout` | create, completeWithBalance, getPaymentMethods |
|
|
325
380
|
| `gc.orders` | list, get, getByPayment, cancel |
|
|
326
|
-
| `gc.profile` | getBalance, getLevelStatus, getTransactions, getOrders, getNotifications, getUnreadCount, markRead, markAllRead |
|
|
381
|
+
| `gc.profile` | getBalance, getLevelStatus, getTransactions, getOrders, getNotifications, getUnreadCount, markRead, markAllRead, getConversations, getConversationMessages, submitCode, submitScreenshot, getPushPublicKey, subscribePush, unsubscribePush |
|
|
327
382
|
| `gc.favorites` | list, add, remove |
|
|
328
383
|
| `gc.coupons` | apply, remove, validate, getActive |
|
|
329
|
-
| `gc.referrals` | getStats, getLinks, createLink, updateLink, deleteLink, getLinkStats, getCommissions |
|
|
384
|
+
| `gc.referrals` | getStats, getLinks, createLink, updateLink, deleteLink, getLinkStats, getCommissions, getPopularProducts, getPerformance |
|
|
330
385
|
| `gc.reviews` | listPublic, getStats, getRandom, getMine, getPending, create |
|
|
331
386
|
| `gc.topup` | getPaymentMethods, create, getStatus |
|
|
332
387
|
| `gc.giftCards` | purchase, redeem, check, getMine |
|
|
@@ -339,5 +394,5 @@ export async function POST(req: Request) {
|
|
|
339
394
|
|
|
340
395
|
| Import | Environment | Includes |
|
|
341
396
|
|--------|-------------|----------|
|
|
342
|
-
| `@gamecore/sdk` | Browser + Node | Client, types, utilities |
|
|
343
|
-
| `@gamecore/sdk/server` | Node only | Webhook verification (uses node:crypto) |
|
|
397
|
+
| `@gamecore-api/sdk` | Browser + Node | Client, types, utilities |
|
|
398
|
+
| `@gamecore-api/sdk/server` | Node only | Webhook verification (uses node:crypto) |
|
package/dist/client.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { TelegramInitResponse, TelegramWidgetUser, TelegramAuthResponse, User, SiteConfig, ExchangeRates, LegalDocument, Game, GameDetail, Product, SearchResult, Category, CartItem, CheckoutRequest, CheckoutResponse, Order, UserBalance, LevelStatus, Transaction, Notification, Favorite, Review, ReviewStats, CouponResult, ReferralStats, ReferralLink, ReferralCommission, TopupMethod, TopupResponse, TopupStatus, GiftCard, Announcement, AnnouncementBar, SiteUIConfig, PaymentMethod, CatalogSection, SiteStats, ProductFilters, PaginatedResponse, PagedGamesResponse } from "./types";
|
|
1
|
+
import type { TelegramInitResponse, TelegramWidgetUser, TelegramAuthResponse, User, SiteConfig, ExchangeRates, LegalDocument, Game, GameDetail, Product, SearchResult, Category, CartItem, CheckoutRequest, CheckoutResponse, Conversation, ConversationDetail, Order, PaymentInfo, CompleteWithBalanceResult, UserBalance, WebPushSubscriptionInput, LevelStatus, Transaction, Notification, Favorite, Review, ReviewCreateResult, ReviewStats, CouponResult, ReferralStats, ReferralLink, ReferralCommission, ReferralPerformance, TopupMethod, TopupResponse, TopupStatus, GiftCard, Announcement, AnnouncementBar, SiteUIConfig, PaymentMethod, CatalogSection, SiteStats, ProductFilters, PaginatedResponse, PagedGamesResponse } from "./types";
|
|
2
2
|
export interface GameCoreOptions {
|
|
3
3
|
/** Site API key (gc_live_xxx or gc_test_xxx) */
|
|
4
4
|
apiKey: string;
|
|
@@ -47,18 +47,59 @@ export declare class GameCoreClient {
|
|
|
47
47
|
}>;
|
|
48
48
|
/** Get per-site catalog sections (category tabs config) */
|
|
49
49
|
getCatalogSections: () => Promise<CatalogSection[]>;
|
|
50
|
-
/**
|
|
50
|
+
/**
|
|
51
|
+
* Get active hero banners for the storefront carousel.
|
|
52
|
+
* Each banner carries optional visual fields plus a `url`
|
|
53
|
+
* destination — `null` means the banner is display-only and
|
|
54
|
+
* should not be rendered as a link.
|
|
55
|
+
*/
|
|
51
56
|
getBanners: () => Promise<{
|
|
52
57
|
id: number;
|
|
53
58
|
title: string | null;
|
|
54
59
|
description: string | null;
|
|
55
60
|
imageUrl: string | null;
|
|
56
61
|
color: string | null;
|
|
62
|
+
url: string | null;
|
|
57
63
|
scope: string;
|
|
58
64
|
priority: number;
|
|
59
65
|
}[]>;
|
|
60
66
|
/** Get announcement bar settings (text, link, enabled) */
|
|
61
67
|
getAnnouncementBar: () => Promise<AnnouncementBar>;
|
|
68
|
+
/**
|
|
69
|
+
* Get sitemap data for self-generated storefront sitemaps.
|
|
70
|
+
* Returns `{ slug, updatedAt }` for every game visible on
|
|
71
|
+
* this site. Use `updatedAt` as `lastModified` in Next.js
|
|
72
|
+
* `sitemap.ts` so Google's crawler respects unchanged pages
|
|
73
|
+
* instead of stamping the whole catalog with `new Date()`.
|
|
74
|
+
*/
|
|
75
|
+
getSitemapData: () => Promise<{
|
|
76
|
+
slug: string;
|
|
77
|
+
updatedAt: string;
|
|
78
|
+
}[]>;
|
|
79
|
+
/**
|
|
80
|
+
* Submit a "please add this game" request. Works for both
|
|
81
|
+
* authenticated users (the server attaches the user id so
|
|
82
|
+
* support can reach back) and anonymous visitors.
|
|
83
|
+
*
|
|
84
|
+
* Rate-limited server-side. The optional `website` field is
|
|
85
|
+
* a honeypot — leave it `undefined` in real code; the
|
|
86
|
+
* storefront can render an invisible input under that name
|
|
87
|
+
* so scrapers self-identify.
|
|
88
|
+
*
|
|
89
|
+
* @returns The new request id and a success flag. A filled
|
|
90
|
+
* honeypot still returns `success: true` with
|
|
91
|
+
* `requestId: -1` so bots cannot distinguish a real save
|
|
92
|
+
* from a silent drop.
|
|
93
|
+
*/
|
|
94
|
+
requestGame: (data: {
|
|
95
|
+
gameName: string;
|
|
96
|
+
comment?: string;
|
|
97
|
+
gameUrl?: string;
|
|
98
|
+
website?: string;
|
|
99
|
+
}) => Promise<{
|
|
100
|
+
success: boolean;
|
|
101
|
+
requestId: number;
|
|
102
|
+
}>;
|
|
62
103
|
};
|
|
63
104
|
auth: {
|
|
64
105
|
/** Start Telegram auth flow → returns bot link for user to click */
|
|
@@ -155,6 +196,22 @@ export declare class GameCoreClient {
|
|
|
155
196
|
}>;
|
|
156
197
|
/** Link VK account to current user */
|
|
157
198
|
linkVk: (accessToken: string) => Promise<void>;
|
|
199
|
+
/**
|
|
200
|
+
* Attach an email + password identity to the currently
|
|
201
|
+
* authenticated user. Use this for Telegram-only / VK-only
|
|
202
|
+
* accounts that want a backup login method. The user must
|
|
203
|
+
* not already have an email set — update flows go through
|
|
204
|
+
* `changePassword()` or a future `changeEmail()` method.
|
|
205
|
+
*
|
|
206
|
+
* Rate-limited to the same bucket as `/auth/register`.
|
|
207
|
+
* Returns the normalised email on success; throws 409 if the
|
|
208
|
+
* user already has an email or if another account on this
|
|
209
|
+
* site owns the address.
|
|
210
|
+
*/
|
|
211
|
+
linkEmail: (email: string, password: string) => Promise<{
|
|
212
|
+
success: boolean;
|
|
213
|
+
email: string;
|
|
214
|
+
}>;
|
|
158
215
|
/** Unlink auth provider */
|
|
159
216
|
unlinkProvider: (provider: string) => Promise<void>;
|
|
160
217
|
/** Preview VK account merge (before confirming) */
|
|
@@ -196,7 +253,22 @@ export declare class GameCoreClient {
|
|
|
196
253
|
getHomepageGames: () => Promise<Game[]>;
|
|
197
254
|
/** Get single game with categories */
|
|
198
255
|
getGame: (slug: string, locale?: string) => Promise<GameDetail>;
|
|
199
|
-
/**
|
|
256
|
+
/**
|
|
257
|
+
* Get recommended games for a game detail page
|
|
258
|
+
* ("you might also like" / "related games").
|
|
259
|
+
*
|
|
260
|
+
* Returns games of the same `type` as the source game, randomly
|
|
261
|
+
* selected and filtered by per-site visibility overrides.
|
|
262
|
+
*
|
|
263
|
+
* @param gameSlug - Source game slug. Must be the **canonical**
|
|
264
|
+
* slug — this endpoint does NOT resolve aliases and returns an
|
|
265
|
+
* empty array for alias inputs. If you only have an alias,
|
|
266
|
+
* call `getGame()` first and use
|
|
267
|
+
* `result.canonicalSlug ?? result.slug`.
|
|
268
|
+
* @param limit - Max recommendations (default 8). The backend
|
|
269
|
+
* caps the effective limit; very large values are trimmed
|
|
270
|
+
* server-side.
|
|
271
|
+
*/
|
|
200
272
|
getRecommendations: (gameSlug: string, limit?: number) => Promise<Game[]>;
|
|
201
273
|
/** Get categories for a game with product counts and delivery metadata */
|
|
202
274
|
getCategories: (gameSlug: string) => Promise<Category[]>;
|
|
@@ -229,17 +301,22 @@ export declare class GameCoreClient {
|
|
|
229
301
|
minItems: number;
|
|
230
302
|
discountPercent: number;
|
|
231
303
|
}[]>;
|
|
232
|
-
/**
|
|
304
|
+
/**
|
|
305
|
+
* Get active promo campaigns. `targetUrl` is the clickable
|
|
306
|
+
* destination for the banner artwork (nullable — null means
|
|
307
|
+
* display-only).
|
|
308
|
+
*/
|
|
233
309
|
getPromos: () => Promise<{
|
|
234
310
|
id: number;
|
|
235
311
|
name: string;
|
|
236
312
|
type: string;
|
|
237
313
|
value: number;
|
|
238
314
|
scope: string;
|
|
239
|
-
bannerTitle?: string;
|
|
240
|
-
bannerDescription?: string;
|
|
241
|
-
bannerImageUrl?: string;
|
|
242
|
-
bannerColor?: string;
|
|
315
|
+
bannerTitle?: string | null;
|
|
316
|
+
bannerDescription?: string | null;
|
|
317
|
+
bannerImageUrl?: string | null;
|
|
318
|
+
bannerColor?: string | null;
|
|
319
|
+
targetUrl?: string | null;
|
|
243
320
|
endsAt?: string;
|
|
244
321
|
}[]>;
|
|
245
322
|
/** Get all games with full metadata (categories, delivery types) */
|
|
@@ -254,12 +331,35 @@ export declare class GameCoreClient {
|
|
|
254
331
|
}>;
|
|
255
332
|
};
|
|
256
333
|
cart: {
|
|
257
|
-
/**
|
|
334
|
+
/**
|
|
335
|
+
* Get cart items for the authenticated user. Each row comes
|
|
336
|
+
* back with `quantity`, `addedAt`, and `gameIcon` /
|
|
337
|
+
* `productIcon` populated by the server (2 extra lookups for
|
|
338
|
+
* the whole cart, not per row).
|
|
339
|
+
*/
|
|
258
340
|
get: () => Promise<CartItem[]>;
|
|
259
|
-
/**
|
|
341
|
+
/**
|
|
342
|
+
* Add a single item to the cart. `quantity` is optional and
|
|
343
|
+
* defaults to `1`. The server caps per-row quantity at 999.
|
|
344
|
+
*/
|
|
260
345
|
add: (item: Omit<CartItem, "id">) => Promise<CartItem>;
|
|
261
|
-
/**
|
|
346
|
+
/**
|
|
347
|
+
* Replace the server cart with the given list. Kept for
|
|
348
|
+
* backwards compatibility with SDK <= 0.12 and for the
|
|
349
|
+
* "clear everything and insert fresh" use case. Prefer
|
|
350
|
+
* `cart.merge()` when handling the guest → authed handoff so
|
|
351
|
+
* an existing server cart is not wiped.
|
|
352
|
+
*/
|
|
262
353
|
sync: (items: Omit<CartItem, "id">[]) => Promise<CartItem[]>;
|
|
354
|
+
/**
|
|
355
|
+
* Merge the given items into the server cart without wiping
|
|
356
|
+
* it. Items colliding on `(productId, deliveryData)` add
|
|
357
|
+
* their quantities (capped at 999 per row); new rows are
|
|
358
|
+
* inserted. Use this when a guest with a localStorage cart
|
|
359
|
+
* logs in and we want to keep both the old server rows and
|
|
360
|
+
* the newly collected ones.
|
|
361
|
+
*/
|
|
362
|
+
merge: (items: Omit<CartItem, "id">[]) => Promise<CartItem[]>;
|
|
263
363
|
/** Remove item from cart by ID */
|
|
264
364
|
remove: (id: number) => Promise<void>;
|
|
265
365
|
/** Clear all cart items */
|
|
@@ -275,10 +375,20 @@ export declare class GameCoreClient {
|
|
|
275
375
|
* No Telegram/VK login required. Balance payments still require auth.
|
|
276
376
|
*/
|
|
277
377
|
create: (data: CheckoutRequest, idempotencyKey?: string) => Promise<CheckoutResponse>;
|
|
278
|
-
/**
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
378
|
+
/**
|
|
379
|
+
* Complete a pending payment by deducting the amount from the
|
|
380
|
+
* user's balance (bonus first, then permanent). Returns the
|
|
381
|
+
* deduction breakdown and the post-deduction balance so the
|
|
382
|
+
* success page can render the receipt without a second
|
|
383
|
+
* `profile.getBalance()` fetch (which is race-prone right
|
|
384
|
+
* after a completion).
|
|
385
|
+
*
|
|
386
|
+
* Idempotent replay: a second call for an already-completed
|
|
387
|
+
* payment still succeeds and returns the current `newBalance`,
|
|
388
|
+
* but the deduction breakdown is absent because the original
|
|
389
|
+
* move is not re-derivable from persisted state.
|
|
390
|
+
*/
|
|
391
|
+
completeWithBalance: (paymentCode: string) => Promise<CompleteWithBalanceResult>;
|
|
282
392
|
/** Get available payment methods for checkout */
|
|
283
393
|
getPaymentMethods: () => Promise<PaymentMethod[]>;
|
|
284
394
|
};
|
|
@@ -298,7 +408,7 @@ export declare class GameCoreClient {
|
|
|
298
408
|
* the endpoint does not leak existence of valid codes).
|
|
299
409
|
*/
|
|
300
410
|
getByPayment: (paymentCode: string, guestEmail?: string) => Promise<{
|
|
301
|
-
payment:
|
|
411
|
+
payment: PaymentInfo;
|
|
302
412
|
orders: Order[];
|
|
303
413
|
}>;
|
|
304
414
|
/** Preview cancel — check if cancellable and refund amount */
|
|
@@ -360,6 +470,57 @@ export declare class GameCoreClient {
|
|
|
360
470
|
getBroadcastStatus: () => Promise<{
|
|
361
471
|
subscribed: boolean;
|
|
362
472
|
}>;
|
|
473
|
+
/** List the current user's conversations (most-recent first). */
|
|
474
|
+
getConversations: () => Promise<Conversation[]>;
|
|
475
|
+
/**
|
|
476
|
+
* Fetch a conversation header plus its messages. Marks any
|
|
477
|
+
* unread admin/system messages as read as a side effect.
|
|
478
|
+
*/
|
|
479
|
+
getConversationMessages: (conversationId: number, params?: {
|
|
480
|
+
limit?: number;
|
|
481
|
+
offset?: number;
|
|
482
|
+
}) => Promise<ConversationDetail>;
|
|
483
|
+
/**
|
|
484
|
+
* Submit a code to fulfill an open "code requested" request.
|
|
485
|
+
* Rate-limited to 5 submissions/minute per IP.
|
|
486
|
+
*/
|
|
487
|
+
submitCode: (conversationId: number, requestId: number, code: string) => Promise<{
|
|
488
|
+
success: boolean;
|
|
489
|
+
data?: {
|
|
490
|
+
requestId: number;
|
|
491
|
+
};
|
|
492
|
+
}>;
|
|
493
|
+
/**
|
|
494
|
+
* Submit a screenshot file to fulfill an open "screenshot
|
|
495
|
+
* requested" request. Accepts a browser `File` object and
|
|
496
|
+
* delivers it via multipart upload. Rate-limited to 3
|
|
497
|
+
* submissions/minute per IP.
|
|
498
|
+
*/
|
|
499
|
+
submitScreenshot: (conversationId: number, requestId: number, file: File) => Promise<{
|
|
500
|
+
success: boolean;
|
|
501
|
+
data?: {
|
|
502
|
+
requestId: number;
|
|
503
|
+
};
|
|
504
|
+
}>;
|
|
505
|
+
/** Get the VAPID public key needed to create a PushSubscription. */
|
|
506
|
+
getPushPublicKey: () => Promise<{
|
|
507
|
+
publicKey: string;
|
|
508
|
+
}>;
|
|
509
|
+
/**
|
|
510
|
+
* Register a push subscription for the current user + site.
|
|
511
|
+
* Call `PushSubscription.toJSON()` in the browser and pass the
|
|
512
|
+
* result directly.
|
|
513
|
+
*/
|
|
514
|
+
subscribePush: (subscription: WebPushSubscriptionInput) => Promise<{
|
|
515
|
+
success: boolean;
|
|
516
|
+
}>;
|
|
517
|
+
/**
|
|
518
|
+
* Remove a push subscription by its endpoint. Call this when
|
|
519
|
+
* the user opts out or changes devices.
|
|
520
|
+
*/
|
|
521
|
+
unsubscribePush: (endpoint: string) => Promise<{
|
|
522
|
+
success: boolean;
|
|
523
|
+
}>;
|
|
363
524
|
};
|
|
364
525
|
favorites: {
|
|
365
526
|
/** List user's favorite products */
|
|
@@ -408,6 +569,38 @@ export declare class GameCoreClient {
|
|
|
408
569
|
}>;
|
|
409
570
|
/** Get commission history */
|
|
410
571
|
getCommissions: () => Promise<ReferralCommission[]>;
|
|
572
|
+
/**
|
|
573
|
+
* Get a small grid of popular products so referrers can
|
|
574
|
+
* create a one-click reflink without hunting through the
|
|
575
|
+
* catalog. Sorted primarily by referral transaction count on
|
|
576
|
+
* the current site — i.e. "games that other referrers are
|
|
577
|
+
* actually earning commission on". Falls back to the homepage
|
|
578
|
+
* ranking on brand-new sites that have no referral history
|
|
579
|
+
* yet, so the grid is never empty for a new referrer.
|
|
580
|
+
*
|
|
581
|
+
* @param limit - Max number of entries to return (default 10,
|
|
582
|
+
* capped at 50 server-side).
|
|
583
|
+
*/
|
|
584
|
+
getPopularProducts: (limit?: number) => Promise<{
|
|
585
|
+
slug: string;
|
|
586
|
+
name: string;
|
|
587
|
+
icon: string | null;
|
|
588
|
+
}[]>;
|
|
589
|
+
/**
|
|
590
|
+
* Get performance stats aggregated over an optional date range.
|
|
591
|
+
*
|
|
592
|
+
* Pass `from` / `to` as Date or ISO-8601 string to filter
|
|
593
|
+
* commissions, transactions, and new-member counts. Clicks
|
|
594
|
+
* remain lifetime — see `ReferralPerformance` docstring for
|
|
595
|
+
* the rationale. Omit both to get all-time totals.
|
|
596
|
+
*
|
|
597
|
+
* Typical usage: wire to 7d / 30d / 90d / all buttons on the
|
|
598
|
+
* profile referrals dashboard.
|
|
599
|
+
*/
|
|
600
|
+
getPerformance: (params?: {
|
|
601
|
+
from?: Date | string;
|
|
602
|
+
to?: Date | string;
|
|
603
|
+
}) => Promise<ReferralPerformance>;
|
|
411
604
|
};
|
|
412
605
|
reviews: {
|
|
413
606
|
/** Get public reviews (paginated) */
|
|
@@ -425,8 +618,14 @@ export declare class GameCoreClient {
|
|
|
425
618
|
getMine: () => Promise<Review[]>;
|
|
426
619
|
/** Get orders eligible for review */
|
|
427
620
|
getPending: () => Promise<Order[]>;
|
|
428
|
-
/**
|
|
429
|
-
|
|
621
|
+
/**
|
|
622
|
+
* Submit a review for a completed order. Returns a narrow
|
|
623
|
+
* {@link ReviewCreateResult} with the new id, rating, text echo,
|
|
624
|
+
* and optional bonus grant. To fetch the full review record
|
|
625
|
+
* (including `createdAt`, `authorName`, `adminReply`, etc.),
|
|
626
|
+
* call `reviews.getMine()` after the create completes.
|
|
627
|
+
*/
|
|
628
|
+
create: (orderId: number, rating: number, text?: string) => Promise<ReviewCreateResult>;
|
|
430
629
|
};
|
|
431
630
|
topup: {
|
|
432
631
|
/** Get available topup payment methods */
|
|
@@ -437,8 +636,19 @@ export declare class GameCoreClient {
|
|
|
437
636
|
getStatus: (code: string) => Promise<TopupStatus>;
|
|
438
637
|
};
|
|
439
638
|
giftCards: {
|
|
440
|
-
/**
|
|
441
|
-
|
|
639
|
+
/**
|
|
640
|
+
* Purchase a gift card. The amount is in RUB (despite the
|
|
641
|
+
* legacy SDK parameter name — the column was misnamed and has
|
|
642
|
+
* always held RUB values, see Wave 3 audit tasks/112a). The
|
|
643
|
+
* method accepts either a plain number for backwards
|
|
644
|
+
* compatibility or an options object with an explicit message.
|
|
645
|
+
*
|
|
646
|
+
* @param amountRub - RUB amount to deduct from the user's
|
|
647
|
+
* balance and encode into the gift card.
|
|
648
|
+
* @param message - Optional personal note shown to the
|
|
649
|
+
* redeeming user.
|
|
650
|
+
*/
|
|
651
|
+
purchase: (amountRub: number, message?: string) => Promise<GiftCard>;
|
|
442
652
|
/** Redeem a gift card code */
|
|
443
653
|
redeem: (code: string) => Promise<{
|
|
444
654
|
amount: number;
|
package/dist/index.js
CHANGED
|
@@ -84,7 +84,9 @@ class GameCoreClient {
|
|
|
84
84
|
getCookieConsent: () => this.request("GET", "/site/cookie-consent"),
|
|
85
85
|
getCatalogSections: () => this.request("GET", "/site/catalog-sections"),
|
|
86
86
|
getBanners: () => this.request("GET", "/site/banners"),
|
|
87
|
-
getAnnouncementBar: () => this.request("GET", "/site/announcement-bar")
|
|
87
|
+
getAnnouncementBar: () => this.request("GET", "/site/announcement-bar"),
|
|
88
|
+
getSitemapData: () => this.request("GET", "/seo/sitemap-data"),
|
|
89
|
+
requestGame: (data) => this.request("POST", "/site/request-game", data)
|
|
88
90
|
};
|
|
89
91
|
auth = {
|
|
90
92
|
initTelegram: () => this.request("POST", "/auth/telegram/init"),
|
|
@@ -123,6 +125,7 @@ class GameCoreClient {
|
|
|
123
125
|
logout: () => this.request("POST", "/auth/logout"),
|
|
124
126
|
getIdentities: () => this.request("GET", "/auth/identities", undefined, { rawResponse: true }),
|
|
125
127
|
linkVk: (accessToken) => this.request("POST", "/auth/link/vk", { accessToken }),
|
|
128
|
+
linkEmail: (email, password) => this.request("POST", "/auth/link-email", { email, password }),
|
|
126
129
|
unlinkProvider: (provider) => this.request("POST", `/auth/unlink/${provider}`),
|
|
127
130
|
mergePreview: (accessToken) => this.request("POST", "/auth/merge/preview", { accessToken }),
|
|
128
131
|
mergeConfirm: (accessToken) => this.request("POST", "/auth/merge/confirm", { accessToken })
|
|
@@ -191,6 +194,7 @@ class GameCoreClient {
|
|
|
191
194
|
get: () => this.request("GET", "/cart"),
|
|
192
195
|
add: (item) => this.request("POST", "/cart", item),
|
|
193
196
|
sync: (items) => this.request("POST", "/cart/sync", { items }),
|
|
197
|
+
merge: (items) => this.request("POST", "/cart/merge", { items }),
|
|
194
198
|
remove: (id) => this.request("DELETE", `/cart/${id}`),
|
|
195
199
|
clear: () => this.request("DELETE", "/cart")
|
|
196
200
|
};
|
|
@@ -237,7 +241,27 @@ class GameCoreClient {
|
|
|
237
241
|
updateSettings: (data) => this.request("PUT", "/profile/settings", data),
|
|
238
242
|
subscribeBroadcast: () => this.request("POST", "/profile/broadcast/subscribe"),
|
|
239
243
|
unsubscribeBroadcast: () => this.request("POST", "/profile/broadcast/unsubscribe"),
|
|
240
|
-
getBroadcastStatus: () => this.request("GET", "/profile/broadcast/status")
|
|
244
|
+
getBroadcastStatus: () => this.request("GET", "/profile/broadcast/status"),
|
|
245
|
+
getConversations: () => this.request("GET", "/profile/conversations"),
|
|
246
|
+
getConversationMessages: (conversationId, params) => {
|
|
247
|
+
const qs = new URLSearchParams;
|
|
248
|
+
if (params?.limit)
|
|
249
|
+
qs.set("limit", String(params.limit));
|
|
250
|
+
if (params?.offset)
|
|
251
|
+
qs.set("offset", String(params.offset));
|
|
252
|
+
const q = qs.toString();
|
|
253
|
+
return this.request("GET", `/profile/conversations/${conversationId}/messages${q ? `?${q}` : ""}`);
|
|
254
|
+
},
|
|
255
|
+
submitCode: (conversationId, requestId, code) => this.request("POST", `/profile/conversations/${conversationId}/submit-code`, { requestId, code }),
|
|
256
|
+
submitScreenshot: (conversationId, requestId, file) => {
|
|
257
|
+
const form = new FormData;
|
|
258
|
+
form.append("requestId", String(requestId));
|
|
259
|
+
form.append("file", file);
|
|
260
|
+
return this.request("POST", `/profile/conversations/${conversationId}/submit-screenshot`, form);
|
|
261
|
+
},
|
|
262
|
+
getPushPublicKey: () => this.request("GET", "/profile/push/public-key"),
|
|
263
|
+
subscribePush: (subscription) => this.request("POST", "/profile/push/subscribe", subscription),
|
|
264
|
+
unsubscribePush: (endpoint) => this.request("POST", "/profile/push/unsubscribe", { endpoint })
|
|
241
265
|
};
|
|
242
266
|
favorites = {
|
|
243
267
|
list: () => this.request("GET", "/favorites"),
|
|
@@ -257,7 +281,22 @@ class GameCoreClient {
|
|
|
257
281
|
updateLink: (id, data) => this.request("PUT", `/referral/links/${id}`, data),
|
|
258
282
|
deleteLink: (id) => this.request("DELETE", `/referral/links/${id}`),
|
|
259
283
|
getLinkStats: (id) => this.request("GET", `/referral/links/${id}/stats`),
|
|
260
|
-
getCommissions: () => this.request("GET", "/referral/commissions")
|
|
284
|
+
getCommissions: () => this.request("GET", "/referral/commissions"),
|
|
285
|
+
getPopularProducts: (limit) => {
|
|
286
|
+
const qs = limit ? `?limit=${limit}` : "";
|
|
287
|
+
return this.request("GET", `/referral/popular-products${qs}`);
|
|
288
|
+
},
|
|
289
|
+
getPerformance: (params) => {
|
|
290
|
+
const qs = new URLSearchParams;
|
|
291
|
+
if (params?.from) {
|
|
292
|
+
qs.set("from", params.from instanceof Date ? params.from.toISOString() : params.from);
|
|
293
|
+
}
|
|
294
|
+
if (params?.to) {
|
|
295
|
+
qs.set("to", params.to instanceof Date ? params.to.toISOString() : params.to);
|
|
296
|
+
}
|
|
297
|
+
const q = qs.toString();
|
|
298
|
+
return this.request("GET", `/referral/performance${q ? `?${q}` : ""}`);
|
|
299
|
+
}
|
|
261
300
|
};
|
|
262
301
|
reviews = {
|
|
263
302
|
listPublic: (params) => {
|
|
@@ -280,7 +319,11 @@ class GameCoreClient {
|
|
|
280
319
|
getRandom: (limit = 5) => this.request("GET", `/reviews/public/random?limit=${limit}`),
|
|
281
320
|
getMine: () => this.request("GET", "/reviews/mine"),
|
|
282
321
|
getPending: () => this.request("GET", "/reviews/pending"),
|
|
283
|
-
create: (orderId, rating, text) => this.request("POST", "/reviews", {
|
|
322
|
+
create: (orderId, rating, text) => this.request("POST", "/reviews", {
|
|
323
|
+
orderId,
|
|
324
|
+
rating,
|
|
325
|
+
text
|
|
326
|
+
})
|
|
284
327
|
};
|
|
285
328
|
topup = {
|
|
286
329
|
getPaymentMethods: () => this.request("GET", "/topup/payment-methods"),
|
|
@@ -288,7 +331,10 @@ class GameCoreClient {
|
|
|
288
331
|
getStatus: (code) => this.request("GET", `/topup/${code}`)
|
|
289
332
|
};
|
|
290
333
|
giftCards = {
|
|
291
|
-
purchase: (
|
|
334
|
+
purchase: (amountRub, message) => this.request("POST", "/gift-cards/purchase", {
|
|
335
|
+
amountRub,
|
|
336
|
+
message
|
|
337
|
+
}),
|
|
292
338
|
redeem: (code) => this.request("POST", "/gift-cards/redeem", { code }),
|
|
293
339
|
check: (code) => this.request("GET", `/gift-cards/check/${code}`),
|
|
294
340
|
getMine: () => this.request("GET", "/profile/gift-cards")
|
package/dist/types.d.ts
CHANGED
|
@@ -140,6 +140,21 @@ export interface GameDetail {
|
|
|
140
140
|
productCount?: number;
|
|
141
141
|
categories: Category[];
|
|
142
142
|
seo?: SeoContent | null;
|
|
143
|
+
/**
|
|
144
|
+
* Present only when the request used an alias slug and the backend
|
|
145
|
+
* resolved it to a different canonical slug. When set, the storefront
|
|
146
|
+
* should issue a 301 redirect to `/games/${canonicalSlug}` instead
|
|
147
|
+
* of rendering the page. Absent when the requested slug is already
|
|
148
|
+
* canonical.
|
|
149
|
+
*/
|
|
150
|
+
canonicalSlug?: string;
|
|
151
|
+
/**
|
|
152
|
+
* ISO-8601 timestamp of the last meaningful edit on this canonical
|
|
153
|
+
* game — bumped on catalog changes (products, pricing, metadata).
|
|
154
|
+
* Storefronts should use this as `lastModified` in sitemap entries
|
|
155
|
+
* so Google doesn't refetch pages that haven't changed.
|
|
156
|
+
*/
|
|
157
|
+
updatedAt?: string;
|
|
143
158
|
}
|
|
144
159
|
/**
|
|
145
160
|
* Per-site SEO content for a game page.
|
|
@@ -237,6 +252,29 @@ export interface CartItem {
|
|
|
237
252
|
productName: string;
|
|
238
253
|
price: number;
|
|
239
254
|
deliveryData: Record<string, string>;
|
|
255
|
+
/**
|
|
256
|
+
* Quantity for multi-amount products (e.g. N × 500 Robux).
|
|
257
|
+
* Optional when writing (server defaults to `1`); always
|
|
258
|
+
* present on server responses.
|
|
259
|
+
*/
|
|
260
|
+
quantity?: number;
|
|
261
|
+
/**
|
|
262
|
+
* ISO-8601 timestamp of when the row was inserted. Absent when
|
|
263
|
+
* the item was freshly constructed client-side and hasn't been
|
|
264
|
+
* sent to the server yet.
|
|
265
|
+
*/
|
|
266
|
+
addedAt?: string;
|
|
267
|
+
/**
|
|
268
|
+
* Canonical game icon (localIcon → icon fallback). May be `null`
|
|
269
|
+
* if the underlying game has no artwork. Never present on
|
|
270
|
+
* client-authored items — only populated on server responses.
|
|
271
|
+
*/
|
|
272
|
+
gameIcon?: string | null;
|
|
273
|
+
/**
|
|
274
|
+
* Supplier product icon (localIcon → icon fallback). Same
|
|
275
|
+
* semantics as `gameIcon`.
|
|
276
|
+
*/
|
|
277
|
+
productIcon?: string | null;
|
|
240
278
|
}
|
|
241
279
|
export interface CheckoutRequest {
|
|
242
280
|
email?: string;
|
|
@@ -264,6 +302,46 @@ export interface CheckoutResponse {
|
|
|
264
302
|
}>;
|
|
265
303
|
}
|
|
266
304
|
export type OrderStatus = "pending" | "paid" | "processing" | "completed" | "cancelled" | "refunded" | "failed";
|
|
305
|
+
/**
|
|
306
|
+
* Payment record returned by `checkout.getByPayment()`.
|
|
307
|
+
*
|
|
308
|
+
* A payment groups one or more orders created in the same checkout
|
|
309
|
+
* call. The `code` is the external identifier exposed to the
|
|
310
|
+
* payment gateway and returned via the success URL query string.
|
|
311
|
+
*
|
|
312
|
+
* `paymentMethod` and `gatewayType` are `null` when the payment is
|
|
313
|
+
* fully settled from the user's balance (no external gateway is
|
|
314
|
+
* involved). `completedAt` is set when the payment transitions to
|
|
315
|
+
* `completed` via webhook confirmation or balance settlement; it
|
|
316
|
+
* remains `null` for `pending`, `failed`, and `cancelled` payments
|
|
317
|
+
* (the failure/cancellation timestamp is tracked separately by
|
|
318
|
+
* the stale-payment scanner and not surfaced on this shape).
|
|
319
|
+
*/
|
|
320
|
+
export interface PaymentInfo {
|
|
321
|
+
code: string;
|
|
322
|
+
totalAmount: number;
|
|
323
|
+
status: "pending" | "completed" | "failed" | "cancelled";
|
|
324
|
+
paymentMethod: string | null;
|
|
325
|
+
gatewayType: string | null;
|
|
326
|
+
createdAt: string;
|
|
327
|
+
completedAt: string | null;
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Normalised delivery hint attached to an order. Used by the
|
|
331
|
+
* storefront "My Orders" page to decide which CTA to show
|
|
332
|
+
* (robux-via-pass → "Check Roblox transactions"; gift card →
|
|
333
|
+
* "Copy code"; login delivery → "Waiting for credentials"; etc).
|
|
334
|
+
*
|
|
335
|
+
* `helpTextKey` is an i18n key — the storefront owns the
|
|
336
|
+
* translation layer, so copy changes do not require a backend
|
|
337
|
+
* deploy. Unknown `kind` values should render a generic
|
|
338
|
+
* "processing" message as a fallback.
|
|
339
|
+
*/
|
|
340
|
+
export interface OrderDeliveryMeta {
|
|
341
|
+
kind: "cdkey" | "login" | "pass_based" | "service" | "manual" | (string & {});
|
|
342
|
+
helpTextKey: string;
|
|
343
|
+
checkTransactionsUrl?: string;
|
|
344
|
+
}
|
|
267
345
|
export interface Order {
|
|
268
346
|
id: number;
|
|
269
347
|
code: string;
|
|
@@ -280,6 +358,13 @@ export interface Order {
|
|
|
280
358
|
};
|
|
281
359
|
createdAt: string;
|
|
282
360
|
completedAt?: string;
|
|
361
|
+
/**
|
|
362
|
+
* Delivery hint for the storefront UI — present on responses
|
|
363
|
+
* from `/profile/orders` and `/orders/:code` starting with
|
|
364
|
+
* SDK 0.13. Absent on legacy responses and on endpoints that
|
|
365
|
+
* do not currently surface it.
|
|
366
|
+
*/
|
|
367
|
+
deliveryMeta?: OrderDeliveryMeta;
|
|
283
368
|
}
|
|
284
369
|
export interface OrderItem {
|
|
285
370
|
id: number;
|
|
@@ -306,6 +391,20 @@ export interface UserBalance {
|
|
|
306
391
|
expiresAt: string;
|
|
307
392
|
}>;
|
|
308
393
|
}
|
|
394
|
+
/**
|
|
395
|
+
* One rung of the loyalty level ladder — used by the "how levels
|
|
396
|
+
* work" explainer on the profile page. Shape matches what the
|
|
397
|
+
* server computes from `loyalty_levels` (per-site custom) or the
|
|
398
|
+
* hardcoded default ladder.
|
|
399
|
+
*/
|
|
400
|
+
export interface LevelSystemLevel {
|
|
401
|
+
level: number;
|
|
402
|
+
name: string;
|
|
403
|
+
discountPercent: number;
|
|
404
|
+
minSpendingUsd: number;
|
|
405
|
+
minReviews: number;
|
|
406
|
+
minReferrals: number;
|
|
407
|
+
}
|
|
309
408
|
export interface LevelStatus {
|
|
310
409
|
currentLevel: number;
|
|
311
410
|
currentDiscount: number;
|
|
@@ -329,6 +428,19 @@ export interface LevelStatus {
|
|
|
329
428
|
met: boolean;
|
|
330
429
|
};
|
|
331
430
|
};
|
|
431
|
+
/**
|
|
432
|
+
* Highest level in the ladder. Storefronts should use this
|
|
433
|
+
* instead of hardcoding "15" so per-site custom ladders render
|
|
434
|
+
* correctly. Always present on responses from SDK 0.13+.
|
|
435
|
+
*/
|
|
436
|
+
maxLevel?: number;
|
|
437
|
+
/** Highest discount percent in the ladder. */
|
|
438
|
+
maxDiscount?: number;
|
|
439
|
+
/**
|
|
440
|
+
* Full ordered ladder. Use this to render the explainer table
|
|
441
|
+
* or a progress timeline. Omitted on very old responses.
|
|
442
|
+
*/
|
|
443
|
+
allLevels?: LevelSystemLevel[];
|
|
332
444
|
}
|
|
333
445
|
export interface Transaction {
|
|
334
446
|
id: number;
|
|
@@ -360,27 +472,212 @@ export interface Favorite {
|
|
|
360
472
|
categorySlug?: string;
|
|
361
473
|
createdAt: string;
|
|
362
474
|
}
|
|
475
|
+
/**
|
|
476
|
+
* Review record. Fields are endpoint-specific — this interface is a
|
|
477
|
+
* union of everything any review endpoint may return, and real-world
|
|
478
|
+
* shape depends on which route you call:
|
|
479
|
+
*
|
|
480
|
+
* - `reviews.listPublic()` → `id`, `rating`, `text`, `authorName`,
|
|
481
|
+
* `gameName`, `adminReply`, `adminReplyAt`,
|
|
482
|
+
* `createdAt`
|
|
483
|
+
* - `reviews.mine()` → `id`, `rating`, `text`, `adminReply`,
|
|
484
|
+
* `adminReplyAt`, `createdAt`, `order`
|
|
485
|
+
* - `reviews.create()` → the freshly-created review + `bonus`
|
|
486
|
+
*
|
|
487
|
+
* Fields that are not relevant to a given endpoint are `undefined`. Do
|
|
488
|
+
* not assume any single field is always present.
|
|
489
|
+
*/
|
|
363
490
|
export interface Review {
|
|
364
491
|
id: number;
|
|
365
|
-
userId: number;
|
|
366
|
-
userName?: string;
|
|
367
|
-
orderId: number;
|
|
368
|
-
orderCode?: string;
|
|
369
492
|
rating: number;
|
|
370
493
|
text?: string;
|
|
494
|
+
createdAt: string;
|
|
495
|
+
/** Public display name (author). Returned by `listPublic()`. */
|
|
496
|
+
authorName?: string;
|
|
497
|
+
/** Legacy alias for `authorName`. Prefer `authorName` on new code. */
|
|
498
|
+
userName?: string;
|
|
499
|
+
/** Owner of the review. Returned only to authenticated admins. */
|
|
500
|
+
userId?: number;
|
|
501
|
+
/** Link back to the order the review was left for. */
|
|
502
|
+
orderId?: number;
|
|
503
|
+
orderCode?: string;
|
|
371
504
|
gameName?: string;
|
|
505
|
+
/**
|
|
506
|
+
* Public reply from site support. Rendered under the review body
|
|
507
|
+
* as "Ответ поддержки". `null` if support has not replied or the
|
|
508
|
+
* reply was removed. Included in both the public listing and the
|
|
509
|
+
* authenticated profile reviews list.
|
|
510
|
+
*/
|
|
511
|
+
adminReply?: string | null;
|
|
512
|
+
/**
|
|
513
|
+
* ISO-8601 timestamp of when the support reply was created or last
|
|
514
|
+
* updated. `null` when no reply is present.
|
|
515
|
+
*/
|
|
516
|
+
adminReplyAt?: string | null;
|
|
517
|
+
/**
|
|
518
|
+
* Nested order summary, returned only by `reviews.mine()` so the
|
|
519
|
+
* profile "My reviews" page can show game + order code without a
|
|
520
|
+
* second fetch. `null` when the underlying order was deleted.
|
|
521
|
+
*/
|
|
522
|
+
order?: {
|
|
523
|
+
code: string;
|
|
524
|
+
gameName: string;
|
|
525
|
+
createdAt: string;
|
|
526
|
+
} | null;
|
|
527
|
+
/** Bonus granted for leaving the review, if any. */
|
|
372
528
|
bonus?: {
|
|
373
529
|
amount: number;
|
|
374
530
|
percent: number;
|
|
375
531
|
expiresAt: string;
|
|
376
532
|
};
|
|
377
|
-
createdAt: string;
|
|
378
533
|
}
|
|
379
534
|
export interface ReviewStats {
|
|
380
535
|
averageRating: number;
|
|
381
536
|
totalCount: number;
|
|
382
537
|
distribution?: Record<string, number>;
|
|
383
538
|
}
|
|
539
|
+
/**
|
|
540
|
+
* Response shape returned by `checkout.completeWithBalance()`.
|
|
541
|
+
*
|
|
542
|
+
* The post-deduction balance (`newBalance`) is always present on a
|
|
543
|
+
* successful response — the storefront success page can render
|
|
544
|
+
* "new balance: X" without a second `profile.getBalance()` fetch
|
|
545
|
+
* (which is race-prone right after completion).
|
|
546
|
+
*
|
|
547
|
+
* The deduction breakdown (`balanceUsed`, `balanceDeduction`) is
|
|
548
|
+
* present **only on the first successful call** — it reflects what
|
|
549
|
+
* was actually moved by this request. If the client retries the
|
|
550
|
+
* endpoint for an already-completed payment (idempotent replay),
|
|
551
|
+
* those fields are `undefined` because the original breakdown is
|
|
552
|
+
* not re-derivable from persisted state. The `newBalance` is still
|
|
553
|
+
* included in that case so the receipt UI has something to render.
|
|
554
|
+
*
|
|
555
|
+
* When present, `balanceUsed` equals
|
|
556
|
+
* `balanceDeduction.fromBonus + balanceDeduction.fromPermanent`.
|
|
557
|
+
*/
|
|
558
|
+
export interface CompleteWithBalanceResult {
|
|
559
|
+
paymentCode: string;
|
|
560
|
+
status: "completed";
|
|
561
|
+
balanceUsed?: number;
|
|
562
|
+
balanceDeduction?: {
|
|
563
|
+
fromBonus: number;
|
|
564
|
+
fromPermanent: number;
|
|
565
|
+
};
|
|
566
|
+
newBalance: {
|
|
567
|
+
total: number;
|
|
568
|
+
permanent: number;
|
|
569
|
+
bonus: number;
|
|
570
|
+
};
|
|
571
|
+
}
|
|
572
|
+
/**
|
|
573
|
+
* Short summary of a conversation as returned by
|
|
574
|
+
* `profile.getConversations()` — one row per user-owned order
|
|
575
|
+
* thread. Use the `id` to fetch full messages via
|
|
576
|
+
* `profile.getConversationMessages(id)`.
|
|
577
|
+
*/
|
|
578
|
+
export interface Conversation {
|
|
579
|
+
id: number;
|
|
580
|
+
orderId: number;
|
|
581
|
+
orderCode: string | null;
|
|
582
|
+
gameName: string | null;
|
|
583
|
+
orderStatus: string | null;
|
|
584
|
+
/** Number of unread system / admin messages. User messages don't count. */
|
|
585
|
+
unreadCount: number;
|
|
586
|
+
/**
|
|
587
|
+
* Preview of the most recent message. `body` is truncated to ~100
|
|
588
|
+
* chars. `null` if the conversation has no messages yet.
|
|
589
|
+
*/
|
|
590
|
+
lastMessage: {
|
|
591
|
+
body: string;
|
|
592
|
+
authorType: "user" | "admin" | "system";
|
|
593
|
+
messageType: "text" | "status_change" | "code_requested" | "screenshot_requested" | (string & {});
|
|
594
|
+
createdAt: string;
|
|
595
|
+
} | null;
|
|
596
|
+
/** Last time either party posted a message, ISO-8601. */
|
|
597
|
+
lastMessageAt: string;
|
|
598
|
+
}
|
|
599
|
+
/**
|
|
600
|
+
* Single message inside a conversation. `metadata` is parsed JSON —
|
|
601
|
+
* system messages may carry status-change payloads, request ids, etc.
|
|
602
|
+
* Free chat (user-authored `text` messages) is currently disabled —
|
|
603
|
+
* expect only `admin`, `system`, and request-response messages.
|
|
604
|
+
*/
|
|
605
|
+
export interface ConversationMessage {
|
|
606
|
+
id: number;
|
|
607
|
+
authorType: "user" | "admin" | "system";
|
|
608
|
+
authorId: number | null;
|
|
609
|
+
messageType: "text" | "status_change" | "code_requested" | "screenshot_requested" | (string & {});
|
|
610
|
+
body: string;
|
|
611
|
+
metadata: Record<string, unknown> | null;
|
|
612
|
+
/** Set when the current viewer marked the message as read. */
|
|
613
|
+
readAt: string | null;
|
|
614
|
+
createdAt: string;
|
|
615
|
+
}
|
|
616
|
+
/**
|
|
617
|
+
* Open supplier-initiated request inside a conversation. The UI
|
|
618
|
+
* decides which input to show the user based on `requestType`.
|
|
619
|
+
*/
|
|
620
|
+
export interface ConversationRequest {
|
|
621
|
+
id: number;
|
|
622
|
+
conversationId: number;
|
|
623
|
+
orderId: number;
|
|
624
|
+
orderItemId: number;
|
|
625
|
+
requestType: "code" | "screenshot";
|
|
626
|
+
status: "open" | "fulfilled" | "cancelled" | "expired";
|
|
627
|
+
requestedAt: string;
|
|
628
|
+
fulfilledAt: string | null;
|
|
629
|
+
}
|
|
630
|
+
/**
|
|
631
|
+
* Full detail returned by `profile.getConversationMessages(id)`.
|
|
632
|
+
* The conversation header is included so the UI does not need a
|
|
633
|
+
* separate fetch to render the page title / order summary.
|
|
634
|
+
*/
|
|
635
|
+
export interface ConversationDetail {
|
|
636
|
+
conversation: {
|
|
637
|
+
id: number;
|
|
638
|
+
orderId: number;
|
|
639
|
+
orderCode: string | null;
|
|
640
|
+
gameName: string | null;
|
|
641
|
+
orderStatus: string | null;
|
|
642
|
+
};
|
|
643
|
+
messages: ConversationMessage[];
|
|
644
|
+
/** Open supplier requests the user can respond to right now. */
|
|
645
|
+
openRequests: ConversationRequest[];
|
|
646
|
+
}
|
|
647
|
+
/**
|
|
648
|
+
* Web Push subscription payload returned by the browser's
|
|
649
|
+
* `PushSubscription.toJSON()`. Pass this to
|
|
650
|
+
* `profile.subscribePush()` to start receiving push notifications
|
|
651
|
+
* for order status updates and conversation replies.
|
|
652
|
+
*/
|
|
653
|
+
export interface WebPushSubscriptionInput {
|
|
654
|
+
endpoint: string;
|
|
655
|
+
keys: {
|
|
656
|
+
p256dh: string;
|
|
657
|
+
auth: string;
|
|
658
|
+
};
|
|
659
|
+
}
|
|
660
|
+
/**
|
|
661
|
+
* Narrow response returned by `reviews.create()`.
|
|
662
|
+
*
|
|
663
|
+
* The create endpoint deliberately returns less than a full `Review`
|
|
664
|
+
* — only the fields the storefront needs to acknowledge the
|
|
665
|
+
* submission: the new review id, the numeric rating, the text echo,
|
|
666
|
+
* and the optional bonus grant metadata.
|
|
667
|
+
*
|
|
668
|
+
* Callers who need the full review (with `createdAt`, `authorName`,
|
|
669
|
+
* etc.) should re-fetch via `reviews.getMine()` after the create.
|
|
670
|
+
*/
|
|
671
|
+
export interface ReviewCreateResult {
|
|
672
|
+
id: number;
|
|
673
|
+
rating: number;
|
|
674
|
+
text: string | null;
|
|
675
|
+
bonus: {
|
|
676
|
+
amount: number;
|
|
677
|
+
percent: number;
|
|
678
|
+
expiresAt: string;
|
|
679
|
+
} | null;
|
|
680
|
+
}
|
|
384
681
|
export interface CouponResult {
|
|
385
682
|
type: string;
|
|
386
683
|
value: number;
|
|
@@ -415,13 +712,50 @@ export interface ReferralCommission {
|
|
|
415
712
|
commissionPercent: number;
|
|
416
713
|
createdAt: string;
|
|
417
714
|
}
|
|
715
|
+
/**
|
|
716
|
+
* Date-range aggregated performance metrics for a referrer.
|
|
717
|
+
*
|
|
718
|
+
* Commission, transaction and member counts honour the `from` /
|
|
719
|
+
* `to` range passed to `referrals.getPerformance()`. `totalClicks`
|
|
720
|
+
* is **lifetime across all of the referrer's links** and is NOT
|
|
721
|
+
* date-filtered — click events are not individually timestamped in
|
|
722
|
+
* the current schema. Storefronts should label the clicks metric
|
|
723
|
+
* accordingly ("all-time clicks" vs. "period earnings").
|
|
724
|
+
*/
|
|
725
|
+
export interface ReferralPerformance {
|
|
726
|
+
totalCommission: number;
|
|
727
|
+
totalClicks: number;
|
|
728
|
+
totalTransactions: number;
|
|
729
|
+
newMembers: number;
|
|
730
|
+
}
|
|
418
731
|
export interface TopupMethod {
|
|
419
732
|
type: string;
|
|
420
733
|
label: string;
|
|
421
734
|
description?: string;
|
|
422
735
|
feePercent?: number;
|
|
423
736
|
gatewayType?: string;
|
|
424
|
-
invoiceCurrency?: string;
|
|
737
|
+
invoiceCurrency?: string | null;
|
|
738
|
+
/**
|
|
739
|
+
* Minimum topup amount in RUB. `null` means the storefront
|
|
740
|
+
* should use its global minimum (typically 50 RUB). The topup
|
|
741
|
+
* route enforces this server-side too.
|
|
742
|
+
*/
|
|
743
|
+
minAmount?: number | null;
|
|
744
|
+
/**
|
|
745
|
+
* Maximum topup amount in RUB. `null` means "use global cap".
|
|
746
|
+
*/
|
|
747
|
+
maxAmount?: number | null;
|
|
748
|
+
/**
|
|
749
|
+
* Grouping bucket for collapsible UIs. Known values: `"direct"`,
|
|
750
|
+
* `"p2p"`, `"intl"`. Unknown values should render as a
|
|
751
|
+
* generic "Other" group.
|
|
752
|
+
*/
|
|
753
|
+
group?: "direct" | "p2p" | "intl" | (string & {}) | null;
|
|
754
|
+
/**
|
|
755
|
+
* Optional warning text rendered underneath the method
|
|
756
|
+
* selector (e.g. "Временно нестабильно"). `null` means no note.
|
|
757
|
+
*/
|
|
758
|
+
stabilityNote?: string | null;
|
|
425
759
|
}
|
|
426
760
|
export interface TopupResponse {
|
|
427
761
|
code: string;
|
|
@@ -434,13 +768,48 @@ export interface TopupStatus {
|
|
|
434
768
|
amount: number;
|
|
435
769
|
createdAt?: string;
|
|
436
770
|
}
|
|
771
|
+
/**
|
|
772
|
+
* Gift card voucher. Stored in RUB; the field was historically
|
|
773
|
+
* called `denomination` in the SDK and `amount_usd` in the DB
|
|
774
|
+
* column, but both were misnomers — the value has always been RUB
|
|
775
|
+
* (see Wave 3 #34 audit `tasks/112a-giftcard-currency-audit.md`).
|
|
776
|
+
* Use `amountRub` on new code; `amountUsd` and `denomination` are
|
|
777
|
+
* deprecated aliases that echo the same value.
|
|
778
|
+
*/
|
|
437
779
|
export interface GiftCard {
|
|
438
780
|
id: number;
|
|
439
781
|
code: string;
|
|
440
|
-
|
|
441
|
-
|
|
782
|
+
/** Canonical RUB value. Use this on new code. */
|
|
783
|
+
amountRub: number;
|
|
784
|
+
/**
|
|
785
|
+
* @deprecated Legacy alias echoing `amountRub`. The name was
|
|
786
|
+
* wrong historically — the value was always RUB despite the
|
|
787
|
+
* `Usd` suffix. Remove once storefronts upgrade past SDK 0.13.x.
|
|
788
|
+
*/
|
|
789
|
+
amountUsd?: number;
|
|
790
|
+
/**
|
|
791
|
+
* @deprecated Legacy alias echoing `amountRub`. Kept for
|
|
792
|
+
* storefronts that were reading this field from typed code; at
|
|
793
|
+
* runtime the backend never emitted `denomination`, so most
|
|
794
|
+
* consumers were already getting `undefined`.
|
|
795
|
+
*/
|
|
796
|
+
denomination?: number;
|
|
797
|
+
/** Currency of `amountRub`. Always `"RUB"` for now. */
|
|
798
|
+
currency: "RUB";
|
|
799
|
+
/**
|
|
800
|
+
* Remaining balance if the card supports partial redemption.
|
|
801
|
+
* For active and expired cards this equals `amountRub`. For
|
|
802
|
+
* redeemed or cancelled cards it is `0`. Partial redemption is
|
|
803
|
+
* not implemented yet — the field is reserved so storefronts
|
|
804
|
+
* can start rendering it now without a future breaking change.
|
|
805
|
+
*/
|
|
806
|
+
remainingBalance: number;
|
|
807
|
+
status: "active" | "redeemed" | "expired" | "cancelled" | (string & {});
|
|
808
|
+
message?: string | null;
|
|
809
|
+
/** ISO-8601 expiry timestamp; `null` if the card never expires. */
|
|
810
|
+
expiresAt?: string | null;
|
|
442
811
|
createdAt: string;
|
|
443
|
-
redeemedAt?: string;
|
|
812
|
+
redeemedAt?: string | null;
|
|
444
813
|
}
|
|
445
814
|
export interface Announcement {
|
|
446
815
|
id: number;
|
package/package.json
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gamecore-api/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"description": "TypeScript SDK for GameCore API — browser-safe, zero dependencies",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
|
-
"module": "dist/index.mjs",
|
|
8
7
|
"exports": {
|
|
9
8
|
".": {
|
|
10
9
|
"import": "./dist/index.js",
|