@gamecore-api/sdk
v0.32.0
Published
TypeScript SDK for GameCore API — browser-safe, zero dependencies
Maintainers
Readme
@gamecore-api/sdk
TypeScript SDK for GameCore API — zero external dependencies, browser-safe.
Install
npm install @gamecore-api/sdkFor AI agents
If an AI coding assistant is reading this, start with AGENTS.md for a short orientation, then look at runnable code in examples/:
| File | Covers |
|---|---|
| examples/01-quickstart.ts | catalog → checkout → status polling |
| examples/02-locale-switching.ts | RU/EN switching: constructor, runtime, per-call |
| examples/03-error-handling.ts | GameCoreError, status/code patterns, retries |
| examples/04-webhook-verify.ts | HMAC verification with /server entry point |
| examples/05-currency-switching.ts | Display currency (RUB / USD / EUR / KZT / UAH / TRY …) |
Locale switching (RU / EN)
The SDK ships with built-in multilingual support. Pass locale: "ru" | "en"
to the constructor and every catalog / CMS response comes back in that
language. The client sends an Accept-Language header on every request;
the API resolves it against the unified catalog_translations store and
falls back to the base copy when a translation is missing.
import { GameCoreClient, type SdkLocale } from "@gamecore-api/sdk";
const gc = new GameCoreClient({
apiKey: "gc_live_...",
baseUrl: "https://api.gamecore-api.tech",
locale: "en", // default for this client instance
});
// Runtime switch — wire this to a storefront language toggle:
gc.setLocale("ru");
const game = await gc.catalog.getGame("afk-journey");
// game.name / game.description / game.shortDescription are now in RU
// Per-call override still wins over the client default:
const enGame = await gc.catalog.getGame("afk-journey", "en");Supported locales: "ru" (default when nothing is passed) and "en".
Display currency (RUB / USD / EUR / KZT / UAH / TRY …)
Since 0.27.0 the SDK can quote product prices in any of the supported ISO-4217 currencies. The server uses live FX rates (cached 30 minutes) and rounds per currency convention (whole KZT/UAH/TRY/RUB, 2-decimal USD/EUR/GBP).
import { GameCoreClient, type SdkCurrency } from "@gamecore-api/sdk";
const gc = new GameCoreClient({
apiKey: "gc_live_...",
baseUrl: "https://api.gamecore-api.tech",
currency: "USD", // SDK adds X-Currency: USD on every catalog request
});
// Runtime switch — wire to a storefront currency picker:
gc.setCurrency("KZT");
const products = await gc.catalog.getProducts("free-fire");
console.log(products[0].price, products[0].currency); // 480, "KZT"
// Per-call override:
const usdProducts = await gc.catalog.getProducts("free-fire", { currency: "USD" });Supported currencies: RUB (default), USD, EUR, GBP, KZT,
UAH, TRY, BRL, ARS, INR, PLN, CZK. Every product
response carries the resolved currency field — read that instead of
tracking the requested code separately.
Checkout is unrelated: payment gateways still settle in RUB or USD
depending on the chosen paymentMethod. The display currency is a
catalog-side feature today.
What's new in 0.25.0
- Locale switching (RU / EN). New
localeoption onGameCoreClientplussetLocale/getLocaleruntime helpers. See section above. - New exported type
SdkLocale = "ru" | "en". - Backwards-compatible: clients that don't pass
localekeep the previous "server falls back to RU" behaviour.
What's new in 0.14.0
- BREAKING
giftCards.purchase()signature changed: first arg is nowamountRub(wasamountUsd). GiftCard payload fields renamed —amount_usd→amount_rub, addedcurrency,remainingBalance,expiresAt.denominationis now optional (legacy) cart.merge(items)— guest → authed cart handoff; new response fieldsquantity,addedAt,gameIconauth.linkEmail(email, password)— add email identity to an existing Telegram/VK accountprofile.getConversations / getConversationMessages / submitCode / submitScreenshot— in-profile support chatprofile.getPushPublicKey / subscribePush / unsubscribePush— web push subscriptionsreferrals.getPopularProducts(limit)+referrals.getPerformance({ from, to })— affiliate analyticssite.requestGame({ gameName, contact })— public "request a game" lead capturesite.getSitemapData()— data source forsitemap.xmlcheckout.completeWithBalancenow returns{ newBalance }
Quick Start
import { GameCoreClient } from "@gamecore-api/sdk";
const gc = new GameCoreClient({
apiKey: "gc_live_YOUR_KEY",
baseUrl: "https://api.gamecore-api.tech",
onAuthError: () => window.location.href = "/login",
});
// Browse catalog
const games = await gc.catalog.getGames();
const game = await gc.catalog.getGame("honkai-star-rail");
const products = await gc.catalog.getProducts("honkai-star-rail");
// Search
const results = await gc.catalog.search("roblox");Authentication
Telegram Auth — two paths, pick both
Two flavours, designed to coexist as two buttons in the UI:
1. Official Login Widget (fastest, needs official Telegram Web session)
// Mount the blue "Log in with Telegram" button into your own <div>.
// Bot username is pulled from /site/config; no hardcoding.
// BotFather /setdomain must point at your storefront's origin, or
// telegram.org refuses to render the widget.
const cleanup = await gc.auth.renderTelegramWidget({
container: document.querySelector("#tg-login")!,
size: "large",
onAuth: (user) => {
console.log("Logged in:", user.firstName);
window.location.href = "/profile";
},
onError: (err) => console.error(err),
});
// Later (React unmount, SPA route change):
cleanup();2. Bot-link flow (works in every Telegram client including 3rd-party)
const user = await gc.auth.loginViaTelegramBot({
onBotLinkReady: (botLink) => {
// Open in new tab — every TG client handles tg:// deep links.
// For desktop-only users you could also render botLink as a QR.
window.open(botLink, "_blank");
},
pollMs: 2000,
timeoutMs: 120_000,
});
console.log("Logged in:", user.firstName);Low-level pieces (for custom flows)
// Manual init+poll — equivalent to loginViaTelegramBot above
const { token, botLink } = await gc.auth.initTelegram();
window.open(botLink, "_blank");
const user = await gc.auth.pollTelegramStatus(token);
// Manual widget verification — when you render Telegram's <script> yourself
// and wire data-onauth to your own JS callback
const auth = await gc.auth.verifyTelegramWidget(telegramWidgetUser);
// Mini App (inside the Telegram bot's built-in WebApp)
const auth = await gc.auth.verifyMiniApp(window.Telegram.WebApp.initData);VK Auth
const { user } = await gc.auth.verifyVk(vkAccessToken);Email + Password
await gc.auth.register(email, password, firstName, ref);
await gc.auth.login(email, password);
await gc.auth.changePassword(currentPassword, newPassword);Link additional identity
// Add email to an existing Telegram/VK account
await gc.auth.linkEmail(email, password);
// Or link a VK access token
await gc.auth.linkVk(vkAccessToken);Session
const me = await gc.auth.getMe(); // Get current user
await gc.auth.logout(); // Clear session
const identities = await gc.auth.getIdentities(); // Linked providersCatalog
// All games
const games = await gc.catalog.getGames({ type: "game" });
// Homepage ranked games
const homepage = await gc.catalog.getHomepageGames();
// Single game with categories
const game = await gc.catalog.getGame("genshin-impact");
// Products (optionally filtered by category)
const products = await gc.catalog.getProducts("genshin-impact", "crystals");
// Products grouped by category
const grouped = await gc.catalog.getProductsGrouped("genshin-impact");
// Search (returns games + products)
const { games, products } = await gc.catalog.search("roblox");
// Search suggestions
const { suggestions } = await gc.catalog.searchSuggestions("rob");Cart & Checkout
// Cart
const items = await gc.cart.get();
// items: [{ id, productId, quantity, addedAt, gameIcon, ... }]
await gc.cart.add({ productId: 10, gameId: "roblox", gameName: "Roblox", productName: "800 Robux", price: 799, deliveryData: { username: "player123" }, quantity: 2 });
await gc.cart.remove(itemId);
await gc.cart.clear();
// Merge guest cart into authed session (on login)
await gc.cart.merge(guestItems);
// Checkout (auto-generates idempotency key)
const checkout = await gc.checkout.create({
items: [{ productId: 10, deliveryData: { username: "player123" } }],
paymentMethod: "antilopay",
});
// Redirect to payment page
if (checkout.payment?.paymentUrl) {
window.location.href = checkout.payment.paymentUrl;
}
// Or pay with balance
await gc.checkout.completeWithBalance(checkout.payment.code);Orders
const orders = await gc.orders.list();
const order = await gc.orders.get("ORD-A7X9K2");
await gc.orders.cancel("ORD-A7X9K2");
// Track order in real-time (SSE)
const source = gc.sse.trackOrder("ORD-A7X9K2");
source.addEventListener("order_status", (e) => {
const data = JSON.parse(e.data);
console.log("Status:", data.status, "Items:", data.items);
});Profile
// Balance (permanent + bonus with expiration details)
const balance = await gc.profile.getBalance();
// { permanent: 500, bonus: 100, total: 600, bonusDetails: [{ remaining: 100, expiresAt: "..." }] }
// Level status with progress
const level = await gc.profile.getLevelStatus();
// { currentLevel: 3, currentDiscount: 5, nextLevel: 4, requirements: { spending: { current: 5000, required: 10000 } } }
// Transaction history
const transactions = await gc.profile.getTransactions({ limit: 20 });
// Orders
const orders = await gc.profile.getOrders();
// Notifications
const notifications = await gc.profile.getNotifications();
const { count } = await gc.profile.getUnreadCount();
await gc.profile.markRead(notificationId);
await gc.profile.markAllRead();
// Support conversations (in-profile chat)
const conversations = await gc.profile.getConversations();
const messages = await gc.profile.getConversationMessages(conversationId);
await gc.profile.submitCode(conversationId, requestId, "ABC-123");
await gc.profile.submitScreenshot(conversationId, requestId, file);
// Web push subscriptions
const { publicKey } = await gc.profile.getPushPublicKey();
await gc.profile.subscribePush({ endpoint, keys: { p256dh, auth } });
await gc.profile.unsubscribePush(endpoint);Favorites
const favorites = await gc.favorites.list();
await gc.favorites.add(productId, "genshin-impact"); // gameId as slug string
await gc.favorites.remove(productId);Reviews
// Public reviews (paginated)
const { data, pagination } = await gc.reviews.listPublic({ limit: 10 });
// Stats
const stats = await gc.reviews.getStats("genshin-impact");
// { averageRating: 4.8, totalReviews: 156 }
// Random reviews (for homepage)
const random = await gc.reviews.getRandom(5);
// Submit review (authenticated)
const review = await gc.reviews.create(orderId, 5, "Great service!");
// Orders waiting for review
const pending = await gc.reviews.getPending();Coupons & Gift Cards
// Apply coupon
const result = await gc.coupons.apply("WELCOME10");
// { type: "bonus_balance", value: 10, code: "WELCOME10", bonusAmount: 100 }
await gc.coupons.remove();
const active = await gc.coupons.getActive();
// Gift cards
const card = await gc.giftCards.purchase(500, "Happy birthday!"); // amountRub + optional message
await gc.giftCards.redeem("GC-XXXX-XXXX-XXXX");
const mine = await gc.giftCards.getMine();
// { code, amount_rub, currency, remainingBalance, expiresAt, ... }Referrals
const stats = await gc.referrals.getStats();
const links = await gc.referrals.getLinks();
const link = await gc.referrals.createLink({ label: "YouTube", slug: "my-channel" });
await gc.referrals.updateLink(link.id, { label: "Updated" });
const linkStats = await gc.referrals.getLinkStats(link.id);
const commissions = await gc.referrals.getCommissions();
// Popular products referred by this user
const popular = await gc.referrals.getPopularProducts(10);
// Performance over a date range
const perf = await gc.referrals.getPerformance({
from: "2026-04-01",
to: "2026-04-30",
});Balance Top-up
const methods = await gc.topup.getPaymentMethods();
const topup = await gc.topup.create(500, "lava");
// Redirect to topup.paymentUrl
const status = await gc.topup.getStatus(topup.code);SSE (Real-time Events)
// Authenticated notification stream
const events = gc.sse.connectEvents();
events.addEventListener("notification", (e) => {
const { type, data } = JSON.parse(e.data);
// type: "order_completed", "balance_updated", "level_up", etc.
});
// Order tracking (no auth, uses order code)
const tracker = gc.sse.trackOrder("ORD-A7X9K2");SEO
// entityId is numeric (canonical game ID), not slug
const seo = await gc.seo.getContent("game", 1076, "ru");
// Schema is available for "product" page type
const schema = await gc.seo.getSchema("product", 42);Webhook Verification (Server-side)
// Import from server entrypoint (uses node:crypto)
import { verifyWebhookSignature, parseWebhookPayload } from "@gamecore-api/sdk/server";
const isValid = verifyWebhookSignature(
requestBody,
request.headers["x-webhook-signature"],
WEBHOOK_SECRET,
);
if (isValid) {
const payload = parseWebhookPayload(requestBody);
console.log(payload.event, payload.data);
}Utilities
import { convertPrice, formatPrice, generateIdempotencyKey } from "@gamecore-api/sdk";
const rub = convertPrice(1.99, 92.5); // 184.08
const formatted = formatPrice(rub, "RUB"); // "184 ₽"
const key = generateIdempotencyKey(); // UUID v4Next.js App Router Examples
Server Component (SSR catalog)
// app/catalog/page.tsx
import { GameCoreClient } from "@gamecore-api/sdk";
const gc = new GameCoreClient({
apiKey: process.env.GAMECORE_API_KEY!,
baseUrl: process.env.GAMECORE_API_URL!,
});
export default async function CatalogPage() {
const games = await gc.catalog.getGames();
return <GameGrid games={games} />;
}Client Component (cart)
"use client";
import { useEffect, useState } from "react";
import { gc } from "@/lib/gamecore-browser";
export function CartWidget() {
const [items, setItems] = useState([]);
useEffect(() => { gc.cart.get().then(setItems); }, []);
return <span>{items.length} items</span>;
}Route Handler (webhook)
// app/api/webhooks/gamecore/route.ts
import { verifyWebhookSignature, parseWebhookPayload } from "@gamecore-api/sdk/server";
export async function POST(req: Request) {
const body = await req.text();
const sig = req.headers.get("x-webhook-signature") || "";
if (!verifyWebhookSignature(body, sig, process.env.WEBHOOK_SECRET!)) {
return new Response("Unauthorized", { status: 401 });
}
const payload = parseWebhookPayload(body);
// Handle event...
return new Response("OK");
}API Namespaces
| Namespace | Methods |
|-----------|---------|
| gc.site | getConfig, getRates, getLegal, getStats, getSocialProof, getThemeConfig, getTranslations, getUIConfig, getCookieConsent, getCatalogSections, getBanners, getAnnouncementBar, getSitemapData, requestGame |
| gc.auth | initTelegram, pollTelegramStatus, verifyMiniApp, verifyTelegramWidget, getVkAuthUrl, vkCallback, verifyVk, register, login, changePassword, getMe, logout, getIdentities, linkVk, linkEmail, unlinkProvider, mergePreview, mergeConfirm |
| gc.catalog | getGames, getHomepageGames, getGame, getRecommendations, getCategories, getProducts, getProductsGrouped, search, searchSuggestions, getProduct |
| gc.cart | get, add, merge, sync, remove, clear |
| gc.checkout | create, completeWithBalance, getPaymentMethods |
| gc.orders | list, get, getByPayment, cancel |
| gc.profile | getBalance, getLevelStatus, getTransactions, getOrders, getNotifications, getUnreadCount, markRead, markAllRead, getConversations, getConversationMessages, submitCode, submitScreenshot, getPushPublicKey, subscribePush, unsubscribePush |
| gc.favorites | list, add, remove |
| gc.coupons | apply, remove, validate, getActive |
| gc.referrals | getStats, getLinks, createLink, updateLink, deleteLink, getLinkStats, getCommissions, getPopularProducts, getPerformance |
| gc.reviews | listPublic, getStats, getRandom, getMine, getPending, create |
| gc.topup | getPaymentMethods, create, getStatus |
| gc.giftCards | purchase, redeem, check, getMine |
| gc.announcements | list, get |
| gc.analytics | recordView |
| gc.seo | getContent, getSchema |
| gc.sse | connectEvents, trackOrder |
Browser vs Server
| Import | Environment | Includes |
|--------|-------------|----------|
| @gamecore-api/sdk | Browser + Node | Client, types, utilities |
| @gamecore-api/sdk/server | Node only | Webhook verification (uses node:crypto) |
