# SDK를 통한 구매 수행

> 구매 정보를 검색하고 플레이어가 구매한 상품의 구매 상태를 결정합니다.

> **Important:**
>
> 결제 제공업체의 경우 `UnityIAPServices` 또는 `StoreController`에 호출할 때 스토어 이름(`PaymentProvider.Name`)을 지정해야 합니다. `PaymentProvider.Name`는 D2C(Direct-to-Consumer) 제공업체 통합을 사용하려는 것을 지정하고 제공업체의 표시 이름을 반환하지 않습니다.

Unity IAP는 애플리케이션이 플레이어가 구매한 것을 인식하고 이를 이행할 수 있도록 스토어에서 구매 정보를 가져옵니다. 이렇게 하면 게임이 앱 외부 또는 다른 기기에서 아이템을 구매한 경우에도 구매 이력을 기반으로 콘텐츠나 자격을 사용자에게 제공할 수 있습니다.

구매는 [`Order`](https://docs.unity3d.com/Packages/com.unity.purchasing@latest?subfolder=/api/UnityEngine.Purchasing.Order.html) 객체로 표시됩니다. `Order`에는 구매에 대한 모든 관련 세부 정보가 포함되어 있으며 스토어에서 구매를 추적하고 관리하는 데 필요한 정보를 제공합니다.

## 구매 검색

사용자가 구매한 상품은 스토어에서 가져올 수 있습니다. 그러나 스토어에서 이미 충족된 소모품을 반환하지 않으므로 소모품은 소모된 후 애플리케이션에서 트래킹해야 합니다. 비소비성 제품 및 구독의 경우, [`FetchPurchases`](https://docs.unity3d.com/Packages/com.unity.purchasing@latest?subfolder=/api/UnityEngine.Purchasing.IPurchaseService.html#UnityEngine_Purchasing_IPurchaseService_FetchPurchases) 또는 [`CheckEntitlement`](https://docs.unity3d.com/Packages/com.unity.purchasing@latest?subfolder=/api/UnityEngine.Purchasing.IPurchaseService.html#UnityEngine_Purchasing_IPurchaseService_CheckEntitlement_UnityEngine_Purchasing_Product_)에 전화할 때 스토어에서 이러한 구매를 정확하게 반환합니다.

## 구매 상태 확인

다음 두 가지 방법으로 구매 상태를 결정할 수 있습니다.

* [`Order`](https://docs.unity3d.com/Packages/com.unity.purchasing@latest?subfolder=/api/UnityEngine.Purchasing.Orders.html)를 사용하여 `Order`가 `PendingOrder`, `ConfirmedOrder`, `DeferredOrder` 또는 `FailedOrder`인지 확인합니다.
* `CheckEntitlement`를 사용하여 `EntitledButNotFinished`, `EntitledUntilConsumed`, `FullyEntitled`, `NotEntitled` 또는 `Unknown`를 반환하는 [`EntitlementStatus`](https://docs.unity3d.com/Packages/com.unity.purchasing@latest?subfolder=/api/UnityEngine.Purchasing.Entitlement.html#UnityEngine_Purchasing_Entitlement_Status)를 수신합니다.

### 구매 속성

| 속성              | 설명                            |
| --------------- | ----------------------------- |
| `transactionId` | 구매에 대한 고유 ID입니다.              |
| `product`       | 구매한 제품입니다.                    |
| `quantity`      | 구매한 제품의 양입니다.                 |
| `receipt`       | 스토어에서 구매를 확인하기 위한 영수증 데이터입니다. |

### 구매 상태

| State       | 설명                        |
| ----------- | ------------------------- |
| `Pending`   | 구매가 지급되었지만 아직 이행되지 않았습니다. |
| `Confirmed` | 구매가 이행되고 확인되었습니다.         |
| `Failed`    | 오류로 인해 구매가 실패했습니다.        |
| `Deferred`  | 구매가 결제를 대기 중입니다.          |

## 구매 프로세스

[`OnPurchasePending`](https://docs.unity3d.com/Packages/com.unity.purchasing@latest?subfolder=/api/UnityEngine.Purchasing.IPurchaseService.html#UnityEngine_Purchasing_IPurchaseService_OnPurchasePending) 콜백은 구매가 이루어지고 이행을 대기 중일 때 호출됩니다. 이 시점에서 애플리케이션은 로컬 콘텐츠를 잠금 해제하거나 구매 영수증을 서버로 전송하여 서버 측 게임 모델을 업데이트하여 구매를 완료해야 합니다.

성공적인 초기화 후 언제든지 `OnPurchasePending`가 호출될 수 있습니다. `OnPurchasePending` 핸들러 실행 중에 애플리케이션에 크래시가 발생하면 다음 번에 Unity IAP가 초기화될 때 다시 호출됩니다. 자체적인 중복 제거 로직을 구현하는 것이 좋습니다.

> **Note:**
>
> 구매를 확인하지 않으면 스토어에서 구매를 다시 전송하며, 일부 스토어에서는 사용자를 보호하기 위해 자동으로 구매를 반환할 수도 있습니다.

```cs
// Handle restore on initialization
private async void Start()
{
    // Setup, e.g. add listeners to your StoreController...
    m_StoreController.OnPurchasePending += OnPurchasePending;
    m_StoreController.OnPurchasesFetched += OnPurchasesFetched;
    await m_StoreController.Connect();

    // Fetch previous purchases (includes confirmed orders)
    m_StoreController.FetchPurchases();
}

// Handle new purchases and pending transactions
private void OnPurchasePending(PendingOrder 순서)
{
    ProcessPurchase(order);
}

// Handle fetched purchases (includes previously confirmed orders)
private void OnPurchasesFetched(정렬 주문)
{
    Foreach (var confirmedOrder in orders.ConfirmedOrders)
    {
        if (confirmedOrder.CartOrdered.Items().FirstOrDefault()?.Product.definition.type != ProductType.Consumable)
        {
            // Mark non-consumable and subscription products as entitled on fetch, as they only need to be granted once
            MarkAsEntitled(confirmedOrder.CartOrdered.Items().FirstOrDefault().Product);
        }
    }
}

// Your ProcessPurchase logic
private void ProcessPurchase(PendingOrder 순서)
{
    Foreach (var product in order.CartOrdered.Items())
    {
        // Grant product
        GrantProduct(product);
    }
    // Confirm the order to finalize the transaction
    m_StoreController.ConfirmPurchase(order);
}
```

## 구매 확인 및 신뢰성

Unity IAP에서는 네트워크 중단 또는 애플리케이션 크래시 중에도 구매가 신뢰할 수 있도록 구매를 명시적으로 확인해야 합니다. 구매에 대한 지급이 이루어지지만 이행되지 않으면 Unity IAP는 다음에 애플리케이션이 초기화될 때 구매를 애플리케이션에 제공합니다. 이 프로세스는 구매 흐름이 중단되거나 애플리케이션이 오프라인 상태일 때 구매가 완료될 때 구매가 손실되는 것을 방지합니다.

구매가 성공적으로 완료되면 해당 `PendingOrder`와 함께 [`ConfirmPurchase`](https://docs.unity3d.com/Packages/com.unity.purchasing@latest?subfolder=/api/UnityEngine.Purchasing.IPurchaseService.html#UnityEngine_Purchasing_IPurchaseService_ConfirmPurchase_UnityEngine_Purchasing_PendingOrder_)에 문의하여 구매를 상점에 확인하십시오.

> **Warning:**
>
> 항상 구매를 확인합니다. 그렇지 않으면 다음과 같은 일이 발생할 수 있습니다.
>
> * Unity IAP는 주문을 확인할 때까지 모든 후속 `FetchPurchases` 호출 또는 세션 시작 시 주문에 대한 `OnPurchasePending`를 다시 호출합니다. 자체적인 중복 제거 로직이 없으면 동일한 아이템을 두 번 이상 허용할 수 있습니다.
> * 일부 스토어는 구매를 자동으로 되돌립니다. 예를 들어 Google Play는 3일 후 확인되지 않은 구매를 환불하고 Apple 앱 스토어 비슷한 보호 로직을 타임라인에 적용합니다.
> * 플레이어에게는 구매가 성공한 것으로 나타나고 사라지는 것으로, 구매가 끊어지는 것처럼 보입니다.

> **Note:**
>
> 소모품의 경우 구매를 확인하면 스토어에서 다시 반환하지 않습니다. 소모품 보상을 항상 원격으로 유지합니다. 소모품 보상을 로컬에 저장하면 데이터를 복구할 수 없는 데이터가 손실될 수 있습니다.

## 클라우드에 대한 지속적인 구매 확인

소모품 구매를 클라우드에 저장하는 경우 구매가 성공적으로 지속되면 [`ConfirmPurchase`](https://docs.unity3d.com/Packages/com.unity.purchasing@latest?subfolder=/api/UnityEngine.Purchasing.IPurchaseService.html#UnityEngine_Purchasing_IPurchaseService_ConfirmPurchase_UnityEngine_Purchasing_PendingOrder_)에 전화해야 합니다.

`Pending`를 반환할 때 Unity IAP는 처리된 것으로 확인될 때까지 기본 스토어에서의 거래를 계속 열립니다. 이렇게 하면 사용자가 소모품이 보류 중인 동안 애플리케이션을 다시 설치해도 소모품 구매가 손실되지 않습니다.

## 구매 복원##restore-purchases

사용자가 앱을 다시 설치하거나 디바이스를 전환할 때 이전에 소유한 제품과 구독에 대한 액세스 권한을 다시 얻을 수 있습니다. IAP가 자격 기록을 가져오고 액세스 권한을 부여하는 방법을 이해합니다.

* 사용자가 애플리케이션을 다시 설치하면 Unity IAP는 첫 번째 `StoreController.FetchPurchases()` 호출 시 소유 제품을 복원합니다.
* IAP는 모든 구매(모든 상태)를 포함하는 `Orders` 객체를 사용하여 `OnPurchasesFetched` 리스너를 호출합니다.
* `PurchaseService.ProcessPendingOrdersOnPurchasesFetched` 설정이 `true`로 설정된 경우 IAP는 실행되지 않은 각 구매에 대해 `OnPurchasePending` 리스너를 호출합니다.
* 동일한 세션에서 후속 `FetchPurchases()` 호출은 동일한 세션에서 이미 본 주문의 `OnPurchasePending`를 트리거하지 않습니다.

## SDK와 함께 서버 측 확인 사용##use-server-side-validation-alongside-the-sdk

Unity IAP(In-App Purchases) SDK를 사용하는 동안 백엔드 API 호출할 수 있습니다. 이 하이브리드 접근 방식으로 서버는 플레이어에게 보상을 제공하기 전에 Unity 기록에 대한 거래를 직접 확인하여 권한을 부여할 수 있습니다. 표준 백엔드 API 메서드와는 달리, 웹후크에 대한 공용 엔드포인트를 노출할 필요가 없으므로 공격 표면을 줄이고 서버 인프라를 단순화할 수 있습니다.

### 클라이언트 측 구현##client-side-implementation

구매가 시작되면 SDK는 `PendingOrder` 오브젝트를 반환합니다. `OrderInfo.TransactionId`를 추출하고 백엔드에 전달합니다.

### 백엔드 인증##backend-authentication

> **Important:**
>
> Unity IAP API 호출하려면 백엔드가 [서비스 계정](/cloud/accounts/create-service-account.md)으로 인증해야 합니다.

서비스 계정의 키 ID와 비밀 키를 사용하여 토큰 교환을 수행하여 상태 비보존 토큰을 받습니다. 자세한 내용은 상태 비보존 토큰을 사용하여 [API 인증](https://services.docs.unity.com/docs/service-account-auth/#authenticate-an-api-using-a-stateless-token)하는 방법을 참고하십시오.

다음 토큰 교환 엔드포인트를 참조하십시오.

```http
POST https://services.api.unity.com/auth/v1/token-exchange?projectId={projectId}&environmentId={envId}
```

### 순서 확인##validate-the-order

백엔드에 Bearer 토큰이 있으면 다음 요청을 통해 Unity IAP Order 서비스를 쿼리하십시오.

```http
GET https://iap.services.api.unity.com/v1/projects/{projectId}/environments/{envId}/orders/{orderId}
```

`orderId`는 이전 단계의 `TransactionId`와 동일합니다.
다음과 같은 중요한 응답 필드를 참조하십시오.

* 항목을 승인하기 전에 `status`가 `paid`여야 합니다.
* `paymentProviderResourceId`에는 Stripe 대시보드에서 교차 참조에 필요한 기본 Stripe Checkout 세션 ID가 포함되어 있습니다.

### 주문 수행 및 완료##fulfill-and-complete-the-order

서버 권한으로 주문을 완료하려면 다음 워크플로를 참고하십시오.

1. 상태가 유료되고 productSku가 예상 아이템과 일치하는지 확인합니다.
2. 플레이어의 데이터베이스를 새 권한으로 업데이트합니다.
3. 백엔드가 클라이언트에 성공 코드를 반환하면 다음 방법 중 하나를 사용하여 Unity IAP 시스템에서 거래를 완료합니다.
   * [API로 직접 주문 업데이트](https://staging.docs.unity.com/en-us/iap/payment-providers/implement-backend.md#update-an-order)
   * 클라이언트 호출 `m_StoreController.ConfirmPurchase(order)`를 만듭니다.

자세한 내용은 API를 통해 수행된 주문을 [마킹하는 방법](./implement-backend.md#mark-orders-as-fulfilled-via-api) 섹션을 참조하십시오.

## 다음 단계##next-steps

이 페이지는 IAP를 사용하여 D2C(Direct-to-Consumer) 결제 제공업체를 설정하는 워크플로의 일부입니다. 이 워크플로를 계속하려면 다음 옵션 중 하나를 선택합니다.

[Integrate D2C payment providers](./workflow.md#fulfill-purchases-through-the-sdk): Integrate D2C payment providers with IAP 워크플로 페이지로 돌아갑니다.
[Test Stripe](./test-stripe.md): 워크플로의 다음 단계로 이동하여 D2C 결제 제공업체를 설정합니다.
