> 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 已在使用的名称相同的名称，也不能以其他含义使用。

**\[示例]**

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

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

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

***

### 游戏迷你应用

即使不额外联动服务器， **也可以在游戏迷你应用内向用户发放托斯积分**，并在福利标签中展示。

**SDK 函数： `grantPromotionRewardForGame`**

此函数仅可在游戏类迷你应用中调用。在非游戏类中执行会发生错误。

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

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

**签名**

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

**参数**

* **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" %}

```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);
  }
}
```

{% endtab %}

{% tab title="React" %}

```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>;
}
```

{% endtab %}

{% tab title="React Native" %}

```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="发放积分" />;
}
```

{% endtab %}
{% endtabs %}

***

### 非游戏迷你应用

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

* **无需服务器发放**: 无需额外联动服务器，仅通过调用 SDK 函数即可发放积分。
* **通过服务器发放**: 由合作方服务器直接调用 API 发放积分。用于需要防止请求篡改等完整性很重要的场景。

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

**SDK 函数： `grantPromotionReward`**

即使不额外联动服务器， **在非游戏迷你应用内向用户发放托斯积分**，并在福利标签中展示。

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

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

**签名**

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

**参数**

* **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" %}

```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);
  }
}
```

{% endtab %}

{% tab title="React" %}

```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>;
}
```

{% endtab %}

{% tab title="React Native" %}

```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="发放积分" />;
}
```

{% endtab %}
{% endtabs %}

***

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

这是由合作方服务器直接调用 API 向用户发放托斯积分的方式。

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

促销 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 向用户发放奖励时， **在促销预算限额内** 会持续发放。

* **仅允许发放 1 次**若要实现，需要由合作方自行控制。

* 如果使用已经用过的发放 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 | 任选 1 | [通过 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 | 任选 1 | [通过用户识别密钥发放](https://developers-apps-in-toss.toss.im/documentation/common/authentication/hash-key)获得的 `获得。` 值。                                                                                                                      |

**响应参数**

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

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

#### ② 发放促销奖励

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

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

**请求头**

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

| 名称                | 类型     | 必需   | 说明                                                                                                                                                                                                                                  |
| ----------------- | ------ | ---- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `x-toss-user-key` | string | 任选 1 | [通过 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 | 任选 1 | [通过用户识别密钥发放](https://developers-apps-in-toss.toss.im/documentation/common/authentication/hash-key)获得的 `获得。` 值。                                                                                                                      |

**请求参数**

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

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

**响应参数**

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

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

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

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

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

**请求头**

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

| 名称                | 类型     | 必需   | 说明                                                                                                                                                                                                                                  |
| ----------------- | ------ | ---- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `x-toss-user-key` | string | 任选 1 | [通过 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 | 任选 1 | [通过用户识别密钥发放](https://developers-apps-in-toss.toss.im/documentation/common/authentication/hash-key)获得的 `获得。` 值。                                                                                                                      |

**请求参数**

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

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

**响应参数**

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

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

**错误代码**

这是促销 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.
