記事一覧

In-App Purchase で 購入履歴を復元する際の注意点 | Xamarin.iOS


iOS 用アプリにて 決済機能を追加する際に、かなりハマりポイントがありましたので、注意する点について述べたいと思います。


前提条件
・Windows10
・Visual Studio 2015 Community Update3
・Xamarin.Forms 4.2.0.703 (NuGet2.3.2.127)
・macOS Sierra 10.12 / Xcode8 / Xamarin.iOS 10.0.1.8
iTunes Connect にて テスターの設定が完了していること



1.デバッグはシミュレータではなく、実機で行う

 シミュレータで購入履歴を復元しようとすると、Sandbox テストの場合、エラーになってしまいます。

 SKPaymentTransactionObserverを継承したクラスにて、以下のメソッドが発生します。エラーの内容は 「Cannot connect to iTunes Store. 」 iTunes Storeに接続ができないといわれてしまいます。
 その場合、以下のメソッドにてエラー内容が渡っています。
public override void RestoreCompletedTransactionsFailedWithError(SKPaymentQueue queue, NSError error)

RestoreProducts_1.png   
また、テストアカウントではない正規のAppleIDを実機に埋め込んでいる場合は「code 0 : iTunes Store に接続できません」というエラーが返ってきますのでご注意ください。



2.プログラムの発生順序について

購入履歴を正常に復元した場合
public void RestoreProducts()
{
    SKPaymentQueue.DefaultQueue.RestoreCompletedTransactions();
}
↓非同期
public override void SKPaymentTransactionObserver.UpdatedTransactions(SKPaymentQueue queue, SKPaymentTransaction[] transactions)

public void RestoreTransaction(SKPaymentTransaction transaction)
{
    SKPaymentQueue.DefaultQueue.FinishTransaction(transaction);
}
↓登録イベント発生
this._restoreProductsObserver = NSNotificationCenter.DefaultCenter.AddObserver(InAppService.InAppRestoreProductsNotification,
    (notification) =>
    {
        // Notify anyone who needed to know that products were restored
        if (this.OnRestoreProducts != null)
        {
            this.OnRestoreProducts();
        }
    });

※上記だと購入履歴が無い場合に呼び出されませんので、
SKPaymentTransactionObserver.RestoreCompletedTransactionsFinished(SKPaymentQueue queue)
からOnRestoreCompleted等のイベントを発生させたほうが良いでしょう。

購入手続きが正常に行われた場合
public void PurchaseProduct(string productId)
{
    var payment = SKPayment.PaymentWithProduct(productId);
    SKPaymentQueue.DefaultQueue.AddPayment(payment);
}
↓非同期
public override void SKPaymentTransactionObserver.UpdatedTransactions(SKPaymentQueue queue, SKPaymentTransaction[] transactions)

public void PurchaseTransaction(SKPaymentTransaction transaction)
{
    SKPaymentQueue.DefaultQueue.FinishTransaction(transaction);
}
↓登録イベント発生
this._purchaseProductObserver = NSNotificationCenter.DefaultCenter.AddObserver(InAppService.InAppPurchaseProductNotification,
    (notification) =>
    {
        // Notify anyone who needed to know that product was purchased
        if (this.OnPurchaseProduct != null)
        {
            this.OnPurchaseProduct();
        }
    });


購入履歴を復元できなかった場合(エラーの場合)
public void RestoreProducts()
{
    SKPaymentQueue.DefaultQueue.RestoreCompletedTransactions();
}
↓非同期
public override void RestoreCompletedTransactionsFailedWithError(SKPaymentQueue queue, NSError error)



3.SKPaymentTransactionState について

<State>
Purchasing:購入処理中
Purchased:購入処理完了
Failed    :失敗
Restored :購入履歴を取得完了
Deferred  : ペアレンタルコントロールで保留中

public override void UpdatedTransactions(SKPaymentQueue queue, SKPaymentTransaction[] transactions)
{
    foreach (SKPaymentTransaction transaction in transactions)
    {
        switch (transaction.TransactionState)
        {
            case SKPaymentTransactionState.Purchasing:
                break;
            case SKPaymentTransactionState.Purchased:
                this._inAppService.PurchaseTransaction(transaction);
                break;
            case SKPaymentTransactionState.Failed:
                this._inAppService.FailedTransaction(transaction);
                break;
            case SKPaymentTransactionState.Restored:
                this._inAppService.RestoreTransaction(transaction);
                break;
            default:
                break;
        }
    }
}

Deferred が iOS8 から追加になっていますが、default: に落ちるのであれば現状は問題ないでしょう。



4.Subscriptions の有効期間について
SandBoxにてテストを実施する際、Subscriptions の有効期間が実際とは異なります。
有効期間は以下の通りです。
本番環境 SandBox
1週間 3分
1ヵ月 5分
2ヵ月 10分
3ヵ月 15分
6ヵ月 30分
1年 1時間

 AppStoreに配布されてからはiPhoneの設定から購読の停止ができますが、Sandboxでは停止することができません。自動更新はされませんので年間購読であれば1時間待つしかありません。
 その後有効期間を迎えても、UpdatedTransactionsによって購読が継続の処理(自動更新)が行われます。それが6回発生しますので、結局6時間待つことになっています。おそらく本番と同じ仕様なのでしょう。
だったら停止(キャンセル)処理ができるようにしておいて欲しいですね。。。



5.Apple の無駄な仕様について

 これはAppleの審査チーム(iTune Store Review Team)から言われたことですが、必ず復元ボタンを用意しなければいけないということです。
 端末初期化や機種変更時などの対策として、購入履歴のデータをアプリ内に復元する方法は必要ですが、iOSの場合はボタンとして存在しないと Review にて Reject されます。いちいちボタンを押させるという無駄な行為で、手間ですが、そういう規則だそうです。私は自動で復元する機能を搭載したアプリを審査に出しましたが、Rejectされ、電話で議論しましたが、一歩も応じませんでした。



6.In-App Purchase Shared Secret (App内課金共有シークレット) の取得方法

 iTunes Connect の マイApp のページにて取得します。
 詳しくは以下のページにて取得場所をご紹介しています。
In-App Purchase Shared Secret (App内課金共有シークレット) の取得方法について | iTunes Connect



7.レシート取得時のステータスについて

以下のURLに記載ありますが、
https://developer.apple.com/jp/documentation/ValidateAppStoreReceipt.pdf

status 説明
        0 正常に接続できています。
21000 App Storeは、提供したJSONオブジェクトを読むことができません。
21002 receipt-dataプロパティのデータが不正であるか、または欠落しています。
21003 レシートを認証できません。
21004 この共有秘密鍵は、アカウントのファイルに保存された共有秘密鍵と一致し ません。自動更新型の購読に用いる、iOS 6型のトランザクションレシートの場合の み。
21005 レシートサーバは現在利用できません。
21006 このレシートは有効ですが、定期購読の期限が切れています。ステータス コードがサーバに返される際、レシートデータもデコードされ、応答の一部 として返されます。自動更新型の購読に用いる、iOS 6型のトランザクションレシートの場合のみ。
21007 テスト環境のレシートを、実稼働環境に送信して検証しようとしました。こ れはテスト環境に送信してください。
21008 実稼働環境のレシートを、テスト環境に送信して検証しようとしました。こ れは実稼働環境に送信してください。

status ==21007 の場合、SandBoxに送信するようにプログラムすることが iTunes Connect の審査上、必須となっています。
レシートに関するコーディングについては以下のURLにてご説明しております。
Xamarin.iOS でレシートを取得するサンプルコード | Xamarin.forms



当ブログの内容をまとめた Xamarin逆引きメニュー は以下のURLからご覧になれます。
http://itblogdsi.blog.fc2.com/blog-entry-81.html


関連記事

コメント

コメントの投稿

アルバム

広告

プロフィール

石河 純


著者名 :石河 純
自己紹介:素人上がりのIT技術者。趣味は卓球・車・ボウリング

IT関連の知識はざっくりとこんな感じです。
【OS関連】
WindowsServer: 2012/2008R2/2003/2000/NT4
Windows: 10/8/7/XP/2000/me/NT4/98
Linux: CentOS RedHatLinux9
Mac: macOS Sierra 10.12 / OSX Lion 10.7.5 / OSX Snow Leopard 10.6.8
【言語・データベース】
VB.net ASP.NET C#.net Java VBA
Xamarin.Forms
Oracle10g SQLServer2008R2 SQLAnywhere8/11/16
ActiveReport CrystalReport ReportNet(IBM)
【ネットワーク関連】
CCNP シスコ技術者認定
Cisco Catalyst シリーズ
Yamaha RTXシリーズ
FireWall関連
【WEB関連】
SEO SEM CSS IIS6/7 apache2

休みの日は卓球をやっています。
現在、卓球用品通販ショップは休業中です。