> For the complete documentation index, see [llms.txt](https://developers-apps-in-toss.toss.im/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developers-apps-in-toss.toss.im/documentation/api-and-sdk-zh/common/growth/promotion.md).

# 推广

服务介绍和控制台设置方法是 [促销介绍文档](https://developers-apps-in-toss.toss.im/guide/marketing/promotion)请参考。

{% hint style="info" %}
**在开始开发促销前请务必确认**

为了避免用户误解，不能使用与 Toss 中已在使用的名称相同的名称，或用作其他含义。

**\[示例]**

* **积分**
  * 如果在迷你应用内通用的自有奖励 **“积分”** 这个名称就不能使用。
    * 可能会被误认为已发放“Toss 积分”。
  * 请使用能与“Toss 积分”明确区分的术语。
* **提款、提现等** — 不能使用会被误认为是变现的术语。
  * 如果在迷你应用内虚拟资产转换为“Toss 积分”， **“Toss 积分发放”** 来标示。
    {% endhint %}

{% hint style="info" %}
**调用限制**

按 userKey 每分钟最多可调用 10 次。超出时会返回错误。
{% endhint %}

***

### 游戏迷你应用

即使没有单独的服务器联动 **也可以在游戏迷你应用内向用户发放 Toss 积分**并展示在福利标签页中。

**SDK 函数： `grantPromotionRewardForGame`**

这个函数只能在游戏分类的迷你应用中调用。在非游戏分类中执行会发生错误。

{% hint style="info" %}
**请注意**

* **Toss App 5.232.0 版本及以上**支持。 `undefined` 值会在低于该版本时返回，此时进入迷你应用时会显示引导更新的页面。
* 为了稳定获取所有用户的标识符， **将 Toss App 最低支持版本上调至 5.232.0**。
* 游戏用户标识符是 **游戏公司内部识别用的密钥**仅用于此目的，不能用这个密钥直接向 Toss 服务器发起请求。
* 如果重复调用函数，同一用户可能会被重复发放奖励， **请务必应用防护逻辑**。
* **在正式开始促销前，需要至少使用测试用促销代码调用 1 次以上**。（通过测试调用，促销会正常注册并转换为已批准状态。）
  {% endhint %}

**签名**

{% code collapsedlinecount="10" %}

```typescript
function grantPromotionRewardForGame({
  params,
}: {
  params: {
    promotionCode: string;
    amount: number;
  };
}): Promise<GrantPromotionRewardForGameResult>;
```

{% endcode %}

**参数**

* **params** · 必填 · `{ params: { promotionCode: string; amount: number } }`

  这是发放积分所需的信息。

  * **params.promotionCode** · 必填 · `string`

    促销代码。
  * **params.amount** · 必填 · `number`

    要发放的积分金额。

**返回值**

* `Promise<{ key: string } | { errorCode: string; message: string } | 'ERROR' | undefined>`

返回积分发放结果。

* `{ key: string }`: 发放积分成功。key 表示奖励密钥。
* `{ errorCode: string, message: string }`: 发放积分失败。请确认错误代码。

**错误代码**

这是在使用促销函数时可能发生的错误代码列表。请参考响应代码或消息， **应用适当的异常处理逻辑**。

{% hint style="info" %}
**`4109` 如果发生错误？**

* 促销预算的 **80% 消耗时会通过邮件通知**。
* 如果要继续进行促销， **请在控制台中增加预算**。
* 如果预算不足， **可在 Biz Wallet 中充值金额**以增加预算。
* 当预算全部消耗完时，促销会 **自动结束， `4109` 并发生错误**。
* 如果因预算不足导致积分发放失败， **可能会引发用户 CS 问题，请注意**。
  {% endhint %}

| 代码          | 消息            | 发生原因 / 应对方法                              |
| ----------- | ------------- | ---------------------------------------- |
| `40000`     |               | 在非游戏迷你应用中调用时                             |
| `4100`      | 找不到促销信息       | 使用未在控制台注册的促销密钥调用时                        |
| `4109`      | 促销未在执行中       | 未在控制台启动促销，或因预算全部耗尽而自动结束时                 |
| `4110`      | 无法发放/回收奖励     | 这是内部系统发生错误的情况， **重新发放逻辑**。               |
| `4111`      | 找不到奖励发放记录     | 查询了不存在的发放记录时                             |
| `4112`      | 促销资金不足        | 由于预算不足导致发放失败时，需要在控制台增加预算或为 Biz Wallet 充值 |
| `4114`      | 超过单次发放金额      |                                          |
| `4116`      | 最大发放金额超过了预算   |                                          |
| `ERROR`     | 发生了未知错误。      |                                          |
| `undefined` | 应用版本低于最低支持版本。 |                                          |

**示例**

{% tabs %}
{% tab title="js" %}
{% code collapsedlinecount="10" %}

```js
import { grantPromotionRewardForGame } from '@apps-in-toss/web-framework';

async function handleGrantPromotionRewardForGame() {
  const result = await grantPromotionRewardForGame({
    params: {
      promotionCode: 'GAME_EVENT_2024',
      amount: 1000,
    },
  });

  if (!result) {
    console.warn('不支持的应用版本。');
  } else if (result === 'ERROR') {
    console.error('发放积分时发生未知错误。');
  } else if ('key' in result) {
    console.log('发放积分成功！', result.key);
  } else if ('errorCode' in result) {
    console.error('发放积分失败：', result.errorCode, result.message);
  }
}
```

{% endcode %}
{% endtab %}

{% tab title="React" %}
{% code collapsedlinecount="10" %}

```tsx
import { grantPromotionRewardForGame } from '@apps-in-toss/web-framework';

function GrantRewardButton() {
  async function handleClick() {
    const result = await grantPromotionRewardForGame({
      params: {
        promotionCode: 'GAME_EVENT_2024',
        amount: 1000,
      },
    });

    if (!result) {
      console.warn('不支持的应用版本。');
      return;
    }

    if (result === 'ERROR') {
      console.error('发放积分时发生未知错误。');
      return;
    }

    if ('key' in result) {
      console.log('发放积分成功！', result.key);
    } else if ('errorCode' in result) {
      console.error('发放积分失败：', result.errorCode, result.message);
    }
  }

  return <button onClick={handleClick}>发放积分</button>;
}
```

{% endcode %}
{% endtab %}

{% tab title="React Native" %}
{% code collapsedlinecount="10" %}

```tsx
import { Button } from 'react-native';
import { grantPromotionRewardForGame } from '@apps-in-toss/framework';

function GrantRewardButton() {
  async function handlePress() {
    const result = await grantPromotionRewardForGame({
      params: {
        promotionCode: 'GAME_EVENT_2024',
        amount: 1000,
      },
    });

    if (!result) {
      console.warn('不支持的应用版本。');
      return;
    }

    if (result === 'ERROR') {
      console.error('发放积分时发生未知错误。');
      return;
    }

    if ('key' in result) {
      console.log('发放积分成功！', result.key);
    } else if ('errorCode' in result) {
      console.error('发放积分失败：', result.errorCode, result.message);
    }
  }

  return <Button onPress={handlePress} title="发放积分" />;
}
```

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

***

### 非游戏迷你应用

在非游戏分类的迷你应用中，通过促销向用户发放 Toss 积分的方法有两种。

* **无需服务器发放**: 无需单独的服务器联动，仅通过调用 SDK 函数即可发放积分。
* **通过服务器发放**: 在合作方服务器上直接调用 API 发放积分。适用于需要防止请求篡改等完整性很重要的情况。

#### 无需服务器发放促销积分

**SDK 函数： `grantPromotionReward`**

即使没有单独的服务器联动 **在非游戏迷你应用内向用户发放 Toss 积分**并展示在福利标签页中。

{% hint style="info" %}
**请注意**

* **Toss App 5.232.0 版本及以上**支持。低于该版本时， `undefined`会返回，并在进入迷你应用时显示更新提示页面。
* 如果重复调用函数，同一用户可能会被重复发放奖励， **请务必应用防护逻辑**。
* **在正式开始促销前，需要至少使用测试用促销代码调用 1 次以上**需要这样做。测试促销代码应在非沙盒应用中 **Toss App（QR 码测试）** 中调用。
  {% endhint %}

**签名**

{% code collapsedlinecount="10" %}

```typescript
function grantPromotionReward({
  params,
}: {
  params: {
    promotionCode: string;
    amount: number;
  };
}): Promise<GrantPromotionRewardResult>;
```

{% endcode %}

**参数**

* **params** · 必填 · `{ params: { promotionCode: string; amount: number } }`

  这是发放积分所需的信息。

  * **params.promotionCode** · 必填 · `string`

    促销代码。
  * **params.amount** · 必填 · `number`

    要发放的积分金额。

**返回值**

* `Promise<{ key: string } | { errorCode: string; message: string } | 'ERROR' | undefined>`

返回积分发放结果。

* `{ key: string }`: 发放积分成功。key 表示奖励密钥。
* `{ errorCode: string, message: string }`: 发放积分失败。请确认错误代码。

**错误代码**

这是在使用促销函数时可能发生的错误代码列表。请参考响应代码或消息， **应用适当的异常处理逻辑**。

{% hint style="info" %}
**`4109` 如果发生错误？**

* 促销预算的 **80% 消耗时会通过邮件通知**。
* 如果要继续进行促销， **请在控制台中增加预算**。
* 如果预算不足， **可在 Biz Wallet 中充值金额**以增加预算。
* 当预算全部消耗完时，促销会 **自动结束， `4109` 并发生错误**。
* 如果因预算不足导致积分发放失败， **可能会引发用户 CS 问题，请注意**。
  {% endhint %}

| 代码          | 消息            | 发生原因 / 应对方法                              |
| ----------- | ------------- | ---------------------------------------- |
| `4100`      | 找不到促销信息       | 使用未在控制台注册的促销密钥调用时                        |
| `4109`      | 促销未在执行中       | 未在控制台启动促销，或因预算全部耗尽而自动结束时                 |
| `4110`      | 无法发放/回收奖励     | 这是内部系统发生错误的情况， **重新发放逻辑**。               |
| `4111`      | 找不到奖励发放记录     | 查询了不存在的发放记录时                             |
| `4112`      | 促销资金不足        | 由于预算不足导致发放失败时，需要在控制台增加预算或为 Biz Wallet 充值 |
| `4114`      | 超过单次发放金额      |                                          |
| `4116`      | 最大发放金额超过了预算   |                                          |
| `ERROR`     | 发生了未知错误。      |                                          |
| `undefined` | 应用版本低于最低支持版本。 |                                          |

**示例**

{% tabs %}
{% tab title="js" %}
{% code collapsedlinecount="10" %}

```js
import { grantPromotionReward } from '@apps-in-toss/web-framework';

async function handleGrantPromotionReward() {
  const result = await grantPromotionReward({
    params: {
      promotionCode: 'EVENT_2024',
      amount: 1000,
    },
  });

  if (!result) {
    console.warn('不支持的应用版本。');
  } else if (result === 'ERROR') {
    console.error('发放积分时发生未知错误。');
  } else if ('key' in result) {
    console.log('发放积分成功！', result.key);
  } else if ('errorCode' in result) {
    console.error('发放积分失败：', result.errorCode, result.message);
  }
}
```

{% endcode %}
{% endtab %}

{% tab title="React" %}
{% code collapsedlinecount="10" %}

```tsx
import { grantPromotionReward } from '@apps-in-toss/web-framework';

function GrantRewardButton() {
  async function handleClick() {
    const result = await grantPromotionReward({
      params: {
        promotionCode: 'EVENT_2024',
        amount: 1000,
      },
    });

    if (!result) {
      console.warn('不支持的应用版本。');
      return;
    }

    if (result === 'ERROR') {
      console.error('发放积分时发生未知错误。');
      return;
    }

    if ('key' in result) {
      console.log('发放积分成功！', result.key);
    } else if ('errorCode' in result) {
      console.error('发放积分失败：', result.errorCode, result.message);
    }
  }

  return <button onClick={handleClick}>发放积分</button>;
}
```

{% endcode %}
{% endtab %}

{% tab title="React Native" %}
{% code collapsedlinecount="10" %}

```tsx
import { Button } from 'react-native';
import { grantPromotionReward } from '@apps-in-toss/framework';

function GrantRewardButton() {
  async function handlePress() {
    const result = await grantPromotionReward({
      params: {
        promotionCode: 'EVENT_2024',
        amount: 1000,
      },
    });

    if (!result) {
      console.warn('不支持的应用版本。');
      return;
    }

    if (result === 'ERROR') {
      console.error('发放积分时发生未知错误。');
      return;
    }

    if ('key' in result) {
      console.log('发放积分成功！', result.key);
    } else if ('errorCode' in result) {
      console.error('发放积分失败：', result.errorCode, result.message);
    }
  }

  return <Button onPress={handlePress} title="发放积分" />;
}
```

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

***

#### 通过服务器发放促销积分

这是在合作方服务器上直接调用 API，向用户发放 Toss 积分的方式。

#### 识别促销目标用户

促销 API 会通过下列 2 种方法之一识别促销对象。请不要同时传递两个值，只选择一个。

| 区分                | 发放方式                                                                                                              |
| ----------------- | ----------------------------------------------------------------------------------------------------------------- |
| `x-toss-user-key` | [Toss 登录](https://developers-apps-in-toss.toss.im/documentation/common/authentication/toss-login)获得的 `userKey` 值。 |
| `x-anon-key`      | [用户识别密钥发放](https://developers-apps-in-toss.toss.im/documentation/common/authentication/hash-key)获得的 hash 值。       |

请根据用途选择。

* 如果已经接入 Toss 登录，或者想与姓名、邮箱等会员信息绑定进行统一管理，就使用 Toss 登录。
* 如果不接入登录，想轻量识别用户，就使用用户识别密钥发放功能。

`x-anon-key`如果想提前确认 (hash) 是否是有效值， [请使用识别密钥验证](/documentation/api-and-sdk-zh/common/authentication/hash-key.md#undefined-4) API。

***

#### 基本信息

| 项目           | 值                                  |
| ------------ | ---------------------------------- |
| Base URL     | `https://apps-in-toss-api.toss.im` |
| 服务器认证        | mTLS（客户端证书）                        |
| Content-Type | `application/json`                 |

{% hint style="info" %}
**服务器间通信需要 mTLS 证书**

促销 API 是从合作方服务器调用 Apps in Toss 服务器的服务器间通信。为保证安全，请先在服务器上配置 mTLS 证书后再调用。证书发放方法请参考 [mTLS 证书发放方法](https://developers-apps-in-toss.toss.im/documentation/integration/getting-started)。
{% endhint %}

#### ① 创建促销奖励发放 Key

发放用于促销的 Key。使用这个 Key 可以向用户发放奖励。

{% hint style="info" %}
**请注意**

* 向用户发放奖励的主体是合作方。使用已获得的 Key 向用户发放奖励时， **在促销预算限额内** 会持续发放。

* **仅允许单次发放**则需要由合作方自行控制。

* 如果尝试使用已使用过的发放 Key 再次发放， `4113` 会发生错误。若需要追加发放， **请发放新的 Key**。

* 已发放的 **Key 有效期为 1 小时**。
  {% endhint %}

* Content-type: application/json

* Method: `POST`

* Endpoint: `/api-partner/v1/apps-in-toss/promotion/execute-promotion/get-key`

**请求头**

识别促销对象的请求头使用以下 2 个中的一个。请不要同时传递两个请求头。

| 名称                | 类型     | 必填  | 说明                                                                                                                                                                                                                                 |
| ----------------- | ------ | --- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `x-toss-user-key` | string | 二选一 | [Toss 登录](https://developers-apps-in-toss.toss.im/documentation/common/authentication/toss-login)获得的 `userKey`。 [获取用户信息](https://developers-apps-in-toss.toss.im/documentation/common/authentication/toss-login#_4-사용자-정보-받기)即可获得。 |
| `x-anon-key`      | string | 二选一 | [用户识别密钥发放](https://developers-apps-in-toss.toss.im/documentation/common/authentication/hash-key)获得的 `hash` 值。                                                                                                                      |

**响应参数**

| 名称  | 类型     | 说明                          |
| --- | ------ | --------------------------- |
| key | String | 用于促销发放的 key 值（base64 编码后的值） |

{% code collapsedlinecount="10" %}

```json
{
  "resultType": "SUCCESS",
  "success": {
    "key": "3oBpxjUgl5r66edcVi7ynHGIjhzr9KOka6FfEKikev0="
  }
}
```

{% endcode %}

#### ② 发放促销奖励

使用已发放的 key **执行促销奖励发放**。发放时会从促销预算中扣除，实际发放可能会有些许延迟。

* Content-type: application/json
* Method: `POST`
* Endpoint: `/api-partner/v1/apps-in-toss/promotion/execute-promotion`

**请求头**

识别促销对象的请求头使用以下 2 个中的一个。请不要同时传递两个请求头。

| 名称                | 类型     | 必填  | 说明                                                                                                                                                                                                                                 |
| ----------------- | ------ | --- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `x-toss-user-key` | string | 二选一 | [Toss 登录](https://developers-apps-in-toss.toss.im/documentation/common/authentication/toss-login)获得的 `userKey`。 [获取用户信息](https://developers-apps-in-toss.toss.im/documentation/common/authentication/toss-login#_4-사용자-정보-받기)即可获得。 |
| `x-anon-key`      | string | 二选一 | [用户识别密钥发放](https://developers-apps-in-toss.toss.im/documentation/common/authentication/hash-key)获得的 `hash` 值。                                                                                                                      |

**请求参数**

| 名称            | 类型      | 必填 | 说明              |
| ------------- | ------- | -- | --------------- |
| promotionCode | String  | Y  | 在控制台中创建的促销代码 ID |
| key           | String  | Y  | 为促销发放而获得的 KEY   |
| amount        | Integer | Y  | 促销发放金额          |

{% code collapsedlinecount="10" %}

```json
{
  "promotionCode": "01JPPJ6SB66BQXXDAKRQZ6SZD7",
  "key": "3oBpxjUgl5r66edcVi7ynHGIjhzr9KOka6FfEKikev0=",
  "amount": 10
}
```

{% endcode %}

**响应参数**

| 名称  | 类型     | 说明            |
| --- | ------ | ------------- |
| key | String | 为促销发放而获得的 KEY |

{% code collapsedlinecount="10" %}

```json
{
  "resultType": "SUCCESS",
  "success": {
    "key": "3oBpxjUgl5r66edcVi7ynHGIjhzr9KOka6FfEKikev0="
  }
}
```

{% endcode %}

#### ③ 查询促销发放结果

发放请求之后的 **查询促销发放状态**。

* Content-type: application/json
* Method: `POST`
* Endpoint: `/api-partner/v1/apps-in-toss/promotion/execution-result`

**请求头**

识别促销对象的请求头使用以下 2 个中的一个。请不要同时传递两个请求头。

| 名称                | 类型     | 必填  | 说明                                                                                                                                                                                                                                 |
| ----------------- | ------ | --- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `x-toss-user-key` | string | 二选一 | [Toss 登录](https://developers-apps-in-toss.toss.im/documentation/common/authentication/toss-login)获得的 `userKey`。 [获取用户信息](https://developers-apps-in-toss.toss.im/documentation/common/authentication/toss-login#_4-사용자-정보-받기)即可获得。 |
| `x-anon-key`      | string | 二选一 | [用户识别密钥发放](https://developers-apps-in-toss.toss.im/documentation/common/authentication/hash-key)获得的 `hash` 值。                                                                                                                      |

**请求参数**

| 名称            | 类型     | 必填 | 说明              |
| ------------- | ------ | -- | --------------- |
| promotionCode | String | Y  | 在控制台中创建的促销代码 ID |
| key           | String | Y  | 为促销发放而获得的 KEY   |

{% code collapsedlinecount="10" %}

```json
{
  "promotionCode": "01JPPJ6SB66BQXXDAKRQZ6SZD7",
  "key": "3oBpxjUgl5r66edcVi7ynHGIjhzr9KOka6FfEKikev0="
}
```

{% endcode %}

**响应参数**

| 名称      | 类型     | 说明                                       |
| ------- | ------ | ---------------------------------------- |
| success | String | 促销发放结果（`SUCCESS` / `PENDING` / `FAILED`) |

{% code collapsedlinecount="10" %}

```json
{
  "resultType": "SUCCESS",
  "success": "PENDING"
}
```

{% endcode %}

**错误代码**

这是在使用促销 API 时可能发生的错误代码列表。请参考响应代码或消息， **应用适当的异常处理逻辑**。

{% hint style="info" %}
**`4109` 如果发生错误？**

* 促销预算的 **80% 消耗时会通过邮件通知**。
* 如果要继续进行促销， **请在控制台中增加预算**。
* 如果预算不足， **可在 Biz Wallet 中充值金额**以增加预算。
* 当预算全部消耗完时，促销会 **自动结束， `4109` 并发生错误**。
* 如果因预算不足导致积分发放失败， **可能会引发用户 CS 问题，请注意**。
  {% endhint %}

| 代码     | 消息          | 发生原因 / 应对方法                              |
| ------ | ----------- | ---------------------------------------- |
| `4100` | 找不到促销信息     | 使用未在控制台注册的促销密钥调用时                        |
| `4109` | 促销未在执行中     | 未在控制台启动促销，或因预算全部耗尽而自动结束时                 |
| `4110` | 无法发放/回收奖励   | 这是内部系统发生错误的情况， **重新发放逻辑**。               |
| `4111` | 找不到奖励发放记录   | 查询了不存在的发放记录时                             |
| `4112` | 促销资金不足      | 由于预算不足导致发放失败时，需要在控制台增加预算或为 Biz Wallet 充值 |
| `4113` | 已发放/已回收的记录  | 在使用同一个 Key 重复发放时，请重新发放新的 Key 后重试。        |
| `4114` | 超过单次发放金额    |                                          |
| `4116` | 最大发放金额超过了预算 |                                          |


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://developers-apps-in-toss.toss.im/documentation/api-and-sdk-zh/common/growth/promotion.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
