@gamecore-api/sdk 0.2.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 ADDED
@@ -0,0 +1,343 @@
1
+ # @gamecore/sdk
2
+
3
+ TypeScript SDK for GameCore API — zero external dependencies, browser-safe.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @gamecore/sdk
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```typescript
14
+ import { GameCoreClient } from "@gamecore/sdk";
15
+
16
+ const gc = new GameCoreClient({
17
+ apiKey: "gc_live_YOUR_KEY",
18
+ baseUrl: "https://api.gamecore-api.tech",
19
+ onAuthError: () => window.location.href = "/login",
20
+ });
21
+
22
+ // Browse catalog
23
+ const games = await gc.catalog.getGames();
24
+ const game = await gc.catalog.getGame("honkai-star-rail");
25
+ const products = await gc.catalog.getProducts("honkai-star-rail");
26
+
27
+ // Search
28
+ const results = await gc.catalog.search("roblox");
29
+ ```
30
+
31
+ ## Authentication
32
+
33
+ ### Telegram Auth Flow
34
+
35
+ ```typescript
36
+ // 1. Start auth → get bot link
37
+ const { token, botLink } = await gc.auth.initTelegram();
38
+
39
+ // 2. Open Telegram bot (user clicks "Start")
40
+ window.open(botLink, "_blank");
41
+
42
+ // 3. Poll until user authenticates
43
+ const user = await gc.auth.pollTelegramStatus(token);
44
+ console.log("Logged in:", user.firstName);
45
+ ```
46
+
47
+ ### VK Auth
48
+
49
+ ```typescript
50
+ const { user } = await gc.auth.verifyVk(vkAccessToken);
51
+ ```
52
+
53
+ ### Session
54
+
55
+ ```typescript
56
+ const me = await gc.auth.getMe(); // Get current user
57
+ await gc.auth.logout(); // Clear session
58
+ const identities = await gc.auth.getIdentities(); // Linked providers
59
+ ```
60
+
61
+ ## Catalog
62
+
63
+ ```typescript
64
+ // All games
65
+ const games = await gc.catalog.getGames({ type: "game" });
66
+
67
+ // Homepage ranked games
68
+ const homepage = await gc.catalog.getHomepageGames();
69
+
70
+ // Single game with categories
71
+ const game = await gc.catalog.getGame("genshin-impact");
72
+
73
+ // Products (optionally filtered by category)
74
+ const products = await gc.catalog.getProducts("genshin-impact", "crystals");
75
+
76
+ // Products grouped by category
77
+ const grouped = await gc.catalog.getProductsGrouped("genshin-impact");
78
+
79
+ // Search (returns games + products)
80
+ const { games, products } = await gc.catalog.search("roblox");
81
+
82
+ // Search suggestions
83
+ const { suggestions } = await gc.catalog.searchSuggestions("rob");
84
+ ```
85
+
86
+ ## Cart & Checkout
87
+
88
+ ```typescript
89
+ // Cart
90
+ const items = await gc.cart.get();
91
+ await gc.cart.add({ productId: 10, gameId: "roblox", gameName: "Roblox", productName: "800 Robux", price: 799, deliveryData: { username: "player123" } });
92
+ await gc.cart.remove(itemId);
93
+ await gc.cart.clear();
94
+
95
+ // Checkout (auto-generates idempotency key)
96
+ const checkout = await gc.checkout.create({
97
+ items: [{ productId: 10, deliveryData: { username: "player123" } }],
98
+ paymentMethod: "antilopay",
99
+ });
100
+
101
+ // Redirect to payment page
102
+ if (checkout.payment?.paymentUrl) {
103
+ window.location.href = checkout.payment.paymentUrl;
104
+ }
105
+
106
+ // Or pay with balance
107
+ await gc.checkout.completeWithBalance(checkout.payment.code);
108
+ ```
109
+
110
+ ## Orders
111
+
112
+ ```typescript
113
+ const orders = await gc.orders.list();
114
+ const order = await gc.orders.get("ORD-A7X9K2");
115
+ await gc.orders.cancel("ORD-A7X9K2");
116
+
117
+ // Track order in real-time (SSE)
118
+ const source = gc.sse.trackOrder("ORD-A7X9K2");
119
+ source.addEventListener("order_status", (e) => {
120
+ const data = JSON.parse(e.data);
121
+ console.log("Status:", data.status, "Items:", data.items);
122
+ });
123
+ ```
124
+
125
+ ## Profile
126
+
127
+ ```typescript
128
+ // Balance (permanent + bonus with expiration details)
129
+ const balance = await gc.profile.getBalance();
130
+ // { permanent: 500, bonus: 100, total: 600, bonusDetails: [{ remaining: 100, expiresAt: "..." }] }
131
+
132
+ // Level status with progress
133
+ const level = await gc.profile.getLevelStatus();
134
+ // { currentLevel: 3, currentDiscount: 5, nextLevel: 4, requirements: { spending: { current: 5000, required: 10000 } } }
135
+
136
+ // Transaction history
137
+ const transactions = await gc.profile.getTransactions({ limit: 20 });
138
+
139
+ // Orders
140
+ const orders = await gc.profile.getOrders();
141
+
142
+ // Notifications
143
+ const notifications = await gc.profile.getNotifications();
144
+ const { count } = await gc.profile.getUnreadCount();
145
+ await gc.profile.markRead(notificationId);
146
+ await gc.profile.markAllRead();
147
+ ```
148
+
149
+ ## Favorites
150
+
151
+ ```typescript
152
+ const favorites = await gc.favorites.list();
153
+ await gc.favorites.add(productId, "genshin-impact"); // gameId as slug string
154
+ await gc.favorites.remove(productId);
155
+ ```
156
+
157
+ ## Reviews
158
+
159
+ ```typescript
160
+ // Public reviews (paginated)
161
+ const { data, pagination } = await gc.reviews.listPublic({ limit: 10 });
162
+
163
+ // Stats
164
+ const stats = await gc.reviews.getStats("genshin-impact");
165
+ // { averageRating: 4.8, totalReviews: 156 }
166
+
167
+ // Random reviews (for homepage)
168
+ const random = await gc.reviews.getRandom(5);
169
+
170
+ // Submit review (authenticated)
171
+ const review = await gc.reviews.create(orderId, 5, "Great service!");
172
+
173
+ // Orders waiting for review
174
+ const pending = await gc.reviews.getPending();
175
+ ```
176
+
177
+ ## Coupons & Gift Cards
178
+
179
+ ```typescript
180
+ // Apply coupon
181
+ const result = await gc.coupons.apply("WELCOME10");
182
+ // { type: "bonus_balance", value: 10, code: "WELCOME10", bonusAmount: 100 }
183
+
184
+ await gc.coupons.remove();
185
+ const active = await gc.coupons.getActive();
186
+
187
+ // Gift cards
188
+ const card = await gc.giftCards.purchase(5); // amountUsd
189
+ await gc.giftCards.redeem("GC-XXXX-XXXX-XXXX");
190
+ ```
191
+
192
+ ## Referrals
193
+
194
+ ```typescript
195
+ const stats = await gc.referrals.getStats();
196
+ const links = await gc.referrals.getLinks();
197
+ const link = await gc.referrals.createLink({ label: "YouTube", slug: "my-channel" });
198
+ await gc.referrals.updateLink(link.id, { label: "Updated" });
199
+ const linkStats = await gc.referrals.getLinkStats(link.id);
200
+ const commissions = await gc.referrals.getCommissions();
201
+ ```
202
+
203
+ ## Balance Top-up
204
+
205
+ ```typescript
206
+ const methods = await gc.topup.getPaymentMethods();
207
+ const topup = await gc.topup.create(500, "lava");
208
+ // Redirect to topup.paymentUrl
209
+ const status = await gc.topup.getStatus(topup.code);
210
+ ```
211
+
212
+ ## SSE (Real-time Events)
213
+
214
+ ```typescript
215
+ // Authenticated notification stream
216
+ const events = gc.sse.connectEvents();
217
+ events.addEventListener("notification", (e) => {
218
+ const { type, data } = JSON.parse(e.data);
219
+ // type: "order_completed", "balance_updated", "level_up", etc.
220
+ });
221
+
222
+ // Order tracking (no auth, uses order code)
223
+ const tracker = gc.sse.trackOrder("ORD-A7X9K2");
224
+ ```
225
+
226
+ ## SEO
227
+
228
+ ```typescript
229
+ // entityId is numeric (canonical game ID), not slug
230
+ const seo = await gc.seo.getContent("game", 1076, "ru");
231
+ // Schema is available for "product" page type
232
+ const schema = await gc.seo.getSchema("product", 42);
233
+ ```
234
+
235
+ ## Webhook Verification (Server-side)
236
+
237
+ ```typescript
238
+ // Import from server entrypoint (uses node:crypto)
239
+ import { verifyWebhookSignature, parseWebhookPayload } from "@gamecore/sdk/server";
240
+
241
+ const isValid = verifyWebhookSignature(
242
+ requestBody,
243
+ request.headers["x-webhook-signature"],
244
+ WEBHOOK_SECRET,
245
+ );
246
+
247
+ if (isValid) {
248
+ const payload = parseWebhookPayload(requestBody);
249
+ console.log(payload.event, payload.data);
250
+ }
251
+ ```
252
+
253
+ ## Utilities
254
+
255
+ ```typescript
256
+ import { convertPrice, formatPrice, generateIdempotencyKey } from "@gamecore/sdk";
257
+
258
+ const rub = convertPrice(1.99, 92.5); // 184.08
259
+ const formatted = formatPrice(rub, "RUB"); // "184 ₽"
260
+ const key = generateIdempotencyKey(); // UUID v4
261
+ ```
262
+
263
+ ## Next.js App Router Examples
264
+
265
+ ### Server Component (SSR catalog)
266
+
267
+ ```typescript
268
+ // app/catalog/page.tsx
269
+ import { GameCoreClient } from "@gamecore/sdk";
270
+
271
+ const gc = new GameCoreClient({
272
+ apiKey: process.env.GAMECORE_API_KEY!,
273
+ baseUrl: process.env.GAMECORE_API_URL!,
274
+ });
275
+
276
+ export default async function CatalogPage() {
277
+ const games = await gc.catalog.getGames();
278
+ return <GameGrid games={games} />;
279
+ }
280
+ ```
281
+
282
+ ### Client Component (cart)
283
+
284
+ ```typescript
285
+ "use client";
286
+ import { useEffect, useState } from "react";
287
+ import { gc } from "@/lib/gamecore-browser";
288
+
289
+ export function CartWidget() {
290
+ const [items, setItems] = useState([]);
291
+ useEffect(() => { gc.cart.get().then(setItems); }, []);
292
+ return <span>{items.length} items</span>;
293
+ }
294
+ ```
295
+
296
+ ### Route Handler (webhook)
297
+
298
+ ```typescript
299
+ // app/api/webhooks/gamecore/route.ts
300
+ import { verifyWebhookSignature, parseWebhookPayload } from "@gamecore/sdk/server";
301
+
302
+ export async function POST(req: Request) {
303
+ const body = await req.text();
304
+ const sig = req.headers.get("x-webhook-signature") || "";
305
+
306
+ if (!verifyWebhookSignature(body, sig, process.env.WEBHOOK_SECRET!)) {
307
+ return new Response("Unauthorized", { status: 401 });
308
+ }
309
+
310
+ const payload = parseWebhookPayload(body);
311
+ // Handle event...
312
+ return new Response("OK");
313
+ }
314
+ ```
315
+
316
+ ## API Namespaces
317
+
318
+ | Namespace | Methods |
319
+ |-----------|---------|
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 |
324
+ | `gc.checkout` | create, completeWithBalance, getPaymentMethods |
325
+ | `gc.orders` | list, get, getByPayment, cancel |
326
+ | `gc.profile` | getBalance, getLevelStatus, getTransactions, getOrders, getNotifications, getUnreadCount, markRead, markAllRead |
327
+ | `gc.favorites` | list, add, remove |
328
+ | `gc.coupons` | apply, remove, validate, getActive |
329
+ | `gc.referrals` | getStats, getLinks, createLink, updateLink, deleteLink, getLinkStats, getCommissions |
330
+ | `gc.reviews` | listPublic, getStats, getRandom, getMine, getPending, create |
331
+ | `gc.topup` | getPaymentMethods, create, getStatus |
332
+ | `gc.giftCards` | purchase, redeem, check, getMine |
333
+ | `gc.announcements` | list, get |
334
+ | `gc.analytics` | recordView |
335
+ | `gc.seo` | getContent, getSchema |
336
+ | `gc.sse` | connectEvents, trackOrder |
337
+
338
+ ## Browser vs Server
339
+
340
+ | Import | Environment | Includes |
341
+ |--------|-------------|----------|
342
+ | `@gamecore/sdk` | Browser + Node | Client, types, utilities |
343
+ | `@gamecore/sdk/server` | Node only | Webhook verification (uses node:crypto) |
@@ -0,0 +1,251 @@
1
+ import type { TelegramInitResponse, 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, PaginatedResponse } from "./types";
2
+ export interface GameCoreOptions {
3
+ /** Site API key (gc_live_xxx or gc_test_xxx) */
4
+ apiKey: string;
5
+ /** Base URL of GameCore API (e.g. https://api.gamecore-api.tech) */
6
+ baseUrl: string;
7
+ /** Called on 401 — use to redirect to login */
8
+ onAuthError?: () => void;
9
+ }
10
+ export declare class GameCoreClient {
11
+ private apiKey;
12
+ private baseUrl;
13
+ private onAuthError?;
14
+ constructor(options: GameCoreOptions);
15
+ private request;
16
+ site: {
17
+ /** Get site configuration (modules, auth methods, payments, currency) */
18
+ getConfig: () => Promise<SiteConfig>;
19
+ /** Get current exchange rates (USD→RUB and others) */
20
+ getRates: () => Promise<ExchangeRates>;
21
+ /** Get legal document by type (privacy, terms, etc.) */
22
+ getLegal: (type: string, locale?: string) => Promise<LegalDocument>;
23
+ };
24
+ auth: {
25
+ /** Start Telegram auth flow → returns bot link for user to click */
26
+ initTelegram: () => Promise<TelegramInitResponse>;
27
+ /** Poll Telegram auth status until authenticated or expired */
28
+ pollTelegramStatus: (token: string, intervalMs?: number) => Promise<User>;
29
+ /** Verify VK access token and login/register */
30
+ verifyVk: (accessToken: string, ref?: string) => Promise<{
31
+ status: string;
32
+ user: User;
33
+ }>;
34
+ /** Get current authenticated user */
35
+ getMe: () => Promise<User>;
36
+ /** Logout and clear session */
37
+ logout: () => Promise<void>;
38
+ /** List linked auth providers (Telegram, VK) */
39
+ getIdentities: () => Promise<{
40
+ providers: Array<{
41
+ provider: string;
42
+ linked: boolean;
43
+ displayName: string;
44
+ }>;
45
+ }>;
46
+ /** Link VK account to current user */
47
+ linkVk: (accessToken: string) => Promise<void>;
48
+ /** Unlink auth provider */
49
+ unlinkProvider: (provider: string) => Promise<void>;
50
+ };
51
+ catalog: {
52
+ /** Get all games with product counts */
53
+ getGames: (params?: {
54
+ locale?: string;
55
+ type?: string;
56
+ }) => Promise<Game[]>;
57
+ /** Get homepage ranked games */
58
+ getHomepageGames: () => Promise<Game[]>;
59
+ /** Get single game with categories */
60
+ getGame: (slug: string, locale?: string) => Promise<GameDetail>;
61
+ /** Get products for a game */
62
+ getProducts: (gameSlug: string, categorySlug?: string) => Promise<Product[]>;
63
+ /** Get products grouped by category */
64
+ getProductsGrouped: (gameSlug: string) => Promise<{
65
+ category: Category;
66
+ products: Product[];
67
+ }[]>;
68
+ /** Search catalog (returns both games and products) */
69
+ search: (query: string, limit?: number) => Promise<SearchResult>;
70
+ /** Get search suggestions */
71
+ searchSuggestions: (query: string, limit?: number) => Promise<{
72
+ suggestions: string[];
73
+ }>;
74
+ /** Get single product by ID */
75
+ getProduct: (productId: number) => Promise<Product>;
76
+ };
77
+ cart: {
78
+ /** Get cart items for authenticated user */
79
+ get: () => Promise<CartItem[]>;
80
+ /** Add single item to cart */
81
+ add: (item: Omit<CartItem, "id">) => Promise<CartItem>;
82
+ /** Sync localStorage cart to server (replaces server cart) */
83
+ sync: (items: Omit<CartItem, "id">[]) => Promise<CartItem[]>;
84
+ /** Remove item from cart by ID */
85
+ remove: (id: number) => Promise<void>;
86
+ /** Clear all cart items */
87
+ clear: () => Promise<void>;
88
+ };
89
+ checkout: {
90
+ /** Create checkout (payment + orders). Auto-generates idempotency key. */
91
+ create: (data: CheckoutRequest, idempotencyKey?: string) => Promise<CheckoutResponse>;
92
+ /** Complete payment with balance (no external gateway) */
93
+ completeWithBalance: (paymentCode: string) => Promise<{
94
+ success: boolean;
95
+ }>;
96
+ /** Get available payment methods for checkout */
97
+ getPaymentMethods: () => Promise<{
98
+ type: string;
99
+ label: string;
100
+ description?: string;
101
+ feePercent?: number;
102
+ gatewayType?: string;
103
+ }[]>;
104
+ };
105
+ orders: {
106
+ /** List all orders for authenticated user */
107
+ list: () => Promise<Order[]>;
108
+ /** Get single order by code (includes items + payment) */
109
+ get: (code: string) => Promise<Order>;
110
+ /** Get orders by payment code (route is under /checkout prefix) */
111
+ getByPayment: (paymentCode: string) => Promise<{
112
+ payment: unknown;
113
+ orders: Order[];
114
+ }>;
115
+ /** Cancel a pending order */
116
+ cancel: (code: string) => Promise<{
117
+ status: string;
118
+ }>;
119
+ };
120
+ profile: {
121
+ /** Get user balance (permanent + bonus with details) */
122
+ getBalance: () => Promise<UserBalance>;
123
+ /** Get level status with progress toward next level */
124
+ getLevelStatus: () => Promise<LevelStatus>;
125
+ /** Get balance transaction history */
126
+ getTransactions: (params?: {
127
+ limit?: number;
128
+ offset?: number;
129
+ }) => Promise<Transaction[]>;
130
+ /** Get user's orders */
131
+ getOrders: () => Promise<Order[]>;
132
+ /** Get notifications */
133
+ getNotifications: () => Promise<Notification[]>;
134
+ /** Get unread notification count */
135
+ getUnreadCount: () => Promise<{
136
+ count: number;
137
+ }>;
138
+ /** Mark notification as read */
139
+ markRead: (id: number) => Promise<void>;
140
+ /** Mark all notifications as read */
141
+ markAllRead: () => Promise<void>;
142
+ };
143
+ favorites: {
144
+ /** List user's favorite products */
145
+ list: () => Promise<Favorite[]>;
146
+ /** Add product to favorites */
147
+ add: (productId: number, gameId: string) => Promise<{
148
+ id: number;
149
+ }>;
150
+ /** Remove product from favorites */
151
+ remove: (productId: number) => Promise<void>;
152
+ };
153
+ coupons: {
154
+ /** Apply coupon code (sets active coupon for checkout) */
155
+ apply: (code: string) => Promise<CouponResult>;
156
+ /** Remove active coupon */
157
+ remove: () => Promise<void>;
158
+ /** Validate coupon without applying */
159
+ validate: (code: string, gameId?: string) => Promise<CouponResult>;
160
+ /** Get user's currently active coupon */
161
+ getActive: () => Promise<CouponResult | null>;
162
+ };
163
+ referrals: {
164
+ /** Get referral dashboard stats */
165
+ getStats: () => Promise<ReferralStats>;
166
+ /** List user's referral links */
167
+ getLinks: () => Promise<ReferralLink[]>;
168
+ /** Create new referral link */
169
+ createLink: (data?: {
170
+ label?: string;
171
+ slug?: string;
172
+ targetUrl?: string;
173
+ }) => Promise<ReferralLink>;
174
+ /** Update referral link */
175
+ updateLink: (id: number, data: {
176
+ label?: string;
177
+ slug?: string;
178
+ targetUrl?: string;
179
+ }) => Promise<void>;
180
+ /** Delete referral link */
181
+ deleteLink: (id: number) => Promise<void>;
182
+ /** Get stats for a specific link */
183
+ getLinkStats: (id: number) => Promise<{
184
+ link: ReferralLink;
185
+ commissions: ReferralCommission[];
186
+ totalCommission: number;
187
+ }>;
188
+ /** Get commission history */
189
+ getCommissions: () => Promise<ReferralCommission[]>;
190
+ };
191
+ reviews: {
192
+ /** Get public reviews (paginated) */
193
+ listPublic: (params?: {
194
+ gameSlug?: string;
195
+ limit?: number;
196
+ offset?: number;
197
+ }) => Promise<PaginatedResponse<Review>>;
198
+ /** Get review stats (average rating, total count) */
199
+ getStats: (gameSlug?: string) => Promise<ReviewStats>;
200
+ /** Get random reviews for homepage */
201
+ getRandom: (limit?: number) => Promise<Review[]>;
202
+ /** Get user's own reviews */
203
+ getMine: () => Promise<Review[]>;
204
+ /** Get orders eligible for review */
205
+ getPending: () => Promise<Order[]>;
206
+ /** Submit review for a completed order */
207
+ create: (orderId: number, rating: number, text?: string) => Promise<Review>;
208
+ };
209
+ topup: {
210
+ /** Get available topup payment methods */
211
+ getPaymentMethods: () => Promise<TopupMethod[]>;
212
+ /** Create topup (returns payment URL for redirect) */
213
+ create: (amount: number, paymentMethod?: string) => Promise<TopupResponse>;
214
+ /** Get topup status by code */
215
+ getStatus: (code: string) => Promise<TopupStatus>;
216
+ };
217
+ giftCards: {
218
+ /** Purchase a gift card (amount in USD) */
219
+ purchase: (amountUsd: number) => Promise<GiftCard>;
220
+ /** Redeem a gift card code */
221
+ redeem: (code: string) => Promise<{
222
+ amount: number;
223
+ }>;
224
+ /** Check gift card status */
225
+ check: (code: string) => Promise<GiftCard>;
226
+ /** Get user's purchased gift cards */
227
+ getMine: () => Promise<GiftCard[]>;
228
+ };
229
+ announcements: {
230
+ /** List published announcements */
231
+ list: (limit?: number) => Promise<Announcement[]>;
232
+ /** Get single announcement */
233
+ get: (id: number) => Promise<Announcement>;
234
+ };
235
+ sse: {
236
+ /** Connect to authenticated notification stream. Returns EventSource. */
237
+ connectEvents: () => EventSource;
238
+ /** Track order status by code (no auth required). Returns EventSource. */
239
+ trackOrder: (orderCode: string) => EventSource;
240
+ };
241
+ analytics: {
242
+ /** Record game page view (for ranking) */
243
+ recordView: (gameId: string) => Promise<void>;
244
+ };
245
+ seo: {
246
+ /** Get SEO content for a page */
247
+ getContent: (pageType: string, entityId: string | number, locale?: string) => Promise<Record<string, unknown>>;
248
+ /** Get schema.org JSON-LD for a page */
249
+ getSchema: (pageType: string, entityId: string | number) => Promise<Record<string, unknown>>;
250
+ };
251
+ }
@@ -0,0 +1,4 @@
1
+ export { GameCoreClient } from "./client";
2
+ export type { GameCoreOptions } from "./client";
3
+ export * from "./types";
4
+ export { convertPrice, formatPrice, generateIdempotencyKey } from "./utils";
package/dist/index.js ADDED
@@ -0,0 +1,245 @@
1
+ // src/types.ts
2
+ class GameCoreError extends Error {
3
+ status;
4
+ code;
5
+ constructor(message, status, code) {
6
+ super(message);
7
+ this.name = "GameCoreError";
8
+ this.status = status;
9
+ this.code = code;
10
+ }
11
+ }
12
+
13
+ // src/utils.ts
14
+ function convertPrice(priceUsd, rate) {
15
+ return Math.round(priceUsd * rate * 100) / 100;
16
+ }
17
+ function formatPrice(amount, currency = "RUB") {
18
+ if (currency === "RUB") {
19
+ const formatted = Math.round(amount).toString().replace(/\B(?=(\d{3})+(?!\d))/g, " ");
20
+ return `${formatted} ₽`;
21
+ }
22
+ if (currency === "USD") {
23
+ return `$${amount.toFixed(2)}`;
24
+ }
25
+ if (currency === "EUR") {
26
+ return `€${amount.toFixed(2)}`;
27
+ }
28
+ return `${amount.toFixed(2)} ${currency}`;
29
+ }
30
+ function generateIdempotencyKey() {
31
+ return crypto.randomUUID();
32
+ }
33
+
34
+ // src/client.ts
35
+ class GameCoreClient {
36
+ apiKey;
37
+ baseUrl;
38
+ onAuthError;
39
+ constructor(options) {
40
+ this.apiKey = options.apiKey;
41
+ this.baseUrl = options.baseUrl.replace(/\/$/, "");
42
+ this.onAuthError = options.onAuthError;
43
+ }
44
+ async request(method, path, body, options) {
45
+ const headers = {
46
+ "X-Api-Key": this.apiKey,
47
+ "Content-Type": "application/json"
48
+ };
49
+ if (options?.idempotencyKey) {
50
+ headers["X-Idempotency-Key"] = options.idempotencyKey;
51
+ }
52
+ const res = await fetch(`${this.baseUrl}${path}`, {
53
+ method,
54
+ headers,
55
+ body: body ? JSON.stringify(body) : undefined,
56
+ credentials: "include"
57
+ });
58
+ if (res.status === 401) {
59
+ this.onAuthError?.();
60
+ throw new GameCoreError("Unauthorized", 401, "UNAUTHORIZED");
61
+ }
62
+ if (res.status === 429) {
63
+ throw new GameCoreError("Rate limit exceeded", 429, "RATE_LIMITED");
64
+ }
65
+ if (!res.ok) {
66
+ const data = await res.json().catch(() => ({}));
67
+ throw new GameCoreError(data.error || `HTTP ${res.status}`, res.status, data.code);
68
+ }
69
+ const json = await res.json();
70
+ if (!options?.rawResponse && json && typeof json === "object" && "success" in json && "data" in json) {
71
+ return json.data;
72
+ }
73
+ return json;
74
+ }
75
+ site = {
76
+ getConfig: () => this.request("GET", "/site/config"),
77
+ getRates: () => this.request("GET", "/rates"),
78
+ getLegal: (type, locale) => this.request("GET", `/legal/${type}${locale ? `?locale=${locale}` : ""}`)
79
+ };
80
+ auth = {
81
+ initTelegram: () => this.request("POST", "/auth/telegram/init"),
82
+ pollTelegramStatus: (token, intervalMs = 2000) => new Promise((resolve, reject) => {
83
+ const poll = setInterval(async () => {
84
+ try {
85
+ const res = await this.request("GET", `/auth/telegram/status/${token}`, undefined, { rawResponse: true });
86
+ if (res.status === "authenticated" && res.user) {
87
+ clearInterval(poll);
88
+ resolve(res.user);
89
+ } else if (res.status === "expired" || res.status === "used") {
90
+ clearInterval(poll);
91
+ reject(new GameCoreError("Auth token expired", 410, "TOKEN_EXPIRED"));
92
+ }
93
+ } catch (err) {
94
+ clearInterval(poll);
95
+ reject(err);
96
+ }
97
+ }, intervalMs);
98
+ }),
99
+ verifyVk: (accessToken, ref) => this.request("POST", "/auth/vk/verify", { accessToken, ref }, { rawResponse: true }),
100
+ getMe: () => this.request("GET", "/auth/me", undefined, { rawResponse: true }),
101
+ logout: () => this.request("POST", "/auth/logout"),
102
+ getIdentities: () => this.request("GET", "/auth/identities", undefined, { rawResponse: true }),
103
+ linkVk: (accessToken) => this.request("POST", "/auth/link/vk", { accessToken }),
104
+ unlinkProvider: (provider) => this.request("POST", `/auth/unlink/${provider}`)
105
+ };
106
+ catalog = {
107
+ getGames: (params) => {
108
+ const qs = new URLSearchParams;
109
+ if (params?.locale)
110
+ qs.set("locale", params.locale);
111
+ if (params?.type)
112
+ qs.set("type", params.type);
113
+ const q = qs.toString();
114
+ return this.request("GET", `/catalog/games${q ? `?${q}` : ""}`);
115
+ },
116
+ getHomepageGames: () => this.request("GET", "/catalog/homepage-games"),
117
+ getGame: (slug, locale) => {
118
+ const qs = locale ? `?locale=${locale}` : "";
119
+ return this.request("GET", `/catalog/games/${slug}${qs}`);
120
+ },
121
+ getProducts: (gameSlug, categorySlug) => {
122
+ const qs = categorySlug ? `?category=${categorySlug}` : "";
123
+ return this.request("GET", `/catalog/games/${gameSlug}/products${qs}`);
124
+ },
125
+ getProductsGrouped: (gameSlug) => this.request("GET", `/catalog/games/${gameSlug}/products/grouped`),
126
+ search: (query, limit = 20) => this.request("GET", `/catalog/search?q=${encodeURIComponent(query)}&limit=${limit}`),
127
+ searchSuggestions: (query, limit = 5) => this.request("GET", `/catalog/search/suggestions?q=${encodeURIComponent(query)}&limit=${limit}`),
128
+ getProduct: (productId) => this.request("GET", `/catalog/products/${productId}`)
129
+ };
130
+ cart = {
131
+ get: () => this.request("GET", "/cart"),
132
+ add: (item) => this.request("POST", "/cart", item),
133
+ sync: (items) => this.request("POST", "/cart/sync", { items }),
134
+ remove: (id) => this.request("DELETE", `/cart/${id}`),
135
+ clear: () => this.request("DELETE", "/cart")
136
+ };
137
+ checkout = {
138
+ create: (data, idempotencyKey) => this.request("POST", "/checkout", data, {
139
+ idempotencyKey: idempotencyKey || generateIdempotencyKey()
140
+ }),
141
+ completeWithBalance: (paymentCode) => this.request("POST", `/checkout/${paymentCode}/complete`),
142
+ getPaymentMethods: () => this.request("GET", "/payment-methods")
143
+ };
144
+ orders = {
145
+ list: () => this.request("GET", "/orders"),
146
+ get: (code) => this.request("GET", `/orders/${code}`),
147
+ getByPayment: (paymentCode) => this.request("GET", `/checkout/orders/payment/${paymentCode}`, undefined, { rawResponse: true }),
148
+ cancel: (code) => this.request("POST", `/orders/${code}/cancel`)
149
+ };
150
+ profile = {
151
+ getBalance: () => this.request("GET", "/profile/balance"),
152
+ getLevelStatus: () => this.request("GET", "/profile/level-status"),
153
+ getTransactions: (params) => {
154
+ const qs = new URLSearchParams;
155
+ if (params?.limit)
156
+ qs.set("limit", String(params.limit));
157
+ if (params?.offset)
158
+ qs.set("offset", String(params.offset));
159
+ const q = qs.toString();
160
+ return this.request("GET", `/profile/balance/transactions${q ? `?${q}` : ""}`);
161
+ },
162
+ getOrders: () => this.request("GET", "/profile/orders"),
163
+ getNotifications: () => this.request("GET", "/profile/notifications"),
164
+ getUnreadCount: () => this.request("GET", "/profile/notifications/unread-count"),
165
+ markRead: (id) => this.request("POST", `/profile/notifications/${id}/read`),
166
+ markAllRead: () => this.request("POST", "/profile/notifications/read-all")
167
+ };
168
+ favorites = {
169
+ list: () => this.request("GET", "/favorites"),
170
+ add: (productId, gameId) => this.request("POST", "/favorites", { productId, gameId }),
171
+ remove: (productId) => this.request("DELETE", `/favorites/${productId}`)
172
+ };
173
+ coupons = {
174
+ apply: (code) => this.request("POST", "/coupons/apply", { code }),
175
+ remove: () => this.request("DELETE", "/coupons/active"),
176
+ validate: (code, gameId) => this.request("POST", "/coupons/validate", { code, gameId }),
177
+ getActive: () => this.request("GET", "/profile/active-coupon")
178
+ };
179
+ referrals = {
180
+ getStats: () => this.request("GET", "/referral/stats"),
181
+ getLinks: () => this.request("GET", "/referral/links"),
182
+ createLink: (data) => this.request("POST", "/referral/links", data),
183
+ updateLink: (id, data) => this.request("PATCH", `/referral/links/${id}`, data),
184
+ deleteLink: (id) => this.request("DELETE", `/referral/links/${id}`),
185
+ getLinkStats: (id) => this.request("GET", `/referral/links/${id}/stats`),
186
+ getCommissions: () => this.request("GET", "/referral/commissions")
187
+ };
188
+ reviews = {
189
+ listPublic: (params) => {
190
+ const qs = new URLSearchParams;
191
+ if (params?.gameSlug)
192
+ qs.set("gameSlug", params.gameSlug);
193
+ if (params?.limit)
194
+ qs.set("limit", String(params.limit));
195
+ if (params?.offset)
196
+ qs.set("offset", String(params.offset));
197
+ const q = qs.toString();
198
+ return this.request("GET", `/reviews/public${q ? `?${q}` : ""}`, undefined, { rawResponse: true });
199
+ },
200
+ getStats: (gameSlug) => {
201
+ const qs = gameSlug ? `?gameSlug=${gameSlug}` : "";
202
+ return this.request("GET", `/reviews/public/stats${qs}`);
203
+ },
204
+ getRandom: (limit = 5) => this.request("GET", `/reviews/public/random?limit=${limit}`),
205
+ getMine: () => this.request("GET", "/reviews/mine"),
206
+ getPending: () => this.request("GET", "/reviews/orders-without-reviews"),
207
+ create: (orderId, rating, text) => this.request("POST", "/reviews", { orderId, rating, text })
208
+ };
209
+ topup = {
210
+ getPaymentMethods: () => this.request("GET", "/topup/payment-methods"),
211
+ create: (amount, paymentMethod) => this.request("POST", "/topup", { amount, paymentMethod }),
212
+ getStatus: (code) => this.request("GET", `/topup/${code}`)
213
+ };
214
+ giftCards = {
215
+ purchase: (amountUsd) => this.request("POST", "/gift-cards/purchase", { amountUsd }),
216
+ redeem: (code) => this.request("POST", "/gift-cards/redeem", { code }),
217
+ check: (code) => this.request("GET", `/gift-cards/check?code=${code}`),
218
+ getMine: () => this.request("GET", "/profile/gift-cards")
219
+ };
220
+ announcements = {
221
+ list: (limit = 10) => this.request("GET", `/announcements?limit=${limit}`),
222
+ get: (id) => this.request("GET", `/announcements/${id}`)
223
+ };
224
+ sse = {
225
+ connectEvents: () => new EventSource(`${this.baseUrl}/sse/events`, { withCredentials: true }),
226
+ trackOrder: (orderCode) => new EventSource(`${this.baseUrl}/sse/orders/${orderCode}`)
227
+ };
228
+ analytics = {
229
+ recordView: (gameId) => this.request("POST", "/analytics/game-view", { gameId })
230
+ };
231
+ seo = {
232
+ getContent: (pageType, entityId, locale) => {
233
+ const qs = locale ? `?locale=${locale}` : "";
234
+ return this.request("GET", `/seo/${pageType}/${entityId}${qs}`, undefined, { rawResponse: true });
235
+ },
236
+ getSchema: (pageType, entityId) => this.request("GET", `/seo/schema/${pageType}/${entityId}`, undefined, { rawResponse: true })
237
+ };
238
+ }
239
+ export {
240
+ generateIdempotencyKey,
241
+ formatPrice,
242
+ convertPrice,
243
+ GameCoreError,
244
+ GameCoreClient
245
+ };
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Server-side entrypoint for @gamecore/sdk.
3
+ * Includes webhook verification (requires node:crypto).
4
+ *
5
+ * Usage: import { verifyWebhookSignature } from "@gamecore/sdk/server"
6
+ */
7
+ export { verifyWebhookSignature, parseWebhookPayload } from "./webhooks";
package/dist/server.js ADDED
@@ -0,0 +1,29 @@
1
+ // src/webhooks.ts
2
+ import { createHmac, timingSafeEqual } from "node:crypto";
3
+ function verifyWebhookSignature(payload, signature, secret, maxAgeSeconds = 300) {
4
+ const expected = `sha256=${createHmac("sha256", secret).update(payload).digest("hex")}`;
5
+ const sigBuf = Buffer.from(signature);
6
+ const expectedBuf = Buffer.from(expected);
7
+ if (sigBuf.length !== expectedBuf.length)
8
+ return false;
9
+ if (!timingSafeEqual(sigBuf, expectedBuf))
10
+ return false;
11
+ if (maxAgeSeconds > 0) {
12
+ try {
13
+ const parsed = JSON.parse(payload);
14
+ if (parsed.timestamp) {
15
+ const age = (Date.now() - new Date(parsed.timestamp).getTime()) / 1000;
16
+ if (age > maxAgeSeconds)
17
+ return false;
18
+ }
19
+ } catch {}
20
+ }
21
+ return true;
22
+ }
23
+ function parseWebhookPayload(body) {
24
+ return JSON.parse(body);
25
+ }
26
+ export {
27
+ verifyWebhookSignature,
28
+ parseWebhookPayload
29
+ };
@@ -0,0 +1,353 @@
1
+ export interface TelegramInitResponse {
2
+ success: boolean;
3
+ token: string;
4
+ botLink: string;
5
+ expiresIn: number;
6
+ }
7
+ export interface AuthStatusResponse {
8
+ status: "pending" | "verified" | "authenticated" | "expired" | "used";
9
+ user?: User;
10
+ }
11
+ export interface User {
12
+ id: number;
13
+ firstName: string;
14
+ lastName?: string;
15
+ username?: string;
16
+ photoUrl?: string;
17
+ email?: string;
18
+ role: string;
19
+ level: number;
20
+ telegramId?: string;
21
+ vkId?: string;
22
+ }
23
+ export interface SiteConfig {
24
+ apiVersion: string;
25
+ site: {
26
+ slug: string;
27
+ name: string;
28
+ };
29
+ modules: Record<string, boolean>;
30
+ auth: string[];
31
+ payments: string[];
32
+ displayCurrency: string;
33
+ rateMode: "auto" | "manual";
34
+ currentRate: number;
35
+ supportedLocales?: string[];
36
+ }
37
+ export interface ExchangeRates {
38
+ usdToRub: number;
39
+ base: string;
40
+ updatedAt: string;
41
+ rates: Record<string, number>;
42
+ }
43
+ export interface LegalDocument {
44
+ title: string;
45
+ content: string;
46
+ version?: string;
47
+ locale?: string;
48
+ updatedAt: string;
49
+ }
50
+ export interface Game {
51
+ id: number;
52
+ slug: string;
53
+ name: string;
54
+ icon: string | null;
55
+ localIcon?: string | null;
56
+ productCount: number;
57
+ minPrice?: number | null;
58
+ type?: string;
59
+ }
60
+ export interface GameDetail {
61
+ id: number;
62
+ slug: string;
63
+ name: string;
64
+ icon: string | null;
65
+ localIcon?: string | null;
66
+ description: string | null;
67
+ categories: Category[];
68
+ }
69
+ export interface Category {
70
+ id: number;
71
+ slug: string;
72
+ name: string;
73
+ productCount: number;
74
+ }
75
+ export interface Product {
76
+ id: number;
77
+ name: string;
78
+ icon: string | null;
79
+ price: number | null;
80
+ priceWithoutDiscount?: number;
81
+ discountPercent?: number;
82
+ gameId: string;
83
+ gameName: string;
84
+ gameIcon: string | null;
85
+ categoryId: number;
86
+ categoryName: string;
87
+ productType: string;
88
+ deliveryDataSchema: unknown[];
89
+ region?: string;
90
+ }
91
+ export interface SearchResult {
92
+ games: Game[];
93
+ products: Product[];
94
+ }
95
+ export interface CartItem {
96
+ id?: number;
97
+ productId: number;
98
+ gameId: string;
99
+ gameName: string;
100
+ productName: string;
101
+ price: number;
102
+ deliveryData: Record<string, string>;
103
+ }
104
+ export interface CheckoutRequest {
105
+ email?: string;
106
+ items: Array<{
107
+ productId: number;
108
+ gameId?: string;
109
+ gameName?: string;
110
+ productName?: string;
111
+ amount?: number;
112
+ deliveryData?: Record<string, string>;
113
+ }>;
114
+ paymentMethod?: string;
115
+ couponCode?: string;
116
+ }
117
+ export interface CheckoutResponse {
118
+ payment?: {
119
+ code: string;
120
+ total: number;
121
+ paymentUrl?: string;
122
+ status?: string;
123
+ };
124
+ orders?: Array<{
125
+ code: string;
126
+ status: string;
127
+ }>;
128
+ }
129
+ export type OrderStatus = "pending" | "paid" | "processing" | "completed" | "cancelled" | "refunded" | "failed";
130
+ export interface Order {
131
+ id: number;
132
+ code: string;
133
+ status: OrderStatus;
134
+ totalAmount: number;
135
+ gameName: string;
136
+ gameId: string;
137
+ paymentCode?: string;
138
+ items: OrderItem[];
139
+ payment?: {
140
+ code: string;
141
+ status: string;
142
+ totalAmount: number;
143
+ };
144
+ createdAt: string;
145
+ completedAt?: string;
146
+ }
147
+ export interface OrderItem {
148
+ id: number;
149
+ productId: number;
150
+ productName: string;
151
+ gameName: string;
152
+ price: number;
153
+ amount: number;
154
+ status?: string;
155
+ supplierStatus?: string;
156
+ cdKey?: string;
157
+ deliveryData: Record<string, unknown>;
158
+ }
159
+ export interface UserBalance {
160
+ permanent: number;
161
+ bonus: number;
162
+ total: number;
163
+ level: number;
164
+ levelDiscount: number;
165
+ totalSpent: number;
166
+ bonusDetails: Array<{
167
+ id: number;
168
+ remaining: number;
169
+ expiresAt: string;
170
+ }>;
171
+ }
172
+ export interface LevelStatus {
173
+ currentLevel: number;
174
+ currentDiscount: number;
175
+ nextLevel: number | null;
176
+ nextDiscount: number | null;
177
+ isMaxLevel: boolean;
178
+ requirements?: {
179
+ spending?: {
180
+ current: number;
181
+ required: number;
182
+ met: boolean;
183
+ };
184
+ reviews?: {
185
+ current: number;
186
+ required: number;
187
+ met: boolean;
188
+ };
189
+ referrals?: {
190
+ current: number;
191
+ required: number;
192
+ met: boolean;
193
+ };
194
+ };
195
+ }
196
+ export interface Transaction {
197
+ id: number;
198
+ type: string;
199
+ amount: number;
200
+ description: string;
201
+ createdAt: string;
202
+ }
203
+ export interface Notification {
204
+ id: number;
205
+ type: string;
206
+ title: string;
207
+ body: string;
208
+ isRead: boolean;
209
+ data?: Record<string, unknown>;
210
+ createdAt: string;
211
+ }
212
+ export interface Favorite {
213
+ id: number;
214
+ productId: number;
215
+ productName: string;
216
+ productSlug?: string;
217
+ productIcon?: string | null;
218
+ price: number;
219
+ gameId: string;
220
+ gameSlug?: string;
221
+ gameName: string;
222
+ gameIcon?: string | null;
223
+ categorySlug?: string;
224
+ createdAt: string;
225
+ }
226
+ export interface Review {
227
+ id: number;
228
+ userId: number;
229
+ userName?: string;
230
+ orderId: number;
231
+ orderCode?: string;
232
+ rating: number;
233
+ text?: string;
234
+ gameName?: string;
235
+ bonus?: {
236
+ amount: number;
237
+ percent: number;
238
+ expiresAt: string;
239
+ };
240
+ createdAt: string;
241
+ }
242
+ export interface ReviewStats {
243
+ averageRating: number;
244
+ totalCount: number;
245
+ distribution?: Record<string, number>;
246
+ }
247
+ export interface CouponResult {
248
+ type: string;
249
+ value: number;
250
+ code: string;
251
+ gameId?: number | null;
252
+ bonusAmount?: number;
253
+ }
254
+ export interface ReferralStats {
255
+ totalReferrals: number;
256
+ lifetimeEarnings: number;
257
+ commissionRate: number;
258
+ commissionBasis: string;
259
+ links?: ReferralLink[];
260
+ }
261
+ export interface ReferralLink {
262
+ id: number;
263
+ code: string;
264
+ slug?: string | null;
265
+ label?: string;
266
+ targetUrl?: string | null;
267
+ clicks: number;
268
+ registrations: number;
269
+ url?: string;
270
+ createdAt: string;
271
+ }
272
+ export interface ReferralCommission {
273
+ id: number;
274
+ referredUserId: number;
275
+ orderId: number;
276
+ orderAmount: number;
277
+ commissionAmount: number;
278
+ commissionPercent: number;
279
+ createdAt: string;
280
+ }
281
+ export interface TopupMethod {
282
+ type: string;
283
+ label: string;
284
+ description?: string;
285
+ feePercent?: number;
286
+ gatewayType?: string;
287
+ invoiceCurrency?: string;
288
+ }
289
+ export interface TopupResponse {
290
+ code: string;
291
+ amount: number;
292
+ paymentUrl?: string;
293
+ }
294
+ export interface TopupStatus {
295
+ code: string;
296
+ status: string;
297
+ amount: number;
298
+ createdAt?: string;
299
+ }
300
+ export interface GiftCard {
301
+ id: number;
302
+ code: string;
303
+ denomination: number;
304
+ status: string;
305
+ createdAt: string;
306
+ redeemedAt?: string;
307
+ }
308
+ export interface Announcement {
309
+ id: number;
310
+ title: string;
311
+ body: string;
312
+ type?: string;
313
+ imageUrl?: string | null;
314
+ createdAt: string;
315
+ }
316
+ export interface OrderUpdateEvent {
317
+ orderId: number;
318
+ orderCode: string;
319
+ status: string;
320
+ items?: Array<{
321
+ productName: string;
322
+ status: string;
323
+ cdKey?: string;
324
+ }>;
325
+ }
326
+ export type WebhookEvent = "order.created" | "order.completed" | "order.failed" | "order.cancelled" | "payment.received" | "payment.failed" | "user.registered" | "catalog.updated";
327
+ export interface WebhookPayload {
328
+ event: WebhookEvent;
329
+ timestamp: string;
330
+ siteId: number;
331
+ sandbox: boolean;
332
+ data: Record<string, unknown>;
333
+ }
334
+ /** Standard API response wrapper. Most endpoints return { success, data } or { success, error }. */
335
+ export interface ApiResponse<T = unknown> {
336
+ success: boolean;
337
+ data?: T;
338
+ error?: string;
339
+ }
340
+ export interface PaginatedResponse<T> {
341
+ data: T[];
342
+ pagination: {
343
+ limit: number;
344
+ offset: number;
345
+ total: number;
346
+ hasMore: boolean;
347
+ };
348
+ }
349
+ export declare class GameCoreError extends Error {
350
+ status: number;
351
+ code?: string;
352
+ constructor(message: string, status: number, code?: string);
353
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Convert a price from USD to target currency.
3
+ */
4
+ export declare function convertPrice(priceUsd: number, rate: number): number;
5
+ /**
6
+ * Format a price with currency symbol.
7
+ * Examples: "1 234 ₽", "$12.34", "€12.34"
8
+ */
9
+ export declare function formatPrice(amount: number, currency?: string): string;
10
+ /**
11
+ * Generate a unique idempotency key for checkout requests.
12
+ */
13
+ export declare function generateIdempotencyKey(): string;
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Webhook verification utilities — SERVER-SIDE ONLY.
3
+ * Uses node:crypto, do not import in browser bundles.
4
+ *
5
+ * Import from "@gamecore/sdk/server" instead of "@gamecore/sdk".
6
+ */
7
+ import type { WebhookPayload } from "./types";
8
+ /**
9
+ * Verify webhook signature (HMAC-SHA256) with timing-safe comparison.
10
+ * Use on your server to validate incoming webhooks from GameCore.
11
+ *
12
+ * @param payload - Raw request body string
13
+ * @param signature - Value of X-Webhook-Signature header
14
+ * @param secret - Your webhook secret from site settings
15
+ * @param maxAgeSeconds - Max age of webhook (default 300 = 5 minutes). Set to 0 to disable.
16
+ */
17
+ export declare function verifyWebhookSignature(payload: string, signature: string, secret: string, maxAgeSeconds?: number): boolean;
18
+ /**
19
+ * Parse a webhook payload string into a typed object.
20
+ */
21
+ export declare function parseWebhookPayload(body: string): WebhookPayload;
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "@gamecore-api/sdk",
3
+ "version": "0.2.0",
4
+ "description": "TypeScript SDK for GameCore API — browser-safe, zero dependencies",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "module": "dist/index.mjs",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "require": "./dist/index.js",
12
+ "types": "./dist/index.d.ts"
13
+ },
14
+ "./server": {
15
+ "import": "./dist/server.js",
16
+ "require": "./dist/server.js",
17
+ "types": "./dist/server.d.ts"
18
+ }
19
+ },
20
+ "files": ["dist"],
21
+ "scripts": {
22
+ "build": "bun build src/index.ts --outdir dist --target browser && bun build src/server.ts --outdir dist --target node && bun x tsc --emitDeclarationOnly",
23
+ "typecheck": "bun x tsc --noEmit"
24
+ },
25
+ "devDependencies": {
26
+ "@types/node": "^25.5.0",
27
+ "typescript": "^5.0.0"
28
+ },
29
+ "keywords": ["gamecore", "game-donation", "sdk", "api-client", "typescript"],
30
+ "license": "MIT"
31
+ }