# バックエンドAPIによる購入の履行

> 購入イベントをリッスンし、プレイヤーのエンタイトルメントを追跡する独自のバックエンドを実装します。

ゲームでサーバー権限エンタイトルメントを使用する場合は、以下の実行するバックエンドシステムを実装します。

1. [Webhook イベント (JWT)](#validate-webhook-events-\(jwt\)) を検証します。
2. [イベントを解析](#webhook-event-shape)します。
3. [イベントタイプ](#event-types)に基づいて、システム内のエンタイトルメントまたはインベントリを更新します。
4. [注文を履行](#mark-orders-as-fulfilled-via-api)済みとしてマークします。
5. [イベント反応を返し](#return-an-event-response)ます。

これにより、システムと Unity Iap の両方が同期されます。

> **Important:**
>
> バックエンドがプレイヤーのエンタイトルメントを付与している場合でも、すべての有料注文を完了済みとしてマークする必要があります。プレイヤーはどちらの場合でもエンタイトルメントを維持しますが、`paid`状態のままのオーダーでは、Unity Iapオーダーのトラッキングと分析は不完全のままになります。SDK と Webhook が混在するハイブリッド設定では、未確認応答の注文がシステム間で注文状態をドリフトさせる可能性もあります。

## Webhook イベントの検証 (JWT)##validate-webhook-events-(jwt)

検証を有効にするために、Unity Iap は JSON ウェブ Token (JWT) でウェブフックリクエストに署名します。バックエンドでこのトークンの以下の情報を確認し、イベントが本物であることを確認します。

* Authorization ヘッダー
* **SIGNATURE**秘密鍵で署名されています。検証する \*\*JWKS（公開鍵）\*\*を取得してキャッシュします： [`https://services.api.unity.com/webhooks/.well-known/jwks.json`](https://services.api.unity.com/webhooks/.well-known/jwks.json)
* **Issuer**： `https://services.api.unity.com/webhooks/`
* **オーディエンスクレーム(配列)**:`upid` (Unity プロジェクト ID)、`envId` (環境 ID)
* **トークンの有効期限**：`exp`（有効期限）が経過していないことを確認します。

JWT の妥当性検査に使用できる[ほとんどの言語](https://www.jwt.io/libraries?programming_language)のライブラリがあります。

## Webhook イベント形状##webhook-event-shape

Unity Iap は、サポートされているすべての支払いプロバイダーからのイベントをこの一貫した形式に正規化します。以下の例を参照してください。

```json
{
  "id": "018d5e5e-5e5e-7e5e-5e5e-5e5e5e5e5e5e",
  "version": "25270591"
  "eventType": "order.paid",
  "time":"2024-01-15T14:30:00Z",
  "projectId":"018d5e5e-1111-7e5e-5e5e-111111111111",
  "environmentId":"018d5e5e-2222-7e5e-5e5e-222222222222",
  "dataType": "order",
  "data": {
    "id": "018d5e5e-3333-7e5e-5e5e-333333333333",
    "playerId": "player_12345",
    "paymentProvider": "stripe",
    "paymentProviderResourceId": "cs_test_a1b2c3d4e5f6",
    "url": "https://checkout.stripe.com/pay/cs_test_a1b2c3d4e5f6",
    "lineItems": [
      {
        "sku": "com.game.coins_100",
        "productType":"Consumable",
        "price": {
          "amountMicros":"25270591"
          "currency":"USD"
        }
      }
    ],
    "total": {
      "amountMicros":"25270591"
      "currency":"USD",
      "refundedAmountMicros":0
    
    "status": "paid",
    "customReferenceId": "order_xyz_789",
    "metadata": {
      "campaign": "summer_sale",
      "platform": "iOS"
    
    "createdAt":"2024-01-15T14:25:00Z",
    "updatedAt":"2024-01-15T14:30:00Z",
    "paidAt":"2024-01-15T14:30:00Z",
    "fulfilledAt": null
  }
}
```

Webhook イベントの以下のフィールドを参照してください。

**id** (string, required): Webhook イベントの一意の識別子。**version** (string, required): Webhook イベントスキーマのバージョン。**eventType** (string, required): イベントのタイプ。可能な値については、[イベントタイプ](#event-types)を参照してください。**time** (string, required): イベントが生成された時刻 (ISO 8601 形式)。**projectId** (string, required): この順序が属する Unity プロジェクトの ID。**environmentId** (string, required): この順序が発生した Unity 環境の ID。**dataType** (string, required): イベントのデータのタイプ。**data** (object, required): 購入に関する詳細を含む注文オブジェクト**id** (string, required): オーダーの一意の識別子。**playerId** (string, required): Unity 認証プレイヤー ID。**paymentProvider** (string, required): 注文を処理した支払いプロバイダー。例えば、`stripe`や`coda`など。**paymentProviderResourceId** (string, required): 支払いプロバイダーによってリソースに割り当てられた一意の ID。**url** (string, required): 注文のチェックアウト URL。**lineItems** (object array, required): 注文で購入された製品のリスト。**sku** (string, required): 製品の一意の識別子。**productType** (string, required): 製品のタイプ。例えば、`Consumable`。**price** (object, required): プロダクトの価格。**amountMicros** (integer, required): マイクロ単位での価格（1,000,000 マイクロは $1.00 に相当）。これらの値はユーザーに表示されたものです。**currency** (string, required): 3 文字の ISO 4217 通貨コード。**total** (object, required): 合計注文金額。**amountMicros** (integer, required): マイクロ単位の合計注文金額。**currency** (string, required): 3 文字の ISO 4217 通貨コード。**refundedAmountMicros** (integer, required): 返金された合計金額（単位：マイクロ）一部返金の場合、これは`amountMicros`よりも小さい可能性があります。**status** (string, required): オーダーの状態。使用可能な値: `created`、`paid`、`failed`、`fulfilled`、`revoked`**customReferenceId** (string): 注文の任意のカスタム リファレンスID。**metadata** (object): 購入フロー中に渡されるカスタムメタデータを含むキーと値のオブジェクト。**createdAt** (string, required): 注文が作成されたときのタイムスタンプ（ISO 8601 形式）。**updatedAt** (string, required): 順序が最後に更新されたときのタイムスタンプ（ISO 8601 形式）。**paidAt** (string): 注文が支払われたタイムスタンプ（ISO 8601 形式）。**fulfilledAt** (string): 注文が約定したときのタイムスタンプ。まだ約定していない場合は`null`。**revokedAt** (string): 注文が取り消されたタイムスタンプ。取り消されない場合は`null`。

## イベントタイプ##event-types

Unity Iap は以下の Webhook イベント タイプを送信します。

### order.paid##order.paid

Unity Iap は、プレイヤーが購入を正常に完了したときに `order.paid` イベントを送信します。このイベントを受け取ったら、購入したエンタイトルメントまたは通貨をプレイヤーに付与し、[注文を履行](#mark-orders-as-fulfilled-via-api)済みとしてマークします。

### order.updated##order.updated

Unity Iap は、順序が更新されるたびに `order.updated` イベントを送信します。これには以下のような変更が含まれます。

* 約定済みとしてマークされている注文。
* 返金処理中です。
* その他の注文の変更。

`data.total` の `refundedAmountMicros` フィールドには、現在の払い戻し合計額が反映されます。

返金は開発者主導で行われます。例えばプロバイプロバイダーボダッシュボードイヤーに払い戻しを発行できます。

Unity Iap は、払い戻しが発生してもエンタイトルメントを自動的に取り消すことはありません。返金時にエンタイトルメントを取り消す場合は、このイベントをリッスンし、独自のエンタイトルメントシステムでハンドルできます。

### order.revoked##order.revoked

Unity Iap は、オーダー状態が `revoked` に変化したときに `order.revoked` イベントを送信します。このイベントを受け取ったら、元の購入で付与されたプレイヤーの権利または通貨を取り消してください。

チャージバックはプレイヤーが開始します。例えば、プレイヤーはクレジット カード会社または銀行を通じて請求に異議を唱える場合があります。

チャージバックの紛争が解決し、プレイヤーが勝訴すると、Unity Iap は自動的に注文を取り消し、このイベントを送信します。返金とは異なり、Unity Iap はチャージバック時にエンタイトルメントを取り消します。

## APIによる注文の履行のマーク##mark-orders-as-fulfilled-via-api

バックエンドでエンタイトルメントを付与したら、Orders API を使用して注文を履行済みとしてマークします。これにより、`fulfilledAt`のタイムスタンプが更新され、注文状態が正しく追跡されます。

### Authentication##authentication

これらのエンドポイントを呼び出しには、以下のいずれかの方法で認証する必要があります。

* サービスアカウントUnity Dashboard でサービスアカウントを作成し、それを使用してバックエンドサーバーを認証します。詳細については、[サービスアカウント認証](https://services.docs.unity.com/docs/service-account-auth/) を参照してください。
* **Cloud Code**プロジェクトの代わりに認証できる Cloud Code スクリプトまたはモジュールからエンドポイントを呼び出します。詳細については、「 [Use a Cloud](./cloud-code-fulfillment.md) Code module to fulfill Purchases 」を参照してください。

### 順序の取得##get-order

[`GET`エンドポイント](/oas-iap-client/1.0.0/.md#get-order-status)を呼び出して順序を取得します。

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

#### 注文状況##order-statuses

| **status**  | **説明**                                                       |
| ----------- | ------------------------------------------------------------ |
| `created`   | 注文は作成されましたが、支払いは完了していません。`paid`、`failed`、または`cancelled`に遷移可能 |
| `paid`      | 支払いが受領され、支払いプロバイダーによって確認されました。`fulfilled`または`revoked`に遷移可能   |
| `fulfilled` | 注文が満たされ、プレイヤーが報酬を受領しました。`以下に遷移可能`revoked\`                   |
| `failed`    | 注文が失敗しました。これは端末状態です。                                         |
| `revoked`   | 命令は取り消されたこれは端末状態です。                                          |
| `cancelled` | 支払い前に注文がキャンセルされました。これは端末状態です。                                |

#### 反応ボディの例##example-response-body

`GET` と `PATCH` の両方のエンドポイントが、Order オブジェクト全体を返します。

```json
{
  "id": "018d5e5e-3333-7e5e-5e5e-333333333333",
  "projectId":"018d5e5e-1111-7e5e-5e5e-111111111111",
  "environmentId":"018d5e5e-2222-7e5e-5e5e-222222222222",
  "playerId": "player_12345",
  "paymentProvider": "stripe",
  "paymentProviderResourceId": "cs_test_12345",
  "url": "https://checkout.stripe.com/pay/cs_test_12345",
  "lineItems": [
    {
      "sku": "com.game.coins_100",
      "productType":"Consumable"
    }
  ],
  "status": "paid",
  "fulfilledAt": null,
  "revokedAt": null,
  "customReferenceId": "order_xyz_789",
  "metadata": {
    "campaign": "summer_sale"
  
  "createdAt":"2024-01-15T14:25:00Z",
  "updatedAt":"2024-01-15T14:30:00Z"
}
```

### 注文の更新##update-an-order

[`PATCH`エンドポイント](/oas-iap-client/1.0.0/.md#update-an-order)を呼び出して注文状態を更新します。

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

#### リクエスト本文##request-body

```json
{
  "status": "fulfilled"
}
```

**status** (string, required): 注文に設定する新しい状態。使用可能な値: 。`fulfilled`プレイヤーにエンタイトルメントを付与した後に設定されます。`paid`オーダーでのみ設定できます。
プレイヤーのエンタイトルメントを取り消します (例えば、返金後)。`paid`または`fulfilled`オーダーでのみ設定できます。
注文をキャンセルします。`created`注文時 (支払い前) にのみ設定できます。オーダーが`revoked`または`cancelled`になると、その状態は変更できません。

[HTTP 反応状態コード](/oas-iap-client/1.0.0/.md#http-response-status-codes-for-update-an-order:)については、IAP Client API のドキュメントを参照してください。

## イベント反応を返す##return-an-event-response

イベント処理の成功または失敗を示すイベント反応を返す必要があります。

* "response": \[]イベント処理が成功したことを示します。
* **非`2xx`反応**:失敗を示します。Unity Iap はリプレイ ポリシーに従って[イベントを再生](#event-replay-behavior)します。

### イベントリプレイ動作##event-replay-behavior

Webhook エンドポイントが `2xx` 以外の反応を返すと、Unity Iap は自動的にイベントの配信を再試行します。Webhook ハンドラーが適切であることを確認し、リプレイされたイベントによってエンタイトルメントが複製されないようにします。

## 次のステップ##next-steps

このページは、IAPによるD2C(ダイレクト ツー コンシューマー)支払いプロバイダーを設定するワークフローの一部です。このワークフローを続行するには、以下のオプションのいずれかを選択します。

[Integrate D2C payment providers](./workflow.md#fulfill-purchases-through-the-backend-api): Integrate D2C payment provider with IAP ワークフロー (D2C 支払いプロバイダーと IAP ワークフローの統合) ページに戻ります。
[Test your integration](./test-integration.md): ワークフローの次のステップに進み、D2C支払いプロバイダーを設定します。
