Purchasing products

Once the user has selected a product for purchase, we can start the checkout process.

How to do it...

When we have obtained a list of products, we can present the user with the opportunity to make a purchase. There are two ways to make a purchase, either using a Product instance or just the product ID:

  1. The QueryInventoryAsync() method returns a list of:
    var products = await billing.QueryInventoryAsync(
      new [] { "managed.one" }, ItemType.Product);
    var product = products[0];
    billing.BuyProduct(product);
    
  2. This method also has an overload that accepts a string payload that will be included with any server responses:
    billing.BuyProduct(product, "DeveloperPayload");
  3. Another way to purchase a product is to simply pass the product ID to the BuyProduct() method along with the product type and an optional payload:
    billing.BuyProduct(
      "managed.one", ItemType.Product, "DeveloperPayload");

After the purchase has been made, we will be notified about the success, or failure, of the purchase. This is done through the use of several events:

  1. Before any events are raised, we need to override the OnActivityResult() method on the Activity instance to pass the response to the billing handler:
    protected override void OnActivityResult(
    int requestCode, Result result, Intent data) {
      billing.HandleActivityResult(requestCode, result, data);
    }
  2. Then, we can use the OnProductPurchased event to read any payload data that was passed to the BuyProduct() method:
    billing.OnProductPurchased += (resp, purch, data, sig) => {
      // product was purchased successfully
      var payload = purch.DeveloperPayload;
    };
  3. As with all transactions, there may be a time when something fails for a valid reason. We can respond to these failures using the various error events:
    billing.BuyProductError += (code, sku) => {
      // error attempting to purchase
    };
    billing.OnPurchaseFailedValidation += (purch, data, sig) => {
      // error validating the purchase
    };
    billing.OnUserCanceled += () => {
      // user canceled the purchase
    };

Purchases made using the ReservedTestProductIDs.Purchased product will always succeed and can be used for testing the purchase workflow. However, when we want to test the app making actual product purchases, we have to ensure that the app is signed using the same keystore as the app that was uploaded to Google Play:

  1. We can archive the app for publishing, sign the package and then manually install it on the device using the following command:
    adb install path/to/signed.apk
    
  2. However, if we have access to the keystore, we can add the credentials to the project file:
    How to do it...

    Adding the keystore and credentials to the project file

  3. As with any password, we should never store this into any public location or check it in to a source control.

How it works...

Once the user has selected the desired product, we need to initiate the purchase request. There are two methods to do this. We can specify the Product instance that we obtained from the result of the QueryInventoryAsync() method, or we can just use the product ID and product type.

Note

The BuyProduct instance accepts either a Product instance or a product ID and product type.

When we specify the product by the product ID, we additionally provide a payload. When we use the Product instance, there is an overload method that accepts a payload. This payload can be any string and is always distributed with the Purchase instance when it is returned from the server. This occurs as part of the response to the purchase request as well as when requesting the list of purchased products. The payload is passed as an argument to BuyProduct and returned in the DeveloperPayload property on the Purchase instance.

Tip

The purchase payload can be used to verify that the current user did indeed make the purchase.

To receive the purchase events, whether a success or an error, we need to catch the result from the purchase activity. As soon as the purchase completes, the response will be received and deserialized from the intent data and into a Purchase instance.

To do this, we override the OnActivityResult() method on the requesting activity. Then, we pass the OnActivityResult() method parameter values to the HandleActivityResult() method of the InAppBillingHandler instance. We must override the OnActivityResult() method because the purchase process is performed outside the activity, and the result is then passed to the activity via this method.

As the response is processed, various events are raised depending on the result. If the purchase is successful, the deserialized Purchase instance is provided in the OnProductPurchased event. In the case of an error, one of the several events is raised. If there is an error making the purchase, then the BuyProductError event is raised. If there is an error validating the signature on the purchase product, then the OnPurchaseFailedValidation event is raised. If the user cancels the purchase, then the OnUserCanceled event is raised.

Tip

Purchasing the ReservedTestProductIDs.Purchased product can be used to test a complete successful purchase operation.

Testing purchases can be performed in two ways—either using a test product ID or by making a purchase from a test account. The ReservedTestProductIDs.Purchased reserved ID can be used to test a successful purchase without having to run the app as a signed package from the App Store. This is useful when testing the workflow of a purchase, with the other reserved product IDs being used to test error cases.

Once the purchase workflow is complete and a specific purchase is to be tested, we can purchase the product using a signed app package. We don't have to upload each build, but we do have to ensure that we sign the package with the same keystore as the one already uploaded. Another requirement is that the package name, version name, and version code match exactly.

In addition to the package matching the uploaded package, the user must be a tester that is not the merchant. Also, the user must have been added to the Gmail accounts with testing access section in the console settings. If the user is the owner of the console, then this happens automatically and does not need to be added.

When installing the app on the device, we can download the app from the store, or we can build a custom version during development. We should ensure that the package is still signed with the same keystore, but it can be a Debug build. One way to install the app is to build, archive, and sign the app. The final package can then be installed via ADB.

Another way is to add the credentials into the project file. This is done under the Android Package Signing section of the project options. However, all the values, including the passwords, are stored in plain text in the project file and should not be checked into source control.

There's more...

As the Product instance is nothing special, we can also just construct our own instance without having to first query the server:

var product = new Product {
  ProductId = "managed.one",
  Type = ItemType.Product
};
billing.BuyProduct(product);

This will work; however, we will be bypassing the initial confirmation that the product exists that we get from QueryInventoryAsync. Further, this might not be a problem if we are sure that the product exists. The advantage is that we do not have to first query the service before making a purchase.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset