「Unity/課金/サンプル」の版間の差分
(→iOSの場合) |
(→全部コードで処理する場合) |
||
| (同じ利用者による、間の20版が非表示) | |||
| 行6: | 行6: | ||
日本語を追加した場合は、日本語の説明も入れないと、保存されないので気を付ける。 | 日本語を追加した場合は、日本語の説明も入れないと、保存されないので気を付ける。 | ||
| + | |||
| + | クローズドなAPKの登録が必要。 | ||
===iOSの場合=== | ===iOSの場合=== | ||
| − | # | + | #AppStoreConnect管理画面/app内課金/管理/+をクリック |
#消耗型を選択し、id(stone10)などを入れてく | #消耗型を選択し、id(stone10)などを入れてく | ||
#審査用の画像は(640 x 920)で登録すればよい | #審査用の画像は(640 x 920)で登録すればよい | ||
| 行34: | 行36: | ||
===iOSの場合=== | ===iOSの場合=== | ||
| − | # | + | #AppStoreConnect管理画面/app内課金/管理/+をクリック |
#自動更新サブスクリプションを追加(自動更新サブスクリプションがない場合は、ユーザ権限のとこの有料Appの契約とアメリカ納税情報が入ってるか確認する) | #自動更新サブスクリプションを追加(自動更新サブスクリプションがない場合は、ユーザ権限のとこの有料Appの契約とアメリカ納税情報が入ってるか確認する) | ||
| + | #参照名に製品名を適宜"プレミアム(継続)"など入れる | ||
#プロダクトidを(com.example.hogeapp.monthly)などに | #プロダクトidを(com.example.hogeapp.monthly)などに | ||
#期間を1ヶ月とかに設定する | #期間を1ヶ月とかに設定する | ||
| 行43: | 行46: | ||
====iOSでサブスクのアイテム名が変更できない場合==== | ====iOSでサブスクのアイテム名が変更できない場合==== | ||
翻訳の英語などを消したときなどは、英語のままになる、一度、日本語などの文字を更新すると良い。 | 翻訳の英語などを消したときなどは、英語のままになる、一度、日本語などの文字を更新すると良い。 | ||
| + | |||
| + | ===文言例=== | ||
| + | サブスクグループ名 | ||
| + | 英語:flatrate | ||
| + | 日本語:継続課金 | ||
| + | 月額 | ||
| + | 製品id:com.example.hogeapp.monthly | ||
| + | 表示名日本語:プレミアム(毎月継続) | ||
| + | 表示名英語:Premium (monthly continued) | ||
| + | 説明日本語:月額で1ヶ月間広告削除できます。 | ||
| + | 説明英語:You can delete ads for a month for a month. | ||
| + | |||
| + | 年額 | ||
| + | 製品id:com.example.hogeapp.yearly | ||
| + | 説明日本語:プレミアム(毎年継続) | ||
| + | 説明英語:Premium (yearly continued) | ||
| + | 説明日本語:年額で1年間広告削除できます。 | ||
| + | 説明英語:You can delete ads for a year for a year. | ||
| + | |||
| + | ===価格設定=== | ||
| + | applemusic例 | ||
| + | *月額:980円 | ||
| + | *年額:9800円 | ||
| + | *ファミリー月額:1480円 | ||
| + | 年額は月額の10倍で、ファミリーは月額の1.5倍っぽい。 | ||
| + | |||
| + | 参考:https://www.kyodotokyo.com/music/apple-fee/#:~:text=%E6%9C%88%E9%A1%8D980%E5%86%86%EF%BC%88%E7%A8%8E%E8%BE%BC%EF%BC%89%E3%82%92,%E9%9D%9E%E5%B8%B8%E3%81%AB%E3%81%8A%E5%BE%97%E3%81%A7%E3%81%99%E3%80%82 | ||
| + | |||
| + | ===ios審査情報のスクリーンショットサイズ=== | ||
| + | 1290 x 2796 | ||
==Unityから課金用ボタン設定== | ==Unityから課金用ボタン設定== | ||
| 行71: | 行104: | ||
using UnityEngine; | using UnityEngine; | ||
using UnityEngine.Purchasing; | using UnityEngine.Purchasing; | ||
| + | using UnityEngine.Purchasing.Extension; | ||
#if RECEIPT_VALIDATION | #if RECEIPT_VALIDATION | ||
| 行76: | 行110: | ||
#endif | #endif | ||
| − | public class UnityIAPManager : IStoreListener | + | public class UnityIAPManager : IStoreListener, IDetailedStoreListener |
{ | { | ||
private IStoreController controller; | private IStoreController controller; | ||
| 行87: | 行121: | ||
{ | { | ||
var builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance()); | var builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance()); | ||
| − | builder.AddProduct("stone10", ProductType.Consumable); | + | builder.AddProduct("stone10", ProductType.Consumable); // 都度課金の場合 |
| − | UnityPurchasing.Initialize(this, builder); | + | builder.AddProduct("premium_monthly", ProductType.Consumable, ProductType.Subscription); // 定額課金の場合 |
| + | UnityPurchasing.Initialize((IDetailedStoreListener)this, builder); | ||
} | } | ||
| 行189: | 行224: | ||
public void OnInitializeFailed(InitializationFailureReason error) | public void OnInitializeFailed(InitializationFailureReason error) | ||
{ | { | ||
| − | Debug.Log("OnInitializeFailed"); | + | Debug.Log("OnInitializeFailed " + error.ToString()); |
| + | } | ||
| + | public void OnInitializeFailed(InitializationFailureReason error, string message) | ||
| + | { | ||
| + | Debug.Log("OnInitializeFailed " + error.ToString() + " " + message); | ||
| + | } | ||
| + | public void OnPurchaseFailed(Product product, PurchaseFailureDescription failureDescription) | ||
| + | { | ||
| + | Debug.Log("OnPurchaseFailed " + failureDescription.productId + " " + failureDescription.message); | ||
} | } | ||
| − | |||
/// <summary> | /// <summary> | ||
/// 購入が終了したときに呼び出されます。 | /// 購入が終了したときに呼び出されます。 | ||
| 行307: | 行349: | ||
return false; | return false; | ||
} | } | ||
| + | /* | ||
| + | // IAP 2.2 supports end : developerPayload == null | ||
var original_json_payload_wrapper = (Dictionary<string, object>)MiniJson.JsonDecode((string)payload_wrapper["json"]); | var original_json_payload_wrapper = (Dictionary<string, object>)MiniJson.JsonDecode((string)payload_wrapper["json"]); | ||
if (original_json_payload_wrapper == null || !original_json_payload_wrapper.ContainsKey("developerPayload")) { | if (original_json_payload_wrapper == null || !original_json_payload_wrapper.ContainsKey("developerPayload")) { | ||
| 行318: | 行362: | ||
return false; | return false; | ||
} | } | ||
| + | */ | ||
return true; | return true; | ||
} | } | ||
| 行435: | 行480: | ||
the number of product introductory price period cycles is: 0 | the number of product introductory price period cycles is: 0 | ||
</pre> | </pre> | ||
| + | |||
| + | 公式:CrossPlatformValidatorの使い方:https://docs.unity3d.com/ja/2019.4/Manual/UnityIAPValidatingReceipts.html | ||
===全部コードで処理する場合(その2)=== | ===全部コードで処理する場合(その2)=== | ||
| 行458: | 行505: | ||
*Assets/Plugins/UnityPurchasing/scenes/IAP Demo.unity | *Assets/Plugins/UnityPurchasing/scenes/IAP Demo.unity | ||
*Assets/Plugins/UnityPurchasing/script/IAPDemo.cs | *Assets/Plugins/UnityPurchasing/script/IAPDemo.cs | ||
| + | ==AndroidのPlayStoreの為替レートによる課金設定で7D7DE1A7が出る場合== | ||
| + | エラー詳細 | ||
| + | 予期しないエラーが発生しました。もう一度お試しください(7D7DE1A7) | ||
| + | レバノンだけを抜いておけば、このエラーが出なくなる | ||
==参考== | ==参考== | ||
2025年9月14日 (日) 20:29時点における最新版
都度購入
Androidの場合
- GooglePlayDeveloper/指定アプリ/ストアで表示/アプリ内サービス/管理対象のアイテム/管理対象のアイテム作成
- プロダクトidを(stone10)などで入れる(storeと連携してないと、購入ボタンを押しても商品が無いとなる)
- 有効にするを選択して、有効になっていることを確認
日本語を追加した場合は、日本語の説明も入れないと、保存されないので気を付ける。
クローズドなAPKの登録が必要。
iOSの場合
- AppStoreConnect管理画面/app内課金/管理/+をクリック
- 消耗型を選択し、id(stone10)などを入れてく
- 審査用の画像は(640 x 920)で登録すればよい
- ituneconnectから契約を選択して、有料Appの利用規約に同意
- 口座情報を入れる(ゆうちょなら"Japan Post Bank"で"9900-[口座番号]"
- 納税にアメリカ納税情報を入れる
- 納税情報を入れたところOnInitializeFailed(NoProductsAvailable)の失敗イベントが呼ばれなくなり、正常にOnInitializedが呼ばれるようになった!
"Type of Income"は"Income from the sale of applications" Capacity in which acting欄に "Self"
参考:https://re35.org/release-ios-app/
参考:http://lab.studioheat.com/?p=335
定額課金
サブスクリプション(サブスク)ともいう
Androidの場合
- GooglePlayDeveloper/指定アプリ/ストアで表示/アプリ内サービス/定期購入
- プロダクトidを(com.example.hogeapp.monthly)などで入れる(storeと連携してないと、購入ボタンを押しても商品が無いとなる)
- 有効にするを選択して、有効になっていることを確認
日本語を追加した場合は、日本語の説明も入れないと、保存されないので気を付ける。
iOSの場合
- AppStoreConnect管理画面/app内課金/管理/+をクリック
- 自動更新サブスクリプションを追加(自動更新サブスクリプションがない場合は、ユーザ権限のとこの有料Appの契約とアメリカ納税情報が入ってるか確認する)
- 参照名に製品名を適宜"プレミアム(継続)"など入れる
- プロダクトidを(com.example.hogeapp.monthly)などに
- 期間を1ヶ月とかに設定する
- app内課金/サブスクリプショングループができるので審査用の画面キャプチャなどを登録し、ステータスを送信準備完了にする。
- プロジェクトビルドを設定する画面に、app内課金の項目が出るので、追加したapp内課金を選択して、app審査してもらう。
iOSでサブスクのアイテム名が変更できない場合
翻訳の英語などを消したときなどは、英語のままになる、一度、日本語などの文字を更新すると良い。
文言例
サブスクグループ名
英語:flatrate 日本語:継続課金
月額
製品id:com.example.hogeapp.monthly 表示名日本語:プレミアム(毎月継続) 表示名英語:Premium (monthly continued) 説明日本語:月額で1ヶ月間広告削除できます。 説明英語:You can delete ads for a month for a month.
年額
製品id:com.example.hogeapp.yearly 説明日本語:プレミアム(毎年継続) 説明英語:Premium (yearly continued) 説明日本語:年額で1年間広告削除できます。 説明英語:You can delete ads for a year for a year.
価格設定
applemusic例
- 月額:980円
- 年額:9800円
- ファミリー月額:1480円
年額は月額の10倍で、ファミリーは月額の1.5倍っぽい。
ios審査情報のスクリーンショットサイズ
1290 x 2796
Unityから課金用ボタン設定
課金の記述方法として以下2パターンがある
- 全部コードで処理する方法
- コードレス処理がある。
全部コードで処理する場合
- 以下課金呼び出しコードを記述(例:stone10はproductID)
UnityIAPManager manager;
manager = new UnityIAPManager();
manager.OnPurchaseClicked("stone10");
UnityIAPManager.cs
#if UNITY_PURCHASING
#if UNITY_ANDROID || UNITY_IPHONE || UNITY_STANDALONE_OSX || UNITY_TVOS
// You must obfuscate your secrets using Window > Unity IAP > Receipt Validation Obfuscator
// before receipt validation will compile in this sample.
#define RECEIPT_VALIDATION
#endif
#define SUBSCRIPTION_MANAGER //Enables subscription product manager for AppleStore and GooglePlay store
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Purchasing;
using UnityEngine.Purchasing.Extension;
#if RECEIPT_VALIDATION
using UnityEngine.Purchasing.Security;
#endif
public class UnityIAPManager : IStoreListener, IDetailedStoreListener
{
private IStoreController controller;
private IExtensionProvider extensions;
private IAppleExtensions m_AppleExtensions;
private List<Product> products;
public bool initialized = false;
public UnityIAPManager()
{
var builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance());
builder.AddProduct("stone10", ProductType.Consumable); // 都度課金の場合
builder.AddProduct("premium_monthly", ProductType.Consumable, ProductType.Subscription); // 定額課金の場合
UnityPurchasing.Initialize((IDetailedStoreListener)this, builder);
}
/// <summary>
/// iOS Specific.
/// This is called as part of Apple's 'Ask to buy' functionality,
/// when a purchase is requested by a minor and referred to a parent
/// for approval.
///
/// When the purchase is approved or rejected, the normal purchase events
/// will fire.
/// </summary>
/// <param name="item">Item.</param>
private void OnDeferred(Product item)
{
Debug.Log("Purchase deferred: " + item.definition.id);
}
public Product GetProductById(string productId)
{
foreach (Product product in products)
{
if (product.definition.id.Equals(productId)) {
return product;
}
}
return null; // error
}
/// <summary>
/// Unity IAP が購入処理を行える場合に呼び出されます
/// </summary>
public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
{
this.controller = controller;
this.extensions = extensions;
Debug.Log("UnityIAPManager OnInitialized");
products = new List<Product>();
m_AppleExtensions = extensions.GetExtension<IAppleExtensions>();
m_AppleExtensions.RegisterPurchaseDeferredListener(OnDeferred);
#if SUBSCRIPTION_MANAGER
Dictionary<string, string> introductory_info_dict = m_AppleExtensions.GetIntroductoryPriceDictionary();
#endif
foreach (Product item in controller.products.all)
{
if (item.availableToPurchase)
{
products.Add(item);
Debug.Log("definition.id=" + item.definition.id);
Debug.Log("localizedTitle=" + item.metadata.localizedTitle);
Debug.Log("localizedDescription=" + item.metadata.localizedDescription);
Debug.Log("localizedPriceString=" + item.metadata.localizedPriceString);
Debug.Log("isoCurrencyCode=" + item.metadata.isoCurrencyCode);
Debug.Log("localizedPrice=" + item.metadata.localizedPrice.ToString());
Debug.Log("transactionID=" + item.transactionID);
Debug.Log("receipt=" + item.receipt);
#if SUBSCRIPTION_MANAGER
// this is the usage of SubscriptionManager class
if (item.receipt != null) {
if (item.definition.type == ProductType.Subscription) {
if (checkIfProductIsAvailableForSubscriptionManager(item.receipt)) {
string intro_json = (introductory_info_dict == null || !introductory_info_dict.ContainsKey(item.definition.storeSpecificId)) ? null : introductory_info_dict[item.definition.storeSpecificId];
SubscriptionManager p = new SubscriptionManager(item, intro_json);
SubscriptionInfo info = p.getSubscriptionInfo();
Debug.Log("product id is: " + info.getProductId());
Debug.Log("purchase date is: " + info.getPurchaseDate());
Debug.Log("subscription next billing date is: " + info.getExpireDate());
Debug.Log("is subscribed? " + info.isSubscribed().ToString());
Debug.Log("is expired? " + info.isExpired().ToString());
Debug.Log("is cancelled? " + info.isCancelled());
Debug.Log("product is in free trial peroid? " + info.isFreeTrial());
Debug.Log("product is auto renewing? " + info.isAutoRenewing());
Debug.Log("subscription remaining valid time until next billing date is: " + info.getRemainingTime());
Debug.Log("is this product in introductory price period? " + info.isIntroductoryPricePeriod());
Debug.Log("the product introductory localized price is: " + info.getIntroductoryPrice());
Debug.Log("the product introductory price period is: " + info.getIntroductoryPricePeriod());
Debug.Log("the number of product introductory price period cycles is: " + info.getIntroductoryPricePeriodCycles());
} else {
Debug.Log("This product is not available for SubscriptionManager class, only products that are purchase by 1.19+ SDK can use this class.");
}
} else {
Debug.Log("the product is not a subscription product");
}
} else {
Debug.Log("the product should have a valid receipt");
}
#endif
initialized = true;
}
}
/// <summary>
/// Unity IAP 回復不可能な初期エラーに遭遇したときに呼び出されます。
///
/// これは、インターネットが使用できない場合は呼び出されず、
/// インターネットが使用可能になるまで初期化を試みます。
/// </summary>
public void OnInitializeFailed(InitializationFailureReason error)
{
Debug.Log("OnInitializeFailed " + error.ToString());
}
public void OnInitializeFailed(InitializationFailureReason error, string message)
{
Debug.Log("OnInitializeFailed " + error.ToString() + " " + message);
}
public void OnPurchaseFailed(Product product, PurchaseFailureDescription failureDescription)
{
Debug.Log("OnPurchaseFailed " + failureDescription.productId + " " + failureDescription.message);
}
/// <summary>
/// 購入が終了したときに呼び出されます。
///
/// OnInitialized() 後、いつでも呼び出される場合があります。
/// </summary>
public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs e)
{
Debug.Log("PurchaseProcessingResult " + e.purchasedProduct.metadata.localizedTitle);
bool validPurchase = true; // R.V. のないプラットフォームに有効です
// Unity IAP の検証ロジックはこれらのプラットフォームにのみ含まれます。
#if UNITY_ANDROID || UNITY_IOS || UNITY_STANDALONE_OSX
// エディターの難読化ウィンドウで準備した機密を持つ
// バリデーターを準備します。
#if RECEIPT_VALIDATION
Debug.Log("RECEIPT_VALIDATION");
string appIdentifier;
#if UNITY_5_6_OR_NEWER
appIdentifier = Application.identifier;
Debug.Log("5.6 appIdentifier=" + appIdentifier);
#else
appIdentifier = Application.bundleIdentifier;
Debug.Log("other appIdentifier=" + appIdentifier);
#endif
var validator = new CrossPlatformValidator(GooglePlayTangle.Data(), AppleTangle.Data(), appIdentifier);
try
{
// Google Play で、result は 1 つの product ID を取得します
// Apple stores で、receipts には複数のプロダクトが含まれます
var result = validator.Validate(e.purchasedProduct.receipt);
// 情報提供の目的で、ここにレシートをリストします
Debug.Log("Receipt is valid. Contents:");
foreach (IPurchaseReceipt productReceipt in result)
{
Debug.Log("Receipt productID=" + productReceipt.productID);
Debug.Log("Receipt purchaseDate=" + productReceipt.purchaseDate);
Debug.Log("Receipt transactionID=" + productReceipt.transactionID);
GooglePlayReceipt google = productReceipt as GooglePlayReceipt;
if (null != google)
{
// ここに Google のオーダー ID
// sandbox でテストする場合は null にするように注意
// なぜなら、Google の sandbox はオーダー IDを発行しないため
Debug.Log("google.transactionID=" + google.transactionID);
Debug.Log("google.purchaseState=" + google.purchaseState);
Debug.Log("google.purchaseToken=" + google.purchaseToken);
}
AppleInAppPurchaseReceipt apple = productReceipt as AppleInAppPurchaseReceipt;
if (null != apple)
{
Debug.Log("apple.originalTransactionIdentifier=" + apple.originalTransactionIdentifier);
Debug.Log("apple.subscriptionExpirationDate=" + apple.subscriptionExpirationDate);
Debug.Log("apple.cancellationDate=" + apple.cancellationDate);
Debug.Log("apple.quantity=" + apple.quantity);
}
}
}
catch (IAPSecurityException)
{
Debug.Log("Invalid receipt, not unlocking content");
validPurchase = false;
}
#endif
#endif
if (validPurchase)
{
// 適当なコンテンツをここにアンロックします
}
return PurchaseProcessingResult.Complete;
}
/// <summary>
/// 購入が失敗したときに呼び出されます。
/// </summary>
public void OnPurchaseFailed(Product i, PurchaseFailureReason p)
{
Debug.Log("OnPurchaseFailed product=" + i.ToString() + " " + p.ToString());
if (p == PurchaseFailureReason.PurchasingUnavailable)
{
// デバイス設定で IAP が無効である場合があります。
}
}
public void OnPurchaseClicked(string productId)
{
Debug.Log("OnPurchaseClicked productId=" + productId);
if (controller != null)
{
controller.InitiatePurchase(productId);
}
}
#if SUBSCRIPTION_MANAGER
private bool checkIfProductIsAvailableForSubscriptionManager(string receipt) {
var receipt_wrapper = (Dictionary<string, object>)MiniJson.JsonDecode(receipt);
if (!receipt_wrapper.ContainsKey("Store") || !receipt_wrapper.ContainsKey("Payload")) {
Debug.Log("The product receipt does not contain enough information");
return false;
}
var store = (string)receipt_wrapper ["Store"];
var payload = (string)receipt_wrapper ["Payload"];
if (payload != null ) {
switch (store) {
case GooglePlay.Name:
{
var payload_wrapper = (Dictionary<string, object>)MiniJson.JsonDecode(payload);
if (!payload_wrapper.ContainsKey("json")) {
Debug.Log("The product receipt does not contain enough information, the 'json' field is missing");
return false;
}
/*
// IAP 2.2 supports end : developerPayload == null
var original_json_payload_wrapper = (Dictionary<string, object>)MiniJson.JsonDecode((string)payload_wrapper["json"]);
if (original_json_payload_wrapper == null || !original_json_payload_wrapper.ContainsKey("developerPayload")) {
Debug.Log("The product receipt does not contain enough information, the 'developerPayload' field is missing");
return false;
}
var developerPayloadJSON = (string)original_json_payload_wrapper["developerPayload"];
var developerPayload_wrapper = (Dictionary<string, object>)MiniJson.JsonDecode(developerPayloadJSON);
if (developerPayload_wrapper == null || !developerPayload_wrapper.ContainsKey("is_free_trial") || !developerPayload_wrapper.ContainsKey("has_introductory_price_trial")) {
Debug.Log("The product receipt does not contain enough information, the product is not purchased using 1.19 or later");
return false;
}
*/
return true;
}
case AppleAppStore.Name:
case AmazonApps.Name:
case MacAppStore.Name:
{
return true;
}
default:
{
return false;
}
}
}
return false;
}
#endif
}
#endif // UNITY_PURCHASING
android処理ログ
// 購入初期化
07-12 22:25:15.646 19383 19444 I Unity : UnityIAPManager OnInitialized
07-12 22:25:15.664 19383 19444 I Unity : Title=This is stone10 (Flick Typing input practice app)
07-12 22:25:15.681 19383 19444 I Unity : Description=This is stone10!!
07-12 22:25:15.698 19383 19444 I Unity : PriceString=¥100
// 購入確認
07-12 22:25:17.046 19383 19444 I Unity : OnPurchaseClicked productId=stone10
// 購入後
07-12 22:25:27.994 19383 19444 I Unity : PurchaseProcessingResult This is stone10 (Flick Typing input practice app)
07-13 01:15:54.698 31878 31955 I Unity : RECEIPT_VALIDATION
07-13 01:15:54.715 31878 31955 I Unity : 5.6 appIdentifier=com.example.hogeapp
07-13 01:28:23.648 1078 1232 I Unity : Receipt is valid. Contents:
07-13 01:28:23.659 1078 1232 I Unity : Receipt productID=stone10
07-13 01:28:23.672 1078 1232 I Unity : Receipt purchaseDate=07/12/2020 16:28:23
07-13 01:28:23.683 1078 1232 I Unity : Receipt transactionID=GPA.3366-6958-2462-12345
07-13 02:36:40.517 5111 5247 I Unity : google.transactionID=GPA.3366-6958-2462-12345
07-13 02:36:40.526 5111 5247 I Unity : google.purchaseState=Purchased
07-13 02:36:40.534 5111 5247 I Unity : google.purchaseToken=lahnjifjnlchkhmbjjlghhbn.AO-J1OyyXV_avfKglYJsff9pPoW_5sOC3YshbqKuj8fE52bAzgnVMjwtVCj45b97yDcg4CV5arAfDndgwo-CcgHTqJ_vMFNsjULWZOuS-6K3C1qXbeVLGh9qQ2cGdJUvgVJsy123456_
// 購入後再表示(定期課金の場合)
07-15 22:30:32.488 9185 9280 I Unity : UnityIAPManager OnInitialized
07-15 22:30:32.626 9185 9280 I Unity : the product should have a valid receipt
07-15 22:30:32.645 9185 9280 I Unity : definition.id=com.example.hogeapp1.monthly
07-15 22:30:32.645 9185 9280 I Unity : localizedTitle=1ヶ月間広告削除 (ホゲアプリ1)
07-15 22:30:32.662 9185 9280 I Unity : localizedDescription=1ヶ月間広告削除
07-15 22:30:32.674 9185 9280 I Unity : localizedPriceString=¥600
07-15 22:30:32.686 9185 9280 I Unity : isoCurrencyCode=JPY
07-15 22:30:32.699 9185 9280 I Unity : localizedPrice=600
07-15 22:30:32.713 9185 9280 I Unity : transactionID=GPA.3364-9546-9734-46033
07-15 22:30:32.723 9185 9280 I Unity : receipt={"Store":"GooglePlay","TransactionID":"GPA.3364-9546-9734-46033","Payload":"{\"json\":\"{\\\"orderId\\\":\\\"GPA.3364-9546-9734-46033\\\",\\\"packageName\\\":\\\"com.example.hogeapp1\\\",\\\"productId\\\":\\\"com.example.hogeapp1.monthly\\\",\\\"purchaseTime\\\":1594818391067,\\\"purchaseState\\\":0,\\\"developerPayload\\\":\\\"{\\\\\\\"developerPayload\\\\\\\":\\\\\\\"\\\\\\\",\\\\\\\"is_free_trial\\\\\\\":false,\\\\\\\"has_introductory_price_trial\\\\\\\":false,\\\\\\\"is_updated\\\\\\\":false,\\\\\\\"accountId\\\\\\\":\\\\\\\"\\\\\\\"}\\\",\\\"purchaseToken\\\":\\\"lhcgpmbhnhbgcemjphdcgcpf.AO-J1OyNfcQCd1PdWqDIS9JJt3QXUV5f9gWXag_ABxHLk4w_nGLn7OVdsEufD-7-FAsvcvvdK4Ouw-XgwJH9VCTZ4BXrZAE2UYc78BM6p7br2PAh9s1Rpa0QymVjLFEooCbNeAj0BqbDQRW0S1ELlzWC-3odxYqs8w\\\",\\\"autoRenewing\\\":true}\",\"signature\":\"cQzMST2TT3tduG7mqHu8Q1AmSgWFnh1nXpZWYOvD6dRWSRz+OCfdrxOkZIiDGO8tVsXhb9XNq8vIMbMdzzV7\\/rh5LjQDbs1vm1ojxY5Id2UNMw\\/MghnNfCj78kkJHF1N8Ea+pQdVyErIjmWJvLLo6Vdx7aPH8dGnRXqMiXdoRC9utsvjWCegntAnTPM
07-15 22:30:32.735 9185 9280 I Unity : product id is: com.example.hogeapp1.monthly
07-15 22:30:32.746 9185 9280 I Unity : purchase date is: 07/15/2020 13:06:31
07-15 22:30:32.758 9185 9280 I Unity : subscription next billing date is: 08/15/2020 13:06:31
07-15 22:30:32.769 9185 9280 I Unity : is subscribed? True
07-15 22:30:32.780 9185 9280 I Unity : is expired? False
07-15 22:30:32.794 9185 9280 I Unity : is cancelled? False
07-15 22:30:32.808 9185 9280 I Unity : product is in free trial peroid? False
07-15 22:30:32.826 9185 9280 I Unity : product is auto renewing? True
07-15 22:30:32.843 9185 9280 I Unity : subscription remaining valid time until next billing date is: 30.23:35:58.3409010
07-15 22:30:32.860 9185 9280 I Unity : is this product in introductory price period? False
07-15 22:30:32.875 9185 9280 I Unity : the product introductory localized price is: not available
07-15 22:30:32.888 9185 9280 I Unity : the product introductory price period is: 00:00:00
07-15 22:30:32.903 9185 9280 I Unity : the number of product introductory price period cycles is: 0
ios処理ログ
// 購入初期化
UnityIAPManager OnInitialized
localizedTitle=
localizedDescription=
localizedPriceString=¥600
isoCurrencyCode=JPY
localizedPrice=600
transactionID=
receipt=
// 購入確認
OnPurchaseClicked productId=com.example.hogeapp1.monthly
// 購入後
PurchaseProcessingResult
RECEIPT_VALIDATION
5.6 appIdentifier=com.example.hogeapp1
Receipt is valid. Contents:
Receipt productID=com.example.hogeapp1.monthly
Receipt purchaseDate=2020/07/15 12:18:16
Receipt transactionID=1000000693275565
apple.originalTransactionIdentifier=1000000693275565
apple.subscriptionExpirationDate=2020/07/15 12:23:16
apple.cancellationDate=0001/01/01 0:00:00
apple.quantity=1
// 購入後再表示(定期課金の場合)
UnityIAPManager OnInitialized
definition.id=com.example.hogeapp1.monthly
localizedTitle=
localizedDescription=
localizedPriceString=¥600
isoCurrencyCode=JPY
localizedPrice=600
transactionID=1000000693295003
receipt={"Store":"AppleAppStore","TransactionID":"1000000693295003","Payload":"MIIUCwYJKoAQBhdMzH・・・TxhlpfU="} // Payloadは6000文字ぐらいある
product id is: com.example.hogeapp1.monthly
purchase date is: 2020/07/15 12:49:45
subscription next billing date is: 2020/07/15 12:54:45
is subscribed? True
is expired? False
is cancelled? False
product is in free trial peroid? False
product is auto renewing? True
subscription remaining valid time until next billing date is: 00:02:19.6844300
is this product in introductory price period? False
the product introductory localized price is: not available
the product introductory price period is: 00:00:00
the number of product introductory price period cycles is: 0
公式:CrossPlatformValidatorの使い方:https://docs.unity3d.com/ja/2019.4/Manual/UnityIAPValidatingReceipts.html
全部コードで処理する場合(その2)
こちらを使ってもいける。
https://gist.github.com/YoshihideSogawa/f7c118127ce50e593a5b4a12e8426d6e
- 上記をPurchaser.csで保存する
- Unityのヒエラルキーに新規Objectを作成し、適当な名前で、Purchaser.csをアタッチする
コードレスの場合
(未完成です)
- unityメニュー/windows/UnityIAP/CreateIAPButtonでボタンを作成する
- unityメニュー/windows/IAP Catalogをクリックし以下のような詳細データを入れる
id:monthlyなど type:Subscription(自動課金)
- 作ったbuttonのプロパティのproductIdに先ほどいれたid(monthlyなど)いれる
- Assets/Plugins/UnityPurchasing/script/CodelessIAPStoreListener.csにイベントが発生するので確認する
公式デモ
課金プラグインを入れると、以下に入ってるので確認する
- Assets/Plugins/UnityPurchasing/scenes/IAP Demo.unity
- Assets/Plugins/UnityPurchasing/script/IAPDemo.cs
AndroidのPlayStoreの為替レートによる課金設定で7D7DE1A7が出る場合
エラー詳細
予期しないエラーが発生しました。もう一度お試しください(7D7DE1A7)
レバノンだけを抜いておけば、このエラーが出なくなる
参考
コードレスの場合の参考
http://it-happens.info/unity-purchase/
全部コードの場合の参考
https://docs.unity3d.com/ja/current/Manual/UnityIAPSettingUp.html
http://www.kyucon.com/blog/2018/12/unity-playfab.html
https://gist.github.com/YoshihideSogawa/f7c118127ce50e593a5b4a12e8426d6e
