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

# WebView + Webhook Integration

> Provide boost UI via WebView and receive results through webhooks

Embed PlayCamp's WebView UI in your game to provide creator boost functionality. This is the fastest integration method — no additional UI development required.

## When to Use This Method

* You want to provide creator boost functionality quickly
* You don't need to build a custom UI
* PlayCamp's built-in creator search/selection UI works for your game

## Full Flow

```mermaid theme={null}
sequenceDiagram
    participant U as Game User
    participant C as Game Client
    participant S as Game Server
    participant P as PlayCamp SDK API

    rect rgb(40, 40, 60)
    Note over U,P: 1. WebView Integration (Creator Boost Selection)
    U->>C: Tap PlayCamp icon
    C->>S: Request to open WebView
    S->>P: Issue OTT (POST /webview/ott)
    P-->>S: Return OTT token
    S-->>C: Pass OTT
    C->>P: Open WebView (/webview/?ott=xxx)
    U->>P: Select creator → Register boost
    end

    rect rgb(40, 60, 40)
    Note over U,P: 2. Webhook Callback (Receive Match Result)
    P-->>S: Webhook: sponsor.created
    Note right of S: Store userId + creatorKey
    end

    rect rgb(60, 40, 40)
    Note over U,P: 3. Payment Registration (On In-Game Purchase)
    U->>C: In-game purchase
    C->>S: Payment complete notification
    S->>P: Register payment (POST /payments)
    P-->>S: Creator attribution confirmed
    end

    rect rgb(60, 60, 40)
    Note over U,P: 4. Settlement
    Note over P: Monthly settlement → Calculate per-creator revenue
    end
```

## Development Tasks

| # | Task                       | Owner       | Description                        |
| - | -------------------------- | ----------- | ---------------------------------- |
| 1 | Place PlayCamp entry point | Game Client | Add icon/button in game            |
| 2 | OTT issuance endpoint      | Game Server | Issue OTT on client request        |
| 3 | Open WebView               | Game Client | Open WebView URL with OTT          |
| 4 | Webhook receiver endpoint  | Game Server | Receive boost match results        |
| 5 | Payment registration API   | Game Server | Send in-game purchases to PlayCamp |

***

## Step 1: Issue OTT (Game Server)

Issue a One-Time Token (OTT) from your game server to PlayCamp SDK API. OTT is **single-use with 60-second TTL**, so you must issue a new one each time the user opens WebView.

<Tabs>
  <Tab title="cURL">
    ```bash theme={null}
    curl -X POST "https://sandbox-sdk-api.playcamp.io/v1/server/webview/ott" \
      -H "Authorization: Bearer ak_server_xxx:secret" \
      -H "Content-Type: application/json" \
      -d '{
        "userId": "game_user_id"
      }'
    ```
  </Tab>

  <Tab title="Node SDK">
    ```typescript theme={null}
    const result = await server.webview.createOtt({
      userId: 'game_user_id',
    });

    // result.ott → OTT token (pass to client)
    ```
  </Tab>

  <Tab title="Go SDK">
    ```go theme={null}
    result, err := server.Webview.CreateOTT(ctx, playcamp.WebviewOttParams{
        UserID: "game_user_id",
    })

    // result.OTT → OTT token (pass to client)
    ```
  </Tab>
</Tabs>

**Response**

```json theme={null}
{
  "data": {
    "ott": "a1b2c3d4e5f6...64char_hex",
    "expiresIn": 60
  }
}
```

<Warning>
  OTT must be issued from the **game server**. Issuing directly from the client exposes the Server Key.
</Warning>

> Details: [WebView Integration - OTT API](/guides/developers/game-integration/webview#ott-issuance-api)

## Step 2: Open WebView (Game Client)

Open the WebView URL with the OTT received from the game server.

### Base URL

| Environment | URL                                                        |
| ----------- | ---------------------------------------------------------- |
| **Sandbox** | `https://sandbox-sdk-api.playcamp.io/webview/?ott={token}` |
| **Live**    | `https://sdk-api.playcamp.io/webview/?ott={token}`         |

### Optional Parameters

| Parameter      | Description                  | Example                |
| -------------- | ---------------------------- | ---------------------- |
| `lang`         | Language (ko, en)            | `&lang=ko`             |
| `tabs`         | Limit visible tabs           | `&tabs=sponsor`        |
| `primaryColor` | Theme color (hex, without #) | `&primaryColor=FF6B35` |

**Full Example**

```
https://sandbox-sdk-api.playcamp.io/webview/?ott=a1b2c3d4...&lang=ko&tabs=sponsor&primaryColor=FF6B35
```

<Info>
  WebView supports both in-game embedded browsers and external browsers (mobile/PC). Choose what fits your game environment.
</Info>

> Details: [WebView Integration - URL Configuration](/guides/developers/game-integration/webview#webview-url-configuration)

## Step 3: Receive Boost Match via Webhook (Game Server)

When the user selects a creator in WebView, PlayCamp sends the match result to your game server's webhook URL.

### Payload (`sponsor.created`)

```json theme={null}
{
  "events": [
    {
      "event": "sponsor.created",
      "timestamp": "2024-01-15T10:30:00.000Z",
      "data": {
        "userId": "game_user_id",
        "campaignId": "campaign_001",
        "creatorKey": "ABC12"
      }
    }
  ]
}
```

| Field        | Description                                      |
| ------------ | ------------------------------------------------ |
| `userId`     | Game user ID (the value passed when issuing OTT) |
| `campaignId` | Campaign ID                                      |
| `creatorKey` | Creator identifier selected by the user          |

### Signature Verification

Verify that the request came from PlayCamp using the `X-Webhook-Signature` header.

<Tabs>
  <Tab title="Node SDK">
    ```typescript theme={null}
    import { verifyWebhook } from '@playcamp/node-sdk';

    app.post('/webhooks/playcamp', express.raw({ type: 'application/json' }), (req, res) => {
      const result = verifyWebhook({
        payload: req.body.toString(),
        signature: req.headers['x-webhook-signature'],
        secret: process.env.WEBHOOK_SECRET,
      });

      if (!result.valid) {
        return res.status(401).json({ error: result.error });
      }

      for (const event of result.payload.events) {
        if (event.event === 'sponsor.created') {
          // Store userId and creatorKey match
          console.log(`User ${event.data.userId} → Creator ${event.data.creatorKey}`);
        }
      }

      res.json({ received: true });
    });
    ```
  </Tab>

  <Tab title="Go SDK">
    ```go theme={null}
    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:    os.Getenv("WEBHOOK_SECRET"),
        })

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

        for _, event := range result.Payload.Events {
            if event.Event == playcamp.WebhookEventSponsorCreated {
                // Store userId and creatorKey match
                fmt.Printf("User %s → Creator %s\n", event.Data.UserID, event.Data.CreatorKey)
            }
        }

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

<Note>
  When a boost is removed, a `sponsor.ended` event is triggered with the same data structure (`userId`, `campaignId`, `creatorKey`).
</Note>

> Details: [Webhook Events](/guides/developers/game-integration/webhook)

## Step 4: Register Payments (Game Server)

When an in-game purchase occurs, send the payment information to PlayCamp. Registered payments are automatically attributed to the user's boosted creator.

<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": "game_user_id",
        "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: 'game_user_id',
      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:           "game_user_id",
        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>

<Info>
  Payments can be registered individually or in bulk (up to 1,000 at once). For refunds, call `POST /payments/{transactionId}/refund`.
</Info>

> Details: [Payment Registration](/guides/developers/game-integration/payment)

## Step 5: Settlement

Monthly settlement is processed based on payment data.

1. **Revenue Close** — Payment data aggregated at end of each month
2. **Revenue Reconciliation** — Match PlayCamp settlement data with your internal records
3. **Settlement Payment** — Payment after reconciliation confirmation
4. **Creator Settlement** — PlayCamp distributes revenue to creators

> Details: [Settlement](/guides/developers/settlement)
