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.
PlayCamp SDK API 게임사 연동 가이드
이 문서는 게임사가 PlayCamp SDK API를 연동하여 크리에이터 캠페인 기능을 구현하는 방법을 설명합니다.
- 아키텍처 개요
- 시작하기
- 크리에이터 후원 (Sponsor)
- 결제 등록 (Payment)
- 쿠폰 사용 (Coupon)
- 조회 API
- 웹훅 수신
- 테스트 모드
- 에러 처리
- 빠른 참조
1. 아키텍처 개요
PlayCamp SDK API는 게임 서버를 통해 연동됩니다.
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐
│ │ │ │ │ │
│ 게임 클라이언트 │◄───────►│ 게임 서버 │◄───────►│ PlayCamp SDK API │
│ │ │ (귀사 서버) │ │ 서버 │
└─────────────────┘ └─────────────────┘ └─────────────────────┘
│ │ │
│ 1. 크리에이터 선택 │ 2. API 호출 │
│ 3. 쿠폰 코드 입력 │ (Server Key 사용) │
│ 4. 인게임 결제 │ │
│ │ ▼
│ │ ┌─────────────────┐
│ │◄──────────────────│ 웹훅 이벤트 │
│ │ 5. 이벤트 수신 │ (실시간 알림) │
│ │ └─────────────────┘
│ │
└───────────────────────────┘
6. 결과 반환
역할 분담
| 구성 요소 | 역할 |
|---|
| 게임 클라이언트 | 유저 인터페이스 (크리에이터 선택 UI, 쿠폰 입력 화면 등) |
| 게임 서버 | API 호출, 비즈니스 로직 처리, Server Key 보관 |
| PlayCamp SDK API | 데이터 저장, 정산 계산, 통계 집계, 웹훅 발송 |
데이터 흐름 예시
- 유저가 게임에서 크리에이터를 선택
- 게임 서버가
POST /v1/server/sponsors 호출
- 유저가 인게임 결제 진행
- 게임 서버가
POST /v1/server/payments 호출
- SDK API가 해당 결제를 크리에이터에게 자동 귀속
- 월말 정산 시 크리에이터별 수익 계산
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 | 용도 |
|---|
| Sandbox | https://sandbox-sdk-api.playcamp.io | 개발/테스트 |
| Live | https://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.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 | 코드 | 설명 | 대응 |
|---|
| 400 | BAD_REQUEST | 활성 캠페인 없음 | 캠페인 상태 확인 |
| 404 | NOT_FOUND | 크리에이터 없음 | creatorKey 확인 |
| 409 | CONFLICT | 이미 후원 중 | 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 필수 파라미터
| 필드 | 타입 | 설명 |
|---|
userId | string | 게임 내 유저 식별자 |
transactionId | string | 플랫폼별 고유 거래 ID (중복 방지용) |
productId | string | 상품 ID |
amount | number | 결제 금액 |
currency | string | 통화 코드 (ISO 4217: USD, KRW 권장) |
platform | string | 플랫폼 (iOS, Android, Web, Roblox, Other) |
purchasedAt | string | 실제 결제 발생 시각 (ISO 8601) |
4.4 선택적 파라미터
| 필드 | 타입 | 설명 |
|---|
productName | string | 상품명 (표시용) |
distributionType | string | 유통 타입 (아래 표 참조) |
4.5 유통 타입 (distributionType)
| 값 | 설명 | 기본 적용 플랫폼 |
|---|
MOBILE_STORE | 모바일 앱스토어 | iOS, Android |
PC_SELF_STORE | PC 자체 스토어 | Web |
PC_STORE | PC 스토어 (스팀 등) | 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 | 코드 | 설명 |
|---|
| 400 | VALIDATION_ERROR | 필수 파라미터 누락 |
| 409 | CONFLICT | 중복된 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
| API | isTest 지원 |
|---|
| 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 | 코드 | 설명 |
|---|
| 400 | VALIDATION_ERROR | 요청 파라미터 검증 실패 |
| 400 | BAD_REQUEST | 잘못된 요청 |
| 401 | UNAUTHORIZED | 인증 실패 (API 키 오류) |
| 403 | FORBIDDEN | 권한 없음 |
| 404 | NOT_FOUND | 리소스를 찾을 수 없음 |
| 409 | CONFLICT | 중복 리소스 |
| 500 | INTERNAL_ERROR | 서버 내부 오류 |
10. 빠른 참조
10.1 Server API 엔드포인트
| 기능 | Method | Endpoint |
|---|
| 후원 | | |
| 후원 등록 | 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 결제 플랫폼
| 값 | 설명 |
|---|
iOS | Apple App Store |
Android | Google Play Store |
Web | 웹 결제 |
Roblox | Roblox 플랫폼 |
Other | 기타 |
추가 자료