Third-Party APIs Used We integrated three external APIs, each serving a different purpose: Steam Store API — Our primary data source for game information and pricing. The backend proxies requests to Steam's appdetails and featuredcategories endpoints so the frontend never talks to Steam directly.
1// SteamStoreService.ts — Fetching game details from the Steam Store API
2const STORE_API_BASE: string = "https://store.steampowered.com/api";
3
4public async getAppDetails(appId: string): Promise<SteamAppDetails | null> {
5 const response: Response = await fetch(
6 `${STORE_API_BASE}/appdetails?appids=${encodeURIComponent(appId)}`
7 );
8
9 if (!response.ok) {
10 throw new Error(`Steam Store API error: ${response.status}`);
11 }
12
13 const json = await response.json();
14 const entry = json[appId];
15
16 if (!entry.success || !entry.data) {
17 return null;
18 }
19
20 return entry.data;
21}SteamSpy API — Used alongside the Steam Store API to get community data like review scores, player counts, and genre tags that Steam's own API doesn't expose easily.
1// SteamSpyService.ts — Fetching community stats from SteamSpy
2const STEAMSPY_BASE: string = "https://steamspy.com/api.php";
3
4public async getAppDetails(appId: string): Promise<SteamSpyAppDetails> {
5 const response: Response = await fetch(
6 `${STEAMSPY_BASE}?request=appdetails&appid=${encodeURIComponent(appId)}`
7 );
8
9 if (!response.ok) {
10 throw new Error(`SteamSpy API error: ${response.status}`);
11 }
12
13 return await response.json() as SteamSpyAppDetails;
14}Stripe API — Handles the entire checkout and payment flow. We built a StripeService on the backend that creates Checkout Sessions and verifies payment status, keeping the secret key server-side.
1// StripeService.ts — Creating a Stripe Checkout Session
2public async createCheckoutSession(
3 items: { name: string; price: number; quantity: number }[],
4 successUrl: string,
5 cancelUrl: string
6): Promise<{ checkoutUrl: string; sessionId: string }> {
7 const bodyParams: URLSearchParams = new URLSearchParams();
8 bodyParams.append("mode", "payment");
9 bodyParams.append("success_url", successUrl);
10 bodyParams.append("cancel_url", cancelUrl);
11
12 items.forEach((item, index) => {
13 bodyParams.append(`line_items[${index}][price_data][currency]`, "eur");
14 bodyParams.append(`line_items[${index}][price_data][product_data][name]`, item.name);
15 bodyParams.append(`line_items[${index}][price_data][unit_amount]`,
16 Math.round(item.price * 100).toString());
17 bodyParams.append(`line_items[${index}][quantity]`, item.quantity.toString());
18 });
19
20 const response = await fetch(`${this._stripeApiUrl}/checkout/sessions`, {
21 method: "POST",
22 headers: {
23 "Content-Type": "application/x-www-form-urlencoded",
24 "Authorization": `Bearer ${this.getSecretKey()}`,
25 },
26 body: bodyParams.toString(),
27 });
28
29 const data = await response.json();
30 return { checkoutUrl: data.url, sessionId: data.id };
31}Frontend API Consumption On the frontend, a service layer abstracts all API calls. Game data and prices are fetched in parallel to keep the UI responsive:
1// CurrentGameService.ts — Fetching game detail + prices in parallel
2const [gameResponse, priceResponse] = await Promise.all([
3 fetch(`${VITE_API_URL}games/${gameID}`, {
4 method: "GET",
5 headers: { "Content-Type": "application/json" },
6 credentials: "include",
7 }),
8 fetch(`${VITE_API_URL}products/prices/${gameID}`, {
9 method: "GET",
10 headers: { "Content-Type": "application/json" },
11 }),
12]);