アプリ内購入の統合(Unityプラグイン)
iOS Billing SDKは、Aptoideの課金を実装するためのシンプルなソリューションです。そのUnityプラグインは、UnityゲームがSDKと通信するためのシンプルなインターフェースを提供します。これは、Aptoide Connectから製品を取得し、それらのアイテムの購入を処理できるBillingクライアントで構成されています。
SDKは、Core Technology Commission(CTC)の計算のためのAppleへのトランザクション報告を自動的に処理し、この負担を開発者から取り除きます。購入、返金、その他のトランザクションイベントを報告するためのインテリジェントなロジックが含まれており、どの地域でCTC報告が必要でどの地域で不要かを区別する地域対応の処理を備えています。
概要
プラグインを使用したアプリケーションでの課金フローは以下のとおりです。
- AppCoins Unityプラグインを追加する。
- アプリ内製品を照会する。
- ユーザーが製品を購入しようとする。
- アプリケーションが購入を開始し、プラグインがそれを処理し、完了時に購入ステータスと検証データを返す。
- アプリケーションがユーザーに製品を提供する。
要件
- Unity 2019.4以降。
- iOS 17.4以降。
ステップバイステップガイド
セットアップ
- AppCoins Unityプラグインを追加する
Unityで、リポジトリ https://github.com/Catappult/appcoins-sdk-ios-unity-plugin で入手可能な最新リリースのプラグインを、Assetsフォルダーに追加します。
名前空間
プラグイン内のすべてのパブリッククラスは AppCoins 名前空間の下で定義されています。プラグインの型を参照するC#ファイルの先頭に、次の using ディレクティブを追加してください。
using AppCoins;
あるいは、完全修飾名(例:AppCoins.Product、AppCoins.Purchase)を使用することもできます。
実装
プラグインのセットアップが完了したので、その機能を使い始めることができます。
-
AppCoins Billingの利用可否を確認する
AppCoins Billingは、iOSバージョンが17.4以上のデバイスでのみ利用可能であり、かつアプリケーションがApple App Store経由でインストールされていない場合に限り利用可能です。したがって、購入を試みる前に、
AppCoinsSDK.Instance.IsAvailable()を呼び出してSDKが利用可能かどうかを確認する必要があります。using AppCoins;
var isAvailable = await AppCoinsSDK.Instance.IsAvailable();
if (isAvailable)
{
// make purchase
} -
アプリ内製品を照会する
まず、ユーザーに提供したいアプリ内製品を取得することから始めます。このメソッドは、Catappultのすべてのアプリ内製品、または特定のリストのいずれかを返すことができます。
-
AppCoinsSDK.Instance.GetProducts()アプリケーションのすべてのCatappultアプリ内製品を返します。
using AppCoins;
var productsResult = await AppCoinsSDK.Instance.GetProducts();
if (productsResult.IsSuccess)
{
var products = productsResult.Value;
// Process products
}
else
{
Debug.Log("Error: " + productsResult.Error);
} -
AppCoinsSDK.Instance.GetProducts(skus)Catappultアプリ内製品の特定のリストを返します。
using AppCoins;
var productsResult = await AppCoinsSDK.Instance.GetProducts(new string[] { "coins_100", "gas" });
if (productsResult.IsSuccess)
{
var products = productsResult.Value;
// Process products
}
else
{
Debug.Log("Error: " + productsResult.Error);
}⚠️警告: アプリ内製品を照会できるのは、アプリケーションがAptoide Connectでレビューされ承認された後に限られます。
-
-
アプリ内製品を購入する
アプリ内製品を購入するには、関数
AppCoinsSDK.Instance.Purchase(sku, payload)を呼び出す必要があります。プラグインがすべての購入ロジックを処理し、完了時に購入の結果を返します。この結果は、次のプロパティを持つAppCoinsSDKPurchaseResultオブジェクトです。State: String - 購入の状態(AppCoinsSDK.PURCHASE_STATE_SUCCESS、AppCoinsSDK.PURCHASE_STATE_PENDING、AppCoinsSDK.PURCHASE_STATE_USER_CANCELLED、AppCoinsSDK.PURCHASE_STATE_FAILED)Value: 以下を含むオブジェクト:VerificationResult: String - 検証結果(AppCoinsSDK.PURCHASE_VERIFICATION_STATE_VERIFIED、AppCoinsSDK.PURCHASE_VERIFICATION_STATE_UNVERIFIED)Purchase: PurchaseオブジェクトVerificationError: AppCoinsSDKError(検証が失敗した場合にのみ存在)
Error: AppCoinsSDKError - エラーの詳細(状態がFAILEDの場合にのみ存在)
成功した場合、アプリケーションはトランザクションの署名をローカルで検証します。この検証の後、その結果を処理する必要があります。
- 購入が検証された場合は、アイテムを消費してユーザーに提供する必要があります。
- 検証されない場合は、ビジネスロジックに基づいて判断する必要があります。アイテムを消費してユーザーに提供するか、あるいは購入を承認せず、24時間以内にユーザーへ返金することになります。
失敗した場合は、さまざまな種類のエラーに対処できます。
また、特定の購入に何らかの情報を関連付けるために、購入メソッドにPayloadを渡すこともできます。たとえば、特定のユーザーを購入に関連付けるために使用できます:
AppCoinsSDK.Instance.Purchase("gas", "User123")。
using AppCoins;
var purchaseResult = await AppCoinsSDK.Instance.Purchase("gas", "User123");
switch (purchaseResult.State)
{
case AppCoinsSDK.PURCHASE_STATE_SUCCESS:
switch (purchaseResult.Value.VerificationResult)
{
case AppCoinsSDK.PURCHASE_VERIFICATION_STATE_VERIFIED:
// Consume the item and give it to the user
var consumeResult = await AppCoinsSDK.Instance.ConsumePurchase(purchaseResult.Value.Purchase.Sku);
if (consumeResult.IsSuccess)
{
Debug.Log("Purchase consumed successfully");
}
else
{
Debug.Log("Error consuming purchase: " + consumeResult.Error);
}
break;
case AppCoinsSDK.PURCHASE_VERIFICATION_STATE_UNVERIFIED:
// Handle unverified purchase according to your game logic
break;
}
break;
case AppCoinsSDK.PURCHASE_STATE_PENDING:
// Handle pending purchase according to your game logic
Debug.Log("Purchase is pending.");
break;
case AppCoinsSDK.PURCHASE_STATE_USER_CANCELLED:
// Handle cancelled purchase according to your game logic
Debug.Log("Purchase was cancelled.");
break;
case AppCoinsSDK.PURCHASE_STATE_FAILED:
// Handle failed purchase according to your game logic
Debug.Log("Purchase failed with error: " + purchaseResult.Error);
break;
} -
アプリ起動時に未完了の購入を処理する(重要)
⚠️重要: アプリケーションが起動するたびに、未完了の購入を照会して消費する必要があります。これを行わないと、ユーザーが既に支払ったアイテムを受け取れなくなり、消費されなかった購入は24時間後に自動的に返金されます。
未完了の購入とは何か?
未完了の購入とは、支払いは済んでいるものの、アプリケーションによってまだ消費されていないトランザクションです。これは次のような場合に発生する可能性があります。
- 購入中にアプリが閉じられたか、クラッシュした
- 購入が処理される前にユーザーがアプリを強制終了した
- 購入完了中にネットワークエラーが発生した
これが重要である理由:
- ユーザーは既にこれらのアイテムの支払いを済ませている
- 24時間以内に消費されない場合、購入は自動的に返金される
- ユーザーは、アプリを再度開いた際に、購入したアイテムを即座に受け取ることを期待している
実装:
このコードをアプリケーションの起動ロジックに追加します(例:メインシーンの
Start()またはAwake()メソッド内)。using AppCoins;
private async void Start()
{
// Check if AppCoins Billing is available
var isAvailable = await AppCoinsSDK.Instance.IsAvailable();
if (!isAvailable)
{
return;
}
// Query and consume unfinished purchases
var unfinishedPurchasesResult = await AppCoinsSDK.Instance.GetUnfinishedPurchases();
if (unfinishedPurchasesResult.IsSuccess)
{
var purchases = unfinishedPurchasesResult.Value;
foreach (var purchase in purchases)
{
// Give the item to the user
GiveItemToUser(purchase.Sku);
// Consume the purchase
var consumeResult = await AppCoinsSDK.Instance.ConsumePurchase(purchase.Sku);
if (consumeResult.IsSuccess)
{
Debug.Log($"Unfinished purchase consumed successfully: {purchase.Sku}");
}
else
{
Debug.Log($"Error consuming purchase: {consumeResult.Error}");
}
}
}
else
{
Debug.Log("Error querying unfinished purchases: " + unfinishedPurchasesResult.Error);
}
}
private void GiveItemToUser(string sku)
{
// Your logic to grant the purchased item to the user
Debug.Log($"Giving item to user: {sku}");
} -
間接購入を処理する
標準のアプリ内購入に加えて、AppCoins SDKはアプリ内購入インテント、つまりユーザーのアクション(アプリ内の「購入」ボタンのタップなど)によって直接トリガーされない購入をサポートしています。一般的なユースケースには以下が含まれます。
- Aptoideストアのアプリ内製品カタログからアイテムを直接購入する。
- ウェブリンクを通じてアイテムを購入する。
購入インテントは、次のURL形式で開始できます。
AppCoinsPurchaseManager.OnPurchaseUpdatedUnity Actionを使用すると、開発者はこれらの購入インテントを管理できます。このイベントは購入インテントの更新を継続的にストリーミングし、リアルタイムのトランザクション同期を保証します。このイベントは、以下を含む
PurchaseIntentオブジェクトを返します。ID: String - インテントの一意の識別子Product: Product - ユーザーが購入しようとしている製品Timestamp: String - インテントが作成された日時
PurchaseIntentを受け取った際は、購入を完了するためにAppCoinsSDK.Instance.ConfirmPurchaseIntent(payload)を使用して確認するか、キャンセルするためにAppCoinsSDK.Instance.RejectPurchaseIntent()を使用して拒否するか、いずれかを行う必要があります。インテントを確認するとAppCoinsSDKPurchaseResultが返され、これは標準の購入と同じ方法で処理する必要があります。購入インテントを適切に処理するには、シングルトンクラス内でイベントをサブスクライブし、アプリケーションのライフサイクル全体を通じてアクティブな状態を維持するようにしてください。
注:
AppCoinsSDK.Instance.GetPurchaseIntent()を使用して、保留中の購入インテントを手動で確認することもできます。これは、ユーザーがサインインしたときやアプリがアクティブになったときに、保留中のインテントを見逃さないようにするのに役立ちます。
using AppCoins;
private void Awake()
{
// Singleton enforcement
if (Instance != null && Instance != this)
{
Destroy(gameObject); // Destroy duplicate instances
return;
}
Instance = this;
DontDestroyOnLoad(gameObject); // Persist across scenes
// Subscribe to purchase intent updates
AppCoinsPurchaseManager.OnPurchaseUpdated += HandlePurchaseIntent;
}
private async void HandlePurchaseIntent(PurchaseIntent purchaseIntent)
{
Debug.Log($"Received purchase intent for: {purchaseIntent.Product.Title}");
// Confirm the purchase intent to complete the transaction
var purchaseResult = await AppCoinsSDK.Instance.ConfirmPurchaseIntent("User123");
// Handle the purchase result the same way as a standard purchase
switch (purchaseResult.State)
{
case AppCoinsSDK.PURCHASE_STATE_SUCCESS:
switch (purchaseResult.Value.VerificationResult)
{
case AppCoinsSDK.PURCHASE_VERIFICATION_STATE_VERIFIED:
var consumeResult = await AppCoinsSDK.Instance.ConsumePurchase(purchaseResult.Value.Purchase.Sku);
if (consumeResult.IsSuccess)
{
Debug.Log("Purchase consumed successfully");
}
else
{
Debug.Log("Error consuming purchase: " + consumeResult.Error);
}
break;
case AppCoinsSDK.PURCHASE_VERIFICATION_STATE_UNVERIFIED:
// Handle unverified purchase according to your game logic
break;
}
break;
case AppCoinsSDK.PURCHASE_STATE_USER_CANCELLED:
Debug.Log("Purchase was cancelled.");
break;
case AppCoinsSDK.PURCHASE_STATE_FAILED:
Debug.Log("Purchase failed with error: " + purchaseResult.Error);
break;
}
// Alternatively, reject the purchase intent to cancel:
// AppCoinsSDK.Instance.RejectPurchaseIntent();
} -
購入を照会する
次のいずれかのメソッドを使用して、ユーザーの購入を照会できます。
-
AppCoinsSDK.Instance.GetAllPurchases()このメソッドは、ユーザーがアプリケーション内で行ったすべての購入を返します。
using AppCoins;
var purchasesResult = await AppCoinsSDK.Instance.GetAllPurchases();
if (purchasesResult.IsSuccess)
{
var purchases = purchasesResult.Value;
// Process purchases
}
else
{
Debug.Log("Error: " + purchasesResult.Error);
} -
AppCoinsSDK.Instance.GetLatestPurchase(string sku)このメソッドは、特定のアプリ内製品に対するユーザーの最新の購入を返します。購入が見つからない場合は
nullを返します。using AppCoins;
var latestPurchaseResult = await AppCoinsSDK.Instance.GetLatestPurchase("gas");
if (latestPurchaseResult.IsSuccess)
{
if (latestPurchaseResult.Value != null)
{
var purchase = latestPurchaseResult.Value;
// Process purchase
}
else
{
Debug.Log("No latest purchase found for this SKU");
}
}
else
{
Debug.Log("Error: " + latestPurchaseResult.Error);
} -
AppCoinsSDK.Instance.GetUnfinishedPurchases()このメソッドは、アプリケーション内のユーザーのすべての未完了の購入を返します。未完了の購入とは、(SDKによって)承認されておらず、かつ消費もされていない購入です。このメソッドを使用して、未完了の購入を消費できます。
using AppCoins;
var unfinishedPurchasesResult = await AppCoinsSDK.Instance.GetUnfinishedPurchases();
if (unfinishedPurchasesResult.IsSuccess)
{
var purchases = unfinishedPurchasesResult.Value;
foreach (var purchase in purchases)
{
var consumeResult = await AppCoinsSDK.Instance.ConsumePurchase(purchase.Sku);
if (consumeResult.IsSuccess)
{
Debug.Log("Unfinished purchase consumed successfully");
}
else
{
Debug.Log("Error consuming purchase: " + consumeResult.Error);
}
}
}
else
{
Debug.Log("Error: " + unfinishedPurchasesResult.Error);
}
-
テスト
以下のXcode設定は、開発中にインストールソースをシミュレートするためだけに使用されます。本番環境では、SDKがAppleのAPIを通じて実際のインストールソースを自動的に検出するため、これらのテスト設定は無視されます。これらは本番ビルドには影響しません。
開発中にSDKの統合をテストするには、開発ビルドのインストールソースを設定して、アプリがAptoideを通じて配布されているかのようにシミュレートする必要があります。この操作により、SDKの isAvailable メソッドが有効になります。
Xcodeで次の手順に従ってください。
-
ターゲットのビルド設定で「Marketplaces」を検索します。
-
「Deployment」の下で、キー「Marketplaces」または「Alternative Distribution - Marketplaces」を「com.aptoide.ios.store」に設定します。

-
スキームで「Run」タブに移動し、次に「Options」タブに移動します。「Distribution」ドロップダウンで「com.aptoide.ios.store」を選択します。

詳細については、Appleの公式ドキュメントを参照してください:https://developer.apple.com/documentation/appdistribution/distributing-your-app-on-an-alternative-marketplace#Test-your-app-during-development
1つのビルドで両方の課金システムをテストする
アプリケーションの別バージョンを生成することなく、単一のビルド内でApple課金とAptoide課金の両方をテストしやすくするために、AppCoins SDKには、SDKの isAvailable メソッドを true と false の間で切り替えるディープリンクメカニズムが含まれています。これにより、AppCoins SDK(利用可能な場合)とApple課金(利用不可の場合)のテストをシームレスに切り替えることができます。
AppCoins SDKを有効または無効にするには、デバイスのブラウザを開いて次のURLを入力します。
{domain}.iap://wallet.appcoins.io/default?value={value}
ここで:
domain– アプリケーションのBundle ID。valuetrue→ テストのためにAppCoins SDKを有効にします。false→ AppCoins SDKを無効にし、代わりにApple課金をテストできるようにします。
サンドボックス
課金統合のセットアップが正常に完了したことを確認するために、購入をシミュレートして、クライアントが製品をスムーズに購入できることを確認できるサンドボックス環境を提供しています。この環境の使用方法に関するドキュメントは、こちらで確認できます:サンドボックス
クラスの定義とプロパティ
Unityプラグインの統合は、そのロジックを処理するいくつかの主要なオブジェクトクラスに基づいています。
Product
Product はアプリ内製品を表します。
プロパティ:
Sku: String - 一意の製品識別子。例:gasTitle: String - 製品の表示タイトル。例:Best GasDescription: String - 製品の説明。例:Buy gas to fill the tank.PriceCurrency: String - ユーザーの地理的位置に基づく通貨。例:EURPriceValue: String - 指定された通貨での製品の価値。例:0.93PriceLabel: String - ユーザーに表示される価格のラベル。例:€0.93PriceSymbol: String - 地理的位置に基づく通貨の記号。例:€
Purchase
Purchase はアプリ内購入を表します。
プロパティ:
UID: String - 一意の購入識別子。例:catappult.inapp.purchase.ABCDEFGHIJ1234Sku: String - 購入された製品の一意の識別子。例:gasState: String - 購入の状態は、PENDING、ACKNOWLEDGED、CONSUMEDの3つのいずれかになります。Pendingの購入は、SDKによって検証されておらず、アプリケーションによっても消費されていない購入です。Acknowledgedの購入は、SDKによって検証されているが、まだ消費されていない購入です。例:CONSUMEDOrderUID: String - 購入に関連付けられたorderUid。例:ZWYXGYZCPWHZDZUK4HPayload: String - 開発者のPayload。例:707048467.998992Created: String - 購入の作成日。例:2023-01-01T10:21:29.014456ZVerification: PurchaseVerification - 購入に関連付けられた検証データ。
PurchaseVerification
PurchaseVerification はアプリ内購入の検証データを表します。
プロパティ:
Type: String - 行われた検証の種類。例:GOOGLESignature: String - 購入の署名。例:C4x6cr0HJk0KkRqJXUrRAhdANespHEsyx6ajRjbG5G/v3uBzlthkUe8BO7NXH/1Yi/UhS5sk7huA+hB8EbaQK9bwaiV/Z3dISl5jgYqzSEz1c/PFPwVEHZTMrdU07i/q4FD33x0LZIxrv2XYbAcyNVRY3GLJpgzAB8NvKtumbWrbV6XG4gBmYl9w4oUgJLnedii02beKlvmR7suQcqIqlSKA9WEH2s7sCxB5+kYwjQ5oHttmOQENnJXlFRBQrhW89bl18rccF05ur71wNOU6KgMcwppUccvIfXUpDFKhXQs4Ut6c492/GX1+KzbhotDmxSLQb6aw6/l/kzaSxNyjHg==Data: PurchaseVerificationData - 購入の検証に関連付けられたデータ。
PurchaseVerificationData
PurchaseVerificationData はアプリ内購入の検証データの本体を表します。
プロパティ:
OrderId: String - 購入に関連付けられたorderUid。例:372EXWQFTVMKS6HIPackageName: String - 製品のアプリケーションのBundle ID。例:com.appcoins.trivialdrivesampleProductId: String - 購入された製品の一意の識別子。例:gasPurchaseTime: Integer - 製品が購入された時刻。例:1583058465823PurchaseToken: String - 製品が購入された際にユーザーのデバイスに提供されたトークン。例:catappult.inapp.purchase.SZYJ5ZRWUATW5YU2PurchaseState: Integer - 注文の購入状態。可能な値は次のとおりです:0(購入済み)および1(キャンセル済み)DeveloperPayload: String - 注文に関する補足情報を含む、開発者が指定した文字列。例:myOrderId:12345678
PurchaseIntent
PurchaseIntent は間接的なアプリ内購入インテントを表します。
プロパティ:
ID: String - インテントの一意の識別子。例:550e8400-e29b-41d4-a716-446655440000Product: Product - ユーザーが購入しようとしている製品Timestamp: String - インテントが作成された日時。例:2025-01-15T10:21:29.014456Z
AppCoinsSDKPurchaseResult
AppCoinsSDKPurchaseResult は購入操作の結果を表します。
プロパティ:
State: String - 購入の状態。次のいずれかになります。AppCoinsSDK.PURCHASE_STATE_SUCCESS- 購入が正常に完了したAppCoinsSDK.PURCHASE_STATE_PENDING- 購入が保留中であるAppCoinsSDK.PURCHASE_STATE_USER_CANCELLED- ユーザーが購入をキャンセルしたAppCoinsSDK.PURCHASE_STATE_FAILED- 購入が失敗した
Value: Object(Stateが SUCCESS の場合にのみ存在)以下を含む:VerificationResult: String -AppCoinsSDK.PURCHASE_VERIFICATION_STATE_VERIFIEDまたはAppCoinsSDK.PURCHASE_VERIFICATION_STATE_UNVERIFIEDのいずれかPurchase: Purchase - 購入オブジェクトVerificationError: AppCoinsSDKError(オプション) - 検証が失敗した場合のエラーの詳細
Error: AppCoinsSDKError(Stateが FAILED の場合にのみ存在) - エラーの詳細
AppCoinsSDKResult<T>
AppCoinsSDKResult<T> はデータを返すSDK操作の結果を表します。
プロパティ:
IsSuccess: Boolean - 操作が成功したかどうかValue: T - 結果の値(IsSuccessがtrueの場合にのみ存在)Error: AppCoinsSDKError(IsSuccessがfalseの場合にのみ存在) - エラーの詳細
使用元:
GetProducts()-AppCoinsSDKResult<Product[]>を返すGetAllPurchases()-AppCoinsSDKResult<Purchase[]>を返すGetLatestPurchase(sku)-AppCoinsSDKResult<Purchase>を返すGetUnfinishedPurchases()-AppCoinsSDKResult<Purchase[]>を返すConsumePurchase(sku)-AppCoinsSDKResult<bool>を返すGetTestingWalletAddress()-AppCoinsSDKResult<string>を返すGetPurchaseIntent()-AppCoinsSDKResult<PurchaseIntent>を返す
AppCoinsSDKError
AppCoinsSDKError はSDK操作が失敗した際のエラー情報を表します。
プロパティ:
Type: String - エラーの種類。次のいずれかになります。networkError- ネットワーク関連のエラーsystemError- システムまたはSDKのエラーnotEntitled- ユーザーに製品の権限がないproductUnavailable- 製品が利用できないpurchaseNotAllowed- 購入が許可されていないunknown- 不明なエラー
Message: String - 簡潔なエラーメッセージDescription: String - 詳細なエラーの説明Request: ErrorRequest(オプション) - 利用可能な場合のリクエストの詳細
ErrorRequest
プロパティ:
URL: String - リクエストのURLMethod: String - HTTPメソッドBody: String - リクエストの本体ResponseData: String - レスポンスのデータStatusCode: Integer - HTTPステータスコード
PurchaseIntent は、ユーザーがアプリ内購入を行う意図を表します。これは通常、アプリケーション外で開始された購入を確認または拒否するために使用されます。
このクラスは汎用的なメソッドを担当し、AppCoinsSDK.Instance を介したシングルトンアクセスを提供します。