Xamarin.Formsでサクッとクロスプラットフォーム課金処理実装

2021-04-02

この記事は、「InAppBillingPlugin」のv2に関する記事です。
最新のv4の実装に関する記事は以下を参考にしてください。2021年8月以降はv2での実装でAndroidのストアに公開できなくなります。

https://ondwn.com/xamarin-forms-billing-v4/

課金処理の実装について。
数年前と比べて課金の実装やテストはネイティブ実装でもかなりシンプルになってきている。そのため、Xamarinのネイティブ実装もかなり簡単なのだが、今回はライブラリをご紹介する。

Xamarin.Formsクロスプラットフォーム課金ライブラリ

クロスプラットフォームで課金処理を実装するなら以下のライブラリ「InAppBillingPlugin」がおすすめ。
https://jamesmontemagno.github.io/InAppBillingPlugin/

AndroidとiOSの非消耗型課金しか試していないが非常に簡単に実装できてしまう。

課金処理service

以下の様なインターフェイスクラスを用意。

    public interface IBillingAccess
    {

        Task<InAppBillingProduct> GetProductInfoAsync(string productId);
        Task<bool> PurchaseItem(string productId, string payload);
        Task<bool> WasItemPurchased(string productId);
    }

そして以下の様なサービスを作成(Documentほぼそのままだが)

public class BillingAccess : IBillingAccess
    {
        public BillingAccess()
        {

        }

        public async Task<InAppBillingProduct> GetProductInfoAsync(string productId)
        {
            var productIds = new string[] { productId };
            var billing = CrossInAppBilling.Current;
            try
            {
                var connected = await billing.ConnectAsync(ItemType.InAppPurchase);

                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(ItemType.InAppPurchase);
                if (!connected)
                {
                    return false;
                }

                var purchase = await billing.PurchaseAsync(productId, ItemType.InAppPurchase, payload);

                if (purchase == null)
                {
                    return false;
                }
                else if (purchase.State == PurchaseState.Purchased)
                {
                    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 billing.ConnectAsync(ItemType.InAppPurchase);

                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;
        }
    }

Android

Androidの方はMainActivity.csに以下を追加しなければ、GooglePlayの課金モーダル画面を閉じる時のコールバックを取得できない。



protected override void OnCreate(Bundle savedInstanceState)
        {
	    // 以下を追加
            Plugin.CurrentActivity.CrossCurrentActivity.Current.Activity = this;
        }
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
        {
            base.OnActivityResult(requestCode, resultCode, data);
            InAppBillingImplementation.HandleActivityResult(requestCode, resultCode, data);
        }

Xamarinからの課金処理呼び出し

普通に上記のserviceを使用して、課金アイテムの情報取得、課金実行、リストア処理を実行できる。ここではサーバーでの検証実装等は紹介しない。

_billingAccess = DependencyService.Resolve<IBillingAccess>();
// アイテム情報の取得
var info = await _billingAccess.GetProductInfoAsync("hoge-productId");
// 課金実行
var result = await _billingAccess.PurchaseItem("hoge-productId", "任意の識別文字列");
// リストア
var result = await _billingAccess.WasItemPurchased("hoge-productId");

この記事は、「InAppBillingPlugin」のv2に関する記事です。
最新のv4の実装に関する記事は以下を参考にしてください。2021年8月以降はv2での実装でAndroidのストアに公開できなくなります。

https://ondwn.com/xamarin-forms-billing-v4/