> ## Documentation Index
> Fetch the complete documentation index at: https://playcamp.io/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Node SDK

> PlayCamp API 연동을 위한 TypeScript 기반 Node.js SDK

공식 Node SDK (`@playcamp/node-sdk`)는 PlayCamp API와 타입 안전한 연동을 제공합니다. 인증, 페이지네이션, 재시도, 에러 처리를 자동으로 처리합니다.

## 설치

```bash theme={null}
npm install @playcamp/node-sdk
```

**요구사항:** Node.js >= 18.0.0

## 빠른 시작

```typescript theme={null}
import { PlayCampServer } from '@playcamp/node-sdk';

const server = new PlayCampServer('your_server_key:your_secret', {
  environment: 'sandbox',
});

// 부스트 등록
const sponsor = await server.sponsors.create({
  userId: 'user_12345',
  creatorKey: 'ABC12',
});

// 결제 등록
const payment = await server.payments.create({
  userId: 'user_12345',
  transactionId: 'txn_abc123',
  productId: 'gem_pack_100',
  amount: 9900,
  currency: 'KRW',
  platform: 'Android',
  distributionType: 'MOBILE_STORE',
  purchasedAt: new Date(),
});
```

## Client vs Server

SDK는 API 키 유형에 따라 두 가지 클래스를 제공합니다:

| 클래스              | API 키      | 접근 권한 | 용도              |
| ---------------- | ---------- | ----- | --------------- |
| `PlayCampClient` | Client Key | 읽기 전용 | 게임 클라이언트, 공개 조회 |
| `PlayCampServer` | Server Key | 읽기/쓰기 | 게임 서버, 모든 작업    |

```typescript theme={null}
import { PlayCampClient, PlayCampServer } from '@playcamp/node-sdk';

// 읽기 전용 (Client Key)
const client = new PlayCampClient('your_client_key:your_secret');

// 전체 접근 (Server Key)
const server = new PlayCampServer('your_server_key:your_secret');
```

<Warning>
  클라이언트 측에서 `PlayCampServer`를 사용하거나 Server Key를 노출하지 마세요.
</Warning>

### Client 리소스

| 리소스         | 메서드                                                                                        |
| ----------- | ------------------------------------------------------------------------------------------ |
| `campaigns` | `listCampaigns()`, `listAllCampaigns()`, `getCampaign()`, `getCreators()`, `getPackages()` |
| `creators`  | `getCreator()`, `search()`                                                                 |
| `coupons`   | `validate()`                                                                               |
| `sponsors`  | `getSponsor()`                                                                             |

### Server 리소스

| 리소스                | 메서드                                                                                               |
| ------------------ | ------------------------------------------------------------------------------------------------- |
| `campaigns`        | `listCampaigns()`, `listAllCampaigns()`, `getCampaign()`, `getCreators()`                         |
| `creators`         | `getCreator()`, `search()`, `getCoupons()`                                                        |
| `coupons`          | `validate()`, `redeem()`, `getUserHistory()`, `listAllUserHistory()`                              |
| `sponsors`         | `create()`, `getByUser()`, `update()`, `remove()`, `getHistory()`, `listAllHistory()`             |
| `payments`         | `create()`, `createBulk()`, `getByTransactionId()`, `listByUser()`, `listAllByUser()`, `refund()` |
| `playtimeSessions` | `create()`, `createBulk()`                                                                        |
| `webhooks`         | `listWebhooks()`, `create()`, `update()`, `remove()`, `getLogs()`, `test()`                       |
| `webview`          | `createOtt()`                                                                                     |

## 설정

```typescript theme={null}
const client = new PlayCampClient('key:secret', {
  environment: 'sandbox',  // 'sandbox' | 'live' (기본값: 'live')
  timeout: 30000,          // 요청 타임아웃 ms (기본값: 30000)
  isTest: false,           // 테스트 모드 (기본값: false)
  maxRetries: 3,           // 최대 재시도 횟수 (기본값: 3)
  debug: true,             // 디버그 로깅 (기본값: false)
});
```

### 환경

| 환경        | URL                                   | 용도         |
| --------- | ------------------------------------- | ---------- |
| `sandbox` | `https://sandbox-sdk-api.playcamp.io` | 개발/테스트     |
| `live`    | `https://sdk-api.playcamp.io`         | 프로덕션 (기본값) |

커스텀 URL을 직접 지정할 수도 있습니다:

```typescript theme={null}
const client = new PlayCampClient('key:secret', {
  baseUrl: 'http://localhost:3003',  // environment 설정을 덮어씀
});
```

## 페이지네이션

목록 조회 엔드포인트는 페이지네이션된 결과를 반환합니다:

```typescript theme={null}
// 단일 페이지 조회
const { data, pagination, hasNextPage } = await client.campaigns.listCampaigns({
  page: 1,
  limit: 20,
});
```

비동기 이터레이션으로 전체 결과를 자동으로 순회합니다:

```typescript theme={null}
for await (const campaign of client.campaigns.listAllCampaigns()) {
  console.log(campaign.campaignId);
}
```

## 에러 처리

SDK는 시나리오별로 타입이 지정된 에러를 throw합니다:

```typescript theme={null}
import {
  PlayCampApiError,
  PlayCampAuthError,
  PlayCampNotFoundError,
  PlayCampRateLimitError,
  PlayCampNetworkError,
} from '@playcamp/node-sdk';

try {
  await client.campaigns.getCampaign('invalid_id');
} catch (error) {
  if (error instanceof PlayCampNotFoundError) {
    console.log('캠페인을 찾을 수 없음');
  } else if (error instanceof PlayCampAuthError) {
    console.log('잘못된 API 키');
  } else if (error instanceof PlayCampRateLimitError) {
    console.log('속도 제한, 재시도 시간:', error.retryAfter);
  } else if (error instanceof PlayCampNetworkError) {
    console.log('네트워크 에러:', error.message);
  } else if (error instanceof PlayCampApiError) {
    console.log('API 에러:', error.code, error.message);
  }
}
```

## 웹훅 검증

SDK는 수신 웹훅 서명 검증 유틸리티를 제공합니다:

```typescript theme={null}
import { verifyWebhook } from '@playcamp/node-sdk';

app.post('/webhooks/playcamp', (req, res) => {
  const result = verifyWebhook({
    payload: req.rawBody,
    signature: req.headers['x-webhook-signature'],
    secret: 'your_webhook_secret',
    tolerance: 300,  // 최대 허용 시간 초 (기본값: 300)
  });

  if (!result.valid) {
    return res.status(401).json({ error: result.error });
  }

  for (const event of result.payload.events) {
    switch (event.event) {
      case 'coupon.redeemed':
        console.log('쿠폰 사용:', event.data.couponCode);
        break;
      case 'payment.created':
        console.log('결제 등록:', event.data.transactionId);
        break;
      case 'sponsor.created':
        console.log('부스트 등록:', event.data.userId);
        break;
    }
  }

  res.status(200).json({ received: true });
});
```

테스트를 위해 로컬에서 서명을 생성할 수 있습니다:

```typescript theme={null}
import { constructWebhookSignature } from '@playcamp/node-sdk';

const payload = JSON.stringify({
  events: [{
    event: 'coupon.redeemed',
    timestamp: new Date().toISOString(),
    data: { couponCode: 'TEST', userId: 'user_123', usageId: 1, reward: [] },
  }]
});

const signature = constructWebhookSignature(payload, 'your_webhook_secret');
```

웹훅 이벤트 유형과 페이로드에 대한 자세한 내용은 [웹훅 이벤트](/ko/guides/developers/game-integration/webhook) 페이지를 참조하세요.

## 예제 프로젝트

모든 API 엔드포인트를 포함한 Express 서버와 Web UI 예제는 [playcamp-node-sdk-example](https://github.com/PlayCamp/playcamp-node-sdk-example) 저장소를 참조하세요.

## TypeScript 타입

SDK는 애플리케이션에서 사용할 수 있는 모든 타입을 export합니다:

```typescript theme={null}
import type {
  Campaign,
  Creator,
  CouponValidation,
  RedeemResult,
  Sponsor,
  Payment,
  Webhook,
  WebhookPayload,
} from '@playcamp/node-sdk';
```
