Once the user has selected a product for purchase, we can start the checkout process.
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:
QueryInventoryAsync()
method returns a list of:var products = await billing.QueryInventoryAsync(
new [] { "managed.one" }, ItemType.Product);
var product = products[0];
billing.BuyProduct(product);
string
payload that will be included with any server responses:billing.BuyProduct(product, "DeveloperPayload");
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:
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); }
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; };
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:
adb install path/to/signed.apk
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.
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.
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.
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.
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.