> 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/monetization/iap/in-app-purchase.md).

# 应用内支付

这是用于消耗品、非消耗品等一次购买即可完成的商品的单次支付 SDK。服务介绍和控制台设置方法请参考 [应用内支付介绍文档](https://developers-apps-in-toss.toss.im/guide/monetization/in-app-payment)。

{% hint style="info" %}
**BaseURL**

`https://apps-in-toss-api.toss.im`
{% endhint %}

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

应用内支付的订单状态查询 API 是从合作方服务器调用 Apps in Toss 服务器的服务器间通信。为了安全，请先在服务器上配置 mTLS 证书后再调用。证书发放方法请参考 [mTLS 证书发放方法](https://developers-apps-in-toss.toss.im/guide/getting-started/launch/integration-process#mtls-인증서-발급-방법)。
{% endhint %}

联动流程请按照以下顺序进行。

1. [获取商品列表](#getproductitemlist) — `getProductItemList`
2. [发起支付请求](#createonetimepurchaseorder) — `createOneTimePurchaseOrder`
3. [恢复未完成订单](#getpendingorders) — `getPendingOrders`, `completeProductGrant`
4. [查询订单状态](#getcompletedorrefundedorders) — `getCompletedOrRefundedOrders` 或 [订单状态查询 API](#주문-상태-조회-api)

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

* SDK **1.1.3 版本以上**请使用。
* 从 SDK 1.1.3 版本开始， **商品发放完成流程**已新增，函数接口有所变更。
* SDK **1.2.2 版本**起， **购买恢复功能**已新增。
* **请务必联动，以便即使用户的设备更换，应用内支付商品也能继续发放。**
* [原生存储功能](https://developers-apps-in-toss.toss.im/api-and-sdk/common/device/storage)请加以利用。
* 请使用 Toss 登录联动和应用内支付状态查询 API。
* 要使用应用内支付状态查询 API，必须先完成 [Toss 登录联动](https://developers-apps-in-toss.toss.im/guide/user/auth/login)。
  {% endhint %}

***

## IAP 对象

`IAP`是一个汇总应用内支付相关函数的对象。

{% hint style="info" %}
**支持环境**

* 支持平台：React Native、WebView
* 运行环境：Toss App
* SDK 版本：WebView v1.0.3，React Native v1.0.3
* Toss App 最低版本：v5.219.0
  {% endhint %}

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

从 Toss App 5.219.0 版本开始支持。在不支持应用内支付的版本中， `undefined`会返回。
{% endhint %}

**签名**

```typescript
IAP {
  getProductItemList: typeof getProductItemList;
  createOneTimePurchaseOrder: typeof createOneTimePurchaseOrder;
  getPendingOrders: typeof getPendingOrders;
  getCompletedOrRefundedOrders: typeof getCompletedOrRefundedOrders;
  completeProductGrant: typeof completeProductGrant;
}
```

**属性**

| 名称                           | 类型                                    | 说明                    |
| ---------------------------- | ------------------------------------- | --------------------- |
| getProductItemList           | `typeof getProductItemList`           | 获取可通过应用内支付购买的商品列表的函数。 |
| createOneTimePurchaseOrder   | `typeof createOneTimePurchaseOrder`   | 发起应用内支付请求的函数。         |
| getPendingOrders             | `typeof getPendingOrders`             | 获取待处理订单列表。            |
| getCompletedOrRefundedOrders | `typeof getCompletedOrRefundedOrders` | 获取通过应用内支付购买或退款的订单列表。  |
| completeProductGrant         | `typeof completeProductGrant`         | 向应用传递商品发放处理已完成的消息。    |

## 查询商品列表 <a href="#getproductitemlist" id="getproductitemlist"></a>

**SDK 函数：** `getProductItemList`

{% hint style="info" %}
**支持环境**

* 支持平台：React Native、WebView
* 运行环境：Toss App
* SDK 版本：WebView v1.0.3，React Native v1.0.3
* Toss App 最低版本：v5.219.0
  {% endhint %}

`getProductItemList` 是一个包含应用内支付可购买商品列表的函数。用于在界面上展示商品列表。

**签名**

```typescript
function getProductItemList(): Promise<{ products: IapProductListItem[] } | undefined>;
```

**返回值**

`Promise<{ products: IapProductListItem[] } | undefined>` 返回该类型。会返回包含商品列表的对象，如果应用版本低于最低支持版本（5.219.0）， `undefined`会返回。

**属性**

```typescript
interface IapProductListItem {
  sku: string;
  displayAmount: string;
  displayName: string;
  iconUrl: string;
  description: string;
}
```

| 名称            | 必填 | 类型       | 说明                                                            |
| ------------- | -- | -------- | ------------------------------------------------------------- |
| sku           | 必填 | `string` | 商品的唯一 ID。 `IAP.createOneTimePurchaseOrder`时使用的 `productId`相同。 |
| displayAmount | 必填 | `string` | 包含货币单位的价格信息。比如 `"1,000韩元"`会同时显示价格和货币。                         |
| displayName   | 必填 | `string` | 是在界面上显示的商品名称。商品名称是 Apps in Toss 控制台中设置的值。                     |
| iconUrl       | 必填 | `string` | 是商品图标图片的 URL。图标是在 Apps in Toss 控制台中设置的图片。                     |
| description   | 必填 | `string` | 是关于商品的说明。说明是 Apps in Toss 控制台中设置的值。                           |

**示例**

获取可购买的应用内支付商品列表

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

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

async function handleGetProductItemList() {
  const response = await IAP.getProductItemList();

  return response?.products ?? [];
}
```

{% endtab %}

{% tab title="React" %}

```tsx
import { IAP, IapProductListItem } from '@apps-in-toss/web-framework';
import { Button, List, ListRow } from '@toss/tds-mobile';
import { useEffect, useState, useCallback } from 'react';

function IapProductList() {
  const [products, setProducts] = useState<IapProductListItem[]>([]);

  const handleBuy = useCallback((sku: string) => {
    const cleanup = IAP.createOneTimePurchaseOrder({
      options: {
        sku,
        processProductGrant: ({ orderId }) => {
          return true;
        },
      },
      onEvent: (event) => {
        console.log(event);

        if (event.type === 'success') {
          cleanup();
        }
      },
      onError: (error) => {
        console.error(error);
        cleanup();
      },
    });
  }, []);

  useEffect(() => {
    async function fetchProducts() {
      try {
        const response = await IAP.getProductItemList();
        setProducts(response?.products ?? []);
      } catch (error) {
        console.error('获取商品列表失败：', error);
      }
    }

    fetchProducts();
  }, []);

  return (
    <List>
      {products.map((product) => (
        <ListRow
          key={product.sku}
          left={<ListRow.Image type="square" src={product.iconUrl} />}
          contents={
            <ListRow.Texts
              type="3RowTypeA"
              top={product.displayName}
              middle={product.description}
              bottom={product.displayAmount}
            />
          }
          right={
            <Button size="medium" onClick={() => handleBuy(product.sku)}>
              购买
            </Button>
          }
        />
      ))}
    </List>
  );
}
```

{% endtab %}

{% tab title="React Native" %}

```tsx
import { IAP, IapProductListItem } from '@apps-in-toss/framework';
import { Button, List, ListRow } from '@toss/tds-react-native';
import { useEffect, useState, useCallback } from 'react';

function IapProductList() {
  const [products, setProducts] = useState<IapProductListItem[]>([]);

  const handleBuy = useCallback((sku: string) => {
    const cleanup = IAP.createOneTimePurchaseOrder({
      options: {
        sku,
        processProductGrant: ({ orderId }) => {
          return true;
        },
      },
      onEvent: (event) => {
        console.log(event);

        if (event.type === 'success') {
          cleanup();
        }
      },
      onError: (error) => {
        console.error(error);
        cleanup();
      },
    });
  }, []);

  useEffect(() => {
    async function fetchProducts() {
      try {
        const response = await IAP.getProductItemList();
        setProducts(response?.products ?? []);
      } catch (error) {
        console.error('获取商品列表失败：', error);
      }
    }

    fetchProducts();
  }, []);

  return (
    <List>
      {products.map((product) => (
        <ListRow
          key={product.sku}
          left={<ListRow.Image type="square" source={{ uri: product.iconUrl }} />}
          right={
            <Button size="medium" onPress={() => handleBuy(product.sku)}>
              购买
            </Button>
          }
          contents={
            <ListRow.Texts
              type="3RowTypeA"
              top={product.displayName}
              middle={product.description}
              bottom={product.displayAmount}
            />
          }
        />
      ))}
    </List>
  );
}
```

{% endtab %}
{% endtabs %}

**示例响应**

```json
{
  "products": [
    {
      "sku": "sku1",
      "displayName": "去除广告",
      "displayAmount": "4,900韩元",
      "iconUrl": "https://cdn.example.com/icons/premium-monthly.png",
      "description": "去除广告并提供高级功能"
    },
    {
      "sku": "sku2",
      "displayName": "100个金币",
      "displayAmount": "9,900韩元",
      "iconUrl": "https://cdn.example.com/icons/coin-100.png",
      "description": "可在应用内使用的100个金币"
    }
  ]
}
```

**体验示例应用**

[apps-in-toss-examples](https://github.com/toss/apps-in-toss-examples) 仓库中 [with-in-app-purchase](https://github.com/toss/apps-in-toss-examples/tree/main/with-in-app-purchase) 下载代码来体验。

## 发起一次性支付请求 <a href="#createonetimepurchaseorder" id="createonetimepurchaseorder"></a>

**SDK 函数：** `createOneTimePurchaseOrder`

{% hint style="info" %}
**支持环境**

* 支持平台：React Native、WebView
* 运行环境：Toss App
* SDK 版本：WebView v1.0.3，React Native v1.0.3
* Toss App 最低版本：v5.219.0
  {% endhint %}

`createOneTimePurchaseOrder` 该函数会弹出应用内支付窗口，用户会继续完成支付。如果支付过程中发生错误，会根据错误类型跳转到错误页面。

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

支付成功后 30 秒内 `processProductGrant` 回调未被调用，或者该回调的结果不是 true 时， `{appName} 出现问题了。请申请退款` 页面可能会显示。
{% endhint %}

**签名**

```typescript
function createOneTimePurchaseOrder(params: IapCreateOneTimePurchaseOrderOptions): () => void;
```

**参数**

```typescript
interface IapCreateOneTimePurchaseOrderOptions {
  options: { sku: string; processProductGrant: (params: { orderId: string }) => boolean | Promise<boolean> };
  onEvent: (event: SuccessEvent) => void | Promise<void>;
  onError: (error: unknown) => void | Promise<void>;
}

interface IapCreateOneTimePurchaseOrderResult {
  orderId: string;
  displayName: string;
  displayAmount: string;
  amount: number;
  currency: string;
  fraction: number;
  miniAppIconUrl: string | null;
}

interface SuccessEvent {
  type: 'success';
  data: IapCreateOneTimePurchaseOrderResult;
}
```

| 名称                          | 必填 | 类型       | 说明                                                                   |
| --------------------------- | -- | -------- | -------------------------------------------------------------------- |
| options                     | 必填 | 支付选项对象   | 这是应用内支付所需的选项。                                                        |
| options.sku                 | 必填 | `string` | 是要下单的商品 ID。                                                          |
| options.processProductGrant | 必填 | 商品发放回调   | 在订单创建后实际发放商品时调用。 `orderId`接收 `true` 或 `Promise<true>`返回给 `false`会返回。 |
| onEvent                     | 必填 | 成功事件回调   | 在支付成功时调用。                                                            |
| onError                     | 必填 | 错误回调     | 在支付过程中发生错误时调用。可以接收错误对象并进行日志记录或执行恢复流程。                                |

**SuccessEvent 属性**

| 名称                  | 必填 | 类型                                    | 说明                          |
| ------------------- | -- | ------------------------------------- | --------------------------- |
| type                | 必填 | `"success"`                           | 是事件类型。                      |
| data                | 必填 | `IapCreateOneTimePurchaseOrderResult` | 在应用内支付完成后，会连同支付明细和商品信息一起返回。 |
| data.orderId        | 必填 | `string`                              | 支付订单 ID。支付完成后查询支付状态时使用。     |
| data.displayName    | 必填 | `string`                              | 是在界面上显示的商品名称。               |
| data.displayAmount  | 必填 | `string`                              | 是包含货币单位的价格信息。               |
| data.amount         | 必填 | `number`                              | 是商品价格的数值。                   |
| data.currency       | 必填 | `string`                              | 是商品价格的货币单位。                 |
| data.fraction       | 必填 | `number`                              | 是用于确定显示价格时小数点后保留几位的值。       |
| data.miniAppIconUrl | 可选 | `string` 或 `null`                     | 是迷你应用图标图片的 URL。             |

**错误代码**

| 错误代码                              | 说明                                          |
| --------------------------------- | ------------------------------------------- |
| `INVALID_PRODUCT_ID`              | 在商品 ID 无效，或者该商品不存在时发生。                      |
| `PAYMENT_PENDING`                 | 在用户请求的支付尚在等待批准时发生。                          |
| `NETWORK_ERROR`                   | 在发生网络错误时发生。                                 |
| `INVALID_USER_ENVIRONMENT`        | 在某些设备、账户或设置环境下无法购买该商品时发生。                   |
| `APP_MARKET_VERIFICATION_FAILED`  | 在用户完成支付但在应用商店中验证用户信息失败时发生。用户需要联系应用商店申请退款。   |
| `TOSS_SERVER_VERIFICATION_FAILED` | 在用户完成支付但因发送到服务器失败而无法保存支付信息时发生。              |
| `INTERNAL_ERROR`                  | 在服务器内部出现问题而无法处理请求时发生。                       |
| `KOREAN_ACCOUNT_ONLY`             | 在 iOS 环境下，用户账号不是韩国账号时发生。                    |
| `USER_CANCELED`                   | 在用户未完成支付就离开订单页时发生。                          |
| `PRODUCT_NOT_GRANTED_BY_PARTNER`  | 在合作方的商品发放失败时发生。仅在 Toss App 5.230.0 以上版本中发生。 |

**返回值**

`() => void` 返回类型为 () => void 的 App Bridge cleanup 函数。应用内支付功能结束后，必须调用该函数以释放资源。

**示例**

跳转到特定的应用内支付订单页

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

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

let cleanup;

function handleBuyProduct(sku) {
  cleanup = IAP.createOneTimePurchaseOrder({
    options: {
      sku,
      processProductGrant: ({ orderId }) => {
        console.log('商品发放逻辑执行：', orderId);
        return true;
      },
    },
    onEvent: (event) => {
      console.log('事件：', event);
      cleanup?.();
    },
    onError: (error) => {
      console.error('应用内支付失败了：', error);
      cleanup?.();
    },
  });
}

window.addEventListener('pagehide', () => {
  cleanup?.();
});
```

{% endtab %}

{% tab title="React" %}

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

interface Props {
  sku: string;
}

function IapCreateOneTimePurchaseOrderButton({ sku }: Props) {
  const handleBuy = useCallback(() => {
    const cleanup = IAP.createOneTimePurchaseOrder({
      options: {
        sku,
        processProductGrant: ({ orderId }) => {
          return true;
        },
      },
      onEvent: (event) => {
        console.log(event);
        cleanup();
      },
      onError: (error) => {
        console.error(error);
        cleanup();
      },
    });
  }, [sku]);

  return <Button onClick={handleBuy}>购买</Button>;
}
```

{% endtab %}

{% tab title="React Native" %}

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

interface Props {
  sku: string;
}

function IapCreateOneTimePurchaseOrderButton({ sku }: Props) {
  const handleClick = useCallback(() => {
    const cleanup = IAP.createOneTimePurchaseOrder({
      options: {
        sku,
        processProductGrant: ({ orderId }) => {
          return true;
        },
      },
      onEvent: (event) => {
        console.log(event);
        cleanup();
      },
      onError: (error) => {
        console.error(error);
        cleanup();
      },
    });
  }, [sku]);

  return <Button onPress={handleClick}>购买</Button>;
}
```

{% endtab %}
{% endtabs %}

**体验示例应用**

[apps-in-toss-examples](https://github.com/toss/apps-in-toss-examples) 仓库中 [with-in-app-purchase](https://github.com/toss/apps-in-toss-examples/tree/main/with-in-app-purchase) 下载代码来体验。

## 查询未完成订单 <a href="#getpendingorders" id="getpendingorders"></a>

**SDK 函数：** `getPendingOrders`

{% hint style="info" %}
**支持环境**

* 支持平台：React Native、WebView
* 运行环境：Toss App、Sandbox App
* SDK 版本：WebView v1.4.8，React Native v1.4.8
* Toss App 最低版本：iOS v5.231.0，Android v5.235.0
* Sandbox App 版本：iOS 2025-10-28，Android 2025-10-22
  {% endhint %}

`getPendingOrders` 是 **支付已完成但商品尚未发放的订单列表**的函数。请查看查询到的订单信息并向用户发放商品。 `createOneTimePurchaseOrder` 即使在调用函数后未收到结果，也可以查询该订单。

如果应用版本低于最低支持版本（Android 5.234.0、iOS 5.231.0）， `undefined`会返回。

**签名**

```typescript
function getPendingOrders(): Promise<{ orders: Order[] } | undefined>;
```

**返回值**

`Promise<{ orders: Order[] } | undefined>` 返回该类型。会返回包含待处理订单列表的对象，如果应用版本低于最低支持版本（Android 5.234.0、iOS 5.231.0）， `undefined`会返回。

**返回对象属性**

```tsx
interface Order {
  orderId: string;
  sku: string;
  paymentCompletedDate?: string;
}
```

| 名称                             | 必填 | 类型        | 说明                           |
| ------------------------------ | -- | --------- | ---------------------------- |
| orders                         | 必填 | `Order[]` | 这是待处理订单的数组。如果没有待处理订单，则返回空数组。 |
| orders\[].orderId              | 必填 | `string`  | 这是订单的唯一 ID。                  |
| orders\[].sku                  | 必填 | `string`  | 这是订单商品的唯一 ID。                |
| orders\[].paymentCompletedDate | 可选 | `string`  | 表示支付完成的时间点。                  |

{% hint style="info" %}
**字段更新说明**

* **SDK 1.4.2**: `sku` 已新增字段。此字段 **Android 5.234.0 及以上，iOS 5.231.0 及以上**仅会在
* **SDK 1.4.8**: `paymentCompletedDate` 已新增字段。可以确认支付完成时间点。
  {% endhint %}

**示例**

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

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

async function fetchOrders() {
  try {
    const pendingOrders = await IAP.getPendingOrders();
    return pendingOrders;
  } catch (error) {
    console.error(error);
  }
}
```

{% endtab %}

{% tab title="React" %}

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

async function fetchOrders() {
  try {
    const pendingOrders = await IAP.getPendingOrders();
    return pendingOrders;
  } catch (error) {
    console.error(error);
  }
}
```

{% endtab %}

{% tab title="React Native" %}

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

async function fetchOrders() {
  try {
    const pendingOrders = await IAP.getPendingOrders();
    return pendingOrders;
  } catch (error) {
    console.error(error);
  }
}
```

{% endtab %}
{% endtabs %}

## 将商品发放标记为完成 <a href="#completeproductgrant" id="completeproductgrant"></a>

**SDK 函数：** `completeProductGrant`

{% hint style="info" %}
**支持环境**

* 支持平台：React Native、WebView
* 运行环境：Toss App
* SDK 版本：WebView v1.2.2，React Native v1.2.2
* Toss App 最低版本：iOS v5.231.0，Android v5.231.0
  {% endhint %}

`completeProductGrant` 函数是 **用于将待处理订单的商品发放标记为完成的函数**。向用户发放商品后， `completeProductGrant` 请调用函数将发放状态更改为完成。

如果 App 版本低于最低支持版本（Android 5.231.0，iOS 5.231.0）， `undefined`会返回。

**签名**

```typescript
function completeProductGrant(params: {
  params: {
    orderId: string;
  };
}): Promise<boolean | undefined>;
```

**参数**

| 名称             | 必填 | 类型                    | 说明                         |
| -------------- | -- | --------------------- | -------------------------- |
| params         | 必填 | `{ orderId: string }` | 这是包含已完成支付的订单信息的对象。         |
| params.orderId | 必填 | `string`              | 这是订单的唯一 ID。用于指定要完成商品发放的订单。 |

**返回值**

`Promise<boolean | undefined>` 返回类型。会返回商品发放是否已完成；如果 App 版本低于最低支持版本（Android 5.233.0，iOS 5.233.0）， `undefined`会返回。

**示例**

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

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

async function handleCompleteProductGrant(orderId) {
  try {
    await IAP.completeProductGrant({ params: { orderId } });
  } catch (error) {
    console.error(error);
  }
}
```

{% endtab %}

{% tab title="React" %}

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

async function handleCompleteProductGrant(orderId: string) {
  try {
    await IAP.completeProductGrant({ params: { orderId } });
  } catch (error) {
    console.error(error);
  }
}
```

{% endtab %}

{% tab title="React Native" %}

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

async function handleCompleteProductGrant(orderId: string) {
  try {
    await IAP.completeProductGrant({ params: { orderId } });
  } catch (error) {
    console.error(error);
  }
}
```

{% endtab %}
{% endtabs %}

## 查询已完成·已退款订单 <a href="#getcompletedorrefundedorders" id="getcompletedorrefundedorders"></a>

**SDK 函数：** `getCompletedOrRefundedOrders`

{% hint style="info" %}
**支持环境**

* 支持平台：React Native、WebView
* 运行环境：Toss App
* SDK 版本：WebView v1.2.2，React Native v1.2.2
* Toss App 最低版本：iOS v5.231.0，Android v5.231.0
  {% endhint %}

`getCompletedOrRefundedOrders` 会获取通过应用内支付购买并退款的订单列表。可以查询应用内支付支付及商品发放已完成的订单，以及已退款的订单。

支付已完成但商品尚未发放的订单不会被查询到。 [`getPendingOrders`](#getpendingorders)通过函数 `orderId`进行查询并向用户发放商品后 [`completeProductGrant`](#completeproductgrant)请通过函数将商品发放标记为完成。

如果 App 版本低于最低支持版本（Android 5.231.0，iOS 5.231.0）， `undefined`会返回。

{% hint style="info" %}
**分页**

* **每页最多 50 个**订单会被返回。
* 当存在下一页时， `hasNext`为 `true`，并且响应中的 `nextKey`作为下一次调用的 `key` 参数继续查询。
  {% endhint %}

**签名**

```typescript
function getCompletedOrRefundedOrders(params?: {
  key?: string | null;
}): Promise<CompletedOrRefundedOrdersResult | undefined>;
```

**返回值**

`Promise<CompletedOrRefundedOrdersResult | undefined>` 返回类型。会返回包含分页的订单列表对象；如果 App 版本低于最低支持版本（Android 5.231.0，iOS 5.231.0）， `undefined`会返回。

**返回对象属性**

```tsx
interface CompletedOrRefundedOrdersResult {
  hasNext: boolean;
  nextKey?: string | null;
  orders: {
    orderId: string;
    sku: string;
    status: 'COMPLETED' | 'REFUNDED';
    date: string;
  }[];
}
```

| 名称                | 必填 | 类型                       | 说明                                                                                               |
| ----------------- | -- | ------------------------ | ------------------------------------------------------------------------------------------------ |
| hasNext           | 必填 | `boolean`                | 这是是否有下一页。 `true`表示还有更多订单。                                                                        |
| nextKey           | 可选 | `string` 或 `null`        | 这是用于查询下一页的游标键。使用前一次响应中的 `nextKey` 值。首次调用时可省略或 `null`传入。                                          |
| orders            | 必填 | `Array`                  | 这是包含订单信息的数组。每个元素表示一个订单。                                                                          |
| orders\[].orderId | 必填 | `string`                 | 这是订单的唯一 ID。                                                                                      |
| orders\[].sku     | 必填 | `string`                 | 这是订单商品的唯一 ID。                                                                                    |
| orders\[].status  | 必填 | `COMPLETED` 或 `REFUNDED` | 这是订单状态。 `COMPLETED`表示订单已完成状态， `REFUNDED`表示已退款状态。                                                 |
| orders\[].date    | 必填 | `string`                 | 这是订单的日期信息。采用 ISO 8601 格式（`YYYY-MM-DDTHH:mm:ss`）。如果订单状态为 `COMPLETED`，则表示下单日期， `REFUNDED`，则表示退款日期。 |

**示例**

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

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

async function fetchOrders() {
  try {
    const orders = await IAP.getCompletedOrRefundedOrders();
    return orders;
  } catch (error) {
    console.error(error);
  }
}
```

{% endtab %}

{% tab title="React" %}

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

async function fetchOrders() {
  try {
    const orders = await IAP.getCompletedOrRefundedOrders();
    return orders;
  } catch (error) {
    console.error(error);
  }
}
```

{% endtab %}

{% tab title="React Native" %}

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

async function fetchOrders() {
  try {
    const orders = await IAP.getCompletedOrRefundedOrders();
    return orders;
  } catch (error) {
    console.error(error);
  }
}
```

{% endtab %}
{% endtabs %}

***

## 订单状态查询 API

可在服务器端通过 API 直接查询应用内支付订单状态。即使未收到批准或退款响应，也可以使用。

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

要使用支付状态查询 API， [Toss 登录联动](https://developers-apps-in-toss.toss.im/guide/user/auth/login)。
{% endhint %}

* Content-type: `application/json`
* Method: `POST`
* URL: `/api-partner/v1/apps-in-toss/order/get-order-status`

**请求头**

| 名称              | 类型     | 是否必填 | 说明                      |
| --------------- | ------ | ---- | ----------------------- |
| x-toss-user-key | string | N    | 通过 Toss 登录获取的 userKey 值 |

* 如果不包含该头信息，将返回所有订单。
* 在头信息中 `x-toss-user-key` 如果包含该值，则只返回该 userKey 的订单。

**请求参数**

| 名称      | 类型     | 必填 | 说明                   |
| ------- | ------ | -- | -------------------- |
| orderId | String | Y  | 支付创建后获得的订单号（uuid v7） |

```json
{
  "orderId": "13c9a1ff-2baa-4495-bbfa-a0826ba8c7c0"
}
```

**响应**

| 名称                 | 类型     | 说明                                                                      |
| ------------------ | ------ | ----------------------------------------------------------------------- |
| orderId            | String | 请求的订单号                                                                  |
| sku                | String | 下单商品 ID                                                                 |
| statusDeterminedAt | String | 订单完成时间（`yyyy-MM-dd'T'HH:mm:ss`，固定为 KST）。 `status`为 `REFUNDED`时表示退款完成时间。 |
| status             | String | 订单状态（enum）                                                              |
| reason             | String | 状态说明                                                                    |

**status (enum)**

| 状态                  | 说明    | 详细说明                               |
| ------------------- | ----- | ---------------------------------- |
| `PURCHASED`         | 订单完成  | 应用内支付和商品发放都已完成的状态                  |
| `PAYMENT_COMPLETED` | 支付完成  | 在 SDK 1.1.3 及以上版本中，支付已完成但商品发放失败的状态 |
| `FAILED`            | 订单失败  | 支付失败时                              |
| `REFUNDED`          | 订单已退款 | 退款已完成时                             |
| `ORDER_IN_PROGRESS` | 订单进行中 | 订单已创建但支付/发放处理尚未完成时                 |
| `NOT_FOUND`         | 无订单   | 找不到对应订单号时                          |
| `MINIAPP_MISMATCH`  | 商品不匹配 | 所下订单的商品不是该应用的商品时                   |
| `ERROR`             | 内部错误  | 系统内部发生错误时                          |

**响应示例**

```json
{
  "resultType": "SUCCESS",
  "success": {
    "orderId": "13c9a1ff-2baa-4495-bbfa-a0826ba8c7c0",
    "sku": "ait.0000010000.af647449.3bd55cfd00.0000000475",
    "statusDeterminedAt": "2025-09-12T16:57:12",
    "status": "PAYMENT_COMPLETED",
    "reason": "支付已完成。"
  }
}
```

```json
{
  "resultType": "SUCCESS",
  "success": {
    "orderId": "13c9a1ff-2baa-4495-bbfa-0000000000",
    "sku": "ait.0000010000.af647449.00000000000.0000000475",
    "statusDeterminedAt": "2025-09-12T16:57:12",
    "status": "PURCHASED",
    "reason": "这是已完成的订单。"
  }
}
```

***

## 沙盒测试

在发布前务必 **沙盒 App 环境**请测试应用内支付在其中是否正常运行。沙盒中不会产生真实支付（扣费），所有支付都会按测试场景处理。

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

目前沙盒测试仅 **一次性支付**支持。当前不支持订阅支付的沙盒测试。
{% endhint %}

**1. 在沙盒中查询商品列表时的行为**

在沙盒 App 中 `getProductItemList()`时，控制台中注册的应用内支付商品里 **曝光状态为 ON**的商品才会被查询到。

* 实际在控制台注册的商品列表会原样返回。
* 在控制台中 **曝光 OFF**的商品在沙盒 App 中也不会显示。

**2. 必测测试场景**

在沙盒中必须分别执行以下 3 种测试。请确认应用在每个场景下都能正确响应。

**① 支付成功测试**

* 成功回调（`event.type: success`）是否正常传递。
* 不会产生真实支付（扣费）。
* 在 SDK 1.1.3 及以上版本中，需要连同合作方的 **商品发放逻辑也成功，才算最终成功**。

{% hint style="info" %}
**需确认的项目**

* `orderId`, `amount` 等 `event.data` 是否正常返回
* 内部发放逻辑是否正常运行
* 发放完成后的界面/UI 更新
  {% endhint %}

**② 支付成功（服务器失败）测试**

必须测试支付成功但合作方服务器的发放逻辑失败的情况。

应用需要支持以下处理。

* 向用户告知发放失败
* 重新启动应用时 `getPendingOrders`通过其恢复未完成订单
* 发放完成后 `completeProductGrant` 调用

这是在正式服务中也很可能发生的场景，因此必须测试。

**③ 错误测试**

请提前模拟支付过程中发生错误的各种情况。

{% hint style="info" %}
**需要测试的典型场景**

* 网络错误
* 用户取消支付
* 内部错误
* 合作方商品发放失败
  {% endhint %}

**3. 测试清单**

| 测试项                  | 必填 | 确认点                        |
| -------------------- | -- | -------------------------- |
| 商品列表展示               | 必填 | 控制台中注册的商品是否正常返回            |
| 支付成功测试               | 必填 | `event.data` 处理、发放逻辑、UI 处理 |
| 支付成功 + 服务器发放失败（订单恢复） | 必填 | 未完成订单恢复及重新发放处理             |
| 错误测试                 | 必填 | 错误 UI、错误处理、重试流程            |
| 订单状态查询 API           | 推荐 | 服务器验证及一致性确认                |

***

## 常见问题

**应用内支付失败时 `orderId` 属性不会返回。**

在 SDK 1.0.3 及以上版本中，包含批准/失败在内的所有订单都会 `orderId` 传递该属性。不过，如果由于网络错误等原因 `orderId`在生成之前发生错误时 `orderId`可能不会返回。

**应用内支付失败时 `errorCode` 属性不会返回。**

在 SDK 1.1.3 及以上版本中 `errorCode`已修改为可正常返回。请更新到最新 SDK。

**如果发生订单失败，应如何处理？**

请在 SDK 1.2.2 及以上版本中使用订单恢复函数进行对接。 `getPendingOrders` 使用函数查询未完成订单，并将查询到订单的 `orderId`用于向用户发放商品。 `completeProductGrant` 请调用函数将发放状态改为完成。

**如何在 Toss App 中查看应用内支付购买记录？**

Toss App **5.229.1 及以上版本**中，用户可以查看应用内支付购买记录。

* **Google 支付订单**：点击“申请退款”按钮，选择原因后可发起退款请求
* **合作方**：可在控制台的退款记录中批准或拒绝退款请求
* **结果通知**：退款处理结果将通过推送通知发送给用户


---

# 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/monetization/iap/in-app-purchase.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.
