ドキュメント

​
​

Development

User Acquisition

Monetization

産業

In-App Purchasing

IAP Client API

IAP SDK API

Webshop Admin API

Webshop Client API

In-App Purchasing

LiveOps
​
​
使用の準備
  • 概要
  • IAP の概要
  • IAP v5の新機能
  • IAP v4からv5へのアップグレード
コンシューマー (D2C)への直接支払い
  • コンシューマー (D2C)支払いプロバイダーへの直接送信
    • D2C支払いプロバイダーの統合ワークフロー
      • Create Remote Catalog
      • Initialize services to handle the start up sequence
      • Fetch Remote Catalog
      • Configure a webhook or Cloud Code module for purchase fulfillment
      • Configure your payment provider
      • Add a routing rule
      • Fulfill purchases
        • Fulfill purchases through the SDK
        • Fulfill purchases through the backend API
        • Use a Cloud Code module to fulfill purchases
      • Test your integration
    • 購入の開始
    • AppleおよびGoogleの外部購入要件への準拠
    • ディープリンク成功URL例
ウェブショップ
  • 概要
  • 使用の準備
  • ビルドと公開
  • 設定
  • ゲームに接続
  • 管理
  • Webshop のトラブルシューティング
プラットフォームネイティブ
  • プラットプラットフォームネイティブの IAP の設定
  • Codeless IAP
カタログとストア管理
  • 製品カタログの作成
  • サポートされているストア
  • リファレンス
購入 /Purchases
  • 購入管理とフルフィルメント
IAP パフォーマンスの監視
  • 概要
  • IAP収益パフォーマンス
  • D2C パフォーマンス
プライバシー
  • プライバシーと同意
  1. Unity アプリ内課金
  2. コンシューマー向け直接(D2C)支払いプロバイダー

Cloud Codeを使用して購入を完了

Cloud Codeモジュールをバックエンド ソリューションとして実装して、購入に対応します。
読み終わるまでの所要時間 12 分
最終更新 2ヶ月前

D2C支払いプロバイダーを使用する場合は、Cloud Codeモジュールを使用して購入を完了できます。
Cloud Code は Webhook 購入イベントを検証し、成功の反応を返します。以下の実装するにはモジュールを記述する必要があります。
  1. SKU(Stock Keeping単位)に基づいて、Cloud Saveなどのデータベースで製品資格を付与し、プレイヤーのインベントリを更新します。
  2. orders API を呼び出して、注文を履行済みとしてマークします。
重要
注文を約定済みとしてマークすることは必須であり、任意ではありません。プレイヤーはどちらの場合でもエンタイトルメントを維持しますが、
paid
状態のままのオーダーでは、Unity Iapオーダーのトラッキングと分析は不完全のままになります。
orders APIの詳細については、イベント タイプのドキュメントを参照してください。

モジュールのデプロイ

モジュールのデプロイ方法については、『Cloud Code Get started guide』を参照してください。

IAP ダッシュボードで Cloud Code モジュールを設定する

Unity DashboardでCloud Codeモジュールから購入情報を受け取るようにプロジェクトを設定します。
  1. Unity Dashboard で、IAP> Payment Providers を選択します。
  2. Entitlement 配信メソッドで、編集 を選択します。
  3. Cloud Code モジュールオプションを選択し、ドロップダウンからモジュールとエンドポイントを選択します。
  4. Save configuration (設定の保存) を選択します。

ログの使用

Cloud Code モジュールがトランザクションを処理する場合、Cloud Code ダッシュボードでログをビューできます。詳細については、Cloud Code Logging のドキュメントを参照してください。

Cloud Code サンプルモジュール

購入イベントの Webhook を処理する以下のモジュール例を参照してください。
using System;using System.Collections.Generic;using System.Net.Http;using System.Net.Http.Headers;using System.Text;using System.Text.Json;using System.Text.Json.Serialization;using System.Threading;using System.Threading.Tasks;using Microsoft.Extensions.DependencyInjection;using Microsoft.Extensions.Logging;using Unity.Services.CloudCode.Core;using Unity.Services.CloudCode.Apis;using Unity.Services.CloudSave.Model;名前空間の履行;// Handles IAP purchase webhooks: grants product entitlements via Cloud Save and marks orders as fulfilled.public class IapFulfillmentHandler{ private readonly ILogger<IapFulfillmentHandler> m_Logger; . private static readonly HttpClient s_HttpClient = new() { Timeout = TimeSpan.FromSeconds(10) }; private const string k_IapTransactionVerifierBaseUrl = "https://iap.services.api.unity.com/v1"; // Accepts and stores the logger used for diagnostics. public IapFulfillmentHandler(ILogger<IapFulfillmentHandler> logger) { m_Logger = logger; } // Webhook entry point: grants entitlements from line items via Cloud Save, then marks the order as fulfilled and returns a response. [CloudCodeFunction("ProcessPurchaseFulfillment")] public async Task<WebhookResponse> ProcessPurchaseFulfillment( IExecutionContext コンテキスト IGameApiClient gameApiClient, GUID ID 文字列バージョン、 文字列 eventType、 DateTime time、 string projectId, string environmentId, 文字列データ型、 WebhookOrderData data) { try { m_Logger.LogInformation( "購入処理 - EventID: {EventID}, EventType: {EventType}, Time: {Time}, " + "ProjectID: {ProjectID}, EnvironmentId: {EnvironmentId}, PlayerId: {PlayerId}, " + " "PaymentProvider: {PaymentProvider}, PaymentProviderResourceId: {PaymentProviderResourceId}, " + "OrderId: {OrderId}, LineItems: {LineItems}, CustomReferenceId: {CustomReferenceId}", id.ToString(), eventType time.ToString(), projectId environmentId, data.PlayerId, data.PaymentProvider, data.PaymentProviderResourceId, データ.Id、 JsonSerializer.Serialize(data.LineItems), data.CustomReferenceId); List<string> entitlements = await GrantProductEntitlements( context, gameApiClient, data.PlayerId, data.LineItems); m_Logger.LogInformation( " " +" の付いたオーダー {OrderId} の購入処理が正常に終了しました" "{Entitlements}", データ.Id、エンタイトルメント); try { m_Logger.LogInformation("Calling fulfillment API for order {OrderId}", data.Id); bool fulfillmentSuccess = await FulfillOrderAsync( データ.Id、 projectId environmentId, context.ServiceToken ?? string.Empty); if (fulfillmentSuccess) { m_Logger.LogInformation("Successfully marked order {OrderId} as fulfilled", data.Id); } else { m_Logger.LogWarning( "Failed to mark order {OrderId} as fulfilled, but entitlements was " (注文 {OrderId} を完了済みとしてマークできませんでしたが、エンタイトルメントは " + "granted", データ.Id); } } catch (例外) { m_Logger.LogError(ex, "Error calling fulfillment API for order {OrderId}: {Error}", データ.Id, ex.Message); } return new WebhookResponse { Status = WebhookResponseCodes.WebhookStatusOK }; } catch (例外) { m_Logger.LogError( "注文{OrderId}の履行中に予期しないエラーが発生しました: {Error}", データ.Id, ex.Message); return new WebhookResponse { Status = WebhookResponseCodes.WebhookStatusError, Code = WebhookResponseCodes.WebhookErrorDeclined, Description = ex.Message }; } } // Loads the player inventory, applies each line item by SKU (gems, pass, storage), saves inventory, and returns the list of granted entitlement descriptions. private async Task<List<string>> GrantProductEntitlements( IExecutionContext コンテキスト IGameApiClient gameApiClient, 文字列 playerId、 リスト<WebhookLineItem>ラインアイテム) { if (lineItems == null || lineItems.Count == 0) { m_Logger.LogWarning("GrantProductEntitlements called with null or empty line items list"); return new List<string>(); } リスト<string>エンタイトルメント = new リスト<string>(); try { m_Logger.LogInformation("Getting player inventory for player {PlayerId}", playerId); PlayerInventory currentInventory = await GetPlayerInventory( context, gameApiClient, playerId); m_Logger.LogInformation("Retrieved inventory: {Inventory}", JsonSerializer.Serialize(currentInventory)); Foreach (lineItems の WebhookLineItem 項目) { string sku = item.Sku; m_Logger.LogInformation("Processing line item:SKU={SKU}, ProductType={ProductType}", sku, 項目.ProductType); switch (sku) { case "com.unity.iap.test.adventure.pass.not": int days = 30; currentInventory.AdventurePass = DateTime.UtcNow.AddDays(days); entitlements.Add($"{days} Days Adventure Pass"); break; case "com.unity.iap.test.30.gems": int gems30 = 30; currentInventory.Gems += gems30; entitlements.Add($"{gems30} Gems"); break; case "com.unity.iap.test.premium.storage": currentInventory.PremiumStorage = true; entitlements.Add($"PremiumStorage = true"); break; デフォルト m_Logger.LogWarning("Unknown SKU {SKU} in fulfillment request", sku); break; } } m_Logger.LogInformation("Updated inventory: inventory: {Inventory}", JsonSerializer.Serialize(currentInventory)); m_Logger.LogInformation("Saving player inventory"); await SavePlayerInventory(context, gameApiClient, playerId, currentInventory); m_Logger.LogInformation("Successfully saved player inventory"); } catch (例外) { m_Logger.LogError("Error in GrantProductEntitlements: {Error}", ex.Message); throw; } return entitlements; } // Fetches the "player_inventory" key from Cloud Save for the player and deserializes it; returns a new empty inventory if missing or on error. private async Task<PlayerInventory> GetPlayerInventory( ) IExecutionContext コンテキスト IGameApiClient gameApiClient, string playerId) { try { var result = await gameApiClient.CloudSaveData.GetItemsAsync( context context.ServiceToken!, context.ProjectId!, PlayerId new List<string> { "player_inventory" }); if (result.Data.Results.Count <= 0) { m_Logger.LogInformation("No existing inventory found, creating new inventory"); return new PlayerInventory(); } var resultItem = result.Data.Results[0]; if (resultItem.Value == null) { m_Logger.LogWarning("Inventory value is null, creating new inventory"); return new PlayerInventory(); } string inventoryJson; try { inventoryJson = resultItem.Value.ToString(); if (string.IsNullOrWhiteSpace(inventoryJson)) { m_Logger.LogWarning("Inventory JSON is empty, creating new inventory"); return new PlayerInventory(); } } catch (例外) { m_Logger.LogError("Failed to convert inventory value to string: {Error}", ex.Message); return new PlayerInventory(); } try { PlayerInventory inventory = JsonSerializer.Deserialize<PlayerInventory>( inventoryJson); return inventory ?? new PlayerInventory(); } catch (例外) { m_Logger.LogError( インベントリのデシリアライズライズに失敗しました JSON: {Error}。JSON: {Json}", ex.Message, inventoryJson); return new PlayerInventory(); } } catch (例外) { m_Logger.LogError("Unexpected error while getting inventory: {Error}", ex.Message); return new PlayerInventory(); } } // Serializes the inventory to JSON and writes it to Cloud Save under the "player_inventory" key for the player. private async Task SavePlayerInventory( IExecutionContext コンテキスト IGameApiClient gameApiClient, 文字列 playerId、 PlayerInventory インベントリ) { try { if (inventory == null) { m_Logger.LogWarning("Attempted to save null inventory, skipping"); return; } string inventoryJson; try { inventoryJson = JsonSerializer.Serialize(inventory); } catch (例外) { m_Logger.LogError("Failed to serialize inventory: {Error}", ex.Message); throw; } await gameApiClient.CloudSaveData.SetItemAsync( context context.ServiceToken!, context.ProjectId!, PlayerId new SetItemBody("player_inventory", inventoryJson)); m_Logger.LogInformation("Successfully saved inventory for player {PlayerId}", playerId); } catch (例外) { m_Logger.LogError("Failed to save inventory: {Error}", ex.Message); throw; } } // Sends a PATCH request to the IAP orders API to set the order status to fulfilled; returns true on success. private async Task<bool> FulfillOrderAsync() 文字列 orderId、 string projectId, string environmentId, 文字列 serviceToken) { if (string.IsNullOrEmpty(orderId)) { m_Logger.LogWarning("Cannot fulfill order - order ID is null or empty"); return false; } if (string.IsNullOrEmpty(projectId)) { m_Logger.LogWarning("Cannot fulfill order - project ID is null or empty"); return false; } if (string.IsNullOrEmpty(environmentId)) { m_Logger.LogWarning("Cannot fulfill order - environment ID is null or empty"); return false; } if (string.IsNullOrEmpty(serviceToken)) { m_Logger.LogWarning("Cannot fulfill order - service token is null or empty"); return false; } try { string url = $"{k_IapTransactionVerifierBaseUrl}/projects/{projectId}/environments/" + $"{environmentId}/orders/{orderId}"; var requestBody = new { status = "fulfilled" }; string jsonContent = JsonSerializer.Serialize(requestBody); StringContent content = new StringContent( jsonContent, Encoding.UTF8, "application/json"); HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Patch, url) { Content = content }; request.Headers.Authorization = new AuthenticationHeaderValue( "Bearer", serviceToken); m_Logger.LogInformation("Sending fulfillment request to {Url} for order {OrderId}", URL, orderId); HttpResponseMessage response = await s_HttpClient.SendAsync(request); if (response.IsSuccessStatusCode) { m_Logger.LogInformation("注文 {OrderId} の完了 API 呼び出しが成功しました。状態: {StatusCode}", orderId, response.StatusCode); return true; } string responseBody = await response.Content.ReadAsStringAsync(); m_Logger.LogWarning( "オーダー {OrderId} の完了 API 呼び出しが失敗しました。"状態: {StatusCode}, " + "反応: {ResponseBody}", orderId, response.StatusCode, responseBody); return false; } catch (例外) { m_Logger.LogError(ex, "Unexpected error while fulfilling order {OrderId}: {Error}", orderId, ex.Message); return false; } }}// Player inventory stored in Cloud Save (gems, adventure pass, premium storage, and similar).public class PlayerInventory{ public int Gems { get; set; } public int XpBooster { get; set; } public DateTime?AdventurePass { get; set; } public bool PremiumStorage { get; set; }}// Order payload sent by the IAP webhook for a purchase event.public class WebhookOrderData{ [JsonPropertyName("id")] public string Id { get; set; } = string.Empty; [JsonPropertyName("playerId")] public string PlayerId { get; set; } = string.Empty; [JsonPropertyName("paymentProvider")] public string PaymentProvider { get; set; } = string.Empty; [JsonPropertyName("paymentProviderResourceId")] public string?PaymentProviderResourceId { get; set; } [JsonPropertyName("url")] public string Url { get; set; } = string.Empty; [JsonPropertyName("lineItems")] public List<WebhookLineItem> LineItems { get; set; } = new(); [JsonPropertyName("currency")] public string Currency { get; set; } = string.Empty; [JsonPropertyName("amounts")] public WebhookAmounts?Amounts { get; set; } [JsonPropertyName("status")] public string Status { get; set; } = string.Empty; [JsonPropertyName("customReferenceId")] public string?CustomReferenceId { get; set; } [JsonPropertyName("metadata")] public Dictionary<string, string>?Metadata { get; set; } [JsonPropertyName("createdAt")] public DateTime CreatedAt { get; set; } [JsonPropertyName("updatedAt")] public DateTime UpdatedAt { get; set; } [JsonPropertyName("paidAt")] public DateTime?PaidAt { get; set; } [JsonPropertyName("fulfilledAt")] public DateTime?FulfilledAt { get; set; }}// A single purchasable item in an order (SKU, product type, and price).public class WebhookLineItem{ [JsonPropertyName("sku")] public string Sku { get; set; } = string.Empty; [JsonPropertyName("productType")] public string ProductType { get; set; } = string.Empty; [JsonPropertyName("price")] public WebhookMoney Price { get; set; } = null!;}// Total and refunded amounts for an order, in micros.public class WebhookAmounts{ [JsonPropertyName("totalMicros")] public long TotalMicros { get; set; } [JsonPropertyName("refundedMicros")] public long RefundedMicros { get; set; }}// A monetary amount and currency (amount in micros).public class WebhookMoney{ [JsonPropertyName("amountMicros")] public long AmountMicros { get; set; } [JsonPropertyName("currency")] public string Currency { get; set; } = string.Empty;}// Status and error code constants for IAP webhook responses.public static class WebhookResponseCodes{ public const string WebhookStatusOK = "ok"; public const string WebhookStatusError = "error"; public const string WebhookErrorDeclined = "declined";}// Response returned by the IAP fulfillment webhook to the IAP service.public class WebhookResponse{ public string Status { get; set; } = string.Empty; public string?コード { get; set; } public string?Description { get; set; }}// Registers Cloud Code module dependencies (for example, the game API client).public class ModuleConfig :ICloudCodeSetup{ // Registers the game API client as a singleton for re-use by Cloud Code functions. public void Setup(ICloudCodeConfig config) { config.Dependencies.AddSingleton(GameApiClient.Create()); }}
Integrate D2C payment providers
Integrate D2C payment provider with IAP ワークフロー (D2C 支払いプロバイダーと IAP ワークフローの統合) ページに戻ります。
Test your integration
ワークフローの次のステップに進み、D2C支払いプロバイダーを設定します。

Copyright © 2026 Unity Technologies
法規事項プライバシーポリシークッキーDocumentation Terms of Use私の個人情報を販売または共有しないプライバシーに関する選択 (クッキー設定)

"Unity" の名称、Unity のロゴ、およびその他の Unity の商標は、米国およびその他の国における Unity Technologies またはその関係会社の商標または登録商標です (詳しくはこちら)。その他の名称またはブランドは該当する所有者の商標です。

一部のページは利便性向上のため機械翻訳を使用しており、内容に不正確な表現が含まれる場合があります。内容に齟齬または不一致が生じた場合は、英語版を正本とします。

  • このページ
    • モジュールのデプロイ

    • IAP ダッシュボードで Cloud Code モジュールを設定する

    • ログの使用

    • Cloud Code サンプルモジュール

  • 追加リソース
  • Direct to コンシューマー (D2C)支払いプロバイダー ワークフローの統合
  • Cloud Code ドキュメント

このページの問題を報告する