---
title: "テスト実装ガイド"
site: "Game8 Store – Developer Center"
lang: "ja"
category: "技術仕様 & API"
canonical: "https://developers.store.game8.jp/ja/test-impl/"
---

# テスト実装ガイド

Game8 Storeと連携し、`GET /check`（購入可否チェック）および `POST /register`（購入登録）を簡単なテスト実装で試す方法を紹介します。

**所要時間目安：**15分

AIコーディングツールをお使いの場合は [AIエージェント連携（llms.txt / MCP）](https://developers.store.game8.jp/ja/ai-agent/) の手順で本ドキュメントを読み込ませると、仕様を参照しながら実装できます。

## 1. エンドポイントの実装（目安：10分）

### 1.1 必要な環境

- Node.js（Express）または Python（FastAPI） を使用
- サーバーをローカルまたはクラウド（AWS, GCP, Vercel, Renderなど）にデプロイ可能な状態

### 1.2 Node.js（Express）での実装

```javascript
const express = require("express");
const cors = require("cors");
const crypto = require("crypto");

const app = express();
app.use(express.json());
app.use(cors());

const VALID_ACCESS_TOKEN = "your_access_token";
const SECRET_KEY = "your_secret_key";

// 認証ミドルウェア
app.use((req, res, next) => {
  const authHeader = req.headers.authorization;
  if (!authHeader) {
    return res.status(401).json({ result_code: "PUB1001", message: "認証情報が不足しています" });
  }
  const [scheme, token] = authHeader.split(" ");
  if (scheme !== "Bearer" || token !== VALID_ACCESS_TOKEN) {
    return res.status(401).json({ result_code: "PUB1002", message: "無効なアクセストークンです" });
  }
  next();
});

// X-Signature 検証ミドルウェア
const verifySignature = (req, res, next) => {
  const signature = req.headers["x-signature"];
  if (!signature) {
    return res.status(400).json({ result_code: "PUB1004", message: "署名がありません" });
  }
  const requestData = req.method === "GET" ? req.query : req.body
  const payload = JSON.stringify(requestData);
  const computedSignature = crypto.createHmac("sha256", SECRET_KEY).update(payload).digest("base64");
  if (signature !== computedSignature) {
    return res.status(400).json({ result_code: "PUB1004", message: "署名が無効です" });
  }
  next();
};

// 購入可否チェック（アイテム確保対応）
app.get("/check", verifySignature, (req, res) => {
    const { game, user, item, price, transaction_id } = req.query;
    if (!game || !user || !item || !price || !transaction_id || isNaN(price)) {
        return res.status(400).json({ result_code: "PUB2001", message: "必須パラメータ不足または無効な値" });
    }
    res.json({
        request_id: "abc123",
        timestamp: new Date().toISOString(),
        result_code: "PUB0000",
        message: "購入可能",
        purchasable: "available",
        age_category: "under_18",
        account_limit: { total: 10000, remaining: 5000 },
        requested_price: Number(price),
        transaction_id: transaction_id
    });
});

// 購入登録
app.post("/register", verifySignature, (req, res) => {
    try {
        const { game, user, item, transaction_id, item_name, price, quantity } = req.body;
        if (!game || !user || !item || !transaction_id || !item_name || !price || !quantity || isNaN(price) || isNaN(quantity)) {
            return res.status(400).json({ result_code: "PUB2001", message: "必須パラメータ不足または無効な値" });
        }
        res.json({
            request_id: "def456",
            timestamp: new Date().toISOString(),
            result_code: "PUB0000",
            message: "購入実績を登録しました。",
            item_granted: true,
            transaction_id: transaction_id
        });
    } catch (error) {
        res.status(500).json({ result_code: "PUB5000", message: "内部サーバーエラー" });
    }
});

app.listen(3000, () => console.log("Server running on port 3000"));
```

### 1.3 Python（FastAPI）での実装

```python
from fastapi import FastAPI, Request, HTTPException, Header, Depends
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
import hmac
import hashlib
import base64
import json
from datetime import datetime
from typing import Optional
from urllib.parse import urlencode

app = FastAPI()

# CORSミドルウェアの設定
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

SECRET_KEY = "your_secret_key"
VALID_ACCESS_TOKEN = "your_access_token"

# リクエストモデル
class RegisterRequest(BaseModel):
    game: str
    user: str
    item: str
    transaction_id: str
    item_name: str
    price: int
    quantity: int

# 認証の依存関数
async def verify_auth(authorization: Optional[str] = Header(None)):
    if not authorization:
        raise HTTPException(
            status_code=401,
            detail={"result_code": "PUB1001", "message": "認証情報が不足しています"}
        )

    if authorization != f"Bearer {VALID_ACCESS_TOKEN}":
        raise HTTPException(
            status_code=401,
            detail={"result_code": "PUB1002", "message": "無効なアクセストークンです"}
        )
    return True

# 署名検証の依存関数（GETリクエスト用）
async def verify_signature_get(request: Request, x_signature: Optional[str] = Header(None)):
    if not x_signature:
        raise HTTPException(
            status_code=400,
            detail={"result_code": "PUB1004", "message": "署名がありません"}
        )

    # クエリパラメータを取得
    query_params = dict(request.query_params)
    payload = urlencode(query_params)

    # 署名を計算
    computed_signature = base64.b64encode(
        hmac.new(SECRET_KEY.encode(), payload.encode(), hashlib.sha256).digest()
    ).decode()

    if x_signature != computed_signature:
        raise HTTPException(
            status_code=400,
            detail={"result_code": "PUB1004", "message": "署名が無効です"}
        )
    return True

# 署名検証の依存関数（POSTリクエスト用）
async def verify_signature_post(request: Request, x_signature: Optional[str] = Header(None)):
    if not x_signature:
        raise HTTPException(
            status_code=400,
            detail={"result_code": "PUB1004", "message": "署名がありません"}
        )

    # リクエストボディを取得
    body_bytes = await request.body()
    if not body_bytes:
        raise HTTPException(
            status_code=400,
            detail={"result_code": "PUB2001", "message": "必須パラメータ不足または無効な値"}
        )

    # JSONとしてパース
    try:
        body_json = json.loads(body_bytes)
        payload = json.dumps(body_json)
    except json.JSONDecodeError:
        raise HTTPException(
            status_code=400,
            detail={"result_code": "PUB2001", "message": "無効なJSONフォーマット"}
        )

    # 署名を計算
    computed_signature = base64.b64encode(
        hmac.new(SECRET_KEY.encode(), payload.encode(), hashlib.sha256).digest()
    ).decode()

    if x_signature != computed_signature:
        raise HTTPException(
            status_code=400,
            detail={"result_code": "PUB1004", "message": "署名が無効です"}
        )
    return True

@app.get("/check")
async def check(
    game: str,
    user: str,
    item: str,
    price: int,
    transaction_id: str,
    _auth: bool = Depends(verify_auth),
    _sig: bool = Depends(verify_signature_get)
):
    return {
        "request_id": "abc123",
        "timestamp": datetime.utcnow().isoformat(),
        "result_code": "PUB0000",
        "message": "購入可能",
        "purchasable": "available",
        "age_category": "under_18",
        "account_limit": {"total": 10000, "remaining": 5000},
        "requested_price": price,
        "transaction_id": transaction_id
    }

@app.post("/register")
async def register(
    request: RegisterRequest,
    _auth: bool = Depends(verify_auth),
    _sig: bool = Depends(verify_signature_post)
):
    try:
        return {
            "request_id": "def456",
            "timestamp": datetime.utcnow().isoformat(),
            "result_code": "PUB0000",
            "message": "購入実績を登録しました。",
            "item_granted": True,
            "transaction_id": request.transaction_id
        }
    except Exception as e:
        raise HTTPException(
            status_code=500,
            detail={"result_code": "PUB5000", "message": "内部サーバーエラー"}
        )

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=3000)
```

## 2. Postmanを使ったテスト（目安：5分）

Authorizationヘッダーに `Bearer your_access_token` 、X-Signature に算定した署名を設定した上で以下のリクエストを試す

※本ページのサンプルは疎通確認用の最小構成です。実際のリクエスト・レスポンスの全項目は[API仕様書](https://developers.store.game8.jp/ja/api-v5/)をご参照ください。

### 2.1 GET `/check`のリクエスト例

```
GET http://localhost:3000/check
X-Signature: <SIGNED_DATA>

{
  "game": "game123",
  "user": "user456",
  "item": "item789",
  "transaction_id": "txn_123456",
  "price": 500
}
```

**レスポンス例:**

```json
{
  "request_id": "abc123",
  "timestamp": "2025-02-27T12:00:00Z",
  "result_code": "PUB0000",
  "message": "購入可能",
  "purchasable": true,
  "transaction_id": "txn_123456"
}
```

### 2.2 POST `/register`のリクエスト例

```
POST http://localhost:3000/register
Content-Type: application/json
X-Signature: <SIGNED_DATA>

{
  "game": "game123",
  "user": "user456",
  "item": "item789",
  "transaction_id": "txn_123456",
  "item_name": "100ジェム",
  "price": 500,
  "quantity": 1
}
```

**レスポンス例:**

```json
{
  "request_id": "def456",
  "timestamp": "2025-02-27T12:00:00Z",
  "result_code": "PUB0000",
  "message": "購入実績を登録しました。",
  "item_granted": true,
  "transaction_id": "txn_123456"
}
```
