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

# Payment Registration

> Registering in-game payments for creator attribution

Register in-game payment records. Registered payments are automatically attributed to the user's boosted creator.

## Flow

```
1. In-game payment complete  →  2. Send payment info via API  →  3. Creator attribution + settlement
```

## Create Payment

**Request**

<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 Gem Pack",
        "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 Gem Pack',
      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 Gem Pack"),
        Amount:           9900,
        Currency:         "KRW",
        Platform:         playcamp.PaymentPlatformAndroid,
        DistributionType: playcamp.String("MOBILE_STORE"),
        PurchasedAt:      purchasedAt,
    })
    ```
  </Tab>
</Tabs>

**Response (201 Created)**

```json theme={null}
{
  "data": {
    "id": 1234,
    "transactionId": "txn_abc123",
    "userId": "user_12345",
    "productId": "gem_pack_100",
    "productName": "100 Gem Pack",
    "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"
  }
}
```

## Required Parameters

| Field              | Type   | Description                                                 |
| ------------------ | ------ | ----------------------------------------------------------- |
| `userId`           | string | In-game user identifier                                     |
| `transactionId`    | string | Platform-specific unique transaction ID (for deduplication) |
| `productId`        | string | Product ID                                                  |
| `amount`           | number | Payment amount                                              |
| `currency`         | string | Currency code (ISO 4217: USD, KRW recommended)              |
| `platform`         | string | Platform (iOS, Android, Web, Roblox, Other)                 |
| `distributionType` | string | Distribution type (see below)                               |
| `purchasedAt`      | string | Actual payment timestamp (ISO 8601)                         |

## Optional Parameters

| Field         | Type    | Description                                      |
| ------------- | ------- | ------------------------------------------------ |
| `productName` | string  | Product name                                     |
| `callbackId`  | string  | Webhook tracking ID (included in webhook events) |
| `isTest`      | boolean | Test mode (does not create actual data)          |

## Distribution Type (Required)

`distributionType` is a **required parameter** when registering payments. It's used for settlement calculation.

| Value               | Description                                    | Store Fee |
| ------------------- | ---------------------------------------------- | --------- |
| `MOBILE_STORE`      | Mobile external store (Google Play, App Store) | 30%       |
| `PC_STORE`          | PC external store (Steam, etc.)                | 30%       |
| `MOBILE_SELF_STORE` | Mobile self-payment                            | 0%        |
| `PC_SELF_STORE`     | PC self-published store                        | 0%        |

<Info>
  **Settlement Calculation**: `Net Amount = Payment Amount × (1 - Store Fee)`, then PlayCamp platform fee and creator fee are applied separately.

  **Important**: Specify the value matching the actual distribution channel where the payment occurred for accurate settlement.
</Info>

## Currency Conversion

For non-USD currencies, amount is automatically converted to USD (`amountUsd` field).

```javascript theme={null}
// KRW payment example
{ amount: 9900, currency: "KRW" }
// → amountUsd: 7.62 (auto-calculated)
```

## Refund Payment

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

Refunded amounts are deducted from settlement.

## Error Handling

| HTTP | Code               | Description                |
| ---- | ------------------ | -------------------------- |
| 400  | `VALIDATION_ERROR` | Missing required parameter |
| 409  | `CONFLICT`         | Duplicate transactionId    |

## Bulk Payment

Register up to 1,000 payments at once.

**Request**

<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);     // successful count
    console.log(result.failed);         // failed count
    console.log(result.skipped);        // skipped count
    ```
  </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>

**Response (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" }
      }
    ]
  }
}
```

### Bulk Payment Parameters

| Field        | Type    | Description                                                                                   |
| ------------ | ------- | --------------------------------------------------------------------------------------------- |
| `payments`   | array   | Payment items array (max 1,000). Each item has the same required parameters as single payment |
| `callbackId` | string  | Webhook tracking ID (optional)                                                                |
| `isTest`     | boolean | Test mode (optional)                                                                          |

### Result Status

| Status    | Description                                  |
| --------- | -------------------------------------------- |
| `SUCCESS` | Payment registered successfully              |
| `SKIPPED` | Already existing transactionId (duplicate)   |
| `FAILED`  | Registration failed (validation error, etc.) |

<Info>
  Bulk payments support partial success. Even if some items fail, the remaining items are processed normally. A `payment.bulk_created` webhook event is triggered when the bulk payment completes.
</Info>
