> 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/share/reward.md).

# 分享奖励

服务介绍和控制台设置方法请参考 [分享奖励介绍文档](https://developers-apps-in-toss.toss.im/guide/marketing/share-reward)。

`contactsViral`是 **用户可以将迷你应用分享给好友，并根据结果发放奖励的分享奖励功能**。用户完成分享后，事件会通过应用桥接传递，并基于该事件 **可查看奖励是否发放及发放信息**可查看。

分享奖励可用于以下用途。

* 基于好友邀请 **病毒式拉新**
* 推荐人奖励 / 邀请奖励
* 迷你应用增长活动
* **游戏** · `非游戏迷你应用通用促销`

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

* 此功能在 **Toss App 5.223.0 及以上**才支持。在更低版本中 `undefined`会返回。
* 要使用此功能， **迷你应用审批**是必需的。未审批状态下， `Internal Server Error`会发生。
* 分享奖励 **在控制台中注册的奖励 ID** 为基准运行。
* 奖励发放条件、数量和单位 **App in Toss 控制台**中设置。
  {% endhint %}

{% hint style="info" %}
**测试环境说明**

* 在沙盒应用中不会显示实际的分享 UI， **空白页面**会显示为空白页面。
* 在沙盒中不会实际发放奖励。
* 必须 **控制台提供的二维码**请使用它在 Toss App 中测试。
* 好友列表可能会因以下条件而异。
  * 是否同意接收营销信息
  * 是否同意接收夜间营销信息
  * 是否注册推送令牌
  * 是否屏蔽联系人通知
    {% endhint %}

***

### 流程

**游戏**

**非游戏**

***

**签名**

```ts
function contactsViral(params: ContactsViralParams): () => void;
```

**参数**

* **params** · 必填

  这是在执行联系人分享功能时使用的参数。包含选项设置和事件处理器。详细类型请参见 `ContactsViralParams` 。

**返回值**

* () => void

  会返回应用桥接的 cleanup 函数。分享功能结束后，必须调用此函数释放资源。

**示例：分享并领取奖励**

下面的示例展示了执行分享奖励，并处理分享完成或模块结束事件的基本流程。

{% tabs %}
{% tab title="js" %}

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

function handleContactsViral(moduleId) {
  const cleanup = contactsViral({
    options: { moduleId: moduleId.trim() },
    onEvent: (event) => {
      if (event.type === 'sendViral') {
        console.log('奖励发放：', event.data.rewardAmount, event.data.rewardUnit);
      } else if (event.type === 'close') {
        console.log('模块结束：', event.data.closeReason);
        cleanup();
      }
    },
    onError: (error) => {
      console.error('发生错误：', error);
      cleanup?.();
    },
  });
}
```

{% endtab %}

{% tab title="React" %}

```tsx
import { contactsViral } from '@apps-in-toss/web-framework';
import { Button } from '@toss/tds-mobile';
import { useCallback } from 'react';

function ContactsViralButton({ moduleId }: { moduleId: string }) {
  const handleContactsViral = useCallback(() => {
    try {
      const cleanup = contactsViral({
        options: { moduleId: moduleId.trim() },
        onEvent: (event) => {
          if (event.type === 'sendViral') {
            console.log('奖励发放：', event.data.rewardAmount, event.data.rewardUnit);
          } else if (event.type === 'close') {
            console.log('模块结束：', event.data.closeReason);
            cleanup();
          }
        },
        onError: (error) => {
          console.error('发生错误：', error);
          cleanup?.();
        },
      });
    } catch (error) {
      console.error('执行中错误：', error);
    }
  }, [moduleId]);

  return <Button onClick={handleContactsViral}>分享给好友并领取奖励</Button>;
}
```

{% endtab %}

{% tab title="React Native" %}

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

function ContactsViralButton({ moduleId }: { moduleId: string }) {
  const handleContactsViral = useCallback(() => {
    try {
      const cleanup = contactsViral({
        options: { moduleId: moduleId.trim() },
        onEvent: (event) => {
          if (event.type === 'sendViral') {
            console.log('奖励发放：', event.data.rewardAmount, event.data.rewardUnit);
          } else if (event.type === 'close') {
            console.log('模块结束：', event.data.closeReason);
            cleanup();
          }
        },
        onError: (error) => {
          console.error('发生错误：', error);
          cleanup?.();
        },
      });
    } catch (error) {
      console.error('执行中错误：', error);
    }
  }, [moduleId]);

  return <Button onPress={handleContactsViral}>分享给好友并领取奖励</Button>;
}
```

{% endtab %}
{% endtabs %}

**体验示例应用**

[apps-in-toss-examples](https://github.com/toss/apps-in-toss-examples) 在仓库中 [with-contacts-viral](https://github.com/toss/apps-in-toss-examples/tree/main/with-contacts-viral) 下载代码，或扫描下方二维码亲自体验。

二维码链接: intoss\://with-contacts-viral

***

**类型定义： `ContactsViralOption`**

**签名**

```ts
type ContactsViralOption = {
  moduleId: string;
};
```

**属性**

* **moduleId** · 必填

  用于区分分享奖励的 UUID 格式唯一 ID。请在 App in Toss 控制台的 **迷你应用 > 分享奖励** 菜单中查看。

**`ContactsViralParams`**

`ContactsViralParams` 是 `contactsViral` 这是执行函数时使用的参数类型。可设置选项，并指定事件和错误处理回调。

**签名**

```ts
interface ContactsViralParams {
  options: ContactsViralOption;
  onEvent: (event: ContactsViralEvent) => void;
  onError: (error: unknown) => void;
}
```

**属性**

* **options** · 必填

  这是用于分享功能的选项对象。详细类型请参考 \[ContactsViralOption]\(/pages/jNMtkgvrarYfZ4oqNamT) 文档。
* **onEvent** · 必填

  在发生分享事件时执行的函数。会传递 \[RewardFromContactsViralEvent]\(/pages/jNMtkgvrarYfZ4oqNamT) 或 \[ContactsViralSuccessEvent]\(/pages/jNMtkgvrarYfZ4oqNamT) 类型的事件对象。
* **onError** · 必填

  在发生意外错误时执行的函数。错误对象的类型为 `unknown`。

**`ContactsViralSuccessEvent`**

`ContactsViralSuccessEvent` 是联系人分享模块正常结束时传递的事件对象。会连同结束原因一起提供奖励状态及剩余好友数等相关信息。

**签名**

```ts
type ContactsViralSuccessEvent = {
  type: 'close';
  data: {
    closeReason: 'clickBackButton' | 'noReward';
    sentRewardAmount?: number;
    sendableRewardsCount?: number;
    sentRewardsCount: number;
    rewardUnit?: string;
  };
};
```

**属性**

* **type** · 必填

  这是事件的类型。分享模块结束时 `close` 会带着值返回。
* **data** · 必填

  包含与模块结束相关的详细信息。

  * **data.closeReason** · 必填

    是模块结束的原因。

    `clickBackButton`：用户点击返回按钮结束的情况

    `noReward`：因没有可领取的奖励而结束的情况
  * **data.sentRewardAmount**

    是用户获得的总奖励数量。可选传递。
  * **data.sendableRewardsCount**

    是仍可分享的好友数量。可选传递。
  * **data.sentRewardsCount** · 必填

    是用户已完成分享的好友数量。
  * **data.rewardUnit**

    是奖励的单位。App in Toss 控制台中设置的 `爱心`, `宝石` 等名称会包含在内。可选传递。

**示例**

处理模块结束事件

```ts
contactsViral({
  options: { moduleId: 'your-module-id' },
  onEvent: (event) => {
    if (event.type === 'close') {
      console.log('结束原因：', event.data.closeReason);
      console.log('已完成分享的好友数：', event.data.sentRewardsCount);
    }
  },
  onError: (error) => {
    console.error('发生错误：', error);
  },
});
```

**`RewardFromContactsViralEvent`**

`RewardFromContactsViralEvent` 是一个包含在完成分享给好友后要发放的奖励信息的类型。使用此类型可在分享完成时确认要发放的奖励信息。

**签名**

```ts
type RewardFromContactsViralEvent = {
  type: 'sendViral';
  data: {
    rewardAmount: number;
    rewardUnit: string;
  };
};
```

**属性**

* **type** · 必填

  这是事件的类型。完成向好友分享时 `'sendViral'` 会带着值返回。
* **data** · 必填

  包含要发放的奖励相关信息。

  * **data.rewardAmount** · 必填

    是要发放的奖励数量。为 App in Toss 控制台中设置的数量及金额值。
  * **data.rewardUnit** · 必填

    是奖励的单位。App in Toss 控制台中设置的奖励名称，如 `'爱心'`, `'宝石'` 等。

**示例**

分享完成后处理奖励信息

```ts
contactsViral({
  options: { moduleId: 'your-module-id' },
  onEvent: (event) => {
    if (event.type === 'sendViral') {
      console.log('奖励发放：', event.data.rewardAmount, event.data.rewardUnit);
    }
  },
  onError: (error) => {
    console.error('发生错误：', error);
  },
});
```

### 参考事项

* 分享奖励 **游戏/非游戏迷你应用均可使用**。
* 奖励政策和发放条件 **由控制台设置而非 SDK**决定。
* 事件处理后请务必调用 cleanup 函数。


---

# 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/share/reward.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.
