# Copy in Pool

### Overview

The **Copy In Pool** mode is an extension of the CopyStake product. In this mode, users copy the streamer's bets by contributing funds to a shared pool. The size of their win or loss is proportional to their share of the pool. All actions are processed via the streamer's balance on the operator's side.

This document defines the API contract required for operators to integrate the Copy In Pool functionality.

API Access:

* all requests from CopyStake to Operator contain `X-API-KEY` with value issued by Operator;
* all requests from Operator to CopyStake contain `X-API-KEY` with value issued by CopyStake;

Signature:

* all requests from CopyStake must be signed by CopyStake and verified by Operator;
* all requests from Operator must be signed by Operator and verified by CopyStake;
* **secretKey** for signing the payload in both cases is issued by CopyStake
* the signature will be placed in the `X-REQUEST-SIGNATURE` header of each request and uses **Base64(HmacSHA512(SecretKey, MD5(request body)))**
* dealing with `X-REQUEST-SIGNATURE`&#x20;

<details>

<summary>Example of how to generate a signature for SecretKey = <code>secret</code></summary>

node \
\
Welcome to Node.js v16.13.0. Type ".help" for more information.\
\
var crypto = require('crypto'); \
undefined \
\
var hasher = crypto.createHash('md5'); \
undefined \
\
var hashed = hasher.update('{"id":"103193","action":"CREDIT","amount":4.123,"currency":"USD","exchangeRate":0.00203250655485,"tokenAmount":2500,"operatorId":123,"operatorUserId":"333"}') \
undefined\
\
var hash = hashed.digest('hex'); \
undefined \
\
var hmac = crypto.createHmac('sha512', 'secret'); \
undefined \
\
hmac.update(hash); \
Hmac { \_options: undefined, \[Symbol(kHandle)]: Hmac {}, \[Symbol(kState)]: { \[Symbol(kFinalized)]: false } } \
\
var sign = hmac.digest('base64'); \
undefined \
\
console.log('signature: ' + sign); \
signature: IifPVbfXptagZ6qSXH9vYrQiqSP4sKIC+hV+39z2K+FlLzRoEboPGTTymjmeuAhN1i0ICDyyrrufYspgAJmxHQ==

</details>

Callback URL:

* `callback_url` - provided by Operator, e.g. `callback_url=https://integration.operator.com/api/v1/copystake`

**IMPORTANT:**&#x20;

Operators need to whitelist the Trueplay IPs to receive callbacks about the status of transactions. Otherwise, Trueplay will return errors for deposit/withdrawal tokens requests.

<details>

<summary><strong>IPs for whitelisting</strong></summary>

18.193.249.95

18.184.86.250

18.196.113.251

</details>

**Validation rules**:

**Operator side**:

* to prevent fraud and loss of funds by the follower, Operator should ensure control over the withdrawal of funds from the streamer’s balance on the operator’s site to streamer’s personal account (out of operator’s site);

**CopyStake side**:

* to ensure the correct execution of operations, CopyStake should perform transfer operations only for users identified in the CopyStake system as streamers

### **Transfer operation** <a href="#transfer-operation" id="transfer-operation"></a>

OPERATOR

* **type = DEPOSIT** only under the condition of a successfully executed BET transaction on the follower’s account, i.e., the action when the follower joined the pool;
* **type = WITHDRAWAL** under the condition that the follower decided to leave the pool (or the stream ended, or other scenarios). This operation must be completed with a WIN transaction on the follower’s account.

Performing deposit/withdraw operations concerning the streamer’s balance. This method can be invoked:

\ <mark style="color:green;">`POST`</mark>

`{callback_url}/transfer`

**Headers**

{% code overflow="wrap" %}

```
Content-Type: application/json
X-API-KEY: value issued by Operator
X-REQUEST-SIGNATURE: value generated based on the request payload (secret key issued by CopyStake)
```

{% endcode %}

**Request**

<table data-header-hidden><thead><tr><th width="99.27734375"></th><th width="116.703125"></th><th width="141.4765625"></th><th width="292.74609375"></th></tr></thead><tbody><tr><td><strong>Field</strong></td><td><strong>Type</strong></td><td><strong>Require</strong></td><td><strong>Description</strong></td></tr><tr><td>userId</td><td>String</td><td>mandatory</td><td>user identifier in the operator system</td></tr><tr><td>currency</td><td>String</td><td>optional</td><td><p>currency ISO 4217</p><ul><li>field is present, return the account balance information in the specified currency</li><li>field is absent, return the account balance information with which the game was launched (not a balance inside the game)</li><li><strong>field is absent and there is no active game session - throw</strong> ERR_OPEN_SESSION_NOT_FOUND</li></ul></td></tr></tbody></table>

**Response success**

| **Field** | **Type** | **Require** | **Description**                                                                         |
| --------- | -------- | ----------- | --------------------------------------------------------------------------------------- |
| balance   | Double   | mandatory   | <ul><li>rounding to 8 decimal places</li><li>current available user’s balance</li></ul> |
| currency  | String   | mandatory   | currency ISO 4217                                                                       |

**Response error**

HTTP 400 Bad Request

<table data-header-hidden><thead><tr><th width="150"></th><th width="86"></th><th width="118"></th><th></th></tr></thead><tbody><tr><td><strong>Field</strong></td><td><strong>Type</strong></td><td><strong>Require</strong></td><td><strong>Description</strong></td></tr><tr><td>errorCode</td><td>String</td><td>mandatory</td><td><p><code>ERR_UNKNOWN</code><br>General error status, for cases without a special error code</p><p><code>ERR_TRANSFER_FORBIDDEN</code><br>Any conditions under which the user is prohibited for deposit/withdraw operations</p><p><code>ERR_INSUFFICIENT_BALANCE</code><br>Insufficient streamer balance</p><p><code>ERR_INVALID_USER</code><br>User does not exist</p><p><code>ERR_OPEN_SESSION_NOT_FOUND</code><br>User session not found</p></td></tr><tr><td>errorMessage</td><td>String</td><td>optional</td><td>more detailed description of the errorRequest example</td></tr></tbody></table>

<details>

<summary><strong>Request example</strong></summary>

{% code overflow="wrap" %}

```
curl --location ’{callback_url}/streamer-balance’ \
--header ’Content-Type: application/json’ \
--header ’X-API-KEY: e58d1990-31b9-41f5-aa39-fd150643a8fe’ \
--header ’X-REQUEST-SIGNATURE: FbS2341Uu0mSUAGw4LwF7GVBa7GT7drJXCvHkI+ORHJQ07uxVKAi3pA/0vB8/shlzX16x234FD==’ \
--data ’{
    "userId": "29e946a1-e801-4e27-952d-1d6ebbd19525"
}’
```

{% endcode %}

</details>

**Response example**

{% tabs %}
{% tab title="200: OK Request is succesful" %}
{% code overflow="wrap" %}

```
200 OK
{
    "balance": 805.00000000,
    "currency": "USD",
}
```

{% endcode %}
{% endtab %}

{% tab title="400: Bad Request " %}

```
400 Bad Request
{
    "errorCode": "ERR_BALANCE_FORBIDDEN",
    "errorMessage": "user's balance is prohibited to display"
}
```

{% endtab %}
{% endtabs %}

### **Get balance (Copy In Pool mode)** <a href="#get-balance-copy-in-pool-mode" id="get-balance-copy-in-pool-mode"></a>

This API method allows operators to retrieve the current balance of the streamer before join the pool.

<mark style="color:green;">`POST`</mark>

`{callback_url}/streamer-balance`

**Headers**

{% code overflow="wrap" %}

```
Content-Type: application/json
X-API-KEY: value issued by CopyStake
```

{% endcode %}

**Request**

<table data-header-hidden><thead><tr><th width="168"></th><th width="92"></th><th width="129"></th><th></th></tr></thead><tbody><tr><td><strong>Field</strong></td><td><strong>Type</strong></td><td><strong>Require</strong></td><td><strong>Description</strong></td></tr><tr><td>language</td><td>String</td><td>optional</td><td>User's UI language code from the permitted set in the backoffice (ISO 639)</td></tr><tr><td>streamId</td><td>String</td><td>optional</td><td>User identifier for active stream. If present, followers are directed to the Broadcast page, skipping the Lobby</td></tr></tbody></table>

Response success

<table data-header-hidden><thead><tr><th width="183"></th><th width="132"></th><th width="141"></th><th></th></tr></thead><tbody><tr><td><strong>Field</strong></td><td><strong>Type</strong></td><td><strong>Require</strong></td><td><strong>Description</strong></td></tr><tr><td>url</td><td>String</td><td>mandatory</td><td>CopyStake url with auth token</td></tr></tbody></table>

Response error HTTP&#x20;

400 Bad Request

<table data-header-hidden><thead><tr><th width="156"></th><th width="126"></th><th width="161"></th><th></th></tr></thead><tbody><tr><td><strong>Field</strong></td><td><strong>Type</strong></td><td><strong>Require</strong></td><td><strong>Description</strong></td></tr><tr><td>errorCode</td><td>String</td><td>mandatory</td><td><p>ERR_UNKNOWN<br>General error status, for cases without a special error code.</p><p>ERR_COPYSTAKE_OFF<br>CopyStake is disabled</p></td></tr><tr><td>errorMessage</td><td>String</td><td>optional</td><td>more detailed description of the error</td></tr></tbody></table>

<details>

<summary><strong>Request example</strong></summary>

{% code overflow="wrap" %}

```
Request
curl --location 'https://integration.trueplay.io/api/v1/copystake/init-demo' \
--header 'Content-Type: application/json' \
--header 'X-API-KEY: d922bd1c-e7ac-4c98-9b1a-baa724440b0e' \
--header 'X-REQUEST-SIGNATURE: XhNkfy6WCTdiewUu0mSUAGw4LwF7GVBa7GT7drJXCvHkI+ORHJQ07uxVKAi3pA/0vB8/shlzX16x+77DvqCGRw==' \
--data '{
    "language": "en",
    "streamId": "7b699688-f4ad-407d-baf5-c006ba8d50e2"
}'
```

{% endcode %}

</details>

**Response example**

{% tabs %}
{% tab title="200: OK Request is succesful" %}
{% code overflow="wrap" %}

```
200 OK
{
    "url": "https://copystake.trueplay.io?token=eyJhbGciOiJIUzUxMiJ9.eyJpZF9vcGVyYXRvciI6”
}
```

{% endcode %}
{% endtab %}

{% tab title="400: Bad Request " %}

```
400 Bad Request
{
    "errorCode": "ERR_COPYSTAKE_OFF",
    "errorMessage": "CopyStake is disabled"
}
```

{% endtab %}
{% endtabs %}

### **Game Pool state**

Send information to the Operator that the streamer's balance is the Game Pool.&#x20;

It could be OPEN before the first follower decides to join the pool or CLOSED when the last follower leaves the pool. **Based on the information about state, Operator able to create internal rules to forbidden some operator actions (withdrawal of funds from the streamer’s balance on the operator’s site to streamer’s personal account, token exchanges and other that can impact on streamer’s balance)**

<mark style="color:green;">`POST`</mark>&#x20;

`{callback_url}/game-pool`

**Headers**

```
Content-Type: application/json
X-API-KEY: value issued by CopyStake

Request
```

<table data-header-hidden><thead><tr><th width="217"></th><th width="102"></th><th width="115"></th><th></th></tr></thead><tbody><tr><td><strong>Field</strong></td><td><strong>Type</strong></td><td><strong>Require</strong></td><td><strong>Description</strong></td></tr><tr><td>userId</td><td>String</td><td>mandatory</td><td>User identifier in the operator system</td></tr><tr><td>state</td><td>String</td><td>mandatory</td><td><p>OPEN - first follower decides to join the pool</p><p>CLOSE - last follower leaves the pool</p></td></tr></tbody></table>

<details>

<summary><strong>Request example</strong></summary>

{% code overflow="wrap" %}

```
curl --location ’{callback_url}/game-pool’ \
--header ’Content-Type: application/json’ \
--header ’X-API-KEY: e58d1990-31b9-41f5-aa39-fd150643a8fe’ \
--header ’X-REQUEST-SIGNATURE: FbS2341Uu0mSUAGw4LwF7GVBa7GT7drJXCvHkI+ORHJQ07uxVKAi3pA/0vB8/shlzX16x234FD==’ \
--data ’{
    "userId": "29e946a1-e801-4e27-952d-1d6ebbd19525",
    "state": "OPEN"
}’
```

{% endcode %}

</details>

{% tabs %}
{% tab title="200: OK Request is successful" %}
{% code overflow="wrap" %}

```
200 OK
```

{% endcode %}
{% endtab %}

{% tab title="400: Bad Request " %}
{% code overflow="wrap" %}

```
400 Bad Request
{
    "errorCode": "ERR_INVALID_USER",
    "errorMessage": "user does not exist"
}
```

{% endcode %}
{% endtab %}
{% endtabs %}
