[Win Store App] 對於IAP範例程式碼的研究(2)

再來看看消耗性商品的頁面吧。
InAppPurchaseConsumables
變數:
  • rootPage:為了使用MainPage的方法所創建的變數,為MainPage型別
  • numberOfConsumablesPurchased:消耗性商品購買次數,為int型別
  • grantedConsumableTransactionIds:為一字典,key是string,value是Guid的List。
方法(10個):
  • InAppPurchaseConsumables:初始子
  • OnNavigatedTo:設定numberOfConsumablesPurchased為0,並讀取xml檔案資料
  • OnNavigatingFrom:單純叫用super class的OnNavigatingFrom
  • LoadInAppPurchaseConsumablesProxyFileAsync:讀取消耗性商品的proxy file,並將購買訊息顯示於畫面上。
  • BuyAndFulfillProduct1Button_Click:按鈕,處理按下購買商品1按鈕的事件。根據回傳的string處理不同的條件之結果,
  • FulfillProduct1:模擬購買商品1的處理,
  • Log:擴充NotifyUser顯示訊息的方法
  • GetFulfillmentResults:配合Log,只是增加一句訊息內容
  • GrantFeatureLocally:主要是要處理購買後增加實際內容物的部分,像是金幣或道具
  • IsLocallyFulfilled
一個一個看吧。首先初始子和兩個公用方法OnNavigatedTo以及OnNavigatingFrom就不多說了,OnNavigatedTo初始化了變數numberOfConsumablesPurchased和grantedConsumableTransactionIds,LoadInAppPurchaseConsumablesProxyFileAsync的寫法也和之前講過的其他幾個頁面一樣。
首先看一下Log:
    private void Log(string message, NotifyType type)
    {
        string logMessage = message + "\n\n" + GetFulfillmentResults();
        rootPage.NotifyUser(logMessage, type);
    }
第一行是建立訊息,第二行是使用MainPage的方法NotifyUser來顯示訊息,訊息字串內容第一行是message,然後換行兩次,加上由GetFulfillmentResults提供的訊息。NotifyType型別的type變數,是指明訊息的種類,是狀態訊息還是錯誤訊息。注意到它只是在訊息字串裡面多加一個方法GetFulfillmentResults,其他基本上還是NotifyUser的形式,所以Log只是一個NotifyUser的擴充方法而已。接下來你只要把它看成是NotifyUser就很容易接受了。
那GetFulfillmentResults是什麼呢?來看一下內容:
    private string GetFulfillmentResults()
    {
        string message = "Product 1 has been fulfilled " + numberOfConsumablesPurchased + " time" + (numberOfConsumablesPurchased == 1 ? "" : "s") + ".";
        return message;
    }
這邊是處理Log方法所要顯示的訊息內容的第二行,簡單說,是要告訴使用者,交易了幾次,然後如果是超過一次的話,注意複數要加s(所以只是處理文法上的問題)。
然後我們來看GrantFeatureLocally方法:
    private void GrantFeatureLocally(string productId, Guid transactionId)
    {
        if (!grantedConsumableTransactionIds.ContainsKey(productId))
        {
            grantedConsumableTransactionIds.Add(productId, new List<Guid>());
        }
        grantedConsumableTransactionIds[productId].Add(transactionId);

        // Grant the user their content. You will likely increase some kind of gold/coins/some other asset count.
        numberOfConsumablesPurchased++;
    }
首先他看變數grantedConsumableTransactionIds裡面有沒有Key為productId,如果沒有,就把這Key-Value值加進去,value的部分是新建立的List。然後再加入transactionId。再來的一步他註記說:產生使用者它們的內容物,比方說增加金幣之類的。這就看個人的要求了。最後是增加numberOfConsumablesPurchased的次數(會顯示在Log)。所以這個方法主要是要處理購買後增加實際內容物的部分。
然後是方法IsLocallyFulfilled:
    private Boolean IsLocallyFulfilled(string productId, Guid transactionId)
    {
        return grantedConsumableTransactionIds.ContainsKey(productId) && grantedConsumableTransactionIds[productId].Contains(transactionId);
    }
判斷變數grantedConsumableTransactionIds是否含有Key值productId以及如果有此key的話,是否也有Value的值。單純作為if陳述的判斷而已。
再來看FulfillProduct1:
    private async void FulfillProduct1(string productId, Guid transactionId)
    {
        try
        {
            FulfillmentResult result = await CurrentAppSimulator.ReportConsumableFulfillmentAsync(productId, transactionId);
            switch (result)
            {
                case FulfillmentResult.Succeeded:
                    Log("You bought and fulfilled product 1.", NotifyType.StatusMessage);
                    break;
                case FulfillmentResult.NothingToFulfill:
                    Log("There is no purchased product 1 to fulfill.", NotifyType.StatusMessage);
                    break;
                case FulfillmentResult.PurchasePending:
                    Log("You bought product 1. The purchase is pending so we cannot fulfill the product.", NotifyType.StatusMessage);
                    break;
                case FulfillmentResult.PurchaseReverted:
                    Log("You bought product 1. But your purchase has been reverted.", NotifyType.StatusMessage);
                    // Since the user's purchase was revoked, they got their money back.
                    // You may want to revoke the user's access to the consumable content that was granted.
                    break;
                case FulfillmentResult.ServerError:
                    Log("You bought product 1. There was an error when fulfilling.", NotifyType.StatusMessage);
                    break;
            }
        }
        catch (Exception)
        {
            Log("You bought Product 1. There was an error when fulfilling.", NotifyType.ErrorMessage);
        }
    }
第一行是模擬告訴Win Store說使用者已購買、並且有權訪問該內容。他回傳一個FulfillmentResult型別的值,說明App內消耗品購買的狀況。該型別列舉了五種情形,可自行參考連結內容。單看上述程式碼,大概可以發現這五種情況的意思:
  1. Succeeded 買成功了,商品也實現了
  2. NothingToFulfill 沒有買成,沒有已購買的商品1
  3. PurchasePending 買了,但購買程序還未被清除,還在處理/暫停中,所以還沒實現此商品
  4. PurchaseReverted 買了,但購買已被回復。使用者的錢已退回,你可以在這邊撤銷產生的金幣/道具
  5. ServerError 買了,但要實現商品時有錯誤。
最後要看最主要的方法啦。BuyAndFulfillProduct1Button_Click吧是處理按鈕的方法,如下:
    private async void BuyAndFulfillProduct1Button_Click(object sender, RoutedEventArgs e)
    {
        Log("Buying Product 1...", NotifyType.StatusMessage);
        try
        {
            PurchaseResults purchaseResults = await CurrentAppSimulator.RequestProductPurchaseAsync("product1");
            switch (purchaseResults.Status)
            {
                case ProductPurchaseStatus.Succeeded:
                    GrantFeatureLocally("product1", purchaseResults.TransactionId);
                    FulfillProduct1("product1", purchaseResults.TransactionId);
                    break;
                case ProductPurchaseStatus.NotFulfilled:
                    if (!IsLocallyFulfilled("product1", purchaseResults.TransactionId))
                    {
                        GrantFeatureLocally("product1", purchaseResults.TransactionId);
                    }
                    FulfillProduct1("product1", purchaseResults.TransactionId);
                    break;
                case ProductPurchaseStatus.NotPurchased:
                    Log("Product 1 was not purchased.", NotifyType.StatusMessage);
                    break;
            }
        }
        catch (Exception)
        {
            Log("Unable to buy Product 1.", NotifyType.ErrorMessage);
        }
    }
首先他有更新頁面上的訊息,這個頁面是把訊息更新放在Log這個方法裡面處理,因為訊息都需要處理GetFulfillmentResults方法,所以他另外放在Log裡面,這等一下一起講。
然後是try&catch去處理,你按下購買按鈕後,可以選擇四種情況,除了S_OK之外,其他三種都會失敗,這會丟出catch的例外狀況訊息。然後他檢查purchaseResults.Status,這是App內購買的狀態,有四種,這邊幫大家介紹一下:
  1. Succeeded 購買成功,使用者也已收到通知
  2. AlreadyPurchased 使用者之前已經買過了,無法再次購買
  3. NotFulfilled 前一次此消耗品購買未完成,所以無法完成
  4. NotPurchased 使用者決定不買,或有其他原因導致交易沒有完成
在這邊除了AlreadyPurchased之外,其他都有處理,顯然這是可以重複購買的。
狀況一,成功購買的話,先增加金幣或道具,然後再模擬購買的動作。狀況二,未完成,那就看IsLocallyFulfilled檢查結果,若檢查結果是失敗,那就呼叫GrantFeatureLocally,然後把它完成。最後一個狀況,沒有購買;這你在模擬購買時按取消,就會被呼叫,它就是顯示一個訊息。catch的部分就是其他狀況。

留言