Xamarin.Forms-InAppBillingPluginでの課金実装方法と注意点
xamarinでの課金処理の実装について。
以前書いた記事で、ライブラリ「InAppBillingPlugin」を紹介しましたが、こちらLatestバージョンだととんでもない落とし穴があります。
Xamarin.Forms-InAppBillingPlugin v4について
https://jamesmontemagno.github.io/InAppBillingPlugin/
このライブラリはXamarinでiOS, Androidの両方の課金実装が簡単にできる代物です。v2の記事で紹介した通り、とても簡単です。
そして、Androidの課金APIが更新されたため、この「InAppBillingPlugin」もv4に更新されました。が、注意点と落とし穴が2つありました。
1.公式ページの実装サンプルがv4のものではない
2021年4月現在、公式のページに記載されている実装方法はv2のもので、そのままではビルドがとおりません。なので以下のように修正する必要があります。(サーバーサイドのtokenチェックはなし)
InAppBillingPlugin v4での実装
以下の様なインターフェイスクラスを用意。
public interface IBillingAccess
{
Task<InAppBillingProduct> GetProductInfoAsync(string productId);
Task<bool> PurchaseItem(string productId, string payload);
Task<bool> WasItemPurchased(string productId);
}
そして以下の様なサービスを作成。billing.ConnectAsyncとbilling.GetProductInfoAsyncのinterfaceがv2から変更されています。
public class BillingAccess : IBillingAccess
{
public async Task<InAppBillingProduct> GetProductInfoAsync(string productId)
{
var productIds = new string[] { productId };
var billing = CrossInAppBilling.Current;
try
{
var connected = await billing.ConnectAsync();
if (!connected)
{
return null;
}
var items = await billing.GetProductInfoAsync(ItemType.InAppPurchase, productIds);
InAppBillingProduct ret = items.FirstOrDefault();
return ret;
}
catch (InAppBillingPurchaseException pEx)
{
return null;
}
catch (Exception ex)
{
return null;
}
finally
{
await billing.DisconnectAsync();
}
}
public async Task<bool> PurchaseItem(string productId, string payload)
{
var billing = CrossInAppBilling.Current;
try
{
var connected = await billing.ConnectAsync();
if (!connected)
{
return false;
}
var purchase = await billing.PurchaseAsync(productId, ItemType.InAppPurchase);
if (purchase == null)
{
return false;
}
else if (purchase.State == PurchaseState.Purchased)
{
if (Device.RuntimePlatform == Device.iOS) return true;
// 非消耗型なら
await billing.AcknowledgePurchaseAsync(purchase.PurchaseToken);
return true;
}
}
catch (InAppBillingPurchaseException purchaseEx)
{
Console.WriteLine("Error: " + purchaseEx);
}
catch (Exception ex)
{
Console.WriteLine("Issue connecting: " + ex);
}
finally
{
await billing.DisconnectAsync();
}
return false;
}
public async Task<bool> WasItemPurchased(string productId)
{
var billing = CrossInAppBilling.Current;
try
{
var connected = await await billing.ConnectAsync();
if (!connected)
{
return false;
}
var purchases = await billing.GetPurchasesAsync(ItemType.InAppPurchase);
if (purchases?.Any(p => p.ProductId == productId) ?? false)
{
return true;
}
else
{
return false;
}
}
catch (InAppBillingPurchaseException purchaseEx)
{
Console.WriteLine("Error: " + purchaseEx);
}
catch (Exception ex)
{
//エラー処理
}
finally
{
await billing.DisconnectAsync();
}
return false;
}
}
2.非消耗アイテムの場合はAcknowledgePurchaseAsyncが必要!
1.のようにDocページが古いv2で書かれていたとしても、コンパイルが通らないので、そのことに簡単に気づけますし対応も簡単です。
しかし、非消耗アイテムのアプリ内課金を実装しようとしている場合は、もう一つの落とし穴があります。
1.で書いているコードは正しくAcknowledgePurchaseAsyncをコールしていますが、これをコールしないとユーザーの購入処理後3日でその課金が取り消されてしまいます。
まぁGoogle Play Billing Libraryの最新版では当然の情報らしいのですが、私のようなAndroid開発に全く注力していない人間にとってはさっぱりなわけです。Googleのドキュメントにもしっかりとそのことが書かれています。
https://developer.android.com/google/play/billing/integrate?hl=ja
つまり、2021年8月まではGoogle Play Billing Libraryの最新を利用していなければ、ユーザーの非消耗アイテムの「購入」と同時に「承認」がされていましたが、新しいバージョンでは明示的に「承認」を実行しないと3日後に「購入」がキャンセルされ払い戻しになるということです。
そしてxamarinの「InAppBillingPlugin」でもv4によりこの新しいGoogle Play Billing Libraryに対応したとのことでしたが、この重大な修正点について「InAppBillingPlugin」のページには書かれていないように見えます。(Githubページにはしっかり書かれています)
そのため、お恥ずかしながら購入処理完了後、AcknowledgePurchaseAsyncをコールしていないアプリをストアで公開し、数日間のユーザー様の課金がすべて自動キャンセルされるという事態が発生。
「InAppBillingPlugin」のページだけを鵜呑みにせず、しっかりとGithubも見ていればこのようなことにはならなかったのですが。
「InAppBillingPlugin」を利用される方、私のようなミスを犯さないためにも、
必ずAcknowledgePurchaseAsyncが必要かどうか確認してください。
本当は、Google Developer APIでサーバーサイドから「承認」するのが正しいのかもですが。
https://developers.google.com/android-publisher/api-ref/rest/v3/purchases.products/acknowledge
ディスカッション
コメント一覧
まだ、コメントがありません