> ## 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.

# Go SDK

> PlayCamp API 연동을 위한 공식 Go SDK

공식 Go SDK (`github.com/playcamp/playcamp-go-sdk`)는 PlayCamp API와 타입 안전한 연동을 제공합니다. 인증, 페이지네이션, 재시도, 에러 처리를 자동으로 처리하며 외부 의존성이 없습니다.

## 설치

```bash theme={null}
go get github.com/playcamp/playcamp-go-sdk
```

**요구사항:** Go >= 1.21

## 빠른 시작

```go theme={null}
import playcamp "github.com/playcamp/playcamp-go-sdk"

server, err := playcamp.NewServer("your_server_key:your_secret",
    playcamp.WithEnvironment(playcamp.EnvironmentSandbox),
)
if err != nil {
    log.Fatal(err)
}

// 부스트 등록
sponsor, err := server.Sponsors.Create(ctx, playcamp.CreateSponsorParams{
    UserID:     "user_12345",
    CreatorKey: "ABC12",
})

// 결제 등록
payment, err := server.Payments.Create(ctx, playcamp.CreatePaymentParams{
    UserID:        "user_12345",
    TransactionID: "txn_abc123",
    ProductID:     "gem_pack_100",
    Amount:        9900,
    Currency:      "KRW",
    Platform:      playcamp.PaymentPlatformAndroid,
    DistributionType: playcamp.String("MOBILE_STORE"),
    PurchasedAt:   time.Now(),
})
```

## Client vs Server

SDK는 API 키 유형에 따라 두 가지 생성자를 제공합니다:

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

```go theme={null}
import playcamp "github.com/playcamp/playcamp-go-sdk"

// 읽기 전용 (Client Key)
client, err := playcamp.NewClient("your_client_key:your_secret")

// 전체 접근 (Server Key)
server, err := playcamp.NewServer("your_server_key:your_secret")
```

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

### Client 리소스

| 리소스         | 메서드                                                              |
| ----------- | ---------------------------------------------------------------- |
| `Campaigns` | `List()`, `ListAll()`, `Get()`, `GetCreators()`, `GetPackages()` |
| `Creators`  | `Get()`, `Search()`                                              |
| `Coupons`   | `Validate()`                                                     |
| `Sponsors`  | `Get()`                                                          |

### Server 리소스

| 리소스                | 메서드                                                                                   |
| ------------------ | ------------------------------------------------------------------------------------- |
| `Campaigns`        | `List()`, `ListAll()`, `Get()`, `GetCreators()`                                       |
| `Creators`         | `Get()`, `Search()`, `GetCoupons()`                                                   |
| `Coupons`          | `Validate()`, `Redeem()`, `GetUserHistory()`, `ListAllUserHistory()`                  |
| `Sponsors`         | `Create()`, `GetByUser()`, `Update()`, `Delete()`, `GetHistory()`, `ListAllHistory()` |
| `Payments`         | `Create()`, `CreateBulk()`, `Get()`, `ListByUser()`, `ListAllByUser()`, `Refund()`    |
| `PlaytimeSessions` | `Create()`, `CreateBulk()`                                                            |
| `Webhooks`         | `List()`, `Create()`, `Update()`, `Delete()`, `GetLogs()`, `Test()`                   |
| `Webview`          | `CreateOTT()`                                                                         |

## 설정

```go theme={null}
server, err := playcamp.NewServer("key:secret",
    playcamp.WithEnvironment(playcamp.EnvironmentSandbox), // 기본값: EnvironmentLive
    playcamp.WithTimeout(30 * time.Second),                // 기본값: 30s
    playcamp.WithTestMode(true),                           // 기본값: false
    playcamp.WithMaxRetries(3),                            // 기본값: 3
    playcamp.WithDebug(playcamp.DebugOptions{              // 기본값: 비활성
        Enabled:         true,
        LogRequestBody:  true,
        LogResponseBody: true,
    }),
)
```

### 환경

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

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

```go theme={null}
server, err := playcamp.NewServer("key:secret",
    playcamp.WithBaseURL("http://localhost:3003"), // environment 설정을 덮어씀
)
```

### 포인터 헬퍼

선택적 필드에 사용하는 내장 헬퍼:

```go theme={null}
playcamp.String("value")   // *string
playcamp.Int(10)           // *int
playcamp.Bool(true)        // *bool
playcamp.Float64(99.99)    // *float64
```

## 페이지네이션

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

```go theme={null}
// 단일 페이지 조회
result, err := server.Campaigns.List(ctx, &playcamp.PaginationOptions{
    Page:  playcamp.Int(1),
    Limit: playcamp.Int(20),
})
// result.Data, result.Pagination, result.HasNextPage
```

이터레이터를 사용하면 전체 결과를 자동으로 순회합니다:

```go theme={null}
iterator := server.Campaigns.ListAll(&playcamp.PaginationOptions{
    Limit: playcamp.Int(50),
})

for iterator.Next(ctx) {
    campaign := iterator.Item()
    fmt.Println(campaign.CampaignID)
    iterator.Advance()
}

if err := iterator.Err(); err != nil {
    log.Fatal(err)
}
```

## 에러 처리

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

```go theme={null}
import "errors"

campaign, err := server.Campaigns.Get(ctx, "invalid_id")
if err != nil {
    var notFoundErr *playcamp.NotFoundError
    var authErr     *playcamp.AuthError
    var rateLimitErr *playcamp.RateLimitError
    var networkErr  *playcamp.NetworkError
    var apiErr      *playcamp.APIError

    switch {
    case errors.As(err, &notFoundErr):
        fmt.Println("캠페인을 찾을 수 없음")
    case errors.As(err, &authErr):
        fmt.Println("잘못된 API 키")
    case errors.As(err, &rateLimitErr):
        fmt.Println("속도 제한:", rateLimitErr.Message)
    case errors.As(err, &networkErr):
        fmt.Println("네트워크 에러:", networkErr.Message)
    case errors.As(err, &apiErr):
        fmt.Printf("API 에러 [%d]: %s\n", apiErr.StatusCode, apiErr.Message)
    default:
        fmt.Println("예기치 않은 에러:", err)
    }
}
```

| 에러 타입                  | HTTP 상태 | 설명              |
| ---------------------- | ------- | --------------- |
| `BadRequestError`      | 400     | 잘못된 요청          |
| `AuthError`            | 401     | 인증 실패           |
| `ForbiddenError`       | 403     | 권한 부족           |
| `NotFoundError`        | 404     | 리소스를 찾을 수 없음    |
| `ConflictError`        | 409     | 리소스 충돌 (중복)     |
| `ValidationError`      | 422     | 서버 측 유효성 검사 실패  |
| `RateLimitError`       | 429     | 속도 제한 초과        |
| `NetworkError`         | —       | 네트워크/타임아웃 에러    |
| `InputValidationError` | —       | 클라이언트 측 파라미터 검증 |

## 웹훅 검증

SDK는 수신 웹훅 서명 검증을 위한 `webhookutil` 패키지를 제공합니다:

```go theme={null}
import "github.com/playcamp/playcamp-go-sdk/webhookutil"

func handleWebhook(w http.ResponseWriter, r *http.Request) {
    body, _ := io.ReadAll(r.Body)

    result := webhookutil.Verify(webhookutil.VerifyOptions{
        Payload:   body,
        Signature: r.Header.Get("X-Webhook-Signature"),
        Secret:    "your_webhook_secret",
        Tolerance: 300, // 최대 허용 시간 초 (기본값: 300)
    })

    if !result.Valid {
        http.Error(w, result.Error, http.StatusUnauthorized)
        return
    }

    for _, event := range result.Payload.Events {
        switch event.Event {
        case playcamp.WebhookEventCouponRedeemed:
            var data playcamp.CouponRedeemedData
            json.Unmarshal(event.Data, &data)
            fmt.Printf("쿠폰 사용: %s\n", data.CouponCode)
        case playcamp.WebhookEventPaymentCreated:
            var data playcamp.PaymentCreatedData
            json.Unmarshal(event.Data, &data)
            fmt.Printf("결제 등록: %s\n", data.TransactionID)
        case playcamp.WebhookEventSponsorCreated:
            var data playcamp.SponsorCreatedData
            json.Unmarshal(event.Data, &data)
            fmt.Printf("부스트 등록: %s\n", data.UserID)
        }
    }

    w.WriteHeader(http.StatusOK)
    json.NewEncoder(w).Encode(map[string]bool{"received": true})
}
```

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

```go theme={null}
import "github.com/playcamp/playcamp-go-sdk/webhookutil"

payload := []byte(`{"events":[{"event":"coupon.redeemed","timestamp":"2024-01-15T10:30:00Z","data":{"couponCode":"TEST","userId":"user_123","usageId":1,"reward":[]}}]}`)

signature := webhookutil.ConstructSignature(payload, "your_webhook_secret", nil)
```

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

## 예제 프로젝트

전체 API 엔드포인트를 포함하는 완전한 예제는 [playcamp-go-sdk-example](https://github.com/PlayCamp/playcamp-go-sdk-example) 저장소를 참조하세요.
