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

# 결제 등록

> 크리에이터 귀속을 위한 인게임 결제 등록

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

## 흐름

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

## 결제 등록

**요청**

<Tabs>
  <Tab title="cURL">
    ```bash theme={null}
    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",
        "distributionType": "MOBILE_STORE",
        "purchasedAt": "2024-01-15T10:30:00.000Z"
      }'
    ```
  </Tab>

  <Tab title="Node SDK">
    ```typescript theme={null}
    const payment = await server.payments.create({
      userId: 'user_12345',
      transactionId: 'txn_abc123',
      productId: 'gem_pack_100',
      productName: '100 보석 팩',
      amount: 9900,
      currency: 'KRW',
      platform: 'Android',
      distributionType: 'MOBILE_STORE',
      purchasedAt: new Date('2024-01-15T10:30:00Z'),
    });
    ```
  </Tab>

  <Tab title="Go SDK">
    ```go theme={null}
    purchasedAt, _ := time.Parse(time.RFC3339, "2024-01-15T10:30:00Z")

    payment, err := server.Payments.Create(ctx, playcamp.CreatePaymentParams{
        UserID:           "user_12345",
        TransactionID:    "txn_abc123",
        ProductID:        "gem_pack_100",
        ProductName:      playcamp.String("100 보석 팩"),
        Amount:           9900,
        Currency:         "KRW",
        Platform:         playcamp.PaymentPlatformAndroid,
        DistributionType: playcamp.String("MOBILE_STORE"),
        PurchasedAt:      purchasedAt,
    })
    ```
  </Tab>
</Tabs>

**응답 (201 Created)**

```json theme={null}
{
  "data": {
    "id": 1234,
    "transactionId": "txn_abc123",
    "userId": "user_12345",
    "productId": "gem_pack_100",
    "productName": "100 보석 팩",
    "amount": 9900,
    "currency": "KRW",
    "platform": "Android",
    "campaignId": "campaign_001",
    "creatorKey": "ABC12",
    "status": "COMPLETED",
    "purchasedAt": "2024-01-15T10:30:00.000Z",
    "createdAt": "2024-01-15T10:30:05.000Z"
  }
}
```

## 필수 파라미터

| 필드                 | 타입     | 설명                                     |
| ------------------ | ------ | -------------------------------------- |
| `userId`           | string | 게임 내 유저 식별자                            |
| `transactionId`    | string | 플랫폼별 고유 거래 ID (중복 방지용)                 |
| `productId`        | string | 상품 ID                                  |
| `amount`           | number | 결제 금액                                  |
| `currency`         | string | 통화 코드 (ISO 4217: USD, KRW 권장)          |
| `platform`         | string | 플랫폼 (iOS, Android, Web, Roblox, Other) |
| `distributionType` | string | 유통 타입 (아래 참조)                          |
| `purchasedAt`      | string | 실제 결제 발생 시각 (ISO 8601)                 |

## 선택 파라미터

| 필드            | 타입      | 설명                      |
| ------------- | ------- | ----------------------- |
| `productName` | string  | 상품명                     |
| `callbackId`  | string  | 웹훅 추적용 ID (웹훅 이벤트에 포함됨) |
| `isTest`      | boolean | 테스트 모드 (실제 데이터 생성 안 함)  |

## 유통 타입 (필수)

결제 등록 시 `distributionType`은 **필수 파라미터**입니다. 정산 계산에 사용됩니다.

| 값                   | 설명                                  | 스토어 수수료 |
| ------------------- | ----------------------------------- | ------- |
| `MOBILE_STORE`      | 모바일 외부 스토어 (Google Play, App Store) | 30%     |
| `PC_STORE`          | PC 외부 스토어 (Steam 등)                 | 30%     |
| `MOBILE_SELF_STORE` | 모바일 자체 결제                           | 0%      |
| `PC_SELF_STORE`     | PC 자체 스토어                           | 0%      |

<Info>
  **정산 계산**: `Net Amount = 결제금액 × (1 - 스토어 수수료)` 후, PlayCamp 플랫폼 수수료와 크리에이터 수수료가 별도 적용됩니다.

  **중요**: 정확한 정산을 위해 실제 결제가 발생한 유통 채널에 맞는 값을 지정하세요.
</Info>

## 통화 변환

USD가 아닌 통화의 경우 자동으로 USD로 변환됩니다 (`amountUsd` 필드).

```javascript theme={null}
// KRW 결제 예시
{ amount: 9900, currency: "KRW" }
// → amountUsd: 7.62 (자동 계산)
```

## 결제 환불

<Tabs>
  <Tab title="cURL">
    ```bash theme={null}
    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 '{}'
    ```
  </Tab>

  <Tab title="Node SDK">
    ```typescript theme={null}
    const refund = await server.payments.refund('txn_abc123');
    ```
  </Tab>

  <Tab title="Go SDK">
    ```go theme={null}
    payment, err := server.Payments.Refund(ctx, "txn_abc123", nil)
    ```
  </Tab>
</Tabs>

환불 시 정산에서 해당 금액이 차감됩니다.

## 에러 처리

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

## 벌크 결제

최대 1,000건의 결제를 한 번에 등록할 수 있습니다.

**요청**

<Tabs>
  <Tab title="cURL">
    ```bash theme={null}
    curl -X POST "https://sandbox-sdk-api.playcamp.io/v1/server/payments/bulk" \
      -H "Authorization: Bearer ak_server_xxx:secret" \
      -H "Content-Type: application/json" \
      -d '{
        "payments": [
          {
            "userId": "user_1",
            "transactionId": "txn_001",
            "productId": "gem_pack_100",
            "amount": 9900,
            "currency": "KRW",
            "platform": "iOS",
            "distributionType": "MOBILE_STORE",
            "purchasedAt": "2026-03-17T00:00:00Z"
          },
          {
            "userId": "user_2",
            "transactionId": "txn_002",
            "productId": "gem_pack_200",
            "amount": 19900,
            "currency": "KRW",
            "platform": "Android",
            "distributionType": "MOBILE_STORE",
            "purchasedAt": "2026-03-17T00:00:00Z"
          }
        ],
        "callbackId": "bulk-001"
      }'
    ```
  </Tab>

  <Tab title="Node SDK">
    ```typescript theme={null}
    const result = await server.payments.createBulk({
      payments: [
        {
          userId: 'user_1',
          transactionId: 'txn_001',
          productId: 'gem_pack_100',
          amount: 9900,
          currency: 'KRW',
          platform: 'iOS',
          distributionType: 'MOBILE_STORE',
          purchasedAt: '2026-03-17T00:00:00Z',
        },
        {
          userId: 'user_2',
          transactionId: 'txn_002',
          productId: 'gem_pack_200',
          amount: 19900,
          currency: 'KRW',
          platform: 'Android',
          distributionType: 'MOBILE_STORE',
          purchasedAt: '2026-03-17T00:00:00Z',
        },
      ],
      callbackId: 'bulk-001', // optional
    });

    console.log(result.totalRequested); // 2
    console.log(result.successful);     // 성공 건수
    console.log(result.failed);         // 실패 건수
    console.log(result.skipped);        // 스킵 건수
    ```
  </Tab>

  <Tab title="Go SDK">
    ```go theme={null}
    purchasedAt, _ := time.Parse(time.RFC3339, "2026-03-17T00:00:00Z")

    result, err := server.Payments.CreateBulk(ctx, playcamp.CreateBulkPaymentParams{
        Payments: []playcamp.CreatePaymentParams{
            {
                UserID:           "user_1",
                TransactionID:    "txn_001",
                ProductID:        "gem_pack_100",
                Amount:           9900,
                Currency:         "KRW",
                Platform:         playcamp.PaymentPlatformIOS,
                DistributionType: playcamp.String("MOBILE_STORE"),
                PurchasedAt:      purchasedAt,
            },
            {
                UserID:           "user_2",
                TransactionID:    "txn_002",
                ProductID:        "gem_pack_200",
                Amount:           19900,
                Currency:         "KRW",
                Platform:         playcamp.PaymentPlatformAndroid,
                DistributionType: playcamp.String("MOBILE_STORE"),
                PurchasedAt:      purchasedAt,
            },
        },
        CallbackID: "bulk-001", // optional
    })
    ```
  </Tab>
</Tabs>

**응답 (200 OK)**

```json theme={null}
{
  "data": {
    "totalRequested": 2,
    "successful": 2,
    "failed": 0,
    "skipped": 0,
    "results": [
      {
        "transactionId": "txn_001",
        "status": "SUCCESS",
        "data": { "id": 1234, "transactionId": "txn_001", "userId": "user_1" }
      },
      {
        "transactionId": "txn_002",
        "status": "SUCCESS",
        "data": { "id": 1235, "transactionId": "txn_002", "userId": "user_2" }
      }
    ]
  }
}
```

### 벌크 결제 파라미터

| 필드           | 타입      | 설명                                             |
| ------------ | ------- | ---------------------------------------------- |
| `payments`   | array   | 결제 항목 배열 (최대 1,000건). 각 항목은 단건 결제와 동일한 필수 파라미터 |
| `callbackId` | string  | 웹훅 추적용 ID (optional)                           |
| `isTest`     | boolean | 테스트 모드 (optional)                              |

### 결과 상태

| 상태        | 설명                         |
| --------- | -------------------------- |
| `SUCCESS` | 결제 등록 성공                   |
| `SKIPPED` | 이미 존재하는 transactionId (중복) |
| `FAILED`  | 등록 실패 (유효성 검증 오류 등)        |

<Info>
  벌크 결제는 부분 성공을 지원합니다. 일부 항목이 실패해도 나머지 항목은 정상 처리됩니다. 벌크 결제 완료 시 `payment.bulk_created` 웹훅 이벤트가 발생합니다.
</Info>
