Test Implementation Guide
This guide explains how to try outReview the API specification and prepare the required APIs (purchase eligibility check(purchase eligibility check) and POST /register(purchase registration) with a simple test implementation for integrating with Game8 Store.
Estimated time required:15 minutes
If you use an AI coding tool, follow the steps in AI Agent Integration (llms.txt / MCP) to load this documentation, and you can implement while referring to the specifications.
Implementing the Endpoints (Approx. 10 minutes)
Required Environment
- Use Node.js (Express) or Python (FastAPI)
- A server that can be deployed locally or to the cloud (AWS, GCP, Vercel, Render, etc.)
Implementation in Node.js (Express)
const express = require("express");
const cors = require("cors");
const crypto = require("crypto");
const app = express();
// Keep the raw request body for signature verification
app.use(express.json({ verify: (req, res, buf) => { req.rawBody = buf.toString("utf8"); } }));
app.use(cors());
const VALID_ACCESS_TOKEN = "your_access_token";
const SECRET_KEY = "your_secret_key";
// Authentication middleware
app.use((req, res, next) => {
const authHeader = req.headers.authorization;
if (!authHeader) {
return res.status(401).json({ result_code: "PUB1001", message: "Missing authentication credentials" });
}
const [scheme, token] = authHeader.split(" ");
if (scheme !== "Bearer" || token !== VALID_ACCESS_TOKEN) {
return res.status(401).json({ result_code: "PUB1002", message: "Invalid access token" });
}
next();
});
// X-Signature verification middleware (GET uses the query string, POST uses the request body as is)
const verifySignature = (req, res, next) => {
const signature = req.headers["x-signature"];
if (!signature) {
return res.status(400).json({ result_code: "PUB1004", message: "Missing signature" });
}
const payload = req.method === "GET"
? (req.originalUrl.split("?")[1] || "")
: (req.rawBody || "");
const computedSignature = crypto.createHmac("sha256", SECRET_KEY).update(payload).digest("base64");
if (signature !== computedSignature) {
return res.status(400).json({ result_code: "PUB1004", message: "Invalid signature" });
}
next();
};
const isInt = (v) => v !== undefined && v !== null && Number.isInteger(Number(v));
// Purchase eligibility check (with item reservation support)
app.get("/check", verifySignature, (req, res) => {
const { game, user, transaction_id, item, item_category, price, selling_price, billing_amount } = req.query;
if (!game || !user || !transaction_id || !item || !["paid", "free"].includes(item_category)
|| !isInt(price) || !isInt(selling_price) || !isInt(billing_amount)) {
return res.status(400).json({ result_code: "PUB2001", message: "Missing required parameters or invalid values" });
}
res.json({
request_id: "abc123",
timestamp: new Date().toISOString(),
result_code: "PUB0000",
message: "Purchase allowed",
purchasable: "available",
age_category: "under_18",
account_limit: { total: 10000, remaining: 5000, reset_at: null },
stock: null,
requested_price: Number(price)
});
});
// Purchase registration
app.post("/register", verifySignature, (req, res) => {
try {
const { game, user, item, transaction_id, item_category, item_name,
price, selling_price, billing_amount, purchased_at, contents } = req.body;
// ref・payment_method are required keys that accept null, so only check that the keys exist
const hasNullable = "ref" in req.body && "payment_method" in req.body;
if (!game || !user || !item || !transaction_id || !["paid", "free"].includes(item_category) || !item_name
|| !isInt(price) || !isInt(selling_price) || !isInt(billing_amount) || !purchased_at || !hasNullable
|| !Array.isArray(contents) || contents.length === 0) {
return res.status(400).json({ result_code: "PUB2001", message: "Missing required parameters or invalid values" });
}
// Grant the item here based on contents(content_id / content_name / quantity)
res.json({
request_id: "def456",
timestamp: new Date().toISOString(),
result_code: "PUB0000",
message: "The purchase record has been registered.",
item_granted: true
});
} catch (error) {
res.status(500).json({ result_code: "PUB5000", message: "Internal server error" });
}
});
app.listen(3000, () => console.log("Server running on port 3000"));Implementation in Python (FastAPI)
from fastapi import FastAPI, Request, HTTPException, Header, Depends
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
from typing import Literal, Optional
from datetime import datetime, timezone
import hmac
import hashlib
import base64
app = FastAPI()
# CORSMiddleware configuration
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
SECRET_KEY = "your_secret_key"
VALID_ACCESS_TOKEN = "your_access_token"
# Return errors in the common response body format (result_code・message)
@app.exception_handler(HTTPException)
async def http_error_handler(request: Request, exc: HTTPException):
return JSONResponse(status_code=exc.status_code, content=exc.detail)
# Return PUB2001 for missing required parameters or invalid types
@app.exception_handler(RequestValidationError)
async def validation_error_handler(request: Request, exc: RequestValidationError):
return JSONResponse(
status_code=400,
content={"result_code": "PUB2001", "message": "Missing required parameters or invalid values"}
)
# Request models
class Content(BaseModel):
content_id: str
content_name: str
quantity: int
class RegisterRequest(BaseModel):
game: str
user: str
item: str
transaction_id: str
item_category: Literal["paid", "free"]
item_name: str
price: int
selling_price: int
billing_amount: int
purchased_at: str
ref: Optional[str] # null allowed; key is required
payment_method: Optional[str] # null allowed; key is required
contents: list[Content]
# Authentication dependency function
async def verify_auth(authorization: Optional[str] = Header(None)):
if not authorization:
raise HTTPException(
status_code=401,
detail={"result_code": "PUB1001", "message": "Missing authentication credentials"}
)
if authorization != f"Bearer {VALID_ACCESS_TOKEN}":
raise HTTPException(
status_code=401,
detail={"result_code": "PUB1002", "message": "Invalid access token"}
)
return True
def _check_signature(payload: bytes, x_signature: Optional[str]):
if not x_signature:
raise HTTPException(
status_code=400,
detail={"result_code": "PUB1004", "message": "Missing signature"}
)
computed_signature = base64.b64encode(
hmac.new(SECRET_KEY.encode(), payload, hashlib.sha256).digest()
).decode()
if not hmac.compare_digest(x_signature, computed_signature):
raise HTTPException(
status_code=400,
detail={"result_code": "PUB1004", "message": "Invalid signature"}
)
return True
# Signature verification (GET): use the received query string as is
async def verify_signature_get(request: Request, x_signature: Optional[str] = Header(None)):
return _check_signature(request.url.query.encode(), x_signature)
# Signature verification (POST): use the received request body as is
async def verify_signature_post(request: Request, x_signature: Optional[str] = Header(None)):
return _check_signature(await request.body(), x_signature)
def now_iso():
return datetime.now(timezone.utc).isoformat()
@app.get("/check")
async def check(
game: str,
user: str,
transaction_id: str,
item: str,
item_category: Literal["paid", "free"],
price: int,
selling_price: int,
billing_amount: int,
_auth: bool = Depends(verify_auth),
_sig: bool = Depends(verify_signature_get)
):
return {
"request_id": "abc123",
"timestamp": now_iso(),
"result_code": "PUB0000",
"message": "Purchase allowed",
"purchasable": "available",
"age_category": "under_18",
"account_limit": {"total": 10000, "remaining": 5000, "reset_at": None},
"stock": None,
"requested_price": price
}
@app.post("/register")
async def register(
request: RegisterRequest,
_auth: bool = Depends(verify_auth),
_sig: bool = Depends(verify_signature_post)
):
try:
# Grant the item here based on request.contents(content_id / content_name / quantity)
return {
"request_id": "def456",
"timestamp": now_iso(),
"result_code": "PUB0000",
"message": "The purchase record has been registered.",
"item_granted": True
}
except Exception:
raise HTTPException(
status_code=500,
detail={"result_code": "PUB5000", "message": "Internal server error"}
)
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=3000)Testing with Postman (Approx. 5 minutes)
In the Authorization header, set Bearer your_access_token , set the calculated signature in X-Signature, and then try the following requests
* The samples on this page are minimal configurations for connectivity testing. For the complete list of request and response fields, please refer to theAPI Specification.
GET /check request example
GET http://localhost:3000/check?game=game123&user=user456&transaction_id=txn_123456&item=item789&item_category=paid&price=500&selling_price=500&billing_amount=500
Authorization: Bearer your_access_token
X-Signature: <SIGNED_DATA>Example response:
{
"request_id": "abc123",
"timestamp": "2025-02-27T12:00:00Z",
"result_code": "PUB0000",
"message": "Purchase allowed",
"purchasable": "available",
"age_category": "under_18",
"account_limit": {
"total": 10000,
"remaining": 5000,
"reset_at": null
},
"stock": null,
"requested_price": 500
}POST /register request example
POST http://localhost:3000/register
Authorization: Bearer your_access_token
Content-Type: application/json
X-Signature: <SIGNED_DATA>
{"game":"game123","user":"user456","item":"item789","transaction_id":"txn_123456","item_category":"paid","item_name":"100Gems","price":500,"selling_price":500,"billing_amount":500,"purchased_at":"2025-02-27T12:00:00Z","ref":"game8.jp","payment_method":"Card","contents":[{"content_id":"item789-1","content_name":"Gems","quantity":100}]}Example response:
{
"request_id": "def456",
"timestamp": "2025-02-27T12:00:00Z",
"result_code": "PUB0000",
"message": "The purchase record has been registered.",
"item_granted": true
}