Skip to main content

PlayCamp SDK API 게임사 연동 가이드

이 문서는 게임사가 PlayCamp SDK API를 연동하여 크리에이터 캠페인 기능을 구현하는 방법을 설명합니다.

목차

  1. 아키텍처 개요
  2. 시작하기
  3. 크리에이터 후원 (Sponsor)
  4. 결제 등록 (Payment)
  5. 쿠폰 사용 (Coupon)
  6. 조회 API
  7. 웹훅 수신
  8. 테스트 모드
  9. 에러 처리
  10. 빠른 참조

1. 아키텍처 개요

PlayCamp SDK API는 게임 서버를 통해 연동됩니다.
┌─────────────────┐         ┌─────────────────┐         ┌─────────────────────┐
│                 │         │                 │         │                     │
│  게임 클라이언트 │◄───────►│   게임 서버      │◄───────►│  PlayCamp SDK API   │
│                 │         │   (귀사 서버)    │         │      서버           │
└─────────────────┘         └─────────────────┘         └─────────────────────┘
        │                           │                            │
        │   1. 크리에이터 선택       │   2. API 호출              │
        │   3. 쿠폰 코드 입력        │      (Server Key 사용)     │
        │   4. 인게임 결제           │                            │
        │                           │                            ▼
        │                           │                   ┌─────────────────┐
        │                           │◄──────────────────│   웹훅 이벤트    │
        │                           │   5. 이벤트 수신   │   (실시간 알림)  │
        │                           │                   └─────────────────┘
        │                           │
        └───────────────────────────┘
                6. 결과 반환

역할 분담

구성 요소역할
게임 클라이언트유저 인터페이스 (크리에이터 선택 UI, 쿠폰 입력 화면 등)
게임 서버API 호출, 비즈니스 로직 처리, Server Key 보관
PlayCamp SDK API데이터 저장, 정산 계산, 통계 집계, 웹훅 발송

데이터 흐름 예시

  1. 유저가 게임에서 크리에이터를 선택
  2. 게임 서버가 POST /v1/server/sponsors 호출
  3. 유저가 인게임 결제 진행
  4. 게임 서버가 POST /v1/server/payments 호출
  5. SDK API가 해당 결제를 크리에이터에게 자동 귀속
  6. 월말 정산 시 크리에이터별 수익 계산

2. 시작하기

2.1 API 키 발급

PlayCamp 플랫폼에서 프로젝트 생성 후 API 키를 발급받습니다.
키 타입용도보관 위치
Server Key모든 API 호출 (읽기/쓰기)게임 서버 (안전하게 보관)
Client Key조회 API만 (읽기 전용)게임 클라이언트 (선택적 사용)
주의: Server Key는 절대 클라이언트에 노출하지 마세요.

2.2 인증 방식

모든 API 요청에 Bearer Token 인증을 사용합니다.
Authorization: Bearer {keyId}:{secret}
예시:
Authorization: Bearer ak_server_abc123def456:xyz789secretkey

2.3 서버 환경

환경URL용도
Sandboxhttps://sandbox-sdk-api.playcamp.io개발/테스트
Livehttps://sdk-api.playcamp.io실제 서비스
개발 중에는 Sandbox 환경을 사용하세요. Sandbox는 Live와 완전히 격리되어 있습니다.

2.4 기본 설정 예시

# 환경 변수
SDK_API_URL=https://sandbox-sdk-api.playcamp.io
SDK_SERVER_KEY=ak_server_xxx:secret

3. 크리에이터 후원 (Sponsor)

유저가 응원하는 크리에이터를 설정합니다. 후원 설정 후 해당 유저의 결제는 자동으로 크리에이터에게 귀속됩니다.

3.1 흐름

1. 크리에이터 검색/선택  →  2. 후원 등록  →  3. 이후 결제 자동 귀속

3.2 후원 등록

요청
curl -X POST "https://sandbox-sdk-api.playcamp.io/v1/server/sponsors" \
  -H "Authorization: Bearer ak_server_xxx:secret" \
  -H "Content-Type: application/json" \
  -d '{
    "userId": "user_12345",
    "creatorKey": "ABC12"
  }'
응답 (201 Created)
{
  "userId": "user_12345",
  "campaignId": "campaign_001",
  "creatorKey": "ABC12",
  "isActive": true,
  "sponsoredAt": "2024-01-15T10:30:00.000Z"
}
참고: campaignId를 지정하지 않으면 현재 활성화된 캠페인으로 자동 귀속됩니다.

3.3 후원 변경

이미 후원 중인 유저가 다른 크리에이터로 변경할 때 사용합니다. 요청
curl -X PUT "https://sandbox-sdk-api.playcamp.io/v1/server/sponsors/user/user_12345" \
  -H "Authorization: Bearer ak_server_xxx:secret" \
  -H "Content-Type: application/json" \
  -d '{
    "campaignId": "campaign_001",
    "newCreatorKey": "XYZ99"
  }'

3.4 후원 해제

요청
curl -X DELETE "https://sandbox-sdk-api.playcamp.io/v1/server/sponsors/user/user_12345?campaignId=campaign_001" \
  -H "Authorization: Bearer ak_server_xxx:secret"

3.5 에러 처리

HTTP코드설명대응
400BAD_REQUEST활성 캠페인 없음캠페인 상태 확인
404NOT_FOUND크리에이터 없음creatorKey 확인
409CONFLICT이미 후원 중PUT으로 변경 요청
409 처리 예시 (Node.js)
async function setSponsor(userId: string, creatorKey: string) {
  try {
    // 먼저 등록 시도
    return await sdk.createSponsor({ userId, creatorKey });
  } catch (error) {
    if (error.statusCode === 409) {
      // 이미 후원 중이면 변경으로 처리
      const current = await sdk.getUserSponsor(userId);
      return await sdk.updateSponsor({
        userId,
        campaignId: current.campaignId,
        newCreatorKey: creatorKey
      });
    }
    throw error;
  }
}

4. 결제 등록 (Payment)

인게임 결제 내역을 등록합니다. 등록된 결제는 유저의 후원 크리에이터에게 자동 귀속됩니다.

4.1 흐름

1. 게임 내 결제 완료  →  2. API로 결제 정보 전송  →  3. 크리에이터 귀속 + 정산 반영

4.2 결제 등록

요청
curl -X POST "https://sandbox-sdk-api.playcamp.io/v1/server/payments" \
  -H "Authorization: Bearer ak_server_xxx:secret" \
  -H "Content-Type: application/json" \
  -d '{
    "userId": "user_12345",
    "transactionId": "txn_abc123",
    "productId": "gem_pack_100",
    "productName": "100 보석 팩",
    "amount": 9900,
    "currency": "KRW",
    "platform": "Android",
    "purchasedAt": "2024-01-15T10:30:00.000Z"
  }'
응답 (201 Created)
{
  "id": 1234,
  "transactionId": "txn_abc123",
  "userId": "user_12345",
  "productId": "gem_pack_100",
  "productName": "100 보석 팩",
  "amount": 9900,
  "currency": "KRW",
  "amountUsd": 7.62,
  "platform": "Android",
  "distributionType": "MOBILE_STORE",
  "campaignId": "campaign_001",
  "creatorKey": "ABC12",
  "status": "COMPLETED",
  "purchasedAt": "2024-01-15T10:30:00.000Z",
  "createdAt": "2024-01-15T10:30:05.000Z"
}

4.3 필수 파라미터

필드타입설명
userIdstring게임 내 유저 식별자
transactionIdstring플랫폼별 고유 거래 ID (중복 방지용)
productIdstring상품 ID
amountnumber결제 금액
currencystring통화 코드 (ISO 4217: USD, KRW 권장)
platformstring플랫폼 (iOS, Android, Web, Roblox, Other)
purchasedAtstring실제 결제 발생 시각 (ISO 8601)

4.4 선택적 파라미터

필드타입설명
productNamestring상품명 (표시용)
distributionTypestring유통 타입 (아래 표 참조)

4.5 유통 타입 (distributionType)

설명기본 적용 플랫폼
MOBILE_STORE모바일 앱스토어iOS, Android
PC_SELF_STOREPC 자체 스토어Web
PC_STOREPC 스토어 (스팀 등)Roblox, Other
참고: distributionType을 지정하지 않으면 platform 값에 따라 자동으로 설정됩니다.

4.6 환율 변환

USD가 아닌 통화로 결제 시 자동으로 USD로 환산됩니다 (amountUsd 필드).
// KRW 결제 예시
{ amount: 9900, currency: "KRW" }
// → amountUsd: 7.62 (자동 계산)

4.7 결제 환불

요청
curl -X POST "https://sandbox-sdk-api.playcamp.io/v1/server/payments/txn_abc123/refund" \
  -H "Authorization: Bearer ak_server_xxx:secret" \
  -H "Content-Type: application/json" \
  -d '{}'
환불 시 정산에서 해당 금액이 차감됩니다.

4.8 에러 처리

HTTP코드설명
400VALIDATION_ERROR필수 파라미터 누락
409CONFLICT중복된 transactionId

5. 쿠폰 사용 (Coupon)

크리에이터가 배포한 쿠폰 코드를 사용합니다.

5.1 흐름

1. 쿠폰 코드 입력  →  2. 유효성 검증  →  3. 쿠폰 사용  →  4. 보상 지급

5.2 쿠폰 검증

사용 전에 쿠폰이 유효한지 확인합니다. 요청
curl -X POST "https://sandbox-sdk-api.playcamp.io/v1/server/coupons/validate" \
  -H "Authorization: Bearer ak_server_xxx:secret" \
  -H "Content-Type: application/json" \
  -d '{
    "couponCode": "CREATOR-ABC12-001",
    "userId": "user_12345"
  }'
응답 (유효한 경우)
{
  "valid": true,
  "couponCode": "CREATOR-ABC12-001",
  "reward": [
    { "itemId": "gem", "itemQuantity": 100 }
  ],
  "packageName": { "ko": "웰컴 패키지", "en": "Welcome Package" },
  "creatorKey": "ABC12",
  "campaignId": "campaign_001"
}
응답 (유효하지 않은 경우)
{
  "valid": false,
  "couponCode": "INVALID-CODE",
  "errorCode": "COUPON_NOT_FOUND",
  "errorMessage": "쿠폰을 찾을 수 없습니다"
}

5.3 쿠폰 사용

검증 후 실제로 쿠폰을 사용합니다. 요청
curl -X POST "https://sandbox-sdk-api.playcamp.io/v1/server/coupons/redeem" \
  -H "Authorization: Bearer ak_server_xxx:secret" \
  -H "Content-Type: application/json" \
  -d '{
    "couponCode": "CREATOR-ABC12-001",
    "userId": "user_12345"
  }'
응답 (200 OK)
{
  "success": true,
  "usageId": 5678,
  "couponCode": "CREATOR-ABC12-001",
  "reward": [
    { "itemId": "gem", "itemQuantity": 100 }
  ],
  "packageName": { "ko": "웰컴 패키지" },
  "creatorKey": "ABC12",
  "campaignId": "campaign_001",
  "redeemedAt": "2024-01-15T10:30:00.000Z"
}

5.4 에러 코드

코드설명
COUPON_NOT_FOUND존재하지 않는 쿠폰 코드
COUPON_INACTIVE비활성화된 쿠폰
COUPON_NOT_YET_VALID사용 기간 전
COUPON_EXPIRED만료된 쿠폰
USER_CODE_LIMIT유저별 코드 사용 한도 초과
USER_PACKAGE_LIMIT유저별 패키지 사용 한도 초과
TOTAL_USAGE_LIMIT전체 사용 한도 초과

5.5 전체 흐름 예시 (Node.js)

async function redeemCoupon(userId: string, couponCode: string) {
  // 1. 검증
  const validation = await sdk.validateCoupon({ couponCode, userId });

  if (!validation.valid) {
    return {
      success: false,
      error: validation.errorCode,
      message: validation.errorMessage
    };
  }

  // 2. 사용
  const result = await sdk.redeemCoupon({ couponCode, userId });

  // 3. 게임 내 보상 지급
  for (const reward of result.reward) {
    await giveItemToUser(userId, reward.itemId, reward.itemQuantity);
  }

  return { success: true, reward: result.reward };
}

6. 조회 API

캠페인, 크리에이터, 후원 상태 등을 조회합니다.

6.1 기본 조회 (Server API)

Server Key로 모든 조회가 가능합니다.
# 캠페인 목록
curl "https://sandbox-sdk-api.playcamp.io/v1/server/campaigns" \
  -H "Authorization: Bearer ak_server_xxx:secret"

# 크리에이터 검색
curl "https://sandbox-sdk-api.playcamp.io/v1/server/creators/search?keyword=ABC" \
  -H "Authorization: Bearer ak_server_xxx:secret"

# 유저 후원 상태
curl "https://sandbox-sdk-api.playcamp.io/v1/server/sponsors/user/user_12345" \
  -H "Authorization: Bearer ak_server_xxx:secret"

# 유저 결제 내역
curl "https://sandbox-sdk-api.playcamp.io/v1/server/payments/user/user_12345" \
  -H "Authorization: Bearer ak_server_xxx:secret"

6.2 Client API (선택)

게임 클라이언트에서 직접 API를 호출하고 싶다면 Client Key를 사용할 수 있습니다.
  • Client Key는 읽기 전용이라 노출되어도 안전합니다
  • 게임 서버를 거치지 않아 응답이 빠릅니다
  • 서버 부하를 줄이고 싶을 때 고려하세요
# Client API 예시
curl "https://sandbox-sdk-api.playcamp.io/v1/client/campaigns" \
  -H "Authorization: Bearer ak_client_xxx:secret"

7. 웹훅 수신

SDK API에서 이벤트 발생 시 게임 서버로 알림을 보냅니다.

7.1 이벤트 종류

이벤트설명
coupon.redeemed쿠폰 사용됨
payment.created결제 등록됨
payment.refunded결제 환불됨
sponsor.created후원 등록됨
sponsor.changed후원 변경됨

7.2 웹훅 형식

여러 이벤트가 배치로 전송됩니다. 헤더
Content-Type: application/json
X-Webhook-Batch: true
X-Webhook-Signature: {HMAC-SHA256 서명}
페이로드
{
  "events": [
    {
      "event": "payment.created",
      "timestamp": "2024-01-15T10:30:00.000Z",
      "data": {
        "transactionId": "txn_abc123",
        "userId": "user_12345",
        "amount": 9900,
        "currency": "KRW",
        "creatorKey": "ABC12"
      }
    },
    {
      "event": "sponsor.created",
      "timestamp": "2024-01-15T10:30:01.000Z",
      "data": {
        "userId": "user_67890",
        "creatorKey": "XYZ99"
      }
    }
  ]
}

7.3 서명 검증

웹훅 요청이 PlayCamp에서 온 것인지 확인하기 위해 서명을 검증해야 합니다. 서명 생성 방식
  • 알고리즘: HMAC-SHA256
  • 키: 웹훅 등록 시 발급된 secret
  • 데이터: 요청 body (raw string)
  • 결과: hex 문자열
검증 예시 (Node.js)
import crypto from 'crypto';

function verifyWebhookSignature(
  payload: string,      // raw request body
  signature: string,    // X-Webhook-Signature 헤더 값
  secret: string        // 웹훅 시크릿
): boolean {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');

  // timing-safe 비교로 타이밍 공격 방지
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

7.4 수신 예시 (Node.js/Express)

import crypto from 'crypto';

// raw body를 받기 위해 express.raw() 사용
app.post('/webhooks/playcamp', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.headers['x-webhook-signature'] as string;
  const isBatch = req.headers['x-webhook-batch'] === 'true';
  const webhookSecret = process.env.WEBHOOK_SECRET;

  // raw body를 string으로 변환
  const payload = Buffer.isBuffer(req.body)
    ? req.body.toString()
    : req.body;

  // 1. 서명 검증
  if (webhookSecret && signature) {
    const expected = crypto
      .createHmac('sha256', webhookSecret)
      .update(payload)
      .digest('hex');

    if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
      console.log('Invalid webhook signature');
      return res.status(401).json({ error: 'Invalid signature' });
    }
  }

  // 2. JSON 파싱
  const data = JSON.parse(payload);

  // 3. 배치 형식 처리
  const events = isBatch || data.events ? data.events : [data];

  for (const event of events) {
    switch (event.event) {
      case 'coupon.redeemed':
        console.log('쿠폰 사용:', event.data);
        break;
      case 'payment.created':
        console.log('결제 등록:', event.data);
        break;
      case 'sponsor.created':
        console.log('후원 등록:', event.data);
        break;
    }
  }

  res.json({ received: true });
});
중요: express.json() 대신 express.raw({ type: 'application/json' })를 사용해야 원본 body로 서명 검증이 가능합니다.

8. 테스트 모드

실제 데이터를 생성하지 않고 API 연동을 테스트할 수 있습니다.

8.1 사용 방법

요청 body에 isTest: true를 추가합니다.
curl -X POST "https://sandbox-sdk-api.playcamp.io/v1/server/payments" \
  -H "Authorization: Bearer ak_server_xxx:secret" \
  -H "Content-Type: application/json" \
  -d '{
    "userId": "test_user",
    "transactionId": "test_txn_001",
    "productId": "gem_pack_100",
    "amount": 9900,
    "currency": "KRW",
    "platform": "Android",
    "purchasedAt": "2024-01-15T10:30:00.000Z",
    "isTest": true
  }'

8.2 동작

  • 요청 파라미터 검증은 실제와 동일하게 수행
  • 데이터는 DB에 저장되지 않음
  • 모의 응답 반환

8.3 지원 API

APIisTest 지원
POST /sponsors
PUT /sponsors/user/:userId
DELETE /sponsors/user/:userId
POST /payments
POST /payments/:id/refund
POST /coupons/validate
POST /coupons/redeem

9. 에러 처리

9.1 에러 응답 형식

{
  "error": "에러 메시지",
  "code": "ERROR_CODE"
}

9.2 Validation 에러

필수 파라미터 누락이나 형식 오류 시:
{
  "error": "Validation Error",
  "code": "VALIDATION_ERROR",
  "details": [
    { "path": "userId", "message": "Required" },
    { "path": "amount", "message": "Expected number, received string" }
  ]
}

9.3 공통 에러 코드

HTTP코드설명
400VALIDATION_ERROR요청 파라미터 검증 실패
400BAD_REQUEST잘못된 요청
401UNAUTHORIZED인증 실패 (API 키 오류)
403FORBIDDEN권한 없음
404NOT_FOUND리소스를 찾을 수 없음
409CONFLICT중복 리소스
500INTERNAL_ERROR서버 내부 오류

10. 빠른 참조

10.1 Server API 엔드포인트

기능MethodEndpoint
후원
후원 등록POST/v1/server/sponsors
후원 상태 조회GET/v1/server/sponsors/user/:userId
후원 변경PUT/v1/server/sponsors/user/:userId
후원 해제DELETE/v1/server/sponsors/user/:userId
결제
결제 등록POST/v1/server/payments
결제 조회GET/v1/server/payments/:transactionId
유저 결제 내역GET/v1/server/payments/user/:userId
결제 환불POST/v1/server/payments/:transactionId/refund
쿠폰
쿠폰 검증POST/v1/server/coupons/validate
쿠폰 사용POST/v1/server/coupons/redeem
유저 쿠폰 내역GET/v1/server/coupons/user/:userId
조회
캠페인 목록GET/v1/server/campaigns
캠페인 상세GET/v1/server/campaigns/:id
크리에이터 검색GET/v1/server/creators/search
크리에이터 상세GET/v1/server/creators/:creatorKey

10.2 쿠폰 에러 코드

코드설명
COUPON_NOT_FOUND존재하지 않는 쿠폰
COUPON_INACTIVE비활성화된 쿠폰
COUPON_NOT_YET_VALID사용 기간 전
COUPON_EXPIRED만료됨
USER_CODE_LIMIT유저별 코드 한도 초과
USER_PACKAGE_LIMIT유저별 패키지 한도 초과
TOTAL_USAGE_LIMIT전체 한도 초과

10.3 결제 플랫폼

설명
iOSApple App Store
AndroidGoogle Play Store
Web웹 결제
RobloxRoblox 플랫폼
Other기타

추가 자료