@gamecore-api/sdk 0.26.2 → 0.27.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/AGENTS.md CHANGED
@@ -61,6 +61,17 @@ import { verifyWebhookSignature } from "@gamecore-api/sdk/server";
61
61
  - The backend overlays **game names**, short descriptions, and descriptions in the requested locale. Slugs / IDs never change.
62
62
  - Original-language responses: just don't set `locale`.
63
63
 
64
+ ## Display currency (since 0.27.0)
65
+
66
+ - Pass `currency: "USD" | "EUR" | "KZT" | "UAH" | "TRY" | …` in constructor → SDK sends `X-Currency` automatically on every catalog request.
67
+ - Override at runtime: `gc.setCurrency("EUR")` / `gc.getCurrency()`.
68
+ - Per-call override: `gc.catalog.getProducts(slug, { currency: "USD" })`.
69
+ - Every product response carries `product.currency` so the storefront can render the right symbol without tracking the requested code.
70
+ - Server applies **live FX rates** (open.er-api.com, 30-min cache) and rounds per currency convention (whole KZT/UAH/TRY/RUB, 2-decimal USD/EUR/GBP).
71
+ - Default is RUB — existing storefronts keep working unchanged.
72
+ - **Checkout is unrelated**: payments still settle in RUB or USD via the chosen gateway. Display currency is purely catalog-side.
73
+ - See `examples/05-currency-switching.ts`.
74
+
64
75
  ## Pitfalls
65
76
 
66
77
  - **Don't put `apiKey` in client-side JS bundles** — proxy through your own
package/README.md CHANGED
@@ -20,6 +20,7 @@ for a short orientation, then look at runnable code in
20
20
  | `examples/02-locale-switching.ts` | RU/EN switching: constructor, runtime, per-call |
21
21
  | `examples/03-error-handling.ts` | `GameCoreError`, status/code patterns, retries |
22
22
  | `examples/04-webhook-verify.ts` | HMAC verification with `/server` entry point |
23
+ | `examples/05-currency-switching.ts` | Display currency (RUB / USD / EUR / KZT / UAH / TRY …) |
23
24
 
24
25
  ## Locale switching (RU / EN)
25
26
 
@@ -49,6 +50,40 @@ const enGame = await gc.catalog.getGame("afk-journey", "en");
49
50
 
50
51
  Supported locales: `"ru"` (default when nothing is passed) and `"en"`.
51
52
 
53
+ ## Display currency (RUB / USD / EUR / KZT / UAH / TRY …)
54
+
55
+ Since 0.27.0 the SDK can quote product prices in any of the supported
56
+ ISO-4217 currencies. The server uses live FX rates (cached 30 minutes)
57
+ and rounds per currency convention (whole KZT/UAH/TRY/RUB, 2-decimal
58
+ USD/EUR/GBP).
59
+
60
+ ```ts
61
+ import { GameCoreClient, type SdkCurrency } from "@gamecore-api/sdk";
62
+
63
+ const gc = new GameCoreClient({
64
+ apiKey: "gc_live_...",
65
+ baseUrl: "https://api.gamecore-api.tech",
66
+ currency: "USD", // SDK adds X-Currency: USD on every catalog request
67
+ });
68
+
69
+ // Runtime switch — wire to a storefront currency picker:
70
+ gc.setCurrency("KZT");
71
+ const products = await gc.catalog.getProducts("free-fire");
72
+ console.log(products[0].price, products[0].currency); // 480, "KZT"
73
+
74
+ // Per-call override:
75
+ const usdProducts = await gc.catalog.getProducts("free-fire", { currency: "USD" });
76
+ ```
77
+
78
+ Supported currencies: `RUB` (default), `USD`, `EUR`, `GBP`, `KZT`,
79
+ `UAH`, `TRY`, `BRL`, `ARS`, `INR`, `PLN`, `CZK`. Every product
80
+ response carries the resolved `currency` field — read that instead of
81
+ tracking the requested code separately.
82
+
83
+ **Checkout is unrelated**: payment gateways still settle in RUB or USD
84
+ depending on the chosen `paymentMethod`. The display currency is a
85
+ catalog-side feature today.
86
+
52
87
  ## What's new in 0.25.0
53
88
 
54
89
  - **Locale switching (RU / EN).** New `locale` option on `GameCoreClient`
package/dist/client.d.ts CHANGED
@@ -1,6 +1,13 @@
1
1
  import type { Announcement, AnnouncementBar, CartItem, CashbackPreview, CatalogSection, Category, CategoryInfo, CheckoutRequest, CheckoutResponse, CheckoutStatus, CmsArticleListResponse, CmsArticleLocale, CmsArticleResponse, CmsArticleType, CompleteWithBalanceResult, Conversation, ConversationDetail, CouponResult, DailyBonusClaimResult, DailyBonusStatus, DeliveryHelpResponse, ExchangeRates, FaqListResponse, Favorite, Game, GameDetail, GameRequestList, GiftCard, LegalDocument, LevelStatus, Notification, Order, PagedGamesResponse, PlatformInfo, PaginatedResponse, PaymentInfo, PaymentMethod, Product, ProductFilters, ProfileSummary, PublicCoupon, Quest, QuestCompleteResult, ReferralCommission, ReferralLink, ReferralPerformance, ReferralStats, ReferralTransferResult, Review, ReviewCreateResult, ReviewPolicy, ReviewProof, ReviewStats, ScreenshotsResponse, SearchResult, SiteConfig, SiteStats, SiteUIConfig, SystemRequirementsResponse, TelegramAuthResponse, TelegramBotLoginOptions, TelegramInitResponse, TelegramWidgetRenderOptions, TelegramWidgetUser, TopupMethod, TopupResponse, TopupStatus, Transaction, User, UserBalance, WebPushSubscriptionInput } from "./types";
2
2
  /** Supported storefront locales — extend as new languages land. */
3
3
  export type SdkLocale = "ru" | "en";
4
+ /**
5
+ * ISO-4217 codes the API can quote prices in. Matches the server-side
6
+ * `DisplayCurrency` union in `apps/api/src/utils/currency.ts`. RUB is
7
+ * the default when nothing is configured; USD is the canonical base
8
+ * price in the DB.
9
+ */
10
+ export type SdkCurrency = "RUB" | "USD" | "EUR" | "GBP" | "KZT" | "UAH" | "TRY" | "BRL" | "ARS" | "INR" | "PLN" | "CZK";
4
11
  export interface GameCoreOptions {
5
12
  /** Site API key (gc_live_xxx or gc_test_xxx) */
6
13
  apiKey: string;
@@ -16,12 +23,25 @@ export interface GameCoreOptions {
16
23
  * Omit to keep the legacy behaviour (server falls back to "ru").
17
24
  */
18
25
  locale?: SdkLocale;
26
+ /**
27
+ * Default display currency for catalog product prices. When set,
28
+ * every request sends an `X-Currency` header so the API converts
29
+ * `price` / `priceWithoutDiscount` into that ISO-4217 code (using
30
+ * live FX rates) and stamps the resolved code on each product
31
+ * response under the `currency` field.
32
+ *
33
+ * Per-call `?currency=` overrides this default. Omit to keep RUB
34
+ * (the legacy default — every existing storefront keeps working
35
+ * without a code change).
36
+ */
37
+ currency?: SdkCurrency;
19
38
  }
20
39
  export declare class GameCoreClient {
21
40
  private apiKey;
22
41
  private baseUrl;
23
42
  private onAuthError?;
24
43
  private defaultLocale?;
44
+ private defaultCurrency?;
25
45
  constructor(options: GameCoreOptions);
26
46
  /**
27
47
  * Switch the client's default locale at runtime — useful for a
@@ -31,6 +51,14 @@ export declare class GameCoreClient {
31
51
  setLocale(locale: SdkLocale | undefined): void;
32
52
  /** Current default locale (undefined when none set). */
33
53
  getLocale(): SdkLocale | undefined;
54
+ /**
55
+ * Switch the client's default display currency at runtime — wire to
56
+ * a storefront currency picker so the next product fetch comes back
57
+ * already converted, without re-instantiating the client.
58
+ */
59
+ setCurrency(currency: SdkCurrency | undefined): void;
60
+ /** Current default currency (undefined when none set → API returns RUB). */
61
+ getCurrency(): SdkCurrency | undefined;
34
62
  private request;
35
63
  site: {
36
64
  /** Get site configuration (modules, auth methods, payments, currency) */
@@ -468,8 +496,17 @@ export declare class GameCoreClient {
468
496
  getDeliveryHelp: (gameSlug: string) => Promise<DeliveryHelpResponse>;
469
497
  /** Get categories for a game with product counts and delivery metadata */
470
498
  getCategories: (gameSlug: string) => Promise<Category[]>;
471
- /** Get products for a game with optional filters */
472
- getProducts: (gameSlug: string, filters?: ProductFilters) => Promise<Product[]>;
499
+ /**
500
+ * Get products for a game with optional filters.
501
+ *
502
+ * `filters.currency` overrides the client-level default for
503
+ * this call only — useful for an admin tool that has a
504
+ * Storefront in one currency but needs to preview USD pricing
505
+ * without flipping the whole client.
506
+ */
507
+ getProducts: (gameSlug: string, filters?: ProductFilters & {
508
+ currency?: SdkCurrency;
509
+ }) => Promise<Product[]>;
473
510
  /** Get products grouped by category */
474
511
  getProductsGrouped: (gameSlug: string) => Promise<{
475
512
  category: Category;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  export { GameCoreClient } from "./client";
2
- export type { GameCoreOptions } from "./client";
2
+ export type { GameCoreOptions, SdkCurrency, SdkLocale } from "./client";
3
3
  export * from "./types";
4
4
  export { convertPrice, formatPrice, generateIdempotencyKey } from "./utils";
package/dist/index.js CHANGED
@@ -37,11 +37,13 @@ class GameCoreClient {
37
37
  baseUrl;
38
38
  onAuthError;
39
39
  defaultLocale;
40
+ defaultCurrency;
40
41
  constructor(options) {
41
42
  this.apiKey = options.apiKey;
42
43
  this.baseUrl = options.baseUrl.replace(/\/$/, "");
43
44
  this.onAuthError = options.onAuthError;
44
45
  this.defaultLocale = options.locale;
46
+ this.defaultCurrency = options.currency;
45
47
  }
46
48
  setLocale(locale) {
47
49
  this.defaultLocale = locale;
@@ -49,6 +51,12 @@ class GameCoreClient {
49
51
  getLocale() {
50
52
  return this.defaultLocale;
51
53
  }
54
+ setCurrency(currency) {
55
+ this.defaultCurrency = currency;
56
+ }
57
+ getCurrency() {
58
+ return this.defaultCurrency;
59
+ }
52
60
  async request(method, path, body, options) {
53
61
  const headers = {
54
62
  "X-Api-Key": this.apiKey,
@@ -57,6 +65,9 @@ class GameCoreClient {
57
65
  if (this.defaultLocale) {
58
66
  headers["Accept-Language"] = this.defaultLocale;
59
67
  }
68
+ if (this.defaultCurrency) {
69
+ headers["X-Currency"] = this.defaultCurrency;
70
+ }
60
71
  if (options?.idempotencyKey) {
61
72
  headers["X-Idempotency-Key"] = options.idempotencyKey;
62
73
  }
@@ -307,6 +318,8 @@ class GameCoreClient {
307
318
  qs.set("minPrice", String(filters.minPrice));
308
319
  if (filters?.maxPrice != null)
309
320
  qs.set("maxPrice", String(filters.maxPrice));
321
+ if (filters?.currency)
322
+ qs.set("currency", filters.currency);
310
323
  const q = qs.toString();
311
324
  return this.request("GET", `/catalog/games/${gameSlug}/products${q ? `?${q}` : ""}`);
312
325
  },
package/dist/types.d.ts CHANGED
@@ -381,6 +381,14 @@ export interface Product {
381
381
  estimatedDelivery?: string;
382
382
  icon: string | null;
383
383
  price: number | null;
384
+ /**
385
+ * ISO-4217 code (`RUB`, `USD`, `EUR`, `KZT`, …) for `price` and
386
+ * `priceWithoutDiscount`. Set by the API on every product response
387
+ * since gamecore-api 2026-05-11 / SDK 0.27 — the storefront should
388
+ * use this directly rather than tracking the requested currency
389
+ * separately. Falls through as `undefined` from older API builds.
390
+ */
391
+ currency?: string;
384
392
  priceWithoutDiscount?: number;
385
393
  discountPercent?: number;
386
394
  gameId: string;
@@ -46,25 +46,30 @@ console.log("picked product:", product.id, product.name, "price:", product.price
46
46
  // 4. Create checkout (guest — pass email; no Telegram/VK login required).
47
47
  // For balance-based payment, the user must be authenticated first.
48
48
  //
49
- // Item fields:
50
- // productId — REQUIRED, the supplier product
51
- // gameId — REQUIRED (despite being typed optional). It's
52
- // the game slug; the backend reads it directly to
53
- // route Steam top-ups, SuperPass bundles, and
54
- // Robux-via-pass through their special validation
55
- // paths. A missing `gameId` crashes the request.
56
- // amount — quantity for fixed-price items (default 1) OR
57
- // the top-up amount in local currency for Steam /
58
- // variable-amount products.
59
- // • deliveryData — product-specific fields (account name, server
60
- // id, etc.). For simple gift cards this can be
61
- // an empty object.
49
+ // The SDK's `CheckoutRequest.items` type currently marks several
50
+ // fields optional, but the API's body schema (TypeBox) rejects
51
+ // requests that omit them:
52
+ // productId — REQUIRED, the supplier product
53
+ // gameId — REQUIRED, the game slug. Drives routing for
54
+ // Steam top-ups, SuperPass bundles, and
55
+ // Robux-via-pass; the backend also reads it
56
+ // directly outside those paths.
57
+ // gameName — REQUIRED by body validation (maxLength 500).
58
+ // productName — REQUIRED by body validation (maxLength 500).
59
+ // • deliveryData — REQUIRED object (can be `{}`). For Steam /
60
+ // Roblox / login-required products it carries
61
+ // account name, server id, top-up currency, etc.
62
+ // • amount — optional. Quantity for fixed-price items
63
+ // (default 1) OR the top-up amount in local
64
+ // currency for Steam / variable-amount products.
62
65
  const checkout = await gc.checkout.create({
63
66
  email: "buyer@example.com",
64
67
  items: [
65
68
  {
66
69
  productId: product.id,
67
70
  gameId: game.slug,
71
+ gameName: game.name,
72
+ productName: product.name,
68
73
  amount: 1,
69
74
  deliveryData: {},
70
75
  },
@@ -0,0 +1,74 @@
1
+ /**
2
+ * 05 — Display-currency switching (RUB / USD / EUR / KZT / UAH / TRY / …)
3
+ *
4
+ * Catalog endpoints quote product prices in any of the supported ISO-4217
5
+ * currencies. RUB is the legacy default — a client that doesn't ask for
6
+ * anything else keeps getting ruble prices, same as before SDK 0.27.
7
+ *
8
+ * Resolution order on the server (highest priority first):
9
+ * 1. `?currency=USD` query param (per-call override)
10
+ * 2. `X-Currency` request header (SDK injects when constructor has
11
+ * `currency: "USD"` configured)
12
+ * 3. RUB default
13
+ *
14
+ * Run with:
15
+ * GAMECORE_API_KEY=gc_live_... bun run examples/05-currency-switching.ts
16
+ */
17
+
18
+ import { GameCoreClient, type SdkCurrency } from "@gamecore-api/sdk";
19
+
20
+ const apiKey = process.env.GAMECORE_API_KEY!;
21
+ const baseUrl = "https://api.gamecore-api.tech";
22
+
23
+ // Approach A — one client per currency. Simplest for a single-currency
24
+ // storefront (e.g. a US store that always shows USD).
25
+ const ru = new GameCoreClient({ apiKey, baseUrl, currency: "RUB" });
26
+ const usd = new GameCoreClient({ apiKey, baseUrl, currency: "USD" });
27
+
28
+ const gameSlug = "free-fire";
29
+ const ruProducts = await ru.catalog.getProducts(gameSlug);
30
+ const usdProducts = await usd.catalog.getProducts(gameSlug);
31
+ console.log("RUB first product:", {
32
+ name: ruProducts[0]?.name,
33
+ price: ruProducts[0]?.price,
34
+ currency: ruProducts[0]?.currency, // "RUB"
35
+ });
36
+ console.log("USD first product:", {
37
+ name: usdProducts[0]?.name,
38
+ price: usdProducts[0]?.price,
39
+ currency: usdProducts[0]?.currency, // "USD"
40
+ });
41
+
42
+ // Approach B — single client, switch at runtime. Wire this to a
43
+ // storefront currency picker so the next product fetch comes back
44
+ // already converted.
45
+ const gc = new GameCoreClient({ apiKey, baseUrl });
46
+ console.log("default currency:", gc.getCurrency()); // undefined → RUB
47
+ gc.setCurrency("KZT");
48
+ const kztProducts = await gc.catalog.getProducts(gameSlug);
49
+ console.log(
50
+ "KZT first product:",
51
+ kztProducts[0]?.price,
52
+ kztProducts[0]?.currency,
53
+ );
54
+
55
+ // Approach C — per-call override. Admin tool example: a Russian
56
+ // operator inspects an EN/EUR preview of a product without flipping
57
+ // the rest of the session.
58
+ const previewCurrencies: SdkCurrency[] = ["EUR", "TRY", "UAH"];
59
+ for (const ccy of previewCurrencies) {
60
+ const products = await gc.catalog.getProducts(gameSlug, { currency: ccy });
61
+ console.log(`${ccy}:`, products[0]?.price, products[0]?.currency);
62
+ }
63
+
64
+ // Notes for AI agents copying this example:
65
+ // • Don't try to convert prices yourself — the server applies live FX
66
+ // rates from open.er-api.com and rounds per-currency (whole KZT,
67
+ // 2-decimal EUR, etc.). Doing it client-side will drift.
68
+ // • Always use `product.currency` for the display string instead of
69
+ // hardcoding the requested code — the server is the source of
70
+ // truth when an FX rate is missing.
71
+ // • Checkout is unrelated: payment gateways still settle in RUB or
72
+ // USD depending on the chosen `paymentMethod`. The display
73
+ // currency is purely catalog-side until checkout currency support
74
+ // ships.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gamecore-api/sdk",
3
- "version": "0.26.2",
3
+ "version": "0.27.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",