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

# 인앱 결제

소모품, 비소모품처럼 한 번 구매로 완료되는 상품에 사용하는 일회성 결제 SDK예요. 서비스 소개와 콘솔 설정 방법은 인앱 결제 소개 문서를 참고해 주세요.

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

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

{% hint style="info" %}
**서버 간 통신에는 mTLS 인증서가 필요해요**

인앱 결제의 주문 상태 조회 API는 파트너 서버에서 앱인토스 서버로 호출하는 서버 간 통신이에요. 보안을 위해 서버에 mTLS 인증서를 설정한 뒤 호출해 주세요. 인증서 발급 방법은 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 버전**부터는 **구매 복원 기능**이 추가되었어요.
* **사용자의 기기가 변경되더라도 인앱결제 상품이 지급 유지될 수 있도록 반드시 연동해주세요.**
  * 네이티브 저장소 기능을 활용해 주세요.
  * 토스 로그인 연동과 인앱결제 상태 조회 API를 활용해 주세요.
* 인앱결제 상태 조회 API 사용을 위해서는 반드시 토스 로그인 연동을 먼저 진행해 주세요.
  {% endhint %}

***

### IAP 객체

`IAP`는 인앱 결제 관련 함수를 모아둔 객체예요.

{% hint style="info" %}
**주의하세요**

토스앱 5.219.0 버전부터 지원해요. 인앱 결제를 지원하지 않는 버전에서는 `undefined`를 반환해요.
{% endhint %}

**시그니처**

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

**프로퍼티**

* getProductItemListtypeof getProductItemList

  인앱 결제로 구매할 수 있는 상품 목록을 가져오는 함수예요. 자세한 내용은 [getProductItemList](#getproductitemlist)를 참고하세요.
* createOneTimePurchaseOrdertypeof createOneTimePurchaseOrder

  인앱 결제를 요청하는 함수예요. 자세한 내용은 [createOneTimePurchaseOrder](#createonetimepurchaseorder)를 참고하세요.
* getPendingOrderstypeof getPendingOrders

  대기 중인 주문 목록을 가져와요. 자세한 내용은 [getPendingOrders](#getpendingorders) 문서를 참고하세요.
* getCompletedOrRefundedOrderstypeof getCompletedOrRefundedOrders

  인앱결제로 구매하거나 환불한 주문 목록을 가져와요. 자세한 내용은 [getCompletedOrRefundedOrders](#getcompletedorrefundedorders) 문서를 참고하세요.
* completeProductGranttypeof completeProductGrant

  상품 지급 처리를 완료했다는 메시지를 앱에 전달해요. 자세한 내용은 [completeProductGrant](#completeproductgrant) 문서를 참고하세요.

### 상품 목록 조회하기 <a href="#getproductitemlist" id="getproductitemlist"></a>

**SDK 함수:** `getProductItemList`

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

* IapProductListItem

  인앱결제로 구매할 수 있는 상품 하나의 정보를 담은 객체예요. 상품 목록을 화면에 표시할 때 사용해요.
* **sku** · 필수 · `string`

  상품의 고유 ID예요. [IAP.createOneTimePurchaseOrder](#createonetimepurchaseorder)를 호출할때 사용하는 `productId`와 동일한 값이에요.

**예제**

구매 가능한 인앱결제 상품목록 가져오기

{% 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(() => {
    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();
      },
    });
  }, [sku]);

  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(() => {
    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();
      },
    });
  }, [sku]);

  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`

`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** · 필수

  인앱 결제를 필요한 옵션이에요.

  * **params.sku** · 필수 · `string`

    주문할 상품의 ID예요.
  * **params.processProductGrant** · 필수 · `(params: { orderId: string }) => boolean | Promise<boolean>`

    주문이 만들어진 뒤 실제로 상품을 지급할 때 호출해요. `orderId`를 받아서 지급 성공 여부를 `true` 또는 `Promise<true>`로 반환해요. 지급에 실패하면 `false`를 반환해요.
* **onEvent** · 필수 · `(event: SuccessEvent) => void | Promise<void>`

  결제가 성공했을 때 호출해요.

  * **event.type** · 필수 · `"success"`

    이벤트의 타입이에요. `"success"`를 반환해요.
  * **event.data** · 필수 · `IapCreateOneTimePurchaseOrderResult`

    인앱 결제가 완료되면 결제 세부 정보와 상품 정보를 담아 반환해요. 반환된 정보로 결제한 상품의 정보를 화면에 표시할 때 사용할 수 있어요.

    * **event.data.orderId** · 필수 · `string`

      결제 주문 ID이에요. 결제 완료 후 [결제 상태를 조회](https://developers-apps-in-toss.toss.im/api/getIapOrderStatus.html)할 때 사용해요.
    * **event.data.displayName** · 필수 · `string`

      화면에 표시할 상품 이름이에요.
    * **event.data.displayAmount** · 필수 · `string`

      통화 단위가 포함된 가격 정보예요.
    * **event.data.amount** · 필수 · `number`

      상품 가격 숫자 값이에요.
    * **event.data.currency** · 필수 · `string`

      상품 가격 통화 단위예요.
    * **event.data.fraction** · 필수 · `number`

      가격을 표시할 때 소수점 아래 몇 자리까지 보여줄지 정하는 값이에요.
    * **event.data.miniAppIconUrl** · `string | null`

      미니앱 아이콘 이미지의 URL이에요.
* **onError** · 필수 · `(error: unknown) => void | Promise<void>`

  결제 과정에서 에러가 발생했을 때 호출해요. 에러 객체를 받아서 로깅하거나 복구 절차를 실행할 수 있어요.

**에러코드**

* INVALID\_PRODUCT\_ID : 유효하지 않은 상품 ID이거나, 해당 상품이 존재하지 않습니다. 상품 ID를 확인해주세요.

  유효하지 않은 상품 ID이거나, 해당 상품이 존재하지 않을 때 발생해요.

**반환값**

* () => void

  앱브릿지 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();
      },
    });
  }, []);

  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`

`getPendingOrders` 는 **결제는 완료되었지만 상품이 아직 지급되지 않은 주문 목록**을 가져오는 함수예요. 조회된 주문 정보를 확인하여 사용자에게 상품을 지급하세요. `createOneTimePurchaseOrder` 함수 호출 후 결과를 받지 못한 경우에도 해당 주문을 조회할 수 있어요.

앱 버전이 최소 지원 버전(안드로이드 5.234.0, iOS 5.231.0)보다 낮으면 `undefined`를 반환해요.

**시그니처**

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

**반환값**

* `Promise<{ orders: Order\[] } | undefined>`

  대기 중인 주문 목록(orders)을 포함한 객체를 반환해요. 앱 버전이 최소 지원 버전(안드로이드 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` 필드가 추가되었어요. 이 필드는 **안드로이드 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`

`completeProductGrant` 함수는 **대기 중인 주문의 상품 지급을 완료 처리하는 함수**예요. 사용자에게 상품을 지급하고 `completeProductGrant` 함수를 호출하여 지급 상태를 완료로 변경하세요.

앱 버전이 최소 지원 버전(안드로이드 5.231.0, iOS 5.231.0)보다 낮으면 `undefined`를 반환해요.

**시그니처**

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

**파라미터**

* { params: { orderId: string } }

  결제가 완료된 주문 정보를 담은 객체예요.

  * **params.order** · `Id string`

    주문의 고유 ID예요. 상품 지급을 완료할 주문을 지정할 때 사용해요.

**반환값**

* `Promise<boolean | undefined>`

  상품 지급이 완료됐는지 여부를 반환해요. 앱 버전이 최소 지원 버전(안드로이드 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`

`getCompletedOrRefundedOrders` 는 인앱결제로 구매하고 환불한 주문 목록을 가져와요. 인앱결제 결제 및 상품 지급이 완료된 주문건와 환불된 주문건을 조회할 수 있어요.

결제는 완료되었지만 상품이 아직 지급되지 않은 주문건은 조회되지 않아요. [`getPendingOrders`](#getpendingorders)함수를 통해 `orderId`를 조회하여 사용자에게 상품을 지급한 후 [`completeProductGrant`](#completeproductgrant)함수를 통해 상품 지급을 완료 처리하세요.

앱 버전이 최소 지원 버전(안드로이드 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>`

  페이지네이션을 포함한 주문 목록 객체를 반환해요. 앱 버전이 최소 지원 버전(안드로이드 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 · null`

  다음 페이지 조회를 위한 커서 키예요. 이전 응답의 `nextKey` 값을 사용해요. 첫 호출 시에는 생략하거나 `null`로 전달해요.
* **orders** · 필수 · `Array`

  주문 정보를 담은 배열이에요. 각 요소는 하나의 주문을 나타내요.
* **orders\[].orderId** · 필수 · `string`

  주문의 고유 ID 예요.
* **예제**
*
*
* ### 주문 상태 조회 API
* 서버에서 API로 인앱결제 주문 상태를 직접 조회할 수 있어요. 승인 혹은 환불 응답을 받지 못한 경우에도 사용할 수 있어요.
* <div data-gb-custom-block data-tag="hint" data-style="info" class="hint hint-info"><p><strong>참고하세요</strong></p><p>결제 상태 조회 API 사용을 위해서는 토스 로그인 연동을 먼저 진행해 주세요.</p></div>
* Content-type: `application/json`
* Method: `POST`
* URL: `/api-partner/v1/apps-in-toss/order/get-order-status`
* **요청 헤더**
* 헤더를 포함하지 않으면 모든 주문 건이 응답돼요.
* 헤더에 `x-toss-user-key` 값을 포함하면 해당 userKey의 주문 건만 응답돼요.
* **요청 파라미터**
* ```json
  {
    "orderId": "13c9a1ff-2baa-4495-bbfa-a0826ba8c7c0"
  }
  ```
* **응답**
* **status (enum)**
* **응답 예제**
* ```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": "완료된 주문이에요."
    }
  }
  ```
*
* ### 샌드박스 테스트
* 출시 전에는 반드시 **샌드박스 앱 환경**에서 인앱결제가 정상적으로 동작하는지 테스트해 주세요. 샌드박스에서는 실제 결제(과금)는 발생하지 않으며, 모든 결제가 테스트 시나리오로 처리돼요.
* <div data-gb-custom-block data-tag="hint" data-style="info" class="hint hint-info"><p><strong>참고하세요</strong></p><p>현재 샌드박스 테스트는 <strong>일회성 결제</strong>만 지원해요. 구독결제의 샌드박스 테스트는 현재 지원하지 않아요.</p></div>
* **1. 샌드박스에서 상품 목록 조회 시 동작**
* 샌드박스 앱에서 `getProductItemList()`를 호출하면 콘솔에 등록된 인앱결제 상품 중 **노출 상태가 ON**인 상품만 조회돼요.
* 실제 콘솔에 등록한 상품 목록이 그대로 내려와요.
* 콘솔에서 **노출 OFF**인 상품은 샌드박스 앱에서도 보이지 않아요.
* **2. 필수 테스트 시나리오**
* 샌드박스에서는 아래 3가지 테스트를 반드시 각각 수행해야 해요. 각 시나리오마다 앱이 올바르게 대응하는지 확인해 주세요.
* **① 결제 성공 테스트**
* 성공 콜백(`event.type: success`)이 정상적으로 전달되는지 확인해요.
* 실제 결제(과금)는 발생하지 않아요.
* SDK 1.1.3 이상에서는 파트너사의 **상품 지급 로직까지 성공해야 최종 성공**으로 처리돼요.
* <div data-gb-custom-block data-tag="hint" data-style="info" class="hint hint-info"><p><strong>확인해야 할 항목</strong></p><ul><li><code>orderId</code>, <code>amount</code> 등 <code>event.data</code> 정상 반환 여부</li><li>내부 지급 로직 정상 동작 여부</li><li>지급 완료 후 화면/UI 업데이트</li></ul></div>
* \[동영상 보기]\(../../../../resources/development/iap/iap\_sandbox\_test\_1.mp4)
* **② 결제 성공(서버 실패) 테스트**
* 결제는 성공했지만 파트너 서버의 지급 로직이 실패하는 경우를 반드시 테스트해야 해요.
* 앱은 다음 처리를 지원해야 해요:
* 지급 완료 후 `completeProductGrant` 호출
* 앱 재실행 시 `getPendingOrders`로 미결 주문 복원
* 사용자에게 지급 실패 안내
* 실서비스에서도 충분히 발생 가능한 시나리오이므로 반드시 테스트해야 해요.
* \[동영상 보기]\(../../../../resources/development/iap/iap\_sandbox\_test\_2.mp4)
* **③ 에러 테스트**
* 결제 도중 오류가 발생하는 다양한 상황을 미리 시뮬레이션하세요.
* <div data-gb-custom-block data-tag="hint" data-style="info" class="hint hint-info"><p><strong>테스트해야 할 대표 상황</strong></p><ul><li>네트워크 오류</li><li>사용자가 결제 취소</li><li>내부 오류</li><li>파트너사 상품 지급 실패</li></ul></div>
* \[동영상 보기]\(../../../../resources/development/iap/iap\_sandbox\_test\_3.mp4)
* **3. 테스트 체크리스트**
*
* ### 자주 묻는 질문
*
*
*
*

| 테스트 항목                   | 필수 | 확인 포인트                        |
| ------------------------ | -- | ----------------------------- |
| 상품 목록 노출                 | ✔️ | 콘솔에서 등록한 상품이 정상적으로 내려오는지      |
| 결제 성공 테스트                | ✔️ | `event.data` 처리, 지급 로직, UI 처리 |
| 결제 성공 + 서버 지급 실패 (주문 복원) | ✔️ | 미결 주문 복원 및 재지급 처리             |
| 에러 테스트                   | ✔️ | 에러 UI, 오류 처리, 재시도 흐름          |
| 주문 상태 조회 API             | 권장 | 서버 검증 및 정합성 확인                |

| 상태                  | 설명      | 상세 설명                                   |
| ------------------- | ------- | --------------------------------------- |
| PURCHASED           | 주문 완료   | 인앱 결제 및 상품 지급이 모두 완료된 상태                |
| PAYMENT\_COMPLETED  | 결제 완료   | SDK 1.1.3 이상에서 결제는 완료되었으나 상품 지급이 실패한 상태 |
| FAILED              | 주문 실패   | 결제가 실패한 경우                              |
| REFUNDED            | 주문 환불됨  | 환불 완료된 경우                               |
| ORDER\_IN\_PROGRESS | 주문 진행 중 | 주문이 생성되었지만 결제/지급 처리가 완료되지 않은 경우         |
| NOT\_FOUND          | 주문 없음   | 해당 주문번호를 찾을 수 없는 경우                     |
| MINIAPP\_MISMATCH   | 상품 불일치  | 주문한 상품이 해당 앱의 상품이 아닌 경우                 |
| ERROR               | 내부 오류   | 시스템 내부 오류 발생 시                          |

| 이름                 | 타입     | 설명                                                                               |
| ------------------ | ------ | -------------------------------------------------------------------------------- |
| orderId            | String | 요청한 주문번호                                                                         |
| sku                | String | 주문한 상품 ID                                                                        |
| statusDeterminedAt | String | 주문 완료 일시 (yyyy-MM-dd'T'HH:flag\_mm:ss, KST 고정) `status`가 `REFUNDED`일 경우 환불 완료 일시 |
| status             | String | 주문에 대한 상태 (enum)                                                                 |
| reason             | String | 상태에 대한 설명                                                                        |

| 이름      | 타입     | 필수 | 설명                        |
| ------- | ------ | -- | ------------------------- |
| orderId | String | Y  | 결제 생성 후 취득한 주문번호(uuid v7) |

| 이름              | 타입     | 필수값 여부 | 설명                     |
| --------------- | ------ | ------ | ---------------------- |
| x-toss-user-key | string | N      | 토스 로그인으로 획득한 userKey 값 |


---

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